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