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