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 }