1 /* 2 * CDDL HEADER START 3 * 4 * The contents of this file are subject to the terms of the 5 * Common Development and Distribution License (the "License"). 6 * You may not use this file except in compliance with the License. 7 * 8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 9 * or http://www.opensolaris.org/os/licensing. 10 * See the License for the specific language governing permissions 11 * and limitations under the License. 12 * 13 * When distributing Covered Code, include this CDDL HEADER in each 14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 15 * If applicable, add the following below this CDDL HEADER, with the 16 * fields enclosed by brackets "[]" replaced with your own identifying 17 * information: Portions Copyright [yyyy] [name of copyright owner] 18 * 19 * CDDL HEADER END 20 */ 21 /* 22 * Copyright (c) 2006, 2010, Oracle and/or its affiliates. All rights reserved. 23 * Copyright 2019 Joyent, Inc. 24 * Copyright 2023 Oxide Computer Company 25 */ 26 27 /* 28 * Topology Plugin Modules 29 * 30 * Topology plugin modules are shared libraries that are dlopen'd and 31 * used to enumerate resources in the system and export per-node method 32 * operations. 33 * 34 * They are loaded by our builtin scheme-specific plugins, other modules or 35 * by processing a topo map XML file to enumerate and create nodes for 36 * resources that are present in the system. They may also export a set of 37 * topology node specific methods that can be invoked directly via 38 * topo_method_invoke() or indirectly via the 39 * topo_prop_get* family of functions to access dynamic property data. 40 * 41 * Module Plugin API 42 * 43 * Enumerators must provide entry points for initialization and clean-up 44 * (_topo_init() and _topo_fini()). In their _topo_init() function, an 45 * enumerator should register (topo_mod_register()) its enumeration callback 46 * and allocate resources required for a subsequent call to the callback. 47 * Optionally, methods may also be registered with topo_method_register(). 48 * 49 * In its enumeration callback routine, the module should search for resources 50 * within its realm of responsibility and create any node ranges, 51 * topo_node_range_create() and nodes, topo_node_bind(). The Enumerator 52 * module is handed a node to which it may begin attaching additional 53 * topology nodes. The enumerator may only access those nodes within its 54 * current scope of operation: the node passed into its enumeration op and 55 * any nodes it creates during enumeration. If the enumerator requires walker- 56 * style access to these nodes, it must use 57 * topo_mod_walk_init()/topo_walk_step()/topo_walk_fini(). 58 * 59 * If additional helper modules need to be loaded to complete the enumeration 60 * the module may do so by calling topo_mod_load(). Enumeration may then 61 * continue with the module handing off enumeration to its helper module 62 * by calling topo_mod_enumerate(). Similarly, a module may call 63 * topo_mod_enummap() to kick-off enumeration according to a given XML 64 * topology map file. A module *may* not cause re-entrance to itself 65 * via either of these interfaces. If re-entry is detected an error 66 * will be returned (ETOPO_ENUM_RECURS). 67 * 68 * If the module registers a release callback, it will be called on a node 69 * by node basis during topo_snap_rele(). Any private node data may be 70 * deallocated or methods unregistered at that time. Global module data 71 * should be cleaned up before or at the time that the module _topo_fini 72 * entry point is called. 73 * 74 * Module entry points and method invocations are guaranteed to be 75 * single-threaded for a given snapshot handle. Applications may have 76 * more than one topology snapshot open at a time. This means that the 77 * module operations and methods may be called for different module handles 78 * (topo_mod_t) asynchronously. The enumerator should not use static or 79 * global data structures that may become inconsistent in this situation. 80 * Method operations may be re-entrant if the module invokes one of its own 81 * methods directly or via dynamic property access. Caution should be 82 * exercised with method operations to insure that data remains consistent 83 * within the module and that deadlocks can not occur. 84 */ 85 86 #include <pthread.h> 87 #include <assert.h> 88 #include <errno.h> 89 #include <dirent.h> 90 #include <limits.h> 91 #include <alloca.h> 92 #include <unistd.h> 93 #include <stdio.h> 94 #include <ctype.h> 95 #include <pcidb.h> 96 #include <sys/param.h> 97 #include <sys/utsname.h> 98 #include <sys/smbios.h> 99 #include <sys/fm/protocol.h> 100 #include <sys/types.h> 101 #include <sys/stat.h> 102 #include <fcntl.h> 103 104 #include <topo_alloc.h> 105 #include <topo_error.h> 106 #include <topo_file.h> 107 #include <topo_fmri.h> 108 #include <topo_module.h> 109 #include <topo_method.h> 110 #include <topo_string.h> 111 #include <topo_subr.h> 112 #include <topo_tree.h> 113 114 #define PLUGIN_PATH "plugins" 115 #define PLUGIN_PATH_LEN MAXNAMELEN + 5 116 117 topo_mod_t * 118 topo_mod_load(topo_mod_t *pmod, const char *name, 119 topo_version_t version) 120 { 121 char *path; 122 char file[PLUGIN_PATH_LEN]; 123 topo_mod_t *mod = NULL; 124 topo_hdl_t *thp; 125 126 thp = pmod->tm_hdl; 127 128 /* 129 * Already loaded, topo_mod_lookup will bump the ref count 130 */ 131 if ((mod = topo_mod_lookup(thp, name, 1)) != NULL) { 132 if (mod->tm_info->tmi_version != version) { 133 topo_mod_rele(mod); 134 (void) topo_mod_seterrno(pmod, ETOPO_MOD_VER); 135 return (NULL); 136 } 137 return (mod); 138 } 139 140 (void) snprintf(file, PLUGIN_PATH_LEN, "%s/%s.so", 141 PLUGIN_PATH, name); 142 path = topo_search_path(pmod, thp->th_rootdir, (const char *)file); 143 if (path == NULL || 144 (mod = topo_modhash_load(thp, name, path, &topo_rtld_ops, version)) 145 == NULL) { /* returned with mod held */ 146 topo_mod_strfree(pmod, path); 147 (void) topo_mod_seterrno(pmod, topo_hdl_errno(thp) ? 148 topo_hdl_errno(thp) : ETOPO_MOD_NOENT); 149 return (NULL); 150 } 151 152 topo_mod_strfree(pmod, path); 153 154 return (mod); 155 } 156 157 void 158 topo_mod_unload(topo_mod_t *mod) 159 { 160 topo_mod_rele(mod); 161 } 162 163 static int 164 set_register_error(topo_mod_t *mod, int err) 165 { 166 if (mod->tm_info != NULL) 167 topo_mod_unregister(mod); 168 169 topo_dprintf(mod->tm_hdl, TOPO_DBG_ERR, 170 "module registration failed for %s: %s\n", 171 mod->tm_name, topo_strerror(err)); 172 173 return (topo_mod_seterrno(mod, err)); 174 } 175 176 int 177 topo_mod_register(topo_mod_t *mod, const topo_modinfo_t *mip, 178 topo_version_t version) 179 { 180 181 assert(!(mod->tm_flags & TOPO_MOD_FINI || 182 mod->tm_flags & TOPO_MOD_REG)); 183 184 if (version != TOPO_VERSION) 185 return (set_register_error(mod, EMOD_VER_ABI)); 186 187 if ((mod->tm_info = topo_mod_zalloc(mod, sizeof (topo_imodinfo_t))) 188 == NULL) 189 return (set_register_error(mod, EMOD_NOMEM)); 190 if ((mod->tm_info->tmi_ops = topo_mod_alloc(mod, 191 sizeof (topo_modops_t))) == NULL) 192 return (set_register_error(mod, EMOD_NOMEM)); 193 194 mod->tm_info->tmi_desc = topo_mod_strdup(mod, mip->tmi_desc); 195 if (mod->tm_info->tmi_desc == NULL) 196 return (set_register_error(mod, EMOD_NOMEM)); 197 198 mod->tm_info->tmi_scheme = topo_mod_strdup(mod, mip->tmi_scheme); 199 if (mod->tm_info->tmi_scheme == NULL) 200 return (set_register_error(mod, EMOD_NOMEM)); 201 202 203 mod->tm_info->tmi_version = (topo_version_t)mip->tmi_version; 204 mod->tm_info->tmi_ops->tmo_enum = mip->tmi_ops->tmo_enum; 205 mod->tm_info->tmi_ops->tmo_release = mip->tmi_ops->tmo_release; 206 207 mod->tm_flags |= TOPO_MOD_REG; 208 209 topo_dprintf(mod->tm_hdl, TOPO_DBG_MODSVC, 210 "registration succeeded for %s\n", mod->tm_name); 211 212 return (0); 213 } 214 215 void 216 topo_mod_unregister(topo_mod_t *mod) 217 { 218 if (mod->tm_info == NULL) 219 return; 220 221 assert(!(mod->tm_flags & TOPO_MOD_FINI)); 222 223 mod->tm_flags &= ~TOPO_MOD_REG; 224 225 if (mod->tm_info == NULL) 226 return; 227 228 if (mod->tm_info->tmi_ops != NULL) 229 topo_mod_free(mod, mod->tm_info->tmi_ops, 230 sizeof (topo_modops_t)); 231 if (mod->tm_info->tmi_desc != NULL) 232 topo_mod_strfree(mod, mod->tm_info->tmi_desc); 233 if (mod->tm_info->tmi_scheme != NULL) 234 topo_mod_strfree(mod, mod->tm_info->tmi_scheme); 235 236 topo_mod_free(mod, mod->tm_info, sizeof (topo_imodinfo_t)); 237 238 mod->tm_info = NULL; 239 } 240 241 int 242 topo_mod_enumerate(topo_mod_t *mod, tnode_t *node, const char *enum_name, 243 const char *name, topo_instance_t min, topo_instance_t max, void *data) 244 { 245 int err = 0; 246 topo_mod_t *enum_mod; 247 248 assert(mod->tm_flags & TOPO_MOD_REG); 249 250 if ((enum_mod = topo_mod_lookup(mod->tm_hdl, enum_name, 0)) == NULL) 251 return (topo_mod_seterrno(mod, EMOD_MOD_NOENT)); 252 253 topo_node_hold(node); 254 255 topo_dprintf(mod->tm_hdl, TOPO_DBG_MODSVC, "module %s enumerating " 256 "node %s=%d\n", (char *)mod->tm_name, (char *)node->tn_name, 257 node->tn_instance); 258 259 topo_mod_enter(enum_mod); 260 err = enum_mod->tm_info->tmi_ops->tmo_enum(enum_mod, node, name, min, 261 max, enum_mod->tm_priv, data); 262 topo_mod_exit(enum_mod); 263 264 if (err != 0) { 265 (void) topo_mod_seterrno(mod, EMOD_UKNOWN_ENUM); 266 267 topo_dprintf(mod->tm_hdl, TOPO_DBG_ERR, 268 "module %s failed enumeration for " 269 " node %s=%d\n", (char *)mod->tm_name, 270 (char *)node->tn_name, node->tn_instance); 271 272 topo_node_rele(node); 273 return (-1); 274 } 275 276 topo_node_rele(node); 277 278 return (0); 279 } 280 281 int 282 topo_mod_enummap(topo_mod_t *mod, tnode_t *node, const char *name, 283 const char *scheme) 284 { 285 return (topo_file_load(mod, node, (char *)name, (char *)scheme, 0)); 286 } 287 288 static nvlist_t * 289 set_fmri_err(topo_mod_t *mod, int err) 290 { 291 (void) topo_mod_seterrno(mod, err); 292 return (NULL); 293 } 294 295 nvlist_t * 296 topo_mod_hcfmri(topo_mod_t *mod, tnode_t *pnode, int version, const char *name, 297 topo_instance_t inst, nvlist_t *hc_specific, nvlist_t *auth, 298 const char *part, const char *rev, const char *serial) 299 { 300 int err; 301 nvlist_t *pfmri = NULL, *fmri = NULL, *args = NULL; 302 nvlist_t *nfp = NULL; 303 char *lpart, *lrev, *lserial; 304 305 if (version != FM_HC_SCHEME_VERSION) 306 return (set_fmri_err(mod, EMOD_FMRI_VERSION)); 307 308 /* 309 * Do we have any args to pass? 310 */ 311 if (pnode != NULL || auth != NULL || part != NULL || rev != NULL || 312 serial != NULL || hc_specific != NULL) { 313 if (topo_mod_nvalloc(mod, &args, NV_UNIQUE_NAME) != 0) 314 return (set_fmri_err(mod, EMOD_FMRI_NVL)); 315 } 316 317 if (pnode != NULL) { 318 if (topo_node_resource(pnode, &pfmri, &err) < 0) { 319 nvlist_free(args); 320 return (set_fmri_err(mod, EMOD_NVL_INVAL)); 321 } 322 323 if (nvlist_add_nvlist(args, TOPO_METH_FMRI_ARG_PARENT, 324 pfmri) != 0) { 325 nvlist_free(pfmri); 326 nvlist_free(args); 327 return (set_fmri_err(mod, EMOD_FMRI_NVL)); 328 } 329 nvlist_free(pfmri); 330 } 331 332 /* 333 * Add optional payload 334 */ 335 if (auth != NULL) 336 (void) nvlist_add_nvlist(args, TOPO_METH_FMRI_ARG_AUTH, auth); 337 if (part != NULL) { 338 lpart = topo_cleanup_auth_str(mod->tm_hdl, part); 339 if (lpart != NULL) { 340 (void) nvlist_add_string(args, TOPO_METH_FMRI_ARG_PART, 341 lpart); 342 topo_hdl_free(mod->tm_hdl, lpart, strlen(lpart) + 1); 343 } else { 344 (void) nvlist_add_string(args, TOPO_METH_FMRI_ARG_PART, 345 ""); 346 } 347 } 348 if (rev != NULL) { 349 lrev = topo_cleanup_auth_str(mod->tm_hdl, rev); 350 if (lrev != NULL) { 351 (void) nvlist_add_string(args, TOPO_METH_FMRI_ARG_REV, 352 lrev); 353 topo_hdl_free(mod->tm_hdl, lrev, strlen(lrev) + 1); 354 } else { 355 (void) nvlist_add_string(args, TOPO_METH_FMRI_ARG_REV, 356 ""); 357 } 358 } 359 if (serial != NULL) { 360 lserial = topo_cleanup_auth_str(mod->tm_hdl, serial); 361 if (lserial != NULL) { 362 (void) nvlist_add_string(args, TOPO_METH_FMRI_ARG_SER, 363 lserial); 364 topo_hdl_free(mod->tm_hdl, lserial, 365 strlen(lserial) + 1); 366 } else { 367 (void) nvlist_add_string(args, TOPO_METH_FMRI_ARG_SER, 368 ""); 369 } 370 } 371 if (hc_specific != NULL) 372 (void) nvlist_add_nvlist(args, TOPO_METH_FMRI_ARG_HCS, 373 hc_specific); 374 375 if ((fmri = topo_fmri_create(mod->tm_hdl, FM_FMRI_SCHEME_HC, name, inst, 376 args, &err)) == NULL) { 377 nvlist_free(args); 378 return (set_fmri_err(mod, err)); 379 } 380 381 nvlist_free(args); 382 383 (void) topo_mod_nvdup(mod, fmri, &nfp); 384 nvlist_free(fmri); 385 386 return (nfp); 387 } 388 389 nvlist_t * 390 topo_mod_devfmri(topo_mod_t *mod, int version, const char *dev_path, 391 const char *devid) 392 { 393 int err; 394 nvlist_t *fmri, *args; 395 nvlist_t *nfp = NULL; 396 397 if (version != FM_DEV_SCHEME_VERSION) 398 return (set_fmri_err(mod, EMOD_FMRI_VERSION)); 399 400 if (topo_mod_nvalloc(mod, &args, NV_UNIQUE_NAME) != 0) 401 return (set_fmri_err(mod, EMOD_FMRI_NVL)); 402 403 if (nvlist_add_string(args, FM_FMRI_DEV_PATH, dev_path) != 0) { 404 nvlist_free(args); 405 return (set_fmri_err(mod, EMOD_FMRI_NVL)); 406 } 407 408 (void) nvlist_add_string(args, FM_FMRI_DEV_ID, devid); 409 410 if ((fmri = topo_fmri_create(mod->tm_hdl, FM_FMRI_SCHEME_DEV, 411 FM_FMRI_SCHEME_DEV, 0, args, &err)) == NULL) { 412 nvlist_free(args); 413 return (set_fmri_err(mod, err)); 414 } 415 416 nvlist_free(args); 417 418 (void) topo_mod_nvdup(mod, fmri, &nfp); 419 nvlist_free(fmri); 420 421 return (nfp); 422 } 423 424 nvlist_t * 425 topo_mod_cpufmri(topo_mod_t *mod, int version, uint32_t cpu_id, uint8_t cpumask, 426 const char *serial) 427 { 428 int err; 429 nvlist_t *fmri = NULL, *args = NULL; 430 nvlist_t *nfp = NULL; 431 432 if (version != FM_CPU_SCHEME_VERSION) 433 return (set_fmri_err(mod, EMOD_FMRI_VERSION)); 434 435 if (topo_mod_nvalloc(mod, &args, NV_UNIQUE_NAME) != 0) 436 return (set_fmri_err(mod, EMOD_FMRI_NVL)); 437 438 if (nvlist_add_uint32(args, FM_FMRI_CPU_ID, cpu_id) != 0) { 439 nvlist_free(args); 440 return (set_fmri_err(mod, EMOD_FMRI_NVL)); 441 } 442 443 /* 444 * Add optional payload 445 */ 446 (void) nvlist_add_uint8(args, FM_FMRI_CPU_MASK, cpumask); 447 (void) nvlist_add_string(args, FM_FMRI_CPU_SERIAL_ID, serial); 448 449 if ((fmri = topo_fmri_create(mod->tm_hdl, FM_FMRI_SCHEME_CPU, 450 FM_FMRI_SCHEME_CPU, 0, args, &err)) == NULL) { 451 nvlist_free(args); 452 return (set_fmri_err(mod, err)); 453 } 454 455 nvlist_free(args); 456 457 (void) topo_mod_nvdup(mod, fmri, &nfp); 458 nvlist_free(fmri); 459 460 return (nfp); 461 } 462 463 nvlist_t * 464 topo_mod_memfmri(topo_mod_t *mod, int version, uint64_t pa, uint64_t offset, 465 const char *unum, int flags) 466 { 467 int err; 468 nvlist_t *args = NULL, *fmri = NULL; 469 nvlist_t *nfp = NULL; 470 471 if (version != FM_MEM_SCHEME_VERSION) 472 return (set_fmri_err(mod, EMOD_FMRI_VERSION)); 473 474 if (topo_mod_nvalloc(mod, &args, NV_UNIQUE_NAME) != 0) 475 return (set_fmri_err(mod, EMOD_FMRI_NVL)); 476 477 err = nvlist_add_string(args, FM_FMRI_MEM_UNUM, unum); 478 if (flags & TOPO_MEMFMRI_PA) 479 err |= nvlist_add_uint64(args, FM_FMRI_MEM_PHYSADDR, pa); 480 if (flags & TOPO_MEMFMRI_OFFSET) 481 err |= nvlist_add_uint64(args, FM_FMRI_MEM_OFFSET, offset); 482 483 if (err != 0) { 484 nvlist_free(args); 485 return (set_fmri_err(mod, EMOD_FMRI_NVL)); 486 } 487 488 if ((fmri = topo_fmri_create(mod->tm_hdl, FM_FMRI_SCHEME_MEM, 489 FM_FMRI_SCHEME_MEM, 0, args, &err)) == NULL) { 490 nvlist_free(args); 491 return (set_fmri_err(mod, err)); 492 } 493 494 nvlist_free(args); 495 496 (void) topo_mod_nvdup(mod, fmri, &nfp); 497 nvlist_free(fmri); 498 499 return (nfp); 500 501 } 502 503 nvlist_t * 504 topo_mod_pkgfmri(topo_mod_t *mod, int version, const char *path) 505 { 506 int err; 507 nvlist_t *fmri = NULL, *args = NULL; 508 nvlist_t *nfp = NULL; 509 510 if (version != FM_PKG_SCHEME_VERSION) 511 return (set_fmri_err(mod, EMOD_FMRI_VERSION)); 512 513 if (topo_mod_nvalloc(mod, &args, NV_UNIQUE_NAME) != 0) 514 return (set_fmri_err(mod, EMOD_FMRI_NVL)); 515 516 if (nvlist_add_string(args, "path", path) != 0) { 517 nvlist_free(args); 518 return (set_fmri_err(mod, EMOD_FMRI_NVL)); 519 } 520 521 if ((fmri = topo_fmri_create(mod->tm_hdl, FM_FMRI_SCHEME_PKG, 522 FM_FMRI_SCHEME_PKG, 0, args, &err)) == NULL) { 523 nvlist_free(args); 524 return (set_fmri_err(mod, err)); 525 } 526 527 nvlist_free(args); 528 529 (void) topo_mod_nvdup(mod, fmri, &nfp); 530 nvlist_free(fmri); 531 532 return (nfp); 533 } 534 535 nvlist_t * 536 topo_mod_modfmri(topo_mod_t *mod, int version, const char *driver) 537 { 538 int err; 539 nvlist_t *fmri = NULL, *args = NULL; 540 nvlist_t *nfp = NULL; 541 542 if (version != FM_MOD_SCHEME_VERSION) 543 return (set_fmri_err(mod, EMOD_FMRI_VERSION)); 544 545 if (topo_mod_nvalloc(mod, &args, NV_UNIQUE_NAME) != 0) 546 return (set_fmri_err(mod, EMOD_FMRI_NVL)); 547 548 if (nvlist_add_string(args, "DRIVER", driver) != 0) { 549 nvlist_free(args); 550 return (set_fmri_err(mod, EMOD_FMRI_NVL)); 551 } 552 553 if ((fmri = topo_fmri_create(mod->tm_hdl, FM_FMRI_SCHEME_MOD, 554 FM_FMRI_SCHEME_MOD, 0, args, &err)) == NULL) { 555 nvlist_free(args); 556 return (set_fmri_err(mod, err)); 557 } 558 559 nvlist_free(args); 560 561 (void) topo_mod_nvdup(mod, fmri, &nfp); 562 nvlist_free(fmri); 563 564 return (nfp); 565 } 566 567 #define _SWFMRI_ADD_STRING(nvl, name, val) \ 568 ((val) ? (nvlist_add_string(nvl, name, val) != 0) : 0) 569 570 nvlist_t * 571 topo_mod_swfmri(topo_mod_t *mod, int version, 572 char *obj_path, char *obj_root, nvlist_t *obj_pkg, 573 char *site_token, char *site_module, char *site_file, char *site_func, 574 int64_t site_line, char *ctxt_origin, char *ctxt_execname, 575 int64_t ctxt_pid, char *ctxt_zone, int64_t ctxt_ctid, 576 char **ctxt_stack, uint_t ctxt_stackdepth) 577 { 578 nvlist_t *fmri, *args; 579 nvlist_t *nfp = NULL; 580 int err; 581 582 if (version != FM_SW_SCHEME_VERSION) 583 return (set_fmri_err(mod, EMOD_FMRI_VERSION)); 584 585 if (topo_mod_nvalloc(mod, &args, NV_UNIQUE_NAME) != 0) 586 return (set_fmri_err(mod, EMOD_FMRI_NVL)); 587 588 err = 0; 589 err |= _SWFMRI_ADD_STRING(args, "obj_path", obj_path); 590 err |= _SWFMRI_ADD_STRING(args, "obj_root", obj_root); 591 if (obj_pkg) 592 err |= nvlist_add_nvlist(args, "obj_pkg", obj_pkg); 593 594 err |= _SWFMRI_ADD_STRING(args, "site_token", site_token); 595 err |= _SWFMRI_ADD_STRING(args, "site_module", site_module); 596 err |= _SWFMRI_ADD_STRING(args, "site_file", site_file); 597 err |= _SWFMRI_ADD_STRING(args, "site_func", site_func); 598 if (site_line != -1) 599 err |= nvlist_add_int64(args, "site_line", site_line); 600 601 err |= _SWFMRI_ADD_STRING(args, "ctxt_origin", ctxt_origin); 602 err |= _SWFMRI_ADD_STRING(args, "ctxt_execname", ctxt_execname); 603 if (ctxt_pid != -1) 604 err |= nvlist_add_int64(args, "ctxt_pid", ctxt_pid); 605 err |= _SWFMRI_ADD_STRING(args, "ctxt_zone", ctxt_zone); 606 if (ctxt_ctid != -1) 607 err |= nvlist_add_int64(args, "ctxt_ctid", ctxt_ctid); 608 if (ctxt_stack != NULL && ctxt_stackdepth != 0) 609 err |= nvlist_add_string_array(args, "stack", ctxt_stack, 610 ctxt_stackdepth); 611 612 if (err) { 613 nvlist_free(args); 614 return (set_fmri_err(mod, EMOD_FMRI_NVL)); 615 } 616 617 if ((fmri = topo_fmri_create(mod->tm_hdl, FM_FMRI_SCHEME_SW, 618 FM_FMRI_SCHEME_SW, 0, args, &err)) == NULL) { 619 nvlist_free(args); 620 return (set_fmri_err(mod, err)); 621 } 622 623 nvlist_free(args); 624 625 (void) topo_mod_nvdup(mod, fmri, &nfp); 626 nvlist_free(fmri); 627 628 return (nfp); 629 } 630 631 int 632 topo_mod_str2nvl(topo_mod_t *mod, const char *fmristr, nvlist_t **fmri) 633 { 634 int err; 635 nvlist_t *np = NULL; 636 637 if (topo_fmri_str2nvl(mod->tm_hdl, fmristr, &np, &err) < 0) 638 return (topo_mod_seterrno(mod, err)); 639 640 if (topo_mod_nvdup(mod, np, fmri) < 0) { 641 nvlist_free(np); 642 return (topo_mod_seterrno(mod, EMOD_FMRI_NVL)); 643 } 644 645 nvlist_free(np); 646 647 return (0); 648 } 649 650 int 651 topo_mod_nvl2str(topo_mod_t *mod, nvlist_t *fmri, char **fmristr) 652 { 653 int err; 654 char *sp; 655 656 if (topo_fmri_nvl2str(mod->tm_hdl, fmri, &sp, &err) < 0) 657 return (topo_mod_seterrno(mod, err)); 658 659 if ((*fmristr = topo_mod_strdup(mod, sp)) == NULL) { 660 topo_hdl_strfree(mod->tm_hdl, sp); 661 return (topo_mod_seterrno(mod, EMOD_NOMEM)); 662 } 663 664 topo_hdl_strfree(mod->tm_hdl, sp); 665 666 return (0); 667 } 668 669 void * 670 topo_mod_getspecific(topo_mod_t *mod) 671 { 672 return (mod->tm_priv); 673 } 674 675 void 676 topo_mod_setspecific(topo_mod_t *mod, void *data) 677 { 678 mod->tm_priv = data; 679 } 680 681 void 682 topo_mod_setdebug(topo_mod_t *mod) 683 { 684 mod->tm_debug = 1; 685 } 686 687 ipmi_handle_t * 688 topo_mod_ipmi_hold(topo_mod_t *mod) 689 { 690 topo_hdl_t *thp = mod->tm_hdl; 691 int err; 692 char *errmsg; 693 694 (void) pthread_mutex_lock(&thp->th_ipmi_lock); 695 if (thp->th_ipmi == NULL) { 696 if ((thp->th_ipmi = ipmi_open(&err, &errmsg, IPMI_TRANSPORT_BMC, 697 NULL)) == NULL) { 698 topo_dprintf(mod->tm_hdl, TOPO_DBG_ERR, 699 "ipmi_open() failed: %s (ipmi errno=%d)", errmsg, 700 err); 701 (void) pthread_mutex_unlock(&thp->th_ipmi_lock); 702 } 703 } 704 705 706 return (thp->th_ipmi); 707 } 708 709 void 710 topo_mod_ipmi_rele(topo_mod_t *mod) 711 { 712 topo_hdl_t *thp = mod->tm_hdl; 713 714 (void) pthread_mutex_unlock(&thp->th_ipmi_lock); 715 } 716 717 di_node_t 718 topo_mod_devinfo(topo_mod_t *mod) 719 { 720 return (topo_hdl_devinfo(mod->tm_hdl)); 721 } 722 723 smbios_hdl_t * 724 topo_mod_smbios(topo_mod_t *mod) 725 { 726 topo_hdl_t *thp = mod->tm_hdl; 727 728 if (thp->th_smbios == NULL) 729 thp->th_smbios = smbios_open(NULL, SMB_VERSION, 0, NULL); 730 731 return (thp->th_smbios); 732 } 733 734 di_prom_handle_t 735 topo_mod_prominfo(topo_mod_t *mod) 736 { 737 return (topo_hdl_prominfo(mod->tm_hdl)); 738 } 739 740 pcidb_hdl_t * 741 topo_mod_pcidb(topo_mod_t *mod) 742 { 743 topo_hdl_t *thp = mod->tm_hdl; 744 745 if (thp->th_pcidb == NULL) 746 thp->th_pcidb = pcidb_open(PCIDB_VERSION); 747 748 return (thp->th_pcidb); 749 } 750 751 void 752 topo_mod_clrdebug(topo_mod_t *mod) 753 { 754 mod->tm_debug = 0; 755 } 756 757 /*PRINTFLIKE2*/ 758 void 759 topo_mod_dprintf(topo_mod_t *mod, const char *format, ...) 760 { 761 topo_hdl_t *thp = mod->tm_hdl; 762 va_list alist; 763 764 if (mod->tm_debug == 0 || !(thp->th_debug & TOPO_DBG_MOD)) 765 return; 766 767 va_start(alist, format); 768 topo_vdprintf(mod->tm_hdl, (const char *)mod->tm_name, format, alist); 769 va_end(alist); 770 } 771 772 char * 773 topo_mod_product(topo_mod_t *mod) 774 { 775 return (topo_mod_strdup(mod, mod->tm_hdl->th_product)); 776 } 777 778 static char * 779 topo_mod_server(topo_mod_t *mod) 780 { 781 static struct utsname uts; 782 783 (void) uname(&uts); 784 return (topo_mod_strdup(mod, uts.nodename)); 785 } 786 787 static char * 788 topo_mod_psn(topo_mod_t *mod) 789 { 790 smbios_hdl_t *shp; 791 const char *psn; 792 793 if ((shp = topo_mod_smbios(mod)) == NULL || 794 (psn = smbios_psn(shp)) == NULL) 795 return (NULL); 796 797 return (topo_cleanup_auth_str(mod->tm_hdl, psn)); 798 } 799 800 static char * 801 topo_mod_csn(topo_mod_t *mod) 802 { 803 char csn[MAXNAMELEN]; 804 smbios_hdl_t *shp; 805 di_prom_handle_t promh = DI_PROM_HANDLE_NIL; 806 di_node_t rooth = DI_NODE_NIL; 807 const char *bufp; 808 809 if ((shp = topo_mod_smbios(mod)) != NULL) { 810 bufp = smbios_csn(shp); 811 if (bufp != NULL) 812 (void) strlcpy(csn, bufp, MAXNAMELEN); 813 else 814 return (NULL); 815 } else if ((rooth = topo_mod_devinfo(mod)) != DI_NODE_NIL && 816 (promh = topo_mod_prominfo(mod)) != DI_PROM_HANDLE_NIL) { 817 if (di_prom_prop_lookup_bytes(promh, rooth, "chassis-sn", 818 (unsigned char **)&bufp) != -1) { 819 (void) strlcpy(csn, bufp, MAXNAMELEN); 820 } else { 821 return (NULL); 822 } 823 } else { 824 return (NULL); 825 } 826 827 return (topo_cleanup_auth_str(mod->tm_hdl, csn)); 828 } 829 830 nvlist_t * 831 topo_mod_auth(topo_mod_t *mod, tnode_t *pnode) 832 { 833 int err; 834 char *prod = NULL; 835 char *csn = NULL; 836 char *psn = NULL; 837 char *server = NULL; 838 nvlist_t *auth; 839 840 if ((err = topo_mod_nvalloc(mod, &auth, NV_UNIQUE_NAME)) != 0) { 841 (void) topo_mod_seterrno(mod, EMOD_FMRI_NVL); 842 return (NULL); 843 } 844 845 (void) topo_prop_get_string(pnode, FM_FMRI_AUTHORITY, 846 FM_FMRI_AUTH_PRODUCT, &prod, &err); 847 (void) topo_prop_get_string(pnode, FM_FMRI_AUTHORITY, 848 FM_FMRI_AUTH_PRODUCT_SN, &psn, &err); 849 (void) topo_prop_get_string(pnode, FM_FMRI_AUTHORITY, 850 FM_FMRI_AUTH_CHASSIS, &csn, &err); 851 (void) topo_prop_get_string(pnode, FM_FMRI_AUTHORITY, 852 FM_FMRI_AUTH_SERVER, &server, &err); 853 854 /* 855 * Let's do this the hard way 856 */ 857 if (prod == NULL) 858 prod = topo_mod_product(mod); 859 if (csn == NULL) 860 csn = topo_mod_csn(mod); 861 if (psn == NULL) 862 psn = topo_mod_psn(mod); 863 if (server == NULL) { 864 server = topo_mod_server(mod); 865 } 866 867 /* 868 * No luck, return NULL 869 */ 870 if (!prod && !server && !csn && !psn) { 871 nvlist_free(auth); 872 return (NULL); 873 } 874 875 err = 0; 876 if (prod != NULL) { 877 err |= nvlist_add_string(auth, FM_FMRI_AUTH_PRODUCT, prod); 878 topo_mod_strfree(mod, prod); 879 } 880 if (psn != NULL) { 881 err |= nvlist_add_string(auth, FM_FMRI_AUTH_PRODUCT_SN, psn); 882 topo_mod_strfree(mod, psn); 883 } 884 if (server != NULL) { 885 err |= nvlist_add_string(auth, FM_FMRI_AUTH_SERVER, server); 886 topo_mod_strfree(mod, server); 887 } 888 if (csn != NULL) { 889 err |= nvlist_add_string(auth, FM_FMRI_AUTH_CHASSIS, csn); 890 topo_mod_strfree(mod, csn); 891 } 892 893 if (err != 0) { 894 nvlist_free(auth); 895 (void) topo_mod_seterrno(mod, EMOD_NVL_INVAL); 896 return (NULL); 897 } 898 899 return (auth); 900 } 901 902 topo_walk_t * 903 topo_mod_walk_init(topo_mod_t *mod, tnode_t *node, topo_mod_walk_cb_t cb_f, 904 void *pdata, int *errp) 905 { 906 topo_walk_t *wp; 907 topo_hdl_t *thp = mod->tm_hdl; 908 909 if ((wp = topo_node_walk_init(thp, mod, node, (int (*)())cb_f, pdata, 910 errp)) == NULL) 911 return (NULL); 912 913 return (wp); 914 } 915 916 char * 917 topo_mod_clean_str(topo_mod_t *mod, const char *str) 918 { 919 if (str == NULL) 920 return (NULL); 921 922 return (topo_cleanup_strn(mod->tm_hdl, str, strlen(str))); 923 } 924 925 char * 926 topo_mod_clean_strn(topo_mod_t *mod, const char *str, size_t len) 927 { 928 if (str == NULL) 929 return (NULL); 930 931 return (topo_cleanup_strn(mod->tm_hdl, str, len)); 932 } 933 934 int 935 topo_mod_file_search(topo_mod_t *mod, const char *file, int oflags) 936 { 937 int ret; 938 char *path; 939 topo_hdl_t *thp = mod->tm_hdl; 940 941 path = topo_search_path(mod, thp->th_rootdir, file); 942 if (path == NULL) { 943 return (-1); 944 } 945 946 ret = open(path, oflags); 947 topo_mod_strfree(mod, path); 948 return (ret); 949 } 950 951 /*ARGSUSED*/ 952 int 953 topo_mod_hc_occupied(topo_mod_t *mod, tnode_t *node, topo_version_t version, 954 nvlist_t *in, nvlist_t **out) 955 { 956 nvlist_t *nvl = NULL; 957 tnode_t *cnp; 958 boolean_t is_occupied = B_FALSE; 959 960 if (version > TOPO_METH_OCCUPIED_VERSION) 961 return (topo_mod_seterrno(mod, ETOPO_METHOD_VERNEW)); 962 963 /* 964 * Iterate though the child nodes. If there are no non-facility 965 * node children then it is unoccupied. 966 */ 967 for (cnp = topo_child_first(node); cnp != NULL; 968 cnp = topo_child_next(node, cnp)) { 969 if (topo_node_flags(cnp) != TOPO_NODE_FACILITY) 970 is_occupied = B_TRUE; 971 } 972 973 if (topo_mod_nvalloc(mod, &nvl, NV_UNIQUE_NAME) != 0 || 974 nvlist_add_boolean_value(nvl, TOPO_METH_OCCUPIED_RET, 975 is_occupied) != 0) { 976 topo_mod_dprintf(mod, "Failed to allocate 'out' nvlist\n"); 977 nvlist_free(nvl); 978 return (topo_mod_seterrno(mod, EMOD_NOMEM)); 979 } 980 *out = nvl; 981 982 return (0); 983 } 984 985 /* 986 * Convenience routine for creating a UFM slot node. This routine assumes 987 * that the caller has already created the containing range via a call to 988 * topo_node_range_create(). 989 */ 990 tnode_t * 991 topo_mod_create_ufm_slot(topo_mod_t *mod, tnode_t *ufmnode, 992 topo_ufm_slot_info_t *slotinfo) 993 { 994 nvlist_t *auth = NULL, *fmri = NULL; 995 tnode_t *slotnode; 996 topo_pgroup_info_t pgi; 997 int err, rc; 998 999 if (slotinfo == NULL || slotinfo->usi_mode == 0) { 1000 topo_mod_dprintf(mod, "invalid slot info"); 1001 (void) topo_mod_seterrno(mod, ETOPO_MOD_INVAL); 1002 return (NULL); 1003 } 1004 if ((auth = topo_mod_auth(mod, ufmnode)) == NULL) { 1005 topo_mod_dprintf(mod, "topo_mod_auth() failed: %s", 1006 topo_mod_errmsg(mod)); 1007 /* errno set */ 1008 return (NULL); 1009 } 1010 1011 if ((fmri = topo_mod_hcfmri(mod, ufmnode, FM_HC_SCHEME_VERSION, 1012 SLOT, slotinfo->usi_slotid, NULL, auth, NULL, NULL, NULL)) == 1013 NULL) { 1014 nvlist_free(auth); 1015 topo_mod_dprintf(mod, "topo_mod_hcfmri() failed: %s", 1016 topo_mod_errmsg(mod)); 1017 /* errno set */ 1018 return (NULL); 1019 } 1020 1021 if ((slotnode = topo_node_bind(mod, ufmnode, SLOT, 1022 slotinfo->usi_slotid, fmri)) == NULL) { 1023 nvlist_free(auth); 1024 nvlist_free(fmri); 1025 topo_mod_dprintf(mod, "topo_node_bind() failed: %s", 1026 topo_mod_errmsg(mod)); 1027 /* errno set */ 1028 return (NULL); 1029 } 1030 1031 /* Create authority and system pgroups */ 1032 topo_pgroup_hcset(slotnode, auth); 1033 nvlist_free(auth); 1034 nvlist_free(fmri); 1035 1036 /* Just inherit the parent's FRU */ 1037 if (topo_node_fru_set(slotnode, NULL, 0, &err) != 0) { 1038 topo_mod_dprintf(mod, "failed to set FRU on %s: %s", UFM, 1039 topo_strerror(err)); 1040 (void) topo_mod_seterrno(mod, err); 1041 goto slotfail; 1042 } 1043 1044 pgi.tpi_name = TOPO_PGROUP_SLOT; 1045 pgi.tpi_namestab = TOPO_STABILITY_PRIVATE; 1046 pgi.tpi_datastab = TOPO_STABILITY_PRIVATE; 1047 pgi.tpi_version = TOPO_VERSION; 1048 rc = topo_pgroup_create(slotnode, &pgi, &err); 1049 1050 if (rc == 0) 1051 rc += topo_prop_set_uint32(slotnode, TOPO_PGROUP_SLOT, 1052 TOPO_PROP_SLOT_TYPE, TOPO_PROP_IMMUTABLE, 1053 TOPO_SLOT_TYPE_UFM, &err); 1054 1055 pgi.tpi_name = TOPO_PGROUP_UFM_SLOT; 1056 1057 if (rc == 0) 1058 rc += topo_pgroup_create(slotnode, &pgi, &err); 1059 1060 if (rc == 0) { 1061 rc += topo_prop_set_uint32(slotnode, TOPO_PGROUP_UFM_SLOT, 1062 TOPO_PROP_UFM_SLOT_MODE, TOPO_PROP_IMMUTABLE, 1063 slotinfo->usi_mode, &err); 1064 } 1065 1066 if (rc == 0) { 1067 rc += topo_prop_set_uint32(slotnode, TOPO_PGROUP_UFM_SLOT, 1068 TOPO_PROP_UFM_SLOT_ACTIVE, TOPO_PROP_IMMUTABLE, 1069 (uint32_t)slotinfo->usi_active, &err); 1070 } 1071 1072 /* 1073 * We can have a NULL version for an empty slot. 1074 */ 1075 if (rc == 0 && slotinfo->usi_version != NULL) { 1076 rc += topo_prop_set_string(slotnode, TOPO_PGROUP_UFM_SLOT, 1077 TOPO_PROP_UFM_SLOT_VERSION, TOPO_PROP_IMMUTABLE, 1078 slotinfo->usi_version, &err); 1079 } 1080 1081 if (rc == 0 && slotinfo->usi_extra != NULL) { 1082 nvpair_t *elem = NULL; 1083 char *pname, *pval; 1084 1085 while ((elem = nvlist_next_nvpair(slotinfo->usi_extra, 1086 elem)) != NULL) { 1087 if (nvpair_type(elem) != DATA_TYPE_STRING) 1088 continue; 1089 1090 pname = nvpair_name(elem); 1091 if ((rc -= nvpair_value_string(elem, &pval)) != 0) 1092 break; 1093 1094 rc += topo_prop_set_string(slotnode, 1095 TOPO_PGROUP_UFM_SLOT, pname, TOPO_PROP_IMMUTABLE, 1096 pval, &err); 1097 1098 if (rc != 0) 1099 break; 1100 } 1101 } 1102 1103 if (rc != 0) { 1104 topo_mod_dprintf(mod, "error setting properties on %s node", 1105 SLOT); 1106 (void) topo_mod_seterrno(mod, err); 1107 goto slotfail; 1108 } 1109 return (slotnode); 1110 1111 slotfail: 1112 topo_node_unbind(slotnode); 1113 return (NULL); 1114 } 1115 1116 /* 1117 * This is a convenience routine to allow enumerator modules to easily create 1118 * the necessary UFM node layout for the most common case, which will be a 1119 * single UFM with a single slot. This routine assumes that the caller has 1120 * already created the containing range via a call to topo_node_range_create(). 1121 * 1122 * For more complex scenarios (like multiple slots per UFM), callers can set 1123 * the slotinfo param to NULL. In this case the ufm node will get created, but 1124 * it will skip creating the slot node - allowing the module to manually call 1125 * topo_mod_create_ufm_slot() to create custom UFM slots. 1126 */ 1127 tnode_t * 1128 topo_mod_create_ufm(topo_mod_t *mod, tnode_t *parent, topo_instance_t inst, 1129 const char *descr, topo_ufm_slot_info_t *slotinfo) 1130 { 1131 nvlist_t *auth = NULL, *fmri = NULL; 1132 tnode_t *ufmnode, *slotnode; 1133 topo_pgroup_info_t pgi; 1134 int err, rc; 1135 1136 if ((auth = topo_mod_auth(mod, parent)) == NULL) { 1137 topo_mod_dprintf(mod, "topo_mod_auth() failed: %s", 1138 topo_mod_errmsg(mod)); 1139 /* errno set */ 1140 return (NULL); 1141 } 1142 1143 if ((fmri = topo_mod_hcfmri(mod, parent, FM_HC_SCHEME_VERSION, 1144 UFM, inst, NULL, auth, NULL, NULL, NULL)) == 1145 NULL) { 1146 nvlist_free(auth); 1147 topo_mod_dprintf(mod, "topo_mod_hcfmri() failed: %s", 1148 topo_mod_errmsg(mod)); 1149 /* errno set */ 1150 return (NULL); 1151 } 1152 1153 if ((ufmnode = topo_node_bind(mod, parent, UFM, inst, fmri)) == NULL) { 1154 nvlist_free(auth); 1155 nvlist_free(fmri); 1156 topo_mod_dprintf(mod, "topo_node_bind() failed: %s", 1157 topo_mod_errmsg(mod)); 1158 /* errno set */ 1159 return (NULL); 1160 } 1161 1162 /* Create authority and system pgroups */ 1163 topo_pgroup_hcset(ufmnode, auth); 1164 nvlist_free(auth); 1165 nvlist_free(fmri); 1166 1167 /* Just inherit the parent's FRU */ 1168 if (topo_node_fru_set(ufmnode, NULL, 0, &err) != 0) { 1169 topo_mod_dprintf(mod, "failed to set FRU on %s: %s", UFM, 1170 topo_strerror(err)); 1171 (void) topo_mod_seterrno(mod, err); 1172 goto ufmfail; 1173 } 1174 1175 pgi.tpi_name = TOPO_PGROUP_UFM; 1176 pgi.tpi_namestab = TOPO_STABILITY_PRIVATE; 1177 pgi.tpi_datastab = TOPO_STABILITY_PRIVATE; 1178 pgi.tpi_version = TOPO_VERSION; 1179 rc = topo_pgroup_create(ufmnode, &pgi, &err); 1180 1181 if (rc == 0) 1182 rc += topo_prop_set_string(ufmnode, TOPO_PGROUP_UFM, 1183 TOPO_PROP_UFM_DESCR, TOPO_PROP_IMMUTABLE, descr, &err); 1184 1185 if (rc != 0) { 1186 topo_mod_dprintf(mod, "error setting properties on %s node", 1187 UFM); 1188 (void) topo_mod_seterrno(mod, err); 1189 goto ufmfail; 1190 } 1191 1192 if (slotinfo != NULL) { 1193 if (topo_node_range_create(mod, ufmnode, SLOT, 0, 0) < 0) { 1194 topo_mod_dprintf(mod, "error creating %s range", SLOT); 1195 goto ufmfail; 1196 } 1197 slotnode = topo_mod_create_ufm_slot(mod, ufmnode, slotinfo); 1198 1199 if (slotnode == NULL) 1200 goto ufmfail; 1201 } 1202 return (ufmnode); 1203 1204 ufmfail: 1205 topo_node_unbind(ufmnode); 1206 return (NULL); 1207 } 1208