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
92static void topo_snap_destroy(topo_hdl_t *);
93
94static topo_hdl_t *
95set_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
105topo_hdl_t *
106topo_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
218void
219topo_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
278static char *
279topo_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*/
352static char *
353topo_snap_log_create(topo_hdl_t *thp, const char *uuid, int *errp)
354{
355	return ((char *)uuid);
356}
357
358/*ARGSUSED*/
359static int
360fac_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 */
384char *
385topo_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*/
418static int
419topo_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
433static void
434topo_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
519void
520topo_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
530topo_walk_t *
531topo_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
559static int
560step_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
588static int
589step_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
617int
618topo_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
640int
641topo_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
659int
660topo_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
716void
717topo_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
727int
728topo_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
788di_node_t
789topo_hdl_devinfo(topo_hdl_t *thp)
790{
791	return (thp == NULL ? DI_NODE_NIL : thp->th_di);
792}
793
794di_prom_handle_t
795topo_hdl_prominfo(topo_hdl_t *thp)
796{
797	return (thp == NULL ? DI_PROM_HANDLE_NIL : thp->th_pi);
798}
799