Home | History | Annotate | Download | only in libdevinfo
      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 2008 Sun Microsystems, Inc.  All rights reserved.
     23  * Use is subject to license terms.
     24  */
     25 
     26 #pragma ident	"%Z%%M%	%I%	%E% SMI"
     27 
     28 /*
     29  * Interfaces for getting device configuration data from kernel
     30  * through the devinfo driver.
     31  */
     32 
     33 #include <stdio.h>
     34 #include <stdlib.h>
     35 #include <string.h>
     36 #include <strings.h>
     37 #include <stropts.h>
     38 #include <fcntl.h>
     39 #include <poll.h>
     40 #include <synch.h>
     41 #include <unistd.h>
     42 #include <sys/mkdev.h>
     43 #include <sys/obpdefs.h>
     44 #include <sys/stat.h>
     45 #include <sys/types.h>
     46 #include <sys/time.h>
     47 #include <sys/autoconf.h>
     48 #include <stdarg.h>
     49 
     50 #define	NDEBUG 1
     51 #include <assert.h>
     52 
     53 #include "libdevinfo.h"
     54 
     55 /*
     56  * Debug message levels
     57  */
     58 typedef enum {
     59 	DI_QUIET = 0,	/* No debug messages - the default */
     60 	DI_ERR = 1,
     61 	DI_INFO,
     62 	DI_TRACE,
     63 	DI_TRACE1,
     64 	DI_TRACE2
     65 } di_debug_t;
     66 
     67 int di_debug = DI_QUIET;
     68 
     69 #define	DPRINTF(args)	{ if (di_debug != DI_QUIET) dprint args; }
     70 
     71 void dprint(di_debug_t msglevel, const char *fmt, ...);
     72 
     73 
     74 #pragma init(_libdevinfo_init)
     75 
     76 void
     77 _libdevinfo_init()
     78 {
     79 	char	*debug_str = getenv("_LIBDEVINFO_DEBUG");
     80 
     81 	if (debug_str) {
     82 		errno = 0;
     83 		di_debug = atoi(debug_str);
     84 		if (errno || di_debug < DI_QUIET)
     85 			di_debug = DI_QUIET;
     86 	}
     87 }
     88 
     89 di_node_t
     90 di_init(const char *phys_path, uint_t flag)
     91 {
     92 	return (di_init_impl(phys_path, flag, NULL));
     93 }
     94 
     95 /*
     96  * We use blocking_open() to guarantee access to the devinfo device, if open()
     97  * is failing with EAGAIN.
     98  */
     99 static int
    100 blocking_open(const char *path, int oflag)
    101 {
    102 	int fd;
    103 
    104 	while ((fd = open(path, oflag)) == -1 && errno == EAGAIN)
    105 		(void) poll(NULL, 0, 1 * MILLISEC);
    106 
    107 	return (fd);
    108 }
    109 
    110 /* private interface */
    111 di_node_t
    112 di_init_driver(const char *drv_name, uint_t flag)
    113 {
    114 	int fd;
    115 	char driver[MAXPATHLEN];
    116 
    117 	/*
    118 	 * Don't allow drv_name to exceed MAXPATHLEN - 1, or 1023,
    119 	 * which should be sufficient for any sensible programmer.
    120 	 */
    121 	if ((drv_name == NULL) || (strlen(drv_name) >= MAXPATHLEN)) {
    122 		errno = EINVAL;
    123 		return (DI_NODE_NIL);
    124 	}
    125 	(void) strcpy(driver, drv_name);
    126 
    127 	/*
    128 	 * open the devinfo driver
    129 	 */
    130 	if ((fd = blocking_open("/devices/pseudo/devinfo@0:devinfo",
    131 	    O_RDONLY)) == -1) {
    132 		DPRINTF((DI_ERR, "devinfo open failed: errno = %d\n", errno));
    133 		return (DI_NODE_NIL);
    134 	}
    135 
    136 	if (ioctl(fd, DINFOLODRV, driver) != 0) {
    137 		DPRINTF((DI_ERR, "failed to load driver %s\n", driver));
    138 		(void) close(fd);
    139 		errno = ENXIO;
    140 		return (DI_NODE_NIL);
    141 	}
    142 	(void) close(fd);
    143 
    144 	/*
    145 	 * Driver load succeeded, return a snapshot
    146 	 */
    147 	return (di_init("/", flag));
    148 }
    149 
    150 di_node_t
    151 di_init_impl(const char *phys_path, uint_t flag,
    152 	struct di_priv_data *priv)
    153 {
    154 	caddr_t pa;
    155 	int fd, map_size;
    156 	struct di_all *dap;
    157 	struct dinfo_io dinfo_io;
    158 
    159 	uint_t pageoffset = sysconf(_SC_PAGESIZE) - 1;
    160 	uint_t pagemask = ~pageoffset;
    161 
    162 	DPRINTF((DI_INFO, "di_init: taking a snapshot\n"));
    163 
    164 	/*
    165 	 * Make sure there is no minor name in the path
    166 	 * and the path do not start with /devices....
    167 	 */
    168 	if (strchr(phys_path, ':') ||
    169 	    (strncmp(phys_path, "/devices", 8) == 0) ||
    170 	    (strlen(phys_path) > MAXPATHLEN)) {
    171 		errno = EINVAL;
    172 		return (DI_NODE_NIL);
    173 	}
    174 
    175 	if (strlen(phys_path) == 0)
    176 		(void) sprintf(dinfo_io.root_path, "/");
    177 	else if (*phys_path != '/')
    178 		(void) snprintf(dinfo_io.root_path, sizeof (dinfo_io.root_path),
    179 		    "/%s", phys_path);
    180 	else
    181 		(void) snprintf(dinfo_io.root_path, sizeof (dinfo_io.root_path),
    182 		    "%s", phys_path);
    183 
    184 	/*
    185 	 * If private data is requested, copy the format specification
    186 	 */
    187 	if (flag & DINFOPRIVDATA & 0xff) {
    188 		if (priv)
    189 			bcopy(priv, &dinfo_io.priv,
    190 			    sizeof (struct di_priv_data));
    191 		else {
    192 			errno = EINVAL;
    193 			return (DI_NODE_NIL);
    194 		}
    195 	}
    196 
    197 	/*
    198 	 * Attempt to open the devinfo driver.  Make a second attempt at the
    199 	 * read-only minor node if we don't have privileges to open the full
    200 	 * version _and_ if we're not requesting operations that the read-only
    201 	 * node can't perform.  (Setgid processes would fail an access() test,
    202 	 * of course.)
    203 	 */
    204 	if ((fd = blocking_open("/devices/pseudo/devinfo@0:devinfo",
    205 	    O_RDONLY)) == -1) {
    206 		if ((flag & DINFOFORCE) == DINFOFORCE ||
    207 		    (flag & DINFOPRIVDATA) == DINFOPRIVDATA) {
    208 			/*
    209 			 * We wanted to perform a privileged operation, but the
    210 			 * privileged node isn't available.  Don't modify errno
    211 			 * on our way out (but display it if we're running with
    212 			 * di_debug set).
    213 			 */
    214 			DPRINTF((DI_ERR, "devinfo open failed: errno = %d\n",
    215 			    errno));
    216 			return (DI_NODE_NIL);
    217 		}
    218 
    219 		if ((fd = blocking_open("/devices/pseudo/devinfo@0:devinfo,ro",
    220 		    O_RDONLY)) == -1) {
    221 			DPRINTF((DI_ERR, "devinfo open failed: errno = %d\n",
    222 			    errno));
    223 			return (DI_NODE_NIL);
    224 		}
    225 	}
    226 
    227 	/*
    228 	 * Verify that there is no major conflict, i.e., we are indeed opening
    229 	 * the devinfo driver.
    230 	 */
    231 	if (ioctl(fd, DINFOIDENT, NULL) != DI_MAGIC) {
    232 		DPRINTF((DI_ERR,
    233 		    "driver ID failed; check for major conflict\n"));
    234 		(void) close(fd);
    235 		return (DI_NODE_NIL);
    236 	}
    237 
    238 	/*
    239 	 * create snapshot
    240 	 */
    241 	if ((map_size = ioctl(fd, flag, &dinfo_io)) < 0) {
    242 		DPRINTF((DI_ERR, "devinfo ioctl failed with "
    243 		    "error: %d\n", errno));
    244 		(void) close(fd);
    245 		return (DI_NODE_NIL);
    246 	} else if (map_size == 0) {
    247 		DPRINTF((DI_ERR, "%s not found\n", phys_path));
    248 		errno = ENXIO;
    249 		(void) close(fd);
    250 		return (DI_NODE_NIL);
    251 	}
    252 
    253 	/*
    254 	 * copy snapshot to userland
    255 	 */
    256 	map_size = (map_size + pageoffset) & pagemask;
    257 	if ((pa = valloc(map_size)) == NULL) {
    258 		DPRINTF((DI_ERR, "valloc failed for snapshot\n"));
    259 		(void) close(fd);
    260 		return (DI_NODE_NIL);
    261 	}
    262 
    263 	if (ioctl(fd, DINFOUSRLD, pa) != map_size) {
    264 		DPRINTF((DI_ERR, "failed to copy snapshot to usrld\n"));
    265 		(void) close(fd);
    266 		free(pa);
    267 		errno = EFAULT;
    268 		return (DI_NODE_NIL);
    269 	}
    270 
    271 	(void) close(fd);
    272 
    273 	dap = DI_ALL(pa);
    274 	if (dap->version != DI_SNAPSHOT_VERSION) {
    275 		DPRINTF((DI_ERR, "wrong snapshot version "
    276 		    "(expected=%d, actual=%d)\n",
    277 		    DI_SNAPSHOT_VERSION, dap->version));
    278 		free(pa);
    279 		errno = ESTALE;
    280 		return (DI_NODE_NIL);
    281 	}
    282 	if (dap->top_devinfo == 0) {	/* phys_path not found */
    283 		DPRINTF((DI_ERR, "%s not found\n", phys_path));
    284 		free(pa);
    285 		errno = EINVAL;
    286 		return (DI_NODE_NIL);
    287 	}
    288 
    289 	return (DI_NODE(pa + dap->top_devinfo));
    290 }
    291 
    292 void
    293 di_fini(di_node_t root)
    294 {
    295 	caddr_t pa;		/* starting address of map */
    296 
    297 	DPRINTF((DI_INFO, "di_fini: freeing a snapshot\n"));
    298 
    299 	/*
    300 	 * paranoid checking
    301 	 */
    302 	if (root == DI_NODE_NIL) {
    303 		DPRINTF((DI_ERR, "di_fini called with NIL arg\n"));
    304 		return;
    305 	}
    306 
    307 	/*
    308 	 * The root contains its own offset--self.
    309 	 * Subtracting it from root address, we get the starting addr.
    310 	 * The map_size is stored at the beginning of snapshot.
    311 	 * Once we have starting address and size, we can free().
    312 	 */
    313 	pa = (caddr_t)root - DI_NODE(root)->self;
    314 
    315 	free(pa);
    316 }
    317 
    318 di_node_t
    319 di_parent_node(di_node_t node)
    320 {
    321 	caddr_t pa;		/* starting address of map */
    322 
    323 	if (node == DI_NODE_NIL) {
    324 		errno = EINVAL;
    325 		return (DI_NODE_NIL);
    326 	}
    327 
    328 	DPRINTF((DI_TRACE, "Get parent of node %s\n", di_node_name(node)));
    329 
    330 	pa = (caddr_t)node - DI_NODE(node)->self;
    331 
    332 	if (DI_NODE(node)->parent) {
    333 		return (DI_NODE(pa + DI_NODE(node)->parent));
    334 	}
    335 
    336 	/*
    337 	 * Deal with error condition:
    338 	 *   If parent doesn't exist and node is not the root,
    339 	 *   set errno to ENOTSUP. Otherwise, set errno to ENXIO.
    340 	 */
    341 	if (strcmp(DI_ALL(pa)->root_path, "/") != 0)
    342 		errno = ENOTSUP;
    343 	else
    344 		errno = ENXIO;
    345 
    346 	return (DI_NODE_NIL);
    347 }
    348 
    349 di_node_t
    350 di_sibling_node(di_node_t node)
    351 {
    352 	caddr_t pa;		/* starting address of map */
    353 
    354 	if (node == DI_NODE_NIL) {
    355 		errno = EINVAL;
    356 		return (DI_NODE_NIL);
    357 	}
    358 
    359 	DPRINTF((DI_TRACE, "Get sibling of node %s\n", di_node_name(node)));
    360 
    361 	pa = (caddr_t)node - DI_NODE(node)->self;
    362 
    363 	if (DI_NODE(node)->sibling) {
    364 		return (DI_NODE(pa + DI_NODE(node)->sibling));
    365 	}
    366 
    367 	/*
    368 	 * Deal with error condition:
    369 	 *   Sibling doesn't exist, figure out if ioctl command
    370 	 *   has DINFOSUBTREE set. If it doesn't, set errno to
    371 	 *   ENOTSUP.
    372 	 */
    373 	if (!(DI_ALL(pa)->command & DINFOSUBTREE))
    374 		errno = ENOTSUP;
    375 	else
    376 		errno = ENXIO;
    377 
    378 	return (DI_NODE_NIL);
    379 }
    380 
    381 di_node_t
    382 di_child_node(di_node_t node)
    383 {
    384 	caddr_t pa;		/* starting address of map */
    385 
    386 	DPRINTF((DI_TRACE, "Get child of node %s\n", di_node_name(node)));
    387 
    388 	if (node == DI_NODE_NIL) {
    389 		errno = EINVAL;
    390 		return (DI_NODE_NIL);
    391 	}
    392 
    393 	pa = (caddr_t)node - DI_NODE(node)->self;
    394 
    395 	if (DI_NODE(node)->child) {
    396 		return (DI_NODE(pa + DI_NODE(node)->child));
    397 	}
    398 
    399 	/*
    400 	 * Deal with error condition:
    401 	 *   Child doesn't exist, figure out if DINFOSUBTREE is set.
    402 	 *   If it isn't, set errno to ENOTSUP.
    403 	 */
    404 	if (!(DI_ALL(pa)->command & DINFOSUBTREE))
    405 		errno = ENOTSUP;
    406 	else
    407 		errno = ENXIO;
    408 
    409 	return (DI_NODE_NIL);
    410 }
    411 
    412 di_node_t
    413 di_drv_first_node(const char *drv_name, di_node_t root)
    414 {
    415 	caddr_t		pa;		/* starting address of map */
    416 	int		major, devcnt;
    417 	struct di_devnm	*devnm;
    418 
    419 	DPRINTF((DI_INFO, "Get first node of driver %s\n", drv_name));
    420 
    421 	if (root == DI_NODE_NIL) {
    422 		errno = EINVAL;
    423 		return (DI_NODE_NIL);
    424 	}
    425 
    426 	/*
    427 	 * get major number of driver
    428 	 */
    429 	pa = (caddr_t)root - DI_NODE(root)->self;
    430 	devcnt = DI_ALL(pa)->devcnt;
    431 	devnm = DI_DEVNM(pa + DI_ALL(pa)->devnames);
    432 
    433 	for (major = 0; major < devcnt; major++)
    434 		if (devnm[major].name && (strcmp(drv_name,
    435 		    (char *)(pa + devnm[major].name)) == 0))
    436 			break;
    437 
    438 	if (major >= devcnt) {
    439 		errno = EINVAL;
    440 		return (DI_NODE_NIL);
    441 	}
    442 
    443 	if (!(devnm[major].head)) {
    444 		errno = ENXIO;
    445 		return (DI_NODE_NIL);
    446 	}
    447 
    448 	return (DI_NODE(pa + devnm[major].head));
    449 }
    450 
    451 di_node_t
    452 di_drv_next_node(di_node_t node)
    453 {
    454 	caddr_t		pa;		/* starting address of map */
    455 
    456 	if (node == DI_NODE_NIL) {
    457 		errno = EINVAL;
    458 		return (DI_NODE_NIL);
    459 	}
    460 
    461 	DPRINTF((DI_TRACE, "next node on per driver list:"
    462 	    " current=%s, driver=%s\n",
    463 	    di_node_name(node), di_driver_name(node)));
    464 
    465 	if (DI_NODE(node)->next == (di_off_t)-1) {
    466 		errno = ENOTSUP;
    467 		return (DI_NODE_NIL);
    468 	}
    469 
    470 	pa = (caddr_t)node - DI_NODE(node)->self;
    471 
    472 	if (DI_NODE(node)->next == NULL) {
    473 		errno = ENXIO;
    474 		return (DI_NODE_NIL);
    475 	}
    476 
    477 	return (DI_NODE(pa + DI_NODE(node)->next));
    478 }
    479 
    480 /*
    481  * Internal library interfaces:
    482  *   node_list etc. for node walking
    483  */
    484 struct node_list {
    485 	struct node_list *next;
    486 	di_node_t node;
    487 };
    488 
    489 static void
    490 free_node_list(struct node_list **headp)
    491 {
    492 	struct node_list *tmp;
    493 
    494 	while (*headp) {
    495 		tmp = *headp;
    496 		*headp = (*headp)->next;
    497 		free(tmp);
    498 	}
    499 }
    500 
    501 static void
    502 append_node_list(struct node_list **headp, struct node_list *list)
    503 {
    504 	struct node_list *tmp;
    505 
    506 	if (*headp == NULL) {
    507 		*headp = list;
    508 		return;
    509 	}
    510 
    511 	if (list == NULL)	/* a minor optimization */
    512 		return;
    513 
    514 	tmp = *headp;
    515 	while (tmp->next)
    516 		tmp = tmp->next;
    517 
    518 	tmp->next = list;
    519 }
    520 
    521 static void
    522 prepend_node_list(struct node_list **headp, struct node_list *list)
    523 {
    524 	struct node_list *tmp;
    525 
    526 	if (list == NULL)
    527 		return;
    528 
    529 	tmp = *headp;
    530 	*headp = list;
    531 
    532 	if (tmp == NULL)	/* a minor optimization */
    533 		return;
    534 
    535 	while (list->next)
    536 		list = list->next;
    537 
    538 	list->next = tmp;
    539 }
    540 
    541 /*
    542  * returns 1 if node is a descendant of parent, 0 otherwise
    543  */
    544 static int
    545 is_descendant(di_node_t node, di_node_t parent)
    546 {
    547 	/*
    548 	 * DI_NODE_NIL is parent of root, so it is
    549 	 * the parent of all nodes.
    550 	 */
    551 	if (parent == DI_NODE_NIL) {
    552 		return (1);
    553 	}
    554 
    555 	do {
    556 		node = di_parent_node(node);
    557 	} while ((node != DI_NODE_NIL) && (node != parent));
    558 
    559 	return (node != DI_NODE_NIL);
    560 }
    561 
    562 /*
    563  * Insert list before the first node which is NOT a descendent of parent.
    564  * This is needed to reproduce the exact walking order of link generators.
    565  */
    566 static void
    567 insert_node_list(struct node_list **headp, struct node_list *list,
    568     di_node_t parent)
    569 {
    570 	struct node_list *tmp, *tmp1;
    571 
    572 	if (list == NULL)
    573 		return;
    574 
    575 	tmp = *headp;
    576 	if (tmp == NULL) {	/* a minor optimization */
    577 		*headp = list;
    578 		return;
    579 	}
    580 
    581 	if (!is_descendant(tmp->node, parent)) {
    582 		prepend_node_list(headp, list);
    583 		return;
    584 	}
    585 
    586 	/*
    587 	 * Find first node which is not a descendant
    588 	 */
    589 	while (tmp->next && is_descendant(tmp->next->node, parent)) {
    590 		tmp = tmp->next;
    591 	}
    592 
    593 	tmp1 = tmp->next;
    594 	tmp->next = list;
    595 	append_node_list(headp, tmp1);
    596 }
    597 
    598 /*
    599  *   Get a linked list of handles of all children
    600  */
    601 static struct node_list *
    602 get_children(di_node_t node)
    603 {
    604 	di_node_t child;
    605 	struct node_list *result, *tmp;
    606 
    607 	DPRINTF((DI_TRACE1, "Get children of node %s\n", di_node_name(node)));
    608 
    609 	if ((child = di_child_node(node)) == DI_NODE_NIL) {
    610 		return (NULL);
    611 	}
    612 
    613 	if ((result = malloc(sizeof (struct node_list))) == NULL) {
    614 		DPRINTF((DI_ERR, "malloc of node_list failed\n"));
    615 		return (NULL);
    616 	}
    617 
    618 	result->node = child;
    619 	tmp = result;
    620 
    621 	while ((child = di_sibling_node(tmp->node)) != DI_NODE_NIL) {
    622 		if ((tmp->next = malloc(sizeof (struct node_list))) == NULL) {
    623 			DPRINTF((DI_ERR, "malloc of node_list failed\n"));
    624 			free_node_list(&result);
    625 			return (NULL);
    626 		}
    627 		tmp = tmp->next;
    628 		tmp->node = child;
    629 	}
    630 
    631 	tmp->next = NULL;
    632 
    633 	return (result);
    634 }
    635 
    636 /*
    637  * Internal library interface:
    638  *   Delete all siblings of the first node from the node_list, along with
    639  *   the first node itself.
    640  */
    641 static void
    642 prune_sib(struct node_list **headp)
    643 {
    644 	di_node_t parent, curr_par, curr_gpar;
    645 	struct node_list *curr, *prev;
    646 
    647 	/*
    648 	 * get handle to parent of first node
    649 	 */
    650 	if ((parent = di_parent_node((*headp)->node)) == DI_NODE_NIL) {
    651 		/*
    652 		 * This must be the root of the snapshot, so can't
    653 		 * have any siblings.
    654 		 *
    655 		 * XXX Put a check here just in case.
    656 		 */
    657 		if ((*headp)->next)
    658 			DPRINTF((DI_ERR, "Unexpected err in di_walk_node.\n"));
    659 
    660 		free(*headp);
    661 		*headp = NULL;
    662 		return;
    663 	}
    664 
    665 	/*
    666 	 * To be complete, we should also delete the children
    667 	 * of siblings that have already been visited.
    668 	 * This happens for DI_WALK_SIBFIRST when the first node
    669 	 * is NOT the first in the linked list of siblings.
    670 	 *
    671 	 * Hence, we compare parent with BOTH the parent and grandparent
    672 	 * of nodes, and delete node is a match is found.
    673 	 */
    674 	prev = *headp;
    675 	curr = prev->next;
    676 	while (curr) {
    677 		if (((curr_par = di_parent_node(curr->node)) != DI_NODE_NIL) &&
    678 		    ((curr_par == parent) || ((curr_gpar =
    679 		    di_parent_node(curr_par)) != DI_NODE_NIL) &&
    680 		    (curr_gpar == parent))) {
    681 			/*
    682 			 * match parent/grandparent: delete curr
    683 			 */
    684 			prev->next = curr->next;
    685 			free(curr);
    686 			curr = prev->next;
    687 		} else
    688 			curr = curr->next;
    689 	}
    690 
    691 	/*
    692 	 * delete the first node
    693 	 */
    694 	curr = *headp;
    695 	*headp = curr->next;
    696 	free(curr);
    697 }
    698 
    699 /*
    700  * Internal library function:
    701  *	Update node list based on action (return code from callback)
    702  *	and flag specifying walking behavior.
    703  */
    704 static void
    705 update_node_list(int action, uint_t flag, struct node_list **headp)
    706 {
    707 	struct node_list *children, *tmp;
    708 	di_node_t parent = di_parent_node((*headp)->node);
    709 
    710 	switch (action) {
    711 	case DI_WALK_TERMINATE:
    712 		/*
    713 		 * free the node list and be done
    714 		 */
    715 		children = NULL;
    716 		free_node_list(headp);
    717 		break;
    718 
    719 	case DI_WALK_PRUNESIB:
    720 		/*
    721 		 * Get list of children and prune siblings
    722 		 */
    723 		children = get_children((*headp)->node);
    724 		prune_sib(headp);
    725 		break;
    726 
    727 	case DI_WALK_PRUNECHILD:
    728 		/*
    729 		 * Set children to NULL and pop first node
    730 		 */
    731 		children = NULL;
    732 		tmp = *headp;
    733 		*headp = tmp->next;
    734 		free(tmp);
    735 		break;
    736 
    737 	case DI_WALK_CONTINUE:
    738 	default:
    739 		/*
    740 		 * Get list of children and pop first node
    741 		 */
    742 		children = get_children((*headp)->node);
    743 		tmp = *headp;
    744 		*headp = tmp->next;
    745 		free(tmp);
    746 		break;
    747 	}
    748 
    749 	/*
    750 	 * insert the list of children
    751 	 */
    752 	switch (flag) {
    753 	case DI_WALK_CLDFIRST:
    754 		prepend_node_list(headp, children);
    755 		break;
    756 
    757 	case DI_WALK_SIBFIRST:
    758 		append_node_list(headp, children);
    759 		break;
    760 
    761 	case DI_WALK_LINKGEN:
    762 	default:
    763 		insert_node_list(headp, children, parent);
    764 		break;
    765 	}
    766 }
    767 
    768 /*
    769  * Internal library function:
    770  *   Invoke callback on one node and update the list of nodes to be walked
    771  *   based on the flag and return code.
    772  */
    773 static void
    774 walk_one_node(struct node_list **headp, uint_t flag, void *arg,
    775 	int (*callback)(di_node_t, void *))
    776 {
    777 	DPRINTF((DI_TRACE, "Walking node %s\n", di_node_name((*headp)->node)));
    778 
    779 	update_node_list(callback((*headp)->node, arg),
    780 	    flag & DI_WALK_MASK, headp);
    781 }
    782 
    783 int
    784 di_walk_node(di_node_t root, uint_t flag, void *arg,
    785 	int (*node_callback)(di_node_t, void *))
    786 {
    787 	struct node_list  *head;	/* node_list for tree walk */
    788 
    789 	if (root == NULL) {
    790 		errno = EINVAL;
    791 		return (-1);
    792 	}
    793 
    794 	if ((head = malloc(sizeof (struct node_list))) == NULL) {
    795 		DPRINTF((DI_ERR, "malloc of node_list failed\n"));
    796 		return (-1);
    797 	}
    798 
    799 	head->next = NULL;
    800 	head->node = root;
    801 
    802 	DPRINTF((DI_INFO, "Start node walking from node %s\n",
    803 	    di_node_name(root)));
    804 
    805 	while (head != NULL)
    806 		walk_one_node(&head, flag, arg, node_callback);
    807 
    808 	return (0);
    809 }
    810 
    811 /*
    812  * Internal library function:
    813  *   Invoke callback for each minor on the minor list of first node
    814  *   on node_list headp, and place children of first node on the list.
    815  *
    816  *   This is similar to walk_one_node, except we only walk in child
    817  *   first mode.
    818  */
    819 static void
    820 walk_one_minor_list(struct node_list **headp, const char *desired_type,
    821 	uint_t flag, void *arg, int (*callback)(di_node_t, di_minor_t, void *))
    822 {
    823 	int ddm_type;
    824 	int action = DI_WALK_CONTINUE;
    825 	char *node_type;
    826 	di_minor_t minor = DI_MINOR_NIL;
    827 	di_node_t node = (*headp)->node;
    828 
    829 	while ((minor = di_minor_next(node, minor)) != DI_MINOR_NIL) {
    830 		ddm_type = di_minor_type(minor);
    831 
    832 		if ((ddm_type == DDM_ALIAS) && !(flag & DI_CHECK_ALIAS))
    833 			continue;
    834 
    835 		if ((ddm_type == DDM_INTERNAL_PATH) &&
    836 		    !(flag & DI_CHECK_INTERNAL_PATH))
    837 			continue;
    838 
    839 		node_type = di_minor_nodetype(minor);
    840 		if ((desired_type != NULL) && ((node_type == NULL) ||
    841 		    strncmp(desired_type, node_type, strlen(desired_type))
    842 		    != 0))
    843 			continue;
    844 
    845 		if ((action = callback(node, minor, arg)) ==
    846 		    DI_WALK_TERMINATE) {
    847 			break;
    848 		}
    849 	}
    850 
    851 	update_node_list(action, DI_WALK_LINKGEN, headp);
    852 }
    853 
    854 int
    855 di_walk_minor(di_node_t root, const char *minor_type, uint_t flag, void *arg,
    856 	int (*minor_callback)(di_node_t, di_minor_t, void *))
    857 {
    858 	struct node_list	*head;	/* node_list for tree walk */
    859 
    860 #ifdef DEBUG
    861 	char	*devfspath = di_devfs_path(root);
    862 	DPRINTF((DI_INFO, "walking minor nodes under %s\n", devfspath));
    863 	di_devfs_path_free(devfspath);
    864 #endif
    865 
    866 	if (root == NULL) {
    867 		errno = EINVAL;
    868 		return (-1);
    869 	}
    870 
    871 	if ((head = malloc(sizeof (struct node_list))) == NULL) {
    872 		DPRINTF((DI_ERR, "malloc of node_list failed\n"));
    873 		return (-1);
    874 	}
    875 
    876 	head->next = NULL;
    877 	head->node = root;
    878 
    879 	DPRINTF((DI_INFO, "Start minor walking from node %s\n",
    880 	    di_node_name(root)));
    881 
    882 	while (head != NULL)
    883 		walk_one_minor_list(&head, minor_type, flag, arg,
    884 		    minor_callback);
    885 
    886 	return (0);
    887 }
    888 
    889 /*
    890  * generic node parameters
    891  *   Calling these routines always succeeds.
    892  */
    893 char *
    894 di_node_name(di_node_t node)
    895 {
    896 	return ((caddr_t)node + DI_NODE(node)->node_name - DI_NODE(node)->self);
    897 }
    898 
    899 /* returns NULL ptr or a valid ptr to non-NULL string */
    900 char *
    901 di_bus_addr(di_node_t node)
    902 {
    903 	caddr_t pa = (caddr_t)node - DI_NODE(node)->self;
    904 
    905 	if (DI_NODE(node)->address == 0)
    906 		return (NULL);
    907 
    908 	return ((char *)(pa + DI_NODE(node)->address));
    909 }
    910 
    911 char *
    912 di_binding_name(di_node_t node)
    913 {
    914 	caddr_t pa = (caddr_t)node - DI_NODE(node)->self;
    915 
    916 	if (DI_NODE(node)->bind_name == 0)
    917 		return (NULL);
    918 
    919 	return ((char *)(pa + DI_NODE(node)->bind_name));
    920 }
    921 
    922 int
    923 di_compatible_names(di_node_t node, char **names)
    924 {
    925 	char *c;
    926 	int len, size, entries = 0;
    927 
    928 	if (DI_NODE(node)->compat_names == 0) {
    929 		*names = NULL;
    930 		return (0);
    931 	}
    932 
    933 	*names = (caddr_t)node +
    934 	    DI_NODE(node)->compat_names - DI_NODE(node)->self;
    935 
    936 	c = *names;
    937 	len = DI_NODE(node)->compat_length;
    938 	while (len > 0) {
    939 		entries++;
    940 		size = strlen(c) + 1;
    941 		len -= size;
    942 		c += size;
    943 	}
    944 
    945 	return (entries);
    946 }
    947 
    948 int
    949 di_instance(di_node_t node)
    950 {
    951 	return (DI_NODE(node)->instance);
    952 }
    953 
    954 /*
    955  * XXX: emulate the return value of the old implementation
    956  * using info from devi_node_class and devi_node_attributes.
    957  */
    958 int
    959 di_nodeid(di_node_t node)
    960 {
    961 	if (DI_NODE(node)->node_class == DDI_NC_PROM)
    962 		return (DI_PROM_NODEID);
    963 
    964 	if (DI_NODE(node)->attributes & DDI_PERSISTENT)
    965 		return (DI_SID_NODEID);
    966 
    967 	return (DI_PSEUDO_NODEID);
    968 }
    969 
    970 uint_t
    971 di_state(di_node_t node)
    972 {
    973 	uint_t result = 0;
    974 
    975 	if (di_node_state(node) < DS_ATTACHED)
    976 		result |= DI_DRIVER_DETACHED;
    977 	if (DI_NODE(node)->state & DEVI_DEVICE_OFFLINE)
    978 		result |= DI_DEVICE_OFFLINE;
    979 	if (DI_NODE(node)->state & DEVI_DEVICE_DOWN)
    980 		result |= DI_DEVICE_OFFLINE;
    981 	if (DI_NODE(node)->state & DEVI_DEVICE_DEGRADED)
    982 		result |= DI_DEVICE_DEGRADED;
    983 	if (DI_NODE(node)->state & DEVI_BUS_QUIESCED)
    984 		result |= DI_BUS_QUIESCED;
    985 	if (DI_NODE(node)->state & DEVI_BUS_DOWN)
    986 		result |= DI_BUS_DOWN;
    987 
    988 	return (result);
    989 }
    990 
    991 ddi_node_state_t
    992 di_node_state(di_node_t node)
    993 {
    994 	return (DI_NODE(node)->node_state);
    995 }
    996 
    997 uint_t
    998 di_flags(di_node_t node)
    999 {
   1000 	return (DI_NODE(node)->flags);
   1001 }
   1002 
   1003 uint_t
   1004 di_retired(di_node_t node)
   1005 {
   1006 	return (di_flags(node) & DEVI_RETIRED);
   1007 }
   1008 
   1009 ddi_devid_t
   1010 di_devid(di_node_t node)
   1011 {
   1012 	if (DI_NODE(node)->devid == 0)
   1013 		return (NULL);
   1014 
   1015 	return ((ddi_devid_t)((caddr_t)node +
   1016 	    DI_NODE(node)->devid - DI_NODE(node)->self));
   1017 }
   1018 
   1019 int
   1020 di_driver_major(di_node_t node)
   1021 {
   1022 	int major;
   1023 
   1024 	major = DI_NODE(node)->drv_major;
   1025 	if (major < 0)
   1026 		return (-1);
   1027 	return (major);
   1028 }
   1029 
   1030 char *
   1031 di_driver_name(di_node_t node)
   1032 {
   1033 	int major;
   1034 	caddr_t pa;
   1035 	struct di_devnm *devnm;
   1036 
   1037 	major = DI_NODE(node)->drv_major;
   1038 	if (major < 0)
   1039 		return (NULL);
   1040 
   1041 	pa = (caddr_t)node - DI_NODE(node)->self;
   1042 	devnm = DI_DEVNM(pa + DI_ALL(pa)->devnames);
   1043 
   1044 	if (devnm[major].name)
   1045 		return (pa + devnm[major].name);
   1046 	else
   1047 		return (NULL);
   1048 }
   1049 
   1050 uint_t
   1051 di_driver_ops(di_node_t node)
   1052 {
   1053 	int major;
   1054 	caddr_t pa;
   1055 	struct di_devnm *devnm;
   1056 
   1057 	major = DI_NODE(node)->drv_major;
   1058 	if (major < 0)
   1059 		return (0);
   1060 
   1061 	pa = (caddr_t)node - DI_NODE(node)->self;
   1062 	devnm = DI_DEVNM(pa + DI_ALL(pa)->devnames);
   1063 
   1064 	return (devnm[major].ops);
   1065 }
   1066 
   1067 /*
   1068  * returns the length of the path, caller must free memory
   1069  */
   1070 char *
   1071 di_devfs_path(di_node_t node)
   1072 {
   1073 	caddr_t pa;
   1074 	di_node_t parent;
   1075 	int depth = 0, len = 0;
   1076 	char *buf, *name[MAX_TREE_DEPTH], *addr[MAX_TREE_DEPTH];
   1077 
   1078 	if (node == DI_NODE_NIL) {
   1079 		errno = EINVAL;
   1080 		return (NULL);
   1081 	}
   1082 
   1083 	/*
   1084 	 * trace back to root, note the node_name & address
   1085 	 */
   1086 	while ((parent = di_parent_node(node)) != DI_NODE_NIL) {
   1087 		name[depth] = di_node_name(node);
   1088 		len += strlen(name[depth]) + 1;		/* 1 for '/' */
   1089 
   1090 		if ((addr[depth] = di_bus_addr(node)) != NULL)
   1091 			len += strlen(addr[depth]) + 1;	/* 1 for '@' */
   1092 
   1093 		node = parent;
   1094 		depth++;
   1095 	}
   1096 
   1097 	/*
   1098 	 * get the path to the root of snapshot
   1099 	 */
   1100 	pa = (caddr_t)node - DI_NODE(node)->self;
   1101 	name[depth] = DI_ALL(pa)->root_path;
   1102 	len += strlen(name[depth]) + 1;
   1103 
   1104 	/*
   1105 	 * allocate buffer and assemble path
   1106 	 */
   1107 	if ((buf = malloc(len)) == NULL) {
   1108 		return (NULL);
   1109 	}
   1110 
   1111 	(void) strcpy(buf, name[depth]);
   1112 	len = strlen(buf);
   1113 	if (buf[len - 1] == '/')
   1114 		len--;	/* delete trailing '/' */
   1115 
   1116 	while (depth) {
   1117 		depth--;
   1118 		buf[len] = '/';
   1119 		(void) strcpy(buf + len + 1, name[depth]);
   1120 		len += strlen(name[depth]) + 1;
   1121 		if (addr[depth] && addr[depth][0] != '\0') {
   1122 			buf[len] = '@';
   1123 			(void) strcpy(buf + len + 1, addr[depth]);
   1124 			len += strlen(addr[depth]) + 1;
   1125 		}
   1126 	}
   1127 
   1128 	return (buf);
   1129 }
   1130 
   1131 char *
   1132 di_devfs_minor_path(di_minor_t minor)
   1133 {
   1134 	di_node_t	node;
   1135 	char		*full_path, *name, *devfspath;
   1136 	int		full_path_len;
   1137 
   1138 	if (minor == DI_MINOR_NIL) {
   1139 		errno = EINVAL;
   1140 		return (NULL);
   1141 	}
   1142 
   1143 	name = di_minor_name(minor);
   1144 	node = di_minor_devinfo(minor);
   1145 	devfspath = di_devfs_path(node);
   1146 	if (devfspath == NULL)
   1147 		return (NULL);
   1148 
   1149 	/* make the full path to the device minor node */
   1150 	full_path_len = strlen(devfspath) + strlen(name) + 2;
   1151 	full_path = (char *)calloc(1, full_path_len);
   1152 	if (full_path != NULL)
   1153 		(void) snprintf(full_path, full_path_len, "%s:%s",
   1154 		    devfspath, name);
   1155 
   1156 	di_devfs_path_free(devfspath);
   1157 	return (full_path);
   1158 }
   1159 
   1160 /*
   1161  * Produce a string representation of path to di_path_t (pathinfo node). This
   1162  * string is identical to di_devfs_path had the device been enumerated under
   1163  * the pHCI: it has a base path to pHCI, then uses node_name of client, and
   1164  * device unit-address of pathinfo node.
   1165  */
   1166 char *
   1167 di_path_devfs_path(di_path_t path)
   1168 {
   1169 	di_node_t	phci_node;
   1170 	char		*phci_path, *path_name, *path_addr;
   1171 	char		*full_path;
   1172 	int		full_path_len;
   1173 
   1174 	if (path == DI_PATH_NIL) {
   1175 		errno =