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