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