xref: /illumos-gate/usr/src/lib/fm/topo/libtopo/common/topo_mod.c (revision 84ceaea936ebcf122d4f0756d298adf307fd491d)
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 #if !defined(_ILP32)
115 #define	PLUGIN_PATH	"plugins/64"
116 #else
117 #define	PLUGIN_PATH	"plugins"
118 #endif
119 #define	PLUGIN_PATH_LEN	MAXNAMELEN + 5
120 
121 topo_mod_t *
topo_mod_load(topo_mod_t * pmod,const char * name,topo_version_t version)122 topo_mod_load(topo_mod_t *pmod, const char *name,
123     topo_version_t version)
124 {
125 	char *path;
126 	char file[PLUGIN_PATH_LEN];
127 	topo_mod_t *mod = NULL;
128 	topo_hdl_t *thp;
129 
130 	thp = pmod->tm_hdl;
131 
132 	/*
133 	 * Already loaded, topo_mod_lookup will bump the ref count
134 	 */
135 	if ((mod = topo_mod_lookup(thp, name, 1)) != NULL) {
136 		if (mod->tm_info->tmi_version != version) {
137 			topo_mod_rele(mod);
138 			(void) topo_mod_seterrno(pmod, ETOPO_MOD_VER);
139 			return (NULL);
140 		}
141 		return (mod);
142 	}
143 
144 	(void) snprintf(file, PLUGIN_PATH_LEN, "%s/%s.so",
145 	    PLUGIN_PATH, name);
146 	path = topo_search_path(pmod, thp->th_rootdir, (const char *)file);
147 	if (path == NULL ||
148 	    (mod = topo_modhash_load(thp, name, path, &topo_rtld_ops, version))
149 	    == NULL) { /* returned with mod held */
150 			topo_mod_strfree(pmod, path);
151 			(void) topo_mod_seterrno(pmod, topo_hdl_errno(thp) ?
152 			    topo_hdl_errno(thp) : ETOPO_MOD_NOENT);
153 			return (NULL);
154 	}
155 
156 	topo_mod_strfree(pmod, path);
157 
158 	return (mod);
159 }
160 
161 void
topo_mod_unload(topo_mod_t * mod)162 topo_mod_unload(topo_mod_t *mod)
163 {
164 	topo_mod_rele(mod);
165 }
166 
167 static int
set_register_error(topo_mod_t * mod,int err)168 set_register_error(topo_mod_t *mod, int err)
169 {
170 	if (mod->tm_info != NULL)
171 		topo_mod_unregister(mod);
172 
173 	topo_dprintf(mod->tm_hdl, TOPO_DBG_ERR,
174 	    "module registration failed for %s: %s\n",
175 	    mod->tm_name, topo_strerror(err));
176 
177 	return (topo_mod_seterrno(mod, err));
178 }
179 
180 int
topo_mod_register(topo_mod_t * mod,const topo_modinfo_t * mip,topo_version_t version)181 topo_mod_register(topo_mod_t *mod, const topo_modinfo_t *mip,
182     topo_version_t version)
183 {
184 
185 	assert(!(mod->tm_flags & TOPO_MOD_FINI ||
186 	    mod->tm_flags & TOPO_MOD_REG));
187 
188 	if (version != TOPO_VERSION)
189 		return (set_register_error(mod, EMOD_VER_ABI));
190 
191 	if ((mod->tm_info = topo_mod_zalloc(mod, sizeof (topo_imodinfo_t)))
192 	    == NULL)
193 		return (set_register_error(mod, EMOD_NOMEM));
194 	if ((mod->tm_info->tmi_ops = topo_mod_alloc(mod,
195 	    sizeof (topo_modops_t))) == NULL)
196 		return (set_register_error(mod, EMOD_NOMEM));
197 
198 	mod->tm_info->tmi_desc = topo_mod_strdup(mod, mip->tmi_desc);
199 	if (mod->tm_info->tmi_desc == NULL)
200 		return (set_register_error(mod, EMOD_NOMEM));
201 
202 	mod->tm_info->tmi_scheme = topo_mod_strdup(mod, mip->tmi_scheme);
203 	if (mod->tm_info->tmi_scheme == NULL)
204 		return (set_register_error(mod, EMOD_NOMEM));
205 
206 
207 	mod->tm_info->tmi_version = (topo_version_t)mip->tmi_version;
208 	mod->tm_info->tmi_ops->tmo_enum = mip->tmi_ops->tmo_enum;
209 	mod->tm_info->tmi_ops->tmo_release = mip->tmi_ops->tmo_release;
210 
211 	mod->tm_flags |= TOPO_MOD_REG;
212 
213 	topo_dprintf(mod->tm_hdl, TOPO_DBG_MODSVC,
214 	    "registration succeeded for %s\n", mod->tm_name);
215 
216 	return (0);
217 }
218 
219 void
topo_mod_unregister(topo_mod_t * mod)220 topo_mod_unregister(topo_mod_t *mod)
221 {
222 	if (mod->tm_info == NULL)
223 		return;
224 
225 	assert(!(mod->tm_flags & TOPO_MOD_FINI));
226 
227 	mod->tm_flags &= ~TOPO_MOD_REG;
228 
229 	if (mod->tm_info == NULL)
230 		return;
231 
232 	if (mod->tm_info->tmi_ops != NULL)
233 		topo_mod_free(mod, mod->tm_info->tmi_ops,
234 		    sizeof (topo_modops_t));
235 	if (mod->tm_info->tmi_desc != NULL)
236 		topo_mod_strfree(mod, mod->tm_info->tmi_desc);
237 	if (mod->tm_info->tmi_scheme != NULL)
238 		topo_mod_strfree(mod, mod->tm_info->tmi_scheme);
239 
240 	topo_mod_free(mod, mod->tm_info, sizeof (topo_imodinfo_t));
241 
242 	mod->tm_info = NULL;
243 }
244 
245 int
topo_mod_enumerate(topo_mod_t * mod,tnode_t * node,const char * enum_name,const char * name,topo_instance_t min,topo_instance_t max,void * data)246 topo_mod_enumerate(topo_mod_t *mod, tnode_t *node, const char *enum_name,
247     const char *name, topo_instance_t min, topo_instance_t max, void *data)
248 {
249 	int err = 0;
250 	topo_mod_t *enum_mod;
251 
252 	assert(mod->tm_flags & TOPO_MOD_REG);
253 
254 	if ((enum_mod = topo_mod_lookup(mod->tm_hdl, enum_name, 0)) == NULL)
255 		return (topo_mod_seterrno(mod, EMOD_MOD_NOENT));
256 
257 	topo_node_hold(node);
258 
259 	topo_dprintf(mod->tm_hdl, TOPO_DBG_MODSVC, "module %s enumerating "
260 	    "node %s=%d\n", (char *)mod->tm_name, (char *)node->tn_name,
261 	    node->tn_instance);
262 
263 	topo_mod_enter(enum_mod);
264 	err = enum_mod->tm_info->tmi_ops->tmo_enum(enum_mod, node, name, min,
265 	    max, enum_mod->tm_priv, data);
266 	topo_mod_exit(enum_mod);
267 
268 	if (err != 0) {
269 		(void) topo_mod_seterrno(mod, EMOD_UKNOWN_ENUM);
270 
271 		topo_dprintf(mod->tm_hdl, TOPO_DBG_ERR,
272 		    "module %s failed enumeration for "
273 		    " node %s=%d\n", (char *)mod->tm_name,
274 		    (char *)node->tn_name, node->tn_instance);
275 
276 		topo_node_rele(node);
277 		return (-1);
278 	}
279 
280 	topo_node_rele(node);
281 
282 	return (0);
283 }
284 
285 int
topo_mod_enummap(topo_mod_t * mod,tnode_t * node,const char * name,const char * scheme)286 topo_mod_enummap(topo_mod_t *mod, tnode_t *node, const char *name,
287     const char *scheme)
288 {
289 	return (topo_file_load(mod, node, (char *)name, (char *)scheme, 0));
290 }
291 
292 static nvlist_t *
set_fmri_err(topo_mod_t * mod,int err)293 set_fmri_err(topo_mod_t *mod, int err)
294 {
295 	(void) topo_mod_seterrno(mod, err);
296 	return (NULL);
297 }
298 
299 nvlist_t *
topo_mod_hcfmri(topo_mod_t * mod,tnode_t * pnode,int version,const char * name,topo_instance_t inst,nvlist_t * hc_specific,nvlist_t * auth,const char * part,const char * rev,const char * serial)300 topo_mod_hcfmri(topo_mod_t *mod, tnode_t *pnode, int version, const char *name,
301     topo_instance_t inst, nvlist_t *hc_specific, nvlist_t *auth,
302     const char *part, const char *rev, const char *serial)
303 {
304 	int err;
305 	nvlist_t *pfmri = NULL, *fmri = NULL, *args = NULL;
306 	nvlist_t *nfp = NULL;
307 	char *lpart, *lrev, *lserial;
308 
309 	if (version != FM_HC_SCHEME_VERSION)
310 		return (set_fmri_err(mod, EMOD_FMRI_VERSION));
311 
312 	/*
313 	 * Do we have any args to pass?
314 	 */
315 	if (pnode != NULL || auth != NULL || part != NULL || rev != NULL ||
316 	    serial != NULL || hc_specific != NULL) {
317 		if (topo_mod_nvalloc(mod, &args, NV_UNIQUE_NAME) != 0)
318 			return (set_fmri_err(mod, EMOD_FMRI_NVL));
319 	}
320 
321 	if (pnode != NULL) {
322 		if (topo_node_resource(pnode, &pfmri, &err) < 0) {
323 			nvlist_free(args);
324 			return (set_fmri_err(mod, EMOD_NVL_INVAL));
325 		}
326 
327 		if (nvlist_add_nvlist(args, TOPO_METH_FMRI_ARG_PARENT,
328 		    pfmri) != 0) {
329 			nvlist_free(pfmri);
330 			nvlist_free(args);
331 			return (set_fmri_err(mod, EMOD_FMRI_NVL));
332 		}
333 		nvlist_free(pfmri);
334 	}
335 
336 	/*
337 	 * Add optional payload
338 	 */
339 	if (auth != NULL)
340 		(void) nvlist_add_nvlist(args, TOPO_METH_FMRI_ARG_AUTH, auth);
341 	if (part != NULL) {
342 		lpart = topo_cleanup_auth_str(mod->tm_hdl, part);
343 		if (lpart != NULL) {
344 			(void) nvlist_add_string(args, TOPO_METH_FMRI_ARG_PART,
345 			    lpart);
346 			topo_hdl_free(mod->tm_hdl, lpart, strlen(lpart) + 1);
347 		} else {
348 			(void) nvlist_add_string(args, TOPO_METH_FMRI_ARG_PART,
349 			    "");
350 		}
351 	}
352 	if (rev != NULL) {
353 		lrev = topo_cleanup_auth_str(mod->tm_hdl, rev);
354 		if (lrev != NULL) {
355 			(void) nvlist_add_string(args, TOPO_METH_FMRI_ARG_REV,
356 			    lrev);
357 			topo_hdl_free(mod->tm_hdl, lrev, strlen(lrev) + 1);
358 		} else {
359 			(void) nvlist_add_string(args, TOPO_METH_FMRI_ARG_REV,
360 			    "");
361 		}
362 	}
363 	if (serial != NULL) {
364 		lserial = topo_cleanup_auth_str(mod->tm_hdl, serial);
365 		if (lserial != NULL) {
366 			(void) nvlist_add_string(args, TOPO_METH_FMRI_ARG_SER,
367 			    lserial);
368 			topo_hdl_free(mod->tm_hdl, lserial,
369 			    strlen(lserial) + 1);
370 		} else {
371 			(void) nvlist_add_string(args, TOPO_METH_FMRI_ARG_SER,
372 			    "");
373 		}
374 	}
375 	if (hc_specific != NULL)
376 		(void) nvlist_add_nvlist(args, TOPO_METH_FMRI_ARG_HCS,
377 		    hc_specific);
378 
379 	if ((fmri = topo_fmri_create(mod->tm_hdl, FM_FMRI_SCHEME_HC, name, inst,
380 	    args, &err)) == NULL) {
381 		nvlist_free(args);
382 		return (set_fmri_err(mod, err));
383 	}
384 
385 	nvlist_free(args);
386 
387 	(void) topo_mod_nvdup(mod, fmri, &nfp);
388 	nvlist_free(fmri);
389 
390 	return (nfp);
391 }
392 
393 nvlist_t *
topo_mod_hcfmri_extend(topo_mod_t * mod,nvlist_t * pfmri,int version,const char * name,topo_instance_t inst)394 topo_mod_hcfmri_extend(topo_mod_t *mod, nvlist_t *pfmri, int version,
395     const char *name, topo_instance_t inst)
396 {
397 	nvlist_t *fmri = NULL, *args = NULL, *nfp = NULL;
398 	int err;
399 
400 	if (version != FM_HC_SCHEME_VERSION)
401 		return (set_fmri_err(mod, EMOD_FMRI_VERSION));
402 
403 	if (pfmri == NULL)
404 		return (set_fmri_err(mod, EMOD_NVL_INVAL));
405 
406 	if (topo_mod_nvalloc(mod, &args, NV_UNIQUE_NAME) != 0)
407 		return (set_fmri_err(mod, EMOD_FMRI_NVL));
408 
409 	if (nvlist_add_nvlist(args, TOPO_METH_FMRI_ARG_PARENT, pfmri) != 0) {
410 		nvlist_free(args);
411 		return (set_fmri_err(mod, EMOD_FMRI_NVL));
412 	}
413 
414 	if ((fmri = topo_fmri_create(mod->tm_hdl, FM_FMRI_SCHEME_HC, name, inst,
415 	    args, &err)) == NULL) {
416 		nvlist_free(args);
417 		return (set_fmri_err(mod, err));
418 	}
419 	nvlist_free(args);
420 
421 	(void) topo_mod_nvdup(mod, fmri, &nfp);
422 	nvlist_free(fmri);
423 
424 	return (nfp);
425 }
426 
427 nvlist_t *
topo_mod_devfmri(topo_mod_t * mod,int version,const char * dev_path,const char * devid)428 topo_mod_devfmri(topo_mod_t *mod, int version, const char *dev_path,
429     const char *devid)
430 {
431 	int err;
432 	nvlist_t *fmri, *args;
433 	nvlist_t *nfp = NULL;
434 
435 	if (version != FM_DEV_SCHEME_VERSION)
436 		return (set_fmri_err(mod, EMOD_FMRI_VERSION));
437 
438 	if (topo_mod_nvalloc(mod, &args, NV_UNIQUE_NAME) != 0)
439 		return (set_fmri_err(mod, EMOD_FMRI_NVL));
440 
441 	if (nvlist_add_string(args, FM_FMRI_DEV_PATH, dev_path) != 0) {
442 		nvlist_free(args);
443 		return (set_fmri_err(mod, EMOD_FMRI_NVL));
444 	}
445 
446 	(void) nvlist_add_string(args, FM_FMRI_DEV_ID, devid);
447 
448 	if ((fmri = topo_fmri_create(mod->tm_hdl, FM_FMRI_SCHEME_DEV,
449 	    FM_FMRI_SCHEME_DEV, 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 *
topo_mod_cpufmri(topo_mod_t * mod,int version,uint32_t cpu_id,uint8_t cpumask,const char * serial)463 topo_mod_cpufmri(topo_mod_t *mod, int version, uint32_t cpu_id, uint8_t cpumask,
464     const char *serial)
465 {
466 	int err;
467 	nvlist_t *fmri = NULL, *args = NULL;
468 	nvlist_t *nfp = NULL;
469 
470 	if (version != FM_CPU_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 	if (nvlist_add_uint32(args, FM_FMRI_CPU_ID, cpu_id) != 0) {
477 		nvlist_free(args);
478 		return (set_fmri_err(mod, EMOD_FMRI_NVL));
479 	}
480 
481 	/*
482 	 * Add optional payload
483 	 */
484 	(void) nvlist_add_uint8(args, FM_FMRI_CPU_MASK, cpumask);
485 	(void) nvlist_add_string(args, FM_FMRI_CPU_SERIAL_ID, serial);
486 
487 	if ((fmri = topo_fmri_create(mod->tm_hdl, FM_FMRI_SCHEME_CPU,
488 	    FM_FMRI_SCHEME_CPU, 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 nvlist_t *
topo_mod_memfmri(topo_mod_t * mod,int version,uint64_t pa,uint64_t offset,const char * unum,int flags)502 topo_mod_memfmri(topo_mod_t *mod, int version, uint64_t pa, uint64_t offset,
503     const char *unum, int flags)
504 {
505 	int err;
506 	nvlist_t *args = NULL, *fmri = NULL;
507 	nvlist_t *nfp = NULL;
508 
509 	if (version != FM_MEM_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 	err = nvlist_add_string(args, FM_FMRI_MEM_UNUM, unum);
516 	if (flags & TOPO_MEMFMRI_PA)
517 		err |= nvlist_add_uint64(args, FM_FMRI_MEM_PHYSADDR, pa);
518 	if (flags & TOPO_MEMFMRI_OFFSET)
519 		err |= nvlist_add_uint64(args, FM_FMRI_MEM_OFFSET, offset);
520 
521 	if (err != 0) {
522 		nvlist_free(args);
523 		return (set_fmri_err(mod, EMOD_FMRI_NVL));
524 	}
525 
526 	if ((fmri = topo_fmri_create(mod->tm_hdl, FM_FMRI_SCHEME_MEM,
527 	    FM_FMRI_SCHEME_MEM, 0, args, &err)) == NULL) {
528 		nvlist_free(args);
529 		return (set_fmri_err(mod, err));
530 	}
531 
532 	nvlist_free(args);
533 
534 	(void) topo_mod_nvdup(mod, fmri, &nfp);
535 	nvlist_free(fmri);
536 
537 	return (nfp);
538 
539 }
540 
541 nvlist_t *
topo_mod_pkgfmri(topo_mod_t * mod,int version,const char * path)542 topo_mod_pkgfmri(topo_mod_t *mod, int version, const char *path)
543 {
544 	int err;
545 	nvlist_t *fmri = NULL, *args = NULL;
546 	nvlist_t *nfp = NULL;
547 
548 	if (version != FM_PKG_SCHEME_VERSION)
549 		return (set_fmri_err(mod, EMOD_FMRI_VERSION));
550 
551 	if (topo_mod_nvalloc(mod, &args, NV_UNIQUE_NAME) != 0)
552 		return (set_fmri_err(mod, EMOD_FMRI_NVL));
553 
554 	if (nvlist_add_string(args, "path", path) != 0) {
555 		nvlist_free(args);
556 		return (set_fmri_err(mod, EMOD_FMRI_NVL));
557 	}
558 
559 	if ((fmri = topo_fmri_create(mod->tm_hdl, FM_FMRI_SCHEME_PKG,
560 	    FM_FMRI_SCHEME_PKG, 0, args, &err)) == NULL) {
561 		nvlist_free(args);
562 		return (set_fmri_err(mod, err));
563 	}
564 
565 	nvlist_free(args);
566 
567 	(void) topo_mod_nvdup(mod, fmri, &nfp);
568 	nvlist_free(fmri);
569 
570 	return (nfp);
571 }
572 
573 nvlist_t *
topo_mod_pciefmri(topo_mod_t * mod,tnode_t * pnode,int version,const char * name,topo_instance_t inst,const nvlist_t * auth)574 topo_mod_pciefmri(topo_mod_t *mod, tnode_t *pnode, int version,
575     const char *name, topo_instance_t inst, const nvlist_t *auth)
576 {
577 	nvlist_t *fmri = NULL;
578 	nvlist_t *nfp = NULL;
579 	nvlist_t *args = NULL;
580 	int err;
581 
582 	if (version != FM_PCIE_SCHEME_VERSION)
583 		return (set_fmri_err(mod, EMOD_FMRI_VERSION));
584 
585 	if (pnode != NULL) {
586 		nvlist_t *pfmri;
587 
588 		topo_mod_dprintf(mod, "pnode is set: %p %s=%" PRIu64,
589 		    pnode, pnode->tn_name, pnode->tn_instance);
590 
591 		if (topo_mod_nvalloc(mod, &args, NV_UNIQUE_NAME) != 0)
592 			return (set_fmri_err(mod, EMOD_FMRI_NVL));
593 
594 		if (topo_node_resource(pnode, &pfmri, &err) != 0) {
595 			topo_mod_dprintf(mod, "failed to fetch pmfri: %s",
596 			    topo_strerror(err));
597 		} else {
598 			if (nvlist_add_nvlist(args, TOPO_METH_FMRI_ARG_PARENT,
599 			    pfmri) != 0) {
600 				nvlist_free(pfmri);
601 				nvlist_free(args);
602 				return (set_fmri_err(mod, EMOD_FMRI_NVL));
603 			}
604 			nvlist_free(pfmri);
605 		}
606 
607 		if (auth != NULL) {
608 			if (nvlist_add_nvlist(args, TOPO_METH_FMRI_ARG_AUTH,
609 			    (nvlist_t *)auth) != 0) {
610 				nvlist_free(args);
611 				return (set_fmri_err(mod, EMOD_FMRI_NVL));
612 			}
613 		}
614 	}
615 
616 	if ((fmri = topo_fmri_create(mod->tm_hdl, FM_FMRI_SCHEME_PCIE,
617 	    name, inst, args, &err)) == NULL) {
618 		nvlist_free(args);
619 		return (set_fmri_err(mod, err));
620 	}
621 
622 	nvlist_free(args);
623 
624 	if (topo_mod_nvdup(mod, fmri, &nfp) != 0) {
625 		/*
626 		 * We don't get the failure reason back, but it is likely to be
627 		 * an allocation failure. 'nfp' will still be NULL if an error
628 		 * occurred.
629 		 */
630 		(void) topo_mod_seterrno(mod, EMOD_NOMEM);
631 	}
632 	nvlist_free(fmri);
633 
634 	return (nfp);
635 }
636 
637 nvlist_t *
topo_mod_modfmri(topo_mod_t * mod,int version,const char * driver)638 topo_mod_modfmri(topo_mod_t *mod, int version, const char *driver)
639 {
640 	int err;
641 	nvlist_t *fmri = NULL, *args = NULL;
642 	nvlist_t *nfp = NULL;
643 
644 	if (version != FM_MOD_SCHEME_VERSION)
645 		return (set_fmri_err(mod, EMOD_FMRI_VERSION));
646 
647 	if (topo_mod_nvalloc(mod, &args, NV_UNIQUE_NAME) != 0)
648 		return (set_fmri_err(mod, EMOD_FMRI_NVL));
649 
650 	if (nvlist_add_string(args, "DRIVER", driver) != 0) {
651 		nvlist_free(args);
652 		return (set_fmri_err(mod, EMOD_FMRI_NVL));
653 	}
654 
655 	if ((fmri = topo_fmri_create(mod->tm_hdl, FM_FMRI_SCHEME_MOD,
656 	    FM_FMRI_SCHEME_MOD, 0, args, &err)) == NULL) {
657 		nvlist_free(args);
658 		return (set_fmri_err(mod, err));
659 	}
660 
661 	nvlist_free(args);
662 
663 	(void) topo_mod_nvdup(mod, fmri, &nfp);
664 	nvlist_free(fmri);
665 
666 	return (nfp);
667 }
668 
669 #define	_SWFMRI_ADD_STRING(nvl, name, val) \
670 	((val) ? (nvlist_add_string(nvl, name, val) != 0) : 0)
671 
672 nvlist_t *
topo_mod_swfmri(topo_mod_t * mod,int version,char * obj_path,char * obj_root,nvlist_t * obj_pkg,char * site_token,char * site_module,char * site_file,char * site_func,int64_t site_line,char * ctxt_origin,char * ctxt_execname,int64_t ctxt_pid,char * ctxt_zone,int64_t ctxt_ctid,char ** ctxt_stack,uint_t ctxt_stackdepth)673 topo_mod_swfmri(topo_mod_t *mod, int version,
674     char *obj_path, char *obj_root, nvlist_t *obj_pkg,
675     char *site_token, char *site_module, char *site_file, char *site_func,
676     int64_t site_line, char *ctxt_origin, char *ctxt_execname,
677     int64_t ctxt_pid, char *ctxt_zone, int64_t ctxt_ctid,
678     char **ctxt_stack, uint_t ctxt_stackdepth)
679 {
680 	nvlist_t *fmri, *args;
681 	nvlist_t *nfp = NULL;
682 	int err;
683 
684 	if (version != FM_SW_SCHEME_VERSION)
685 		return (set_fmri_err(mod, EMOD_FMRI_VERSION));
686 
687 	if (topo_mod_nvalloc(mod, &args, NV_UNIQUE_NAME) != 0)
688 		return (set_fmri_err(mod, EMOD_FMRI_NVL));
689 
690 	err = 0;
691 	err |= _SWFMRI_ADD_STRING(args, "obj_path", obj_path);
692 	err |= _SWFMRI_ADD_STRING(args, "obj_root", obj_root);
693 	if (obj_pkg)
694 		err |= nvlist_add_nvlist(args, "obj_pkg", obj_pkg);
695 
696 	err |= _SWFMRI_ADD_STRING(args, "site_token", site_token);
697 	err |= _SWFMRI_ADD_STRING(args, "site_module", site_module);
698 	err |= _SWFMRI_ADD_STRING(args, "site_file", site_file);
699 	err |= _SWFMRI_ADD_STRING(args, "site_func", site_func);
700 	if (site_line != -1)
701 		err |= nvlist_add_int64(args, "site_line", site_line);
702 
703 	err |= _SWFMRI_ADD_STRING(args, "ctxt_origin", ctxt_origin);
704 	err |= _SWFMRI_ADD_STRING(args, "ctxt_execname", ctxt_execname);
705 	if (ctxt_pid != -1)
706 		err |= nvlist_add_int64(args, "ctxt_pid", ctxt_pid);
707 	err |= _SWFMRI_ADD_STRING(args, "ctxt_zone", ctxt_zone);
708 	if (ctxt_ctid != -1)
709 		err |= nvlist_add_int64(args, "ctxt_ctid", ctxt_ctid);
710 	if (ctxt_stack != NULL && ctxt_stackdepth != 0)
711 		err |= nvlist_add_string_array(args, "stack", ctxt_stack,
712 		    ctxt_stackdepth);
713 
714 	if (err) {
715 		nvlist_free(args);
716 		return (set_fmri_err(mod, EMOD_FMRI_NVL));
717 	}
718 
719 	if ((fmri = topo_fmri_create(mod->tm_hdl, FM_FMRI_SCHEME_SW,
720 	    FM_FMRI_SCHEME_SW, 0, args, &err)) == NULL) {
721 		nvlist_free(args);
722 		return (set_fmri_err(mod, err));
723 	}
724 
725 	nvlist_free(args);
726 
727 	(void) topo_mod_nvdup(mod, fmri, &nfp);
728 	nvlist_free(fmri);
729 
730 	return (nfp);
731 }
732 
733 int
topo_mod_str2nvl(topo_mod_t * mod,const char * fmristr,nvlist_t ** fmri)734 topo_mod_str2nvl(topo_mod_t *mod, const char *fmristr, nvlist_t **fmri)
735 {
736 	int err;
737 	nvlist_t *np = NULL;
738 
739 	if (topo_fmri_str2nvl(mod->tm_hdl, fmristr, &np, &err) < 0)
740 		return (topo_mod_seterrno(mod, err));
741 
742 	if (topo_mod_nvdup(mod, np, fmri) < 0) {
743 		nvlist_free(np);
744 		return (topo_mod_seterrno(mod, EMOD_FMRI_NVL));
745 	}
746 
747 	nvlist_free(np);
748 
749 	return (0);
750 }
751 
752 int
topo_mod_nvl2str(topo_mod_t * mod,nvlist_t * fmri,char ** fmristr)753 topo_mod_nvl2str(topo_mod_t *mod, nvlist_t *fmri, char **fmristr)
754 {
755 	int err;
756 	char *sp;
757 
758 	if (topo_fmri_nvl2str(mod->tm_hdl, fmri, &sp, &err) < 0)
759 		return (topo_mod_seterrno(mod, err));
760 
761 	if ((*fmristr = topo_mod_strdup(mod, sp)) == NULL) {
762 		topo_hdl_strfree(mod->tm_hdl, sp);
763 		return (topo_mod_seterrno(mod, EMOD_NOMEM));
764 	}
765 
766 	topo_hdl_strfree(mod->tm_hdl, sp);
767 
768 	return (0);
769 }
770 
771 void *
topo_mod_getspecific(topo_mod_t * mod)772 topo_mod_getspecific(topo_mod_t *mod)
773 {
774 	return (mod->tm_priv);
775 }
776 
777 void
topo_mod_setspecific(topo_mod_t * mod,void * data)778 topo_mod_setspecific(topo_mod_t *mod, void *data)
779 {
780 	mod->tm_priv = data;
781 }
782 
783 void
topo_mod_setdebug(topo_mod_t * mod)784 topo_mod_setdebug(topo_mod_t *mod)
785 {
786 	mod->tm_debug = 1;
787 }
788 
789 ipmi_handle_t *
topo_mod_ipmi_hold(topo_mod_t * mod)790 topo_mod_ipmi_hold(topo_mod_t *mod)
791 {
792 	topo_hdl_t *thp = mod->tm_hdl;
793 	int err;
794 	char *errmsg;
795 
796 	(void) pthread_mutex_lock(&thp->th_ipmi_lock);
797 	if (thp->th_ipmi == NULL) {
798 		if ((thp->th_ipmi = ipmi_open(&err, &errmsg, IPMI_TRANSPORT_BMC,
799 		    NULL)) == NULL) {
800 			topo_dprintf(mod->tm_hdl, TOPO_DBG_ERR,
801 			    "ipmi_open() failed: %s (ipmi errno=%d)", errmsg,
802 			    err);
803 			(void) pthread_mutex_unlock(&thp->th_ipmi_lock);
804 		}
805 	}
806 
807 
808 	return (thp->th_ipmi);
809 }
810 
811 void
topo_mod_ipmi_rele(topo_mod_t * mod)812 topo_mod_ipmi_rele(topo_mod_t *mod)
813 {
814 	topo_hdl_t *thp = mod->tm_hdl;
815 
816 	(void) pthread_mutex_unlock(&thp->th_ipmi_lock);
817 }
818 
819 di_node_t
topo_mod_devinfo(topo_mod_t * mod)820 topo_mod_devinfo(topo_mod_t *mod)
821 {
822 	return (topo_hdl_devinfo(mod->tm_hdl));
823 }
824 
825 smbios_hdl_t *
topo_mod_smbios(topo_mod_t * mod)826 topo_mod_smbios(topo_mod_t *mod)
827 {
828 	topo_hdl_t *thp = mod->tm_hdl;
829 
830 	if (thp->th_smbios == NULL)
831 		thp->th_smbios = smbios_open(NULL, SMB_VERSION, 0, NULL);
832 
833 	return (thp->th_smbios);
834 }
835 
836 di_prom_handle_t
topo_mod_prominfo(topo_mod_t * mod)837 topo_mod_prominfo(topo_mod_t *mod)
838 {
839 	return (topo_hdl_prominfo(mod->tm_hdl));
840 }
841 
842 pcidb_hdl_t *
topo_mod_pcidb(topo_mod_t * mod)843 topo_mod_pcidb(topo_mod_t *mod)
844 {
845 	topo_hdl_t *thp = mod->tm_hdl;
846 
847 	if (thp->th_pcidb == NULL)
848 		thp->th_pcidb = pcidb_open(PCIDB_VERSION);
849 
850 	return (thp->th_pcidb);
851 }
852 
853 void
topo_mod_clrdebug(topo_mod_t * mod)854 topo_mod_clrdebug(topo_mod_t *mod)
855 {
856 	mod->tm_debug = 0;
857 }
858 
859 /*PRINTFLIKE2*/
860 void
topo_mod_dprintf(topo_mod_t * mod,const char * format,...)861 topo_mod_dprintf(topo_mod_t *mod, const char *format, ...)
862 {
863 	topo_hdl_t *thp = mod->tm_hdl;
864 	va_list alist;
865 
866 	if (mod->tm_debug == 0 || !(thp->th_debug & TOPO_DBG_MOD))
867 		return;
868 
869 	va_start(alist, format);
870 	topo_vdprintf(mod->tm_hdl, (const char *)mod->tm_name, format, alist);
871 	va_end(alist);
872 }
873 
874 char *
topo_mod_product(topo_mod_t * mod)875 topo_mod_product(topo_mod_t *mod)
876 {
877 	return (topo_mod_strdup(mod, mod->tm_hdl->th_product));
878 }
879 
880 static char *
topo_mod_server(topo_mod_t * mod)881 topo_mod_server(topo_mod_t *mod)
882 {
883 	static struct utsname uts;
884 
885 	(void) uname(&uts);
886 	return (topo_mod_strdup(mod, uts.nodename));
887 }
888 
889 static char *
topo_mod_psn(topo_mod_t * mod)890 topo_mod_psn(topo_mod_t *mod)
891 {
892 	smbios_hdl_t *shp;
893 	const char *psn;
894 
895 	if ((shp = topo_mod_smbios(mod)) == NULL ||
896 	    (psn = smbios_psn(shp)) == NULL)
897 		return (NULL);
898 
899 	return (topo_cleanup_auth_str(mod->tm_hdl, psn));
900 }
901 
902 static char *
topo_mod_csn(topo_mod_t * mod)903 topo_mod_csn(topo_mod_t *mod)
904 {
905 	char csn[MAXNAMELEN];
906 	smbios_hdl_t *shp;
907 	di_prom_handle_t promh = DI_PROM_HANDLE_NIL;
908 	di_node_t rooth = DI_NODE_NIL;
909 	const char *bufp;
910 
911 	if ((shp = topo_mod_smbios(mod)) != NULL) {
912 		bufp = smbios_csn(shp);
913 		if (bufp != NULL)
914 			(void) strlcpy(csn, bufp, MAXNAMELEN);
915 		else
916 			return (NULL);
917 	} else if ((rooth = topo_mod_devinfo(mod)) != DI_NODE_NIL &&
918 	    (promh = topo_mod_prominfo(mod)) != DI_PROM_HANDLE_NIL) {
919 		if (di_prom_prop_lookup_bytes(promh, rooth, "chassis-sn",
920 		    (unsigned char **)&bufp) != -1) {
921 			(void) strlcpy(csn, bufp, MAXNAMELEN);
922 		} else {
923 			return (NULL);
924 		}
925 	} else {
926 		return (NULL);
927 	}
928 
929 	return (topo_cleanup_auth_str(mod->tm_hdl, csn));
930 }
931 
932 nvlist_t *
topo_mod_auth(topo_mod_t * mod,tnode_t * pnode)933 topo_mod_auth(topo_mod_t *mod, tnode_t *pnode)
934 {
935 	int err;
936 	char *prod = NULL;
937 	char *csn = NULL;
938 	char *psn = NULL;
939 	char *server = NULL;
940 	nvlist_t *auth;
941 
942 	if ((err = topo_mod_nvalloc(mod, &auth, NV_UNIQUE_NAME)) != 0) {
943 		(void) topo_mod_seterrno(mod, EMOD_FMRI_NVL);
944 		return (NULL);
945 	}
946 
947 	(void) topo_prop_get_string(pnode, FM_FMRI_AUTHORITY,
948 	    FM_FMRI_AUTH_PRODUCT, &prod, &err);
949 	(void) topo_prop_get_string(pnode, FM_FMRI_AUTHORITY,
950 	    FM_FMRI_AUTH_PRODUCT_SN, &psn, &err);
951 	(void) topo_prop_get_string(pnode, FM_FMRI_AUTHORITY,
952 	    FM_FMRI_AUTH_CHASSIS, &csn, &err);
953 	(void) topo_prop_get_string(pnode, FM_FMRI_AUTHORITY,
954 	    FM_FMRI_AUTH_SERVER, &server, &err);
955 
956 	/*
957 	 * Let's do this the hard way
958 	 */
959 	if (prod == NULL)
960 		prod = topo_mod_product(mod);
961 	if (csn == NULL)
962 		csn = topo_mod_csn(mod);
963 	if (psn == NULL)
964 		psn = topo_mod_psn(mod);
965 	if (server == NULL) {
966 		server = topo_mod_server(mod);
967 	}
968 
969 	/*
970 	 * No luck, return NULL
971 	 */
972 	if (!prod && !server && !csn && !psn) {
973 		nvlist_free(auth);
974 		return (NULL);
975 	}
976 
977 	err = 0;
978 	if (prod != NULL) {
979 		err |= nvlist_add_string(auth, FM_FMRI_AUTH_PRODUCT, prod);
980 		topo_mod_strfree(mod, prod);
981 	}
982 	if (psn != NULL) {
983 		err |= nvlist_add_string(auth, FM_FMRI_AUTH_PRODUCT_SN, psn);
984 		topo_mod_strfree(mod, psn);
985 	}
986 	if (server != NULL) {
987 		err |= nvlist_add_string(auth, FM_FMRI_AUTH_SERVER, server);
988 		topo_mod_strfree(mod, server);
989 	}
990 	if (csn != NULL) {
991 		err |= nvlist_add_string(auth, FM_FMRI_AUTH_CHASSIS, csn);
992 		topo_mod_strfree(mod, csn);
993 	}
994 
995 	if (err != 0) {
996 		nvlist_free(auth);
997 		(void) topo_mod_seterrno(mod, EMOD_NVL_INVAL);
998 		return (NULL);
999 	}
1000 
1001 	return (auth);
1002 }
1003 
1004 topo_walk_t *
topo_mod_walk_init(topo_mod_t * mod,tnode_t * node,topo_mod_walk_cb_t cb_f,void * pdata,int * errp)1005 topo_mod_walk_init(topo_mod_t *mod, tnode_t *node, topo_mod_walk_cb_t cb_f,
1006     void *pdata, int *errp)
1007 {
1008 	topo_walk_t *wp;
1009 	topo_hdl_t *thp = mod->tm_hdl;
1010 
1011 	if ((wp = topo_node_walk_init(thp, mod, node, (int (*)())cb_f, pdata,
1012 	    errp)) == NULL)
1013 		return (NULL);
1014 
1015 	return (wp);
1016 }
1017 
1018 char *
topo_mod_clean_str(topo_mod_t * mod,const char * str)1019 topo_mod_clean_str(topo_mod_t *mod, const char *str)
1020 {
1021 	if (str == NULL)
1022 		return (NULL);
1023 
1024 	return (topo_cleanup_strn(mod->tm_hdl, str, strlen(str)));
1025 }
1026 
1027 char *
topo_mod_clean_strn(topo_mod_t * mod,const char * str,size_t len)1028 topo_mod_clean_strn(topo_mod_t *mod, const char *str, size_t len)
1029 {
1030 	if (str == NULL)
1031 		return (NULL);
1032 
1033 	return (topo_cleanup_strn(mod->tm_hdl, str, len));
1034 }
1035 
1036 int
topo_mod_file_search(topo_mod_t * mod,const char * file,int oflags)1037 topo_mod_file_search(topo_mod_t *mod, const char *file, int oflags)
1038 {
1039 	int ret;
1040 	char *path;
1041 	topo_hdl_t *thp = mod->tm_hdl;
1042 
1043 	path = topo_search_path(mod, thp->th_rootdir, file);
1044 	if (path == NULL) {
1045 		return (-1);
1046 	}
1047 
1048 	ret = open(path, oflags);
1049 	topo_mod_strfree(mod, path);
1050 	return (ret);
1051 }
1052 
1053 /*ARGSUSED*/
1054 int
topo_mod_hc_occupied(topo_mod_t * mod,tnode_t * node,topo_version_t version,nvlist_t * in,nvlist_t ** out)1055 topo_mod_hc_occupied(topo_mod_t *mod, tnode_t *node, topo_version_t version,
1056     nvlist_t *in, nvlist_t **out)
1057 {
1058 	nvlist_t *nvl = NULL;
1059 	tnode_t *cnp;
1060 	boolean_t is_occupied = B_FALSE;
1061 
1062 	if (version > TOPO_METH_OCCUPIED_VERSION)
1063 		return (topo_mod_seterrno(mod, ETOPO_METHOD_VERNEW));
1064 
1065 	/*
1066 	 * Iterate though the child nodes.  If there are no non-facility
1067 	 * node children then it is unoccupied.
1068 	 */
1069 	for (cnp = topo_child_first(node); cnp != NULL;
1070 	    cnp = topo_child_next(node, cnp)) {
1071 		if (topo_node_flags(cnp) != TOPO_NODE_FACILITY)
1072 			is_occupied = B_TRUE;
1073 	}
1074 
1075 	if (topo_mod_nvalloc(mod, &nvl, NV_UNIQUE_NAME) != 0 ||
1076 	    nvlist_add_boolean_value(nvl, TOPO_METH_OCCUPIED_RET,
1077 	    is_occupied) != 0) {
1078 		topo_mod_dprintf(mod, "Failed to allocate 'out' nvlist\n");
1079 		nvlist_free(nvl);
1080 		return (topo_mod_seterrno(mod, EMOD_NOMEM));
1081 	}
1082 	*out = nvl;
1083 
1084 	return (0);
1085 }
1086 
1087 /*
1088  * Convenience routine for creating a UFM slot node.  This routine assumes
1089  * that the caller has already created the containing range via a call to
1090  * topo_node_range_create().
1091  */
1092 tnode_t *
topo_mod_create_ufm_slot(topo_mod_t * mod,tnode_t * ufmnode,topo_ufm_slot_info_t * slotinfo)1093 topo_mod_create_ufm_slot(topo_mod_t *mod, tnode_t *ufmnode,
1094     topo_ufm_slot_info_t *slotinfo)
1095 {
1096 	nvlist_t *auth = NULL, *fmri = NULL;
1097 	tnode_t *slotnode;
1098 	topo_pgroup_info_t pgi;
1099 	int err, rc;
1100 
1101 	if (slotinfo == NULL || slotinfo->usi_mode == 0) {
1102 		topo_mod_dprintf(mod, "invalid slot info");
1103 		(void) topo_mod_seterrno(mod, ETOPO_MOD_INVAL);
1104 		return (NULL);
1105 	}
1106 	if ((auth = topo_mod_auth(mod, ufmnode)) == NULL) {
1107 		topo_mod_dprintf(mod, "topo_mod_auth() failed: %s",
1108 		    topo_mod_errmsg(mod));
1109 		/* errno set */
1110 		return (NULL);
1111 	}
1112 
1113 	if ((fmri = topo_mod_hcfmri(mod, ufmnode, FM_HC_SCHEME_VERSION,
1114 	    SLOT, slotinfo->usi_slotid, NULL, auth, NULL, NULL, NULL)) ==
1115 	    NULL) {
1116 		nvlist_free(auth);
1117 		topo_mod_dprintf(mod, "topo_mod_hcfmri() failed: %s",
1118 		    topo_mod_errmsg(mod));
1119 		/* errno set */
1120 		return (NULL);
1121 	}
1122 
1123 	if ((slotnode = topo_node_bind(mod, ufmnode, SLOT,
1124 	    slotinfo->usi_slotid, fmri)) == NULL) {
1125 		nvlist_free(auth);
1126 		nvlist_free(fmri);
1127 		topo_mod_dprintf(mod, "topo_node_bind() failed: %s",
1128 		    topo_mod_errmsg(mod));
1129 		/* errno set */
1130 		return (NULL);
1131 	}
1132 
1133 	/* Create authority and system pgroups */
1134 	topo_pgroup_hcset(slotnode, auth);
1135 	nvlist_free(auth);
1136 	nvlist_free(fmri);
1137 
1138 	/* Just inherit the parent's FRU */
1139 	if (topo_node_fru_set(slotnode, NULL, 0, &err) != 0) {
1140 		topo_mod_dprintf(mod, "failed to set FRU on %s: %s", UFM,
1141 		    topo_strerror(err));
1142 		(void) topo_mod_seterrno(mod, err);
1143 		goto slotfail;
1144 	}
1145 
1146 	pgi.tpi_name = TOPO_PGROUP_SLOT;
1147 	pgi.tpi_namestab = TOPO_STABILITY_PRIVATE;
1148 	pgi.tpi_datastab = TOPO_STABILITY_PRIVATE;
1149 	pgi.tpi_version = TOPO_VERSION;
1150 	rc = topo_pgroup_create(slotnode, &pgi, &err);
1151 
1152 	if (rc == 0)
1153 		rc += topo_prop_set_uint32(slotnode, TOPO_PGROUP_SLOT,
1154 		    TOPO_PROP_SLOT_TYPE, TOPO_PROP_IMMUTABLE,
1155 		    TOPO_SLOT_TYPE_UFM, &err);
1156 
1157 	pgi.tpi_name = TOPO_PGROUP_UFM_SLOT;
1158 
1159 	if (rc == 0)
1160 		rc += topo_pgroup_create(slotnode, &pgi, &err);
1161 
1162 	if (rc == 0) {
1163 		rc += topo_prop_set_uint32(slotnode, TOPO_PGROUP_UFM_SLOT,
1164 		    TOPO_PROP_UFM_SLOT_MODE, TOPO_PROP_IMMUTABLE,
1165 		    slotinfo->usi_mode, &err);
1166 	}
1167 
1168 	if (rc == 0) {
1169 		rc += topo_prop_set_uint32(slotnode, TOPO_PGROUP_UFM_SLOT,
1170 		    TOPO_PROP_UFM_SLOT_ACTIVE, TOPO_PROP_IMMUTABLE,
1171 		    (uint32_t)slotinfo->usi_active, &err);
1172 	}
1173 
1174 	/*
1175 	 * We can have a NULL version for an empty slot.
1176 	 */
1177 	if (rc == 0 && slotinfo->usi_version != NULL) {
1178 		rc += topo_prop_set_string(slotnode, TOPO_PGROUP_UFM_SLOT,
1179 		    TOPO_PROP_UFM_SLOT_VERSION, TOPO_PROP_IMMUTABLE,
1180 		    slotinfo->usi_version, &err);
1181 	}
1182 
1183 	if (rc == 0 && slotinfo->usi_extra != NULL) {
1184 		nvpair_t *elem = NULL;
1185 		char *pname, *pval;
1186 
1187 		while ((elem = nvlist_next_nvpair(slotinfo->usi_extra,
1188 		    elem)) != NULL) {
1189 			if (nvpair_type(elem) != DATA_TYPE_STRING)
1190 				continue;
1191 
1192 			pname = nvpair_name(elem);
1193 			if ((rc -= nvpair_value_string(elem, &pval)) != 0)
1194 				break;
1195 
1196 			rc += topo_prop_set_string(slotnode,
1197 			    TOPO_PGROUP_UFM_SLOT, pname, TOPO_PROP_IMMUTABLE,
1198 			    pval, &err);
1199 
1200 			if (rc != 0)
1201 				break;
1202 		}
1203 	}
1204 
1205 	if (rc != 0) {
1206 		topo_mod_dprintf(mod, "error setting properties on %s node",
1207 		    SLOT);
1208 		(void) topo_mod_seterrno(mod, err);
1209 		goto slotfail;
1210 	}
1211 	return (slotnode);
1212 
1213 slotfail:
1214 	topo_node_unbind(slotnode);
1215 	return (NULL);
1216 }
1217 
1218 /*
1219  * This is a convenience routine to allow enumerator modules to easily create
1220  * the necessary UFM node layout for the most common case, which will be a
1221  * single UFM with a single slot.  This routine assumes that the caller has
1222  * already created the containing range via a call to topo_node_range_create().
1223  *
1224  * For more complex scenarios (like multiple slots per UFM), callers can set
1225  * the slotinfo param to NULL.  In this case the ufm node will get created, but
1226  * it will skip creating the slot node - allowing the module to manually call
1227  * topo_mod_create_ufm_slot() to create custom UFM slots.
1228  */
1229 tnode_t *
topo_mod_create_ufm(topo_mod_t * mod,tnode_t * parent,topo_instance_t inst,const char * descr,topo_ufm_slot_info_t * slotinfo)1230 topo_mod_create_ufm(topo_mod_t *mod, tnode_t *parent, topo_instance_t inst,
1231     const char *descr, topo_ufm_slot_info_t *slotinfo)
1232 {
1233 	nvlist_t *auth = NULL, *fmri = NULL;
1234 	tnode_t *ufmnode, *slotnode;
1235 	topo_pgroup_info_t pgi;
1236 	int err, rc;
1237 
1238 	if ((auth = topo_mod_auth(mod, parent)) == NULL) {
1239 		topo_mod_dprintf(mod, "topo_mod_auth() failed: %s",
1240 		    topo_mod_errmsg(mod));
1241 		/* errno set */
1242 		return (NULL);
1243 	}
1244 
1245 	if ((fmri = topo_mod_hcfmri(mod, parent, FM_HC_SCHEME_VERSION,
1246 	    UFM, inst, NULL, auth, NULL, NULL, NULL)) ==
1247 	    NULL) {
1248 		nvlist_free(auth);
1249 		topo_mod_dprintf(mod, "topo_mod_hcfmri() failed: %s",
1250 		    topo_mod_errmsg(mod));
1251 		/* errno set */
1252 		return (NULL);
1253 	}
1254 
1255 	if ((ufmnode = topo_node_bind(mod, parent, UFM, inst, fmri)) == NULL) {
1256 		nvlist_free(auth);
1257 		nvlist_free(fmri);
1258 		topo_mod_dprintf(mod, "topo_node_bind() failed: %s",
1259 		    topo_mod_errmsg(mod));
1260 		/* errno set */
1261 		return (NULL);
1262 	}
1263 
1264 	/* Create authority and system pgroups */
1265 	topo_pgroup_hcset(ufmnode, auth);
1266 	nvlist_free(auth);
1267 	nvlist_free(fmri);
1268 
1269 	/* Just inherit the parent's FRU */
1270 	if (topo_node_fru_set(ufmnode, NULL, 0, &err) != 0) {
1271 		topo_mod_dprintf(mod, "failed to set FRU on %s: %s", UFM,
1272 		    topo_strerror(err));
1273 		(void) topo_mod_seterrno(mod, err);
1274 		goto ufmfail;
1275 	}
1276 
1277 	pgi.tpi_name = TOPO_PGROUP_UFM;
1278 	pgi.tpi_namestab = TOPO_STABILITY_PRIVATE;
1279 	pgi.tpi_datastab = TOPO_STABILITY_PRIVATE;
1280 	pgi.tpi_version = TOPO_VERSION;
1281 	rc = topo_pgroup_create(ufmnode, &pgi, &err);
1282 
1283 	if (rc == 0)
1284 		rc += topo_prop_set_string(ufmnode, TOPO_PGROUP_UFM,
1285 		    TOPO_PROP_UFM_DESCR, TOPO_PROP_IMMUTABLE, descr, &err);
1286 
1287 	if (rc != 0) {
1288 		topo_mod_dprintf(mod, "error setting properties on %s node",
1289 		    UFM);
1290 		(void) topo_mod_seterrno(mod, err);
1291 		goto ufmfail;
1292 	}
1293 
1294 	if (slotinfo != NULL) {
1295 		if (topo_node_range_create(mod, ufmnode, SLOT, 0, 0) < 0) {
1296 			topo_mod_dprintf(mod, "error creating %s range", SLOT);
1297 			goto ufmfail;
1298 		}
1299 		slotnode = topo_mod_create_ufm_slot(mod, ufmnode, slotinfo);
1300 
1301 		if (slotnode == NULL)
1302 			goto ufmfail;
1303 	}
1304 	return (ufmnode);
1305 
1306 ufmfail:
1307 	topo_node_unbind(ufmnode);
1308 	return (NULL);
1309 }
1310