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