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