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