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