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