xref: /illumos-gate/usr/src/lib/libdevinfo/devinfo.c (revision 8c4f8890)
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, Version 1.0 only
6  * (the "License").  You may not use this file except in compliance
7  * with the License.
8  *
9  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
10  * or http://www.opensolaris.org/os/licensing.
11  * See the License for the specific language governing permissions
12  * and limitations under the License.
13  *
14  * When distributing Covered Code, include this CDDL HEADER in each
15  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
16  * If applicable, add the following below this CDDL HEADER, with the
17  * fields enclosed by brackets "[]" replaced with your own identifying
18  * information: Portions Copyright [yyyy] [name of copyright owner]
19  *
20  * CDDL HEADER END
21  */
22 
23 /*
24  * Copyright 2005 Sun Microsystems, Inc.  All rights reserved.
25  * Use is subject to license terms.
26  */
27 
28 #pragma ident	"%Z%%M%	%I%	%E% SMI"
29 
30 /*
31  * Interfaces for getting device configuration data from kernel
32  * through the devinfo driver.
33  */
34 
35 #include <stdio.h>
36 #include <stdlib.h>
37 #include <string.h>
38 #include <strings.h>
39 #include <stropts.h>
40 #include <fcntl.h>
41 #include <poll.h>
42 #include <synch.h>
43 #include <unistd.h>
44 #include <sys/mkdev.h>
45 #include <sys/obpdefs.h>
46 #include <sys/stat.h>
47 #include <sys/types.h>
48 #include <sys/time.h>
49 #include <sys/autoconf.h>
50 #include <stdarg.h>
51 
52 #define	NDEBUG 1
53 #include <assert.h>
54 
55 #include "libdevinfo.h"
56 
57 /*
58  * Debug message levels
59  */
60 typedef enum {
61 	DI_QUIET = 0,	/* No debug messages - the default */
62 	DI_ERR = 1,
63 	DI_INFO,
64 	DI_TRACE,
65 	DI_TRACE1,
66 	DI_TRACE2
67 } di_debug_t;
68 
69 int di_debug = DI_QUIET;
70 
71 #define	DPRINTF(args)	{ if (di_debug != DI_QUIET) dprint args; }
72 
73 void dprint(di_debug_t msglevel, const char *fmt, ...);
74 
75 
76 #pragma init(_libdevinfo_init)
77 
78 void
79 _libdevinfo_init()
80 {
81 	char	*debug_str = getenv("_LIBDEVINFO_DEBUG");
82 
83 	if (debug_str) {
84 		errno = 0;
85 		di_debug = atoi(debug_str);
86 		if (errno || di_debug < DI_QUIET)
87 			di_debug = DI_QUIET;
88 	}
89 }
90 
91 di_node_t
92 di_init(const char *phys_path, uint_t flag)
93 {
94 	return (di_init_impl(phys_path, flag, NULL));
95 }
96 
97 /*
98  * We use blocking_open() to guarantee access to the devinfo device, if open()
99  * is failing with EAGAIN.
100  */
101 static int
102 blocking_open(const char *path, int oflag)
103 {
104 	int fd;
105 
106 	while ((fd = open(path, oflag)) == -1 && errno == EAGAIN)
107 		(void) poll(NULL, 0, 1 * MILLISEC);
108 
109 	return (fd);
110 }
111 
112 /* private interface */
113 di_node_t
114 di_init_driver(const char *drv_name, uint_t flag)
115 {
116 	int fd;
117 	char driver[MAXPATHLEN];
118 
119 	/*
120 	 * Don't allow drv_name to exceed MAXPATHLEN - 1, or 1023,
121 	 * which should be sufficient for any sensible programmer.
122 	 */
123 	if ((drv_name == NULL) || (strlen(drv_name) >= MAXPATHLEN)) {
124 		errno = EINVAL;
125 		return (DI_NODE_NIL);
126 	}
127 	(void) strcpy(driver, drv_name);
128 
129 	/*
130 	 * open the devinfo driver
131 	 */
132 	if ((fd = blocking_open("/devices/pseudo/devinfo@0:devinfo",
133 	    O_RDONLY)) == -1) {
134 		DPRINTF((DI_ERR, "devinfo open failed: errno = %d\n", errno));
135 		return (DI_NODE_NIL);
136 	}
137 
138 	if (ioctl(fd, DINFOLODRV, driver) != 0) {
139 		DPRINTF((DI_ERR, "failed to load driver %s\n", driver));
140 		(void) close(fd);
141 		errno = ENXIO;
142 		return (DI_NODE_NIL);
143 	}
144 	(void) close(fd);
145 
146 	/*
147 	 * Driver load succeeded, return a snapshot
148 	 */
149 	return (di_init("/", flag));
150 }
151 
152 di_node_t
153 di_init_impl(const char *phys_path, uint_t flag,
154 	struct di_priv_data *priv)
155 {
156 	caddr_t pa;
157 	int fd, map_size;
158 	struct di_all *dap;
159 	struct dinfo_io dinfo_io;
160 
161 	uint_t pageoffset = sysconf(_SC_PAGESIZE) - 1;
162 	uint_t pagemask = ~pageoffset;
163 
164 	DPRINTF((DI_INFO, "di_init: taking a snapshot\n"));
165 
166 	/*
167 	 * Make sure there is no minor name in the path
168 	 * and the path do not start with /devices....
169 	 */
170 	if (strchr(phys_path, ':') ||
171 	    (strncmp(phys_path, "/devices", 8) == 0) ||
172 	    (strlen(phys_path) > MAXPATHLEN)) {
173 		errno = EINVAL;
174 		return (DI_NODE_NIL);
175 	}
176 
177 	if (strlen(phys_path) == 0)
178 		(void) sprintf(dinfo_io.root_path, "/");
179 	else if (*phys_path != '/')
180 		(void) snprintf(dinfo_io.root_path, sizeof (dinfo_io.root_path),
181 		    "/%s", phys_path);
182 	else
183 		(void) snprintf(dinfo_io.root_path, sizeof (dinfo_io.root_path),
184 		    "%s", phys_path);
185 
186 	/*
187 	 * If private data is requested, copy the format specification
188 	 */
189 	if (flag & DINFOPRIVDATA & 0xff) {
190 		if (priv)
191 			bcopy(priv, &dinfo_io.priv,
192 			    sizeof (struct di_priv_data));
193 		else {
194 			errno = EINVAL;
195 			return (DI_NODE_NIL);
196 		}
197 	}
198 
199 	/*
200 	 * Attempt to open the devinfo driver.  Make a second attempt at the
201 	 * read-only minor node if we don't have privileges to open the full
202 	 * version _and_ if we're not requesting operations that the read-only
203 	 * node can't perform.  (Setgid processes would fail an access() test,
204 	 * of course.)
205 	 */
206 	if ((fd = blocking_open("/devices/pseudo/devinfo@0:devinfo",
207 	    O_RDONLY)) == -1) {
208 		if ((flag & DINFOFORCE) == DINFOFORCE ||
209 		    (flag & DINFOPRIVDATA) == DINFOPRIVDATA) {
210 			/*
211 			 * We wanted to perform a privileged operation, but the
212 			 * privileged node isn't available.  Don't modify errno
213 			 * on our way out (but display it if we're running with
214 			 * di_debug set).
215 			 */
216 			DPRINTF((DI_ERR, "devinfo open failed: errno = %d\n",
217 			    errno));
218 			return (DI_NODE_NIL);
219 		}
220 
221 		if ((fd = blocking_open("/devices/pseudo/devinfo@0:devinfo,ro",
222 		    O_RDONLY)) == -1) {
223 			DPRINTF((DI_ERR, "devinfo open failed: errno = %d\n",
224 			    errno));
225 			return (DI_NODE_NIL);
226 		}
227 	}
228 
229 	/*
230 	 * Verify that there is no major conflict, i.e., we are indeed opening
231 	 * the devinfo driver.
232 	 */
233 	if (ioctl(fd, DINFOIDENT, NULL) != DI_MAGIC) {
234 		DPRINTF((DI_ERR,
235 		    "driver ID failed; check for major conflict\n"));
236 		(void) close(fd);
237 		return (DI_NODE_NIL);
238 	}
239 
240 	/*
241 	 * create snapshot
242 	 */
243 	if ((map_size = ioctl(fd, flag, &dinfo_io)) < 0) {
244 		DPRINTF((DI_ERR, "devinfo ioctl failed with "
245 		    "error: %d\n", errno));
246 		(void) close(fd);
247 		return (DI_NODE_NIL);
248 	} else if (map_size == 0) {
249 		DPRINTF((DI_ERR, "%s not found\n", phys_path));
250 		errno = ENXIO;
251 		(void) close(fd);
252 		return (DI_NODE_NIL);
253 	}
254 
255 	/*
256 	 * copy snapshot to userland
257 	 */
258 	map_size = (map_size + pageoffset) & pagemask;
259 	if ((pa = valloc(map_size)) == NULL) {
260 		DPRINTF((DI_ERR, "valloc failed for snapshot\n"));
261 		(void) close(fd);
262 		return (DI_NODE_NIL);
263 	}
264 
265 	if (ioctl(fd, DINFOUSRLD, pa) != map_size) {
266 		DPRINTF((DI_ERR, "failed to copy snapshot to usrld\n"));
267 		(void) close(fd);
268 		free(pa);
269 		errno = EFAULT;
270 		return (DI_NODE_NIL);
271 	}
272 
273 	(void) close(fd);
274 
275 	dap = DI_ALL(pa);
276 	if (dap->top_devinfo == 0) {	/* phys_path not found */
277 		DPRINTF((DI_ERR, "%s not found\n", phys_path));
278 		free(pa);
279 		errno = EINVAL;
280 		return (DI_NODE_NIL);
281 	}
282 
283 	return (DI_NODE(pa + dap->top_devinfo));
284 }
285 
286 void
287 di_fini(di_node_t root)
288 {
289 	caddr_t pa;		/* starting address of map */
290 
291 	DPRINTF((DI_INFO, "di_fini: freeing a snapshot\n"));
292 
293 	/*
294 	 * paranoid checking
295 	 */
296 	if (root == DI_NODE_NIL) {
297 		DPRINTF((DI_ERR, "di_fini called with NIL arg\n"));
298 		return;
299 	}
300 
301 	/*
302 	 * The root contains its own offset--self.
303 	 * Subtracting it from root address, we get the starting addr.
304 	 * The map_size is stored at the beginning of snapshot.
305 	 * Once we have starting address and size, we can free().
306 	 */
307 	pa = (caddr_t)root - DI_NODE(root)->self;
308 
309 	free(pa);
310 }
311 
312 di_node_t
313 di_parent_node(di_node_t node)
314 {
315 	caddr_t pa;		/* starting address of map */
316 
317 	if (node == DI_NODE_NIL) {
318 		errno = EINVAL;
319 		return (DI_NODE_NIL);
320 	}
321 
322 	DPRINTF((DI_TRACE, "Get parent of node %s\n", di_node_name(node)));
323 
324 	pa = (caddr_t)node - DI_NODE(node)->self;
325 
326 	if (DI_NODE(node)->parent) {
327 		return (DI_NODE(pa + DI_NODE(node)->parent));
328 	}
329 
330 	/*
331 	 * Deal with error condition:
332 	 *   If parent doesn't exist and node is not the root,
333 	 *   set errno to ENOTSUP. Otherwise, set errno to ENXIO.
334 	 */
335 	if (strcmp(DI_ALL(pa)->root_path, "/") != 0)
336 		errno = ENOTSUP;
337 	else
338 		errno = ENXIO;
339 
340 	return (DI_NODE_NIL);
341 }
342 
343 di_node_t
344 di_sibling_node(di_node_t node)
345 {
346 	caddr_t pa;		/* starting address of map */
347 
348 	if (node == DI_NODE_NIL) {
349 		errno = EINVAL;
350 		return (DI_NODE_NIL);
351 	}
352 
353 	DPRINTF((DI_TRACE, "Get sibling of node %s\n", di_node_name(node)));
354 
355 	pa = (caddr_t)node - DI_NODE(node)->self;
356 
357 	if (DI_NODE(node)->sibling) {
358 		return (DI_NODE(pa + DI_NODE(node)->sibling));
359 	}
360 
361 	/*
362 	 * Deal with error condition:
363 	 *   Sibling doesn't exist, figure out if ioctl command
364 	 *   has DINFOSUBTREE set. If it doesn't, set errno to
365 	 *   ENOTSUP.
366 	 */
367 	if (!(DI_ALL(pa)->command & DINFOSUBTREE))
368 		errno = ENOTSUP;
369 	else
370 		errno = ENXIO;
371 
372 	return (DI_NODE_NIL);
373 }
374 
375 di_node_t
376 di_child_node(di_node_t node)
377 {
378 	caddr_t pa;		/* starting address of map */
379 
380 	DPRINTF((DI_TRACE, "Get child of node %s\n", di_node_name(node)));
381 
382 	if (node == DI_NODE_NIL) {
383 		errno = EINVAL;
384 		return (DI_NODE_NIL);
385 	}
386 
387 	pa = (caddr_t)node - DI_NODE(node)->self;
388 
389 	if (DI_NODE(node)->child) {
390 		return (DI_NODE(pa + DI_NODE(node)->child));
391 	}
392 
393 	/*
394 	 * Deal with error condition:
395 	 *   Child doesn't exist, figure out if DINFOSUBTREE is set.
396 	 *   If it isn't, set errno to ENOTSUP.
397 	 */
398 	if (!(DI_ALL(pa)->command & DINFOSUBTREE))
399 		errno = ENOTSUP;
400 	else
401 		errno = ENXIO;
402 
403 	return (DI_NODE_NIL);
404 }
405 
406 di_node_t
407 di_drv_first_node(const char *drv_name, di_node_t root)
408 {
409 	caddr_t		pa;		/* starting address of map */
410 	int		major, devcnt;
411 	struct di_devnm	*devnm;
412 
413 	DPRINTF((DI_INFO, "Get first node of driver %s\n", drv_name));
414 
415 	if (root == DI_NODE_NIL) {
416 		errno = EINVAL;
417 		return (DI_NODE_NIL);
418 	}
419 
420 	/*
421 	 * get major number of driver
422 	 */
423 	pa = (caddr_t)root - DI_NODE(root)->self;
424 	devcnt = DI_ALL(pa)->devcnt;
425 	devnm = DI_DEVNM(pa + DI_ALL(pa)->devnames);
426 
427 	for (major = 0; major < devcnt; major++)
428 		if (devnm[major].name && (strcmp(drv_name,
429 		    (char *)(pa + devnm[major].name)) == 0))
430 			break;
431 
432 	if (major >= devcnt) {
433 		errno = EINVAL;
434 		return (DI_NODE_NIL);
435 	}
436 
437 	if (!(devnm[major].head)) {
438 		errno = ENXIO;
439 		return (DI_NODE_NIL);
440 	}
441 
442 	return (DI_NODE(pa + devnm[major].head));
443 }
444 
445 di_node_t
446 di_drv_next_node(di_node_t node)
447 {
448 	caddr_t		pa;		/* starting address of map */
449 
450 	if (node == DI_NODE_NIL) {
451 		errno = EINVAL;
452 		return (DI_NODE_NIL);
453 	}
454 
455 	DPRINTF((DI_TRACE, "next node on per driver list:"
456 	    " current=%s, driver=%s\n",
457 	    di_node_name(node), di_driver_name(node)));
458 
459 	if (DI_NODE(node)->next == (di_off_t)-1) {
460 		errno = ENOTSUP;
461 		return (DI_NODE_NIL);
462 	}
463 
464 	pa = (caddr_t)node - DI_NODE(node)->self;
465 
466 	if (DI_NODE(node)->next == NULL) {
467 		errno = ENXIO;
468 		return (DI_NODE_NIL);
469 	}
470 
471 	return (DI_NODE(pa + DI_NODE(node)->next));
472 }
473 
474 /*
475  * Internal library interfaces:
476  *   node_list etc. for node walking
477  */
478 struct node_list {
479 	struct node_list *next;
480 	di_node_t node;
481 };
482 
483 static void
484 free_node_list(struct node_list **headp)
485 {
486 	struct node_list *tmp;
487 
488 	while (*headp) {
489 		tmp = *headp;
490 		*headp = (*headp)->next;
491 		free(tmp);
492 	}
493 }
494 
495 static void
496 append_node_list(struct node_list **headp, struct node_list *list)
497 {
498 	struct node_list *tmp;
499 
500 	if (*headp == NULL) {
501 		*headp = list;
502 		return;
503 	}
504 
505 	if (list == NULL)	/* a minor optimization */
506 		return;
507 
508 	tmp = *headp;
509 	while (tmp->next)
510 		tmp = tmp->next;
511 
512 	tmp->next = list;
513 }
514 
515 static void
516 prepend_node_list(struct node_list **headp, struct node_list *list)
517 {
518 	struct node_list *tmp;
519 
520 	if (list == NULL)
521 		return;
522 
523 	tmp = *headp;
524 	*headp = list;
525 
526 	if (tmp == NULL)	/* a minor optimization */
527 		return;
528 
529 	while (list->next)
530 		list = list->next;
531 
532 	list->next = tmp;
533 }
534 
535 /*
536  * returns 1 if node is a descendant of parent, 0 otherwise
537  */
538 static int
539 is_descendant(di_node_t node, di_node_t parent)
540 {
541 	/*
542 	 * DI_NODE_NIL is parent of root, so it is
543 	 * the parent of all nodes.
544 	 */
545 	if (parent == DI_NODE_NIL) {
546 		return (1);
547 	}
548 
549 	do {
550 		node = di_parent_node(node);
551 	} while ((node != DI_NODE_NIL) && (node != parent));
552 
553 	return (node != DI_NODE_NIL);
554 }
555 
556 /*
557  * Insert list before the first node which is NOT a descendent of parent.
558  * This is needed to reproduce the exact walking order of link generators.
559  */
560 static void
561 insert_node_list(struct node_list **headp, struct node_list *list,
562     di_node_t parent)
563 {
564 	struct node_list *tmp, *tmp1;
565 
566 	if (list == NULL)
567 		return;
568 
569 	tmp = *headp;
570 	if (tmp == NULL) {	/* a minor optimization */
571 		*headp = list;
572 		return;
573 	}
574 
575 	if (!is_descendant(tmp->node, parent)) {
576 		prepend_node_list(headp, list);
577 		return;
578 	}
579 
580 	/*
581 	 * Find first node which is not a descendant
582 	 */
583 	while (tmp->next && is_descendant(tmp->next->node, parent)) {
584 		tmp = tmp->next;
585 	}
586 
587 	tmp1 = tmp->next;
588 	tmp->next = list;
589 	append_node_list(headp, tmp1);
590 }
591 
592 /*
593  *   Get a linked list of handles of all children
594  */
595 static struct node_list *
596 get_children(di_node_t node)
597 {
598 	di_node_t child;
599 	struct node_list *result, *tmp;
600 
601 	DPRINTF((DI_TRACE1, "Get children of node %s\n", di_node_name(node)));
602 
603 	if ((child = di_child_node(node)) == DI_NODE_NIL) {
604 		return (NULL);
605 	}
606 
607 	if ((result = malloc(sizeof (struct node_list))) == NULL) {
608 		DPRINTF((DI_ERR, "malloc of node_list failed\n"));
609 		return (NULL);
610 	}
611 
612 	result->node = child;
613 	tmp = result;
614 
615 	while ((child = di_sibling_node(tmp->node)) != DI_NODE_NIL) {
616 		if ((tmp->next = malloc(sizeof (struct node_list))) == NULL) {
617 			DPRINTF((DI_ERR, "malloc of node_list failed\n"));
618 			free_node_list(&result);
619 			return (NULL);
620 		}
621 		tmp = tmp->next;
622 		tmp->node = child;
623 	}
624 
625 	tmp->next = NULL;
626 
627 	return (result);
628 }
629 
630 /*
631  * Internal library interface:
632  *   Delete all siblings of the first node from the node_list, along with
633  *   the first node itself.
634  */
635 static void
636 prune_sib(struct node_list **headp)
637 {
638 	di_node_t parent, curr_par, curr_gpar;
639 	struct node_list *curr, *prev;
640 
641 	/*
642 	 * get handle to parent of first node
643 	 */
644 	if ((parent = di_parent_node((*headp)->node)) == DI_NODE_NIL) {
645 		/*
646 		 * This must be the root of the snapshot, so can't
647 		 * have any siblings.
648 		 *
649 		 * XXX Put a check here just in case.
650 		 */
651 		if ((*headp)->next)
652 			DPRINTF((DI_ERR, "Unexpected err in di_walk_node.\n"));
653 
654 		free(*headp);
655 		*headp = NULL;
656 		return;
657 	}
658 
659 	/*
660 	 * To be complete, we should also delete the children
661 	 * of siblings that have already been visited.
662 	 * This happens for DI_WALK_SIBFIRST when the first node
663 	 * is NOT the first in the linked list of siblings.
664 	 *
665 	 * Hence, we compare parent with BOTH the parent and grandparent
666 	 * of nodes, and delete node is a match is found.
667 	 */
668 	prev = *headp;
669 	curr = prev->next;
670 	while (curr) {
671 		if (((curr_par = di_parent_node(curr->node)) != DI_NODE_NIL) &&
672 		    ((curr_par == parent) || ((curr_gpar =
673 		    di_parent_node(curr_par)) != DI_NODE_NIL) &&
674 		    (curr_gpar == parent))) {
675 			/*
676 			 * match parent/grandparent: delete curr
677 			 */
678 			prev->next = curr->next;
679 			free(curr);
680 			curr = prev->next;
681 		} else
682 			curr = curr->next;
683 	}
684 
685 	/*
686 	 * delete the first node
687 	 */
688 	curr = *headp;
689 	*headp = curr->next;
690 	free(curr);
691 }
692 
693 /*
694  * Internal library function:
695  *	Update node list based on action (return code from callback)
696  *	and flag specifying walking behavior.
697  */
698 static void
699 update_node_list(int action, uint_t flag, struct node_list **headp)
700 {
701 	struct node_list *children, *tmp;
702 	di_node_t parent = di_parent_node((*headp)->node);
703 
704 	switch (action) {
705 	case DI_WALK_TERMINATE:
706 		/*
707 		 * free the node list and be done
708 		 */
709 		children = NULL;
710 		free_node_list(headp);
711 		break;
712 
713 	case DI_WALK_PRUNESIB:
714 		/*
715 		 * Get list of children and prune siblings
716 		 */
717 		children = get_children((*headp)->node);
718 		prune_sib(headp);
719 		break;
720 
721 	case DI_WALK_PRUNECHILD:
722 		/*
723 		 * Set children to NULL and pop first node
724 		 */
725 		children = NULL;
726 		tmp = *headp;
727 		*headp = tmp->next;
728 		free(tmp);
729 		break;
730 
731 	case DI_WALK_CONTINUE:
732 	default:
733 		/*
734 		 * Get list of children and pop first node
735 		 */
736 		children = get_children((*headp)->node);
737 		tmp = *headp;
738 		*headp = tmp->next;
739 		free(tmp);
740 		break;
741 	}
742 
743 	/*
744 	 * insert the list of children
745 	 */
746 	switch (flag) {
747 	case DI_WALK_CLDFIRST:
748 		prepend_node_list(headp, children);
749 		break;
750 
751 	case DI_WALK_SIBFIRST:
752 		append_node_list(headp, children);
753 		break;
754 
755 	case DI_WALK_LINKGEN:
756 	default:
757 		insert_node_list(headp, children, parent);
758 		break;
759 	}
760 }
761 
762 /*
763  * Internal library function:
764  *   Invoke callback on one node and update the list of nodes to be walked
765  *   based on the flag and return code.
766  */
767 static void
768 walk_one_node(struct node_list **headp, uint_t flag, void *arg,
769 	int (*callback)(di_node_t, void *))
770 {
771 	DPRINTF((DI_TRACE, "Walking node %s\n", di_node_name((*headp)->node)));
772 
773 	update_node_list(callback((*headp)->node, arg),
774 	    flag & DI_WALK_MASK, headp);
775 }
776 
777 int
778 di_walk_node(di_node_t root, uint_t flag, void *arg,
779 	int (*node_callback)(di_node_t, void *))
780 {
781 	struct node_list  *head;	/* node_list for tree walk */
782 
783 	if (root == NULL) {
784 		errno = EINVAL;
785 		return (-1);
786 	}
787 
788 	if ((head = malloc(sizeof (struct node_list))) == NULL) {
789 		DPRINTF((DI_ERR, "malloc of node_list failed\n"));
790 		return (-1);
791 	}
792 
793 	head->next = NULL;
794 	head->node = root;
795 
796 	DPRINTF((DI_INFO, "Start node walking from node %s\n",
797 	    di_node_name(root)));
798 
799 	while (head != NULL)
800 		walk_one_node(&head, flag, arg, node_callback);
801 
802 	return (0);
803 }
804 
805 /*
806  * Internal library function:
807  *   Invoke callback for each minor on the minor list of first node
808  *   on node_list headp, and place children of first node on the list.
809  *
810  *   This is similar to walk_one_node, except we only walk in child
811  *   first mode.
812  */
813 static void
814 walk_one_minor_list(struct node_list **headp, const char *desired_type,
815 	uint_t flag, void *arg, int (*callback)(di_node_t, di_minor_t, void *))
816 {
817 	int ddm_type;
818 	int action = DI_WALK_CONTINUE;
819 	char *node_type;
820 	di_minor_t minor = DI_MINOR_NIL;
821 	di_node_t node = (*headp)->node;
822 
823 	while ((minor = di_minor_next(node, minor)) != DI_MINOR_NIL) {
824 		ddm_type = di_minor_type(minor);
825 
826 		if ((ddm_type == DDM_ALIAS) && !(flag & DI_CHECK_ALIAS))
827 			continue;
828 
829 		if ((ddm_type == DDM_INTERNAL_PATH) &&
830 		    !(flag & DI_CHECK_INTERNAL_PATH))
831 			continue;
832 
833 		node_type = di_minor_nodetype(minor);
834 		if ((desired_type != NULL) && ((node_type == NULL) ||
835 		    strncmp(desired_type, node_type, strlen(desired_type))
836 		    != 0))
837 			continue;
838 
839 		if ((action = callback(node, minor, arg)) ==
840 		    DI_WALK_TERMINATE) {
841 			break;
842 		}
843 	}
844 
845 	update_node_list(action, DI_WALK_LINKGEN, headp);
846 }
847 
848 int
849 di_walk_minor(di_node_t root, const char *minor_type, uint_t flag, void *arg,
850 	int (*minor_callback)(di_node_t, di_minor_t, void *))
851 {
852 	struct node_list  *head;	/* node_list for tree walk */
853 
854 #ifdef DEBUG
855 	char *path = di_devfs_path(root);
856 	DPRINTF((DI_INFO, "walking minor nodes under %s\n", path));
857 	di_devfs_path_free(path);
858 #endif
859 
860 	if (root == NULL) {
861 		errno = EINVAL;
862 		return (-1);
863 	}
864 
865 	if ((head = malloc(sizeof (struct node_list))) == NULL) {
866 		DPRINTF((DI_ERR, "malloc of node_list failed\n"));
867 		return (-1);
868 	}
869 
870 	head->next = NULL;
871 	head->node = root;
872 
873 	DPRINTF((DI_INFO, "Start minor walking from node %s\n",
874 		di_node_name(root)));
875 
876 	while (head != NULL)
877 		walk_one_minor_list(&head, minor_type, flag, arg,
878 		    minor_callback);
879 
880 	return (0);
881 }
882 
883 /*
884  * generic node parameters
885  *   Calling these routines always succeeds.
886  */
887 char *
888 di_node_name(di_node_t node)
889 {
890 	return ((caddr_t)node + DI_NODE(node)->node_name - DI_NODE(node)->self);
891 }
892 
893 /* returns NULL ptr or a valid ptr to non-NULL string */
894 char *
895 di_bus_addr(di_node_t node)
896 {
897 	caddr_t pa = (caddr_t)node - DI_NODE(node)->self;
898 
899 	if (DI_NODE(node)->address == 0)
900 		return (NULL);
901 
902 	return ((char *)(pa + DI_NODE(node)->address));
903 }
904 
905 char *
906 di_binding_name(di_node_t node)
907 {
908 	caddr_t pa = (caddr_t)node - DI_NODE(node)->self;
909 
910 	if (DI_NODE(node)->bind_name == 0)
911 		return (NULL);
912 
913 	return ((char *)(pa + DI_NODE(node)->bind_name));
914 }
915 
916 int
917 di_compatible_names(di_node_t node, char **names)
918 {
919 	char *c;
920 	int len, size, entries = 0;
921 
922 	if (DI_NODE(node)->compat_names == 0) {
923 		*names = NULL;
924 		return (0);
925 	}
926 
927 	*names = (caddr_t)node +
928 		DI_NODE(node)->compat_names - DI_NODE(node)->self;
929 
930 	c = *names;
931 	len = DI_NODE(node)->compat_length;
932 	while (len > 0) {
933 		entries++;
934 		size = strlen(c) + 1;
935 		len -= size;
936 		c += size;
937 	}
938 
939 	return (entries);
940 }
941 
942 int
943 di_instance(di_node_t node)
944 {
945 	return (DI_NODE(node)->instance);
946 }
947 
948 /*
949  * XXX: emulate the return value of the old implementation
950  * using info from devi_node_class and devi_node_attributes.
951  */
952 int
953 di_nodeid(di_node_t node)
954 {
955 	if (DI_NODE(node)->node_class == DDI_NC_PROM)
956 		return (DI_PROM_NODEID);
957 
958 	if (DI_NODE(node)->attributes & DDI_PERSISTENT)
959 		return (DI_SID_NODEID);
960 
961 	return (DI_PSEUDO_NODEID);
962 }
963 
964 uint_t
965 di_state(di_node_t node)
966 {
967 	uint_t result = 0;
968 
969 	if (di_node_state(node) < DS_ATTACHED)
970 		result |= DI_DRIVER_DETACHED;
971 	if (DI_NODE(node)->state & DEVI_DEVICE_OFFLINE)
972 		result |= DI_DEVICE_OFFLINE;
973 	if (DI_NODE(node)->state & DEVI_DEVICE_DOWN)
974 		result |= DI_DEVICE_OFFLINE;
975 	if (DI_NODE(node)->state & DEVI_BUS_QUIESCED)
976 		result |= DI_BUS_QUIESCED;
977 	if (DI_NODE(node)->state & DEVI_BUS_DOWN)
978 		result |= DI_BUS_DOWN;
979 
980 	return (result);
981 }
982 
983 ddi_node_state_t
984 di_node_state(di_node_t node)
985 {
986 	return (DI_NODE(node)->node_state);
987 }
988 
989 ddi_devid_t
990 di_devid(di_node_t node)
991 {
992 	if (DI_NODE(node)->devid == 0)
993 		return (NULL);
994 
995 	return ((ddi_devid_t)((caddr_t)node +
996 	    DI_NODE(node)->devid - DI_NODE(node)->self));
997 }
998 
999 int
1000 di_driver_major(di_node_t node)
1001 {
1002 	int major;
1003 
1004 	major = DI_NODE(node)->drv_major;
1005 	if (major < 0)
1006 		return (-1);
1007 	return (major);
1008 }
1009 
1010 char *
1011 di_driver_name(di_node_t node)
1012 {
1013 	int major;
1014 	caddr_t pa;
1015 	struct di_devnm *devnm;
1016 
1017 	major = DI_NODE(node)->drv_major;
1018 	if (major < 0)
1019 		return (NULL);
1020 
1021 	pa = (caddr_t)node - DI_NODE(node)->self;
1022 	devnm = DI_DEVNM(pa + DI_ALL(pa)->devnames);
1023 
1024 	if (devnm[major].name)
1025 		return (pa + devnm[major].name);
1026 	else
1027 		return (NULL);
1028 }
1029 
1030 uint_t
1031 di_driver_ops(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 (0);
1040 
1041 	pa = (caddr_t)node - DI_NODE(node)->self;
1042 	devnm = DI_DEVNM(pa + DI_ALL(pa)->devnames);
1043 
1044 	return (devnm[major].ops);
1045 }
1046 
1047 /*
1048  * returns the length of the path, caller must free memory
1049  */
1050 char *
1051 di_devfs_path(di_node_t node)
1052 {
1053 	caddr_t pa;
1054 	di_node_t parent;
1055 	int depth = 0, len = 0;
1056 	char *buf, *name[MAX_TREE_DEPTH], *addr[MAX_TREE_DEPTH];
1057 
1058 	if (node == DI_NODE_NIL) {
1059 		errno = EINVAL;
1060 		return (NULL);
1061 	}
1062 
1063 	/*
1064 	 * trace back to root, note the node_name & address
1065 	 */
1066 	while ((parent = di_parent_node(node)) != DI_NODE_NIL) {
1067 		name[depth] = di_node_name(node);
1068 		len += strlen(name[depth]) + 1;		/* 1 for '/' */
1069 
1070 		if ((addr[depth] = di_bus_addr(node)) != NULL)
1071 			len += strlen(addr[depth]) + 1;	/* 1 for '@' */
1072 
1073 		node = parent;
1074 		depth++;
1075 	}
1076 
1077 	/*
1078 	 * get the path to the root of snapshot
1079 	 */
1080 	pa = (caddr_t)node - DI_NODE(node)->self;
1081 	name[depth] = DI_ALL(pa)->root_path;
1082 	len += strlen(name[depth]) + 1;
1083 
1084 	/*
1085 	 * allocate buffer and assemble path
1086 	 */
1087 	if ((buf = malloc(len)) == NULL) {
1088 		return (NULL);
1089 	}
1090 
1091 	(void) strcpy(buf, name[depth]);
1092 	len = strlen(buf);
1093 	if (buf[len - 1] == '/')
1094 		len--;	/* delete trailing '/' */
1095 
1096 	while (depth) {
1097 		depth--;
1098 		buf[len] = '/';
1099 		(void) strcpy(buf + len + 1, name[depth]);
1100 		len += strlen(name[depth]) + 1;
1101 		if (addr[depth] && addr[depth][0] != '\0') {
1102 			buf[len] = '@';
1103 			(void) strcpy(buf + len + 1, addr[depth]);
1104 			len += strlen(addr[depth]) + 1;
1105 		}
1106 	}
1107 
1108 	return (buf);
1109 }
1110 
1111 char *
1112 di_devfs_minor_path(di_minor_t minor)
1113 {
1114 	di_node_t	node;
1115 	char		*full_path, *name, *path;
1116 	int		full_path_len;
1117 
1118 	if (minor == DI_MINOR_NIL) {
1119 		errno = EINVAL;
1120 		return (NULL);
1121 	}
1122 
1123 	name = di_minor_name(minor);
1124 	node = di_minor_devinfo(minor);
1125 	path = di_devfs_path(node);
1126 	if (path == NULL)
1127 		return (NULL);
1128 
1129 	/* make the full path to the device minor node */
1130 	full_path_len = strlen(path) + strlen(name) + 2;
1131 	full_path = (char *)calloc(1, full_path_len);
1132 	if (full_path != NULL)
1133 		(void) snprintf(full_path, full_path_len, "%s:%s", path, name);
1134 
1135 	di_devfs_path_free(path);
1136 	return (full_path);
1137 }
1138 
1139 void
1140 di_devfs_path_free(char *buf)
1141 {
1142 	if (buf == NULL) {
1143 		DPRINTF((DI_ERR, "di_devfs_path_free NULL arg!\n"));
1144 		return;
1145 	}
1146 
1147 	free(buf);
1148 }
1149 
1150 /* minor data access */
1151 di_minor_t
1152 di_minor_next(di_node_t node, di_minor_t minor)
1153 {
1154 	caddr_t pa;
1155 
1156 	/*
1157 	 * paranoid error checking
1158 	 */
1159 	if (node == DI_NODE_NIL) {
1160 		errno = EINVAL;
1161 		return (DI_MINOR_NIL);
1162 	}
1163 
1164 	/*
1165 	 * minor is not NIL
1166 	 */
1167 	if (minor != DI_MINOR_NIL) {
1168 		if (DI_MINOR(minor)->next != 0)
1169 			return ((di_minor_t)((void *)((caddr_t)minor -
1170 			    DI_MINOR(minor)->self + DI_MINOR(minor)->next)));
1171 		else {
1172 			errno = ENXIO;
1173 			return (DI_MINOR_NIL);
1174 		}
1175 	}
1176 
1177 	/*
1178 	 * minor is NIL-->caller asks for first minor node
1179 	 */
1180 	if (DI_NODE(node)->minor_data != 0) {
1181 		return (DI_MINOR((caddr_t)node - DI_NODE(node)->self +
1182 		    DI_NODE(node)->minor_data));
1183 	}
1184 
1185 	/*
1186 	 * no minor data-->check if snapshot includes minor data
1187 	 *	in order to set the correct errno
1188 	 */
1189 	pa = (caddr_t)node - DI_NODE(node)->self;
1190 	if (DINFOMINOR & DI_ALL(pa)->command)
1191 		errno = ENXIO;
1192 	else
1193 		errno = ENOTSUP;
1194 
1195 	return (DI_MINOR_NIL);
1196 }
1197 
1198 /* private interface for dealing with alias minor link generation */
1199 di_node_t
1200 di_minor_devinfo(di_minor_t minor)
1201 {
1202 	if (minor == DI_MINOR_NIL) {
1203 		errno = EINVAL;
1204 		return (DI_NODE_NIL);
1205 	}
1206 
1207 	return (DI_NODE((caddr_t)minor - DI_MINOR(minor)->self +
1208 	    DI_MINOR(minor)->node));
1209 }
1210 
1211 ddi_minor_type
1212 di_minor_type(di_minor_t minor)
1213 {
1214 	return (DI_MINOR(minor)->type);
1215 }
1216 
1217 char *
1218 di_minor_name(di_minor_t minor)
1219 {
1220 	if (DI_MINOR(minor)->name == 0)
1221 		return (NULL);
1222 
1223 	return ((caddr_t)minor - DI_MINOR(minor)->self + DI_MINOR(minor)->name);
1224 }
1225 
1226 dev_t
1227 di_minor_devt(di_minor_t minor)
1228 {
1229 	return (makedev(DI_MINOR(minor)->dev_major,
1230 		DI_MINOR(minor)->dev_minor));
1231 }
1232 
1233 int
1234 di_minor_spectype(di_minor_t minor)
1235 {
1236 	return (DI_MINOR(minor)->spec_type);
1237 }
1238 
1239 char *
1240 di_minor_nodetype(di_minor_t minor)
1241 {
1242 	if (DI_MINOR(minor)->node_type == 0)
1243 		return (NULL);
1244 
1245 	return ((caddr_t)minor -
1246 		DI_MINOR(minor)->self + DI_MINOR(minor)->node_type);
1247 }
1248 
1249 /*
1250  * Single public interface for accessing software properties
1251  */
1252 di_prop_t
1253 di_prop_next(di_node_t node, di_prop_t prop)
1254 {
1255 	int list = DI_PROP_DRV_LIST;
1256 
1257 	/*
1258 	 * paranoid check
1259 	 */
1260 	if (node == DI_NODE_NIL) {
1261 		errno = EINVAL;
1262 		return (DI_PROP_NIL);
1263 	}
1264 
1265 	/*
1266 	 * Find which prop list we are at
1267 	 */
1268 	if (prop != DI_PROP_NIL)
1269 		list = DI_PROP(prop)->prop_list;
1270 
1271 	do {
1272 		switch (list++) {
1273 		case DI_PROP_DRV_LIST:
1274 			prop = di_prop_drv_next(node, prop);
1275 			break;
1276 		case DI_PROP_SYS_LIST:
1277 			prop = di_prop_sys_next(node, prop);
1278 			break;
1279 		case DI_PROP_GLB_LIST:
1280 			prop = di_prop_global_next(node, prop);
1281 			break;
1282 		case DI_PROP_HW_LIST:
1283 			prop = di_prop_hw_next(node, prop);
1284 			break;
1285 		default:	/* shouldn't happen */
1286 			errno = EFAULT;
1287 			return (DI_PROP_NIL);
1288 		}
1289 	} while ((prop == DI_PROP_NIL) && (list <= DI_PROP_HW_LIST));
1290 
1291 	return (prop);
1292 }
1293 
1294 dev_t
1295 di_prop_devt(di_prop_t prop)
1296 {
1297 	return (makedev(DI_PROP(prop)->dev_major, DI_PROP(prop)->dev_minor));
1298 }
1299 
1300 char *
1301 di_prop_name(di_prop_t prop)
1302 {
1303 	if (DI_PROP(prop)->prop_name == 0)
1304 		return (NULL);
1305 
1306 	return ((caddr_t)prop - DI_PROP(prop)->self + DI_PROP(prop)->prop_name);
1307 }
1308 
1309 int
1310 di_prop_type(di_prop_t prop)
1311 {
1312 	uint_t flags = DI_PROP(prop)->prop_flags;
1313 
1314 	if (flags & DDI_PROP_UNDEF_IT)
1315 		return (DI_PROP_TYPE_UNDEF_IT);
1316 
1317 	if (DI_PROP(prop)->prop_len == 0)
1318 		return (DI_PROP_TYPE_BOOLEAN);
1319 
1320 	if ((flags & DDI_PROP_TYPE_MASK) == DDI_PROP_TYPE_ANY)
1321 		return (DI_PROP_TYPE_UNKNOWN);
1322 
1323 	if (flags & DDI_PROP_TYPE_INT)
1324 		return (DI_PROP_TYPE_INT);
1325 
1326 	if (flags & DDI_PROP_TYPE_INT64)
1327 		return (DI_PROP_TYPE_INT64);
1328 
1329 	if (flags & DDI_PROP_TYPE_STRING)
1330 		return (DI_PROP_TYPE_STRING);
1331 
1332 	if (flags & DDI_PROP_TYPE_BYTE)
1333 		return (DI_PROP_TYPE_BYTE);
1334 
1335 	/*
1336 	 * Shouldn't get here. In case we do, return unknown type.
1337 	 *
1338 	 * XXX--When DDI_PROP_TYPE_COMPOSITE is implemented, we need
1339 	 *	to add DI_PROP_TYPE_COMPOSITE.
1340 	 */
1341 	DPRINTF((DI_ERR, "Unimplemented property type: 0x%x\n", flags));
1342 
1343 	return (DI_PROP_TYPE_UNKNOWN);
1344 }
1345 
1346 /*
1347  * Extract type-specific values of an property
1348  */
1349 extern int di_prop_decode_common(void *prop_data, int len,
1350 	int ddi_type, int prom);
1351 
1352 int
1353 di_prop_ints(di_prop_t prop, int **prop_data)
1354 {
1355 	if (DI_PROP(prop)->prop_len == 0)
1356 		return (0);	/* boolean property */
1357 
1358 	if ((DI_PROP(prop)->prop_data == 0) ||
1359 	    (DI_PROP(prop)->prop_data == (di_off_t)-1)) {
1360 		errno = EFAULT;
1361 		*prop_data = NULL;
1362 		return (-1);
1363 	}
1364 
1365 	*prop_data = (int *)((void *)((caddr_t)prop - DI_PROP(prop)->self
1366 	    + DI_PROP(prop)->prop_data));
1367 
1368 	return (di_prop_decode_common((void *)prop_data,
1369 	    DI_PROP(prop)->prop_len, DI_PROP_TYPE_INT, 0));
1370 }
1371 
1372 int
1373 di_prop_int64(di_prop_t prop, int64_t **prop_data)
1374 {
1375 	if (DI_PROP(prop)->prop_len == 0)
1376 		return (0);	/* boolean property */
1377 
1378 	if ((DI_PROP(prop)->prop_data == 0) ||
1379 	    (DI_PROP(prop)->prop_data == (di_off_t)-1)) {
1380 		errno = EFAULT;
1381 		*prop_data = NULL;
1382 		return (-1);
1383 	}
1384 
1385 	*prop_data = (int64_t *)((void *)((caddr_t)prop - DI_PROP(prop)->self
1386 	    + DI_PROP(prop)->prop_data));
1387 
1388 	return (di_prop_decode_common((void *)prop_data,
1389 	    DI_PROP(prop)->prop_len, DI_PROP_TYPE_INT64, 0));
1390 }
1391 
1392 int
1393 di_prop_strings(di_prop_t prop, char **prop_data)
1394 {
1395 	if (DI_PROP(prop)->prop_len == 0)
1396 		return (0);	/* boolean property */
1397 
1398 	if ((DI_PROP(prop)->prop_data == 0) ||
1399 	    (DI_PROP(prop)->prop_data == (di_off_t)-1)) {
1400 		errno = EFAULT;
1401 		*prop_data = NULL;
1402 		return (-1);
1403 	}
1404 
1405 	*prop_data = (char *)((caddr_t)prop - DI_PROP(prop)->self
1406 	    + DI_PROP(prop)->prop_data);
1407 
1408 	return (di_prop_decode_common((void *)prop_data,
1409 	    DI_PROP(prop)->prop_len, DI_PROP_TYPE_STRING, 0));
1410 }
1411 
1412 int
1413 di_prop_bytes(di_prop_t prop, uchar_t **prop_data)
1414 {
1415 	if (DI_PROP(prop)->prop_len == 0)
1416 		return (0);	/* boolean property */
1417 
1418 	if ((DI_PROP(prop)->prop_data == 0) ||
1419 	    (DI_PROP(prop)->prop_data == (di_off_t)-1)) {
1420 		errno = EFAULT;
1421 		*prop_data = NULL;
1422 		return (-1);
1423 	}
1424 
1425 	*prop_data = (uchar_t *)((caddr_t)prop - DI_PROP(prop)->self
1426 	    + DI_PROP(prop)->prop_data);
1427 
1428 	return (di_prop_decode_common((void *)prop_data,
1429 	    DI_PROP(prop)->prop_len, DI_PROP_TYPE_BYTE, 0));
1430 }
1431 
1432 /*
1433  * returns 1 for match, 0 for no match
1434  */
1435 static int
1436 match_prop(di_prop_t prop, dev_t match_dev, const char *name, int type)
1437 {
1438 	int prop_type;
1439 
1440 #ifdef DEBUG
1441 	if (di_prop_name(prop) == NULL) {
1442 		DPRINTF((DI_ERR, "libdevinfo: property has no name!\n"));
1443 		return (0);
1444 	}
1445 #endif /* DEBUG */
1446 
1447 	if (strcmp(name, di_prop_name(prop)) != 0)
1448 		return (0);
1449 
1450 	if ((match_dev != DDI_DEV_T_ANY) && (di_prop_devt(prop) != match_dev))
1451 		return (0);
1452 
1453 	/*
1454 	 * XXX prop_type is different from DDI_*. See PSARC 1997/127.
1455 	 */
1456 	prop_type = di_prop_type(prop);
1457 	if ((prop_type != DI_PROP_TYPE_UNKNOWN) && (prop_type != type) &&
1458 	    (prop_type != DI_PROP_TYPE_BOOLEAN))
1459 		return (0);
1460 
1461 	return (1);
1462 }
1463 
1464 static di_prop_t
1465 di_prop_search(dev_t match_dev, di_node_t node, const char *name,
1466     int type)
1467 {
1468 	di_prop_t prop = DI_PROP_NIL;
1469 
1470 	/*
1471 	 * The check on match_dev follows ddi_prop_lookup_common().
1472 	 * Other checks are libdevinfo specific implementation.
1473 	 */
1474 	if ((node == DI_NODE_NIL) || (name == NULL) || (strlen(name) == 0) ||
1475 	    (match_dev == DDI_DEV_T_NONE) || !DI_PROP_TYPE_VALID(type)) {
1476 		errno = EINVAL;
1477 		return (DI_PROP_NIL);
1478 	}
1479 
1480 	while ((prop = di_prop_next(node, prop)) != DI_PROP_NIL) {
1481 		DPRINTF((DI_TRACE1, "match prop name %s, devt 0x%lx, type %d\n",
1482 		    di_prop_name(prop), di_prop_devt(prop),
1483 		    di_prop_type(prop)));
1484 		if (match_prop(prop, match_dev, name, type))
1485 			return (prop);
1486 	}
1487 
1488 	return (DI_PROP_NIL);
1489 }
1490 
1491 int
1492 di_prop_lookup_ints(dev_t dev, di_node_t node, const char *prop_name,
1493 	int **prop_data)
1494 {
1495 	di_prop_t prop;
1496 
1497 	if ((prop = di_prop_search(dev, node, prop_name,
1498 	    DI_PROP_TYPE_INT)) == DI_PROP_NIL)
1499 		return (-1);
1500 
1501 	return (di_prop_ints(prop, (void *)prop_data));
1502 }
1503 
1504 int
1505 di_prop_lookup_int64(dev_t dev, di_node_t node, const char *prop_name,
1506 	int64_t **prop_data)
1507 {
1508 	di_prop_t prop;
1509 
1510 	if ((prop = di_prop_search(dev, node, prop_name,
1511 	    DI_PROP_TYPE_INT64)) == DI_PROP_NIL)
1512 		return (-1);
1513 
1514 	return (di_prop_int64(prop, (void *)prop_data));
1515 }
1516 
1517 int
1518 di_prop_lookup_strings(dev_t dev, di_node_t node, const char *prop_name,
1519     char **prop_data)
1520 {
1521 	di_prop_t prop;
1522 
1523 	if ((prop = di_prop_search(dev, node, prop_name,
1524 	    DI_PROP_TYPE_STRING)) == DI_PROP_NIL)
1525 		return (-1);
1526 
1527 	return (di_prop_strings(prop, (void *)prop_data));
1528 }
1529 
1530 int
1531 di_prop_lookup_bytes(dev_t dev, di_node_t node, const char *prop_name,
1532 	uchar_t **prop_data)
1533 {
1534 	di_prop_t prop;
1535 
1536 	if ((prop = di_prop_search(dev, node, prop_name,
1537 	    DI_PROP_TYPE_BYTE)) == DI_PROP_NIL)
1538 		return (-1);
1539 
1540 	return (di_prop_bytes(prop, (void *)prop_data));
1541 }
1542 
1543 /*
1544  * Consolidation private property access functions
1545  */
1546 enum prop_type {
1547 	PROP_TYPE_DRV,
1548 	PROP_TYPE_SYS,
1549 	PROP_TYPE_GLOB,
1550 	PROP_TYPE_HW
1551 };
1552 
1553 static di_prop_t
1554 di_prop_next_common(di_node_t node, di_prop_t prop, int prop_type)
1555 {
1556 	caddr_t pa;
1557 	di_off_t prop_off = 0;
1558 
1559 	if (prop != DI_PROP_NIL) {
1560 		if (DI_PROP(prop)->next) {
1561 			return (DI_PROP((caddr_t)prop -
1562 			    DI_PROP(prop)->self + DI_PROP(prop)->next));
1563 		} else {
1564 			return (DI_PROP_NIL);
1565 		}
1566 	}
1567 
1568 
1569 	/*
1570 	 * prop is NIL, caller asks for first property
1571 	 */
1572 	pa = (caddr_t)node - DI_NODE(node)->self;
1573 	switch (prop_type) {
1574 	case PROP_TYPE_DRV:
1575 		prop_off = DI_NODE(node)->drv_prop;
1576 		break;
1577 	case PROP_TYPE_SYS:
1578 		prop_off = DI_NODE(node)->sys_prop;
1579 		break;
1580 	case PROP_TYPE_HW:
1581 		prop_off = DI_NODE(node)->hw_prop;
1582 		break;
1583 	case PROP_TYPE_GLOB:
1584 		prop_off = DI_NODE(node)->glob_prop;
1585 		if (prop_off == -1) {
1586 			/* no global property */
1587 			prop_off = 0;
1588 		} else if ((prop_off == 0) && (DI_NODE(node)->drv_major >= 0)) {
1589 			/* refer to devnames array */
1590 			struct di_devnm *devnm = DI_DEVNM(pa +
1591 			    DI_ALL(pa)->devnames + (DI_NODE(node)->drv_major *
1592 			    sizeof (struct di_devnm)));
1593 			prop_off = devnm->global_prop;
1594 		}
1595 		break;
1596 	}
1597 
1598 	if (prop_off) {
1599 		return (DI_PROP(pa + prop_off));
1600 	}
1601 
1602 	/*
1603 	 * no prop found. Check the reason for not found
1604 	 */
1605 	if (DINFOPROP & DI_ALL(pa)->command)
1606 		errno = ENXIO;
1607 	else
1608 		errno = ENOTSUP;
1609 
1610 	return (DI_PROP_NIL);
1611 }
1612 
1613 di_prop_t
1614 di_prop_drv_next(di_node_t node, di_prop_t prop)
1615 {
1616 	return (di_prop_next_common(node, prop, PROP_TYPE_DRV));
1617 }
1618 
1619 di_prop_t
1620 di_prop_sys_next(di_node_t node, di_prop_t prop)
1621 {
1622 	return (di_prop_next_common(node, prop, PROP_TYPE_SYS));
1623 }
1624 
1625 di_prop_t
1626 di_prop_global_next(di_node_t node, di_prop_t prop)
1627 {
1628 	return (di_prop_next_common(node, prop, PROP_TYPE_GLOB));
1629 }
1630 
1631 di_prop_t
1632 di_prop_hw_next(di_node_t node, di_prop_t prop)
1633 {
1634 	return (di_prop_next_common(node, prop, PROP_TYPE_HW));
1635 }
1636 
1637 int
1638 di_prop_rawdata(di_prop_t prop, uchar_t **prop_data)
1639 {
1640 #ifdef DEBUG
1641 	if (prop == DI_PROP_NIL) {
1642 		errno = EINVAL;
1643 		return (-1);
1644 	}
1645 #endif /* DEBUG */
1646 
1647 	if (DI_PROP(prop)->prop_len == 0) {
1648 		*prop_data = NULL;
1649 		return (0);
1650 	}
1651 
1652 	if ((DI_PROP(prop)->prop_data == 0) ||
1653 	    (DI_PROP(prop)->prop_data == (di_off_t)-1)) {
1654 		errno = EFAULT;
1655 		*prop_data = NULL;
1656 		return (-1);
1657 	}
1658 
1659 	/*
1660 	 * No memory allocation.
1661 	 */
1662 	*prop_data = (uchar_t *)((caddr_t)prop - DI_PROP(prop)->self +
1663 	    DI_PROP(prop)->prop_data);
1664 
1665 	return (DI_PROP(prop)->prop_len);
1666 }
1667 
1668 /*
1669  * Consolidation private interfaces for accessing I/O multipathing data
1670  */
1671 di_path_t
1672 di_path_next_client(di_node_t node, di_path_t path)
1673 {
1674 	caddr_t pa;
1675 
1676 	/*
1677 	 * path is not NIL
1678 	 */
1679 	if (path != DI_PATH_NIL) {
1680 		if (DI_PATH(path)->path_p_link != 0)
1681 			return (DI_PATH((void *)((caddr_t)path -
1682 			    DI_PATH(path)->self + DI_PATH(path)->path_p_link)));
1683 		else {
1684 			errno = ENXIO;
1685 			return (DI_PATH_NIL);
1686 		}
1687 	}
1688 
1689 	/*
1690 	 * Path is NIL; the caller is asking for the first path info node
1691 	 */
1692 	if (DI_NODE(node)->multipath_phci != 0) {
1693 		DPRINTF((DI_INFO, "phci: returning %p\n", ((caddr_t)node -
1694 		    DI_NODE(node)->self + DI_NODE(node)->multipath_phci)));
1695 		return (DI_PATH((caddr_t)node - DI_NODE(node)->self +
1696 		    DI_NODE(node)->multipath_phci));
1697 	}
1698 
1699 	/*
1700 	 * No pathing data; check if the snapshot includes path data in order
1701 	 * to set errno properly.
1702 	 */
1703 	pa = (caddr_t)node - DI_NODE(node)->self;
1704 	if (DINFOPATH & (DI_ALL(pa)->command))
1705 		errno = ENXIO;
1706 	else
1707 		errno = ENOTSUP;
1708 
1709 	return (DI_PATH_NIL);
1710 }
1711 
1712 di_path_t
1713 di_path_next_phci(di_node_t node, di_path_t path)
1714 {
1715 	caddr_t pa;
1716 
1717 	/*
1718 	 * path is not NIL
1719 	 */
1720 	if (path != DI_PATH_NIL) {
1721 		if (DI_PATH(path)->path_c_link != 0)
1722 			return (DI_PATH((caddr_t)path - DI_PATH(path)->self
1723 			    + DI_PATH(path)->path_c_link));
1724 		else {
1725 			errno = ENXIO;
1726 			return (DI_PATH_NIL);
1727 		}
1728 	}
1729 
1730 	/*
1731 	 * Path is NIL; the caller is asking for the first path info node
1732 	 */
1733 	if (DI_NODE(node)->multipath_client != 0) {
1734 		DPRINTF((DI_INFO, "client: returning %p\n", ((caddr_t)node -
1735 		    DI_NODE(node)->self + DI_NODE(node)->multipath_client)));
1736 		return (DI_PATH((caddr_t)node - DI_NODE(node)->self +
1737 		    DI_NODE(node)->multipath_client));
1738 	}
1739 
1740 	/*
1741 	 * No pathing data; check if the snapshot includes path data in order
1742 	 * to set errno properly.
1743 	 */
1744 	pa = (caddr_t)node - DI_NODE(node)->self;
1745 	if (DINFOPATH & (DI_ALL(pa)->command))
1746 		errno = ENXIO;
1747 	else
1748 		errno = ENOTSUP;
1749 
1750 	return (DI_PATH_NIL);
1751 }
1752 
1753 /*
1754  * XXX Obsolete wrapper to be removed. Won't work under multilevel.
1755  */
1756 di_path_t
1757 di_path_next(di_node_t node, di_path_t path)
1758 {
1759 	if (node == DI_NODE_NIL) {
1760 		errno = EINVAL;
1761 		return (DI_PATH_NIL);
1762 	}
1763 
1764 	if (DI_NODE(node)->multipath_client) {
1765 		return (di_path_next_phci(node, path));
1766 	} else if (DI_NODE(node)->multipath_phci) {
1767 		return (di_path_next_client(node, path));
1768 	} else {
1769 		/*
1770 		 * The node had multipathing data but didn't appear to be a
1771 		 * phci *or* a client; probably a programmer error.
1772 		 */
1773 		errno = EINVAL;
1774 		return (DI_PATH_NIL);
1775 	}
1776 }
1777 
1778 di_path_state_t
1779 di_path_state(di_path_t path)
1780 {
1781 	return ((di_path_state_t)DI_PATH(path)->path_state);
1782 }
1783 
1784 char *
1785 di_path_addr(di_path_t path, char *buf)
1786 {
1787 	caddr_t pa;		/* starting address of map */
1788 
1789 	pa = (caddr_t)path - DI_PATH(path)->self;
1790 
1791 	(void) strncpy(buf, (char *)(pa + DI_PATH(path)->path_addr),
1792 	    MAXPATHLEN);
1793 	return (buf);
1794 }
1795 
1796 di_node_t
1797 di_path_client_node(di_path_t path)
1798 {
1799 	caddr_t pa;		/* starting address of map */
1800 
1801 	if (path == DI_PATH_NIL) {
1802 		errno = EINVAL;
1803 		return (DI_PATH_NIL);
1804 	}
1805 
1806 	DPRINTF((DI_TRACE, "Get client node for path %p\n", path));
1807 
1808 	pa = (caddr_t)path - DI_PATH(path)->self;
1809 
1810 	if (DI_PATH(path)->path_client) {
1811 		return (DI_NODE(pa + DI_PATH(path)->path_client));
1812 	}
1813 
1814 	/*
1815 	 * Deal with error condition:
1816 	 *   If parent doesn't exist and node is not the root,
1817 	 *   set errno to ENOTSUP. Otherwise, set errno to ENXIO.
1818 	 */
1819 	if ((DI_PATH(path)->path_snap_state & DI_PATH_SNAP_NOCLIENT) == 0)
1820 		errno = ENOTSUP;
1821 	else
1822 		errno = ENXIO;
1823 
1824 	return (DI_NODE_NIL);
1825 }
1826 
1827 di_node_t
1828 di_path_phci_node(di_path_t path)
1829 {
1830 	caddr_t pa;		/* starting address of map */
1831 
1832 	if (path == DI_PATH_NIL) {
1833 		errno = EINVAL;
1834 		return (DI_PATH_NIL);
1835 	}
1836 
1837 	DPRINTF((DI_TRACE, "Get phci node for path %p\n", path));
1838 
1839 	pa = (caddr_t)path - DI_PATH(path)->self;
1840 
1841 	if (DI_PATH(path)->path_phci) {
1842 		return (DI_NODE(pa + DI_PATH(path)->path_phci));
1843 	}
1844 
1845 	/*
1846 	 * Deal with error condition:
1847 	 *   If parent doesn't exist and node is not the root,
1848 	 *   set errno to ENOTSUP. Otherwise, set errno to ENXIO.
1849 	 */
1850 	if ((DI_PATH(path)->path_snap_state & DI_PATH_SNAP_NOPHCI) == 0)
1851 		errno = ENOTSUP;
1852 	else
1853 		errno = ENXIO;
1854 
1855 	return (DI_NODE_NIL);
1856 }
1857 
1858 di_path_prop_t
1859 di_path_prop_next(di_path_t path, di_path_prop_t prop)
1860 {
1861 	caddr_t pa;
1862 
1863 	if (path == DI_PATH_NIL) {
1864 		errno = EINVAL;
1865 		return (DI_PROP_NIL);
1866 	}
1867 
1868 	/*
1869 	 * prop is not NIL
1870 	 */
1871 	if (prop != DI_PROP_NIL) {
1872 		if (DI_PROP(prop)->next != 0)
1873 			return (DI_PATHPROP((caddr_t)prop -
1874 			    DI_PROP(prop)->self + DI_PROP(prop)->next));
1875 		else {
1876 			errno = ENXIO;
1877 			return (DI_PROP_NIL);
1878 		}
1879 	}
1880 
1881 	/*
1882 	 * prop is NIL-->caller asks for first property
1883 	 */
1884 	pa = (caddr_t)path - DI_PATH(path)->self;
1885 	if (DI_PATH(path)->path_prop != 0) {
1886 		return (DI_PATHPROP(pa + DI_PATH(path)->path_prop));
1887 	}
1888 
1889 	/*
1890 	 * no property data-->check if snapshot includes props
1891 	 *	in order to set the correct errno
1892 	 */
1893 	if (DINFOPROP & (DI_ALL(pa)->command))
1894 		errno = ENXIO;
1895 	else
1896 		errno = ENOTSUP;
1897 
1898 	return (DI_PROP_NIL);
1899 }
1900 
1901 char *
1902 di_path_prop_name(di_path_prop_t prop)
1903 {
1904 	caddr_t pa;		/* starting address of map */
1905 	pa = (caddr_t)prop - DI_PATHPROP(prop)->self;
1906 	return ((char *)(pa + DI_PATHPROP(prop)->prop_name));
1907 }
1908 
1909 int
1910 di_path_prop_len(di_path_prop_t prop)
1911 {
1912 	return (DI_PATHPROP(prop)->prop_len);
1913 }
1914 
1915 int
1916 di_path_prop_type(di_path_prop_t prop)
1917 {
1918 	switch (DI_PATHPROP(prop)->prop_type) {
1919 		case DDI_PROP_TYPE_INT:
1920 			return (DI_PROP_TYPE_INT);
1921 		case DDI_PROP_TYPE_INT64:
1922 			return (DI_PROP_TYPE_INT64);
1923 		case DDI_PROP_TYPE_BYTE:
1924 			return (DI_PROP_TYPE_BYTE);
1925 		case DDI_PROP_TYPE_STRING:
1926 			return (DI_PROP_TYPE_STRING);
1927 	}
1928 	return (DI_PROP_TYPE_UNKNOWN);
1929 }
1930 
1931 int
1932 di_path_prop_bytes(di_path_prop_t prop, uchar_t **prop_data)
1933 {
1934 	if ((DI_PATHPROP(prop)->prop_data == 0) ||
1935 	    (DI_PATHPROP(prop)->prop_data == (di_off_t)-1)) {
1936 		errno = EFAULT;
1937 		*prop_data = NULL;
1938 		return (-1);
1939 	}
1940 
1941 	*prop_data = (uchar_t *)((caddr_t)prop - DI_PATHPROP(prop)->self
1942 	    + DI_PATHPROP(prop)->prop_data);
1943 
1944 	return (di_prop_decode_common((void *)prop_data,
1945 	    DI_PATHPROP(prop)->prop_len, DI_PROP_TYPE_BYTE, 0));
1946 }
1947 
1948 int
1949 di_path_prop_ints(di_path_prop_t prop, int **prop_data)
1950 {
1951 	if (DI_PATHPROP(prop)->prop_len == 0)
1952 		return (0);
1953 
1954 	if ((DI_PATHPROP(prop)->prop_data == 0) ||
1955 	    (DI_PATHPROP(prop)->prop_data == (di_off_t)-1)) {
1956 		errno = EFAULT;
1957 		*prop_data = NULL;
1958 		return (-1);
1959 	}
1960 
1961 	*prop_data = (int *)((void *)((caddr_t)prop - DI_PATHPROP(prop)->self
1962 	    + DI_PATHPROP(prop)->prop_data));
1963 
1964 	return (di_prop_decode_common((void *)prop_data,
1965 	    DI_PATHPROP(prop)->prop_len, DI_PROP_TYPE_INT, 0));
1966 }
1967 
1968 int
1969 di_path_prop_int64s(di_path_prop_t prop, int64_t **prop_data)
1970 {
1971 	if (DI_PATHPROP(prop)->prop_len == 0)
1972 		return (0);
1973 
1974 	if ((DI_PATHPROP(prop)->prop_data == 0) ||
1975 	    (DI_PATHPROP(prop)->prop_data == (di_off_t)-1)) {
1976 		errno = EFAULT;
1977 		*prop_data = NULL;
1978 		return (-1);
1979 	}
1980 
1981 	*prop_data = (int64_t *)((void *)((caddr_t)prop -
1982 	    DI_PATHPROP(prop)->self + DI_PATHPROP(prop)->prop_data));
1983 
1984 	return (di_prop_decode_common((void *)prop_data,
1985 	    DI_PATHPROP(prop)->prop_len, DI_PROP_TYPE_INT64, 0));
1986 }
1987 
1988 int
1989 di_path_prop_strings(di_path_prop_t prop, char **prop_data)
1990 {
1991 	if (DI_PATHPROP(prop)->prop_len == 0)
1992 		return (0);
1993 
1994 	if ((DI_PATHPROP(prop)->prop_data == 0) ||
1995 	    (DI_PATHPROP(prop)->prop_data == (di_off_t)-1)) {
1996 		errno = EFAULT;
1997 		*prop_data = NULL;
1998 		return (-1);
1999 	}
2000 
2001 	*prop_data = (char *)((caddr_t)prop - DI_PATHPROP(prop)->self
2002 	    + DI_PATHPROP(prop)->prop_data);
2003 
2004 	return (di_prop_decode_common((void *)prop_data,
2005 	    DI_PATHPROP(prop)->prop_len, DI_PROP_TYPE_STRING, 0));
2006 }
2007 
2008 static di_path_prop_t
2009 di_path_prop_search(di_path_t path, const char *name, int type)
2010 {
2011 	di_path_prop_t prop = DI_PROP_NIL;
2012 
2013 	/*
2014 	 * Sanity check arguments
2015 	 */
2016 	if ((path == DI_PATH_NIL) || (name == NULL) || (strlen(name) == 0) ||
2017 	    !DI_PROP_TYPE_VALID(type)) {
2018 		errno = EINVAL;
2019 		return (DI_PROP_NIL);
2020 	}
2021 
2022 	while ((prop = di_path_prop_next(path, prop)) != DI_PROP_NIL) {
2023 		int prop_type = di_path_prop_type(prop);
2024 
2025 		DPRINTF((DI_TRACE1, "match path prop name %s, type %d\n",
2026 		    di_path_prop_name(prop), prop_type));
2027 
2028 		if (strcmp(name, di_path_prop_name(prop)) != 0)
2029 			continue;
2030 
2031 		if ((prop_type != DI_PROP_TYPE_UNKNOWN) && (prop_type != type))
2032 			continue;
2033 
2034 		return (prop);
2035 	}
2036 
2037 	return (DI_PROP_NIL);
2038 }
2039 
2040 int
2041 di_path_prop_lookup_bytes(di_path_t path, const char *prop_name,
2042     uchar_t **prop_data)
2043 {
2044 	di_path_prop_t prop;
2045 
2046 	if ((prop = di_path_prop_search(path, prop_name,
2047 	    DI_PROP_TYPE_BYTE)) == DI_PROP_NIL)
2048 		return (-1);
2049 
2050 	return (di_path_prop_bytes(prop, prop_data));
2051 }
2052 
2053 int
2054 di_path_prop_lookup_ints(di_path_t path, const char *prop_name,
2055     int **prop_data)
2056 {
2057 	di_path_prop_t prop;
2058 
2059 	if ((prop = di_path_prop_search(path, prop_name,
2060 	    DI_PROP_TYPE_INT)) == DI_PROP_NIL)
2061 		return (-1);
2062 
2063 	return (di_path_prop_ints(prop, prop_data));
2064 }
2065 
2066 int
2067 di_path_prop_lookup_int64s(di_path_t path, const char *prop_name,
2068     int64_t **prop_data)
2069 {
2070 	di_path_prop_t prop;
2071 
2072 	if ((prop = di_path_prop_search(path, prop_name,
2073 	    DI_PROP_TYPE_INT64)) == DI_PROP_NIL)
2074 		return (-1);
2075 
2076 	return (di_path_prop_int64s(prop, prop_data));
2077 }
2078 
2079 int di_path_prop_lookup_strings(di_path_t path, const char *prop_name,
2080     char **prop_data)
2081 {
2082 	di_path_prop_t prop;
2083 
2084 	if ((prop = di_path_prop_search(path, prop_name,
2085 	    DI_PROP_TYPE_STRING)) == DI_PROP_NIL)
2086 		return (-1);
2087 
2088 	return (di_path_prop_strings(prop, prop_data));
2089 }
2090 
2091 /*
2092  * Consolidation private interfaces for traversing vhci nodes.
2093  */
2094 di_node_t
2095 di_vhci_first_node(di_node_t root)
2096 {
2097 	struct di_all *dap;
2098 	caddr_t		pa;		/* starting address of map */
2099 
2100 	DPRINTF((DI_INFO, "Get first vhci node\n"));
2101 
2102 	if (root == DI_NODE_NIL) {
2103 		errno = EINVAL;
2104 		return (DI_NODE_NIL);
2105 	}
2106 
2107 	pa = (caddr_t)root - DI_NODE(root)->self;
2108 	dap = DI_ALL(pa);
2109 
2110 	if (dap->top_vhci_devinfo == NULL) {
2111 		errno = ENXIO;
2112 		return (DI_NODE_NIL);
2113 	}
2114 
2115 	return (DI_NODE(pa + dap->top_vhci_devinfo));
2116 }
2117 
2118 di_node_t
2119 di_vhci_next_node(di_node_t node)
2120 {
2121 	caddr_t		pa;		/* starting address of map */
2122 
2123 	if (node == DI_NODE_NIL) {
2124 		errno = EINVAL;
2125 		return (DI_NODE_NIL);
2126 	}
2127 
2128 	DPRINTF((DI_TRACE, "next vhci node on the snap shot:"
2129 	    " current=%s\n", di_node_name(node)));
2130 
2131 	if (DI_NODE(node)->next_vhci == NULL) {
2132 		errno = ENXIO;
2133 		return (DI_NODE_NIL);
2134 	}
2135 
2136 	pa = (caddr_t)node - DI_NODE(node)->self;
2137 
2138 	return (DI_NODE(pa + DI_NODE(node)->next_vhci));
2139 }
2140 
2141 /*
2142  * Consolidation private interfaces for traversing phci nodes.
2143  */
2144 di_node_t
2145 di_phci_first_node(di_node_t vhci_node)
2146 {
2147 	caddr_t		pa;		/* starting address of map */
2148 
2149 	DPRINTF((DI_INFO, "Get first phci node:\n"
2150 	    " current=%s", di_node_name(vhci_node)));
2151 
2152 	if (vhci_node == DI_NODE_NIL) {
2153 		errno = EINVAL;
2154 		return (DI_NODE_NIL);
2155 	}
2156 
2157 	pa = (caddr_t)vhci_node - DI_NODE(vhci_node)->self;
2158 
2159 	if (DI_NODE(vhci_node)->top_phci == NULL) {
2160 		errno = ENXIO;
2161 		return (DI_NODE_NIL);
2162 	}
2163 
2164 	return (DI_NODE(pa + DI_NODE(vhci_node)->top_phci));
2165 }
2166 
2167 di_node_t
2168 di_phci_next_node(di_node_t node)
2169 {
2170 	caddr_t		pa;		/* starting address of map */
2171 
2172 	if (node == DI_NODE_NIL) {
2173 		errno = EINVAL;
2174 		return (DI_NODE_NIL);
2175 	}
2176 
2177 	DPRINTF((DI_TRACE, "next phci node on the snap shot:"
2178 	    " current=%s\n", di_node_name(node)));
2179 
2180 	if (DI_NODE(node)->next_phci == NULL) {
2181 		errno = ENXIO;
2182 		return (DI_NODE_NIL);
2183 	}
2184 
2185 	pa = (caddr_t)node - DI_NODE(node)->self;
2186 
2187 	return (DI_NODE(pa + DI_NODE(node)->next_phci));
2188 }
2189 
2190 /*
2191  * Consolidation private interfaces for private data
2192  */
2193 void *
2194 di_parent_private_data(di_node_t node)
2195 {
2196 	caddr_t pa;
2197 
2198 	if (DI_NODE(node)->parent_data == 0) {
2199 		errno = ENXIO;
2200 		return (NULL);
2201 	}
2202 
2203 	if (DI_NODE(node)->parent_data == (di_off_t)-1) {
2204 		/*
2205 		 * Private data requested, but not obtained due to a memory
2206 		 * error (e.g. wrong format specified)
2207 		 */
2208 		errno = EFAULT;
2209 		return (NULL);
2210 	}
2211 
2212 	pa = (caddr_t)node - DI_NODE(node)->self;
2213 	if (DI_NODE(node)->parent_data)
2214 		return (pa + DI_NODE(node)->parent_data);
2215 
2216 	if (DI_ALL(pa)->command & DINFOPRIVDATA)
2217 		errno = ENXIO;
2218 	else
2219 		errno = ENOTSUP;
2220 
2221 	return (NULL);
2222 }
2223 
2224 void *
2225 di_driver_private_data(di_node_t node)
2226 {
2227 	caddr_t pa;
2228 
2229 	if (DI_NODE(node)->driver_data == 0) {
2230 		errno = ENXIO;
2231 		return (NULL);
2232 	}
2233 
2234 	if (DI_NODE(node)->driver_data == (di_off_t)-1) {
2235 		/*
2236 		 * Private data requested, but not obtained due to a memory
2237 		 * error (e.g. wrong format specified)
2238 		 */
2239 		errno = EFAULT;
2240 		return (NULL);
2241 	}
2242 
2243 	pa = (caddr_t)node - DI_NODE(node)->self;
2244 	if (DI_NODE(node)->driver_data)
2245 		return (pa + DI_NODE(node)->driver_data);
2246 
2247 	if (DI_ALL(pa)->command & DINFOPRIVDATA)
2248 		errno = ENXIO;
2249 	else
2250 		errno = ENOTSUP;
2251 
2252 	return (NULL);
2253 }
2254 
2255 /*
2256  * PROM property access
2257  */
2258 
2259 /*
2260  * openprom driver stuff:
2261  *	The maximum property length depends on the buffer size. We use
2262  *	OPROMMAXPARAM defined in <sys/openpromio.h>
2263  *
2264  *	MAXNAMESZ is max property name. obpdefs.h defines it as 32 based on 1275
2265  *	MAXVALSZ is maximum value size, which is whatever space left in buf
2266  */
2267 
2268 #define	OBP_MAXBUF	OPROMMAXPARAM - sizeof (int)
2269 #define	OBP_MAXPROPLEN	OBP_MAXBUF - OBP_MAXPROPNAME;
2270 
2271 struct di_prom_prop {
2272 	char *name;
2273 	int len;
2274 	uchar_t *data;
2275 	struct di_prom_prop *next;	/* form a linked list */
2276 };
2277 
2278 struct di_prom_handle { /* handle to prom */
2279 	mutex_t lock;	/* synchronize access to openprom fd */
2280 	int	fd;	/* /dev/openprom file descriptor */
2281 	struct di_prom_prop *list;	/* linked list of prop */
2282 	union {
2283 		char buf[OPROMMAXPARAM];
2284 		struct openpromio opp;
2285 	} oppbuf;
2286 };
2287 
2288 di_prom_handle_t
2289 di_prom_init()
2290 {
2291 	struct di_prom_handle *p;
2292 
2293 	if ((p = malloc(sizeof (struct di_prom_handle))) == NULL)
2294 		return (DI_PROM_HANDLE_NIL);
2295 
2296 	DPRINTF((DI_INFO, "di_prom_init: get prom handle 0x%p\n", p));
2297 
2298 	(void) mutex_init(&p->lock, USYNC_THREAD, NULL);
2299 	if ((p->fd = open("/dev/openprom", O_RDONLY)) < 0) {
2300 		free(p);
2301 		return (DI_PROM_HANDLE_NIL);
2302 	}
2303 	p->list = NULL;
2304 
2305 	return ((di_prom_handle_t)p);
2306 }
2307 
2308 static void
2309 di_prom_prop_free(struct di_prom_prop *list)
2310 {
2311 	struct di_prom_prop *tmp = list;
2312 
2313 	while (tmp != NULL) {
2314 		list = tmp->next;
2315 		if (tmp->name != NULL) {
2316 			free(tmp->name);
2317 		}
2318 		if (tmp->data != NULL) {
2319 			free(tmp->data);
2320 		}
2321 		free(tmp);
2322 		tmp = list;
2323 	}
2324 }
2325 
2326 void
2327 di_prom_fini(di_prom_handle_t ph)
2328 {
2329 	struct di_prom_handle *p = (struct di_prom_handle *)ph;
2330 
2331 	DPRINTF((DI_INFO, "di_prom_fini: free prom handle 0x%p\n", p));
2332 
2333 	(void) close(p->fd);
2334 	(void) mutex_destroy(&p->lock);
2335 	di_prom_prop_free(p->list);
2336 
2337 	free(p);
2338 }
2339 
2340 /*
2341  * Internal library interface for locating the property
2342  * XXX: ph->lock must be held for the duration of call.
2343  */
2344 static di_prom_prop_t
2345 di_prom_prop_found(di_prom_handle_t ph, int nodeid,
2346 	di_prom_prop_t prom_prop)
2347 {
2348 	struct di_prom_handle *p = (struct di_prom_handle *)ph;
2349 	struct openpromio *opp = &p->oppbuf.opp;
2350 	int *ip = (int *)((void *)opp->oprom_array);
2351 	struct di_prom_prop *prop = (struct di_prom_prop *)prom_prop;
2352 
2353 	DPRINTF((DI_TRACE1, "Looking for nodeid 0x%x\n", nodeid));
2354 
2355 	/*
2356 	 * Set "current" nodeid in the openprom driver
2357 	 */
2358 	opp->oprom_size = sizeof (int);
2359 	*ip = nodeid;
2360 	if (ioctl(p->fd, OPROMSETNODEID, opp) < 0) {
2361 		DPRINTF((DI_ERR, "*** Nodeid not found 0x%x\n", nodeid));
2362 		return (DI_PROM_PROP_NIL);
2363 	}
2364 
2365 	DPRINTF((DI_TRACE, "Found nodeid 0x%x\n", nodeid));
2366 
2367 	bzero(opp, OBP_MAXBUF);
2368 	opp->oprom_size = OBP_MAXPROPNAME;
2369 	if (prom_prop != DI_PROM_PROP_NIL)
2370 		(void) strcpy(opp->oprom_array, prop->name);
2371 
2372 	if ((ioctl(p->fd, OPROMNXTPROP, opp) < 0) || (opp->oprom_size == 0))
2373 		return (DI_PROM_PROP_NIL);
2374 
2375 	/*
2376 	 * Prom property found. Allocate struct for storing prop
2377 	 *   (reuse variable prop)
2378 	 */
2379 	if ((prop = malloc(sizeof (struct di_prom_prop))) == NULL)
2380 		return (DI_PROM_PROP_NIL);
2381 
2382 	/*
2383 	 * Get a copy of property name
2384 	 */
2385 	if ((prop->name = strdup(opp->oprom_array)) == NULL) {
2386 		free(prop);
2387 		return (DI_PROM_PROP_NIL);
2388 	}
2389 
2390 	/*
2391 	 * get property value and length
2392 	 */
2393 	opp->oprom_size = OBP_MAXPROPLEN;
2394 
2395 	if ((ioctl(p->fd, OPROMGETPROP, opp) < 0) ||
2396 	    (opp->oprom_size == (uint_t)-1)) {
2397 		free(prop->name);
2398 		free(prop);
2399 		return (DI_PROM_PROP_NIL);
2400 	}
2401 
2402 	/*
2403 	 * make a copy of the property value
2404 	 */
2405 	prop->len = opp->oprom_size;
2406 
2407 	if (prop->len == 0)
2408 		prop->data = NULL;
2409 	else if ((prop->data = malloc(prop->len)) == NULL) {
2410 		free(prop->name);
2411 		free(prop);
2412 		return (DI_PROM_PROP_NIL);
2413 	}
2414 
2415 	bcopy(opp->oprom_array, prop->data, prop->len);
2416 
2417 	/*
2418 	 * Prepend prop to list in prom handle
2419 	 */
2420 	prop->next = p->list;
2421 	p->list = prop;
2422 
2423 	return ((di_prom_prop_t)prop);
2424 }
2425 
2426 di_prom_prop_t
2427 di_prom_prop_next(di_prom_handle_t ph, di_node_t node, di_prom_prop_t prom_prop)
2428 {
2429 	struct di_prom_handle *p = (struct di_prom_handle *)ph;
2430 
2431 	DPRINTF((DI_TRACE1, "Search next prop for node 0x%p with ph 0x%p\n",
2432 		node, p));
2433 
2434 	/*
2435 	 * paranoid check
2436 	 */
2437 	if ((ph == DI_PROM_HANDLE_NIL) || (node == DI_NODE_NIL)) {
2438 		errno = EINVAL;
2439 		return (DI_PROM_PROP_NIL);
2440 	}
2441 
2442 	if (di_nodeid(node) != DI_PROM_NODEID) {
2443 		errno = ENXIO;
2444 		return (DI_PROM_PROP_NIL);
2445 	}
2446 
2447 	/*
2448 	 * synchronize access to prom file descriptor
2449 	 */
2450 	(void) mutex_lock(&p->lock);
2451 
2452 	/*
2453 	 * look for next property
2454 	 */
2455 	prom_prop = di_prom_prop_found(ph, DI_NODE(node)->nodeid, prom_prop);
2456 
2457 	(void) mutex_unlock(&p->lock);
2458 
2459 	return (prom_prop);
2460 }
2461 
2462 char *
2463 di_prom_prop_name(di_prom_prop_t prom_prop)
2464 {
2465 	/*
2466 	 * paranoid check
2467 	 */
2468 	if (prom_prop == DI_PROM_PROP_NIL) {
2469 		errno = EINVAL;
2470 		return (NULL);
2471 	}
2472 
2473 	return (((struct di_prom_prop *)prom_prop)->name);
2474 }
2475 
2476 int
2477 di_prom_prop_data(di_prom_prop_t prom_prop, uchar_t **prom_prop_data)
2478 {
2479 	/*
2480 	 * paranoid check
2481 	 */
2482 	if (prom_prop == DI_PROM_PROP_NIL) {
2483 		errno = EINVAL;
2484 		return (NULL);
2485 	}
2486 
2487 	*prom_prop_data = ((struct di_prom_prop *)prom_prop)->data;
2488 
2489 	return (((struct di_prom_prop *)prom_prop)->len);
2490 }
2491 
2492 /*
2493  * Internal library interface for locating the property
2494  *    Returns length if found, -1 if prop doesn't exist.
2495  */
2496 static struct di_prom_prop *
2497 di_prom_prop_lookup_common(di_prom_handle_t ph, di_node_t node,
2498 	const char *prom_prop_name)
2499 {
2500 	struct openpromio *opp;
2501 	struct di_prom_prop *prop;
2502 	struct di_prom_handle *p = (struct di_prom_handle *)ph;
2503 
2504 	/*
2505 	 * paranoid check
2506 	 */
2507 	if ((ph == DI_PROM_HANDLE_NIL) || (node == DI_NODE_NIL)) {
2508 		errno = EINVAL;
2509 		return (NULL);
2510 	}
2511 
2512 	if (di_nodeid(node) != DI_PROM_NODEID) {
2513 		errno = ENXIO;
2514 		return (NULL);
2515 	}
2516 
2517 	opp = &p->oppbuf.opp;
2518 
2519 	(void) mutex_lock(&p->lock);
2520 
2521 	opp->oprom_size = sizeof (int);
2522 	opp->oprom_node = DI_NODE(node)->nodeid;
2523 	if (ioctl(p->fd, OPROMSETNODEID, opp) < 0) {
2524 		errno = ENXIO;
2525 		DPRINTF((DI_ERR, "*** Nodeid not found 0x%x\n",
2526 		    DI_NODE(node)->nodeid));
2527 		(void) mutex_unlock(&p->lock);
2528 		return (NULL);
2529 	}
2530 
2531 	/*
2532 	 * get property length
2533 	 */
2534 	bzero(opp, OBP_MAXBUF);
2535 	opp->oprom_size = OBP_MAXPROPLEN;
2536 	(void) strcpy(opp->oprom_array, prom_prop_name);
2537 
2538 	if ((ioctl(p->fd, OPROMGETPROPLEN, opp) < 0) ||
2539 	    (opp->oprom_len == -1)) {
2540 		/* no such property */
2541 		(void) mutex_unlock(&p->lock);
2542 		return (NULL);
2543 	}
2544 
2545 	/*
2546 	 * Prom property found. Allocate struct for storing prop
2547 	 */
2548 	if ((prop = malloc(sizeof (struct di_prom_prop))) == NULL) {
2549 		(void) mutex_unlock(&p->lock);
2550 		return (NULL);
2551 	}
2552 	prop->name = NULL;	/* we don't need the name */
2553 	prop->len = opp->oprom_len;
2554 
2555 	if (prop->len == 0) {	/* boolean property */
2556 		prop->data = NULL;
2557 		prop->next = p->list;
2558 		p->list = prop;
2559 		(void) mutex_unlock(&p->lock);
2560 		return (prop);
2561 	}
2562 
2563 	/*
2564 	 * retrieve the property value
2565 	 */
2566 	bzero(opp, OBP_MAXBUF);
2567 	opp->oprom_size = OBP_MAXPROPLEN;
2568 	(void) strcpy(opp->oprom_array, prom_prop_name);
2569 
2570 	if ((ioctl(p->fd, OPROMGETPROP, opp) < 0) ||
2571 	    (opp->oprom_size == (uint_t)-1)) {
2572 		/* error retrieving property value */
2573 		(void) mutex_unlock(&p->lock);
2574 		free(prop);
2575 		return (NULL);
2576 	}
2577 
2578 	/*
2579 	 * make a copy of the property value, stick in ph->list
2580 	 */
2581 	if ((prop->data = malloc(prop->len)) == NULL) {
2582 		(void) mutex_unlock(&p->lock);
2583 		free(prop);
2584 		return (NULL);
2585 	}
2586 
2587 	bcopy(opp->oprom_array, prop->data, prop->len);
2588 
2589 	prop->next = p->list;
2590 	p->list = prop;
2591 	(void) mutex_unlock(&p->lock);
2592 
2593 	return (prop);
2594 }
2595 
2596 int
2597 di_prom_prop_lookup_ints(di_prom_handle_t ph, di_node_t node,
2598 	const char *prom_prop_name, int **prom_prop_data)
2599 {
2600 	int len;
2601 	struct di_prom_prop *prop;
2602 
2603 	prop = di_prom_prop_lookup_common(ph, node, prom_prop_name);
2604 
2605 	if (prop == NULL) {
2606 		*prom_prop_data = NULL;
2607 		return (-1);
2608 	}
2609 
2610 	if (prop->len == 0) {	/* boolean property */
2611 		*prom_prop_data = NULL;
2612 		return (0);
2613 	}
2614 
2615 	len = di_prop_decode_common((void *)&prop->data, prop->len,
2616 		DI_PROP_TYPE_INT, 1);
2617 	*prom_prop_data = (int *)((void *)prop->data);
2618 
2619 	return (len);
2620 }
2621 
2622 int
2623 di_prom_prop_lookup_strings(di_prom_handle_t ph, di_node_t node,
2624 	const char *prom_prop_name, char **prom_prop_data)
2625 {
2626 	int len;
2627 	struct di_prom_prop *prop;
2628 
2629 	prop = di_prom_prop_lookup_common(ph, node, prom_prop_name);
2630 
2631 	if (prop == NULL) {
2632 		*prom_prop_data = NULL;
2633 		return (-1);
2634 	}
2635 
2636 	if (prop->len == 0) {	/* boolean property */
2637 		*prom_prop_data = NULL;
2638 		return (0);
2639 	}
2640 
2641 	/*
2642 	 * Fix an openprom bug (OBP string not NULL terminated).
2643 	 * XXX This should really be fixed in promif.
2644 	 */
2645 	if (((char *)prop->data)[prop->len - 1] != '\0') {
2646 		uchar_t *tmp;
2647 		prop->len++;
2648 		if ((tmp = realloc(prop->data, prop->len)) == NULL)
2649 			return (-1);
2650 
2651 		prop->data = tmp;
2652 		((char *)prop->data)[prop->len - 1] = '\0';
2653 		DPRINTF((DI_INFO, "OBP string not NULL terminated: "
2654 		    "node=%s, prop=%s, val=%s\n",
2655 		    di_node_name(node), prom_prop_name, prop->data));
2656 	}
2657 
2658 	len = di_prop_decode_common((void *)&prop->data, prop->len,
2659 	    DI_PROP_TYPE_STRING, 1);
2660 	*prom_prop_data = (char *)prop->data;
2661 
2662 	return (len);
2663 }
2664 
2665 int
2666 di_prom_prop_lookup_bytes(di_prom_handle_t ph, di_node_t node,
2667 	const char *prom_prop_name, uchar_t **prom_prop_data)
2668 {
2669 	int len;
2670 	struct di_prom_prop *prop;
2671 
2672 	prop = di_prom_prop_lookup_common(ph, node, prom_prop_name);
2673 
2674 	if (prop == NULL) {
2675 		*prom_prop_data = NULL;
2676 		return (-1);
2677 	}
2678 
2679 	if (prop->len == 0) {	/* boolean property */
2680 		*prom_prop_data = NULL;
2681 		return (0);
2682 	}
2683 
2684 	len = di_prop_decode_common((void *)&prop->data, prop->len,
2685 	    DI_PROP_TYPE_BYTE, 1);
2686 	*prom_prop_data = prop->data;
2687 
2688 	return (len);
2689 }
2690 
2691 di_lnode_t
2692 di_link_to_lnode(di_link_t link, uint_t endpoint)
2693 {
2694 	struct di_all *di_all;
2695 
2696 	if ((link == DI_LINK_NIL) ||
2697 	    ((endpoint != DI_LINK_SRC) && (endpoint != DI_LINK_TGT))) {
2698 		errno = EINVAL;
2699 		return (DI_LNODE_NIL);
2700 	}
2701 
2702 	di_all = DI_ALL((caddr_t)link - DI_LINK(link)->self);
2703 
2704 	if (endpoint == DI_LINK_SRC) {
2705 		return (DI_LNODE((caddr_t)di_all + DI_LINK(link)->src_lnode));
2706 	} else {
2707 		return (DI_LNODE((caddr_t)di_all + DI_LINK(link)->tgt_lnode));
2708 	}
2709 	/* NOTREACHED */
2710 }
2711 
2712 char *
2713 di_lnode_name(di_lnode_t lnode)
2714 {
2715 	return (di_driver_name(di_lnode_devinfo(lnode)));
2716 }
2717 
2718 di_node_t
2719 di_lnode_devinfo(di_lnode_t lnode)
2720 {
2721 	struct di_all *di_all;
2722 
2723 	di_all = DI_ALL((caddr_t)lnode - DI_LNODE(lnode)->self);
2724 	return (DI_NODE((caddr_t)di_all + DI_LNODE(lnode)->node));
2725 }
2726 
2727 int
2728 di_lnode_devt(di_lnode_t lnode, dev_t *devt)
2729 {
2730 	if ((lnode == DI_LNODE_NIL) || (devt == NULL)) {
2731 		errno = EINVAL;
2732 		return (-1);
2733 	}
2734 	if ((DI_LNODE(lnode)->dev_major == (major_t)-1) &&
2735 	    (DI_LNODE(lnode)->dev_minor == (minor_t)-1))
2736 		return (-1);
2737 
2738 	*devt = makedev(DI_LNODE(lnode)->dev_major, DI_LNODE(lnode)->dev_minor);
2739 	return (0);
2740 }
2741 
2742 int
2743 di_link_spectype(di_link_t link)
2744 {
2745 	return (DI_LINK(link)->spec_type);
2746 }
2747 
2748 void
2749 di_minor_private_set(di_minor_t minor, void *data)
2750 {
2751 	DI_MINOR(minor)->user_private_data = (uintptr_t)data;
2752 }
2753 
2754 void *
2755 di_minor_private_get(di_minor_t minor)
2756 {
2757 	return ((void *)(uintptr_t)DI_MINOR(minor)->user_private_data);
2758 }
2759 
2760 void
2761 di_node_private_set(di_node_t node, void *data)
2762 {
2763 	DI_NODE(node)->user_private_data = (uintptr_t)data;
2764 }
2765 
2766 void *
2767 di_node_private_get(di_node_t node)
2768 {
2769 	return ((void *)(uintptr_t)DI_NODE(node)->user_private_data);
2770 }
2771 
2772 void
2773 di_lnode_private_set(di_lnode_t lnode, void *data)
2774 {
2775 	DI_LNODE(lnode)->user_private_data = (uintptr_t)data;
2776 }
2777 
2778 void *
2779 di_lnode_private_get(di_lnode_t lnode)
2780 {
2781 	return ((void *)(uintptr_t)DI_LNODE(lnode)->user_private_data);
2782 }
2783 
2784 void
2785 di_link_private_set(di_link_t link, void *data)
2786 {
2787 	DI_LINK(link)->user_private_data = (uintptr_t)data;
2788 }
2789 
2790 void *
2791 di_link_private_get(di_link_t link)
2792 {
2793 	return ((void *)(uintptr_t)DI_LINK(link)->user_private_data);
2794 }
2795 
2796 di_lnode_t
2797 di_lnode_next(di_node_t node, di_lnode_t lnode)
2798 {
2799 	struct di_all *di_all;
2800 
2801 	/*
2802 	 * paranoid error checking
2803 	 */
2804 	if (node == DI_NODE_NIL) {
2805 		errno = EINVAL;
2806 		return (DI_LNODE_NIL);
2807 	}
2808 
2809 	di_all = DI_ALL((caddr_t)node - DI_NODE(node)->self);
2810 
2811 	if (lnode == DI_NODE_NIL) {
2812 		if (DI_NODE(node)->lnodes != NULL)
2813 			return (DI_LNODE((caddr_t)di_all +
2814 			    DI_NODE(node)->lnodes));
2815 	} else {
2816 		if (DI_LNODE(lnode)->node_next != NULL)
2817 			return (DI_LNODE((caddr_t)di_all +
2818 			    DI_LNODE(lnode)->node_next));
2819 	}
2820 
2821 	if (DINFOLYR & DI_ALL(di_all)->command)
2822 		errno = ENXIO;
2823 	else
2824 		errno = ENOTSUP;
2825 
2826 	return (DI_LNODE_NIL);
2827 }
2828 
2829 di_link_t
2830 di_link_next_by_node(di_node_t node, di_link_t link, uint_t endpoint)
2831 {
2832 	struct di_all *di_all;
2833 
2834 	/*
2835 	 * paranoid error checking
2836 	 */
2837 	if ((node == DI_NODE_NIL) ||
2838 	    ((endpoint != DI_LINK_SRC) && (endpoint != DI_LINK_TGT))) {
2839 		errno = EINVAL;
2840 		return (DI_LINK_NIL);
2841 	}
2842 
2843 	di_all = DI_ALL((caddr_t)node - DI_NODE(node)->self);
2844 
2845 	if (endpoint == DI_LINK_SRC) {
2846 		if (link == DI_LINK_NIL) {
2847 			if (DI_NODE(node)->src_links != NULL)
2848 				return (DI_LINK((caddr_t)di_all +
2849 				    DI_NODE(node)->src_links));
2850 		} else {
2851 			if (DI_LINK(link)->src_node_next != NULL)
2852 				return (DI_LINK((caddr_t)di_all +
2853 				    DI_LINK(link)->src_node_next));
2854 		}
2855 	} else {
2856 		if (link == DI_LINK_NIL) {
2857 			if (DI_NODE(node)->tgt_links != NULL)
2858 				return (DI_LINK((caddr_t)di_all +
2859 				    DI_NODE(node)->tgt_links));
2860 		} else {
2861 			if (DI_LINK(link)->tgt_node_next != NULL)
2862 				return (DI_LINK((caddr_t)di_all +
2863 				    DI_LINK(link)->tgt_node_next));
2864 		}
2865 	}
2866 
2867 	if (DINFOLYR & DI_ALL(di_all)->command)
2868 		errno = ENXIO;
2869 	else
2870 		errno = ENOTSUP;
2871 
2872 	return (DI_LINK_NIL);
2873 }
2874 
2875 di_link_t
2876 di_link_next_by_lnode(di_lnode_t lnode, di_link_t link, uint_t endpoint)
2877 {
2878 	struct di_all *di_all;
2879 
2880 	/*
2881 	 * paranoid error checking
2882 	 */
2883 	if ((lnode == DI_LNODE_NIL) ||
2884 	    ((endpoint != DI_LINK_SRC) && (endpoint != DI_LINK_TGT))) {
2885 		errno = EINVAL;
2886 		return (DI_LINK_NIL);
2887 	}
2888 
2889 	di_all = DI_ALL((caddr_t)lnode - DI_LNODE(lnode)->self);
2890 
2891 	if (endpoint == DI_LINK_SRC) {
2892 		if (link == DI_LINK_NIL) {
2893 			if (DI_LNODE(lnode)->link_out == NULL)
2894 				return (DI_LINK_NIL);
2895 			return (DI_LINK((caddr_t)di_all +
2896 				    DI_LNODE(lnode)->link_out));
2897 		} else {
2898 			if (DI_LINK(link)->src_link_next == NULL)
2899 				return (DI_LINK_NIL);
2900 			return (DI_LINK((caddr_t)di_all +
2901 				    DI_LINK(link)->src_link_next));
2902 		}
2903 	} else {
2904 		if (link == DI_LINK_NIL) {
2905 			if (DI_LNODE(lnode)->link_in == NULL)
2906 				return (DI_LINK_NIL);
2907 			return (DI_LINK((caddr_t)di_all +
2908 				    DI_LNODE(lnode)->link_in));
2909 		} else {
2910 			if (DI_LINK(link)->tgt_link_next == NULL)
2911 				return (DI_LINK_NIL);
2912 			return (DI_LINK((caddr_t)di_all +
2913 				    DI_LINK(link)->tgt_link_next));
2914 		}
2915 	}
2916 	/* NOTREACHED */
2917 }
2918 
2919 /*
2920  * Internal library function:
2921  *   Invoke callback for each link data on the link list of first node
2922  *   on node_list headp, and place children of first node on the list.
2923  *
2924  *   This is similar to walk_one_node, except we only walk in child
2925  *   first mode.
2926  */
2927 static void
2928 walk_one_link(struct node_list **headp, uint_t ep,
2929     void *arg, int (*callback)(di_link_t link, void *arg))
2930 {
2931 	int		action = DI_WALK_CONTINUE;
2932 	di_link_t	link = DI_LINK_NIL;
2933 	di_node_t	node = (*headp)->node;
2934 
2935 	while ((link = di_link_next_by_node(node, link, ep)) != DI_LINK_NIL) {
2936 		action = callback(link, arg);
2937 		if (action == DI_WALK_TERMINATE) {
2938 			break;
2939 		}
2940 	}
2941 
2942 	update_node_list(action, DI_WALK_LINKGEN, headp);
2943 }
2944 
2945 int
2946 di_walk_link(di_node_t root, uint_t flag, uint_t endpoint, void *arg,
2947     int (*link_callback)(di_link_t link, void *arg))
2948 {
2949 	struct node_list  *head;	/* node_list for tree walk */
2950 
2951 #ifdef DEBUG
2952 	char *path = di_devfs_path(root);
2953 	DPRINTF((DI_INFO, "walking %s link data under %s\n",
2954 		    (endpoint == DI_LINK_SRC) ? "src" : "tgt", path));
2955 	di_devfs_path_free(path);
2956 #endif
2957 
2958 	/*
2959 	 * paranoid error checking
2960 	 */
2961 	if ((root == DI_NODE_NIL) || (link_callback == NULL) || (flag != 0) ||
2962 	    ((endpoint != DI_LINK_SRC) && (endpoint != DI_LINK_TGT))) {
2963 		errno = EINVAL;
2964 		return (-1);
2965 	}
2966 
2967 	if ((head = malloc(sizeof (struct node_list))) == NULL) {
2968 		DPRINTF((DI_ERR, "malloc of node_list failed\n"));
2969 		return (-1);
2970 	}
2971 
2972 	head->next = NULL;
2973 	head->node = root;
2974 
2975 	DPRINTF((DI_INFO, "Start link data walking from node %s\n",
2976 		di_node_name(root)));
2977 
2978 	while (head != NULL)
2979 		walk_one_link(&head, endpoint, arg, link_callback);
2980 
2981 	return (0);
2982 }
2983 
2984 /*
2985  * Internal library function:
2986  *   Invoke callback for each link data on the link list of first node
2987  *   on node_list headp, and place children of first node on the list.
2988  *
2989  *   This is similar to walk_one_node, except we only walk in child
2990  *   first mode.
2991  */
2992 static void
2993 walk_one_lnode(struct node_list **headp, void *arg,
2994     int (*callback)(di_lnode_t lnode, void *arg))
2995 {
2996 	int		action = DI_WALK_CONTINUE;
2997 	di_lnode_t	lnode = DI_LNODE_NIL;
2998 	di_node_t	node = (*headp)->node;
2999 
3000 	while ((lnode = di_lnode_next(node, lnode)) != DI_LNODE_NIL) {
3001 		action = callback(lnode, arg);
3002 		if (action == DI_WALK_TERMINATE) {
3003 			break;
3004 		}
3005 	}
3006 
3007 	update_node_list(action, DI_WALK_LINKGEN, headp);
3008 }
3009 
3010 int
3011 di_walk_lnode(di_node_t root, uint_t flag, void *arg,
3012     int (*lnode_callback)(di_lnode_t lnode, void *arg))
3013 {
3014 	struct node_list  *head;	/* node_list for tree walk */
3015 
3016 #ifdef DEBUG
3017 	char *path = di_devfs_path(root);
3018 	DPRINTF((DI_INFO, "walking lnode data under %s\n", path));
3019 	di_devfs_path_free(path);
3020 #endif
3021 
3022 	/*
3023 	 * paranoid error checking
3024 	 */
3025 	if ((root == DI_NODE_NIL) || (lnode_callback == NULL) || (flag != 0)) {
3026 		errno = EINVAL;
3027 		return (-1);
3028 	}
3029 
3030 	if ((head = malloc(sizeof (struct node_list))) == NULL) {
3031 		DPRINTF((DI_ERR, "malloc of node_list failed\n"));
3032 		return (-1);
3033 	}
3034 
3035 	head->next = NULL;
3036 	head->node = root;
3037 
3038 	DPRINTF((DI_INFO, "Start lnode data walking from node %s\n",
3039 		di_node_name(root)));
3040 
3041 	while (head != NULL)
3042 		walk_one_lnode(&head, arg, lnode_callback);
3043 
3044 	return (0);
3045 }
3046 
3047 di_node_t
3048 di_lookup_node(di_node_t root, char *path)
3049 {
3050 	struct di_all *dap;
3051 	di_node_t node;
3052 	char copy[MAXPATHLEN];
3053 	char *slash, *pname, *paddr;
3054 
3055 	/*
3056 	 * Path must be absolute and musn't have duplicate slashes
3057 	 */
3058 	if (*path != '/' || strstr(path, "//")) {
3059 		DPRINTF((DI_ERR, "Invalid path: %s\n", path));
3060 		return (DI_NODE_NIL);
3061 	}
3062 
3063 	if (root == DI_NODE_NIL) {
3064 		DPRINTF((DI_ERR, "root node is DI_NODE_NIL\n"));
3065 		return (DI_NODE_NIL);
3066 	}
3067 
3068 	dap = DI_ALL((caddr_t)root - DI_NODE(root)->self);
3069 	if (strcmp(dap->root_path, "/") != 0) {
3070 		DPRINTF((DI_ERR, "snapshot root not / : %s\n", dap->root_path));
3071 		return (DI_NODE_NIL);
3072 	}
3073 
3074 	if (strlcpy(copy, path, sizeof (copy)) >= sizeof (copy)) {
3075 		DPRINTF((DI_ERR, "path too long: %s\n", path));
3076 		return (DI_NODE_NIL);
3077 	}
3078 
3079 	for (slash = copy, node = root; slash; ) {
3080 
3081 		/*
3082 		 * Handle path = "/" case as well as trailing '/'
3083 		 */
3084 		if (*(slash + 1) == '\0')
3085 			break;
3086 
3087 		/*
3088 		 * More path-components exist. Deal with the next one
3089 		 */
3090 		pname = slash + 1;
3091 		node = di_child_node(node);
3092 
3093 		if (slash = strchr(pname, '/'))
3094 			*slash = '\0';
3095 		if (paddr = strchr(pname, '@'))
3096 			*paddr++ = '\0';
3097 
3098 		for (; node != DI_NODE_NIL; node = di_sibling_node(node)) {
3099 			char *name, *baddr;
3100 
3101 			name = di_node_name(node);
3102 			baddr = di_bus_addr(node);
3103 
3104 			if (strcmp(pname, name) != 0)
3105 				continue;
3106 
3107 			/*
3108 			 * Mappings between a "path-address" and bus-addr
3109 			 *
3110 			 *	paddr		baddr
3111 			 *	---------------------
3112 			 *	NULL		NULL
3113 			 *	NULL		""
3114 			 *	""		N/A	(invalid paddr)
3115 			 */
3116 			if (paddr && baddr && strcmp(paddr, baddr) == 0)
3117 				break;
3118 			if (paddr == NULL && (baddr == NULL || *baddr == '\0'))
3119 				break;
3120 		}
3121 
3122 		/*
3123 		 * No nodes in the sibling list or there was no match
3124 		 */
3125 		if (node == DI_NODE_NIL) {
3126 			DPRINTF((DI_ERR, "%s@%s: no node\n", pname, paddr));
3127 			return (DI_NODE_NIL);
3128 		}
3129 	}
3130 
3131 	assert(node != DI_NODE_NIL);
3132 	return (node);
3133 }
3134 
3135 static char *
3136 msglevel2str(di_debug_t msglevel)
3137 {
3138 	switch (msglevel) {
3139 		case DI_ERR:
3140 			return ("ERROR");
3141 		case DI_INFO:
3142 			return ("Info");
3143 		case DI_TRACE:
3144 			return ("Trace");
3145 		case DI_TRACE1:
3146 			return ("Trace1");
3147 		case DI_TRACE2:
3148 			return ("Trace2");
3149 		default:
3150 			return ("UNKNOWN");
3151 	}
3152 }
3153 
3154 void
3155 dprint(di_debug_t msglevel, const char *fmt, ...)
3156 {
3157 	va_list	ap;
3158 	char	*estr;
3159 
3160 	if (di_debug <= DI_QUIET)
3161 		return;
3162 
3163 	if (di_debug < msglevel)
3164 		return;
3165 
3166 	estr = msglevel2str(msglevel);
3167 
3168 	assert(estr);
3169 
3170 	va_start(ap, fmt);
3171 
3172 	(void) fprintf(stderr, "libdevinfo[%lu]: %s: ",
3173 	    (ulong_t)getpid(), estr);
3174 	(void) vfprintf(stderr, fmt, ap);
3175 
3176 	va_end(ap);
3177 }
3178 
3179 /* end of devinfo.c */
3180