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