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