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