xref: /illumos-gate/usr/src/lib/fm/topo/libtopo/common/topo_mod.c (revision fc910014e8a32a65612105835a10995f2c13d942)
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