Home | History | Annotate | Download | only in common
      1 /*
      2  * CDDL HEADER START
      3  *
      4  * The contents of this file are subject to the terms of the
      5  * Common Development and Distribution License (the "License").
      6  * You may not use this file except in compliance with the License.
      7  *
      8  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
      9  * or http://www.opensolaris.org/os/licensing.
     10  * See the License for the specific language governing permissions
     11  * and limitations under the License.
     12  *
     13  * When distributing Covered Code, include this CDDL HEADER in each
     14  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
     15  * If applicable, add the following below this CDDL HEADER, with the
     16  * fields enclosed by brackets "[]" replaced with your own identifying
     17  * information: Portions Copyright [yyyy] [name of copyright owner]
     18  *
     19  * CDDL HEADER END
     20  */
     21 
     22 /*
     23  * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
     24  * Use is subject to license terms.
     25  */
     26 
     27 #include <ctype.h>
     28 #include <string.h>
     29 #include <limits.h>
     30 #include <fm/topo_mod.h>
     31 #include <fm/fmd_fmri.h>
     32 #include <sys/fm/protocol.h>
     33 #include <topo_alloc.h>
     34 #include <topo_error.h>
     35 #include <topo_hc.h>
     36 #include <topo_method.h>
     37 #include <topo_subr.h>
     38 #include <topo_string.h>
     39 
     40 /*
     41  * Topology node properties and method operations may be accessed by FMRI.
     42  * The FMRI used to perform property look-ups and method operations is
     43  * the FMRI contained in the matching topology node's protocol property
     44  * grouping for the resource property. The full range of fmd(1M)
     45  * scheme plugin operations are supported as long as a backend method is
     46  * supplied by a scheme-specific enumerator or the enumerator module that
     47  * created the matching topology node.  Support for fmd scheme operations
     48  * include:
     49  *
     50  *	- expand
     51  *	- present
     52  *	- replaced
     53  *	- contains
     54  *	- unusable
     55  *	- service_state
     56  *	- nvl2str
     57  *	- retire
     58  *	- unretire
     59  *
     60  * In addition, the following operations are supported per-FMRI:
     61  *
     62  *	- str2nvl: convert string-based FMRI to nvlist
     63  *	- compare: compare two FMRIs
     64  *	- asru: lookup associated ASRU property by FMRI
     65  *	- fru: lookup associated FRU by FMRI
     66  *	- create: an FMRI nvlist by scheme type
     67  *	- propery lookup
     68  *
     69  * These routines may only be called by consumers of a topology snapshot.
     70  * They may not be called by libtopo enumerator or method modules.
     71  */
     72 
     73 /*ARGSUSED*/
     74 static int
     75 set_error(topo_hdl_t *thp, int err, int *errp, char *method, nvlist_t *nvlp)
     76 {
     77 	if (nvlp != NULL)
     78 		nvlist_free(nvlp);
     79 
     80 	topo_dprintf(thp, TOPO_DBG_ERR, "%s failed: %s\n", method,
     81 	    topo_strerror(err));
     82 
     83 	*errp = err;
     84 	return (-1);
     85 }
     86 
     87 /*ARGSUSED*/
     88 static nvlist_t *
     89 set_nverror(topo_hdl_t *thp, int err, int *errp, char *method, nvlist_t *nvlp)
     90 {
     91 	if (nvlp != NULL)
     92 		nvlist_free(nvlp);
     93 
     94 	topo_dprintf(thp, TOPO_DBG_ERR, "%s failed: %s\n", method,
     95 	    topo_strerror(err));
     96 
     97 	*errp = err;
     98 	return (NULL);
     99 }
    100 
    101 int
    102 topo_fmri_nvl2str(topo_hdl_t *thp, nvlist_t *fmri, char **fmristr, int *err)
    103 {
    104 	char *scheme, *str;
    105 	nvlist_t *out = NULL;
    106 	tnode_t *rnode;
    107 
    108 	if (nvlist_lookup_string(fmri, FM_FMRI_SCHEME, &scheme) != 0)
    109 		return (set_error(thp, ETOPO_FMRI_MALFORM, err,
    110 		    TOPO_METH_NVL2STR, out));
    111 
    112 	if ((rnode = topo_hdl_root(thp, scheme)) == NULL)
    113 		return (set_error(thp, ETOPO_METHOD_NOTSUP, err,
    114 		    TOPO_METH_NVL2STR, out));
    115 
    116 	if (topo_method_invoke(rnode, TOPO_METH_NVL2STR,
    117 	    TOPO_METH_NVL2STR_VERSION, fmri, &out, err) != 0)
    118 		return (set_error(thp, *err, err, TOPO_METH_NVL2STR, out));
    119 
    120 	if (out == NULL || nvlist_lookup_string(out, "fmri-string", &str) != 0)
    121 		return (set_error(thp, ETOPO_METHOD_INVAL, err,
    122 		    TOPO_METH_NVL2STR, out));
    123 
    124 	if ((*fmristr = topo_hdl_strdup(thp, str)) == NULL)
    125 		return (set_error(thp, ETOPO_NOMEM, err,
    126 		    TOPO_METH_NVL2STR, out));
    127 
    128 	nvlist_free(out);
    129 
    130 	return (0);
    131 }
    132 
    133 int
    134 topo_fmri_str2nvl(topo_hdl_t *thp, const char *fmristr, nvlist_t **fmri,
    135     int *err)
    136 {
    137 	char *f, buf[PATH_MAX];
    138 	nvlist_t *out = NULL, *in = NULL;
    139 	tnode_t *rnode;
    140 
    141 	(void) strlcpy(buf, fmristr, sizeof (buf));
    142 	if ((f = strchr(buf, ':')) == NULL)
    143 		return (set_error(thp, ETOPO_FMRI_MALFORM, err,
    144 		    TOPO_METH_STR2NVL, in));
    145 
    146 	*f = '\0'; /* strip trailing FMRI path */
    147 
    148 	if ((rnode = topo_hdl_root(thp, buf)) == NULL)
    149 		return (set_error(thp, ETOPO_METHOD_NOTSUP, err,
    150 		    TOPO_METH_STR2NVL, in));
    151 
    152 	if (topo_hdl_nvalloc(thp, &in, NV_UNIQUE_NAME) != 0)
    153 		return (set_error(thp, ETOPO_FMRI_NVL, err, TOPO_METH_STR2NVL,
    154 		    in));
    155 
    156 	if (nvlist_add_string(in, "fmri-string", fmristr) != 0)
    157 		return (set_error(thp, ETOPO_FMRI_NVL, err, TOPO_METH_STR2NVL,
    158 		    in));
    159 
    160 	if (topo_method_invoke(rnode, TOPO_METH_STR2NVL,
    161 	    TOPO_METH_STR2NVL_VERSION, in, &out, err) != 0)
    162 		return (set_error(thp, *err, err, TOPO_METH_STR2NVL, in));
    163 
    164 	nvlist_free(in);
    165 
    166 	if (out == NULL ||
    167 	    topo_hdl_nvdup(thp, out, fmri) != 0)
    168 		return (set_error(thp, ETOPO_FMRI_NVL, err,
    169 		    TOPO_METH_STR2NVL, out));
    170 
    171 	nvlist_free(out);
    172 
    173 	return (0);
    174 }
    175 
    176 int
    177 topo_fmri_present(topo_hdl_t *thp, nvlist_t *fmri, int *err)
    178 {
    179 	uint32_t present = 0;
    180 	char *scheme;
    181 	nvlist_t *out = NULL;
    182 	tnode_t *rnode;
    183 
    184 	if (nvlist_lookup_string(fmri, FM_FMRI_SCHEME, &scheme) != 0)
    185 		return (set_error(thp, ETOPO_FMRI_MALFORM, err,
    186 		    TOPO_METH_PRESENT, out));
    187 
    188 	if ((rnode = topo_hdl_root(thp, scheme)) == NULL)
    189 		return (set_error(thp, ETOPO_METHOD_NOTSUP, err,
    190 		    TOPO_METH_PRESENT, out));
    191 
    192 	if (topo_method_invoke(rnode, TOPO_METH_PRESENT,
    193 	    TOPO_METH_PRESENT_VERSION, fmri, &out, err) < 0) {
    194 		(void) set_error(thp, *err, err, TOPO_METH_PRESENT, out);
    195 		return (present);
    196 	}
    197 
    198 	(void) nvlist_lookup_uint32(out, TOPO_METH_PRESENT_RET, &present);
    199 	nvlist_free(out);
    200 
    201 	return (present);
    202 }
    203 
    204 int
    205 topo_fmri_replaced(topo_hdl_t *thp, nvlist_t *fmri, int *err)
    206 {
    207 	uint32_t replaced = FMD_OBJ_STATE_NOT_PRESENT;
    208 	char *scheme;
    209 	nvlist_t *out = NULL;
    210 	tnode_t *rnode;
    211 
    212 	if (nvlist_lookup_string(fmri, FM_FMRI_SCHEME, &scheme) != 0)
    213 		return (set_error(thp, ETOPO_FMRI_MALFORM, err,
    214 		    TOPO_METH_REPLACED, out));
    215 
    216 	if ((rnode = topo_hdl_root(thp, scheme)) == NULL)
    217 		return (set_error(thp, ETOPO_METHOD_NOTSUP, err,
    218 		    TOPO_METH_REPLACED, out));
    219 
    220 	if (topo_method_invoke(rnode, TOPO_METH_REPLACED,
    221 	    TOPO_METH_REPLACED_VERSION, fmri, &out, err) < 0) {
    222 		(void) set_error(thp, *err, err, TOPO_METH_REPLACED, out);
    223 		return (FMD_OBJ_STATE_UNKNOWN);
    224 	}
    225 
    226 	(void) nvlist_lookup_uint32(out, TOPO_METH_REPLACED_RET, &replaced);
    227 	nvlist_free(out);
    228 
    229 	return (replaced);
    230 }
    231 
    232 int
    233 topo_fmri_contains(topo_hdl_t *thp, nvlist_t *fmri, nvlist_t *subfmri, int *err)
    234 {
    235 	uint32_t contains;
    236 	char *scheme;
    237 	nvlist_t *in = NULL, *out = NULL;
    238 	tnode_t *rnode;
    239 
    240 	if (nvlist_lookup_string(fmri, FM_FMRI_SCHEME, &scheme) != 0)
    241 		return (set_error(thp, ETOPO_FMRI_MALFORM, err,
    242 		    TOPO_METH_CONTAINS, NULL));
    243 
    244 	if ((rnode = topo_hdl_root(thp, scheme)) == NULL)
    245 		return (set_error(thp, ETOPO_METHOD_NOTSUP, err,
    246 		    TOPO_METH_CONTAINS, NULL));
    247 
    248 	if (topo_hdl_nvalloc(thp, &in, NV_UNIQUE_NAME) != 0)
    249 		return (set_error(thp, ETOPO_FMRI_NVL, err, TOPO_METH_CONTAINS,
    250 		    NULL));
    251 
    252 	if (nvlist_add_nvlist(in, TOPO_METH_FMRI_ARG_FMRI, fmri) != 0 ||
    253 	    nvlist_add_nvlist(in, TOPO_METH_FMRI_ARG_SUBFMRI, subfmri) != 0)
    254 		return (set_error(thp, ETOPO_FMRI_NVL, err, TOPO_METH_CONTAINS,
    255 		    in));
    256 
    257 	if (topo_method_invoke(rnode, TOPO_METH_CONTAINS,
    258 	    TOPO_METH_CONTAINS_VERSION, in, &out, err) < 0)
    259 		return (set_error(thp, *err, err, TOPO_METH_CONTAINS, in));
    260 
    261 	(void) nvlist_lookup_uint32(out, TOPO_METH_CONTAINS_RET, &contains);
    262 	nvlist_free(in);
    263 	nvlist_free(out);
    264 
    265 	return (contains);
    266 }
    267 
    268 int
    269 topo_fmri_unusable(topo_hdl_t *thp, nvlist_t *fmri, int *err)
    270 {
    271 	char *scheme;
    272 	uint32_t unusable = 0;
    273 	nvlist_t *out = NULL;
    274 	tnode_t *rnode;
    275 
    276 	if (nvlist_lookup_string(fmri, FM_FMRI_SCHEME, &scheme) != 0)
    277 		return (set_error(thp, ETOPO_FMRI_MALFORM, err,
    278 		    TOPO_METH_UNUSABLE, out));
    279 
    280 	if ((rnode = topo_hdl_root(thp, scheme)) == NULL)
    281 		return (set_error(thp, ETOPO_METHOD_NOTSUP, err,
    282 		    TOPO_METH_UNUSABLE, out));
    283 
    284 	if (topo_method_invoke(rnode, TOPO_METH_UNUSABLE,
    285 	    TOPO_METH_UNUSABLE_VERSION, fmri, &out, err) < 0)
    286 		return (set_error(thp, *err, err, TOPO_METH_UNUSABLE, out));
    287 
    288 	(void) nvlist_lookup_uint32(out, TOPO_METH_UNUSABLE_RET, &unusable);
    289 	nvlist_free(out);
    290 
    291 	return (unusable);
    292 }
    293 
    294 int
    295 topo_fmri_retire(topo_hdl_t *thp, nvlist_t *fmri, int *err)
    296 {
    297 	char *scheme;
    298 	uint32_t status;
    299 	nvlist_t *out = NULL;
    300 	tnode_t *rnode;
    301 
    302 	if (nvlist_lookup_string(fmri, FM_FMRI_SCHEME, &scheme) != 0)
    303 		return (set_error(thp, ETOPO_FMRI_MALFORM, err,
    304 		    TOPO_METH_RETIRE, out));
    305 
    306 	if ((rnode = topo_hdl_root(thp, scheme)) == NULL)
    307 		return (set_error(thp, ETOPO_METHOD_NOTSUP, err,
    308 		    TOPO_METH_RETIRE, out));
    309 
    310 	if (topo_method_invoke(rnode, TOPO_METH_RETIRE,
    311 	    TOPO_METH_RETIRE_VERSION, fmri, &out, err) < 0)
    312 		return (set_error(thp, *err, err, TOPO_METH_RETIRE, out));
    313 
    314 	if (nvlist_lookup_uint32(out, TOPO_METH_RETIRE_RET, &status) != 0)
    315 		return (set_error(thp, ETOPO_METHOD_FAIL, err,
    316 		    TOPO_METH_RETIRE, out));
    317 	nvlist_free(out);
    318 
    319 	return (status);
    320 }
    321 
    322 int
    323 topo_fmri_unretire(topo_hdl_t *thp, nvlist_t *fmri, int *err)
    324 {
    325 	char *scheme;
    326 	uint32_t status;
    327 	nvlist_t *out = NULL;
    328 	tnode_t *rnode;
    329 
    330 	if (nvlist_lookup_string(fmri, FM_FMRI_SCHEME, &scheme) != 0)
    331 		return (set_error(thp, ETOPO_FMRI_MALFORM, err,
    332 		    TOPO_METH_UNRETIRE, out));
    333 
    334 	if ((rnode = topo_hdl_root(thp, scheme)) == NULL)
    335 		return (set_error(thp, ETOPO_METHOD_NOTSUP, err,
    336 		    TOPO_METH_UNRETIRE, out));
    337 
    338 	if (topo_method_invoke(rnode, TOPO_METH_UNRETIRE,
    339 	    TOPO_METH_UNRETIRE_VERSION, fmri, &out, err) < 0)
    340 		return (set_error(thp, *err, err, TOPO_METH_UNRETIRE, out));
    341 
    342 	if (nvlist_lookup_uint32(out, TOPO_METH_UNRETIRE_RET, &status) != 0) {
    343 		nvlist_free(out);
    344 		return (set_error(thp, ETOPO_METHOD_FAIL, err,
    345 		    TOPO_METH_UNRETIRE, out));
    346 	}
    347 	nvlist_free(out);
    348 
    349 	return (status);
    350 }
    351 
    352 int
    353 topo_fmri_service_state(topo_hdl_t *thp, nvlist_t *fmri, int *err)
    354 {
    355 	char *scheme;
    356 	uint32_t service_state = FMD_SERVICE_STATE_UNKNOWN;
    357 	nvlist_t *out = NULL;
    358 	tnode_t *rnode;
    359 
    360 	if (nvlist_lookup_string(fmri, FM_FMRI_SCHEME, &scheme) != 0)
    361 		return (set_error(thp, ETOPO_FMRI_MALFORM, err,
    362 		    TOPO_METH_SERVICE_STATE, out));
    363 
    364 	if ((rnode = topo_hdl_root(thp, scheme)) == NULL)
    365 		return (set_error(thp, ETOPO_METHOD_NOTSUP, err,
    366 		    TOPO_METH_SERVICE_STATE, out));
    367 
    368 	if (topo_method_invoke(rnode, TOPO_METH_SERVICE_STATE,
    369 	    TOPO_METH_SERVICE_STATE_VERSION, fmri, &out, err) < 0)
    370 		return (set_error(thp, *err, err, TOPO_METH_SERVICE_STATE,
    371 		    out));
    372 
    373 	(void) nvlist_lookup_uint32(out, TOPO_METH_SERVICE_STATE_RET,
    374 	    &service_state);
    375 	nvlist_free(out);
    376 
    377 	return (service_state);
    378 }
    379 
    380 int
    381 topo_fmri_expand(topo_hdl_t *thp, nvlist_t *fmri, int *err)
    382 {
    383 	char *scheme;
    384 	nvlist_t *out = NULL;
    385 	tnode_t *rnode;
    386 
    387 	if (nvlist_lookup_string(fmri, FM_FMRI_SCHEME, &scheme) != 0)
    388 		return (set_error(thp, ETOPO_FMRI_MALFORM, err,
    389 		    TOPO_METH_EXPAND, out));
    390 
    391 	if ((rnode = topo_hdl_root(thp, scheme)) == NULL)
    392 		return (set_error(thp, ETOPO_METHOD_NOTSUP, err,
    393 		    TOPO_METH_EXPAND, out));
    394 
    395 	if (topo_method_invoke(rnode, TOPO_METH_EXPAND,
    396 	    TOPO_METH_EXPAND_VERSION, fmri, &out, err) != 0)
    397 		return (set_error(thp, *err, err, TOPO_METH_EXPAND, out));
    398 
    399 	return (0);
    400 }
    401 
    402 static int
    403 fmri_prop(topo_hdl_t *thp, nvlist_t *rsrc, const char *pgname,
    404     const char *pname, nvlist_t *args, nvlist_t **prop,
    405     int *err)
    406 {
    407 	int rv;
    408 	nvlist_t *in = NULL;
    409 	tnode_t *rnode;
    410 	char *scheme;
    411 
    412 	if (nvlist_lookup_string(rsrc, FM_FMRI_SCHEME, &scheme) != 0)
    413 		return (set_error(thp, ETOPO_FMRI_MALFORM, err,
    414 		    TOPO_METH_PROP_GET, in));
    415 
    416 	if ((rnode = topo_hdl_root(thp, scheme)) == NULL)
    417 		return (set_error(thp, ETOPO_METHOD_NOTSUP, err,
    418 		    TOPO_METH_PROP_GET, in));
    419 
    420 	if (topo_hdl_nvalloc(thp, &in, NV_UNIQUE_NAME) != 0)
    421 		return (set_error(thp, ETOPO_FMRI_NVL, err,
    422 		    TOPO_METH_PROP_GET, in));
    423 
    424 	rv = nvlist_add_nvlist(in, TOPO_PROP_RESOURCE, rsrc);
    425 	rv |= nvlist_add_string(in, TOPO_PROP_GROUP, pgname);
    426 	rv |= nvlist_add_string(in, TOPO_PROP_VAL_NAME, pname);
    427 	if (args != NULL)
    428 		rv |= nvlist_add_nvlist(in, TOPO_PROP_PARGS, args);
    429 	if (rv != 0)
    430 		return (set_error(thp, ETOPO_FMRI_NVL, err,
    431 		    TOPO_METH_PROP_GET, in));
    432 
    433 	*prop = NULL;
    434 	rv = topo_method_invoke(rnode, TOPO_METH_PROP_GET,
    435 	    TOPO_METH_PROP_GET_VERSION, in, prop, err);
    436 
    437 	nvlist_free(in);
    438 
    439 	if (rv != 0)
    440 		return (-1); /* *err is set for us */
    441 
    442 	if (*prop == NULL)
    443 		return (set_error(thp, ETOPO_PROP_NOENT, err,
    444 		    TOPO_METH_PROP_GET, NULL));
    445 	return (0);
    446 }
    447 
    448 int
    449 topo_fmri_asru(topo_hdl_t *thp, nvlist_t *nvl, nvlist_t **asru, int *err)
    450 {
    451 	nvlist_t *ap, *prop = NULL;
    452 
    453 	if (fmri_prop(thp, nvl, TOPO_PGROUP_PROTOCOL, TOPO_PROP_ASRU,
    454 	    nvl, &prop, err) < 0)
    455 		return (set_error(thp, *err, err, "topo_fmri_asru", NULL));
    456 
    457 	if (nvlist_lookup_nvlist(prop, TOPO_PROP_VAL_VAL, &ap) != 0)
    458 		return (set_error(thp, ETOPO_PROP_NVL, err, "topo_fmri_asru",
    459 		    prop));
    460 
    461 	if (topo_hdl_nvdup(thp, ap, asru) < 0)
    462 		return (set_error(thp, ETOPO_PROP_NOMEM, err, "topo_fmri_asru",
    463 		    prop));
    464 
    465 	nvlist_free(prop);
    466 
    467 	return (0);
    468 }
    469 
    470 int
    471 topo_fmri_fru(topo_hdl_t *thp, nvlist_t *nvl, nvlist_t **fru, int *err)
    472 {
    473 	nvlist_t *fp, *prop = NULL;
    474 
    475 	if (fmri_prop(thp, nvl, TOPO_PGROUP_PROTOCOL, TOPO_PROP_FRU,
    476 	    nvl, &prop, err) < 0)
    477 		return (set_error(thp, *err, err, "topo_fmri_fru", NULL));
    478 
    479 	if (nvlist_lookup_nvlist(prop, TOPO_PROP_VAL_VAL, &fp) != 0)
    480 		return (set_error(thp, ETOPO_PROP_NVL, err, "topo_fmri_fru",
    481 		    prop));
    482 
    483 	if (topo_hdl_nvdup(thp, fp, fru) < 0)
    484 		return (set_error(thp, ETOPO_PROP_NOMEM, err, "topo_fmri_fru",
    485 		    prop));
    486 
    487 	nvlist_free(prop);
    488 
    489 	return (0);
    490 }
    491 
    492 int
    493 topo_fmri_label(topo_hdl_t *thp, nvlist_t *nvl, char **label, int *err)
    494 {
    495 	nvlist_t *prop = NULL;
    496 	char *lp;
    497 
    498 	if (fmri_prop(thp, nvl, TOPO_PGROUP_PROTOCOL, TOPO_PROP_LABEL,
    499 	    NULL, &prop, err) < 0)
    500 		return (set_error(thp, *err, err, "topo_fmri_label", NULL));
    501 
    502 	if (nvlist_lookup_string(prop, TOPO_PROP_VAL_VAL, &lp) != 0)
    503 		return (set_error(thp, ETOPO_PROP_NVL, err, "topo_fmri_label",
    504 		    prop));
    505 
    506 	if ((*label = topo_hdl_strdup(thp, lp)) == NULL)
    507 		return (set_error(thp, ETOPO_PROP_NOMEM, err, "topo_fmri_label",
    508 		    prop));
    509 
    510 	nvlist_free(prop);
    511 
    512 	return (0);
    513 }
    514 
    515 int
    516 topo_fmri_serial(topo_hdl_t *thp, nvlist_t *nvl, char **serial, int *err)
    517 {
    518 	nvlist_t *prop = NULL;
    519 	char *sp;
    520 
    521 	/*
    522 	 * If there is a serial id in the resource fmri, then use that.
    523 	 * Otherwise fall back to looking for a serial id property in the
    524 	 * protocol group.
    525 	 */
    526 	if (nvlist_lookup_string(nvl, FM_FMRI_HC_SERIAL_ID, &sp) == 0) {
    527 		if ((*serial = topo_hdl_strdup(thp, sp)) == NULL)
    528 			return (set_error(thp, ETOPO_PROP_NOMEM, err,
    529 			    "topo_fmri_serial", prop));
    530 		else
    531 			return (0);
    532 	}
    533 
    534 	if (fmri_prop(thp, nvl, TOPO_PGROUP_PROTOCOL, FM_FMRI_HC_SERIAL_ID,
    535 	    NULL, &prop, err) < 0)
    536 		return (set_error(thp, *err, err, "topo_fmri_serial", NULL));
    537 
    538 	if (nvlist_lookup_string(prop, TOPO_PROP_VAL_VAL, &sp) != 0)
    539 		return (set_error(thp, ETOPO_PROP_NVL, err, "topo_fmri_serial",
    540 		    prop));
    541 
    542 	if ((*serial = topo_hdl_strdup(thp, sp)) == NULL)
    543 		return (set_error(thp, ETOPO_PROP_NOMEM, err,
    544 		    "topo_fmri_serial", prop));
    545 
    546 	nvlist_free(prop);
    547 
    548 	return (0);
    549 }
    550 
    551 int topo_fmri_getprop(topo_hdl_t *thp, nvlist_t *nvl, const char *pg,
    552     const char *pname, nvlist_t *args,  nvlist_t **prop,
    553     int *err)
    554 {
    555 	*prop = NULL;
    556 
    557 	return (fmri_prop(thp, nvl, pg, pname, args, prop, err));
    558 }
    559 
    560 int topo_fmri_setprop(topo_hdl_t *thp, nvlist_t *nvl, const char *pg,
    561     nvlist_t *prop, int flag, nvlist_t *args, int *err)
    562 {
    563 	int rv;
    564 	nvlist_t *in = NULL, *out = NULL;
    565 	tnode_t *rnode;
    566 	char *scheme;
    567 
    568 	if (nvlist_lookup_string(nvl, FM_FMRI_SCHEME, &scheme) != 0)
    569 		return (set_error(thp, ETOPO_FMRI_MALFORM, err,
    570 		    TOPO_METH_PROP_SET, in));
    571 
    572 	if ((rnode = topo_hdl_root(thp, scheme)) == NULL)
    573 		return (set_error(thp, ETOPO_METHOD_NOTSUP, err,
    574 		    TOPO_METH_PROP_SET, in));
    575 
    576 	if (topo_hdl_nvalloc(thp, &in, NV_UNIQUE_NAME) != 0)
    577 		return (set_error(thp, ETOPO_FMRI_NVL, err,
    578 		    TOPO_METH_PROP_SET, in));
    579 
    580 	rv = nvlist_add_nvlist(in, TOPO_PROP_RESOURCE, nvl);
    581 	rv |= nvlist_add_string(in, TOPO_PROP_GROUP, pg);
    582 	rv |= nvlist_add_nvlist(in, TOPO_PROP_VAL, prop);
    583 	rv |= nvlist_add_int32(in, TOPO_PROP_FLAG, (int32_t)flag);
    584 	if (args != NULL)
    585 		rv |= nvlist_add_nvlist(in, TOPO_PROP_PARGS, args);
    586 	if (rv != 0)
    587 		return (set_error(thp, ETOPO_FMRI_NVL, err,
    588 		    TOPO_METH_PROP_SET, in));
    589 
    590 	rv = topo_method_invoke(rnode, TOPO_METH_PROP_SET,
    591 	    TOPO_METH_PROP_SET_VERSION, in, &out, err);
    592 
    593 	nvlist_free(in);
    594 
    595 	/* no return values */
    596 	if (out != NULL)
    597 		nvlist_free(out);
    598 
    599 	if (rv)
    600 		return (-1);
    601 
    602 	return (0);
    603 
    604 }
    605 
    606 int
    607 topo_fmri_getpgrp(topo_hdl_t *thp, nvlist_t *rsrc, const char *pgname,
    608     nvlist_t **pgroup, int *err)
    609 {
    610 	int rv;
    611 	nvlist_t *in = NULL;
    612 	tnode_t *rnode;
    613 	char *scheme;
    614 
    615 	if (nvlist_lookup_string(rsrc, FM_FMRI_SCHEME, &scheme) != 0)
    616 		return (set_error(thp, ETOPO_FMRI_MALFORM, err,
    617 		    TOPO_METH_PROP_GET, in));
    618 
    619 	if ((rnode = topo_hdl_root(thp, scheme)) == NULL)
    620 		return (set_error(thp, ETOPO_METHOD_NOTSUP, err,
    621 		    TOPO_METH_PROP_GET, in));
    622 
    623 	if (topo_hdl_nvalloc(thp, &in, NV_UNIQUE_NAME) != 0)
    624 		return (set_error(thp, ETOPO_FMRI_NVL, err,
    625 		    TOPO_METH_PROP_GET, in));
    626 
    627 	rv = nvlist_add_nvlist(in, TOPO_PROP_RESOURCE, rsrc);
    628 	rv |= nvlist_add_string(in, TOPO_PROP_GROUP, pgname);
    629 	if (rv != 0)
    630 		return (set_error(thp, ETOPO_FMRI_NVL, err,
    631 		    TOPO_METH_PROP_GET, in));
    632 
    633 	*pgroup = NULL;
    634 	rv = topo_method_invoke(rnode, TOPO_METH_PGRP_GET,
    635 	    TOPO_METH_PGRP_GET_VERSION, in, pgroup, err);
    636 
    637 	nvlist_free(in);
    638 
    639 	if (rv != 0)
    640 		return (-1); /* *err is set for us */
    641 
    642 	if (*pgroup == NULL)
    643 		return (set_error(thp, ETOPO_PROP_NOENT, err,
    644 		    TOPO_METH_PROP_GET, NULL));
    645 	return (0);
    646 }
    647 
    648 int
    649 topo_fmri_compare(topo_hdl_t *thp, nvlist_t *f1, nvlist_t *f2, int *err)
    650 {
    651 	uint32_t compare;
    652 	char *scheme1, *scheme2;
    653 	nvlist_t *in;
    654 	nvlist_t *out = NULL;
    655 	tnode_t *rnode;
    656 
    657 	if (nvlist_lookup_string(f1, FM_FMRI_SCHEME, &scheme1) != 0)
    658 		return (set_error(thp, ETOPO_FMRI_MALFORM, err,
    659 		    TOPO_METH_COMPARE, NULL));
    660 	if (nvlist_lookup_string(f2, FM_FMRI_SCHEME, &scheme2) != 0)
    661 		return (set_error(thp, ETOPO_FMRI_MALFORM, err,
    662 		    TOPO_METH_COMPARE, NULL));
    663 
    664 	if (strcmp(scheme1, scheme2) != 0)
    665 		return (0);
    666 
    667 	if ((rnode = topo_hdl_root(thp, scheme1)) == NULL)
    668 		return (set_error(thp, ETOPO_METHOD_NOTSUP, err,
    669 		    TOPO_METH_COMPARE, NULL));
    670 
    671 	if (topo_hdl_nvalloc(thp, &in, NV_UNIQUE_NAME) != 0)
    672 		return (set_error(thp, ETOPO_FMRI_NVL, err, TOPO_METH_COMPARE,
    673 		    NULL));
    674 
    675 	if (nvlist_add_nvlist(in, TOPO_METH_FMRI_ARG_NV1, f1) != 0 ||
    676 	    nvlist_add_nvlist(in, TOPO_METH_FMRI_ARG_NV2, f2) != 0)
    677 		return (set_error(thp, ETOPO_FMRI_NVL, err, TOPO_METH_COMPARE,
    678 		    in));
    679 
    680 	if (topo_method_invoke(rnode, TOPO_METH_COMPARE,
    681 	    TOPO_METH_COMPARE_VERSION, in, &out, err) < 0)
    682 		return (set_error(thp, *err, err, TOPO_METH_COMPARE, in));
    683 
    684 	(void) nvlist_lookup_uint32(out, TOPO_METH_COMPARE_RET, &compare);
    685 	nvlist_free(out);
    686 	nvlist_free(in);
    687 
    688 	return (compare);
    689 }
    690 
    691 /*
    692  * topo_fmri_create
    693  *
    694  *	If possible, creates an FMRI of the requested version in the
    695  *	requested scheme.  Args are passed as part of the inputs to the
    696  *	fmri-create method of the scheme.
    697  */
    698 nvlist_t *
    699 topo_fmri_create(topo_hdl_t *thp, const char *scheme, const char *name,
    700     topo_instance_t inst, nvlist_t *nvl, int *err)
    701 {
    702 	nvlist_t *ins;
    703 	nvlist_t *out;
    704 	tnode_t *rnode;
    705 
    706 	ins = out = NULL;
    707 
    708 	if ((rnode = topo_hdl_root(thp, scheme)) == NULL)
    709 		return (set_nverror(thp, ETOPO_METHOD_NOTSUP, err,
    710 		    TOPO_METH_FMRI, NULL));
    711 
    712 	if ((*err = topo_hdl_nvalloc(thp, &ins, NV_UNIQUE_NAME)) != 0)
    713 		return (set_nverror(thp, ETOPO_FMRI_NVL, err,
    714 		    TOPO_METH_FMRI, NULL));
    715 
    716 	if (nvlist_add_string(ins, TOPO_METH_FMRI_ARG_NAME, name) != 0 ||
    717 	    nvlist_add_uint32(ins, TOPO_METH_FMRI_ARG_INST, inst) != 0) {
    718 		return (set_nverror(thp, ETOPO_FMRI_NVL, err,
    719 		    TOPO_METH_FMRI, ins));
    720 	}
    721 
    722 	if (nvl != NULL &&
    723 	    nvlist_add_nvlist(ins, TOPO_METH_FMRI_ARG_NVL, nvl) != 0) {
    724 		return (set_nverror(thp, ETOPO_FMRI_NVL, err,
    725 		    TOPO_METH_FMRI, ins));
    726 	}
    727 	if (topo_method_invoke(rnode,
    728 	    TOPO_METH_FMRI, TOPO_METH_FMRI_VERSION, ins, &out, err) != 0) {
    729 		return (set_nverror(thp, *err, err, TOPO_METH_FMRI, ins));
    730 	}
    731 	nvlist_free(ins);
    732 	return (out);
    733 }
    734 
    735 /*
    736  * These private utility functions are used by fmd to maintain its resource
    737  * cache.  Because hc instance numbers are not guaranteed, it's possible to
    738  * have two different FMRI strings represent the same logical entity.  These
    739  * functions hide this implementation detail from unknowing consumers such as
    740  * fmd.
    741  *
    742  * Ideally, we'd like to do a str2nvl() and then a full FMRI hash and
    743  * comparison, but these functions are designed to be fast and efficient.
    744  * Given that there is only a single hc node that has this property
    745  * (ses-enclosure), we hard-code this behavior here.  If there are more
    746  * instances of this behavior in the future, this function could be made more
    747  * generic.
    748  *
    749  * This code also handles changes in the server-id or revision fields of the hc
    750  * FMRI, as these fields have no bearing on equivalence of FRUs.
    751  */
    752 static ulong_t
    753 topo_fmri_strhash_one(const char *fmri, size_t len)
    754 {
    755 	ulong_t g, h = 0;
    756 	size_t i;
    757 
    758 	for (i = 0; i < len; i++) {
    759 		h = (h << 4) + fmri[i];
    760 
    761 		if ((g = (h & 0xf0000000)) != 0) {
    762 			h ^= (g >> 24);
    763 			h ^= g;
    764 		}
    765 	}
    766 
    767 	return (h);
    768 }
    769 
    770 static const char *
    771 topo_fmri_next_auth(const char *auth)
    772 {
    773 	const char *colon, *slash;
    774 
    775 	colon = strchr(auth + 1, ':');
    776 	slash = strchr(auth, '/');
    777 
    778 	if (colon == NULL && slash == NULL)
    779 		return (NULL);
    780 
    781 	if (colon == NULL)
    782 		return (slash);
    783 	else if (slash < colon)
    784 		return (slash);
    785 	else
    786 		return (colon);
    787 }
    788 
    789 /*
    790  * List of authority information we care about.  Note that we explicitly ignore
    791  * things that are properties of the chassis and not the resource itself:
    792  *
    793  * 	FM_FMRI_AUTH_PRODUCT_SN		"product-sn"
    794  * 	FM_FMRI_AUTH_PRODUCT		"product-id"
    795  * 	FM_FMRI_AUTH_DOMAIN		"domain-id"
    796  * 	FM_FMRI_AUTH_SERVER		"server-id"
    797  *	FM_FMRI_AUTH_HOST		"host-id"
    798  *
    799  * We also ignore the "revision" authority member, as that typically indicates
    800  * the firmware revision and is not a static property of the FRU.  This leaves
    801  * the following interesting members:
    802  *
    803  * 	FM_FMRI_AUTH_CHASSIS		"chassis-id"
    804  *	FM_FMRI_HC_SERIAL_ID		"serial"
    805  *	FM_FMRI_HC_PART			"part"
    806  */
    807 typedef enum {
    808 	HC_AUTH_CHASSIS,
    809 	HC_AUTH_SERIAL,
    810 	HC_AUTH_PART,
    811 	HC_AUTH_MAX
    812 } hc_auth_type_t;
    813 
    814 static char *hc_auth_table[] = {
    815 	FM_FMRI_AUTH_CHASSIS,
    816 	FM_FMRI_HC_SERIAL_ID,
    817 	FM_FMRI_HC_PART
    818 };
    819 
    820 /*
    821  * Takes an authority member, with leading ":" and trailing "=", and returns
    822  * one of the above types if it's one of the things we care about.  If
    823  * 'authlen' is specified, it is filled in with the length of the authority
    824  * member, including leading and trailing characters.
    825  */
    826 static hc_auth_type_t
    827 hc_auth_to_type(const char *auth, size_t *authlen)
    828 {
    829 	int i;
    830 	size_t len;
    831 
    832 	if (auth[0] != ':')
    833 		return (HC_AUTH_MAX);
    834 
    835 	for (i = 0; i < HC_AUTH_MAX; i++) {
    836 		len = strlen(hc_auth_table[i]);
    837 
    838 		if (strncmp(auth + 1, hc_auth_table[i], len) == 0 &&
    839 		    auth[len + 1] == '=') {
    840 			if (authlen)
    841 				*authlen = len + 2;
    842 			break;
    843 		}
    844 	}
    845 
    846 	return (i);
    847 }
    848 
    849 /*ARGSUSED*/
    850 ulong_t
    851 topo_fmri_strhash_internal(topo_hdl_t *thp, const char *fmri, boolean_t noauth)
    852 {
    853 	const char *auth, *next;
    854 	const char *enclosure;
    855 	ulong_t h;
    856 	hc_auth_type_t type;
    857 
    858 	if (strncmp(fmri, "hc://", 5) != 0)
    859 		return (topo_fmri_strhash_one(fmri, strlen(fmri)));
    860 
    861 	enclosure = strstr(fmri, SES_ENCLOSURE);
    862 
    863 	h = 0;
    864 
    865 	auth = next = fmri + 5;
    866 	while (*next != '/') {
    867 		auth = next;
    868 
    869 		if ((next = topo_fmri_next_auth(auth)) == NULL) {
    870 			next = auth;
    871 			break;
    872 		}
    873 
    874 		if ((type = hc_auth_to_type(auth, NULL)) == HC_AUTH_MAX)
    875 			continue;
    876 
    877 		if (!noauth || type == HC_AUTH_CHASSIS)
    878 			h += topo_fmri_strhash_one(auth, next - auth);
    879 	}
    880 
    881 	if (enclosure) {
    882 		next = enclosure + sizeof (SES_ENCLOSURE);
    883 		while (isdigit(*next))
    884 			next++;
    885 	}
    886 
    887 	h += topo_fmri_strhash_one(next, strlen(next));
    888 
    889 	return (h);
    890 }
    891 
    892 /*ARGSUSED*/
    893 ulong_t
    894 topo_fmri_strhash(topo_hdl_t *thp, const char *fmri)
    895 {
    896 	return (topo_fmri_strhash_internal(thp, fmri, B_FALSE));
    897 }
    898 
    899 /*ARGSUSED*/
    900 ulong_t
    901 topo_fmri_strhash_noauth(topo_hdl_t *thp, const char *fmri)
    902 {
    903 	return (topo_fmri_strhash_internal(thp, fmri, B_TRUE));
    904 }
    905 
    906 
    907 static void
    908 topo_fmri_strcmp_parse_auth(const char *auth, const char *authtype[],
    909     size_t authlen[])
    910 {
    911 	int i;
    912 	const char *next;
    913 	hc_auth_type_t type;
    914 	size_t len;
    915 
    916 	for (i = 0; i < HC_AUTH_MAX; i++)
    917 		authlen[i] = 0;
    918 
    919 	while (*auth != '/' &&
    920 	    (next = topo_fmri_next_auth(auth)) != NULL) {
    921 		if ((type = hc_auth_to_type(auth, &len)) == HC_AUTH_MAX) {
    922 			auth = next;
    923 			continue;
    924 		}
    925 
    926 		authtype[type] = auth + len;
    927 		authlen[type] = next - (auth + len);
    928 		auth = next;
    929 	}
    930 }
    931 
    932 /*ARGSUSED*/
    933 static boolean_t
    934 topo_fmri_strcmp_internal(topo_hdl_t *thp, const char *a, const char *b,
    935     boolean_t noauth)
    936 {
    937 	const char *fmria, *fmrib;
    938 	const char *autha[HC_AUTH_MAX], *authb[HC_AUTH_MAX];
    939 	size_t authlena[HC_AUTH_MAX], authlenb[HC_AUTH_MAX];
    940 	int i;
    941 
    942 	/*
    943 	 * For non-hc FMRIs, we don't do anything.
    944 	 */
    945 	if (strncmp(a, "hc://", 5) != 0 ||
    946 	    strncmp(b, "hc://", 5) != 0)
    947 		return (strcmp(a, b) == 0);
    948 
    949 	/*
    950 	 * Get the portion of the FMRI independent of the authority
    951 	 * information.
    952 	 */
    953 	fmria = strchr(a + 5, '/');
    954 	fmrib = strchr(b + 5, '/');
    955 	if (fmria == NULL || fmrib == NULL)
    956 		return (strcmp(a, b));
    957 	fmria++;
    958 	fmrib++;
    959 
    960 	/*
    961 	 * Comparing fmri authority information is a bit of a pain, because
    962 	 * there may be a different number of members, and they can (but
    963 	 * shouldn't be) in a different order.  We need to create a copy of the
    964 	 * authority and parse it into pieces.  Because this function is
    965 	 * intended to be fast (and not necessarily extensible), we hard-code
    966 	 * the list of possible authority members in an enum and parse it into
    967 	 * an array.
    968 	 */
    969 	topo_fmri_strcmp_parse_auth(a + 5, autha, authlena);
    970 	topo_fmri_strcmp_parse_auth(b + 5, authb, authlenb);
    971 
    972 	for (i = 0; i < HC_AUTH_MAX; i++) {
    973 		if (noauth && i != HC_AUTH_CHASSIS)
    974 			continue;
    975 
    976 		if (authlena[i] == 0 && authlenb[i] == 0)
    977 			continue;
    978 
    979 		if (authlena[i] != authlenb[i])
    980 			return (B_FALSE);
    981 
    982 		if (strncmp(autha[i], authb[i], authlena[i]) != 0)
    983 			return (B_FALSE);
    984 	}
    985 
    986 	/*
    987 	 * If this is rooted at a ses-enclosure node, skip past the instance
    988 	 * number, as it has no meaning.
    989 	 */
    990 	if (strncmp(fmria, SES_ENCLOSURE, sizeof (SES_ENCLOSURE) - 1) == 0 &&
    991 	    strncmp(fmrib, SES_ENCLOSURE, sizeof (SES_ENCLOSURE) - 1) == 0) {
    992 		fmria += sizeof (SES_ENCLOSURE);
    993 		fmrib += sizeof (SES_ENCLOSURE);
    994 
    995 		while (isdigit(*fmria))
    996 			fmria++;
    997 		while (isdigit(*fmrib))
    998 			fmrib++;
    999 	}
   1000 
   1001 	return (strcmp(fmria, fmrib) == 0);
   1002 }
   1003 
   1004 /*ARGSUSED*/
   1005 boolean_t
   1006 topo_fmri_strcmp(topo_hdl_t *thp, const char *a, const char *b)
   1007 {
   1008 	return (topo_fmri_strcmp_internal(thp, a, b, B_FALSE));
   1009 }
   1010 
   1011 /*ARGSUSED*/
   1012 boolean_t
   1013 topo_fmri_strcmp_noauth(topo_hdl_t *thp, const char *a, const char *b)
   1014 {
   1015 	return (topo_fmri_strcmp_internal(thp, a, b, B_TRUE));
   1016 }
   1017 
   1018 int
   1019 topo_fmri_facility(topo_hdl_t *thp, nvlist_t *rsrc, const char *fac_type,
   1020     uint32_t fac_subtype, topo_walk_cb_t cb, void *cb_args, int *err)
   1021 {
   1022 	int rv;
   1023 	nvlist_t *in = NULL, *out;
   1024 	tnode_t *rnode;
   1025 	char *scheme;
   1026 
   1027 	if (nvlist_lookup_string(rsrc, FM_FMRI_SCHEME, &scheme) != 0)
   1028 		return (set_error(thp, ETOPO_FMRI_MALFORM, err,
   1029 		    TOPO_METH_PROP_GET, in));
   1030 
   1031 	if ((rnode = topo_hdl_root(thp, scheme)) == NULL)
   1032 		return (set_error(thp, ETOPO_METHOD_NOTSUP, err,
   1033 		    TOPO_METH_PROP_GET, in));
   1034 
   1035 	if (topo_hdl_nvalloc(thp, &in, NV_UNIQUE_NAME) != 0)
   1036 		return (set_error(thp, ETOPO_FMRI_NVL, err,
   1037 		    TOPO_METH_PROP_GET, in));
   1038 
   1039 	rv = nvlist_add_nvlist(in, TOPO_PROP_RESOURCE, rsrc);
   1040 	rv |= nvlist_add_string(in, FM_FMRI_FACILITY_TYPE, fac_type);
   1041 	rv |= nvlist_add_uint32(in, "type", fac_subtype);
   1042 #ifdef _LP64
   1043 	rv |= nvlist_add_uint64(in, "callback", (uint64_t)cb);
   1044 	rv |= nvlist_add_uint64(in, "callback-args", (uint64_t)cb_args);
   1045 #else
   1046 	rv |= nvlist_add_uint32(in, "callback", (uint32_t)cb);
   1047 	rv |= nvlist_add_uint32(in, "callback-args", (uint32_t)cb_args);
   1048 #endif
   1049 	if (rv != 0)
   1050 		return (set_error(thp, ETOPO_FMRI_NVL, err,
   1051 		    TOPO_METH_PROP_GET, in));
   1052 
   1053 	rv = topo_method_invoke(rnode, TOPO_METH_FACILITY,
   1054 	    TOPO_METH_FACILITY_VERSION, in, &out, err);
   1055 
   1056 	nvlist_free(in);
   1057 
   1058 	if (rv != 0)
   1059 		return (-1); /* *err is set for us */
   1060 
   1061 	return (0);
   1062 }
   1063