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