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 2020 Joyent, Inc.
24  * Copyright 2023 Oxide Computer Company
25  */
26 
27 /*
28  * Snapshot Library Interfaces
29  *
30  * Consumers of topology data may use the interfaces in this file to open,
31  * snapshot and close a topology exported by FMRI scheme (hc, mem and cpu)
32  * builtin plugins and their helper modules.  A topology handle is obtained
33  * by calling topo_open().  Upon a successful return, the caller may use this
34  * handle to open a new snapshot.  Each snapshot is assigned a Universally
35  * Unique Identifier that in a future enchancement to the libtopo API will be
36  * used as the file locator in /var/fm/topo to persist new snapshots or lookup
37  * a previously captured snapshot.  topo_snap_hold() will capture the current
38  * system topology.  All consumers of the topo_hdl_t argument will be
39  * blocked from accessing the topology trees until the snapshot completes.
40  *
41  * A snapshot may be cleared by calling topo_snap_rele().  As with
42  * topo_snap_hold(), all topology accesses are blocked until the topology
43  * trees have been released and deallocated.
44  *
45  * Walker Library Interfaces
46  *
47  * Once a snapshot has been taken with topo_snap_hold(), topo_hdl_t holders
48  * may initiate topology tree walks on a scheme-tree basis.  topo_walk_init()
49  * will initiate the data structures required to walk any one one of the
50  * FMRI scheme trees.  The walker data structure, topo_walk_t, is an opaque
51  * handle passed to topo_walk_step to begin the walk.  At each node in the
52  * topology tree, a callback function is called with access to the node at
53  * which our current walk falls.  The callback function is passed in during
54  * calls to topo_walk_init() and used throughout the walk_step of the
55  * scheme tree.  At any time, the callback may terminate the walk by returning
56  * TOPO_WALK_TERMINATE or TOPO_WALK_ERR.  TOPO_WALK_NEXT will continue the walk.
57  *
58  * The type of walk through the tree may be sibling first or child first by
59  * respectively passing in TOPO_WALK_SIBLING or TOPO_WALK_CHILD to
60  * the topo_walk_step() function.  Topology nodes
61  * associated with an outstanding walk are held in place and will not be
62  * deallocated until the walk through that node completes.
63  *
64  * Once the walk has terminated, the walking process should call
65  * topo_walk_fini() to clean-up resources created in topo_walk_init()
66  * and release nodes that may be still held.
67  */
68 
69 #include <alloca.h>
70 #include <ctype.h>
71 #include <pthread.h>
72 #include <limits.h>
73 #include <assert.h>
74 #include <fcntl.h>
75 #include <smbios.h>
76 #include <sys/param.h>
77 #include <sys/types.h>
78 #include <sys/stat.h>
79 #include <sys/systeminfo.h>
80 #include <sys/utsname.h>
81 #include <uuid/uuid.h>
82 #include <zone.h>
83 
84 #include <fm/libtopo.h>
85 #include <sys/fm/protocol.h>
86 
87 #include <topo_alloc.h>
88 #include <topo_builtin.h>
89 #include <topo_string.h>
90 #include <topo_error.h>
91 #include <topo_subr.h>
92 
93 static void topo_snap_destroy(topo_hdl_t *);
94 
95 static topo_hdl_t *
set_open_errno(topo_hdl_t * thp,int * errp,int err)96 set_open_errno(topo_hdl_t *thp, int *errp, int err)
97 {
98 	if (thp != NULL) {
99 		topo_close(thp);
100 	}
101 	if (errp != NULL)
102 		*errp = err;
103 	return (NULL);
104 }
105 
106 /*
107  * Set the product name for the topo handle. There are three things that we will
108  * use here in the following order:
109  *
110  * 1) See if SMBIOS gives us a product string that is usable.
111  * 2) Use the root devinfo node's name if we can get a snapshot. This may be the
112  *    same as the uname -i aka th_platform on most systems, but on some it will
113  *    be more specific. In particular this happens if we have a platform-wide
114  *    unix but end up having machine/implementation knowledge without a
115  *    machine/implementation-specific unix.
116  * 3) We fall back like we have historically to the platform name.
117  */
118 static void
topo_hdl_set_product(topo_hdl_t * thp)119 topo_hdl_set_product(topo_hdl_t *thp)
120 {
121 	smbios_hdl_t *shp;
122 	di_node_t root;
123 	id_t id;
124 
125 	thp->th_product = NULL;
126 	if ((shp = smbios_open(NULL, SMB_VERSION, 0, NULL)) != NULL) {
127 		smbios_system_t s1;
128 		smbios_info_t s2;
129 
130 		if ((id = smbios_info_system(shp, &s1)) != SMB_ERR &&
131 		    smbios_info_common(shp, id, &s2) != SMB_ERR) {
132 
133 			if (strcmp(s2.smbi_product, SMB_DEFAULT1) != 0 &&
134 			    strcmp(s2.smbi_product, SMB_DEFAULT2) != 0) {
135 				thp->th_product = topo_cleanup_auth_str(thp,
136 				    (char *)s2.smbi_product);
137 			}
138 		}
139 		smbios_close(shp);
140 	}
141 
142 	if (thp->th_product != NULL)
143 		return;
144 
145 	root = di_init("/", DINFOCACHE);
146 	if (root != DI_NODE_NIL) {
147 		thp->th_product = topo_hdl_strdup(thp, di_node_name(root));
148 		di_fini(root);
149 	}
150 
151 	if (thp->th_product != NULL)
152 		return;
153 
154 	thp->th_product = topo_hdl_strdup(thp, thp->th_platform);
155 }
156 
157 topo_hdl_t *
topo_open(int version,const char * rootdir,int * errp)158 topo_open(int version, const char *rootdir, int *errp)
159 {
160 	topo_hdl_t *thp = NULL;
161 	topo_alloc_t *tap;
162 
163 	char platform[MAXNAMELEN];
164 	char isa[MAXNAMELEN];
165 	struct utsname uts;
166 	struct stat st;
167 
168 	char *dbflags, *dbout;
169 
170 	if (version != TOPO_VERSION)
171 		return (set_open_errno(thp, errp, ETOPO_HDL_ABIVER));
172 
173 	if (rootdir != NULL && stat(rootdir, &st) < 0)
174 		return (set_open_errno(thp, errp, ETOPO_HDL_INVAL));
175 
176 	if ((thp = topo_zalloc(sizeof (topo_hdl_t), 0)) == NULL)
177 		return (set_open_errno(thp, errp, ETOPO_NOMEM));
178 
179 	(void) pthread_mutex_init(&thp->th_lock, NULL);
180 
181 	if ((tap = topo_zalloc(sizeof (topo_alloc_t), 0)) == NULL)
182 		return (set_open_errno(thp, errp, ETOPO_NOMEM));
183 
184 	/*
185 	 * Install default allocators
186 	 */
187 	tap->ta_flags = 0;
188 	tap->ta_alloc = topo_alloc;
189 	tap->ta_zalloc = topo_zalloc;
190 	tap->ta_free = topo_free;
191 	tap->ta_nvops.nv_ao_alloc = topo_nv_alloc;
192 	tap->ta_nvops.nv_ao_free = topo_nv_free;
193 	(void) nv_alloc_init(&tap->ta_nva, &tap->ta_nvops);
194 	thp->th_alloc = tap;
195 
196 	if ((thp->th_modhash = topo_modhash_create(thp)) == NULL)
197 		return (set_open_errno(thp, errp, ETOPO_NOMEM));
198 
199 	/*
200 	 * Set-up system information and search paths for modules
201 	 * and topology map files
202 	 */
203 	if (rootdir == NULL) {
204 		rootdir = topo_hdl_strdup(thp, "/");
205 		thp->th_rootdir = (char *)rootdir;
206 	} else {
207 		int len;
208 		char *rpath;
209 
210 		len = strlen(rootdir);
211 		if (len >= PATH_MAX)
212 			return (set_open_errno(thp, errp, EINVAL));
213 
214 		if (rootdir[len - 1] != '/') {
215 			rpath = alloca(len + 2);
216 			(void) snprintf(rpath, len + 2, "%s/", rootdir);
217 		} else {
218 			rpath = (char *)rootdir;
219 		}
220 		thp->th_rootdir = topo_hdl_strdup(thp, rpath);
221 	}
222 
223 	platform[0] = '\0';
224 	isa[0] = '\0';
225 	(void) sysinfo(SI_PLATFORM, platform, sizeof (platform));
226 	(void) sysinfo(SI_ARCHITECTURE, isa, sizeof (isa));
227 	(void) uname(&uts);
228 	thp->th_platform = topo_hdl_strdup(thp, platform);
229 	thp->th_isa = topo_hdl_strdup(thp, isa);
230 	thp->th_machine = topo_hdl_strdup(thp, uts.machine);
231 	topo_hdl_set_product(thp);
232 
233 	if (thp->th_rootdir == NULL || thp->th_platform == NULL ||
234 	    thp->th_machine == NULL || thp->th_product == NULL ||
235 	    thp->th_isa == NULL)
236 		return (set_open_errno(thp, errp, ETOPO_NOMEM));
237 
238 	dbflags	 = getenv("TOPO_DEBUG");
239 	dbout = getenv("TOPO_DEBUG_OUT");
240 	if (dbflags != NULL)
241 		topo_debug_set(thp, dbflags, dbout);
242 
243 	if (topo_builtin_create(thp, thp->th_rootdir) != 0) {
244 		topo_dprintf(thp, TOPO_DBG_ERR,
245 		    "failed to load builtin modules: %s\n",
246 		    topo_hdl_errmsg(thp));
247 		return (set_open_errno(thp, errp, topo_hdl_errno(thp)));
248 	}
249 
250 	return (thp);
251 }
252 
253 void
topo_close(topo_hdl_t * thp)254 topo_close(topo_hdl_t *thp)
255 {
256 	ttree_t *tp;
257 	topo_digraph_t *tdg;
258 
259 	topo_hdl_lock(thp);
260 	if (thp->th_platform != NULL)
261 		topo_hdl_strfree(thp, thp->th_platform);
262 	if (thp->th_isa != NULL)
263 		topo_hdl_strfree(thp, thp->th_isa);
264 	if (thp->th_machine != NULL)
265 		topo_hdl_strfree(thp, thp->th_machine);
266 	if (thp->th_product != NULL)
267 		topo_hdl_strfree(thp, thp->th_product);
268 	if (thp->th_rootdir != NULL)
269 		topo_hdl_strfree(thp, thp->th_rootdir);
270 	if (thp->th_ipmi != NULL)
271 		ipmi_close(thp->th_ipmi);
272 	if (thp->th_smbios != NULL)
273 		smbios_close(thp->th_smbios);
274 	if (thp->th_pcidb != NULL)
275 		pcidb_close(thp->th_pcidb);
276 
277 	/*
278 	 * Clean-up snapshot
279 	 */
280 	topo_snap_destroy(thp);
281 
282 	/*
283 	 * Clean-up trees
284 	 */
285 	while ((tp = topo_list_next(&thp->th_trees)) != NULL) {
286 		topo_list_delete(&thp->th_trees, tp);
287 		topo_tree_destroy(tp);
288 	}
289 
290 	/*
291 	 * Clean-up digraphs
292 	 */
293 	while ((tdg = topo_list_next(&thp->th_digraphs)) != NULL) {
294 		topo_list_delete(&thp->th_digraphs, tdg);
295 		topo_digraph_destroy(tdg);
296 	}
297 
298 	/*
299 	 * Unload all plugins
300 	 */
301 	topo_modhash_unload_all(thp);
302 
303 	if (thp->th_modhash != NULL)
304 		topo_modhash_destroy(thp);
305 	if (thp->th_alloc != NULL)
306 		topo_free(thp->th_alloc, sizeof (topo_alloc_t));
307 
308 	topo_hdl_unlock(thp);
309 
310 	topo_free(thp, sizeof (topo_hdl_t));
311 }
312 
313 static char *
topo_snap_create(topo_hdl_t * thp,int * errp,boolean_t need_force)314 topo_snap_create(topo_hdl_t *thp, int *errp, boolean_t need_force)
315 {
316 	uuid_t uuid;
317 	char *ustr = NULL;
318 
319 	topo_hdl_lock(thp);
320 	if (thp->th_uuid != NULL) {
321 		*errp = ETOPO_HDL_UUID;
322 		topo_hdl_unlock(thp);
323 		return (NULL);
324 	}
325 
326 	if ((thp->th_uuid = topo_hdl_zalloc(thp, TOPO_UUID_SIZE)) == NULL) {
327 		*errp = ETOPO_NOMEM;
328 		topo_dprintf(thp, TOPO_DBG_ERR, "unable to allocate uuid: %s\n",
329 		    topo_strerror(*errp));
330 		topo_hdl_unlock(thp);
331 		return (NULL);
332 	}
333 
334 	uuid_generate(uuid);
335 	uuid_unparse(uuid, thp->th_uuid);
336 	if ((ustr = topo_hdl_strdup(thp, thp->th_uuid)) == NULL) {
337 		*errp = ETOPO_NOMEM;
338 		topo_hdl_unlock(thp);
339 		return (NULL);
340 	}
341 
342 	if (need_force) {
343 		topo_dprintf(thp, TOPO_DBG_FORCE,
344 		    "taking a DINFOFORCE snapshot\n");
345 		thp->th_di = di_init("/", DINFOFORCE |
346 		    DINFOSUBTREE | DINFOMINOR | DINFOPROP | DINFOPATH);
347 	} else {
348 		thp->th_di = di_init("/", DINFOCACHE);
349 	}
350 	thp->th_pi = di_prom_init();
351 
352 	if (topo_tree_enum_all(thp) < 0) {
353 		topo_dprintf(thp, TOPO_DBG_ERR, "enumeration failure: %s\n",
354 		    topo_hdl_errmsg(thp));
355 		if (topo_hdl_errno(thp) == ETOPO_ENUM_FATAL) {
356 			*errp = thp->th_errno;
357 
358 			if (thp->th_di != DI_NODE_NIL) {
359 				di_fini(thp->th_di);
360 				thp->th_di = DI_NODE_NIL;
361 			}
362 			if (thp->th_pi != DI_PROM_HANDLE_NIL) {
363 				di_prom_fini(thp->th_pi);
364 				thp->th_pi = DI_PROM_HANDLE_NIL;
365 			}
366 
367 			topo_hdl_strfree(thp, ustr);
368 			topo_hdl_unlock(thp);
369 			return (NULL);
370 		}
371 	}
372 
373 	if (thp->th_ipmi != NULL &&
374 	    ipmi_sdr_changed(thp->th_ipmi) &&
375 	    ipmi_sdr_refresh(thp->th_ipmi) != 0) {
376 		topo_dprintf(thp, TOPO_DBG_ERR,
377 		    "failed to refresh IPMI sdr repository: %s\n",
378 		    ipmi_errmsg(thp->th_ipmi));
379 	}
380 
381 	topo_hdl_unlock(thp);
382 
383 	return (ustr);
384 }
385 
386 /*ARGSUSED*/
387 static char *
topo_snap_log_create(topo_hdl_t * thp,const char * uuid,int * errp)388 topo_snap_log_create(topo_hdl_t *thp, const char *uuid, int *errp)
389 {
390 	return ((char *)uuid);
391 }
392 
393 /*ARGSUSED*/
394 static int
fac_walker(topo_hdl_t * thp,tnode_t * node,void * arg)395 fac_walker(topo_hdl_t *thp, tnode_t *node, void *arg)
396 {
397 	int err;
398 	nvlist_t *out;
399 
400 	if (topo_method_supported(node, TOPO_METH_FAC_ENUM, 0)) {
401 		/*
402 		 * If the facility enumeration method fails, note the failure,
403 		 * but continue on with the walk.
404 		 */
405 		if (topo_method_invoke(node, TOPO_METH_FAC_ENUM, 0, NULL, &out,
406 		    &err) != 0) {
407 			topo_dprintf(thp, TOPO_DBG_ERR,
408 			    "facility enumeration method failed on node %s=%d "
409 			    "(%s)\n", topo_node_name(node),
410 			    topo_node_instance(node), topo_strerror(err));
411 		}
412 	}
413 	return (TOPO_WALK_NEXT);
414 }
415 
416 /*
417  * Return snapshot id
418  */
419 char *
topo_snap_hold(topo_hdl_t * thp,const char * uuid,int * errp)420 topo_snap_hold(topo_hdl_t *thp, const char *uuid, int *errp)
421 {
422 	topo_walk_t *twp;
423 
424 	if (thp == NULL)
425 		return (NULL);
426 
427 	if (uuid == NULL) {
428 		char *ret;
429 
430 		if (thp->th_debug & TOPO_DBG_FORCE) {
431 			ret = topo_snap_create(thp, errp, B_TRUE);
432 		} else {
433 			ret = topo_snap_create(thp, errp, B_FALSE);
434 		}
435 
436 		/*
437 		 * Now walk the tree and invoke any facility enumeration methods
438 		 */
439 		if (ret != NULL && getzoneid() == 0) {
440 			if ((twp = topo_walk_init(thp, FM_FMRI_SCHEME_HC,
441 			    fac_walker, (void *)0, errp)) == NULL) {
442 				return (ret);
443 			}
444 			(void) topo_walk_step(twp, TOPO_WALK_CHILD);
445 			topo_walk_fini(twp);
446 		}
447 		return (ret);
448 	}
449 	return (topo_snap_log_create(thp, uuid, errp));
450 }
451 
452 /*ARGSUSED*/
453 static int
topo_walk_destroy(topo_hdl_t * thp,tnode_t * node,void * notused)454 topo_walk_destroy(topo_hdl_t *thp, tnode_t *node, void *notused)
455 {
456 	tnode_t *cnode;
457 
458 	cnode = topo_child_first(node);
459 
460 	if (cnode != NULL)
461 		return (TOPO_WALK_NEXT);
462 
463 	topo_node_unbind(node);
464 
465 	return (TOPO_WALK_NEXT);
466 }
467 
468 static void
topo_snap_destroy(topo_hdl_t * thp)469 topo_snap_destroy(topo_hdl_t *thp)
470 {
471 	int i;
472 	ttree_t *tp;
473 	topo_digraph_t *tdg;
474 	topo_walk_t *twp;
475 	tnode_t *root;
476 	topo_nodehash_t *nhp;
477 	topo_mod_t *mod;
478 
479 	for (tp = topo_list_next(&thp->th_trees); tp != NULL;
480 	    tp = topo_list_next(tp)) {
481 
482 		root = tp->tt_root;
483 		twp = tp->tt_walk;
484 		/*
485 		 * Clean-up tree nodes from the bottom-up
486 		 */
487 		if ((twp->tw_node = topo_child_first(root)) != NULL) {
488 			twp->tw_cb = topo_walk_destroy;
489 			topo_node_hold(root);
490 			topo_node_hold(twp->tw_node); /* released at walk end */
491 			(void) topo_walk_bottomup(twp, TOPO_WALK_CHILD);
492 			topo_node_rele(root);
493 		}
494 
495 		/*
496 		 * Tidy-up the root node
497 		 */
498 		while ((nhp = topo_list_next(&root->tn_children)) != NULL) {
499 			for (i = 0; i < nhp->th_arrlen; i++) {
500 				assert(nhp->th_nodearr[i] == NULL);
501 			}
502 			mod = nhp->th_enum;
503 			topo_mod_strfree(mod, nhp->th_name);
504 			topo_mod_free(mod, nhp->th_nodearr,
505 			    nhp->th_arrlen * sizeof (tnode_t *));
506 			topo_list_delete(&root->tn_children, nhp);
507 			topo_mod_free(mod, nhp, sizeof (topo_nodehash_t));
508 			topo_mod_rele(mod);
509 		}
510 
511 	}
512 
513 	for (tdg = topo_list_next(&thp->th_digraphs); tdg != NULL;
514 	    tdg = topo_list_next(tdg)) {
515 
516 		topo_vertex_t *vtx;
517 
518 		if (tdg->tdg_nvertices == 0)
519 			continue;
520 		/*
521 		 * We maintain an adjacency list in the topo_digraph_t
522 		 * structure, so we can just walk the list to destroy all the
523 		 * vertices.
524 		 */
525 		mod = tdg->tdg_mod;
526 		vtx = topo_list_next(&tdg->tdg_vertices);
527 		while (vtx != NULL) {
528 			topo_vertex_t *tmp = vtx;
529 
530 			vtx = topo_list_next(vtx);
531 			topo_vertex_destroy(mod, tmp);
532 		}
533 		tdg->tdg_nvertices = 0;
534 	}
535 
536 	/*
537 	 * Clean-up our cached devinfo and prom tree handles.
538 	 */
539 	if (thp->th_di != DI_NODE_NIL) {
540 		di_fini(thp->th_di);
541 		thp->th_di = DI_NODE_NIL;
542 	}
543 	if (thp->th_pi != DI_PROM_HANDLE_NIL) {
544 		di_prom_fini(thp->th_pi);
545 		thp->th_pi = DI_PROM_HANDLE_NIL;
546 	}
547 
548 	if (thp->th_uuid != NULL) {
549 		topo_hdl_free(thp, thp->th_uuid, TOPO_UUID_SIZE);
550 		thp->th_uuid = NULL;
551 	}
552 }
553 
554 void
topo_snap_release(topo_hdl_t * thp)555 topo_snap_release(topo_hdl_t *thp)
556 {
557 	if (thp == NULL)
558 		return;
559 
560 	topo_hdl_lock(thp);
561 	topo_snap_destroy(thp);
562 	topo_hdl_unlock(thp);
563 }
564 
565 topo_walk_t *
topo_walk_init(topo_hdl_t * thp,const char * scheme,topo_walk_cb_t cb_f,void * pdata,int * errp)566 topo_walk_init(topo_hdl_t *thp, const char *scheme, topo_walk_cb_t cb_f,
567     void *pdata, int *errp)
568 {
569 	ttree_t *tp;
570 	topo_walk_t *wp;
571 
572 	for (tp = topo_list_next(&thp->th_trees); tp != NULL;
573 	    tp = topo_list_next(tp)) {
574 		if (strcmp(scheme, tp->tt_scheme) == 0) {
575 
576 			/*
577 			 * Hold the root node and start walk at the first
578 			 * child node
579 			 */
580 			assert(tp->tt_root != NULL);
581 
582 			if ((wp = topo_node_walk_init(thp, NULL, tp->tt_root,
583 			    cb_f, pdata, errp)) == NULL) /* errp set */
584 				return (NULL);
585 
586 			return (wp);
587 		}
588 	}
589 
590 	*errp = ETOPO_WALK_NOTFOUND;
591 	return (NULL);
592 }
593 
594 static int
step_child(tnode_t * cnp,topo_walk_t * wp,int flag,int bottomup)595 step_child(tnode_t *cnp, topo_walk_t *wp, int flag, int bottomup)
596 {
597 	int status;
598 	tnode_t *nnp;
599 
600 	nnp = topo_child_first(cnp);
601 
602 	if (nnp == NULL) {
603 		topo_dprintf(wp->tw_thp, TOPO_DBG_WALK,
604 		    "step_child: TOPO_WALK_TERMINATE for %s=%d\n",
605 		    cnp->tn_name, cnp->tn_instance);
606 		return (TOPO_WALK_TERMINATE);
607 	}
608 
609 	topo_dprintf(wp->tw_thp, TOPO_DBG_WALK,
610 	    "step_child: walk through node %s=%d to %s=%d\n",
611 	    cnp->tn_name, cnp->tn_instance, nnp->tn_name, nnp->tn_instance);
612 
613 	topo_node_hold(nnp); /* released on return from walk_step */
614 	wp->tw_node = nnp;
615 	if (bottomup == 1)
616 		status = topo_walk_bottomup(wp, flag);
617 	else
618 		status = topo_walk_step(wp, flag);
619 
620 	return (status);
621 }
622 
623 static int
step_sibling(tnode_t * cnp,topo_walk_t * wp,int flag,int bottomup)624 step_sibling(tnode_t *cnp, topo_walk_t *wp, int flag, int bottomup)
625 {
626 	int status;
627 	tnode_t *nnp;
628 
629 	nnp = topo_child_next(cnp->tn_parent, cnp);
630 
631 	if (nnp == NULL) {
632 		topo_dprintf(wp->tw_thp, TOPO_DBG_WALK,
633 		    "step_sibling: TOPO_WALK_TERMINATE for %s=%d\n",
634 		    cnp->tn_name, cnp->tn_instance);
635 		return (TOPO_WALK_TERMINATE);
636 	}
637 
638 	topo_dprintf(wp->tw_thp, TOPO_DBG_WALK,
639 	    "step_sibling: through sibling node %s=%d to %s=%d\n",
640 	    cnp->tn_name, cnp->tn_instance, nnp->tn_name, nnp->tn_instance);
641 
642 	topo_node_hold(nnp); /* released on return from walk_step */
643 	wp->tw_node = nnp;
644 	if (bottomup == 1)
645 		status = topo_walk_bottomup(wp, flag);
646 	else
647 		status = topo_walk_step(wp, flag);
648 
649 	return (status);
650 }
651 
652 int
topo_walk_byid(topo_walk_t * wp,const char * name,topo_instance_t inst)653 topo_walk_byid(topo_walk_t *wp, const char *name, topo_instance_t inst)
654 {
655 	int status;
656 	tnode_t *nnp, *cnp;
657 
658 	cnp = wp->tw_node;
659 	nnp = topo_node_lookup(cnp, name, inst);
660 	if (nnp == NULL)
661 		return (TOPO_WALK_TERMINATE);
662 
663 	topo_node_hold(nnp);
664 	wp->tw_node = nnp;
665 	if (wp->tw_mod != NULL)
666 		status = wp->tw_cb(wp->tw_mod, nnp, wp->tw_pdata);
667 	else
668 		status = wp->tw_cb(wp->tw_thp, nnp, wp->tw_pdata);
669 	topo_node_rele(nnp);
670 	wp->tw_node = cnp;
671 
672 	return (status);
673 }
674 
675 int
topo_walk_bysibling(topo_walk_t * wp,const char * name,topo_instance_t inst)676 topo_walk_bysibling(topo_walk_t *wp, const char *name, topo_instance_t inst)
677 {
678 	int status;
679 	tnode_t *cnp, *pnp;
680 
681 	cnp = wp->tw_node;
682 	pnp = topo_node_parent(cnp);
683 	assert(pnp != NULL);
684 
685 	topo_node_hold(pnp);
686 	wp->tw_node = pnp;
687 	status = topo_walk_byid(wp, name, inst);
688 	topo_node_rele(pnp);
689 	wp->tw_node = cnp;
690 
691 	return (status);
692 }
693 
694 int
topo_walk_step(topo_walk_t * wp,int flag)695 topo_walk_step(topo_walk_t *wp, int flag)
696 {
697 	int status;
698 	tnode_t *cnp = wp->tw_node;
699 
700 	if (flag != TOPO_WALK_CHILD && flag != TOPO_WALK_SIBLING) {
701 		topo_node_rele(cnp);
702 		return (TOPO_WALK_ERR);
703 	}
704 
705 	/*
706 	 * No more nodes to walk
707 	 */
708 	if (cnp == NULL) {
709 		topo_dprintf(wp->tw_thp, TOPO_DBG_WALK,
710 		    "walk_step terminated\n");
711 		topo_node_rele(cnp);
712 		return (TOPO_WALK_TERMINATE);
713 	}
714 
715 
716 	if (wp->tw_mod != NULL)
717 		status = wp->tw_cb(wp->tw_mod, cnp, wp->tw_pdata);
718 	else
719 		status = wp->tw_cb(wp->tw_thp, cnp, wp->tw_pdata);
720 
721 	/*
722 	 * Walker callback says we're done
723 	 */
724 	if (status != TOPO_WALK_NEXT) {
725 		topo_node_rele(cnp);
726 		return (status);
727 	}
728 
729 	if (flag == TOPO_WALK_CHILD)
730 		status = step_child(cnp, wp, flag, 0);
731 	else
732 		status = step_sibling(cnp, wp, flag, 0);
733 
734 	/*
735 	 * No more nodes in this hash, skip to next node hash by stepping
736 	 * to next sibling (child-first walk) or next child (sibling-first
737 	 * walk).
738 	 */
739 	if (status == TOPO_WALK_TERMINATE) {
740 		if (flag == TOPO_WALK_CHILD)
741 			status = step_sibling(cnp, wp, flag, 0);
742 		else
743 			status = step_child(cnp, wp, flag, 0);
744 	}
745 
746 	topo_node_rele(cnp); /* done with current node */
747 
748 	return (status);
749 }
750 
751 void
topo_walk_fini(topo_walk_t * wp)752 topo_walk_fini(topo_walk_t *wp)
753 {
754 	if (wp == NULL)
755 		return;
756 
757 	topo_node_rele(wp->tw_root);
758 
759 	topo_hdl_free(wp->tw_thp, wp, sizeof (topo_walk_t));
760 }
761 
762 int
topo_walk_bottomup(topo_walk_t * wp,int flag)763 topo_walk_bottomup(topo_walk_t *wp, int flag)
764 {
765 	int status;
766 	tnode_t *cnp;
767 
768 	if (wp == NULL)
769 		return (TOPO_WALK_ERR);
770 
771 	cnp = wp->tw_node;
772 	if (flag != TOPO_WALK_CHILD && flag != TOPO_WALK_SIBLING) {
773 		topo_node_rele(cnp);
774 		return (TOPO_WALK_ERR);
775 	}
776 
777 	/*
778 	 * End of the line
779 	 */
780 	if (cnp == NULL) {
781 		topo_dprintf(wp->tw_thp, TOPO_DBG_WALK,
782 		    "walk_bottomup terminated\n");
783 		topo_node_rele(cnp);
784 		return (TOPO_WALK_TERMINATE);
785 	}
786 
787 	topo_dprintf(wp->tw_thp, TOPO_DBG_WALK,
788 	    "%s walk_bottomup through node %s=%d\n",
789 	    (flag == TOPO_WALK_CHILD ? "TOPO_WALK_CHILD" : "TOPO_WALK_SIBLING"),
790 	    cnp->tn_name, cnp->tn_instance);
791 
792 	if (flag == TOPO_WALK_CHILD)
793 		status = step_child(cnp, wp, flag, 1);
794 	else
795 		status = step_sibling(cnp, wp, flag, 1);
796 
797 	/*
798 	 * At a leaf, run the callback
799 	 */
800 	if (status == TOPO_WALK_TERMINATE) {
801 		if ((status = wp->tw_cb(wp->tw_thp, cnp, wp->tw_pdata))
802 		    != TOPO_WALK_NEXT) {
803 			topo_node_rele(cnp);
804 			return (status);
805 		}
806 	}
807 
808 	/*
809 	 * Try next child or sibling
810 	 */
811 	if (status == TOPO_WALK_NEXT) {
812 		if (flag == TOPO_WALK_CHILD)
813 			status = step_sibling(cnp, wp, flag, 1);
814 		else
815 			status = step_child(cnp, wp, flag, 1);
816 	}
817 
818 	topo_node_rele(cnp); /* done with current node */
819 
820 	return (status);
821 }
822 
823 di_node_t
topo_hdl_devinfo(topo_hdl_t * thp)824 topo_hdl_devinfo(topo_hdl_t *thp)
825 {
826 	return (thp == NULL ? DI_NODE_NIL : thp->th_di);
827 }
828 
829 di_prom_handle_t
topo_hdl_prominfo(topo_hdl_t * thp)830 topo_hdl_prominfo(topo_hdl_t *thp)
831 {
832 	return (thp == NULL ? DI_PROM_HANDLE_NIL : thp->th_pi);
833 }
834 
835 int
topo_scheme_walk(topo_hdl_t * thp,topo_scheme_walk_cb_f cb,void * arg)836 topo_scheme_walk(topo_hdl_t *thp, topo_scheme_walk_cb_f cb, void *arg)
837 {
838 	for (ttree_t *tp = topo_list_next(&thp->th_trees); tp != NULL;
839 	    tp = topo_list_next(tp)) {
840 		int ret;
841 		topo_scheme_info_t info;
842 
843 		info.tsi_scheme = tp->tt_scheme;
844 		info.tsi_type = TOPO_SCHEME_TREE;
845 
846 		ret = cb(thp, &info, arg);
847 		if (ret != TOPO_WALK_NEXT) {
848 			return (ret);
849 		}
850 	}
851 
852 	for (topo_digraph_t *tdg = topo_list_next(&thp->th_digraphs);
853 	    tdg != NULL; tdg = topo_list_next(tdg)) {
854 		int ret;
855 		topo_scheme_info_t info;
856 
857 		info.tsi_scheme = tdg->tdg_scheme;
858 		info.tsi_type = TOPO_SCHEME_DIGRAPH;
859 
860 		ret = cb(thp, &info, arg);
861 		if (ret != TOPO_WALK_NEXT) {
862 			return (ret);
863 		}
864 	}
865 
866 	return (TOPO_WALK_TERMINATE);
867 }
868