xref: /illumos-gate/usr/src/lib/libdevinfo/devinfo.c (revision f00128d8)
17c478bd9Sstevel@tonic-gate /*
27c478bd9Sstevel@tonic-gate  * CDDL HEADER START
37c478bd9Sstevel@tonic-gate  *
47c478bd9Sstevel@tonic-gate  * The contents of this file are subject to the terms of the
53ebafc43Sjveta  * Common Development and Distribution License (the "License").
63ebafc43Sjveta  * You may not use this file except in compliance with the License.
77c478bd9Sstevel@tonic-gate  *
87c478bd9Sstevel@tonic-gate  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
97c478bd9Sstevel@tonic-gate  * or http://www.opensolaris.org/os/licensing.
107c478bd9Sstevel@tonic-gate  * See the License for the specific language governing permissions
117c478bd9Sstevel@tonic-gate  * and limitations under the License.
127c478bd9Sstevel@tonic-gate  *
137c478bd9Sstevel@tonic-gate  * When distributing Covered Code, include this CDDL HEADER in each
147c478bd9Sstevel@tonic-gate  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
157c478bd9Sstevel@tonic-gate  * If applicable, add the following below this CDDL HEADER, with the
167c478bd9Sstevel@tonic-gate  * fields enclosed by brackets "[]" replaced with your own identifying
177c478bd9Sstevel@tonic-gate  * information: Portions Copyright [yyyy] [name of copyright owner]
187c478bd9Sstevel@tonic-gate  *
197c478bd9Sstevel@tonic-gate  * CDDL HEADER END
207c478bd9Sstevel@tonic-gate  */
217c478bd9Sstevel@tonic-gate /*
2294c894bbSVikram Hegde  * Copyright (c) 1997, 2010, Oracle and/or its affiliates. All rights reserved.
237c478bd9Sstevel@tonic-gate  */
247c478bd9Sstevel@tonic-gate 
257c478bd9Sstevel@tonic-gate /*
267c478bd9Sstevel@tonic-gate  * Interfaces for getting device configuration data from kernel
277c478bd9Sstevel@tonic-gate  * through the devinfo driver.
287c478bd9Sstevel@tonic-gate  */
297c478bd9Sstevel@tonic-gate 
307c478bd9Sstevel@tonic-gate #include <stdio.h>
317c478bd9Sstevel@tonic-gate #include <stdlib.h>
327c478bd9Sstevel@tonic-gate #include <string.h>
337c478bd9Sstevel@tonic-gate #include <strings.h>
347c478bd9Sstevel@tonic-gate #include <stropts.h>
357c478bd9Sstevel@tonic-gate #include <fcntl.h>
367c478bd9Sstevel@tonic-gate #include <poll.h>
377c478bd9Sstevel@tonic-gate #include <synch.h>
387c478bd9Sstevel@tonic-gate #include <unistd.h>
397c478bd9Sstevel@tonic-gate #include <sys/mkdev.h>
407c478bd9Sstevel@tonic-gate #include <sys/obpdefs.h>
417c478bd9Sstevel@tonic-gate #include <sys/stat.h>
427c478bd9Sstevel@tonic-gate #include <sys/types.h>
437c478bd9Sstevel@tonic-gate #include <sys/time.h>
447c478bd9Sstevel@tonic-gate #include <sys/autoconf.h>
457c478bd9Sstevel@tonic-gate #include <stdarg.h>
4626947304SEvan Yan #include <sys/ddi_hp.h>
477c478bd9Sstevel@tonic-gate 
487c478bd9Sstevel@tonic-gate #define	NDEBUG 1
497c478bd9Sstevel@tonic-gate #include <assert.h>
507c478bd9Sstevel@tonic-gate 
517c478bd9Sstevel@tonic-gate #include "libdevinfo.h"
527c478bd9Sstevel@tonic-gate 
537c478bd9Sstevel@tonic-gate /*
547c478bd9Sstevel@tonic-gate  * Debug message levels
557c478bd9Sstevel@tonic-gate  */
567c478bd9Sstevel@tonic-gate typedef enum {
577c478bd9Sstevel@tonic-gate 	DI_QUIET = 0,	/* No debug messages - the default */
587c478bd9Sstevel@tonic-gate 	DI_ERR = 1,
597c478bd9Sstevel@tonic-gate 	DI_INFO,
607c478bd9Sstevel@tonic-gate 	DI_TRACE,
617c478bd9Sstevel@tonic-gate 	DI_TRACE1,
627c478bd9Sstevel@tonic-gate 	DI_TRACE2
637c478bd9Sstevel@tonic-gate } di_debug_t;
647c478bd9Sstevel@tonic-gate 
657c478bd9Sstevel@tonic-gate int di_debug = DI_QUIET;
667c478bd9Sstevel@tonic-gate 
677c478bd9Sstevel@tonic-gate #define	DPRINTF(args)	{ if (di_debug != DI_QUIET) dprint args; }
687c478bd9Sstevel@tonic-gate 
697c478bd9Sstevel@tonic-gate void dprint(di_debug_t msglevel, const char *fmt, ...);
707c478bd9Sstevel@tonic-gate 
717c478bd9Sstevel@tonic-gate 
727c478bd9Sstevel@tonic-gate #pragma init(_libdevinfo_init)
737c478bd9Sstevel@tonic-gate 
747c478bd9Sstevel@tonic-gate void
_libdevinfo_init()757c478bd9Sstevel@tonic-gate _libdevinfo_init()
767c478bd9Sstevel@tonic-gate {
777c478bd9Sstevel@tonic-gate 	char	*debug_str = getenv("_LIBDEVINFO_DEBUG");
787c478bd9Sstevel@tonic-gate 
797c478bd9Sstevel@tonic-gate 	if (debug_str) {
807c478bd9Sstevel@tonic-gate 		errno = 0;
817c478bd9Sstevel@tonic-gate 		di_debug = atoi(debug_str);
827c478bd9Sstevel@tonic-gate 		if (errno || di_debug < DI_QUIET)
837c478bd9Sstevel@tonic-gate 			di_debug = DI_QUIET;
847c478bd9Sstevel@tonic-gate 	}
857c478bd9Sstevel@tonic-gate }
867c478bd9Sstevel@tonic-gate 
877c478bd9Sstevel@tonic-gate di_node_t
di_init(const char * phys_path,uint_t flag)887c478bd9Sstevel@tonic-gate di_init(const char *phys_path, uint_t flag)
897c478bd9Sstevel@tonic-gate {
907c478bd9Sstevel@tonic-gate 	return (di_init_impl(phys_path, flag, NULL));
917c478bd9Sstevel@tonic-gate }
927c478bd9Sstevel@tonic-gate 
937c478bd9Sstevel@tonic-gate /*
947c478bd9Sstevel@tonic-gate  * We use blocking_open() to guarantee access to the devinfo device, if open()
957c478bd9Sstevel@tonic-gate  * is failing with EAGAIN.
967c478bd9Sstevel@tonic-gate  */
977c478bd9Sstevel@tonic-gate static int
blocking_open(const char * path,int oflag)987c478bd9Sstevel@tonic-gate blocking_open(const char *path, int oflag)
997c478bd9Sstevel@tonic-gate {
1007c478bd9Sstevel@tonic-gate 	int fd;
1017c478bd9Sstevel@tonic-gate 
1027c478bd9Sstevel@tonic-gate 	while ((fd = open(path, oflag)) == -1 && errno == EAGAIN)
1037c478bd9Sstevel@tonic-gate 		(void) poll(NULL, 0, 1 * MILLISEC);
1047c478bd9Sstevel@tonic-gate 
1057c478bd9Sstevel@tonic-gate 	return (fd);
1067c478bd9Sstevel@tonic-gate }
1077c478bd9Sstevel@tonic-gate 
1087c478bd9Sstevel@tonic-gate /* private interface */
1097c478bd9Sstevel@tonic-gate di_node_t
di_init_driver(const char * drv_name,uint_t flag)1107c478bd9Sstevel@tonic-gate di_init_driver(const char *drv_name, uint_t flag)
1117c478bd9Sstevel@tonic-gate {
1127c478bd9Sstevel@tonic-gate 	int fd;
1137c478bd9Sstevel@tonic-gate 	char driver[MAXPATHLEN];
1147c478bd9Sstevel@tonic-gate 
1157c478bd9Sstevel@tonic-gate 	/*
1167c478bd9Sstevel@tonic-gate 	 * Don't allow drv_name to exceed MAXPATHLEN - 1, or 1023,
1177c478bd9Sstevel@tonic-gate 	 * which should be sufficient for any sensible programmer.
1187c478bd9Sstevel@tonic-gate 	 */
1197c478bd9Sstevel@tonic-gate 	if ((drv_name == NULL) || (strlen(drv_name) >= MAXPATHLEN)) {
1207c478bd9Sstevel@tonic-gate 		errno = EINVAL;
1217c478bd9Sstevel@tonic-gate 		return (DI_NODE_NIL);
1227c478bd9Sstevel@tonic-gate 	}
1237c478bd9Sstevel@tonic-gate 	(void) strcpy(driver, drv_name);
1247c478bd9Sstevel@tonic-gate 
1257c478bd9Sstevel@tonic-gate 	/*
1267c478bd9Sstevel@tonic-gate 	 * open the devinfo driver
1277c478bd9Sstevel@tonic-gate 	 */
1287c478bd9Sstevel@tonic-gate 	if ((fd = blocking_open("/devices/pseudo/devinfo@0:devinfo",
1297c478bd9Sstevel@tonic-gate 	    O_RDONLY)) == -1) {
1307c478bd9Sstevel@tonic-gate 		DPRINTF((DI_ERR, "devinfo open failed: errno = %d\n", errno));
1317c478bd9Sstevel@tonic-gate 		return (DI_NODE_NIL);
1327c478bd9Sstevel@tonic-gate 	}
1337c478bd9Sstevel@tonic-gate 
1347c478bd9Sstevel@tonic-gate 	if (ioctl(fd, DINFOLODRV, driver) != 0) {
1357c478bd9Sstevel@tonic-gate 		DPRINTF((DI_ERR, "failed to load driver %s\n", driver));
1367c478bd9Sstevel@tonic-gate 		(void) close(fd);
1377c478bd9Sstevel@tonic-gate 		errno = ENXIO;
1387c478bd9Sstevel@tonic-gate 		return (DI_NODE_NIL);
1397c478bd9Sstevel@tonic-gate 	}
1407c478bd9Sstevel@tonic-gate 	(void) close(fd);
1417c478bd9Sstevel@tonic-gate 
1427c478bd9Sstevel@tonic-gate 	/*
1437c478bd9Sstevel@tonic-gate 	 * Driver load succeeded, return a snapshot
1447c478bd9Sstevel@tonic-gate 	 */
1457c478bd9Sstevel@tonic-gate 	return (di_init("/", flag));
1467c478bd9Sstevel@tonic-gate }
1477c478bd9Sstevel@tonic-gate 
1487c478bd9Sstevel@tonic-gate di_node_t
di_init_impl(const char * phys_path,uint_t flag,struct di_priv_data * priv)1497c478bd9Sstevel@tonic-gate di_init_impl(const char *phys_path, uint_t flag,
150*f00128d8SToomas Soome     struct di_priv_data *priv)
1517c478bd9Sstevel@tonic-gate {
1527c478bd9Sstevel@tonic-gate 	caddr_t pa;
1537c478bd9Sstevel@tonic-gate 	int fd, map_size;
1547c478bd9Sstevel@tonic-gate 	struct di_all *dap;
1557c478bd9Sstevel@tonic-gate 	struct dinfo_io dinfo_io;
1567c478bd9Sstevel@tonic-gate 
1577c478bd9Sstevel@tonic-gate 	uint_t pageoffset = sysconf(_SC_PAGESIZE) - 1;
1587c478bd9Sstevel@tonic-gate 	uint_t pagemask = ~pageoffset;
1597c478bd9Sstevel@tonic-gate 
1607c478bd9Sstevel@tonic-gate 	DPRINTF((DI_INFO, "di_init: taking a snapshot\n"));
1617c478bd9Sstevel@tonic-gate 
1627c478bd9Sstevel@tonic-gate 	/*
1637c478bd9Sstevel@tonic-gate 	 * Make sure there is no minor name in the path
1647c478bd9Sstevel@tonic-gate 	 * and the path do not start with /devices....
1657c478bd9Sstevel@tonic-gate 	 */
1667c478bd9Sstevel@tonic-gate 	if (strchr(phys_path, ':') ||
1677c478bd9Sstevel@tonic-gate 	    (strncmp(phys_path, "/devices", 8) == 0) ||
1687c478bd9Sstevel@tonic-gate 	    (strlen(phys_path) > MAXPATHLEN)) {
1697c478bd9Sstevel@tonic-gate 		errno = EINVAL;
1707c478bd9Sstevel@tonic-gate 		return (DI_NODE_NIL);
1717c478bd9Sstevel@tonic-gate 	}
1727c478bd9Sstevel@tonic-gate 
1737c478bd9Sstevel@tonic-gate 	if (strlen(phys_path) == 0)
1747c478bd9Sstevel@tonic-gate 		(void) sprintf(dinfo_io.root_path, "/");
1757c478bd9Sstevel@tonic-gate 	else if (*phys_path != '/')
1767c478bd9Sstevel@tonic-gate 		(void) snprintf(dinfo_io.root_path, sizeof (dinfo_io.root_path),
1777c478bd9Sstevel@tonic-gate 		    "/%s", phys_path);
1787c478bd9Sstevel@tonic-gate 	else
1797c478bd9Sstevel@tonic-gate 		(void) snprintf(dinfo_io.root_path, sizeof (dinfo_io.root_path),
1807c478bd9Sstevel@tonic-gate 		    "%s", phys_path);
1817c478bd9Sstevel@tonic-gate 
1827c478bd9Sstevel@tonic-gate 	/*
1837c478bd9Sstevel@tonic-gate 	 * If private data is requested, copy the format specification
1847c478bd9Sstevel@tonic-gate 	 */
1857c478bd9Sstevel@tonic-gate 	if (flag & DINFOPRIVDATA & 0xff) {
1867c478bd9Sstevel@tonic-gate 		if (priv)
1877c478bd9Sstevel@tonic-gate 			bcopy(priv, &dinfo_io.priv,
1887c478bd9Sstevel@tonic-gate 			    sizeof (struct di_priv_data));
1897c478bd9Sstevel@tonic-gate 		else {
1907c478bd9Sstevel@tonic-gate 			errno = EINVAL;
1917c478bd9Sstevel@tonic-gate 			return (DI_NODE_NIL);
1927c478bd9Sstevel@tonic-gate 		}
1937c478bd9Sstevel@tonic-gate 	}
1947c478bd9Sstevel@tonic-gate 
1957c478bd9Sstevel@tonic-gate 	/*
1967c478bd9Sstevel@tonic-gate 	 * Attempt to open the devinfo driver.  Make a second attempt at the
1977c478bd9Sstevel@tonic-gate 	 * read-only minor node if we don't have privileges to open the full
1987c478bd9Sstevel@tonic-gate 	 * version _and_ if we're not requesting operations that the read-only
1997c478bd9Sstevel@tonic-gate 	 * node can't perform.  (Setgid processes would fail an access() test,
2007c478bd9Sstevel@tonic-gate 	 * of course.)
2017c478bd9Sstevel@tonic-gate 	 */
2027c478bd9Sstevel@tonic-gate 	if ((fd = blocking_open("/devices/pseudo/devinfo@0:devinfo",
2037c478bd9Sstevel@tonic-gate 	    O_RDONLY)) == -1) {
2047c478bd9Sstevel@tonic-gate 		if ((flag & DINFOFORCE) == DINFOFORCE ||
2057c478bd9Sstevel@tonic-gate 		    (flag & DINFOPRIVDATA) == DINFOPRIVDATA) {
2067c478bd9Sstevel@tonic-gate 			/*
2077c478bd9Sstevel@tonic-gate 			 * We wanted to perform a privileged operation, but the
2087c478bd9Sstevel@tonic-gate 			 * privileged node isn't available.  Don't modify errno
2097c478bd9Sstevel@tonic-gate 			 * on our way out (but display it if we're running with
2107c478bd9Sstevel@tonic-gate 			 * di_debug set).
2117c478bd9Sstevel@tonic-gate 			 */
2127c478bd9Sstevel@tonic-gate 			DPRINTF((DI_ERR, "devinfo open failed: errno = %d\n",
2137c478bd9Sstevel@tonic-gate 			    errno));
2147c478bd9Sstevel@tonic-gate 			return (DI_NODE_NIL);
2157c478bd9Sstevel@tonic-gate 		}
2167c478bd9Sstevel@tonic-gate 
2177c478bd9Sstevel@tonic-gate 		if ((fd = blocking_open("/devices/pseudo/devinfo@0:devinfo,ro",
2187c478bd9Sstevel@tonic-gate 		    O_RDONLY)) == -1) {
2197c478bd9Sstevel@tonic-gate 			DPRINTF((DI_ERR, "devinfo open failed: errno = %d\n",
2207c478bd9Sstevel@tonic-gate 			    errno));
2217c478bd9Sstevel@tonic-gate 			return (DI_NODE_NIL);
2227c478bd9Sstevel@tonic-gate 		}
2237c478bd9Sstevel@tonic-gate 	}
2247c478bd9Sstevel@tonic-gate 
2257c478bd9Sstevel@tonic-gate 	/*
2267c478bd9Sstevel@tonic-gate 	 * Verify that there is no major conflict, i.e., we are indeed opening
2277c478bd9Sstevel@tonic-gate 	 * the devinfo driver.
2287c478bd9Sstevel@tonic-gate 	 */
2297c478bd9Sstevel@tonic-gate 	if (ioctl(fd, DINFOIDENT, NULL) != DI_MAGIC) {
2307c478bd9Sstevel@tonic-gate 		DPRINTF((DI_ERR,
2317c478bd9Sstevel@tonic-gate 		    "driver ID failed; check for major conflict\n"));
2327c478bd9Sstevel@tonic-gate 		(void) close(fd);
2337c478bd9Sstevel@tonic-gate 		return (DI_NODE_NIL);
2347c478bd9Sstevel@tonic-gate 	}
2357c478bd9Sstevel@tonic-gate 
2367c478bd9Sstevel@tonic-gate 	/*
2377c478bd9Sstevel@tonic-gate 	 * create snapshot
2387c478bd9Sstevel@tonic-gate 	 */
2397c478bd9Sstevel@tonic-gate 	if ((map_size = ioctl(fd, flag, &dinfo_io)) < 0) {
2407c478bd9Sstevel@tonic-gate 		DPRINTF((DI_ERR, "devinfo ioctl failed with "
2417c478bd9Sstevel@tonic-gate 		    "error: %d\n", errno));
2427c478bd9Sstevel@tonic-gate 		(void) close(fd);
2437c478bd9Sstevel@tonic-gate 		return (DI_NODE_NIL);
2447c478bd9Sstevel@tonic-gate 	} else if (map_size == 0) {
2457c478bd9Sstevel@tonic-gate 		DPRINTF((DI_ERR, "%s not found\n", phys_path));
2467c478bd9Sstevel@tonic-gate 		errno = ENXIO;
2477c478bd9Sstevel@tonic-gate 		(void) close(fd);
2487c478bd9Sstevel@tonic-gate 		return (DI_NODE_NIL);
2497c478bd9Sstevel@tonic-gate 	}
2507c478bd9Sstevel@tonic-gate 
2517c478bd9Sstevel@tonic-gate 	/*
2527c478bd9Sstevel@tonic-gate 	 * copy snapshot to userland
2537c478bd9Sstevel@tonic-gate 	 */
2547c478bd9Sstevel@tonic-gate 	map_size = (map_size + pageoffset) & pagemask;
2557c478bd9Sstevel@tonic-gate 	if ((pa = valloc(map_size)) == NULL) {
2567c478bd9Sstevel@tonic-gate 		DPRINTF((DI_ERR, "valloc failed for snapshot\n"));
2577c478bd9Sstevel@tonic-gate 		(void) close(fd);
2587c478bd9Sstevel@tonic-gate 		return (DI_NODE_NIL);
2597c478bd9Sstevel@tonic-gate 	}
2607c478bd9Sstevel@tonic-gate 
2617c478bd9Sstevel@tonic-gate 	if (ioctl(fd, DINFOUSRLD, pa) != map_size) {
2627c478bd9Sstevel@tonic-gate 		DPRINTF((DI_ERR, "failed to copy snapshot to usrld\n"));
2637c478bd9Sstevel@tonic-gate 		(void) close(fd);
2647c478bd9Sstevel@tonic-gate 		free(pa);
2657c478bd9Sstevel@tonic-gate 		errno = EFAULT;
2667c478bd9Sstevel@tonic-gate 		return (DI_NODE_NIL);
2677c478bd9Sstevel@tonic-gate 	}
2687c478bd9Sstevel@tonic-gate 
2697c478bd9Sstevel@tonic-gate 	(void) close(fd);
2707c478bd9Sstevel@tonic-gate 
2717c478bd9Sstevel@tonic-gate 	dap = DI_ALL(pa);
2723e2676e0Svikram 	if (dap->version != DI_SNAPSHOT_VERSION) {
2733e2676e0Svikram 		DPRINTF((DI_ERR, "wrong snapshot version "
2743e2676e0Svikram 		    "(expected=%d, actual=%d)\n",
2753e2676e0Svikram 		    DI_SNAPSHOT_VERSION, dap->version));
2763e2676e0Svikram 		free(pa);
2773e2676e0Svikram 		errno = ESTALE;
2783e2676e0Svikram 		return (DI_NODE_NIL);
2793e2676e0Svikram 	}
2807c478bd9Sstevel@tonic-gate 	if (dap->top_devinfo == 0) {	/* phys_path not found */
2817c478bd9Sstevel@tonic-gate 		DPRINTF((DI_ERR, "%s not found\n", phys_path));
2827c478bd9Sstevel@tonic-gate 		free(pa);
2837c478bd9Sstevel@tonic-gate 		errno = EINVAL;
2847c478bd9Sstevel@tonic-gate 		return (DI_NODE_NIL);
2857c478bd9Sstevel@tonic-gate 	}
2867c478bd9Sstevel@tonic-gate 
2877c478bd9Sstevel@tonic-gate 	return (DI_NODE(pa + dap->top_devinfo));
2887c478bd9Sstevel@tonic-gate }
2897c478bd9Sstevel@tonic-gate 
2907c478bd9Sstevel@tonic-gate void
di_fini(di_node_t root)2917c478bd9Sstevel@tonic-gate di_fini(di_node_t root)
2927c478bd9Sstevel@tonic-gate {
2937c478bd9Sstevel@tonic-gate 	caddr_t pa;		/* starting address of map */
2947c478bd9Sstevel@tonic-gate 
2957c478bd9Sstevel@tonic-gate 	DPRINTF((DI_INFO, "di_fini: freeing a snapshot\n"));
2967c478bd9Sstevel@tonic-gate 
2977c478bd9Sstevel@tonic-gate 	/*
2987c478bd9Sstevel@tonic-gate 	 * paranoid checking
2997c478bd9Sstevel@tonic-gate 	 */
3007c478bd9Sstevel@tonic-gate 	if (root == DI_NODE_NIL) {
3017c478bd9Sstevel@tonic-gate 		DPRINTF((DI_ERR, "di_fini called with NIL arg\n"));
3027c478bd9Sstevel@tonic-gate 		return;
3037c478bd9Sstevel@tonic-gate 	}
3047c478bd9Sstevel@tonic-gate 
3057c478bd9Sstevel@tonic-gate 	/*
3067c478bd9Sstevel@tonic-gate 	 * The root contains its own offset--self.
3077c478bd9Sstevel@tonic-gate 	 * Subtracting it from root address, we get the starting addr.
3087c478bd9Sstevel@tonic-gate 	 * The map_size is stored at the beginning of snapshot.
3097c478bd9Sstevel@tonic-gate 	 * Once we have starting address and size, we can free().
3107c478bd9Sstevel@tonic-gate 	 */
3117c478bd9Sstevel@tonic-gate 	pa = (caddr_t)root - DI_NODE(root)->self;
3127c478bd9Sstevel@tonic-gate 
3137c478bd9Sstevel@tonic-gate 	free(pa);
3147c478bd9Sstevel@tonic-gate }
3157c478bd9Sstevel@tonic-gate 
3167c478bd9Sstevel@tonic-gate di_node_t
di_parent_node(di_node_t node)3177c478bd9Sstevel@tonic-gate di_parent_node(di_node_t node)
3187c478bd9Sstevel@tonic-gate {
3197c478bd9Sstevel@tonic-gate 	caddr_t pa;		/* starting address of map */
3207c478bd9Sstevel@tonic-gate 
3217c478bd9Sstevel@tonic-gate 	if (node == DI_NODE_NIL) {
3227c478bd9Sstevel@tonic-gate 		errno = EINVAL;
3237c478bd9Sstevel@tonic-gate 		return (DI_NODE_NIL);
3247c478bd9Sstevel@tonic-gate 	}
3257c478bd9Sstevel@tonic-gate 
3267c478bd9Sstevel@tonic-gate 	DPRINTF((DI_TRACE, "Get parent of node %s\n", di_node_name(node)));
3277c478bd9Sstevel@tonic-gate 
3287c478bd9Sstevel@tonic-gate 	pa = (caddr_t)node - DI_NODE(node)->self;
3297c478bd9Sstevel@tonic-gate 
3307c478bd9Sstevel@tonic-gate 	if (DI_NODE(node)->parent) {
3317c478bd9Sstevel@tonic-gate 		return (DI_NODE(pa + DI_NODE(node)->parent));
3327c478bd9Sstevel@tonic-gate 	}
3337c478bd9Sstevel@tonic-gate 
3347c478bd9Sstevel@tonic-gate 	/*
3357c478bd9Sstevel@tonic-gate 	 * Deal with error condition:
3367c478bd9Sstevel@tonic-gate 	 *   If parent doesn't exist and node is not the root,
3377c478bd9Sstevel@tonic-gate 	 *   set errno to ENOTSUP. Otherwise, set errno to ENXIO.
3387c478bd9Sstevel@tonic-gate 	 */
3397c478bd9Sstevel@tonic-gate 	if (strcmp(DI_ALL(pa)->root_path, "/") != 0)
3407c478bd9Sstevel@tonic-gate 		errno = ENOTSUP;
3417c478bd9Sstevel@tonic-gate 	else
3427c478bd9Sstevel@tonic-gate 		errno = ENXIO;
3437c478bd9Sstevel@tonic-gate 
3447c478bd9Sstevel@tonic-gate 	return (DI_NODE_NIL);
3457c478bd9Sstevel@tonic-gate }
3467c478bd9Sstevel@tonic-gate 
3477c478bd9Sstevel@tonic-gate di_node_t
di_sibling_node(di_node_t node)3487c478bd9Sstevel@tonic-gate di_sibling_node(di_node_t node)
3497c478bd9Sstevel@tonic-gate {
3507c478bd9Sstevel@tonic-gate 	caddr_t pa;		/* starting address of map */
3517c478bd9Sstevel@tonic-gate 
3527c478bd9Sstevel@tonic-gate 	if (node == DI_NODE_NIL) {
3537c478bd9Sstevel@tonic-gate 		errno = EINVAL;
3547c478bd9Sstevel@tonic-gate 		return (DI_NODE_NIL);
3557c478bd9Sstevel@tonic-gate 	}
3567c478bd9Sstevel@tonic-gate 
3577c478bd9Sstevel@tonic-gate 	DPRINTF((DI_TRACE, "Get sibling of node %s\n", di_node_name(node)));
3587c478bd9Sstevel@tonic-gate 
3597c478bd9Sstevel@tonic-gate 	pa = (caddr_t)node - DI_NODE(node)->self;
3607c478bd9Sstevel@tonic-gate 
3617c478bd9Sstevel@tonic-gate 	if (DI_NODE(node)->sibling) {
3627c478bd9Sstevel@tonic-gate 		return (DI_NODE(pa + DI_NODE(node)->sibling));
3637c478bd9Sstevel@tonic-gate 	}
3647c478bd9Sstevel@tonic-gate 
3657c478bd9Sstevel@tonic-gate 	/*
3667c478bd9Sstevel@tonic-gate 	 * Deal with error condition:
3677c478bd9Sstevel@tonic-gate 	 *   Sibling doesn't exist, figure out if ioctl command
3687c478bd9Sstevel@tonic-gate 	 *   has DINFOSUBTREE set. If it doesn't, set errno to
3697c478bd9Sstevel@tonic-gate 	 *   ENOTSUP.
3707c478bd9Sstevel@tonic-gate 	 */
3717c478bd9Sstevel@tonic-gate 	if (!(DI_ALL(pa)->command & DINFOSUBTREE))
3727c478bd9Sstevel@tonic-gate 		errno = ENOTSUP;
3737c478bd9Sstevel@tonic-gate 	else
3747c478bd9Sstevel@tonic-gate 		errno = ENXIO;
3757c478bd9Sstevel@tonic-gate 
3767c478bd9Sstevel@tonic-gate 	return (DI_NODE_NIL);
3777c478bd9Sstevel@tonic-gate }
3787c478bd9Sstevel@tonic-gate 
3797c478bd9Sstevel@tonic-gate di_node_t
di_child_node(di_node_t node)3807c478bd9Sstevel@tonic-gate di_child_node(di_node_t node)
3817c478bd9Sstevel@tonic-gate {
3827c478bd9Sstevel@tonic-gate 	caddr_t pa;		/* starting address of map */
3837c478bd9Sstevel@tonic-gate 
3847c478bd9Sstevel@tonic-gate 	DPRINTF((DI_TRACE, "Get child of node %s\n", di_node_name(node)));
3857c478bd9Sstevel@tonic-gate 
3867c478bd9Sstevel@tonic-gate 	if (node == DI_NODE_NIL) {
3877c478bd9Sstevel@tonic-gate 		errno = EINVAL;
3887c478bd9Sstevel@tonic-gate 		return (DI_NODE_NIL);
3897c478bd9Sstevel@tonic-gate 	}
3907c478bd9Sstevel@tonic-gate 
3917c478bd9Sstevel@tonic-gate 	pa = (caddr_t)node - DI_NODE(node)->self;
3927c478bd9Sstevel@tonic-gate 
3937c478bd9Sstevel@tonic-gate 	if (DI_NODE(node)->child) {
3947c478bd9Sstevel@tonic-gate 		return (DI_NODE(pa + DI_NODE(node)->child));
3957c478bd9Sstevel@tonic-gate 	}
3967c478bd9Sstevel@tonic-gate 
3977c478bd9Sstevel@tonic-gate 	/*
3987c478bd9Sstevel@tonic-gate 	 * Deal with error condition:
3997c478bd9Sstevel@tonic-gate 	 *   Child doesn't exist, figure out if DINFOSUBTREE is set.
4007c478bd9Sstevel@tonic-gate 	 *   If it isn't, set errno to ENOTSUP.
4017c478bd9Sstevel@tonic-gate 	 */
4027c478bd9Sstevel@tonic-gate 	if (!(DI_ALL(pa)->command & DINFOSUBTREE))
4037c478bd9Sstevel@tonic-gate 		errno = ENOTSUP;
4047c478bd9Sstevel@tonic-gate 	else
4057c478bd9Sstevel@tonic-gate 		errno = ENXIO;
4067c478bd9Sstevel@tonic-gate 
4077c478bd9Sstevel@tonic-gate 	return (DI_NODE_NIL);
4087c478bd9Sstevel@tonic-gate }
4097c478bd9Sstevel@tonic-gate 
4107c478bd9Sstevel@tonic-gate di_node_t
di_drv_first_node(const char * drv_name,di_node_t root)4117c478bd9Sstevel@tonic-gate di_drv_first_node(const char *drv_name, di_node_t root)
4127c478bd9Sstevel@tonic-gate {
4137c478bd9Sstevel@tonic-gate 	caddr_t		pa;		/* starting address of map */
4147c478bd9Sstevel@tonic-gate 	int		major, devcnt;
4157c478bd9Sstevel@tonic-gate 	struct di_devnm	*devnm;
4167c478bd9Sstevel@tonic-gate 
4177c478bd9Sstevel@tonic-gate 	DPRINTF((DI_INFO, "Get first node of driver %s\n", drv_name));
4187c478bd9Sstevel@tonic-gate 
4197c478bd9Sstevel@tonic-gate 	if (root == DI_NODE_NIL) {
4207c478bd9Sstevel@tonic-gate 		errno = EINVAL;
4217c478bd9Sstevel@tonic-gate 		return (DI_NODE_NIL);
4227c478bd9Sstevel@tonic-gate 	}
4237c478bd9Sstevel@tonic-gate 
4247c478bd9Sstevel@tonic-gate 	/*
4257c478bd9Sstevel@tonic-gate 	 * get major number of driver
4267c478bd9Sstevel@tonic-gate 	 */
4277c478bd9Sstevel@tonic-gate 	pa = (caddr_t)root - DI_NODE(root)->self;
4287c478bd9Sstevel@tonic-gate 	devcnt = DI_ALL(pa)->devcnt;
4297c478bd9Sstevel@tonic-gate 	devnm = DI_DEVNM(pa + DI_ALL(pa)->devnames);
4307c478bd9Sstevel@tonic-gate 
4317c478bd9Sstevel@tonic-gate 	for (major = 0; major < devcnt; major++)
4327c478bd9Sstevel@tonic-gate 		if (devnm[major].name && (strcmp(drv_name,
4337c478bd9Sstevel@tonic-gate 		    (char *)(pa + devnm[major].name)) == 0))
4347c478bd9Sstevel@tonic-gate 			break;
4357c478bd9Sstevel@tonic-gate 
4367c478bd9Sstevel@tonic-gate 	if (major >= devcnt) {
4377c478bd9Sstevel@tonic-gate 		errno = EINVAL;
4387c478bd9Sstevel@tonic-gate 		return (DI_NODE_NIL);
4397c478bd9Sstevel@tonic-gate 	}
4407c478bd9Sstevel@tonic-gate 
4417c478bd9Sstevel@tonic-gate 	if (!(devnm[major].head)) {
4427c478bd9Sstevel@tonic-gate 		errno = ENXIO;
4437c478bd9Sstevel@tonic-gate 		return (DI_NODE_NIL);
4447c478bd9Sstevel@tonic-gate 	}
4457c478bd9Sstevel@tonic-gate 
4467c478bd9Sstevel@tonic-gate 	return (DI_NODE(pa + devnm[major].head));
4477c478bd9Sstevel@tonic-gate }
4487c478bd9Sstevel@tonic-gate 
4497c478bd9Sstevel@tonic-gate di_node_t
di_drv_next_node(di_node_t node)4507c478bd9Sstevel@tonic-gate di_drv_next_node(di_node_t node)
4517c478bd9Sstevel@tonic-gate {
4527c478bd9Sstevel@tonic-gate 	caddr_t		pa;		/* starting address of map */
4537c478bd9Sstevel@tonic-gate 
4547c478bd9Sstevel@tonic-gate 	if (node == DI_NODE_NIL) {
4557c478bd9Sstevel@tonic-gate 		errno = EINVAL;
4567c478bd9Sstevel@tonic-gate 		return (DI_NODE_NIL);
4577c478bd9Sstevel@tonic-gate 	}
4587c478bd9Sstevel@tonic-gate 
4597c478bd9Sstevel@tonic-gate 	DPRINTF((DI_TRACE, "next node on per driver list:"
4607c478bd9Sstevel@tonic-gate 	    " current=%s, driver=%s\n",
4617c478bd9Sstevel@tonic-gate 	    di_node_name(node), di_driver_name(node)));
4627c478bd9Sstevel@tonic-gate 
4637c478bd9Sstevel@tonic-gate 	if (DI_NODE(node)->next == (di_off_t)-1) {
4647c478bd9Sstevel@tonic-gate 		errno = ENOTSUP;
4657c478bd9Sstevel@tonic-gate 		return (DI_NODE_NIL);
4667c478bd9Sstevel@tonic-gate 	}
4677c478bd9Sstevel@tonic-gate 
4687c478bd9Sstevel@tonic-gate 	pa = (caddr_t)node - DI_NODE(node)->self;
4697c478bd9Sstevel@tonic-gate 
470*f00128d8SToomas Soome 	if (DI_NODE(node)->next == 0) {
4717c478bd9Sstevel@tonic-gate 		errno = ENXIO;
4727c478bd9Sstevel@tonic-gate 		return (DI_NODE_NIL);
4737c478bd9Sstevel@tonic-gate 	}
4747c478bd9Sstevel@tonic-gate 
4757c478bd9Sstevel@tonic-gate 	return (DI_NODE(pa + DI_NODE(node)->next));
4767c478bd9Sstevel@tonic-gate }
4777c478bd9Sstevel@tonic-gate 
4787c478bd9Sstevel@tonic-gate /*
4797c478bd9Sstevel@tonic-gate  * Internal library interfaces:
4807c478bd9Sstevel@tonic-gate  *   node_list etc. for node walking
4817c478bd9Sstevel@tonic-gate  */
4827c478bd9Sstevel@tonic-gate struct node_list {
4837c478bd9Sstevel@tonic-gate 	struct node_list *next;
4847c478bd9Sstevel@tonic-gate 	di_node_t node;
4857c478bd9Sstevel@tonic-gate };
4867c478bd9Sstevel@tonic-gate 
4877c478bd9Sstevel@tonic-gate static void
free_node_list(struct node_list ** headp)4887c478bd9Sstevel@tonic-gate free_node_list(struct node_list **headp)
4897c478bd9Sstevel@tonic-gate {
4907c478bd9Sstevel@tonic-gate 	struct node_list *tmp;
4917c478bd9Sstevel@tonic-gate 
4927c478bd9Sstevel@tonic-gate 	while (*headp) {
4937c478bd9Sstevel@tonic-gate 		tmp = *headp;
4947c478bd9Sstevel@tonic-gate 		*headp = (*headp)->next;
4957c478bd9Sstevel@tonic-gate 		free(tmp);
4967c478bd9Sstevel@tonic-gate 	}
4977c478bd9Sstevel@tonic-gate }
4987c478bd9Sstevel@tonic-gate 
4997c478bd9Sstevel@tonic-gate static void
append_node_list(struct node_list ** headp,struct node_list * list)5007c478bd9Sstevel@tonic-gate append_node_list(struct node_list **headp, struct node_list *list)
5017c478bd9Sstevel@tonic-gate {
5027c478bd9Sstevel@tonic-gate 	struct node_list *tmp;
5037c478bd9Sstevel@tonic-gate 
5047c478bd9Sstevel@tonic-gate 	if (*headp == NULL) {
5057c478bd9Sstevel@tonic-gate 		*headp = list;
5067c478bd9Sstevel@tonic-gate 		return;
5077c478bd9Sstevel@tonic-gate 	}
5087c478bd9Sstevel@tonic-gate 
5097c478bd9Sstevel@tonic-gate 	if (list == NULL)	/* a minor optimization */
5107c478bd9Sstevel@tonic-gate 		return;
5117c478bd9Sstevel@tonic-gate 
5127c478bd9Sstevel@tonic-gate 	tmp = *headp;
5137c478bd9Sstevel@tonic-gate 	while (tmp->next)
5147c478bd9Sstevel@tonic-gate 		tmp = tmp->next;
5157c478bd9Sstevel@tonic-gate 
5167c478bd9Sstevel@tonic-gate 	tmp->next = list;
5177c478bd9Sstevel@tonic-gate }
5187c478bd9Sstevel@tonic-gate 
5197c478bd9Sstevel@tonic-gate static void
prepend_node_list(struct node_list ** headp,struct node_list * list)5207c478bd9Sstevel@tonic-gate prepend_node_list(struct node_list **headp, struct node_list *list)
5217c478bd9Sstevel@tonic-gate {
5227c478bd9Sstevel@tonic-gate 	struct node_list *tmp;
5237c478bd9Sstevel@tonic-gate 
5247c478bd9Sstevel@tonic-gate 	if (list == NULL)
5257c478bd9Sstevel@tonic-gate 		return;
5267c478bd9Sstevel@tonic-gate 
5277c478bd9Sstevel@tonic-gate 	tmp = *headp;
5287c478bd9Sstevel@tonic-gate 	*headp = list;
5297c478bd9Sstevel@tonic-gate 
5307c478bd9Sstevel@tonic-gate 	if (tmp == NULL)	/* a minor optimization */
5317c478bd9Sstevel@tonic-gate 		return;
5327c478bd9Sstevel@tonic-gate 
5337c478bd9Sstevel@tonic-gate 	while (list->next)
5347c478bd9Sstevel@tonic-gate 		list = list->next;
5357c478bd9Sstevel@tonic-gate 
5367c478bd9Sstevel@tonic-gate 	list->next = tmp;
5377c478bd9Sstevel@tonic-gate }
5387c478bd9Sstevel@tonic-gate 
5397c478bd9Sstevel@tonic-gate /*
5407c478bd9Sstevel@tonic-gate  * returns 1 if node is a descendant of parent, 0 otherwise
5417c478bd9Sstevel@tonic-gate  */
5427c478bd9Sstevel@tonic-gate static int
is_descendant(di_node_t node,di_node_t parent)5437c478bd9Sstevel@tonic-gate is_descendant(di_node_t node, di_node_t parent)
5447c478bd9Sstevel@tonic-gate {
5457c478bd9Sstevel@tonic-gate 	/*
5467c478bd9Sstevel@tonic-gate 	 * DI_NODE_NIL is parent of root, so it is
5477c478bd9Sstevel@tonic-gate 	 * the parent of all nodes.
5487c478bd9Sstevel@tonic-gate 	 */
5497c478bd9Sstevel@tonic-gate 	if (parent == DI_NODE_NIL) {
5507c478bd9Sstevel@tonic-gate 		return (1);
5517c478bd9Sstevel@tonic-gate 	}
5527c478bd9Sstevel@tonic-gate 
5537c478bd9Sstevel@tonic-gate 	do {
5547c478bd9Sstevel@tonic-gate 		node = di_parent_node(node);
5557c478bd9Sstevel@tonic-gate 	} while ((node != DI_NODE_NIL) && (node != parent));
5567c478bd9Sstevel@tonic-gate 
5577c478bd9Sstevel@tonic-gate 	return (node != DI_NODE_NIL);
5587c478bd9Sstevel@tonic-gate }
5597c478bd9Sstevel@tonic-gate 
5607c478bd9Sstevel@tonic-gate /*
5617c478bd9Sstevel@tonic-gate  * Insert list before the first node which is NOT a descendent of parent.
5627c478bd9Sstevel@tonic-gate  * This is needed to reproduce the exact walking order of link generators.
5637c478bd9Sstevel@tonic-gate  */
5647c478bd9Sstevel@tonic-gate static void
insert_node_list(struct node_list ** headp,struct node_list * list,di_node_t parent)5657c478bd9Sstevel@tonic-gate insert_node_list(struct node_list **headp, struct node_list *list,
5667c478bd9Sstevel@tonic-gate     di_node_t parent)
5677c478bd9Sstevel@tonic-gate {
5687c478bd9Sstevel@tonic-gate 	struct node_list *tmp, *tmp1;
5697c478bd9Sstevel@tonic-gate 
5707c478bd9Sstevel@tonic-gate 	if (list == NULL)
5717c478bd9Sstevel@tonic-gate 		return;
5727c478bd9Sstevel@tonic-gate 
5737c478bd9Sstevel@tonic-gate 	tmp = *headp;
5747c478bd9Sstevel@tonic-gate 	if (tmp == NULL) {	/* a minor optimization */
5757c478bd9Sstevel@tonic-gate 		*headp = list;
5767c478bd9Sstevel@tonic-gate 		return;
5777c478bd9Sstevel@tonic-gate 	}
5787c478bd9Sstevel@tonic-gate 
5797c478bd9Sstevel@tonic-gate 	if (!is_descendant(tmp->node, parent)) {
5807c478bd9Sstevel@tonic-gate 		prepend_node_list(headp, list);
5817c478bd9Sstevel@tonic-gate 		return;
5827c478bd9Sstevel@tonic-gate 	}
5837c478bd9Sstevel@tonic-gate 
5847c478bd9Sstevel@tonic-gate 	/*
5857c478bd9Sstevel@tonic-gate 	 * Find first node which is not a descendant
5867c478bd9Sstevel@tonic-gate 	 */
5877c478bd9Sstevel@tonic-gate 	while (tmp->next && is_descendant(tmp->next->node, parent)) {
5887c478bd9Sstevel@tonic-gate 		tmp = tmp->next;
5897c478bd9Sstevel@tonic-gate 	}
5907c478bd9Sstevel@tonic-gate 
5917c478bd9Sstevel@tonic-gate 	tmp1 = tmp->next;
5927c478bd9Sstevel@tonic-gate 	tmp->next = list;
5937c478bd9Sstevel@tonic-gate 	append_node_list(headp, tmp1);
5947c478bd9Sstevel@tonic-gate }
5957c478bd9Sstevel@tonic-gate 
5967c478bd9Sstevel@tonic-gate /*
5977c478bd9Sstevel@tonic-gate  *   Get a linked list of handles of all children
5987c478bd9Sstevel@tonic-gate  */
5997c478bd9Sstevel@tonic-gate static struct node_list *
get_children(di_node_t node)6007c478bd9Sstevel@tonic-gate get_children(di_node_t node)
6017c478bd9Sstevel@tonic-gate {
6027c478bd9Sstevel@tonic-gate 	di_node_t child;
6037c478bd9Sstevel@tonic-gate 	struct node_list *result, *tmp;
6047c478bd9Sstevel@tonic-gate 
6057c478bd9Sstevel@tonic-gate 	DPRINTF((DI_TRACE1, "Get children of node %s\n", di_node_name(node)));
6067c478bd9Sstevel@tonic-gate 
6077c478bd9Sstevel@tonic-gate 	if ((child = di_child_node(node)) == DI_NODE_NIL) {
6087c478bd9Sstevel@tonic-gate 		return (NULL);
6097c478bd9Sstevel@tonic-gate 	}
6107c478bd9Sstevel@tonic-gate 
6117c478bd9Sstevel@tonic-gate 	if ((result = malloc(sizeof (struct node_list))) == NULL) {
6127c478bd9Sstevel@tonic-gate 		DPRINTF((DI_ERR, "malloc of node_list failed\n"));
6137c478bd9Sstevel@tonic-gate 		return (NULL);
6147c478bd9Sstevel@tonic-gate 	}
6157c478bd9Sstevel@tonic-gate 
6167c478bd9Sstevel@tonic-gate 	result->node = child;
6177c478bd9Sstevel@tonic-gate 	tmp = result;
6187c478bd9Sstevel@tonic-gate 
6197c478bd9Sstevel@tonic-gate 	while ((child = di_sibling_node(tmp->node)) != DI_NODE_NIL) {
6207c478bd9Sstevel@tonic-gate 		if ((tmp->next = malloc(sizeof (struct node_list))) == NULL) {
6217c478bd9Sstevel@tonic-gate 			DPRINTF((DI_ERR, "malloc of node_list failed\n"));
6227c478bd9Sstevel@tonic-gate 			free_node_list(&result);
6237c478bd9Sstevel@tonic-gate 			return (NULL);
6247c478bd9Sstevel@tonic-gate 		}
6257c478bd9Sstevel@tonic-gate 		tmp = tmp->next;
6267c478bd9Sstevel@tonic-gate 		tmp->node = child;
6277c478bd9Sstevel@tonic-gate 	}
6287c478bd9Sstevel@tonic-gate 
6297c478bd9Sstevel@tonic-gate 	tmp->next = NULL;
6307c478bd9Sstevel@tonic-gate 
6317c478bd9Sstevel@tonic-gate 	return (result);
6327c478bd9Sstevel@tonic-gate }
6337c478bd9Sstevel@tonic-gate 
6347c478bd9Sstevel@tonic-gate /*
6357c478bd9Sstevel@tonic-gate  * Internal library interface:
6367c478bd9Sstevel@tonic-gate  *   Delete all siblings of the first node from the node_list, along with
6377c478bd9Sstevel@tonic-gate  *   the first node itself.
6387c478bd9Sstevel@tonic-gate  */
6397c478bd9Sstevel@tonic-gate static void
prune_sib(struct node_list ** headp)6407c478bd9Sstevel@tonic-gate prune_sib(struct node_list **headp)
6417c478bd9Sstevel@tonic-gate {
6427c478bd9Sstevel@tonic-gate 	di_node_t parent, curr_par, curr_gpar;
6437c478bd9Sstevel@tonic-gate 	struct node_list *curr, *prev;
6447c478bd9Sstevel@tonic-gate 
6457c478bd9Sstevel@tonic-gate 	/*
6467c478bd9Sstevel@tonic-gate 	 * get handle to parent of first node
6477c478bd9Sstevel@tonic-gate 	 */
6487c478bd9Sstevel@tonic-gate 	if ((parent = di_parent_node((*headp)->node)) == DI_NODE_NIL) {
6497c478bd9Sstevel@tonic-gate 		/*
6507c478bd9Sstevel@tonic-gate 		 * This must be the root of the snapshot, so can't
6517c478bd9Sstevel@tonic-gate 		 * have any siblings.
6527c478bd9Sstevel@tonic-gate 		 *
6537c478bd9Sstevel@tonic-gate 		 * XXX Put a check here just in case.
6547c478bd9Sstevel@tonic-gate 		 */
6557c478bd9Sstevel@tonic-gate 		if ((*headp)->next)
6567c478bd9Sstevel@tonic-gate 			DPRINTF((DI_ERR, "Unexpected err in di_walk_node.\n"));
6577c478bd9Sstevel@tonic-gate 
6587c478bd9Sstevel@tonic-gate 		free(*headp);
6597c478bd9Sstevel@tonic-gate 		*headp = NULL;
6607c478bd9Sstevel@tonic-gate 		return;
6617c478bd9Sstevel@tonic-gate 	}
6627c478bd9Sstevel@tonic-gate 
6637c478bd9Sstevel@tonic-gate 	/*
6647c478bd9Sstevel@tonic-gate 	 * To be complete, we should also delete the children
6657c478bd9Sstevel@tonic-gate 	 * of siblings that have already been visited.
6667c478bd9Sstevel@tonic-gate 	 * This happens for DI_WALK_SIBFIRST when the first node
6677c478bd9Sstevel@tonic-gate 	 * is NOT the first in the linked list of siblings.
6687c478bd9Sstevel@tonic-gate 	 *
6697c478bd9Sstevel@tonic-gate 	 * Hence, we compare parent with BOTH the parent and grandparent
6707c478bd9Sstevel@tonic-gate 	 * of nodes, and delete node is a match is found.
6717c478bd9Sstevel@tonic-gate 	 */
6727c478bd9Sstevel@tonic-gate 	prev = *headp;
6737c478bd9Sstevel@tonic-gate 	curr = prev->next;
6747c478bd9Sstevel@tonic-gate 	while (curr) {
6757c478bd9Sstevel@tonic-gate 		if (((curr_par = di_parent_node(curr->node)) != DI_NODE_NIL) &&
6767c478bd9Sstevel@tonic-gate 		    ((curr_par == parent) || ((curr_gpar =
6777c478bd9Sstevel@tonic-gate 		    di_parent_node(curr_par)) != DI_NODE_NIL) &&
6787c478bd9Sstevel@tonic-gate 		    (curr_gpar == parent))) {
6797c478bd9Sstevel@tonic-gate 			/*
6807c478bd9Sstevel@tonic-gate 			 * match parent/grandparent: delete curr
6817c478bd9Sstevel@tonic-gate 			 */
6827c478bd9Sstevel@tonic-gate 			prev->next = curr->next;
6837c478bd9Sstevel@tonic-gate 			free(curr);
6847c478bd9Sstevel@tonic-gate 			curr = prev->next;
6857c478bd9Sstevel@tonic-gate 		} else
6867c478bd9Sstevel@tonic-gate 			curr = curr->next;
6877c478bd9Sstevel@tonic-gate 	}
6887c478bd9Sstevel@tonic-gate 
6897c478bd9Sstevel@tonic-gate 	/*
6907c478bd9Sstevel@tonic-gate 	 * delete the first node
6917c478bd9Sstevel@tonic-gate 	 */
6927c478bd9Sstevel@tonic-gate 	curr = *headp;
6937c478bd9Sstevel@tonic-gate 	*headp = curr->next;
6947c478bd9Sstevel@tonic-gate 	free(curr);
6957c478bd9Sstevel@tonic-gate }
6967c478bd9Sstevel@tonic-gate 
6977c478bd9Sstevel@tonic-gate /*
6987c478bd9Sstevel@tonic-gate  * Internal library function:
6997c478bd9Sstevel@tonic-gate  *	Update node list based on action (return code from callback)
7007c478bd9Sstevel@tonic-gate  *	and flag specifying walking behavior.
7017c478bd9Sstevel@tonic-gate  */
7027c478bd9Sstevel@tonic-gate static void
update_node_list(int action,uint_t flag,struct node_list ** headp)7037c478bd9Sstevel@tonic-gate update_node_list(int action, uint_t flag, struct node_list **headp)
7047c478bd9Sstevel@tonic-gate {
7057c478bd9Sstevel@tonic-gate 	struct node_list *children, *tmp;
7067c478bd9Sstevel@tonic-gate 	di_node_t parent = di_parent_node((*headp)->node);
7077c478bd9Sstevel@tonic-gate 
7087c478bd9Sstevel@tonic-gate 	switch (action) {
7097c478bd9Sstevel@tonic-gate 	case DI_WALK_TERMINATE:
7107c478bd9Sstevel@tonic-gate 		/*
7117c478bd9Sstevel@tonic-gate 		 * free the node list and be done
7127c478bd9Sstevel@tonic-gate 		 */
7137c478bd9Sstevel@tonic-gate 		children = NULL;
7147c478bd9Sstevel@tonic-gate 		free_node_list(headp);
7157c478bd9Sstevel@tonic-gate 		break;
7167c478bd9Sstevel@tonic-gate 
7177c478bd9Sstevel@tonic-gate 	case DI_WALK_PRUNESIB:
7187c478bd9Sstevel@tonic-gate 		/*
7197c478bd9Sstevel@tonic-gate 		 * Get list of children and prune siblings
7207c478bd9Sstevel@tonic-gate 		 */
7217c478bd9Sstevel@tonic-gate 		children = get_children((*headp)->node);
7227c478bd9Sstevel@tonic-gate 		prune_sib(headp);
7237c478bd9Sstevel@tonic-gate 		break;
7247c478bd9Sstevel@tonic-gate 
7257c478bd9Sstevel@tonic-gate 	case DI_WALK_PRUNECHILD:
7267c478bd9Sstevel@tonic-gate 		/*
7277c478bd9Sstevel@tonic-gate 		 * Set children to NULL and pop first node
7287c478bd9Sstevel@tonic-gate 		 */
7297c478bd9Sstevel@tonic-gate 		children = NULL;
7307c478bd9Sstevel@tonic-gate 		tmp = *headp;
7317c478bd9Sstevel@tonic-gate 		*headp = tmp->next;
7327c478bd9Sstevel@tonic-gate 		free(tmp);
7337c478bd9Sstevel@tonic-gate 		break;
7347c478bd9Sstevel@tonic-gate 
7357c478bd9Sstevel@tonic-gate 	case DI_WALK_CONTINUE:
7367c478bd9Sstevel@tonic-gate 	default:
7377c478bd9Sstevel@tonic-gate 		/*
7387c478bd9Sstevel@tonic-gate 		 * Get list of children and pop first node
7397c478bd9Sstevel@tonic-gate 		 */
7407c478bd9Sstevel@tonic-gate 		children = get_children((*headp)->node);
7417c478bd9Sstevel@tonic-gate 		tmp = *headp;
7427c478bd9Sstevel@tonic-gate 		*headp = tmp->next;
7437c478bd9Sstevel@tonic-gate 		free(tmp);
7447c478bd9Sstevel@tonic-gate 		break;
7457c478bd9Sstevel@tonic-gate 	}
7467c478bd9Sstevel@tonic-gate 
7477c478bd9Sstevel@tonic-gate 	/*
7487c478bd9Sstevel@tonic-gate 	 * insert the list of children
7497c478bd9Sstevel@tonic-gate 	 */
7507c478bd9Sstevel@tonic-gate 	switch (flag) {
7517c478bd9Sstevel@tonic-gate 	case DI_WALK_CLDFIRST:
7527c478bd9Sstevel@tonic-gate 		prepend_node_list(headp, children);
7537c478bd9Sstevel@tonic-gate 		break;
7547c478bd9Sstevel@tonic-gate 
7557c478bd9Sstevel@tonic-gate 	case DI_WALK_SIBFIRST:
7567c478bd9Sstevel@tonic-gate 		append_node_list(headp, children);
7577c478bd9Sstevel@tonic-gate 		break;
7587c478bd9Sstevel@tonic-gate 
7597c478bd9Sstevel@tonic-gate 	case DI_WALK_LINKGEN:
7607c478bd9Sstevel@tonic-gate 	default:
7617c478bd9Sstevel@tonic-gate 		insert_node_list(headp, children, parent);
7627c478bd9Sstevel@tonic-gate 		break;
7637c478bd9Sstevel@tonic-gate 	}
7647c478bd9Sstevel@tonic-gate }
7657c478bd9Sstevel@tonic-gate 
7667c478bd9Sstevel@tonic-gate /*
7677c478bd9Sstevel@tonic-gate  * Internal library function:
7687c478bd9Sstevel@tonic-gate  *   Invoke callback on one node and update the list of nodes to be walked
7697c478bd9Sstevel@tonic-gate  *   based on the flag and return code.
7707c478bd9Sstevel@tonic-gate  */
7717c478bd9Sstevel@tonic-gate static void
walk_one_node(struct node_list ** headp,uint_t flag,void * arg,int (* callback)(di_node_t,void *))7727c478bd9Sstevel@tonic-gate walk_one_node(struct node_list **headp, uint_t flag, void *arg,
773*f00128d8SToomas Soome     int (*callback)(di_node_t, void *))
7747c478bd9Sstevel@tonic-gate {
7757c478bd9Sstevel@tonic-gate 	DPRINTF((DI_TRACE, "Walking node %s\n", di_node_name((*headp)->node)));
7767c478bd9Sstevel@tonic-gate 
7777c478bd9Sstevel@tonic-gate 	update_node_list(callback((*headp)->node, arg),
7787c478bd9Sstevel@tonic-gate 	    flag & DI_WALK_MASK, headp);
7797c478bd9Sstevel@tonic-gate }
7807c478bd9Sstevel@tonic-gate 
7817c478bd9Sstevel@tonic-gate int
di_walk_node(di_node_t root,uint_t flag,void * arg,int (* node_callback)(di_node_t,void *))7827c478bd9Sstevel@tonic-gate di_walk_node(di_node_t root, uint_t flag, void *arg,
783*f00128d8SToomas Soome     int (*node_callback)(di_node_t, void *))
7847c478bd9Sstevel@tonic-gate {
7857c478bd9Sstevel@tonic-gate 	struct node_list  *head;	/* node_list for tree walk */
7867c478bd9Sstevel@tonic-gate 
7877c478bd9Sstevel@tonic-gate 	if (root == NULL) {
7887c478bd9Sstevel@tonic-gate 		errno = EINVAL;
7897c478bd9Sstevel@tonic-gate 		return (-1);
7907c478bd9Sstevel@tonic-gate 	}
7917c478bd9Sstevel@tonic-gate 
7927c478bd9Sstevel@tonic-gate 	if ((head = malloc(sizeof (struct node_list))) == NULL) {
7937c478bd9Sstevel@tonic-gate 		DPRINTF((DI_ERR, "malloc of node_list failed\n"));
7947c478bd9Sstevel@tonic-gate 		return (-1);
7957c478bd9Sstevel@tonic-gate 	}
7967c478bd9Sstevel@tonic-gate 
7977c478bd9Sstevel@tonic-gate 	head->next = NULL;
7987c478bd9Sstevel@tonic-gate 	head->node = root;
7997c478bd9Sstevel@tonic-gate 
8007c478bd9Sstevel@tonic-gate 	DPRINTF((DI_INFO, "Start node walking from node %s\n",
8017c478bd9Sstevel@tonic-gate 	    di_node_name(root)));
8027c478bd9Sstevel@tonic-gate 
8037c478bd9Sstevel@tonic-gate 	while (head != NULL)
8047c478bd9Sstevel@tonic-gate 		walk_one_node(&head, flag, arg, node_callback);
8057c478bd9Sstevel@tonic-gate 
8067c478bd9Sstevel@tonic-gate 	return (0);
8077c478bd9Sstevel@tonic-gate }
8087c478bd9Sstevel@tonic-gate 
8097c478bd9Sstevel@tonic-gate /*
8107c478bd9Sstevel@tonic-gate  * Internal library function:
8117c478bd9Sstevel@tonic-gate  *   Invoke callback for each minor on the minor list of first node
8127c478bd9Sstevel@tonic-gate  *   on node_list headp, and place children of first node on the list.
8137c478bd9Sstevel@tonic-gate  *
8147c478bd9Sstevel@tonic-gate  *   This is similar to walk_one_node, except we only walk in child
8157c478bd9Sstevel@tonic-gate  *   first mode.
8167c478bd9Sstevel@tonic-gate  */
8177c478bd9Sstevel@tonic-gate static void
walk_one_minor_list(struct node_list ** headp,const char * desired_type,uint_t flag,void * arg,int (* callback)(di_node_t,di_minor_t,void *))8187c478bd9Sstevel@tonic-gate walk_one_minor_list(struct node_list **headp, const char *desired_type,
819*f00128d8SToomas Soome     uint_t flag, void *arg, int (*callback)(di_node_t, di_minor_t, void *))
8207c478bd9Sstevel@tonic-gate {
8217c478bd9Sstevel@tonic-gate 	int ddm_type;
8227c478bd9Sstevel@tonic-gate 	int action = DI_WALK_CONTINUE;
8237c478bd9Sstevel@tonic-gate 	char *node_type;
8247c478bd9Sstevel@tonic-gate 	di_minor_t minor = DI_MINOR_NIL;
8257c478bd9Sstevel@tonic-gate 	di_node_t node = (*headp)->node;
8267c478bd9Sstevel@tonic-gate 
8277c478bd9Sstevel@tonic-gate 	while ((minor = di_minor_next(node, minor)) != DI_MINOR_NIL) {
8287c478bd9Sstevel@tonic-gate 		ddm_type = di_minor_type(minor);
8297c478bd9Sstevel@tonic-gate 
8307c478bd9Sstevel@tonic-gate 		if ((ddm_type == DDM_ALIAS) && !(flag & DI_CHECK_ALIAS))
8317c478bd9Sstevel@tonic-gate 			continue;
8327c478bd9Sstevel@tonic-gate 
8337c478bd9Sstevel@tonic-gate 		if ((ddm_type == DDM_INTERNAL_PATH) &&
8347c478bd9Sstevel@tonic-gate 		    !(flag & DI_CHECK_INTERNAL_PATH))
8357c478bd9Sstevel@tonic-gate 			continue;
8367c478bd9Sstevel@tonic-gate 
8377c478bd9Sstevel@tonic-gate 		node_type = di_minor_nodetype(minor);
8387c478bd9Sstevel@tonic-gate 		if ((desired_type != NULL) && ((node_type == NULL) ||
8397c478bd9Sstevel@tonic-gate 		    strncmp(desired_type, node_type, strlen(desired_type))
8407c478bd9Sstevel@tonic-gate 		    != 0))
8417c478bd9Sstevel@tonic-gate 			continue;
8427c478bd9Sstevel@tonic-gate 
8437c478bd9Sstevel@tonic-gate 		if ((action = callback(node, minor, arg)) ==
8447c478bd9Sstevel@tonic-gate 		    DI_WALK_TERMINATE) {
8457c478bd9Sstevel@tonic-gate 			break;
8467c478bd9Sstevel@tonic-gate 		}
8477c478bd9Sstevel@tonic-gate 	}
8487c478bd9Sstevel@tonic-gate 
8497c478bd9Sstevel@tonic-gate 	update_node_list(action, DI_WALK_LINKGEN, headp);
8507c478bd9Sstevel@tonic-gate }
8517c478bd9Sstevel@tonic-gate 
8527c478bd9Sstevel@tonic-gate int
di_walk_minor(di_node_t root,const char * minor_type,uint_t flag,void * arg,int (* minor_callback)(di_node_t,di_minor_t,void *))8537c478bd9Sstevel@tonic-gate di_walk_minor(di_node_t root, const char *minor_type, uint_t flag, void *arg,
854*f00128d8SToomas Soome     int (*minor_callback)(di_node_t, di_minor_t, void *))
8557c478bd9Sstevel@tonic-gate {
856602ca9eaScth 	struct node_list	*head;	/* node_list for tree walk */
8577c478bd9Sstevel@tonic-gate 
8587c478bd9Sstevel@tonic-gate #ifdef DEBUG
859602ca9eaScth 	char	*devfspath = di_devfs_path(root);
860602ca9eaScth 	DPRINTF((DI_INFO, "walking minor nodes under %s\n", devfspath));
861602ca9eaScth 	di_devfs_path_free(devfspath);
8627c478bd9Sstevel@tonic-gate #endif
8637c478bd9Sstevel@tonic-gate 
8647c478bd9Sstevel@tonic-gate 	if (root == NULL) {
8657c478bd9Sstevel@tonic-gate 		errno = EINVAL;
8667c478bd9Sstevel@tonic-gate 		return (-1);
8677c478bd9Sstevel@tonic-gate 	}
8687c478bd9Sstevel@tonic-gate 
8697c478bd9Sstevel@tonic-gate 	if ((head = malloc(sizeof (struct node_list))) == NULL) {
8707c478bd9Sstevel@tonic-gate 		DPRINTF((DI_ERR, "malloc of node_list failed\n"));
8717c478bd9Sstevel@tonic-gate 		return (-1);
8727c478bd9Sstevel@tonic-gate 	}
8737c478bd9Sstevel@tonic-gate 
8747c478bd9Sstevel@tonic-gate 	head->next = NULL;
8757c478bd9Sstevel@tonic-gate 	head->node = root;
8767c478bd9Sstevel@tonic-gate 
8777c478bd9Sstevel@tonic-gate 	DPRINTF((DI_INFO, "Start minor walking from node %s\n",
878602ca9eaScth 	    di_node_name(root)));
8797c478bd9Sstevel@tonic-gate 
8807c478bd9Sstevel@tonic-gate 	while (head != NULL)
8817c478bd9Sstevel@tonic-gate 		walk_one_minor_list(&head, minor_type, flag, arg,
8827c478bd9Sstevel@tonic-gate 		    minor_callback);
8837c478bd9Sstevel@tonic-gate 
8847c478bd9Sstevel@tonic-gate 	return (0);
8857c478bd9Sstevel@tonic-gate }
8867c478bd9Sstevel@tonic-gate 
8877c478bd9Sstevel@tonic-gate /*
8887c478bd9Sstevel@tonic-gate  * generic node parameters
8897c478bd9Sstevel@tonic-gate  *   Calling these routines always succeeds.
8907c478bd9Sstevel@tonic-gate  */
8917c478bd9Sstevel@tonic-gate char *
di_node_name(di_node_t node)8927c478bd9Sstevel@tonic-gate di_node_name(di_node_t node)
8937c478bd9Sstevel@tonic-gate {
8947c478bd9Sstevel@tonic-gate 	return ((caddr_t)node + DI_NODE(node)->node_name - DI_NODE(node)->self);
8957c478bd9Sstevel@tonic-gate }
8967c478bd9Sstevel@tonic-gate 
8977c478bd9Sstevel@tonic-gate /* returns NULL ptr or a valid ptr to non-NULL string */
8987c478bd9Sstevel@tonic-gate char *
di_bus_addr(di_node_t node)8997c478bd9Sstevel@tonic-gate di_bus_addr(di_node_t node)
9007c478bd9Sstevel@tonic-gate {
9017c478bd9Sstevel@tonic-gate 	caddr_t pa = (caddr_t)node - DI_NODE(node)->self;
9027c478bd9Sstevel@tonic-gate 
9037c478bd9Sstevel@tonic-gate 	if (DI_NODE(node)->address == 0)
9047c478bd9Sstevel@tonic-gate 		return (NULL);
9057c478bd9Sstevel@tonic-gate 
9067c478bd9Sstevel@tonic-gate 	return ((char *)(pa + DI_NODE(node)->address));
9077c478bd9Sstevel@tonic-gate }
9087c478bd9Sstevel@tonic-gate 
9097c478bd9Sstevel@tonic-gate char *
di_binding_name(di_node_t node)9107c478bd9Sstevel@tonic-gate di_binding_name(di_node_t node)
9117c478bd9Sstevel@tonic-gate {
9127c478bd9Sstevel@tonic-gate 	caddr_t pa = (caddr_t)node - DI_NODE(node)->self;
9137c478bd9Sstevel@tonic-gate 
9147c478bd9Sstevel@tonic-gate 	if (DI_NODE(node)->bind_name == 0)
9157c478bd9Sstevel@tonic-gate 		return (NULL);
9167c478bd9Sstevel@tonic-gate 
9177c478bd9Sstevel@tonic-gate 	return ((char *)(pa + DI_NODE(node)->bind_name));
9187c478bd9Sstevel@tonic-gate }
9197c478bd9Sstevel@tonic-gate 
9207c478bd9Sstevel@tonic-gate int
di_compatible_names(di_node_t node,char ** names)9217c478bd9Sstevel@tonic-gate di_compatible_names(di_node_t node, char **names)
9227c478bd9Sstevel@tonic-gate {
9237c478bd9Sstevel@tonic-gate 	char *c;
9247c478bd9Sstevel@tonic-gate 	int len, size, entries = 0;
9257c478bd9Sstevel@tonic-gate 
9267c478bd9Sstevel@tonic-gate 	if (DI_NODE(node)->compat_names == 0) {
9277c478bd9Sstevel@tonic-gate 		*names = NULL;
9287c478bd9Sstevel@tonic-gate 		return (0);
9297c478bd9Sstevel@tonic-gate 	}
9307c478bd9Sstevel@tonic-gate 
9317c478bd9Sstevel@tonic-gate 	*names = (caddr_t)node +
932602ca9eaScth 	    DI_NODE(node)->compat_names - DI_NODE(node)->self;
9337c478bd9Sstevel@tonic-gate 
9347c478bd9Sstevel@tonic-gate 	c = *names;
9357c478bd9Sstevel@tonic-gate 	len = DI_NODE(node)->compat_length;
9367c478bd9Sstevel@tonic-gate 	while (len > 0) {
9377c478bd9Sstevel@tonic-gate 		entries++;
9387c478bd9Sstevel@tonic-gate 		size = strlen(c) + 1;
9397c478bd9Sstevel@tonic-gate 		len -= size;
9407c478bd9Sstevel@tonic-gate 		c += size;
9417c478bd9Sstevel@tonic-gate 	}
9427c478bd9Sstevel@tonic-gate 
9437c478bd9Sstevel@tonic-gate 	return (entries);
9447c478bd9Sstevel@tonic-gate }
9457c478bd9Sstevel@tonic-gate 
9467c478bd9Sstevel@tonic-gate int
di_instance(di_node_t node)9477c478bd9Sstevel@tonic-gate di_instance(di_node_t node)
9487c478bd9Sstevel@tonic-gate {
9497c478bd9Sstevel@tonic-gate 	return (DI_NODE(node)->instance);
9507c478bd9Sstevel@tonic-gate }
9517c478bd9Sstevel@tonic-gate 
9527c478bd9Sstevel@tonic-gate /*
9537c478bd9Sstevel@tonic-gate  * XXX: emulate the return value of the old implementation
9547c478bd9Sstevel@tonic-gate  * using info from devi_node_class and devi_node_attributes.
9557c478bd9Sstevel@tonic-gate  */
9567c478bd9Sstevel@tonic-gate int
di_nodeid(di_node_t node)9577c478bd9Sstevel@tonic-gate di_nodeid(di_node_t node)
9587c478bd9Sstevel@tonic-gate {
9597c478bd9Sstevel@tonic-gate 	if (DI_NODE(node)->node_class == DDI_NC_PROM)
9607c478bd9Sstevel@tonic-gate 		return (DI_PROM_NODEID);
9617c478bd9Sstevel@tonic-gate 
9627c478bd9Sstevel@tonic-gate 	if (DI_NODE(node)->attributes & DDI_PERSISTENT)
9637c478bd9Sstevel@tonic-gate 		return (DI_SID_NODEID);
9647c478bd9Sstevel@tonic-gate 
9657c478bd9Sstevel@tonic-gate 	return (DI_PSEUDO_NODEID);
9667c478bd9Sstevel@tonic-gate }
9677c478bd9Sstevel@tonic-gate 
9687c478bd9Sstevel@tonic-gate uint_t
di_state(di_node_t node)9697c478bd9Sstevel@tonic-gate di_state(di_node_t node)
9707c478bd9Sstevel@tonic-gate {
9717c478bd9Sstevel@tonic-gate 	uint_t result = 0;
9727c478bd9Sstevel@tonic-gate 
9737c478bd9Sstevel@tonic-gate 	if (di_node_state(node) < DS_ATTACHED)
9747c478bd9Sstevel@tonic-gate 		result |= DI_DRIVER_DETACHED;
9757c478bd9Sstevel@tonic-gate 	if (DI_NODE(node)->state & DEVI_DEVICE_OFFLINE)
9767c478bd9Sstevel@tonic-gate 		result |= DI_DEVICE_OFFLINE;
9777c478bd9Sstevel@tonic-gate 	if (DI_NODE(node)->state & DEVI_DEVICE_DOWN)
9784c06356bSdh 		result |= DI_DEVICE_DOWN;
97925c6ff4bSstephh 	if (DI_NODE(node)->state & DEVI_DEVICE_DEGRADED)
98025c6ff4bSstephh 		result |= DI_DEVICE_DEGRADED;
9814c06356bSdh 	if (DI_NODE(node)->state & DEVI_DEVICE_REMOVED)
9824c06356bSdh 		result |= DI_DEVICE_REMOVED;
9837c478bd9Sstevel@tonic-gate 	if (DI_NODE(node)->state & DEVI_BUS_QUIESCED)
9847c478bd9Sstevel@tonic-gate 		result |= DI_BUS_QUIESCED;
9857c478bd9Sstevel@tonic-gate 	if (DI_NODE(node)->state & DEVI_BUS_DOWN)
9867c478bd9Sstevel@tonic-gate 		result |= DI_BUS_DOWN;
9877c478bd9Sstevel@tonic-gate 
9887c478bd9Sstevel@tonic-gate 	return (result);
9897c478bd9Sstevel@tonic-gate }
9907c478bd9Sstevel@tonic-gate 
9917c478bd9Sstevel@tonic-gate ddi_node_state_t
di_node_state(di_node_t node)9927c478bd9Sstevel@tonic-gate di_node_state(di_node_t node)
9937c478bd9Sstevel@tonic-gate {
9947c478bd9Sstevel@tonic-gate 	return (DI_NODE(node)->node_state);
9957c478bd9Sstevel@tonic-gate }
9967c478bd9Sstevel@tonic-gate 
9973e2676e0Svikram uint_t
di_flags(di_node_t node)9983e2676e0Svikram di_flags(di_node_t node)
9993e2676e0Svikram {
10003e2676e0Svikram 	return (DI_NODE(node)->flags);
10013e2676e0Svikram }
10023e2676e0Svikram 
100325e8c5aaSvikram uint_t
di_retired(di_node_t node)100425e8c5aaSvikram di_retired(di_node_t node)
100525e8c5aaSvikram {
100625e8c5aaSvikram 	return (di_flags(node) & DEVI_RETIRED);
100725e8c5aaSvikram }
100825e8c5aaSvikram 
10097c478bd9Sstevel@tonic-gate ddi_devid_t
di_devid(di_node_t node)10107c478bd9Sstevel@tonic-gate di_devid(di_node_t node)
10117c478bd9Sstevel@tonic-gate {
10127c478bd9Sstevel@tonic-gate 	if (DI_NODE(node)->devid == 0)
10137c478bd9Sstevel@tonic-gate 		return (NULL);
10147c478bd9Sstevel@tonic-gate 
10157c478bd9Sstevel@tonic-gate 	return ((ddi_devid_t)((caddr_t)node +
10167c478bd9Sstevel@tonic-gate 	    DI_NODE(node)->devid - DI_NODE(node)->self));
10177c478bd9Sstevel@tonic-gate }
10187c478bd9Sstevel@tonic-gate 
10197c478bd9Sstevel@tonic-gate int
di_driver_major(di_node_t node)10207c478bd9Sstevel@tonic-gate di_driver_major(di_node_t node)
10217c478bd9Sstevel@tonic-gate {
10227c478bd9Sstevel@tonic-gate 	int major;
10237c478bd9Sstevel@tonic-gate 
10247c478bd9Sstevel@tonic-gate 	major = DI_NODE(node)->drv_major;
10257c478bd9Sstevel@tonic-gate 	if (major < 0)
10267c478bd9Sstevel@tonic-gate 		return (-1);
10277c478bd9Sstevel@tonic-gate 	return (major);
10287c478bd9Sstevel@tonic-gate }
10297c478bd9Sstevel@tonic-gate 
10307c478bd9Sstevel@tonic-gate char *
di_driver_name(di_node_t node)10317c478bd9Sstevel@tonic-gate di_driver_name(di_node_t node)
10327c478bd9Sstevel@tonic-gate {
10337c478bd9Sstevel@tonic-gate 	int major;
10347c478bd9Sstevel@tonic-gate 	caddr_t pa;
10357c478bd9Sstevel@tonic-gate 	struct di_devnm *devnm;
10367c478bd9Sstevel@tonic-gate 
10377c478bd9Sstevel@tonic-gate 	major = DI_NODE(node)->drv_major;
10387c478bd9Sstevel@tonic-gate 	if (major < 0)
10397c478bd9Sstevel@tonic-gate 		return (NULL);
10407c478bd9Sstevel@tonic-gate 
10417c478bd9Sstevel@tonic-gate 	pa = (caddr_t)node - DI_NODE(node)->self;
10427c478bd9Sstevel@tonic-gate 	devnm = DI_DEVNM(pa + DI_ALL(pa)->devnames);
10437c478bd9Sstevel@tonic-gate 
10447c478bd9Sstevel@tonic-gate 	if (devnm[major].name)
10457c478bd9Sstevel@tonic-gate 		return (pa + devnm[major].name);
10467c478bd9Sstevel@tonic-gate 	else
10477c478bd9Sstevel@tonic-gate 		return (NULL);
10487c478bd9Sstevel@tonic-gate }
10497c478bd9Sstevel@tonic-gate 
10507c478bd9Sstevel@tonic-gate uint_t
di_driver_ops(di_node_t node)10517c478bd9Sstevel@tonic-gate di_driver_ops(di_node_t node)
10527c478bd9Sstevel@tonic-gate {
10537c478bd9Sstevel@tonic-gate 	int major;
10547c478bd9Sstevel@tonic-gate 	caddr_t pa;
10557c478bd9Sstevel@tonic-gate 	struct di_devnm *devnm;
10567c478bd9Sstevel@tonic-gate 
10577c478bd9Sstevel@tonic-gate 	major = DI_NODE(node)->drv_major;
10587c478bd9Sstevel@tonic-gate 	if (major < 0)
10597c478bd9Sstevel@tonic-gate 		return (0);
10607c478bd9Sstevel@tonic-gate 
10617c478bd9Sstevel@tonic-gate 	pa = (caddr_t)node - DI_NODE(node)->self;
10627c478bd9Sstevel@tonic-gate 	devnm = DI_DEVNM(pa + DI_ALL(pa)->devnames);
10637c478bd9Sstevel@tonic-gate 
10647c478bd9Sstevel@tonic-gate 	return (devnm[major].ops);
10657c478bd9Sstevel@tonic-gate }
10667c478bd9Sstevel@tonic-gate 
10677c478bd9Sstevel@tonic-gate /*
106825d35568SYuri Pankov  * Returns pointer to the allocated string, which must be freed by the caller.
10697c478bd9Sstevel@tonic-gate  */
10707c478bd9Sstevel@tonic-gate char *
di_devfs_path(di_node_t node)10717c478bd9Sstevel@tonic-gate di_devfs_path(di_node_t node)
10727c478bd9Sstevel@tonic-gate {
10737c478bd9Sstevel@tonic-gate 	caddr_t pa;
10747c478bd9Sstevel@tonic-gate 	di_node_t parent;
10757c478bd9Sstevel@tonic-gate 	int depth = 0, len = 0;
10767c478bd9Sstevel@tonic-gate 	char *buf, *name[MAX_TREE_DEPTH], *addr[MAX_TREE_DEPTH];
10777c478bd9Sstevel@tonic-gate 
10787c478bd9Sstevel@tonic-gate 	if (node == DI_NODE_NIL) {
10797c478bd9Sstevel@tonic-gate 		errno = EINVAL;
10807c478bd9Sstevel@tonic-gate 		return (NULL);
10817c478bd9Sstevel@tonic-gate 	}
10827c478bd9Sstevel@tonic-gate 
10837c478bd9Sstevel@tonic-gate 	/*
10847c478bd9Sstevel@tonic-gate 	 * trace back to root, note the node_name & address
10857c478bd9Sstevel@tonic-gate 	 */
10867c478bd9Sstevel@tonic-gate 	while ((parent = di_parent_node(node)) != DI_NODE_NIL) {
10877c478bd9Sstevel@tonic-gate 		name[depth] = di_node_name(node);
10887c478bd9Sstevel@tonic-gate 		len += strlen(name[depth]) + 1;		/* 1 for '/' */
10897c478bd9Sstevel@tonic-gate 
10907c478bd9Sstevel@tonic-gate 		if ((addr[depth] = di_bus_addr(node)) != NULL)
10917c478bd9Sstevel@tonic-gate 			len += strlen(addr[depth]) + 1;	/* 1 for '@' */
10927c478bd9Sstevel@tonic-gate 
10937c478bd9Sstevel@tonic-gate 		node = parent;
10947c478bd9Sstevel@tonic-gate 		depth++;
10957c478bd9Sstevel@tonic-gate 	}
10967c478bd9Sstevel@tonic-gate 
10977c478bd9Sstevel@tonic-gate 	/*
10987c478bd9Sstevel@tonic-gate 	 * get the path to the root of snapshot
10997c478bd9Sstevel@tonic-gate 	 */
11007c478bd9Sstevel@tonic-gate 	pa = (caddr_t)node - DI_NODE(node)->self;
11017c478bd9Sstevel@tonic-gate 	name[depth] = DI_ALL(pa)->root_path;
11027c478bd9Sstevel@tonic-gate 	len += strlen(name[depth]) + 1;
11037c478bd9Sstevel@tonic-gate 
11047c478bd9Sstevel@tonic-gate 	/*
11057c478bd9Sstevel@tonic-gate 	 * allocate buffer and assemble path
11067c478bd9Sstevel@tonic-gate 	 */
11077c478bd9Sstevel@tonic-gate 	if ((buf = malloc(len)) == NULL) {
11087c478bd9Sstevel@tonic-gate 		return (NULL);
11097c478bd9Sstevel@tonic-gate 	}
11107c478bd9Sstevel@tonic-gate 
11117c478bd9Sstevel@tonic-gate 	(void) strcpy(buf, name[depth]);
11127c478bd9Sstevel@tonic-gate 	len = strlen(buf);
11137c478bd9Sstevel@tonic-gate 	if (buf[len - 1] == '/')
11147c478bd9Sstevel@tonic-gate 		len--;	/* delete trailing '/' */
11157c478bd9Sstevel@tonic-gate 
11167c478bd9Sstevel@tonic-gate 	while (depth) {
11177c478bd9Sstevel@tonic-gate 		depth--;
11187c478bd9Sstevel@tonic-gate 		buf[len] = '/';
11197c478bd9Sstevel@tonic-gate 		(void) strcpy(buf + len + 1, name[depth]);
11207c478bd9Sstevel@tonic-gate 		len += strlen(name[depth]) + 1;
11217c478bd9Sstevel@tonic-gate 		if (addr[depth] && addr[depth][0] != '\0') {
11227c478bd9Sstevel@tonic-gate 			buf[len] = '@';
11237c478bd9Sstevel@tonic-gate 			(void) strcpy(buf + len + 1, addr[depth]);
11247c478bd9Sstevel@tonic-gate 			len += strlen(addr[depth]) + 1;
11257c478bd9Sstevel@tonic-gate 		}
11267c478bd9Sstevel@tonic-gate 	}
11277c478bd9Sstevel@tonic-gate 
11287c478bd9Sstevel@tonic-gate 	return (buf);
11297c478bd9Sstevel@tonic-gate }
11307c478bd9Sstevel@tonic-gate 
11317c478bd9Sstevel@tonic-gate char *
di_devfs_minor_path(di_minor_t minor)11327c478bd9Sstevel@tonic-gate di_devfs_minor_path(di_minor_t minor)
11337c478bd9Sstevel@tonic-gate {
11347c478bd9Sstevel@tonic-gate 	di_node_t	node;
1135602ca9eaScth 	char		*full_path, *name, *devfspath;
11367c478bd9Sstevel@tonic-gate 	int		full_path_len;
11377c478bd9Sstevel@tonic-gate 
11387c478bd9Sstevel@tonic-gate 	if (minor == DI_MINOR_NIL) {
11397c478bd9Sstevel@tonic-gate 		errno = EINVAL;
11407c478bd9Sstevel@tonic-gate 		return (NULL);
11417c478bd9Sstevel@tonic-gate 	}
11427c478bd9Sstevel@tonic-gate 
11437c478bd9Sstevel@tonic-gate 	name = di_minor_name(minor);
11447c478bd9Sstevel@tonic-gate 	node = di_minor_devinfo(minor);
1145602ca9eaScth 	devfspath = di_devfs_path(