1 /* 2 * 3 * CDDL HEADER START 4 * 5 * The contents of this file are subject to the terms of the 6 * Common Development and Distribution License (the "License"). 7 * You may not use this file except in compliance with the License. 8 * 9 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 10 * or http://www.opensolaris.org/os/licensing. 11 * See the License for the specific language governing permissions 12 * and limitations under the License. 13 * 14 * When distributing Covered Code, include this CDDL HEADER in each 15 * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 16 * If applicable, add the following below this CDDL HEADER, with the 17 * fields enclosed by brackets "[]" replaced with your own identifying 18 * information: Portions Copyright [yyyy] [name of copyright owner] 19 * 20 * CDDL HEADER END 21 */ 22 23 /* 24 * Copyright (c) 2006, 2010, Oracle and/or its affiliates. All rights reserved. 25 * Copyright 2020 Joyent, Inc. 26 */ 27 28 #include <stdio.h> 29 #include <stdlib.h> 30 #include <string.h> 31 #include <errno.h> 32 #include <ctype.h> 33 #include <alloca.h> 34 #include <assert.h> 35 #include <limits.h> 36 #include <zone.h> 37 #include <fm/topo_mod.h> 38 #include <fm/topo_hc.h> 39 #include <fm/fmd_fmri.h> 40 #include <sys/param.h> 41 #include <sys/systeminfo.h> 42 #include <sys/fm/protocol.h> 43 #include <sys/stat.h> 44 #include <sys/systeminfo.h> 45 #include <sys/utsname.h> 46 47 #include <topo_method.h> 48 #include <topo_module.h> 49 #include <topo_subr.h> 50 #include <topo_prop.h> 51 #include <topo_tree.h> 52 #include <hc.h> 53 54 static int hc_enum(topo_mod_t *, tnode_t *, const char *, topo_instance_t, 55 topo_instance_t, void *, void *); 56 static void hc_release(topo_mod_t *, tnode_t *); 57 static int hc_fmri_nvl2str(topo_mod_t *, tnode_t *, topo_version_t, 58 nvlist_t *, nvlist_t **); 59 static int hc_fmri_str2nvl(topo_mod_t *, tnode_t *, topo_version_t, 60 nvlist_t *, nvlist_t **); 61 static int hc_compare(topo_mod_t *, tnode_t *, topo_version_t, nvlist_t *, 62 nvlist_t **); 63 static int hc_fmri_present(topo_mod_t *, tnode_t *, topo_version_t, nvlist_t *, 64 nvlist_t **); 65 static int hc_fmri_replaced(topo_mod_t *, tnode_t *, topo_version_t, nvlist_t *, 66 nvlist_t **); 67 static int hc_fmri_unusable(topo_mod_t *, tnode_t *, topo_version_t, nvlist_t *, 68 nvlist_t **); 69 static int hc_fmri_expand(topo_mod_t *, tnode_t *, topo_version_t, 70 nvlist_t *, nvlist_t **); 71 static int hc_fmri_retire(topo_mod_t *, tnode_t *, topo_version_t, nvlist_t *, 72 nvlist_t **); 73 static int hc_fmri_unretire(topo_mod_t *, tnode_t *, topo_version_t, nvlist_t *, 74 nvlist_t **); 75 static int hc_fmri_service_state(topo_mod_t *, tnode_t *, topo_version_t, 76 nvlist_t *, nvlist_t **); 77 static int hc_fmri_create_meth(topo_mod_t *, tnode_t *, topo_version_t, 78 nvlist_t *, nvlist_t **); 79 static int hc_fmri_prop_get(topo_mod_t *, tnode_t *, topo_version_t, 80 nvlist_t *, nvlist_t **); 81 static int hc_fmri_prop_set(topo_mod_t *, tnode_t *, topo_version_t, 82 nvlist_t *, nvlist_t **); 83 static int hc_fmri_pgrp_get(topo_mod_t *, tnode_t *, topo_version_t, 84 nvlist_t *, nvlist_t **); 85 static int hc_fmri_facility(topo_mod_t *, tnode_t *, topo_version_t, 86 nvlist_t *, nvlist_t **); 87 88 static nvlist_t *hc_fmri_create(topo_mod_t *, nvlist_t *, int, const char *, 89 topo_instance_t inst, const nvlist_t *, const char *, const char *, 90 const char *); 91 92 const topo_method_t hc_methods[] = { 93 { TOPO_METH_NVL2STR, TOPO_METH_NVL2STR_DESC, TOPO_METH_NVL2STR_VERSION, 94 TOPO_STABILITY_INTERNAL, hc_fmri_nvl2str }, 95 { TOPO_METH_STR2NVL, TOPO_METH_STR2NVL_DESC, TOPO_METH_STR2NVL_VERSION, 96 TOPO_STABILITY_INTERNAL, hc_fmri_str2nvl }, 97 { TOPO_METH_COMPARE, TOPO_METH_COMPARE_DESC, TOPO_METH_COMPARE_VERSION, 98 TOPO_STABILITY_INTERNAL, hc_compare }, 99 { TOPO_METH_PRESENT, TOPO_METH_PRESENT_DESC, TOPO_METH_PRESENT_VERSION, 100 TOPO_STABILITY_INTERNAL, hc_fmri_present }, 101 { TOPO_METH_REPLACED, TOPO_METH_REPLACED_DESC, 102 TOPO_METH_REPLACED_VERSION, TOPO_STABILITY_INTERNAL, 103 hc_fmri_replaced }, 104 { TOPO_METH_UNUSABLE, TOPO_METH_UNUSABLE_DESC, 105 TOPO_METH_UNUSABLE_VERSION, TOPO_STABILITY_INTERNAL, 106 hc_fmri_unusable }, 107 { TOPO_METH_EXPAND, TOPO_METH_EXPAND_DESC, 108 TOPO_METH_EXPAND_VERSION, TOPO_STABILITY_INTERNAL, 109 hc_fmri_expand }, 110 { TOPO_METH_RETIRE, TOPO_METH_RETIRE_DESC, 111 TOPO_METH_RETIRE_VERSION, TOPO_STABILITY_INTERNAL, 112 hc_fmri_retire }, 113 { TOPO_METH_UNRETIRE, TOPO_METH_UNRETIRE_DESC, 114 TOPO_METH_UNRETIRE_VERSION, TOPO_STABILITY_INTERNAL, 115 hc_fmri_unretire }, 116 { TOPO_METH_SERVICE_STATE, TOPO_METH_SERVICE_STATE_DESC, 117 TOPO_METH_SERVICE_STATE_VERSION, TOPO_STABILITY_INTERNAL, 118 hc_fmri_service_state }, 119 { TOPO_METH_FMRI, TOPO_METH_FMRI_DESC, TOPO_METH_FMRI_VERSION, 120 TOPO_STABILITY_INTERNAL, hc_fmri_create_meth }, 121 { TOPO_METH_PROP_GET, TOPO_METH_PROP_GET_DESC, 122 TOPO_METH_PROP_GET_VERSION, TOPO_STABILITY_INTERNAL, 123 hc_fmri_prop_get }, 124 { TOPO_METH_PROP_SET, TOPO_METH_PROP_SET_DESC, 125 TOPO_METH_PROP_SET_VERSION, TOPO_STABILITY_INTERNAL, 126 hc_fmri_prop_set }, 127 { TOPO_METH_PGRP_GET, TOPO_METH_PGRP_GET_DESC, 128 TOPO_METH_PGRP_GET_VERSION, TOPO_STABILITY_INTERNAL, 129 hc_fmri_pgrp_get }, 130 { TOPO_METH_FACILITY, TOPO_METH_FACILITY_DESC, 131 TOPO_METH_FACILITY_VERSION, TOPO_STABILITY_INTERNAL, 132 hc_fmri_facility }, 133 { NULL } 134 }; 135 136 static const topo_method_t fru_container_methods[] = { 137 { TOPO_METH_OCCUPIED, TOPO_METH_OCCUPIED_DESC, 138 TOPO_METH_OCCUPIED_VERSION, TOPO_STABILITY_INTERNAL, 139 topo_mod_hc_occupied }, 140 { NULL } 141 }; 142 143 static const topo_modops_t hc_ops = 144 { hc_enum, hc_release }; 145 static const topo_modinfo_t hc_info = 146 { HC, FM_FMRI_SCHEME_HC, HC_VERSION, &hc_ops }; 147 148 static const hcc_t hc_canon[] = { 149 { BANK, TOPO_STABILITY_PRIVATE }, 150 { BAY, TOPO_STABILITY_PRIVATE }, 151 { BLADE, TOPO_STABILITY_PRIVATE }, 152 { BRANCH, TOPO_STABILITY_PRIVATE }, 153 { CACHE, TOPO_STABILITY_PRIVATE }, 154 { CCD, TOPO_STABILITY_PRIVATE }, 155 { CCX, TOPO_STABILITY_PRIVATE }, 156 { CMP, TOPO_STABILITY_PRIVATE }, 157 { CENTERPLANE, TOPO_STABILITY_PRIVATE }, 158 { CHASSIS, TOPO_STABILITY_PRIVATE }, 159 { CHIP, TOPO_STABILITY_PRIVATE }, 160 { CHIPSET, TOPO_STABILITY_PRIVATE }, 161 { CHIP_SELECT, TOPO_STABILITY_PRIVATE }, 162 { CORE, TOPO_STABILITY_PRIVATE }, 163 { CONTROLLER, TOPO_STABILITY_PRIVATE }, 164 { CPU, TOPO_STABILITY_PRIVATE }, 165 { CPUBOARD, TOPO_STABILITY_PRIVATE }, 166 { DIMM, TOPO_STABILITY_PRIVATE }, 167 { DISK, TOPO_STABILITY_PRIVATE }, 168 { DRAM, TOPO_STABILITY_PRIVATE }, 169 { DRAMCHANNEL, TOPO_STABILITY_PRIVATE }, 170 { FAN, TOPO_STABILITY_PRIVATE }, 171 { FANBOARD, TOPO_STABILITY_PRIVATE }, 172 { FANMODULE, TOPO_STABILITY_PRIVATE }, 173 { HBA, TOPO_STABILITY_PRIVATE }, 174 { HOSTBRIDGE, TOPO_STABILITY_PRIVATE }, 175 { INTERCONNECT, TOPO_STABILITY_PRIVATE }, 176 { IOBOARD, TOPO_STABILITY_PRIVATE }, 177 { IPORT, TOPO_STABILITY_PRIVATE }, 178 { MEMBOARD, TOPO_STABILITY_PRIVATE }, 179 { MEMORYBUFFER, TOPO_STABILITY_PRIVATE }, 180 { MEMORYCONTROL, TOPO_STABILITY_PRIVATE }, 181 { MICROCORE, TOPO_STABILITY_PRIVATE }, 182 { MOTHERBOARD, TOPO_STABILITY_PRIVATE }, 183 { NIU, TOPO_STABILITY_PRIVATE }, 184 { NIUFN, TOPO_STABILITY_PRIVATE }, 185 { NVME, TOPO_STABILITY_PRIVATE }, 186 { PCI_BUS, TOPO_STABILITY_PRIVATE }, 187 { PCI_DEVICE, TOPO_STABILITY_PRIVATE }, 188 { PCI_FUNCTION, TOPO_STABILITY_PRIVATE }, 189 { PCIEX_BUS, TOPO_STABILITY_PRIVATE }, 190 { PCIEX_DEVICE, TOPO_STABILITY_PRIVATE }, 191 { PCIEX_FUNCTION, TOPO_STABILITY_PRIVATE }, 192 { PCIEX_ROOT, TOPO_STABILITY_PRIVATE }, 193 { PCIEX_SWUP, TOPO_STABILITY_PRIVATE }, 194 { PCIEX_SWDWN, TOPO_STABILITY_PRIVATE }, 195 { PORT, TOPO_STABILITY_PRIVATE }, 196 { POWERBOARD, TOPO_STABILITY_PRIVATE }, 197 { POWERMODULE, TOPO_STABILITY_PRIVATE }, 198 { PSU, TOPO_STABILITY_PRIVATE }, 199 { RANK, TOPO_STABILITY_PRIVATE }, 200 { RECEPTACLE, TOPO_STABILITY_PRIVATE }, 201 { RISER, TOPO_STABILITY_PRIVATE }, 202 { SASEXPANDER, TOPO_STABILITY_PRIVATE }, 203 { SCSI_DEVICE, TOPO_STABILITY_PRIVATE }, 204 { SHELF, TOPO_STABILITY_PRIVATE }, 205 { SES_ENCLOSURE, TOPO_STABILITY_PRIVATE }, 206 { SLOT, TOPO_STABILITY_PRIVATE }, 207 { SMP_DEVICE, TOPO_STABILITY_PRIVATE }, 208 { SP, TOPO_STABILITY_PRIVATE }, 209 { STRAND, TOPO_STABILITY_PRIVATE }, 210 { SUBCHASSIS, TOPO_STABILITY_PRIVATE }, 211 { SYSTEMBOARD, TOPO_STABILITY_PRIVATE }, 212 { TRANSCEIVER, TOPO_STABILITY_PRIVATE }, 213 { UFM, TOPO_STABILITY_PRIVATE }, 214 { USB_DEVICE, TOPO_STABILITY_PRIVATE }, 215 { XAUI, TOPO_STABILITY_PRIVATE }, 216 { XFP, TOPO_STABILITY_PRIVATE } 217 }; 218 219 static int hc_ncanon = sizeof (hc_canon) / sizeof (hcc_t); 220 221 int 222 hc_init(topo_mod_t *mod, topo_version_t version) 223 { 224 /* 225 * Turn on module debugging output 226 */ 227 if (getenv("TOPOHCDEBUG")) 228 topo_mod_setdebug(mod); 229 230 topo_mod_dprintf(mod, "initializing hc builtin\n"); 231 232 if (version != HC_VERSION) 233 return (topo_mod_seterrno(mod, EMOD_VER_NEW)); 234 235 if (topo_mod_register(mod, &hc_info, TOPO_VERSION) != 0) { 236 topo_mod_dprintf(mod, "failed to register hc: " 237 "%s\n", topo_mod_errmsg(mod)); 238 return (-1); /* mod errno already set */ 239 } 240 241 return (0); 242 } 243 244 void 245 hc_fini(topo_mod_t *mod) 246 { 247 topo_mod_unregister(mod); 248 } 249 250 /*ARGSUSED*/ 251 int 252 hc_enum(topo_mod_t *mod, tnode_t *pnode, const char *name, topo_instance_t min, 253 topo_instance_t max, void *notused1, void *notused2) 254 { 255 int isglobal = (getzoneid() == GLOBAL_ZONEID); 256 nvlist_t *pfmri = NULL; 257 nvlist_t *nvl; 258 nvlist_t *auth; 259 tnode_t *node; 260 int err; 261 /* 262 * Register root node methods 263 */ 264 if (strcmp(name, HC) == 0) { 265 (void) topo_method_register(mod, pnode, hc_methods); 266 return (0); 267 } 268 if (min != max) { 269 topo_mod_dprintf(mod, 270 "Request to enumerate %s component with an " 271 "ambiguous instance number, min (%" PRIu64 ") != max (%" 272 PRIu64 ").\n", HC, min, max); 273 return (topo_mod_seterrno(mod, EINVAL)); 274 } 275 276 if (!isglobal) 277 return (0); 278 279 (void) topo_node_resource(pnode, &pfmri, &err); 280 auth = topo_mod_auth(mod, pnode); 281 nvl = hc_fmri_create(mod, pfmri, FM_HC_SCHEME_VERSION, name, min, 282 auth, NULL, NULL, NULL); 283 nvlist_free(pfmri); /* callee ignores NULLs */ 284 if (nvl == NULL) { 285 nvlist_free(auth); 286 return (-1); 287 } 288 289 if ((node = topo_node_bind(mod, pnode, name, min, nvl)) == NULL) { 290 topo_mod_dprintf(mod, "topo_node_bind failed: %s\n", 291 topo_strerror(topo_mod_errno(mod))); 292 nvlist_free(auth); 293 nvlist_free(nvl); 294 return (-1); 295 } 296 if (strcmp(name, BAY) == 0 || strcmp(name, PORT) == 0 || 297 strcmp(name, RECEPTACLE) == 0 || strcmp(name, SLOT) == 0) { 298 if (topo_method_register(mod, node, fru_container_methods) < 299 0) { 300 topo_mod_dprintf(mod, "failed to register methods on " 301 "%s=%" PRIu64 "\n", name, min); 302 return (-1); 303 } 304 } 305 306 /* 307 * Set FRU for the motherboard node 308 */ 309 if (strcmp(name, MOTHERBOARD) == 0) 310 (void) topo_node_fru_set(node, nvl, 0, &err); 311 312 topo_pgroup_hcset(node, auth); 313 nvlist_free(nvl); 314 nvlist_free(auth); 315 316 return (0); 317 } 318 319 /*ARGSUSED*/ 320 static void 321 hc_release(topo_mod_t *mp, tnode_t *node) 322 { 323 topo_method_unregister_all(mp, node); 324 } 325 326 static int 327 fmri_compare(topo_mod_t *mod, nvlist_t *nv1, nvlist_t *nv2) 328 { 329 uint8_t v1, v2; 330 nvlist_t **hcp1, **hcp2; 331 nvlist_t *f1 = NULL, *f2 = NULL; 332 int err, i; 333 uint_t nhcp1, nhcp2; 334 char *f1str, *f2str; 335 336 if (nvlist_lookup_uint8(nv1, FM_VERSION, &v1) != 0 || 337 nvlist_lookup_uint8(nv2, FM_VERSION, &v2) != 0 || 338 v1 > FM_HC_SCHEME_VERSION || v2 > FM_HC_SCHEME_VERSION) 339 return (topo_mod_seterrno(mod, EMOD_FMRI_VERSION)); 340 341 err = nvlist_lookup_nvlist_array(nv1, FM_FMRI_HC_LIST, &hcp1, &nhcp1); 342 err |= nvlist_lookup_nvlist_array(nv2, FM_FMRI_HC_LIST, &hcp2, &nhcp2); 343 if (err != 0) 344 return (topo_mod_seterrno(mod, EMOD_FMRI_NVL)); 345 346 if (nhcp1 != nhcp2) 347 return (0); 348 349 for (i = 0; i < nhcp1; i++) { 350 char *nm1 = NULL; 351 char *nm2 = NULL; 352 char *id1 = NULL; 353 char *id2 = NULL; 354 355 (void) nvlist_lookup_string(hcp1[i], FM_FMRI_HC_NAME, &nm1); 356 (void) nvlist_lookup_string(hcp2[i], FM_FMRI_HC_NAME, &nm2); 357 (void) nvlist_lookup_string(hcp1[i], FM_FMRI_HC_ID, &id1); 358 (void) nvlist_lookup_string(hcp2[i], FM_FMRI_HC_ID, &id2); 359 if (nm1 == NULL || nm2 == NULL || id1 == NULL || id2 == NULL) 360 return (topo_mod_seterrno(mod, EMOD_FMRI_NVL)); 361 362 if (strcmp(nm1, nm2) == 0 && strcmp(id1, id2) == 0) 363 continue; 364 365 return (0); 366 } 367 368 /* 369 * Finally, check if the FMRI's represent a facility node. If so, then 370 * verify that the facilty type ("sensor"|"indicator") and facility 371 * name match. 372 */ 373 (void) nvlist_lookup_nvlist(nv1, FM_FMRI_FACILITY, &f1); 374 (void) nvlist_lookup_nvlist(nv2, FM_FMRI_FACILITY, &f2); 375 376 if (f1 == NULL && f2 == NULL) 377 return (1); 378 else if (f1 == NULL || f2 == NULL) 379 return (0); 380 381 if (nvlist_lookup_string(f1, FM_FMRI_FACILITY_NAME, &f1str) == 0 && 382 nvlist_lookup_string(f2, FM_FMRI_FACILITY_NAME, &f2str) == 0 && 383 strcmp(f1str, f2str) == 0 && 384 nvlist_lookup_string(f1, FM_FMRI_FACILITY_TYPE, &f1str) == 0 && 385 nvlist_lookup_string(f2, FM_FMRI_FACILITY_TYPE, &f2str) == 0 && 386 strcmp(f1str, f2str) == 0) { 387 return (1); 388 } 389 return (0); 390 } 391 392 /*ARGSUSED*/ 393 static int 394 hc_compare(topo_mod_t *mod, tnode_t *node, topo_version_t version, 395 nvlist_t *in, nvlist_t **out) 396 { 397 int ret; 398 uint32_t compare; 399 nvlist_t *nv1, *nv2; 400 401 if (version > TOPO_METH_COMPARE_VERSION) 402 return (topo_mod_seterrno(mod, EMOD_VER_NEW)); 403 404 if (nvlist_lookup_nvlist(in, TOPO_METH_FMRI_ARG_NV1, &nv1) != 0 || 405 nvlist_lookup_nvlist(in, TOPO_METH_FMRI_ARG_NV2, &nv2) != 0) 406 return (topo_mod_seterrno(mod, EMOD_METHOD_INVAL)); 407 408 ret = fmri_compare(mod, nv1, nv2); 409 if (ret < 0) 410 return (-1); 411 412 compare = ret; 413 if (topo_mod_nvalloc(mod, out, NV_UNIQUE_NAME) == 0) { 414 if (nvlist_add_uint32(*out, TOPO_METH_COMPARE_RET, 415 compare) == 0) 416 return (0); 417 else 418 nvlist_free(*out); 419 } 420 421 return (-1); 422 } 423 424 static ssize_t 425 fmri_nvl2str(nvlist_t *nvl, char *buf, size_t buflen) 426 { 427 nvlist_t **hcprs = NULL; 428 nvlist_t *hcsp = NULL; 429 nvlist_t *anvl = NULL; 430 nvpair_t *apair; 431 nvlist_t *fnvl; 432 uint8_t version; 433 ssize_t size = 0; 434 uint_t hcnprs; 435 char *serial = NULL; 436 char *part = NULL; 437 char *root = NULL; 438 char *rev = NULL; 439 char *aname, *aval; 440 char *fname = NULL, *ftype = NULL; 441 int err, i; 442 443 if (nvlist_lookup_uint8(nvl, FM_VERSION, &version) != 0 || 444 version > FM_HC_SCHEME_VERSION) 445 return (0); 446 447 /* Get authority, if present */ 448 err = nvlist_lookup_nvlist(nvl, FM_FMRI_AUTHORITY, &anvl); 449 if (err != 0 && err != ENOENT) 450 return (0); 451 452 (void) nvlist_lookup_string(nvl, FM_FMRI_HC_ROOT, &root); 453 454 err = nvlist_lookup_nvlist_array(nvl, FM_FMRI_HC_LIST, &hcprs, &hcnprs); 455 if (err != 0 || hcprs == NULL) 456 return (0); 457 458 (void) nvlist_lookup_string(nvl, FM_FMRI_HC_SERIAL_ID, &serial); 459 (void) nvlist_lookup_string(nvl, FM_FMRI_HC_PART, &part); 460 (void) nvlist_lookup_string(nvl, FM_FMRI_HC_REVISION, &rev); 461 462 /* hc:// */ 463 topo_fmristr_build(&size, buf, buflen, FM_FMRI_SCHEME_HC, NULL, "://"); 464 465 /* authority, if any */ 466 if (anvl != NULL) { 467 for (apair = nvlist_next_nvpair(anvl, NULL); 468 apair != NULL; apair = nvlist_next_nvpair(anvl, apair)) { 469 if (nvpair_type(apair) != DATA_TYPE_STRING || 470 nvpair_value_string(apair, &aval) != 0) 471 continue; 472 aname = nvpair_name(apair); 473 topo_fmristr_build(&size, buf, buflen, ":", NULL, NULL); 474 topo_fmristr_build(&size, buf, buflen, "=", 475 aname, aval); 476 } 477 } 478 479 /* hardware-id part */ 480 topo_fmristr_build(&size, 481 buf, buflen, serial, ":" FM_FMRI_HC_SERIAL_ID "=", NULL); 482 topo_fmristr_build(&size, 483 buf, buflen, part, ":" FM_FMRI_HC_PART "=", NULL); 484 topo_fmristr_build(&size, 485 buf, buflen, rev, ":" FM_FMRI_HC_REVISION "=", NULL); 486 487 /* separating slash */ 488 topo_fmristr_build(&size, buf, buflen, "/", NULL, NULL); 489 490 /* hc-root */ 491 if (root) 492 topo_fmristr_build(&size, buf, buflen, root, NULL, NULL); 493 494 /* all the pairs */ 495 for (i = 0; i < hcnprs; i++) { 496 char *nm = NULL; 497 char *id = NULL; 498 499 if (i > 0) 500 topo_fmristr_build(&size, 501 buf, buflen, "/", NULL, NULL); 502 (void) nvlist_lookup_string(hcprs[i], FM_FMRI_HC_NAME, &nm); 503 (void) nvlist_lookup_string(hcprs[i], FM_FMRI_HC_ID, &id); 504 if (nm == NULL || id == NULL) 505 return (0); 506 topo_fmristr_build(&size, buf, buflen, nm, NULL, "="); 507 topo_fmristr_build(&size, buf, buflen, id, NULL, NULL); 508 } 509 510 /* append offset/physaddr if it exists in hc-specific */ 511 if (nvlist_lookup_nvlist(nvl, FM_FMRI_HC_SPECIFIC, &hcsp) == 0) { 512 char *hcsn = NULL; 513 char hexstr[17]; 514 uint64_t val; 515 516 if (nvlist_lookup_uint64(hcsp, FM_FMRI_HC_SPECIFIC_OFFSET, 517 &val) == 0 || nvlist_lookup_uint64(hcsp, 518 "asru-" FM_FMRI_HC_SPECIFIC_OFFSET, &val) == 0) 519 hcsn = FM_FMRI_HC_SPECIFIC_OFFSET; 520 else if (nvlist_lookup_uint64(hcsp, 521 FM_FMRI_HC_SPECIFIC_PHYSADDR, &val) == 0 || 522 nvlist_lookup_uint64(hcsp, 523 "asru-" FM_FMRI_HC_SPECIFIC_PHYSADDR, &val) == 0) 524 hcsn = FM_FMRI_HC_SPECIFIC_PHYSADDR; 525 526 if (hcsn != NULL) { 527 (void) snprintf(hexstr, sizeof (hexstr), "%llx", val); 528 topo_fmristr_build(&size, buf, buflen, "/", NULL, NULL); 529 topo_fmristr_build(&size, buf, buflen, "=", hcsn, 530 hexstr); 531 } 532 } 533 534 /* 535 * If the nvlist represents a facility node, then we append the 536 * facility type and name to the end of the string representation using 537 * the format below: 538 * 539 * ?<ftype>=<fname> 540 */ 541 if (nvlist_lookup_nvlist(nvl, FM_FMRI_FACILITY, &fnvl) == 0) { 542 if (nvlist_lookup_string(fnvl, FM_FMRI_FACILITY_NAME, 543 &fname) != 0 || nvlist_lookup_string(fnvl, 544 FM_FMRI_FACILITY_TYPE, &ftype) != 0) 545 return (0); 546 topo_fmristr_build(&size, buf, buflen, "?", NULL, NULL); 547 topo_fmristr_build(&size, buf, buflen, "=", ftype, fname); 548 } 549 550 return (size); 551 } 552 553 /*ARGSUSED*/ 554 static int 555 hc_fmri_nvl2str(topo_mod_t *mod, tnode_t *node, topo_version_t version, 556 nvlist_t *nvl, nvlist_t **out) 557 { 558 ssize_t len; 559 char *name = NULL; 560 nvlist_t *fmristr; 561 562 if (version > TOPO_METH_NVL2STR_VERSION) 563 return (topo_mod_seterrno(mod, EMOD_VER_NEW)); 564 565 if ((len = fmri_nvl2str(nvl, NULL, 0)) == 0 || 566 (name = topo_mod_alloc(mod, len + 1)) == NULL || 567 fmri_nvl2str(nvl, name, len + 1) == 0) { 568 if (name != NULL) 569 topo_mod_free(mod, name, len + 1); 570 return (topo_mod_seterrno(mod, EMOD_FMRI_NVL)); 571 } 572 573 if (topo_mod_nvalloc(mod, &fmristr, NV_UNIQUE_NAME) != 0) { 574 topo_mod_free(mod, name, len + 1); 575 return (topo_mod_seterrno(mod, EMOD_FMRI_NVL)); 576 } 577 if (nvlist_add_string(fmristr, "fmri-string", name) != 0) { 578 topo_mod_free(mod, name, len + 1); 579 nvlist_free(fmristr); 580 return (topo_mod_seterrno(mod, EMOD_FMRI_NVL)); 581 } 582 topo_mod_free(mod, name, len + 1); 583 *out = fmristr; 584 585 return (0); 586 } 587 588 static nvlist_t * 589 hc_base_fmri_create(topo_mod_t *mod, const nvlist_t *auth, const char *part, 590 const char *rev, const char *serial) 591 { 592 nvlist_t *fmri; 593 int err = 0; 594 595 /* 596 * Create base HC nvlist 597 */ 598 if (topo_mod_nvalloc(mod, &fmri, NV_UNIQUE_NAME) != 0) 599 return (NULL); 600 601 err = nvlist_add_uint8(fmri, FM_VERSION, FM_HC_SCHEME_VERSION); 602 err |= nvlist_add_string(fmri, FM_FMRI_SCHEME, FM_FMRI_SCHEME_HC); 603 err |= nvlist_add_string(fmri, FM_FMRI_HC_ROOT, ""); 604 if (err != 0) { 605 nvlist_free(fmri); 606 return (NULL); 607 } 608 609 /* 610 * Add optional payload members 611 */ 612 if (serial != NULL) 613 (void) nvlist_add_string(fmri, FM_FMRI_HC_SERIAL_ID, serial); 614 if (part != NULL) 615 (void) nvlist_add_string(fmri, FM_FMRI_HC_PART, part); 616 if (rev != NULL) 617 (void) nvlist_add_string(fmri, FM_FMRI_HC_REVISION, rev); 618 if (auth != NULL) 619 (void) nvlist_add_nvlist(fmri, FM_FMRI_AUTHORITY, 620 (nvlist_t *)auth); 621 622 return (fmri); 623 } 624 625 static nvlist_t ** 626 make_hc_pairs(topo_mod_t *mod, char *fmri, int *num) 627 { 628 nvlist_t **pa; 629 char *hc, *fromstr; 630 char *starti, *startn, *endi, *endi2; 631 char *ne, *ns; 632 char *cname = NULL; 633 char *find; 634 char *cid = NULL; 635 int nslashes = 0; 636 int npairs = 0; 637 int i, hclen; 638 639 if ((hc = topo_mod_strdup(mod, fmri + 5)) == NULL) 640 return (NULL); 641 642 hclen = strlen(hc) + 1; 643 644 /* 645 * Count equal signs and slashes to determine how many 646 * hc-pairs will be present in the final FMRI. There should 647 * be at least as many slashes as equal signs. There can be 648 * more, though if the string after an = includes them. 649 */ 650 if ((fromstr = strchr(hc, '/')) == NULL) 651 return (NULL); 652 653 find = fromstr; 654 while ((ne = strchr(find, '=')) != NULL) { 655 find = ne + 1; 656 npairs++; 657 } 658 659 find = fromstr; 660 while ((ns = strchr(find, '/')) != NULL) { 661 find = ns + 1; 662 nslashes++; 663 } 664 665 /* 666 * Do we appear to have a well-formed string version of the FMRI? 667 */ 668 if (nslashes < npairs || npairs == 0) { 669 topo_mod_free(mod, hc, hclen); 670 return (NULL); 671 } 672 673 *num = npairs; 674 675 find = fromstr; 676 677 if ((pa = topo_mod_zalloc(mod, npairs * sizeof (nvlist_t *))) == NULL) { 678 topo_mod_free(mod, hc, hclen); 679 return (NULL); 680 } 681 682 /* 683 * We go through a pretty complicated procedure to find the 684 * name and id for each pair. That's because, unfortunately, 685 * we have some ids that can have slashes within them. So 686 * we can't just search for the next slash after the equal sign 687 * and decide that starts a new pair. Instead we have to find 688 * an equal sign for the next pair and work our way back to the 689 * slash from there. 690 */ 691 for (i = 0; i < npairs; i++) { 692 startn = strchr(find, '/'); 693 if (startn == NULL) 694 break; 695 startn++; 696 starti = strchr(find, '='); 697 if (starti == NULL) 698 break; 699 *starti = '\0'; 700 if ((cname = topo_mod_strdup(mod, startn)) == NULL) 701 break; 702 *starti++ = '='; 703 endi = strchr(starti, '='); 704 if (endi != NULL) { 705 *endi = '\0'; 706 endi2 = strrchr(starti, '/'); 707 if (endi2 == NULL) 708 break; 709 *endi = '='; 710 *endi2 = '\0'; 711 if ((cid = topo_mod_strdup(mod, starti)) == NULL) 712 break; 713 *endi2 = '/'; 714 find = endi2; 715 } else { 716 if ((cid = topo_mod_strdup(mod, starti)) == NULL) 717 break; 718 find = starti + strlen(starti); 719 } 720 if (topo_mod_nvalloc(mod, &pa[i], NV_UNIQUE_NAME) < 0) 721 break; 722 723 if (nvlist_add_string(pa[i], FM_FMRI_HC_NAME, cname) || 724 nvlist_add_string(pa[i], FM_FMRI_HC_ID, cid)) 725 break; 726 727 topo_mod_strfree(mod, cname); 728 topo_mod_strfree(mod, cid); 729 cname = NULL; 730 cid = NULL; 731 } 732 733 topo_mod_strfree(mod, cname); 734 topo_mod_strfree(mod, cid); 735 736 if (i < npairs) { 737 for (i = 0; i < npairs; i++) 738 nvlist_free(pa[i]); 739 topo_mod_free(mod, pa, npairs * sizeof (nvlist_t *)); 740 topo_mod_free(mod, hc, hclen); 741 return (NULL); 742 } 743 744 topo_mod_free(mod, hc, hclen); 745 746 return (pa); 747 } 748 749 int 750 make_hc_auth(topo_mod_t *mod, char *fmri, char **serial, char **part, 751 char **rev, nvlist_t **auth) 752 { 753 char *starti, *startn, *endi, *copy; 754 char *aname = NULL, *aid = NULL, *fs; 755 nvlist_t *na = NULL; 756 size_t len; 757 758 if ((copy = topo_mod_strdup(mod, fmri + 5)) == NULL) 759 return (-1); 760 761 len = strlen(copy); 762 763 /* 764 * Make sure there are a valid authority members 765 */ 766 startn = strchr(copy, ':'); 767 fs = strchr(copy, '/'); 768 769 if (startn == NULL || fs == NULL) { 770 topo_mod_strfree(mod, copy); 771 return (0); 772 } 773 774 /* 775 * The first colon we encounter must occur before the 776 * first slash 777 */ 778 if (startn > fs) 779 goto hcabail; 780 781 do { 782 if (++startn >= copy + len) 783 break; 784 785 if ((starti = strchr(startn, '=')) == NULL) 786 goto hcabail; 787 788 *starti = '\0'; 789 if (++starti > copy + len) 790 goto hcabail; 791 792 if ((aname = topo_mod_strdup(mod, startn)) == NULL) 793 goto hcabail; 794 795 startn = endi = strchr(starti, ':'); 796 if (endi == NULL) 797 if ((endi = strchr(starti, '/')) == NULL) 798 break; 799 800 *endi = '\0'; 801 if ((aid = topo_mod_strdup(mod, starti)) == NULL) 802 goto hcabail; 803 804 /* 805 * Return possible serial, part and revision 806 */ 807 if (strcmp(aname, FM_FMRI_HC_SERIAL_ID) == 0) { 808 *serial = topo_mod_strdup(mod, aid); 809 } else if (strcmp(aname, FM_FMRI_HC_PART) == 0) { 810 *part = topo_mod_strdup(mod, aid); 811 } else if (strcmp(aname, FM_FMRI_HC_REVISION) == 0) { 812 *rev = topo_mod_strdup(mod, aid); 813 } else { 814 if (na == NULL) { 815 if (topo_mod_nvalloc(mod, &na, 816 NV_UNIQUE_NAME) == 0) { 817 (void) nvlist_add_string(na, aname, 818 aid); 819 } 820 } else { 821 (void) nvlist_add_string(na, aname, aid); 822 } 823 } 824 topo_mod_strfree(mod, aname); 825 topo_mod_strfree(mod, aid); 826 aname = aid = NULL; 827 828 } while (startn != NULL); 829 830 *auth = na; 831 832 topo_mod_free(mod, copy, len + 1); 833 return (0); 834 835 hcabail: 836 topo_mod_free(mod, copy, len + 1); 837 topo_mod_strfree(mod, aname); 838 topo_mod_strfree(mod, aid); 839 nvlist_free(na); 840 return (-1); 841 } 842 843 844 /* 845 * This function creates an nvlist to represent the facility portion of an 846 * hc-scheme node, given a string representation of the fmri. This is called by 847 * hc_fmri_str2nvl. If the string does not contain a facility component 848 * (e.g. ?<ftype>=<fname>) then it bails early and returns 0. 849 * 850 * On failure it returns -1 and sets the topo mod errno 851 */ 852 int 853 make_facility(topo_mod_t *mod, char *str, nvlist_t **nvl) 854 { 855 char *fac, *copy, *fname, *ftype; 856 nvlist_t *nf = NULL; 857 size_t len; 858 859 if ((fac = strchr(str, '?')) == NULL) 860 return (0); 861 862 ++fac; 863 if ((copy = topo_mod_strdup(mod, fac)) == NULL) 864 return (topo_mod_seterrno(mod, EMOD_NOMEM)); 865 866 fac = copy; 867 len = strlen(fac); 868 869 if ((fname = strchr(fac, '=')) == NULL) { 870 topo_mod_free(mod, copy, len + 1); 871 return (topo_mod_seterrno(mod, EMOD_FMRI_MALFORM)); 872 } 873 874 fname[0] = '\0'; 875 ++fname; 876 ftype = fac; 877 878 if (topo_mod_nvalloc(mod, &nf, NV_UNIQUE_NAME) != 0) { 879 topo_mod_free(mod, copy, len + 1); 880 return (topo_mod_seterrno(mod, EMOD_NOMEM)); 881 } 882 883 if (nvlist_add_string(nf, FM_FMRI_FACILITY_NAME, fname) != 0 || 884 nvlist_add_string(nf, FM_FMRI_FACILITY_TYPE, ftype) != 0) { 885 topo_mod_free(mod, copy, len + 1); 886 return (topo_mod_seterrno(mod, EMOD_FMRI_NVL)); 887 } 888 889 topo_mod_free(mod, copy, len + 1); 890 891 *nvl = nf; 892 893 return (0); 894 } 895 896 /*ARGSUSED*/ 897 static int 898 hc_fmri_str2nvl(topo_mod_t *mod, tnode_t *node, topo_version_t version, 899 nvlist_t *in, nvlist_t **out) 900 { 901 nvlist_t **pa = NULL; 902 nvlist_t *nf = NULL; 903 nvlist_t *auth = NULL; 904 nvlist_t *fac = NULL; 905 char *str; 906 char *serial = NULL, *part = NULL, *rev = NULL, *hcsn = NULL; 907 int npairs, n; 908 int i, e; 909 910 if (version > TOPO_METH_STR2NVL_VERSION) 911 return (topo_mod_seterrno(mod, EMOD_VER_NEW)); 912 913 if (nvlist_lookup_string(in, "fmri-string", &str) != 0) 914 return (topo_mod_seterrno(mod, EMOD_METHOD_INVAL)); 915 916 /* We're expecting a string version of an hc scheme FMRI */ 917 if (strncmp(str, "hc://", 5) != 0) 918 return (topo_mod_seterrno(mod, EMOD_FMRI_MALFORM)); 919 920 if ((pa = make_hc_pairs(mod, str, &npairs)) == NULL) 921 return (topo_mod_seterrno(mod, EMOD_FMRI_MALFORM)); 922 923 if (make_hc_auth(mod, str, &serial, &part, &rev, &auth) < 0) 924 goto hcfmbail; 925 926 if ((nf = hc_base_fmri_create(mod, auth, part, rev, serial)) == NULL) 927 goto hcfmbail; 928 929 n = npairs; 930 931 /* 932 * If the last pair in hc-list is offset or physaddr, we move 933 * it to hc-specific. 934 */ 935 (void) nvlist_lookup_string(pa[npairs - 1], FM_FMRI_HC_NAME, &hcsn); 936 if (strcmp(hcsn, FM_FMRI_HC_SPECIFIC_OFFSET) == 0 || 937 strcmp(hcsn, FM_FMRI_HC_SPECIFIC_PHYSADDR) == 0) { 938 char *hcid; 939 nvlist_t *hcsp; 940 uint64_t val; 941 942 (void) nvlist_lookup_string(pa[npairs - 1], FM_FMRI_HC_ID, 943 &hcid); 944 val = strtoull(hcid, NULL, 16); 945 if (topo_mod_nvalloc(mod, &hcsp, NV_UNIQUE_NAME) != 0) 946 goto hcfmbail; 947 if (nvlist_add_uint64(hcsp, hcsn, val) != 0 || 948 nvlist_add_nvlist(nf, FM_FMRI_HC_SPECIFIC, hcsp) != 0) { 949 nvlist_free(hcsp); 950 goto hcfmbail; 951 } 952 953 nvlist_free(hcsp); 954 n--; 955 } 956 957 if ((e = nvlist_add_uint32(nf, FM_FMRI_HC_LIST_SZ, n)) == 0) 958 e = nvlist_add_nvlist_array(nf, FM_FMRI_HC_LIST, pa, n); 959 if (e != 0) { 960 topo_mod_dprintf(mod, "construction of new hc nvl failed"); 961 goto hcfmbail; 962 } 963 964 /* 965 * Clean-up 966 */ 967 for (i = 0; i < npairs; i++) 968 nvlist_free(pa[i]); 969 topo_mod_free(mod, pa, npairs * sizeof (nvlist_t *)); 970 topo_mod_strfree(mod, serial); 971 topo_mod_strfree(mod, part); 972 topo_mod_strfree(mod, rev); 973 nvlist_free(auth); 974 975 if (make_facility(mod, str, &fac) == -1) 976 goto hcfmbail; 977 978 if (fac != NULL) { 979 if (nvlist_add_nvlist(nf, FM_FMRI_FACILITY, fac) != 0) 980 goto hcfmbail; 981 } 982 983 *out = nf; 984 985 return (0); 986 987 hcfmbail: 988 nvlist_free(nf); 989 for (i = 0; i < npairs; i++) 990 nvlist_free(pa[i]); 991 topo_mod_free(mod, pa, npairs * sizeof (nvlist_t *)); 992 993 topo_mod_strfree(mod, serial); 994 topo_mod_strfree(mod, part); 995 topo_mod_strfree(mod, rev); 996 nvlist_free(auth); 997 nvlist_free(nf); 998 return (topo_mod_seterrno(mod, EMOD_FMRI_MALFORM)); 999 } 1000 1001 static nvlist_t * 1002 hc_list_create(topo_mod_t *mod, const char *name, char *inst) 1003 { 1004 int err; 1005 nvlist_t *hc; 1006 1007 if (topo_mod_nvalloc(mod, &hc, NV_UNIQUE_NAME) != 0) 1008 return (NULL); 1009 1010 err = nvlist_add_string(hc, FM_FMRI_HC_NAME, name); 1011 err |= nvlist_add_string(hc, FM_FMRI_HC_ID, inst); 1012 if (err != 0) { 1013 nvlist_free(hc); 1014 return (NULL); 1015 } 1016 1017 return (hc); 1018 } 1019 1020 static nvlist_t * 1021 hc_create_seterror(topo_mod_t *mod, nvlist_t **hcl, int n, nvlist_t *fmri, 1022 int err) 1023 { 1024 int i; 1025 1026 if (hcl != NULL) { 1027 for (i = 0; i < n + 1; ++i) 1028 nvlist_free(hcl[i]); 1029 1030 topo_mod_free(mod, hcl, sizeof (nvlist_t *) * (n + 1)); 1031 } 1032 1033 nvlist_free(fmri); 1034 1035 (void) topo_mod_seterrno(mod, err); 1036 1037 topo_mod_dprintf(mod, "unable to create hc FMRI: %s\n", 1038 topo_mod_errmsg(mod)); 1039 1040 return (NULL); 1041 } 1042 1043 static int 1044 hc_name_canonical(topo_mod_t *mod, const char *name) 1045 { 1046 int i; 1047 1048 if (getenv("NOHCCHECK") != NULL) 1049 return (1); 1050 1051 /* 1052 * Only enumerate elements with correct canonical names 1053 */ 1054 for (i = 0; i < hc_ncanon; i++) { 1055 if (strcmp(name, hc_canon[i].hcc_name) == 0) 1056 break; 1057 } 1058 if (i >= hc_ncanon) { 1059 topo_mod_dprintf(mod, "non-canonical name %s\n", 1060 name); 1061 return (0); 1062 } else { 1063 return (1); 1064 } 1065 } 1066 1067 static nvlist_t * 1068 hc_fmri_create(topo_mod_t *mod, nvlist_t *pfmri, int version, const char *name, 1069 topo_instance_t inst, const nvlist_t *auth, const char *part, 1070 const char *rev, const char *serial) 1071 { 1072 int i; 1073 char str[21]; /* sizeof (UINT64_MAX) + '\0' */ 1074 uint_t pelems = 0; 1075 nvlist_t **phcl = NULL; 1076 nvlist_t **hcl = NULL; 1077 nvlist_t *fmri = NULL; 1078 1079 if (version > FM_HC_SCHEME_VERSION) 1080 return (hc_create_seterror(mod, 1081 hcl, pelems, fmri, EMOD_VER_OLD)); 1082 else if (version < FM_HC_SCHEME_VERSION) 1083 return (hc_create_seterror(mod, 1084 hcl, pelems, fmri, EMOD_VER_NEW)); 1085 1086 /* 1087 * Check that the requested name is in our canonical list 1088 */ 1089 if (hc_name_canonical(mod, name) == 0) 1090 return (hc_create_seterror(mod, 1091 hcl, pelems, fmri, EMOD_NONCANON)); 1092 /* 1093 * Copy the parent's HC_LIST 1094 */ 1095 if (pfmri != NULL) { 1096 if (nvlist_lookup_nvlist_array(pfmri, FM_FMRI_HC_LIST, 1097 &phcl, &pelems) != 0) 1098 return (hc_create_seterror(mod, 1099 hcl, pelems, fmri, EMOD_FMRI_MALFORM)); 1100 } 1101 1102 hcl = topo_mod_zalloc(mod, sizeof (nvlist_t *) * (pelems + 1)); 1103 if (hcl == NULL) 1104 return (hc_create_seterror(mod, hcl, pelems, fmri, 1105 EMOD_NOMEM)); 1106 1107 for (i = 0; i < pelems; ++i) 1108 if (topo_mod_nvdup(mod, phcl[i], &hcl[i]) != 0) 1109 return (hc_create_seterror(mod, 1110 hcl, pelems, fmri, EMOD_FMRI_NVL)); 1111 1112 (void) snprintf(str, sizeof (str), "%d", inst); 1113 if ((hcl[i] = hc_list_create(mod, name, str)) == NULL) 1114 return (hc_create_seterror(mod, 1115 hcl, pelems, fmri, EMOD_FMRI_NVL)); 1116 1117 if ((fmri = hc_base_fmri_create(mod, auth, part, rev, serial)) == NULL) 1118 return (hc_create_seterror(mod, 1119 hcl, pelems, fmri, EMOD_FMRI_NVL)); 1120 1121 if (nvlist_add_nvlist_array(fmri, FM_FMRI_HC_LIST, hcl, pelems + 1) 1122 != 0) 1123 return (hc_create_seterror(mod, 1124 hcl, pelems, fmri, EMOD_FMRI_NVL)); 1125 1126 if (hcl != NULL) { 1127 for (i = 0; i < pelems + 1; ++i) { 1128 nvlist_free(hcl[i]); 1129 } 1130 topo_mod_free(mod, hcl, sizeof (nvlist_t *) * (pelems + 1)); 1131 } 1132 1133 return (fmri); 1134 } 1135 1136 /*ARGSUSED*/ 1137 static int 1138 hc_fmri_create_meth(topo_mod_t *mod, tnode_t *node, topo_version_t version, 1139 nvlist_t *in, nvlist_t **out) 1140 { 1141 int ret; 1142 nvlist_t *args, *pfmri = NULL; 1143 nvlist_t *auth; 1144 uint64_t inst; 1145 char *name, *serial, *rev, *part; 1146 1147 if (version > TOPO_METH_FMRI_VERSION) 1148 return (topo_mod_seterrno(mod, EMOD_VER_NEW)); 1149 1150 /* First the must-have fields */ 1151 if (nvlist_lookup_string(in, TOPO_METH_FMRI_ARG_NAME, &name) != 0) 1152 return (topo_mod_seterrno(mod, EMOD_METHOD_INVAL)); 1153 if (nvlist_lookup_uint64(in, TOPO_METH_FMRI_ARG_INST, &inst) != 0) 1154 return (topo_mod_seterrno(mod, EMOD_METHOD_INVAL)); 1155 1156 /* 1157 * args is optional 1158 */ 1159 pfmri = NULL; 1160 auth = NULL; 1161 serial = rev = part = NULL; 1162 if ((ret = nvlist_lookup_nvlist(in, TOPO_METH_FMRI_ARG_NVL, &args)) 1163 != 0) { 1164 if (ret != ENOENT) 1165 return (topo_mod_seterrno(mod, EMOD_METHOD_INVAL)); 1166 } else { 1167 1168 /* And then optional arguments */ 1169 (void) nvlist_lookup_nvlist(args, TOPO_METH_FMRI_ARG_PARENT, 1170 &pfmri); 1171 (void) nvlist_lookup_nvlist(args, TOPO_METH_FMRI_ARG_AUTH, 1172 &auth); 1173 (void) nvlist_lookup_string(args, TOPO_METH_FMRI_ARG_PART, 1174 &part); 1175 (void) nvlist_lookup_string(args, TOPO_METH_FMRI_ARG_REV, &rev); 1176 (void) nvlist_lookup_string(args, TOPO_METH_FMRI_ARG_SER, 1177 &serial); 1178 } 1179 1180 *out = hc_fmri_create(mod, pfmri, version, name, inst, auth, part, 1181 rev, serial); 1182 if (*out == NULL) 1183 return (-1); 1184 return (0); 1185 } 1186 1187 struct hc_walk { 1188 topo_mod_walk_cb_t hcw_cb; 1189 void *hcw_priv; 1190 topo_walk_t *hcw_wp; 1191 nvlist_t **hcw_list; 1192 nvlist_t *hcw_fmri; 1193 nvlist_t *hcw_fac; 1194 uint_t hcw_index; 1195 uint_t hcw_end; 1196 }; 1197 1198 /* 1199 * Returns true if the given node is beneath the specified FMRI. This uses 1200 * the TOPO_METH_CONTAINS method, because some enumerators (such as external 1201 * enclosures) may want to do a comparison based on chassis WWN instead of the 1202 * instance ID. If this comparison function fails or is not supported, then we 1203 * fall back to a direct name/instance comparison. 1204 */ 1205 static int 1206 hc_match(topo_mod_t *mod, tnode_t *node, nvlist_t *fmri, const char *name, 1207 topo_instance_t inst, boolean_t *result) 1208 { 1209 nvlist_t *rsrc; 1210 nvlist_t *arg, *nvl; 1211 uint32_t match = 0; 1212 int err; 1213 1214 if (topo_node_resource(node, &rsrc, &err) != 0) 1215 return (-1); 1216 1217 if (topo_mod_nvalloc(mod, &arg, NV_UNIQUE_NAME) != 0 || 1218 nvlist_add_nvlist(arg, TOPO_METH_FMRI_ARG_FMRI, 1219 rsrc) != 0 || 1220 nvlist_add_nvlist(arg, TOPO_METH_FMRI_ARG_SUBFMRI, 1221 fmri) != 0) { 1222 nvlist_free(rsrc); 1223 (void) topo_mod_seterrno(mod, EMOD_NOMEM); 1224 return (-1); 1225 } 1226 1227 nvlist_free(rsrc); 1228 1229 if (topo_method_invoke(node, TOPO_METH_CONTAINS, 1230 TOPO_METH_CONTAINS_VERSION, arg, &nvl, &err) != 0) { 1231 nvlist_free(arg); 1232 if (err == ETOPO_METHOD_NOTSUP) { 1233 match = (strcmp(name, 1234 topo_node_name(node)) == 0 && 1235 inst == topo_node_instance(node)); 1236 } else { 1237 return (-1); 1238 } 1239 } else { 1240 nvlist_free(arg); 1241 if (nvlist_lookup_uint32(nvl, TOPO_METH_CONTAINS_RET, 1242 &match) != 0) { 1243 nvlist_free(nvl); 1244 (void) topo_mod_seterrno(mod, EMOD_NVL_INVAL); 1245 return (-1); 1246 } 1247 nvlist_free(nvl); 1248 } 1249 1250 *result = (match != 0); 1251 return (0); 1252 } 1253 1254 /* 1255 * Ideally, we should just be able to call topo_walk_bysibling(). But that 1256 * code assumes that the name/instance pair will match, so we need to 1257 * explicitly iterate over children of the parent looking for a matching value. 1258 */ 1259 static int 1260 hc_walk_sibling(topo_mod_t *mod, tnode_t *node, struct hc_walk *hwp, 1261 const char *name, topo_instance_t inst) 1262 { 1263 tnode_t *pnp = topo_node_parent(node); 1264 topo_walk_t *wp = hwp->hcw_wp; 1265 tnode_t *np; 1266 boolean_t matched; 1267 int status; 1268 1269 for (np = topo_child_first(pnp); np != NULL; 1270 np = topo_child_next(pnp, np)) { 1271 topo_node_hold(np); 1272 if (hc_match(mod, np, hwp->hcw_fmri, name, inst, 1273 &matched) == 0 && matched) { 1274 wp->tw_node = np; 1275 if (wp->tw_mod != NULL) 1276 status = wp->tw_cb(mod, np, hwp); 1277 else 1278 status = wp->tw_cb(wp->tw_thp, np, hwp); 1279 topo_node_rele(np); 1280 wp->tw_node = node; 1281 return (status); 1282 } 1283 1284 topo_node_rele(np); 1285 } 1286 1287 return (TOPO_WALK_TERMINATE); 1288 } 1289 1290 /* 1291 * Generic walker for the hc-scheme topo tree. This function uses the 1292 * hierachical nature of the hc-scheme to efficiently step through 1293 * the topo hc tree. Node lookups are done by topo_walk_byid() and 1294 * topo_walk_bysibling() at each component level to avoid unnecessary 1295 * traversal of the tree. hc_walker() never returns TOPO_WALK_NEXT, so 1296 * whether TOPO_WALK_CHILD or TOPO_WALK_SIBLING is specified by 1297 * topo_walk_step() doesn't affect the traversal. 1298 */ 1299 static int 1300 hc_walker(topo_mod_t *mod, tnode_t *node, void *pdata) 1301 { 1302 int i, err; 1303 struct hc_walk *hwp = (struct hc_walk *)pdata; 1304 char *name, *id; 1305 char *fname, *ftype; 1306 topo_instance_t inst; 1307 boolean_t match; 1308 1309 i = hwp->hcw_index; 1310 if (i > hwp->hcw_end) { 1311 if (hwp->hcw_fac != NULL) { 1312 if ((err = hwp->hcw_cb(mod, node, hwp->hcw_priv)) 1313 != 0) { 1314 (void) topo_mod_seterrno(mod, err); 1315 topo_mod_dprintf(mod, "hc_walker: callback " 1316 "failed: %s\n ", topo_mod_errmsg(mod)); 1317 return (TOPO_WALK_ERR); 1318 } 1319 topo_mod_dprintf(mod, "hc_walker: callback " 1320 "complete: terminate walk\n"); 1321 return (TOPO_WALK_TERMINATE); 1322 } else { 1323 topo_mod_dprintf(mod, "hc_walker: node not found\n"); 1324 return (TOPO_WALK_TERMINATE); 1325 } 1326 } 1327 1328 err = nvlist_lookup_string(hwp->hcw_list[i], FM_FMRI_HC_NAME, &name); 1329 err |= nvlist_lookup_string(hwp->hcw_list[i], FM_FMRI_HC_ID, &id); 1330 1331 if (err != 0) { 1332 (void) topo_mod_seterrno(mod, EMOD_NVL_INVAL); 1333 return (TOPO_WALK_ERR); 1334 } 1335 1336 inst = atoi(id); 1337 1338 /* 1339 * Check to see if our node matches the requested FMRI. If it doesn't 1340 * (because the enumerator determines matching based on something other 1341 * than name/instance, or because we're at the first level below the 1342 * root), then iterate over siblings to find the matching node. 1343 */ 1344 if (hc_match(mod, node, hwp->hcw_fmri, name, inst, &match) != 0) 1345 return (TOPO_WALK_ERR); 1346 1347 if (!match) 1348 return (hc_walk_sibling(mod, node, hwp, name, inst)); 1349 1350 topo_mod_dprintf(mod, "hc_walker: walking node:%s=%" PRIu64 " for hc:" 1351 "%s=%" PRIu64 " at %d, end at %d \n", topo_node_name(node), 1352 topo_node_instance(node), name, inst, i, hwp->hcw_end); 1353 1354 if (i == hwp->hcw_end) { 1355 1356 /* 1357 * We are at the end of the hc-list. Now, check for 1358 * a facility leaf and walk one more time. 1359 */ 1360 if (hwp->hcw_fac != NULL) { 1361 err = nvlist_lookup_string(hwp->hcw_fac, 1362 FM_FMRI_FACILITY_NAME, &fname); 1363 err |= nvlist_lookup_string(hwp->hcw_fac, 1364 FM_FMRI_FACILITY_TYPE, &ftype); 1365 if (err != 0) { 1366 (void) topo_mod_seterrno(mod, EMOD_NVL_INVAL); 1367 return (TOPO_WALK_ERR); 1368 } 1369 hwp->hcw_index++; 1370 topo_mod_dprintf(mod, "hc_walker: walk to facility " 1371 "node:%s=%s\n", fname, ftype); 1372 return (topo_walk_byid(hwp->hcw_wp, fname, 0)); 1373 } 1374 1375 /* 1376 * Otherwise, this is the node we're looking for. 1377 */ 1378 if ((err = hwp->hcw_cb(mod, node, hwp->hcw_priv)) != 0) { 1379 (void) topo_mod_seterrno(mod, err); 1380 topo_mod_dprintf(mod, "hc_walker: callback " 1381 "failed: %s\n ", topo_mod_errmsg(mod)); 1382 return (TOPO_WALK_ERR); 1383 } else { 1384 topo_mod_dprintf(mod, "hc_walker: callback " 1385 "complete: terminate walk\n"); 1386 return (TOPO_WALK_TERMINATE); 1387 } 1388 } 1389 1390 /* 1391 * Move on to the next component in the hc-list 1392 */ 1393 hwp->hcw_index = ++i; 1394 err = nvlist_lookup_string(hwp->hcw_list[i], FM_FMRI_HC_NAME, &name); 1395 err |= nvlist_lookup_string(hwp->hcw_list[i], FM_FMRI_HC_ID, &id); 1396 if (err != 0) { 1397 (void) topo_mod_seterrno(mod, err); 1398 return (TOPO_WALK_ERR); 1399 } 1400 inst = atoi(id); 1401 1402 return (topo_walk_byid(hwp->hcw_wp, name, inst)); 1403 1404 } 1405 1406 static struct hc_walk * 1407 hc_walk_init(topo_mod_t *mod, tnode_t *node, nvlist_t *rsrc, 1408 topo_mod_walk_cb_t cb, void *pdata) 1409 { 1410 int err, ret; 1411 uint_t sz; 1412 struct hc_walk *hwp; 1413 topo_walk_t *wp; 1414 1415 if ((hwp = topo_mod_alloc(mod, sizeof (struct hc_walk))) == NULL) { 1416 (void) topo_mod_seterrno(mod, EMOD_NOMEM); 1417 return (NULL); 1418 } 1419 1420 if (nvlist_lookup_nvlist_array(rsrc, FM_FMRI_HC_LIST, &hwp->hcw_list, 1421 &sz) != 0) { 1422 topo_mod_dprintf(mod, "hc_walk_init: failed to lookup %s " 1423 "nvlist\n", FM_FMRI_HC_LIST); 1424 topo_mod_free(mod, hwp, sizeof (struct hc_walk)); 1425 (void) topo_mod_seterrno(mod, EMOD_METHOD_INVAL); 1426 return (NULL); 1427 } 1428 if ((ret = nvlist_lookup_nvlist(rsrc, FM_FMRI_FACILITY, &hwp->hcw_fac)) 1429 != 0) { 1430 if (ret != ENOENT) { 1431 topo_mod_dprintf(mod, "hc_walk_init: unexpected error " 1432 "looking up %s nvlist", FM_FMRI_FACILITY); 1433 topo_mod_free(mod, hwp, sizeof (struct hc_walk)); 1434 (void) topo_mod_seterrno(mod, EMOD_METHOD_INVAL); 1435 return (NULL); 1436 } else { 1437 hwp->hcw_fac = NULL; 1438 } 1439 } 1440 1441 hwp->hcw_fmri = rsrc; 1442 hwp->hcw_end = sz - 1; 1443 hwp->hcw_index = 0; 1444 hwp->hcw_priv = pdata; 1445 hwp->hcw_cb = cb; 1446 if ((wp = topo_mod_walk_init(mod, node, hc_walker, (void *)hwp, &err)) 1447 == NULL) { 1448 topo_mod_dprintf(mod, "hc_walk_init: topo_mod_walk_init failed " 1449 "(%s)\n", topo_strerror(err)); 1450 topo_mod_free(mod, hwp, sizeof (struct hc_walk)); 1451 (void) topo_mod_seterrno(mod, err); 1452 return (NULL); 1453 } 1454 1455 hwp->hcw_wp = wp; 1456 1457 return (hwp); 1458 } 1459 1460 struct prop_lookup { 1461 const char *pl_pgroup; 1462 const char *pl_pname; 1463 int pl_flag; 1464 nvlist_t *pl_args; 1465 nvlist_t *pl_rsrc; 1466 nvlist_t *pl_prop; 1467 }; 1468 1469 /*ARGSUSED*/ 1470 static int 1471 hc_prop_get(topo_mod_t *mod, tnode_t *node, void *pdata) 1472 { 1473 int err = 0; 1474 1475 struct prop_lookup *plp = (struct prop_lookup *)pdata; 1476 1477 (void) topo_prop_getprop(node, plp->pl_pgroup, plp->pl_pname, 1478 plp->pl_args, &plp->pl_prop, &err); 1479 1480 return (err); 1481 } 1482 1483 static int 1484 hc_fmri_prop_get(topo_mod_t *mod, tnode_t *node, topo_version_t version, 1485 nvlist_t *in, nvlist_t **out) 1486 { 1487 int err; 1488 struct hc_walk *hwp; 1489 struct prop_lookup *plp; 1490 1491 if (version > TOPO_METH_PROP_GET_VERSION) 1492 return (topo_mod_seterrno(mod, ETOPO_METHOD_VERNEW)); 1493 1494 if ((plp = topo_mod_alloc(mod, sizeof (struct prop_lookup))) == NULL) 1495 return (topo_mod_seterrno(mod, EMOD_NOMEM)); 1496 1497 err = nvlist_lookup_string(in, TOPO_PROP_GROUP, 1498 (char **)&plp->pl_pgroup); 1499 err |= nvlist_lookup_string(in, TOPO_PROP_VAL_NAME, 1500 (char **)&plp->pl_pname); 1501 err |= nvlist_lookup_nvlist(in, TOPO_PROP_RESOURCE, &plp->pl_rsrc); 1502 if (err != 0) { 1503 topo_mod_free(mod, plp, sizeof (struct prop_lookup)); 1504 return (topo_mod_seterrno(mod, EMOD_METHOD_INVAL)); 1505 } 1506 1507 /* 1508 * Private args to prop method are optional 1509 */ 1510 if ((err = nvlist_lookup_nvlist(in, TOPO_PROP_PARGS, &plp->pl_args)) 1511 != 0) { 1512 if (err != ENOENT) { 1513 topo_mod_free(mod, plp, sizeof (struct prop_lookup)); 1514 return (topo_mod_seterrno(mod, EMOD_METHOD_INVAL)); 1515 } else { 1516 plp->pl_args = NULL; 1517 } 1518 } 1519 1520 plp->pl_prop = NULL; 1521 if ((hwp = hc_walk_init(mod, node, plp->pl_rsrc, hc_prop_get, 1522 (void *)plp)) != NULL) { 1523 if (topo_walk_step(hwp->hcw_wp, TOPO_WALK_CHILD) == 1524 TOPO_WALK_ERR) 1525 err = -1; 1526 else 1527 err = 0; 1528 topo_walk_fini(hwp->hcw_wp); 1529 topo_mod_free(mod, hwp, sizeof (struct hc_walk)); 1530 } else { 1531 err = -1; 1532 } 1533 1534 if (plp->pl_prop != NULL) 1535 *out = plp->pl_prop; 1536 1537 topo_mod_free(mod, plp, sizeof (struct prop_lookup)); 1538 1539 return (err); 1540 } 1541 1542 /*ARGSUSED*/ 1543 static int 1544 hc_pgrp_get(topo_mod_t *mod, tnode_t *node, void *pdata) 1545 { 1546 int err = 0; 1547 1548 struct prop_lookup *plp = (struct prop_lookup *)pdata; 1549 1550 (void) topo_prop_getpgrp(node, plp->pl_pgroup, &plp->pl_prop, &err); 1551 1552 return (err); 1553 } 1554 1555 static int 1556 hc_fmri_pgrp_get(topo_mod_t *mod, tnode_t *node, topo_version_t version, 1557 nvlist_t *in, nvlist_t **out) 1558 { 1559 int err; 1560 struct hc_walk *hwp; 1561 struct prop_lookup *plp; 1562 1563 if (version > TOPO_METH_PGRP_GET_VERSION) 1564 return (topo_mod_seterrno(mod, ETOPO_METHOD_VERNEW)); 1565 1566 if ((plp = topo_mod_alloc(mod, sizeof (struct prop_lookup))) == NULL) 1567 return (topo_mod_seterrno(mod, EMOD_NOMEM)); 1568 1569 err = nvlist_lookup_string(in, TOPO_PROP_GROUP, 1570 (char **)&plp->pl_pgroup); 1571 err |= nvlist_lookup_nvlist(in, TOPO_PROP_RESOURCE, &plp->pl_rsrc); 1572 if (err != 0) { 1573 topo_mod_free(mod, plp, sizeof (struct prop_lookup)); 1574 return (topo_mod_seterrno(mod, EMOD_METHOD_INVAL)); 1575 } 1576 1577 plp->pl_prop = NULL; 1578 if ((hwp = hc_walk_init(mod, node, plp->pl_rsrc, hc_pgrp_get, 1579 (void *)plp)) != NULL) { 1580 if (topo_walk_step(hwp->hcw_wp, TOPO_WALK_CHILD) == 1581 TOPO_WALK_ERR) 1582 err = -1; 1583 else 1584 err = 0; 1585 topo_walk_fini(hwp->hcw_wp); 1586 topo_mod_free(mod, hwp, sizeof (struct hc_walk)); 1587 } else { 1588 err = -1; 1589 } 1590 1591 if (plp->pl_prop != NULL) 1592 *out = plp->pl_prop; 1593 1594 topo_mod_free(mod, plp, sizeof (struct prop_lookup)); 1595 1596 return (err); 1597 } 1598 1599 /*ARGSUSED*/ 1600 static int 1601 hc_prop_setprop(topo_mod_t *mod, tnode_t *node, void *pdata) 1602 { 1603 int err = 0; 1604 1605 struct prop_lookup *plp = (struct prop_lookup *)pdata; 1606 1607 (void) topo_prop_setprop(node, plp->pl_pgroup, plp->pl_prop, 1608 plp->pl_flag, plp->pl_args, &err); 1609 1610 return (err); 1611 } 1612 1613 /*ARGSUSED*/ 1614 static int 1615 hc_fmri_prop_set(topo_mod_t *mod, tnode_t *node, topo_version_t version, 1616 nvlist_t *in, nvlist_t **out) 1617 { 1618 int err; 1619 struct hc_walk *hwp; 1620 struct prop_lookup *plp; 1621 1622 if (version > TOPO_METH_PROP_SET_VERSION) 1623 return (topo_mod_seterrno(mod, ETOPO_METHOD_VERNEW)); 1624 1625 if ((plp = topo_mod_alloc(mod, sizeof (struct prop_lookup))) == NULL) 1626 return (topo_mod_seterrno(mod, EMOD_NOMEM)); 1627 1628 err = nvlist_lookup_string(in, TOPO_PROP_GROUP, 1629 (char **)&plp->pl_pgroup); 1630 err |= nvlist_lookup_nvlist(in, TOPO_PROP_RESOURCE, &plp->pl_rsrc); 1631 err |= nvlist_lookup_nvlist(in, TOPO_PROP_VAL, &plp->pl_prop); 1632 err |= nvlist_lookup_int32(in, TOPO_PROP_FLAG, &plp->pl_flag); 1633 if (err != 0) { 1634 topo_mod_free(mod, plp, sizeof (struct prop_lookup)); 1635 return (topo_mod_seterrno(mod, EMOD_METHOD_INVAL)); 1636 } 1637 1638 /* 1639 * Private args to prop method are optional 1640 */ 1641 if ((err = nvlist_lookup_nvlist(in, TOPO_PROP_PARGS, &plp->pl_args)) 1642 != 0) { 1643 if (err != ENOENT) 1644 return (topo_mod_seterrno(mod, EMOD_METHOD_INVAL)); 1645 else 1646 plp->pl_args = NULL; 1647 } 1648 1649 if ((hwp = hc_walk_init(mod, node, plp->pl_rsrc, hc_prop_setprop, 1650 (void *)plp)) != NULL) { 1651 if (topo_walk_step(hwp->hcw_wp, TOPO_WALK_CHILD) == 1652 TOPO_WALK_ERR) 1653 err = -1; 1654 else 1655 err = 0; 1656 topo_walk_fini(hwp->hcw_wp); 1657 topo_mod_free(mod, hwp, sizeof (struct hc_walk)); 1658 } else { 1659 err = -1; 1660 } 1661 1662 topo_mod_free(mod, plp, sizeof (struct prop_lookup)); 1663 1664 return (err); 1665 } 1666 1667 struct hc_args { 1668 nvlist_t *ha_fmri; 1669 nvlist_t *ha_nvl; 1670 char *ha_method_name; 1671 topo_version_t ha_method_ver; 1672 }; 1673 1674 static int 1675 hc_auth_changed(nvlist_t *nva, nvlist_t *nvb, const char *propname) 1676 { 1677 char *stra, *strb; 1678 1679 if (nvlist_lookup_string(nva, propname, &stra) != 0 || 1680 nvlist_lookup_string(nvb, propname, &strb) != 0) 1681 return (FMD_OBJ_STATE_UNKNOWN); 1682 1683 if (strcmp(stra, strb) != 0) 1684 return (FMD_OBJ_STATE_REPLACED); 1685 else 1686 return (FMD_OBJ_STATE_STILL_PRESENT); 1687 } 1688 1689 static int 1690 hc_is_present(topo_mod_t *mod, tnode_t *node, void *pdata) 1691 { 1692 int err; 1693 struct hc_args *hap = (struct hc_args *)pdata; 1694 nvlist_t *rsrc; 1695 boolean_t present; 1696 1697 /* 1698 * check with the enumerator that created this FMRI 1699 * (topo node) 1700 */ 1701 if (topo_method_invoke(node, TOPO_METH_PRESENT, 1702 TOPO_METH_PRESENT_VERSION, hap->ha_fmri, &hap->ha_nvl, 1703 &err) < 0) { 1704 1705 /* 1706 * If the method exists but failed for some other reason, 1707 * propagate the error as making any decision over presence is 1708 * impossible. 1709 */ 1710 if (err != ETOPO_METHOD_NOTSUP) 1711 return (err); 1712 1713 /* 1714 * Check the authority information. If the part id or serial 1715 * number doesn't match, then it isn't the same FMRI. 1716 * Otherwise, assume presence. 1717 */ 1718 if (topo_node_resource(node, &rsrc, &err) != 0) 1719 return (err); 1720 1721 present = B_TRUE; 1722 if (hc_auth_changed(hap->ha_fmri, rsrc, 1723 FM_FMRI_HC_SERIAL_ID) == FMD_OBJ_STATE_REPLACED || 1724 hc_auth_changed(hap->ha_fmri, rsrc, 1725 FM_FMRI_HC_PART) == FMD_OBJ_STATE_REPLACED) { 1726 present = B_FALSE; 1727 } 1728 nvlist_free(rsrc); 1729 1730 if (topo_mod_nvalloc(mod, &hap->ha_nvl, NV_UNIQUE_NAME) != 0) 1731 return (EMOD_NOMEM); 1732 1733 if (nvlist_add_uint32(hap->ha_nvl, 1734 TOPO_METH_PRESENT_RET, present) != 0) { 1735 nvlist_free(hap->ha_nvl); 1736 hap->ha_nvl = NULL; 1737 return (EMOD_NOMEM); 1738 } 1739 } 1740 1741 return (0); 1742 } 1743 1744 static int 1745 hc_fmri_present(topo_mod_t *mod, tnode_t *node, topo_version_t version, 1746 nvlist_t *in, nvlist_t **out) 1747 { 1748 int err; 1749 struct hc_walk *hwp; 1750 struct hc_args *hap; 1751 1752 if (version > TOPO_METH_PRESENT_VERSION) 1753 return (topo_mod_seterrno(mod, ETOPO_METHOD_VERNEW)); 1754 1755 if ((hap = topo_mod_alloc(mod, sizeof (struct hc_args))) == NULL) 1756 return (topo_mod_seterrno(mod, EMOD_NOMEM)); 1757 1758 hap->ha_fmri = in; 1759 hap->ha_nvl = NULL; 1760 if ((hwp = hc_walk_init(mod, node, hap->ha_fmri, hc_is_present, 1761 (void *)hap)) != NULL) { 1762 if (topo_walk_step(hwp->hcw_wp, TOPO_WALK_CHILD) == 1763 TOPO_WALK_ERR) 1764 err = -1; 1765 else 1766 err = 0; 1767 topo_walk_fini(hwp->hcw_wp); 1768 topo_mod_free(mod, hwp, sizeof (struct hc_walk)); 1769 } else { 1770 err = -1; 1771 } 1772 1773 if (hap->ha_nvl != NULL) 1774 *out = hap->ha_nvl; 1775 1776 topo_mod_free(mod, hap, sizeof (struct hc_args)); 1777 1778 return (err); 1779 } 1780 1781 static int 1782 hc_is_replaced(topo_mod_t *mod, tnode_t *node, void *pdata) 1783 { 1784 int err; 1785 struct hc_args *hap = (struct hc_args *)pdata; 1786 uint32_t present = 0; 1787 nvlist_t *rsrc; 1788 uint32_t rval = FMD_OBJ_STATE_UNKNOWN; 1789 1790 /* 1791 * check with the enumerator that created this FMRI 1792 * (topo node) 1793 */ 1794 if (topo_method_invoke(node, TOPO_METH_REPLACED, 1795 TOPO_METH_REPLACED_VERSION, hap->ha_fmri, &hap->ha_nvl, 1796 &err) < 0) { 1797 /* 1798 * If the method exists but failed for some other 1799 * reason, propagate the error as making any decision 1800 * over presence is impossible. 1801 */ 1802 if (err != ETOPO_METHOD_NOTSUP) 1803 return (err); 1804 1805 /* 1806 * Enumerator didn't provide "replaced" method - 1807 * try "present" method 1808 */ 1809 if (topo_method_invoke(node, TOPO_METH_PRESENT, 1810 TOPO_METH_PRESENT_VERSION, hap->ha_fmri, &hap->ha_nvl, 1811 &err) < 0) { 1812 /* 1813 * If the method exists but failed for some other 1814 * reason, propagate the error as making any decision 1815 * over presence is impossible. 1816 */ 1817 if (err != ETOPO_METHOD_NOTSUP) 1818 return (err); 1819 1820 /* 1821 * Enumerator didn't provide "present" method either - 1822 * so check the authority information. If the part id 1823 * or serial number doesn't match, then it isn't the 1824 * same FMRI. Otherwise, if we have a serial number and 1825 * it hasn't changed, then assume it is the same FMRI. 1826 */ 1827 if (topo_node_resource(node, &rsrc, &err) != 0) 1828 return (err); 1829 rval = hc_auth_changed(hap->ha_fmri, rsrc, 1830 FM_FMRI_HC_PART); 1831 if (rval != FMD_OBJ_STATE_REPLACED) 1832 rval = hc_auth_changed(hap->ha_fmri, rsrc, 1833 FM_FMRI_HC_SERIAL_ID); 1834 nvlist_free(rsrc); 1835 if (topo_mod_nvalloc(mod, &hap->ha_nvl, 1836 NV_UNIQUE_NAME) != 0) 1837 return (EMOD_NOMEM); 1838 if (nvlist_add_uint32(hap->ha_nvl, 1839 TOPO_METH_REPLACED_RET, rval) != 0) { 1840 nvlist_free(hap->ha_nvl); 1841 hap->ha_nvl = NULL; 1842 return (ETOPO_PROP_NVL); 1843 } 1844 } else { 1845 (void) nvlist_lookup_uint32(hap->ha_nvl, 1846 TOPO_METH_PRESENT_RET, &present); 1847 (void) nvlist_remove(hap->ha_nvl, 1848 TOPO_METH_PRESENT_RET, DATA_TYPE_UINT32); 1849 if (nvlist_add_uint32(hap->ha_nvl, 1850 TOPO_METH_REPLACED_RET, 1851 present ? FMD_OBJ_STATE_UNKNOWN : 1852 FMD_OBJ_STATE_NOT_PRESENT) != 0) { 1853 nvlist_free(hap->ha_nvl); 1854 hap->ha_nvl = NULL; 1855 return (ETOPO_PROP_NVL); 1856 } 1857 } 1858 } 1859 return (0); 1860 } 1861 1862 static int 1863 hc_fmri_replaced(topo_mod_t *mod, tnode_t *node, topo_version_t version, 1864 nvlist_t *in, nvlist_t **out) 1865 { 1866 int err; 1867 struct hc_walk *hwp; 1868 struct hc_args *hap; 1869 1870 if (version > TOPO_METH_REPLACED_VERSION) 1871 return (topo_mod_seterrno(mod, ETOPO_METHOD_VERNEW)); 1872 1873 if ((hap = topo_mod_alloc(mod, sizeof (struct hc_args))) == NULL) 1874 return (topo_mod_seterrno(mod, EMOD_NOMEM)); 1875 1876 hap->ha_fmri = in; 1877 hap->ha_nvl = NULL; 1878 if ((hwp = hc_walk_init(mod, node, hap->ha_fmri, hc_is_replaced, 1879 (void *)hap)) != NULL) { 1880 if (topo_walk_step(hwp->hcw_wp, TOPO_WALK_CHILD) == 1881 TOPO_WALK_ERR) 1882 err = -1; 1883 else 1884 err = 0; 1885 topo_walk_fini(hwp->hcw_wp); 1886 topo_mod_free(mod, hwp, sizeof (struct hc_walk)); 1887 } else { 1888 err = -1; 1889 } 1890 1891 if (hap->ha_nvl != NULL) 1892 *out = hap->ha_nvl; 1893 1894 topo_mod_free(mod, hap, sizeof (struct hc_args)); 1895 1896 return (err); 1897 } 1898 1899 static int 1900 hc_unusable(topo_mod_t *mod, tnode_t *node, void *pdata) 1901 { 1902 int err; 1903 struct hc_args *hap = (struct hc_args *)pdata; 1904 1905 /* 1906 * check with the enumerator that created this FMRI 1907 * (topo node) 1908 */ 1909 if (topo_method_invoke(node, TOPO_METH_UNUSABLE, 1910 TOPO_METH_UNUSABLE_VERSION, hap->ha_fmri, &hap->ha_nvl, 1911 &err) < 0) { 1912 1913 /* 1914 * Err on the side of caution and return usable 1915 */ 1916 if (topo_mod_nvalloc(mod, &hap->ha_nvl, NV_UNIQUE_NAME) == 0) 1917 if (nvlist_add_uint32(hap->ha_nvl, 1918 TOPO_METH_UNUSABLE_RET, 0) == 0) 1919 return (0); 1920 1921 return (ETOPO_PROP_NVL); 1922 } 1923 1924 return (0); 1925 } 1926 1927 static int 1928 hc_fmri_unusable(topo_mod_t *mod, tnode_t *node, topo_version_t version, 1929 nvlist_t *in, nvlist_t **out) 1930 { 1931 int err; 1932 struct hc_walk *hwp; 1933 struct hc_args *hap; 1934 1935 if (version > TOPO_METH_UNUSABLE_VERSION) 1936 return (topo_mod_seterrno(mod, ETOPO_METHOD_VERNEW)); 1937 1938 if ((hap = topo_mod_alloc(mod, sizeof (struct hc_args))) == NULL) 1939 return (topo_mod_seterrno(mod, EMOD_NOMEM)); 1940 1941 hap->ha_fmri = in; 1942 hap->ha_nvl = NULL; 1943 if ((hwp = hc_walk_init(mod, node, hap->ha_fmri, hc_unusable, 1944 (void *)hap)) != NULL) { 1945 if (topo_walk_step(hwp->hcw_wp, TOPO_WALK_CHILD) == 1946 TOPO_WALK_ERR) 1947 err = -1; 1948 else 1949 err = 0; 1950 topo_walk_fini(hwp->hcw_wp); 1951 topo_mod_free(mod, hwp, sizeof (struct hc_walk)); 1952 } else { 1953 err = -1; 1954 } 1955 1956 if (hap->ha_nvl != NULL) 1957 *out = hap->ha_nvl; 1958 1959 topo_mod_free(mod, hap, sizeof (struct hc_args)); 1960 1961 return (err); 1962 } 1963 1964 struct fac_lookup { 1965 const char *fl_fac_type; 1966 uint32_t fl_fac_subtype; 1967 #ifdef _LP64 1968 uint64_t fl_callback; 1969 uint64_t fl_callback_args; 1970 #else 1971 uint32_t fl_callback; 1972 uint32_t fl_callback_args; 1973 #endif 1974 nvlist_t *fl_rsrc; 1975 nvlist_t *fl_fac_rsrc; 1976 }; 1977 1978 static int 1979 hc_fac_get(topo_mod_t *mod, tnode_t *node, void *pdata) 1980 { 1981 struct fac_lookup *flp = (struct fac_lookup *)pdata; 1982 topo_walk_cb_t cb = (topo_walk_cb_t)flp->fl_callback; 1983 topo_faclist_t faclist, *tmp; 1984 int err, ret = 0; 1985 1986 /* 1987 * Lookup the specified facility node. Return with an error if we can't 1988 * find it. 1989 */ 1990 if (topo_node_facility(mod->tm_hdl, node, flp->fl_fac_type, 1991 flp->fl_fac_subtype, &faclist, &err) != 0) { 1992 topo_mod_dprintf(mod, "hc_fac_get: topo_node_facility " 1993 "failed\n"); 1994 return (TOPO_WALK_ERR); 1995 } 1996 1997 /* 1998 * Invoke user's callback for each facility node in the topo list, 1999 * passing in a pointer to the facility node 2000 */ 2001 for (tmp = topo_list_next(&faclist.tf_list); tmp != NULL; 2002 tmp = topo_list_next(tmp)) { 2003 2004 if ((err = cb(mod->tm_hdl, tmp->tf_node, 2005 (void *)flp->fl_callback_args)) != 0) { 2006 (void) topo_mod_seterrno(mod, err); 2007 topo_mod_dprintf(mod, "hc_fac_get: callback failed: " 2008 "%s\n ", topo_mod_errmsg(mod)); 2009 ret = TOPO_WALK_ERR; 2010 break; 2011 } 2012 } 2013 2014 while ((tmp = topo_list_next(&faclist.tf_list)) != NULL) { 2015 topo_list_delete(&faclist.tf_list, tmp); 2016 topo_mod_free(mod, tmp, sizeof (topo_faclist_t)); 2017 } 2018 return (ret); 2019 } 2020 2021 static int 2022 hc_fmri_facility(topo_mod_t *mod, tnode_t *node, topo_version_t version, 2023 nvlist_t *in, nvlist_t **out) 2024 { 2025 int err = 0; 2026 struct hc_walk *hwp; 2027 struct fac_lookup *flp; 2028 2029 if (version > TOPO_METH_FACILITY_VERSION) 2030 return (topo_mod_seterrno(mod, ETOPO_METHOD_VERNEW)); 2031 2032 if ((flp = topo_mod_alloc(mod, sizeof (struct fac_lookup))) == NULL) 2033 return (topo_mod_seterrno(mod, EMOD_NOMEM)); 2034 2035 /* 2036 * lookup arguments: hw resource, facility type, facility subtype, 2037 * callback and callback args 2038 */ 2039 err = nvlist_lookup_nvlist(in, TOPO_PROP_RESOURCE, &flp->fl_rsrc); 2040 err |= nvlist_lookup_string(in, FM_FMRI_FACILITY_TYPE, 2041 (char **)&flp->fl_fac_type); 2042 err |= nvlist_lookup_uint32(in, "type", &flp->fl_fac_subtype); 2043 #ifdef _LP64 2044 err |= nvlist_lookup_uint64(in, "callback", &flp->fl_callback); 2045 err |= nvlist_lookup_uint64(in, "callback-args", 2046 &flp->fl_callback_args); 2047 #else 2048 err |= nvlist_lookup_uint32(in, "callback", &flp->fl_callback); 2049 err |= nvlist_lookup_uint32(in, "callback-args", 2050 &flp->fl_callback_args); 2051 #endif 2052 if (err != 0) { 2053 topo_mod_dprintf(mod, "hc_fmri_facility: failed to construct " 2054 "walker arg nvlist\n"); 2055 topo_mod_free(mod, flp, sizeof (struct fac_lookup)); 2056 return (topo_mod_seterrno(mod, EMOD_METHOD_INVAL)); 2057 } 2058 2059 flp->fl_fac_rsrc = NULL; 2060 if ((hwp = hc_walk_init(mod, node, flp->fl_rsrc, hc_fac_get, 2061 (void *)flp)) != NULL) { 2062 if (topo_walk_step(hwp->hcw_wp, TOPO_WALK_CHILD) == 2063 TOPO_WALK_ERR) 2064 err = -1; 2065 else 2066 err = 0; 2067 topo_walk_fini(hwp->hcw_wp); 2068 topo_mod_free(mod, hwp, sizeof (struct hc_walk)); 2069 } else { 2070 topo_mod_dprintf(mod, "hc_fmri_facility: failed to initialize " 2071 "hc walker\n"); 2072 err = -1; 2073 } 2074 2075 if (flp->fl_fac_rsrc != NULL) 2076 *out = flp->fl_fac_rsrc; 2077 2078 topo_mod_free(mod, flp, sizeof (struct fac_lookup)); 2079 2080 return (err); 2081 } 2082 2083 /* ARGSUSED */ 2084 static int 2085 hc_expand(topo_mod_t *mod, tnode_t *node, void *pdata) 2086 { 2087 int err; 2088 nvlist_t *nvl; 2089 const char **namep; 2090 struct hc_args *hap = (struct hc_args *)pdata; 2091 const char *names[] = { 2092 FM_FMRI_HC_SERIAL_ID, 2093 FM_FMRI_HC_PART, 2094 FM_FMRI_HC_REVISION, 2095 NULL 2096 }; 2097 2098 if (topo_node_resource(node, &nvl, &err) != 0) 2099 return (ETOPO_METHOD_FAIL); 2100 2101 for (namep = names; *namep != NULL; namep++) { 2102 char *in_val, *node_val; 2103 2104 if (nvlist_lookup_string(nvl, *namep, &node_val) != 0) 2105 continue; 2106 2107 if (nvlist_lookup_string(hap->ha_fmri, *namep, &in_val) == 0) { 2108 if (strcmp(in_val, node_val) == 0) 2109 continue; 2110 (void) nvlist_remove(hap->ha_fmri, *namep, 2111 DATA_TYPE_STRING); 2112 } 2113 2114 if (nvlist_add_string(hap->ha_fmri, *namep, node_val) != 0) { 2115 nvlist_free(nvl); 2116 return (ETOPO_PROP_NVL); 2117 } 2118 } 2119 nvlist_free(nvl); 2120 2121 return (0); 2122 } 2123 2124 /* ARGSUSED */ 2125 static int 2126 hc_fmri_expand(topo_mod_t *mod, tnode_t *node, topo_version_t version, 2127 nvlist_t *in, nvlist_t **out) 2128 { 2129 int err; 2130 struct hc_walk *hwp; 2131 struct hc_args *hap; 2132 2133 if (version > TOPO_METH_EXPAND_VERSION) 2134 return (topo_mod_seterrno(mod, ETOPO_METHOD_VERNEW)); 2135 2136 if ((hap = topo_mod_alloc(mod, sizeof (struct hc_args))) == NULL) 2137 return (topo_mod_seterrno(mod, EMOD_NOMEM)); 2138 2139 hap->ha_fmri = in; 2140 hap->ha_nvl = NULL; 2141 if ((hwp = hc_walk_init(mod, node, hap->ha_fmri, hc_expand, 2142 (void *)hap)) != NULL) { 2143 if (topo_walk_step(hwp->hcw_wp, TOPO_WALK_CHILD) == 2144 TOPO_WALK_ERR) 2145 err = -1; 2146 else 2147 err = 0; 2148 topo_walk_fini(hwp->hcw_wp); 2149 } else { 2150 err = -1; 2151 } 2152 2153 topo_mod_free(mod, hwp, sizeof (struct hc_walk)); 2154 2155 /* expand method should not return out nvlist */ 2156 assert(hap->ha_nvl == NULL); 2157 2158 topo_mod_free(mod, hap, sizeof (struct hc_args)); 2159 2160 return (err); 2161 } 2162 2163 static int 2164 hc_retire_subr(topo_mod_t *mod, tnode_t *node, void *pdata) 2165 { 2166 int err, rc; 2167 struct hc_args *hap = (struct hc_args *)pdata; 2168 2169 topo_mod_dprintf(mod, "hc_retire_subr: invoking method %s\n", 2170 hap->ha_method_name); 2171 /* 2172 * check with the enumerator that created this FMRI 2173 * (topo node) 2174 */ 2175 rc = topo_method_invoke(node, hap->ha_method_name, 2176 hap->ha_method_ver, hap->ha_fmri, &hap->ha_nvl, &err); 2177 2178 topo_mod_dprintf(mod, "hc_retire_subr: invoking method %s " 2179 "returned %d\n", hap->ha_method_name, rc); 2180 2181 return (rc < 0 ? err : 0); 2182 } 2183 2184 static int 2185 hc_fmri_retire_subr(topo_mod_t *mod, tnode_t *node, char *method_name, 2186 topo_version_t builtin_version, topo_version_t version, nvlist_t *in, 2187 nvlist_t **out) 2188 { 2189 int err; 2190 struct hc_walk *hwp; 2191 struct hc_args *hap; 2192 2193 if (version > builtin_version) 2194 return (topo_mod_seterrno(mod, ETOPO_METHOD_VERNEW)); 2195 2196 if ((hap = topo_mod_alloc(mod, sizeof (struct hc_args))) == NULL) 2197 return (topo_mod_seterrno(mod, EMOD_NOMEM)); 2198 2199 hap->ha_fmri = in; 2200 hap->ha_nvl = NULL; 2201 hap->ha_method_name = method_name; 2202 hap->ha_method_ver = version; 2203 if ((hwp = hc_walk_init(mod, node, hap->ha_fmri, hc_retire_subr, 2204 (void *)hap)) != NULL) { 2205 if (topo_walk_step(hwp->hcw_wp, TOPO_WALK_CHILD) == 2206 TOPO_WALK_ERR) 2207 err = -1; 2208 else 2209 err = 0; 2210 topo_walk_fini(hwp->hcw_wp); 2211 } else { 2212 err = -1; 2213 } 2214 2215 topo_mod_free(mod, hwp, sizeof (struct hc_walk)); 2216 2217 if (hap->ha_nvl != NULL) 2218 *out = hap->ha_nvl; 2219 2220 topo_mod_free(mod, hap, sizeof (struct hc_args)); 2221 2222 return (err); 2223 } 2224 2225 static int 2226 hc_fmri_retire(topo_mod_t *mod, tnode_t *node, topo_version_t version, 2227 nvlist_t *in, nvlist_t **out) 2228 { 2229 return (hc_fmri_retire_subr(mod, node, TOPO_METH_RETIRE, 2230 TOPO_METH_RETIRE_VERSION, version, in, out)); 2231 } 2232 2233 static int 2234 hc_fmri_unretire(topo_mod_t *mod, tnode_t *node, topo_version_t version, 2235 nvlist_t *in, nvlist_t **out) 2236 { 2237 return (hc_fmri_retire_subr(mod, node, TOPO_METH_UNRETIRE, 2238 TOPO_METH_UNRETIRE_VERSION, version, in, out)); 2239 } 2240 2241 static int 2242 hc_fmri_service_state(topo_mod_t *mod, tnode_t *node, topo_version_t version, 2243 nvlist_t *in, nvlist_t **out) 2244 { 2245 return (hc_fmri_retire_subr(mod, node, TOPO_METH_SERVICE_STATE, 2246 TOPO_METH_SERVICE_STATE_VERSION, version, in, out)); 2247 } 2248