xref: /illumos-gate/usr/src/lib/libdevinfo/devinfo.c (revision 25c6ff4b77fcddf4097ce78a8277275ca603b46c)
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 2008 Sun Microsystems, Inc.  All rights reserved.
23  * Use is subject to license terms.
24  */
25 
26 #pragma ident	"%Z%%M%	%I%	%E% SMI"
27 
28 /*
29  * Interfaces for getting device configuration data from kernel
30  * through the devinfo driver.
31  */
32 
33 #include <stdio.h>
34 #include <stdlib.h>
35 #include <string.h>
36 #include <strings.h>
37 #include <stropts.h>
38 #include <fcntl.h>
39 #include <poll.h>
40 #include <synch.h>
41 #include <unistd.h>
42 #include <sys/mkdev.h>
43 #include <sys/obpdefs.h>
44 #include <sys/stat.h>
45 #include <sys/types.h>
46 #include <sys/time.h>
47 #include <sys/autoconf.h>
48 #include <stdarg.h>
49 
50 #define	NDEBUG 1
51 #include <assert.h>
52 
53 #include "libdevinfo.h"
54 
55 /*
56  * Debug message levels
57  */
58 typedef enum {
59 	DI_QUIET = 0,	/* No debug messages - the default */
60 	DI_ERR = 1,
61 	DI_INFO,
62 	DI_TRACE,
63 	DI_TRACE1,
64 	DI_TRACE2
65 } di_debug_t;
66 
67 int di_debug = DI_QUIET;
68 
69 #define	DPRINTF(args)	{ if (di_debug != DI_QUIET) dprint args; }
70 
71 void dprint(di_debug_t msglevel, const char *fmt, ...);
72 
73 
74 #pragma init(_libdevinfo_init)
75 
76 void
77 _libdevinfo_init()
78 {
79 	char	*debug_str = getenv("_LIBDEVINFO_DEBUG");
80 
81 	if (debug_str) {
82 		errno = 0;
83 		di_debug = atoi(debug_str);
84 		if (errno || di_debug < DI_QUIET)
85 			di_debug = DI_QUIET;
86 	}
87 }
88 
89 di_node_t
90 di_init(const char *phys_path, uint_t flag)
91 {
92 	return (di_init_impl(phys_path, flag, NULL));
93 }
94 
95 /*
96  * We use blocking_open() to guarantee access to the devinfo device, if open()
97  * is failing with EAGAIN.
98  */
99 static int
100 blocking_open(const char *path, int oflag)
101 {
102 	int fd;
103 
104 	while ((fd = open(path, oflag)) == -1 && errno == EAGAIN)
105 		(void) poll(NULL, 0, 1 * MILLISEC);
106 
107 	return (fd);
108 }
109 
110 /* private interface */
111 di_node_t
112 di_init_driver(const char *drv_name, uint_t flag)
113 {
114 	int fd;
115 	char driver[MAXPATHLEN];
116 
117 	/*
118 	 * Don't allow drv_name to exceed MAXPATHLEN - 1, or 1023,
119 	 * which should be sufficient for any sensible programmer.
120 	 */
121 	if ((drv_name == NULL) || (strlen(drv_name) >= MAXPATHLEN)) {
122 		errno = EINVAL;
123 		return (DI_NODE_NIL);
124 	}
125 	(void) strcpy(driver, drv_name);
126 
127 	/*
128 	 * open the devinfo driver
129 	 */
130 	if ((fd = blocking_open("/devices/pseudo/devinfo@0:devinfo",
131 	    O_RDONLY)) == -1) {
132 		DPRINTF((DI_ERR, "devinfo open failed: errno = %d\n", errno));
133 		return (DI_NODE_NIL);
134 	}
135 
136 	if (ioctl(fd, DINFOLODRV, driver) != 0) {
137 		DPRINTF((DI_ERR, "failed to load driver %s\n", driver));
138 		(void) close(fd);
139 		errno = ENXIO;
140 		return (DI_NODE_NIL);
141 	}
142 	(void) close(fd);
143 
144 	/*
145 	 * Driver load succeeded, return a snapshot
146 	 */
147 	return (di_init("/", flag));
148 }
149 
150 di_node_t
151 di_init_impl(const char *phys_path, uint_t flag,
152 	struct di_priv_data *priv)
153 {
154 	caddr_t pa;
155 	int fd, map_size;
156 	struct di_all *dap;
157 	struct dinfo_io dinfo_io;
158 
159 	uint_t pageoffset = sysconf(_SC_PAGESIZE) - 1;
160 	uint_t pagemask = ~pageoffset;
161 
162 	DPRINTF((DI_INFO, "di_init: taking a snapshot\n"));
163 
164 	/*
165 	 * Make sure there is no minor name in the path
166 	 * and the path do not start with /devices....
167 	 */
168 	if (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 == NULL) {
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_OFFLINE;
981 	if (DI_NODE(node)->state & DEVI_DEVICE_DEGRADED)
982 		result |= DI_DEVICE_DEGRADED;
983 	if (DI_NODE(node)->state & DEVI_BUS_QUIESCED)
984 		result |= DI_BUS_QUIESCED;
985 	if (DI_NODE(node)->state & DEVI_BUS_DOWN)
986 		result |= DI_BUS_DOWN;
987 
988 	return (result);
989 }
990 
991 ddi_node_state_t
992 di_node_state(di_node_t node)
993 {
994 	return (DI_NODE(node)->node_state);
995 }
996 
997 uint_t
998 di_flags(di_node_t node)
999 {
1000 	return (DI_NODE(node)->flags);
1001 }
1002 
1003 uint_t
1004 di_retired(di_node_t node)
1005 {
1006 	return (di_flags(node) & DEVI_RETIRED);
1007 }
1008 
1009 ddi_devid_t
1010 di_devid(di_node_t node)
1011 {
1012 	if (DI_NODE(node)->devid == 0)
1013 		return (NULL);
1014 
1015 	return ((ddi_devid_t)((caddr_t)node +
1016 	    DI_NODE(node)->devid - DI_NODE(node)->self));
1017 }
1018 
1019 int
1020 di_driver_major(di_node_t node)
1021 {
1022 	int major;
1023 
1024 	major = DI_NODE(node)->drv_major;
1025 	if (major < 0)
1026 		return (-1);
1027 	return (major);
1028 }
1029 
1030 char *
1031 di_driver_name(di_node_t node)
1032 {
1033 	int major;
1034 	caddr_t pa;
1035 	struct di_devnm *devnm;
1036 
1037 	major = DI_NODE(node)->drv_major;
1038 	if (major < 0)
1039 		return (NULL);
1040 
1041 	pa = (caddr_t)node - DI_NODE(node)->self;
1042 	devnm = DI_DEVNM(pa + DI_ALL(pa)->devnames);
1043 
1044 	if (devnm[major].name)
1045 		return (pa + devnm[major].name);
1046 	else
1047 		return (NULL);
1048 }
1049 
1050 uint_t
1051 di_driver_ops(di_node_t node)
1052 {
1053 	int major;
1054 	caddr_t pa;
1055 	struct di_devnm *devnm;
1056 
1057 	major = DI_NODE(node)->drv_major;
1058 	if (major < 0)
1059 		return (0);
1060 
1061 	pa = (caddr_t)node - DI_NODE(node)->self;
1062 	devnm = DI_DEVNM(pa + DI_ALL(pa)->devnames);
1063 
1064 	return (devnm[major].ops);
1065 }
1066 
1067 /*
1068  * returns the length of the path, caller must free memory
1069  */
1070 char *
1071 di_devfs_path(di_node_t node)
1072 {
1073 	caddr_t pa;
1074 	di_node_t parent;
1075 	int depth = 0, len = 0;
1076 	char *buf, *name[MAX_TREE_DEPTH], *addr[MAX_TREE_DEPTH];
1077 
1078 	if (node == DI_NODE_NIL) {
1079 		errno = EINVAL;
1080 		return (NULL);
1081 	}
1082 
1083 	/*
1084 	 * trace back to root, note the node_name & address
1085 	 */
1086 	while ((parent = di_parent_node(node)) != DI_NODE_NIL) {
1087 		name[depth] = di_node_name(node);
1088 		len += strlen(name[depth]) + 1;		/* 1 for '/' */
1089 
1090 		if ((addr[depth] = di_bus_addr(node)) != NULL)
1091 			len += strlen(addr[depth]) + 1;	/* 1 for '@' */
1092 
1093 		node = parent;
1094 		depth++;
1095 	}
1096 
1097 	/*
1098 	 * get the path to the root of snapshot
1099 	 */
1100 	pa = (caddr_t)node - DI_NODE(node)->self;
1101 	name[depth] = DI_ALL(pa)->root_path;
1102 	len += strlen(name[depth]) + 1;
1103 
1104 	/*
1105 	 * allocate buffer and assemble path
1106 	 */
1107 	if ((buf = malloc(len)) == NULL) {
1108 		return (NULL);
1109 	}
1110 
1111 	(void) strcpy(buf, name[depth]);
1112 	len = strlen(buf);
1113 	if (buf[len - 1] == '/')
1114 		len--;	/* delete trailing '/' */
1115 
1116 	while (depth) {
1117 		depth--;
1118 		buf[len] = '/';
1119 		(void) strcpy(buf + len + 1, name[depth]);
1120 		len += strlen(name[depth]) + 1;
1121 		if (addr[depth] && addr[depth][0] != '\0') {
1122 			buf[len] = '@';
1123 			(void) strcpy(buf + len + 1, addr[depth]);
1124 			len += strlen(addr[depth]) + 1;
1125 		}
1126 	}
1127 
1128 	return (buf);
1129 }
1130 
1131 char *
1132 di_devfs_minor_path(di_minor_t minor)
1133 {
1134 	di_node_t	node;
1135 	char		*full_path, *name, *devfspath;
1136 	int		full_path_len;
1137 
1138 	if (minor == DI_MINOR_NIL) {
1139 		errno = EINVAL;
1140 		return (NULL);
1141 	}
1142 
1143 	name = di_minor_name(minor);
1144 	node = di_minor_devinfo(minor);
1145 	devfspath = di_devfs_path(node);
1146 	if (devfspath == NULL)
1147 		return (NULL);
1148 
1149 	/* make the full path to the device minor node */
1150 	full_path_len = strlen(devfspath) + strlen(name) + 2;
1151 	full_path = (char *)calloc(1, full_path_len);
1152 	if (full_path != NULL)
1153 		(void) snprintf(full_path, full_path_len, "%s:%s",
1154 		    devfspath, name);
1155 
1156 	di_devfs_path_free(devfspath);
1157 	return (full_path);
1158 }
1159 
1160 /*
1161  * Produce a string representation of path to di_path_t (pathinfo node). This
1162  * string is identical to di_devfs_path had the device been enumerated under
1163  * the pHCI: it has a base path to pHCI, then uses node_name of client, and
1164  * device unit-address of pathinfo node.
1165  */
1166 char *
1167 di_path_devfs_path(di_path_t path)
1168 {
1169 	di_node_t	phci_node;
1170 	char		*phci_path, *path_name, *path_addr;
1171 	char		*full_path;
1172 	int		full_path_len;
1173 
1174 	if (path == DI_PATH_NIL) {
1175 		errno = EINVAL;
1176 		return (NULL);
1177 	}
1178 
1179 	/* get name@addr for path */
1180 	path_name = di_path_node_name(path);
1181 	path_addr = di_path_bus_addr(path);
1182 	if ((path_name == NULL) || (path_addr == NULL))
1183 		return (NULL);
1184 
1185 	/* base path to pHCI devinfo node */
1186 	phci_node = di_path_phci_node(path);
1187 	if (phci_node == NULL)
1188 		return (NULL);
1189 	phci_path = di_devfs_path(phci_node);
1190 	if (phci_path == NULL)
1191 		return (NULL);
1192 
1193 	/* make the full string representation of path */
1194 	full_path_len = strlen(phci_path) + 1 + strlen(path_name) +
1195 	    1 + strlen(path_addr) + 1;
1196 	full_path = (char *)calloc(1, full_path_len);
1197 
1198 	if (full_path != NULL)
1199 		(void) snprintf(full_path, full_path_len, "%s/%s@%s",
1200 		    phci_path, path_name, path_addr);
1201 	di_devfs_path_free(phci_path);
1202 	return (full_path);
1203 }
1204 
1205 char *
1206 di_path_client_devfs_path(di_path_t path)
1207 {
1208 	return (di_devfs_path(di_path_client_node(path)));
1209 }
1210 
1211 void
1212 di_devfs_path_free(char *buf)
1213 {
1214 	if (buf == NULL) {
1215 		DPRINTF((DI_ERR, "di_devfs_path_free NULL arg!\n"));
1216 		return;
1217 	}
1218 
1219 	free(buf);
1220 }
1221 
1222 /*
1223  * Return 1 if name is a IEEE-1275 generic name. If new generic
1224  * names are defined, they should be added to this table
1225  */
1226 static int
1227 is_generic(const char *name, int len)
1228 {
1229 	const char	**gp;
1230 
1231 	/* from IEEE-1275 recommended practices section 3 */
1232 	static const char *generic_names[] = {
1233 	    "atm",
1234 	    "disk",
1235 	    "display",
1236 	    "dma-controller",
1237 	    "ethernet",
1238 	    "fcs",
1239 	    "fdc",
1240 	    "fddi",
1241 	    "fibre-channel",
1242 	    "ide",
1243 	    "interrupt-controller",
1244 	    "isa",
1245 	    "keyboard",
1246 	    "memory",
1247 	    "mouse",
1248 	    "nvram",
1249 	    "pc-card",
1250 	    "pci",
1251 	    "printer",
1252 	    "rtc",
1253 	    "sbus",
1254 	    "scanner",
1255 	    "scsi",
1256 	    "serial",
1257 	    "sound",
1258 	    "ssa",
1259 	    "tape",
1260 	    "timer",
1261 	    "token-ring",
1262 	    "vme",
1263 	    0
1264 	};
1265 
1266 	for (gp = generic_names; *gp; gp++) {
1267 		if ((strncmp(*gp, name, len) == 0) &&
1268 		    (strlen(*gp) == len))
1269 			return (1);
1270 	}
1271 	return (0);
1272 }
1273 
1274 /*
1275  * Determine if two paths below /devices refer to the same device, ignoring
1276  * any generic .vs. non-generic 'name' issues in "[[/]name[@addr[:minor]]]*".
1277  * Return 1 if the paths match.
1278  */
1279 int
1280 di_devfs_path_match(const char *dp1, const char *dp2)
1281 {
1282 	const char	*p1, *p2;
1283 	const char	*ec1, *ec2;
1284 	const char	*at1, *at2;
1285 	char		nc;
1286 	int		g1, g2;
1287 
1288 	/* progress through both strings */
1289 	for (p1 = dp1, p2 = dp2; (*p1 == *p2) && *p1; p1++, p2++) {
1290 		/* require match until the start of a component */
1291 		if (*p1 != '/')
1292 			continue;
1293 
1294 		/* advance p1 and p2 to start of 'name' in component */
1295 		nc = *(p1 + 1);
1296 		if ((nc == '\0') || (nc == '/'))
1297 			continue;		/* skip trash */
1298 		p1++;
1299 		p2++;
1300 
1301 		/*
1302 		 * Both p1 and p2 point to beginning of 'name' in component.
1303 		 * Determine where current component ends: next '/' or '\0'.
1304 		 */
1305 		ec1 = strchr(p1, '/');
1306 		if (ec1 == NULL)
1307 			ec1 = p1 + strlen(p1);
1308 		ec2 = strchr(p2, '/');
1309 		if (ec2 == NULL)
1310 			ec2 = p2 + strlen(p2);
1311 
1312 		/* Determine where name ends based on whether '@' exists */
1313 		at1 = strchr(p1, '@');
1314 		at2 = strchr(p2, '@');
1315 		if (at1 && (at1 < ec1))
1316 			ec1 = at1;
1317 		if (at2 && (at2 < ec2))
1318 			ec2 = at2;
1319 
1320 		/*
1321 		 * At this point p[12] point to beginning of name and
1322 		 * ec[12] point to character past the end of name. Determine
1323 		 * if the names are generic.
1324 		 */
1325 		g1 = is_generic(p1, ec1 - p1);
1326 		g2 = is_generic(p2, ec2 - p2);
1327 
1328 		if (g1 != g2) {
1329 			/*
1330 			 * one generic and one non-generic
1331 			 * skip past the names in the match.
1332 			 */
1333 			p1 = ec1;
1334 			p2 = ec2;
1335 		} else {
1336 			if (*p1 != *p2)
1337 				break;
1338 		}
1339 	}
1340 
1341 	return ((*p1 == *p2) ? 1 : 0);
1342 }
1343 
1344 /* minor data access */
1345 di_minor_t
1346 di_minor_next(di_node_t node, di_minor_t minor)
1347 {
1348 	caddr_t pa;
1349 
1350 	/*
1351 	 * paranoid error checking
1352 	 */
1353 	if (node == DI_NODE_NIL) {
1354 		errno = EINVAL;
1355 		return (DI_MINOR_NIL);
1356 	}
1357 
1358 	/*
1359 	 * minor is not NIL
1360 	 */
1361 	if (minor != DI_MINOR_NIL) {
1362 		if (DI_MINOR(minor)->next != 0)
1363 			return ((di_minor_t)((void *)((caddr_t)minor -
1364 			    DI_MINOR(minor)->self + DI_MINOR(minor)->next)));
1365 		else {
1366 			errno = ENXIO;
1367 			return (DI_MINOR_NIL);
1368 		}
1369 	}
1370 
1371 	/*
1372 	 * minor is NIL-->caller asks for first minor node
1373 	 */
1374 	if (DI_NODE(node)->minor_data != 0) {
1375 		return (DI_MINOR((caddr_t)node - DI_NODE(node)->self +
1376 		    DI_NODE(node)->minor_data));
1377 	}
1378 
1379 	/*
1380 	 * no minor data-->check if snapshot includes minor data
1381 	 *	in order to set the correct errno
1382 	 */
1383 	pa = (caddr_t)node - DI_NODE(node)->self;
1384 	if (DINFOMINOR & DI_ALL(pa)->command)
1385 		errno = ENXIO;
1386 	else
1387 		errno = ENOTSUP;
1388 
1389 	return (DI_MINOR_NIL);
1390 }
1391 
1392 /* private interface for dealing with alias minor link generation */
1393 di_node_t
1394 di_minor_devinfo(di_minor_t minor)
1395 {
1396 	if (minor == DI_MINOR_NIL) {
1397 		errno = EINVAL;
1398 		return (DI_NODE_NIL);
1399 	}
1400 
1401 	return (DI_NODE((caddr_t)minor - DI_MINOR(minor)->self +
1402 	    DI_MINOR(minor)->node));
1403 }
1404 
1405 ddi_minor_type
1406 di_minor_type(di_minor_t minor)
1407 {
1408 	return (DI_MINOR(minor)->type);
1409 }
1410 
1411 char *
1412 di_minor_name(di_minor_t minor)
1413 {
1414 	if (DI_MINOR(minor)->name == 0)
1415 		return (NULL);
1416 
1417 	return ((caddr_t)minor - DI_MINOR(minor)->self + DI_MINOR(minor)->name);
1418 }
1419 
1420 dev_t
1421 di_minor_devt(di_minor_t minor)
1422 {
1423 	return (makedev(DI_MINOR(minor)->dev_major,
1424 	    DI_MINOR(minor)->dev_minor));
1425 }
1426 
1427 int
1428 di_minor_spectype(di_minor_t minor)
1429 {
1430 	return (DI_MINOR(minor)->spec_type);
1431 }
1432 
1433 char *
1434 di_minor_nodetype(di_minor_t minor)
1435 {
1436 	if (DI_MINOR(minor)->node_type == 0)
1437 		return (NULL);
1438 
1439 	return ((caddr_t)minor -
1440 	    DI_MINOR(minor)->self + DI_MINOR(minor)->node_type);
1441 }
1442 
1443 /*
1444  * Single public interface for accessing software properties
1445  */
1446 di_prop_t
1447 di_prop_next(di_node_t node, di_prop_t prop)
1448 {
1449 	int list = DI_PROP_DRV_LIST;
1450 
1451 	/*
1452 	 * paranoid check
1453 	 */
1454 	if (node == DI_NODE_NIL) {
1455 		errno = EINVAL;
1456 		return (DI_PROP_NIL);
1457 	}
1458 
1459 	/*
1460 	 * Find which prop list we are at
1461 	 */
1462 	if (prop != DI_PROP_NIL)
1463 		list = DI_PROP(prop)->prop_list;
1464 
1465 	do {
1466 		switch (list++) {
1467 		case DI_PROP_DRV_LIST:
1468 			prop = di_prop_drv_next(node, prop);
1469 			break;
1470 		case DI_PROP_SYS_LIST:
1471 			prop = di_prop_sys_next(node, prop);
1472 			break;
1473 		case DI_PROP_GLB_LIST:
1474 			prop = di_prop_global_next(node, prop);
1475 			break;
1476 		case DI_PROP_HW_LIST:
1477 			prop = di_prop_hw_next(node, prop);
1478 			break;
1479 		default:	/* shouldn't happen */
1480 			errno = EFAULT;
1481 			return (DI_PROP_NIL);
1482 		}
1483 	} while ((prop == DI_PROP_NIL) && (list <= DI_PROP_HW_LIST));
1484 
1485 	return (prop);
1486 }
1487 
1488 dev_t
1489 di_prop_devt(di_prop_t prop)
1490 {
1491 	return (makedev(DI_PROP(prop)->dev_major, DI_PROP(prop)->dev_minor));
1492 }
1493 
1494 char *
1495 di_prop_name(di_prop_t prop)
1496 {
1497 	if (DI_PROP(prop)->prop_name == 0)
1498 		return (NULL);
1499 
1500 	return ((caddr_t)prop - DI_PROP(prop)->self + DI_PROP(prop)->prop_name);
1501 }
1502 
1503 int
1504 di_prop_type(di_prop_t prop)
1505 {
1506 	uint_t flags = DI_PROP(prop)->prop_flags;
1507 
1508 	if (flags & DDI_PROP_UNDEF_IT)
1509 		return (DI_PROP_TYPE_UNDEF_IT);
1510 
1511 	if (DI_PROP(prop)->prop_len == 0)
1512 		return (DI_PROP_TYPE_BOOLEAN);
1513 
1514 	if ((flags & DDI_PROP_TYPE_MASK) == DDI_PROP_TYPE_ANY)
1515 		return (DI_PROP_TYPE_UNKNOWN);
1516 
1517 	if (flags & DDI_PROP_TYPE_INT)
1518 		return (DI_PROP_TYPE_INT);
1519 
1520 	if (flags & DDI_PROP_TYPE_INT64)
1521 		return (DI_PROP_TYPE_INT64);
1522 
1523 	if (flags & DDI_PROP_TYPE_STRING)
1524 		return (DI_PROP_TYPE_STRING);
1525 
1526 	if (flags & DDI_PROP_TYPE_BYTE)
1527 		return (DI_PROP_TYPE_BYTE);
1528 
1529 	/*
1530 	 * Shouldn't get here. In case we do, return unknown type.
1531 	 *
1532 	 * XXX--When DDI_PROP_TYPE_COMPOSITE is implemented, we need
1533 	 *	to add DI_PROP_TYPE_COMPOSITE.
1534 	 */
1535 	DPRINTF((DI_ERR, "Unimplemented property type: 0x%x\n", flags));
1536 
1537 	return (DI_PROP_TYPE_UNKNOWN);
1538 }
1539 
1540 /*
1541  * Extract type-specific values of an property
1542  */
1543 extern int di_prop_decode_common(void *prop_data, int len,
1544 	int ddi_type, int prom);
1545 
1546 int
1547 di_prop_ints(di_prop_t prop, int **prop_data)
1548 {
1549 	if (DI_PROP(prop)->prop_len == 0)
1550 		return (0);	/* boolean property */
1551 
1552 	if ((DI_PROP(prop)->prop_data == 0) ||
1553 	    (DI_PROP(prop)->prop_data == (di_off_t)-1)) {
1554 		errno = EFAULT;
1555 		*prop_data = NULL;
1556 		return (-1);
1557 	}
1558 
1559 	*prop_data = (int *)((void *)((caddr_t)prop - DI_PROP(prop)->self
1560 	    + DI_PROP(prop)->prop_data));
1561 
1562 	return (di_prop_decode_common((void *)prop_data,
1563 	    DI_PROP(prop)->prop_len, DI_PROP_TYPE_INT, 0));
1564 }
1565 
1566 int
1567 di_prop_int64(di_prop_t prop, int64_t **prop_data)
1568 {
1569 	if (DI_PROP(prop)->prop_len == 0)
1570 		return (0);	/* boolean property */
1571 
1572 	if ((DI_PROP(prop)->prop_data == 0) ||
1573 	    (DI_PROP(prop)->prop_data == (di_off_t)-1)) {
1574 		errno = EFAULT;
1575 		*prop_data = NULL;
1576 		return (-1);
1577 	}
1578 
1579 	*prop_data = (int64_t *)((void *)((caddr_t)prop - DI_PROP(prop)->self
1580 	    + DI_PROP(prop)->prop_data));
1581 
1582 	return (di_prop_decode_common((void *)prop_data,
1583 	    DI_PROP(prop)->prop_len, DI_PROP_TYPE_INT64, 0));
1584 }
1585 
1586 int
1587 di_prop_strings(di_prop_t prop, char **prop_data)
1588 {
1589 	if (DI_PROP(prop)->prop_len == 0)
1590 		return (0);	/* boolean property */
1591 
1592 	if ((DI_PROP(prop)->prop_data == 0) ||
1593 	    (DI_PROP(prop)->prop_data == (di_off_t)-1)) {
1594 		errno = EFAULT;
1595 		*prop_data = NULL;
1596 		return (-1);
1597 	}
1598 
1599 	*prop_data = (char *)((caddr_t)prop - DI_PROP(prop)->self
1600 	    + DI_PROP(prop)->prop_data);
1601 
1602 	return (di_prop_decode_common((void *)prop_data,
1603 	    DI_PROP(prop)->prop_len, DI_PROP_TYPE_STRING, 0));
1604 }
1605 
1606 int
1607 di_prop_bytes(di_prop_t prop, uchar_t **prop_data)
1608 {
1609 	if (DI_PROP(prop)->prop_len == 0)
1610 		return (0);	/* boolean property */
1611 
1612 	if ((DI_PROP(prop)->prop_data == 0) ||
1613 	    (DI_PROP(prop)->prop_data == (di_off_t)-1)) {
1614 		errno = EFAULT;
1615 		*prop_data = NULL;
1616 		return (-1);
1617 	}
1618 
1619 	*prop_data = (uchar_t *)((caddr_t)prop - DI_PROP(prop)->self
1620 	    + DI_PROP(prop)->prop_data);
1621 
1622 	return (di_prop_decode_common((void *)prop_data,
1623 	    DI_PROP(prop)->prop_len, DI_PROP_TYPE_BYTE, 0));
1624 }
1625 
1626 /*
1627  * returns 1 for match, 0 for no match
1628  */
1629 static int
1630 match_prop(di_prop_t prop, dev_t match_dev, const char *name, int type)
1631 {
1632 	int prop_type;
1633 
1634 #ifdef DEBUG
1635 	if (di_prop_name(prop) == NULL) {
1636 		DPRINTF((DI_ERR, "libdevinfo: property has no name!\n"));
1637 		return (0);
1638 	}
1639 #endif /* DEBUG */
1640 
1641 	if (strcmp(name, di_prop_name(prop)) != 0)
1642 		return (0);
1643 
1644 	if ((match_dev != DDI_DEV_T_ANY) && (di_prop_devt(prop) != match_dev))
1645 		return (0);
1646 
1647 	/*
1648 	 * XXX prop_type is different from DDI_*. See PSARC 1997/127.
1649 	 */
1650 	prop_type = di_prop_type(prop);
1651 	if ((prop_type != DI_PROP_TYPE_UNKNOWN) && (prop_type != type) &&
1652 	    (prop_type != DI_PROP_TYPE_BOOLEAN))
1653 		return (0);
1654 
1655 	return (1);
1656 }
1657 
1658 static di_prop_t
1659 di_prop_search(dev_t match_dev, di_node_t node, const char *name,
1660     int type)
1661 {
1662 	di_prop_t prop = DI_PROP_NIL;
1663 
1664 	/*
1665 	 * The check on match_dev follows ddi_prop_lookup_common().
1666 	 * Other checks are libdevinfo specific implementation.
1667 	 */
1668 	if ((node == DI_NODE_NIL) || (name == NULL) || (strlen(name) == 0) ||
1669 	    (match_dev == DDI_DEV_T_NONE) || !DI_PROP_TYPE_VALID(type)) {
1670 		errno = EINVAL;
1671 		return (DI_PROP_NIL);
1672 	}
1673 
1674 	while ((prop = di_prop_next(node, prop)) != DI_PROP_NIL) {
1675 		DPRINTF((DI_TRACE1, "match prop name %s, devt 0x%lx, type %d\n",
1676 		    di_prop_name(prop), di_prop_devt(prop),
1677 		    di_prop_type(prop)));
1678 		if (match_prop(prop, match_dev, name, type))
1679 			return (prop);
1680 	}
1681 
1682 	return (DI_PROP_NIL);
1683 }
1684 
1685 di_prop_t
1686 di_prop_find(dev_t match_dev, di_node_t node, const char *name)
1687 {
1688 	di_prop_t prop = DI_PROP_NIL;
1689 
1690 	if ((node == DI_NODE_NIL) || (name == NULL) || (strlen(name) == 0) ||
1691 	    (match_dev == DDI_DEV_T_NONE)) {
1692 		errno = EINVAL;
1693 		return (DI_PROP_NIL);
1694 	}
1695 
1696 	while ((prop = di_prop_next(node, prop)) != DI_PROP_NIL) {
1697 		DPRINTF((DI_TRACE1, "found prop name %s, devt 0x%lx, type %d\n",
1698 		    di_prop_name(prop), di_prop_devt(prop),
1699 		    di_prop_type(prop)));
1700 
1701 		if (strcmp(name, di_prop_name(prop)) == 0 &&
1702 		    (match_dev == DDI_DEV_T_ANY ||
1703 		    di_prop_devt(prop) == match_dev))
1704 			return (prop);
1705 	}
1706 
1707 	return (DI_PROP_NIL);
1708 }
1709 
1710 int
1711 di_prop_lookup_ints(dev_t dev, di_node_t node, const char *prop_name,
1712 	int **prop_data)
1713 {
1714 	di_prop_t prop;
1715 
1716 	if ((prop = di_prop_search(dev, node, prop_name,
1717 	    DI_PROP_TYPE_INT)) == DI_PROP_NIL)
1718 		return (-1);
1719 
1720 	return (di_prop_ints(prop, (void *)prop_data));
1721 }
1722 
1723 int
1724 di_prop_lookup_int64(dev_t dev, di_node_t node, const char *prop_name,
1725 	int64_t **prop_data)
1726 {
1727 	di_prop_t prop;
1728 
1729 	if ((prop = di_prop_search(dev, node, prop_name,
1730 	    DI_PROP_TYPE_INT64)) == DI_PROP_NIL)
1731 		return (-1);
1732 
1733 	return (di_prop_int64(prop, (void *)prop_data));
1734 }
1735 
1736 int
1737 di_prop_lookup_strings(dev_t dev, di_node_t node, const char *prop_name,
1738     char **prop_data)
1739 {
1740 	di_prop_t prop;
1741 
1742 	if ((prop = di_prop_search(dev, node, prop_name,
1743 	    DI_PROP_TYPE_STRING)) == DI_PROP_NIL)
1744 		return (-1);
1745 
1746 	return (di_prop_strings(prop, (void *)prop_data));
1747 }
1748 
1749 int
1750 di_prop_lookup_bytes(dev_t dev, di_node_t node, const char *prop_name,
1751 	uchar_t **prop_data)
1752 {
1753 	di_prop_t prop;
1754 
1755 	if ((prop = di_prop_search(dev, node, prop_name,
1756 	    DI_PROP_TYPE_BYTE)) == DI_PROP_NIL)
1757 		return (-1);
1758 
1759 	return (di_prop_bytes(prop, (void *)prop_data));
1760 }
1761 
1762 /*
1763  * Consolidation private property access functions
1764  */
1765 enum prop_type {
1766 	PROP_TYPE_DRV,
1767 	PROP_TYPE_SYS,
1768 	PROP_TYPE_GLOB,
1769 	PROP_TYPE_HW
1770 };
1771 
1772 static di_prop_t
1773 di_prop_next_common(di_node_t node, di_prop_t prop, int prop_type)
1774 {
1775 	caddr_t pa;
1776 	di_off_t prop_off = 0;
1777 
1778 	if (prop != DI_PROP_NIL) {
1779 		if (DI_PROP(prop)->next) {
1780 			return (DI_PROP((caddr_t)prop -
1781 			    DI_PROP(prop)->self + DI_PROP(prop)->next));
1782 		} else {
1783 			return (DI_PROP_NIL);
1784 		}
1785 	}
1786 
1787 
1788 	/*
1789 	 * prop is NIL, caller asks for first property
1790 	 */
1791 	pa = (caddr_t)node - DI_NODE(node)->self;
1792 	switch (prop_type) {
1793 	case PROP_TYPE_DRV:
1794 		prop_off = DI_NODE(node)->drv_prop;
1795 		break;
1796 	case PROP_TYPE_SYS:
1797 		prop_off = DI_NODE(node)->sys_prop;
1798 		break;
1799 	case PROP_TYPE_HW:
1800 		prop_off = DI_NODE(node)->hw_prop;
1801 		break;
1802 	case PROP_TYPE_GLOB:
1803 		prop_off = DI_NODE(node)->glob_prop;
1804 		if (prop_off == -1) {
1805 			/* no global property */
1806 			prop_off = 0;
1807 		} else if ((prop_off == 0) && (DI_NODE(node)->drv_major >= 0)) {
1808 			/* refer to devnames array */
1809 			struct di_devnm *devnm = DI_DEVNM(pa +
1810 			    DI_ALL(pa)->devnames + (DI_NODE(node)->drv_major *
1811 			    sizeof (struct di_devnm)));
1812 			prop_off = devnm->global_prop;
1813 		}
1814 		break;
1815 	}
1816 
1817 	if (prop_off) {
1818 		return (DI_PROP(pa + prop_off));
1819 	}
1820 
1821 	/*
1822 	 * no prop found. Check the reason for not found
1823 	 */
1824 	if (DINFOPROP & DI_ALL(pa)->command)
1825 		errno = ENXIO;
1826 	else
1827 		errno = ENOTSUP;
1828 
1829 	return (DI_PROP_NIL);
1830 }
1831 
1832 di_prop_t
1833 di_prop_drv_next(di_node_t node, di_prop_t prop)
1834 {
1835 	return (di_prop_next_common(node, prop, PROP_TYPE_DRV));
1836 }
1837 
1838 di_prop_t
1839 di_prop_sys_next(di_node_t node, di_prop_t prop)
1840 {
1841 	return (di_prop_next_common(node, prop, PROP_TYPE_SYS));
1842 }
1843 
1844 di_prop_t
1845 di_prop_global_next(di_node_t node, di_prop_t prop)
1846 {
1847 	return (di_prop_next_common(node, prop, PROP_TYPE_GLOB));
1848 }
1849 
1850 di_prop_t
1851 di_prop_hw_next(di_node_t node, di_prop_t prop)
1852 {
1853 	return (di_prop_next_common(node, prop, PROP_TYPE_HW));
1854 }
1855 
1856 int
1857 di_prop_rawdata(di_prop_t prop, uchar_t **prop_data)
1858 {
1859 #ifdef DEBUG
1860 	if (prop == DI_PROP_NIL) {
1861 		errno = EINVAL;
1862 		return (-1);
1863 	}
1864 #endif /* DEBUG */
1865 
1866 	if (DI_PROP(prop)->prop_len == 0) {
1867 		*prop_data = NULL;
1868 		return (0);
1869 	}
1870 
1871 	if ((DI_PROP(prop)->prop_data == 0) ||
1872 	    (DI_PROP(prop)->prop_data == (di_off_t)-1)) {
1873 		errno = EFAULT;
1874 		*prop_data = NULL;
1875 		return (-1);
1876 	}
1877 
1878 	/*
1879 	 * No memory allocation.
1880 	 */
1881 	*prop_data = (uchar_t *)((caddr_t)prop - DI_PROP(prop)->self +
1882 	    DI_PROP(prop)->prop_data);
1883 
1884 	return (DI_PROP(prop)->prop_len);
1885 }
1886 
1887 /*
1888  * Consolidation private interfaces for accessing I/O multipathing data
1889  */
1890 di_path_t
1891 di_path_phci_next_path(di_node_t node, di_path_t path)
1892 {
1893 	caddr_t pa;
1894 
1895 	/*
1896 	 * path is not NIL
1897 	 */
1898 	if (path != DI_PATH_NIL) {
1899 		if (DI_PATH(path)->path_p_link != 0)
1900 			return (DI_PATH((void *)((caddr_t)path -
1901 			    DI_PATH(path)->self + DI_PATH(path)->path_p_link)));
1902 		else {
1903 			errno = ENXIO;
1904 			return (DI_PATH_NIL);
1905 		}
1906 	}
1907 
1908 	/*
1909 	 * Path is NIL; the caller is asking for the first path info node
1910 	 */
1911 	if (DI_NODE(node)->multipath_phci != 0) {
1912 		DPRINTF((DI_INFO, "phci_next_path: returning %p\n",
1913 		    ((caddr_t)node -
1914 		    DI_NODE(node)->self + DI_NODE(node)->multipath_phci)));
1915 		return (DI_PATH((caddr_t)node - DI_NODE(node)->self +
1916 		    DI_NODE(node)->multipath_phci));
1917 	}
1918 
1919 	/*
1920 	 * No pathing data; check if the snapshot includes path data in order
1921 	 * to set errno properly.
1922 	 */
1923 	pa = (caddr_t)node - DI_NODE(node)->self;
1924 	if (DINFOPATH & (DI_ALL(pa)->command))
1925 		errno = ENXIO;
1926 	else
1927 		errno = ENOTSUP;
1928 
1929 	return (DI_PATH_NIL);
1930 }
1931 
1932 di_path_t
1933 di_path_client_next_path(di_node_t node, di_path_t path)
1934 {
1935 	caddr_t pa;
1936 
1937 	/*
1938 	 * path is not NIL
1939 	 */
1940 	if (path != DI_PATH_NIL) {
1941 		if (DI_PATH(path)->path_c_link != 0)
1942 			return (DI_PATH((caddr_t)path - DI_PATH(path)->self
1943 			    + DI_PATH(path)->path_c_link));
1944 		else {
1945 			errno = ENXIO;
1946 			return (DI_PATH_NIL);
1947 		}
1948 	}
1949 
1950 	/*
1951 	 * Path is NIL; the caller is asking for the first path info node
1952 	 */
1953 	if (DI_NODE(node)->multipath_client != 0) {
1954 		DPRINTF((DI_INFO, "client_next_path: returning %p\n",
1955 		    ((caddr_t)node -
1956 		    DI_NODE(node)->self + DI_NODE(node)->multipath_client)));
1957 		return (DI_PATH((caddr_t)node - DI_NODE(node)->self +
1958 		    DI_NODE(node)->multipath_client));
1959 	}
1960 
1961 	/*
1962 	 * No pathing data; check if the snapshot includes path data in order
1963 	 * to set errno properly.
1964 	 */
1965 	pa = (caddr_t)node - DI_NODE(node)->self;
1966 	if (DINFOPATH & (DI_ALL(pa)->command))
1967 		errno = ENXIO;
1968 	else
1969 		errno = ENOTSUP;
1970 
1971 	return (DI_PATH_NIL);
1972 }
1973 
1974 /*
1975  * XXX Remove the private di_path_(addr,next,next_phci,next_client) interfaces
1976  * below after NWS consolidation switches to using di_path_bus_addr,
1977  * di_path_phci_next_path, and di_path_client_next_path per CR6638521.
1978  */
1979 char *
1980 di_path_addr(di_path_t path, char *buf)
1981 {
1982 	caddr_t pa;		/* starting address of map */
1983 
1984 	pa = (caddr_t)path - DI_PATH(path)->self;
1985 
1986 	(void) strncpy(buf, (char *)(pa + DI_PATH(path)->path_addr),
1987 	    MAXPATHLEN);
1988 	return (buf);
1989 }
1990 di_path_t
1991 di_path_next(di_node_t node, di_path_t path)
1992 {
1993 	if (node == DI_NODE_NIL) {
1994 		errno = EINVAL;
1995 		return (DI_PATH_NIL);
1996 	}
1997 
1998 	if (DI_NODE(node)->multipath_client) {
1999 		return (di_path_client_next_path(node, path));
2000 	} else if (DI_NODE(node)->multipath_phci) {
2001 		return (di_path_phci_next_path(node, path));
2002 	} else {
2003 		/*
2004 		 * The node had multipathing data but didn't appear to be a
2005 		 * phci *or* a client; probably a programmer error.
2006 		 */
2007 		errno = EINVAL;
2008 		return (DI_PATH_NIL);
2009 	}
2010 }
2011 di_path_t
2012 di_path_next_phci(di_node_t node, di_path_t path)
2013 {
2014 	return (di_path_client_next_path(node, path));
2015 }
2016 di_path_t
2017 di_path_next_client(di_node_t node, di_path_t path)
2018 {
2019 	return (di_path_phci_next_path(node, path));
2020 }
2021 
2022 
2023 
2024 
2025 di_path_state_t
2026 di_path_state(di_path_t path)
2027 {
2028 	return ((di_path_state_t)DI_PATH(path)->path_state);
2029 }
2030 
2031 char *
2032 di_path_node_name(di_path_t path)
2033 {
2034 	di_node_t	client_node;
2035 
2036 	/* pathinfo gets node_name from client */
2037 	if ((client_node = di_path_client_node(path)) == NULL)
2038 		return (NULL);
2039 	return (di_node_name(client_node));
2040 }
2041 
2042 char *
2043 di_path_bus_addr(di_path_t path)
2044 {
2045 	caddr_t pa = (caddr_t)path - DI_PATH(path)->self;
2046 
2047 	if (DI_PATH(path)->path_addr == 0)
2048 		return (NULL);
2049 
2050 	return ((char *)(pa + DI_PATH(path)->path_addr));
2051 }
2052 
2053 int
2054 di_path_instance(di_path_t path)
2055 {
2056 	return (DI_PATH(path)->path_instance);
2057 }
2058 
2059 di_node_t
2060 di_path_client_node(di_path_t path)
2061 {
2062 	caddr_t pa;		/* starting address of map */
2063 
2064 	if (path == DI_PATH_NIL) {
2065 		errno = EINVAL;
2066 		return (DI_PATH_NIL);
2067 	}
2068 
2069 	DPRINTF((DI_TRACE, "Get client node for path %p\n", path));
2070 
2071 	pa = (caddr_t)path - DI_PATH(path)->self;
2072 
2073 	if (DI_PATH(path)->path_client) {
2074 		return (DI_NODE(pa + DI_PATH(path)->path_client));
2075 	}
2076 
2077 	/*
2078 	 * Deal with error condition:
2079 	 *   If parent doesn't exist and node is not the root,
2080 	 *   set errno to ENOTSUP. Otherwise, set errno to ENXIO.
2081 	 */
2082 	if ((DI_PATH(path)->path_snap_state & DI_PATH_SNAP_NOCLIENT) == 0)
2083 		errno = ENOTSUP;
2084 	else
2085 		errno = ENXIO;
2086 
2087 	return (DI_NODE_NIL);
2088 }
2089 
2090 di_node_t
2091 di_path_phci_node(di_path_t path)
2092 {
2093 	caddr_t pa;		/* starting address of map */
2094 
2095 	if (path == DI_PATH_NIL) {
2096 		errno = EINVAL;
2097 		return (DI_PATH_NIL);
2098 	}
2099 
2100 	DPRINTF((DI_TRACE, "Get phci node for path %p\n", path));
2101 
2102 	pa = (caddr_t)path - DI_PATH(path)->self;
2103 
2104 	if (DI_PATH(path)->path_phci) {
2105 		return (DI_NODE(pa + DI_PATH(path)->path_phci));
2106 	}
2107 
2108 	/*
2109 	 * Deal with error condition:
2110 	 *   If parent doesn't exist and node is not the root,
2111 	 *   set errno to ENOTSUP. Otherwise, set errno to ENXIO.
2112 	 */
2113 	if ((DI_PATH(path)->path_snap_state & DI_PATH_SNAP_NOPHCI) == 0)
2114 		errno = ENOTSUP;
2115 	else
2116 		errno = ENXIO;
2117 
2118 	return (DI_NODE_NIL);
2119 }
2120 
2121 di_path_prop_t
2122 di_path_prop_next(di_path_t path, di_path_prop_t prop)
2123 {
2124 	caddr_t pa;
2125 
2126 	if (path == DI_PATH_NIL) {
2127 		errno = EINVAL;
2128 		return (DI_PROP_NIL);
2129 	}
2130 
2131 	/*
2132 	 * prop is not NIL
2133 	 */
2134 	if (prop != DI_PROP_NIL) {
2135 		if (DI_PROP(prop)->next != 0)
2136 			return (DI_PATHPROP((caddr_t)prop -
2137 			    DI_PROP(prop)->self + DI_PROP(prop)->next));
2138 		else {
2139 			errno = ENXIO;
2140 			return (DI_PROP_NIL);
2141 		}
2142 	}
2143 
2144 	/*
2145 	 * prop is NIL-->caller asks for first property
2146 	 */
2147 	pa = (caddr_t)path - DI_PATH(path)->self;
2148 	if (DI_PATH(path)->path_prop != 0) {
2149 		return (DI_PATHPROP(pa + DI_PATH(path)->path_prop));
2150 	}
2151 
2152 	/*
2153 	 * no property data-->check if snapshot includes props
2154 	 *	in order to set the correct errno
2155 	 */
2156 	if (DINFOPROP & (DI_ALL(pa)->command))
2157 		errno = ENXIO;
2158 	else
2159 		errno = ENOTSUP;
2160 
2161 	return (DI_PROP_NIL);
2162 }
2163 
2164 char *
2165 di_path_prop_name(di_path_prop_t prop)
2166 {
2167 	caddr_t pa;		/* starting address of map */
2168 	pa = (caddr_t)prop - DI_PATHPROP(prop)->self;
2169 	return ((char *)(pa + DI_PATHPROP(prop)->prop_name));
2170 }
2171 
2172 int
2173 di_path_prop_len(di_path_prop_t prop)
2174 {
2175 	return (DI_PATHPROP(prop)->prop_len);
2176 }
2177 
2178 int
2179 di_path_prop_type(di_path_prop_t prop)
2180 {
2181 	switch (DI_PATHPROP(prop)->prop_type) {
2182 		case DDI_PROP_TYPE_INT:
2183 			return (DI_PROP_TYPE_INT);
2184 		case DDI_PROP_TYPE_INT64:
2185 			return (DI_PROP_TYPE_INT64);
2186 		case DDI_PROP_TYPE_BYTE:
2187 			return (DI_PROP_TYPE_BYTE);
2188 		case DDI_PROP_TYPE_STRING:
2189 			return (DI_PROP_TYPE_STRING);
2190 	}
2191 	return (DI_PROP_TYPE_UNKNOWN);
2192 }
2193 
2194 int
2195 di_path_prop_bytes(di_path_prop_t prop, uchar_t **prop_data)
2196 {
2197 	if ((DI_PATHPROP(prop)->prop_data == 0) ||
2198 	    (DI_PATHPROP(prop)->prop_data == (di_off_t)-1)) {
2199 		errno = EFAULT;
2200 		*prop_data = NULL;
2201 		return (-1);
2202 	}
2203 
2204 	*prop_data = (uchar_t *)((caddr_t)prop - DI_PATHPROP(prop)->self
2205 	    + DI_PATHPROP(prop)->prop_data);
2206 
2207 	return (di_prop_decode_common((void *)prop_data,
2208 	    DI_PATHPROP(prop)->prop_len, DI_PROP_TYPE_BYTE, 0));
2209 }
2210 
2211 int
2212 di_path_prop_ints(di_path_prop_t prop, int **prop_data)
2213 {
2214 	if (DI_PATHPROP(prop)->prop_len == 0)
2215 		return (0);
2216 
2217 	if ((DI_PATHPROP(prop)->prop_data == 0) ||
2218 	    (DI_PATHPROP(prop)->prop_data == (di_off_t)-1)) {
2219 		errno = EFAULT;
2220 		*prop_data = NULL;
2221 		return (-1);
2222 	}
2223 
2224 	*prop_data = (int *)((void *)((caddr_t)prop - DI_PATHPROP(prop)->self
2225 	    + DI_PATHPROP(prop)->prop_data));
2226 
2227 	return (di_prop_decode_common((void *)prop_data,
2228 	    DI_PATHPROP(prop)->prop_len, DI_PROP_TYPE_INT, 0));
2229 }
2230 
2231 int
2232 di_path_prop_int64s(di_path_prop_t prop, int64_t **prop_data)
2233 {
2234 	if (DI_PATHPROP(prop)->prop_len == 0)
2235 		return (0);
2236 
2237 	if ((DI_PATHPROP(prop)->prop_data == 0) ||
2238 	    (DI_PATHPROP(prop)->prop_data == (di_off_t)-1)) {
2239 		errno = EFAULT;
2240 		*prop_data = NULL;
2241 		return (-1);
2242 	}
2243 
2244 	*prop_data = (int64_t *)((void *)((caddr_t)prop -
2245 	    DI_PATHPROP(prop)->self + DI_PATHPROP(prop)->prop_data));
2246 
2247 	return (di_prop_decode_common((void *)prop_data,
2248 	    DI_PATHPROP(prop)->prop_len, DI_PROP_TYPE_INT64, 0));
2249 }
2250 
2251 int
2252 di_path_prop_strings(di_path_prop_t prop, char **prop_data)
2253 {
2254 	if (DI_PATHPROP(prop)->prop_len == 0)
2255 		return (0);
2256 
2257 	if ((DI_PATHPROP(prop)->prop_data == 0) ||
2258 	    (DI_PATHPROP(prop)->prop_data == (di_off_t)-1)) {
2259 		errno = EFAULT;
2260 		*prop_data = NULL;
2261 		return (-1);
2262 	}
2263 
2264 	*prop_data = (char *)((caddr_t)prop - DI_PATHPROP(prop)->self
2265 	    + DI_PATHPROP(prop)->prop_data);
2266 
2267 	return (di_prop_decode_common((void *)prop_data,
2268 	    DI_PATHPROP(prop)->prop_len, DI_PROP_TYPE_STRING, 0));
2269 }
2270 
2271 static di_path_prop_t
2272 di_path_prop_search(di_path_t path, const char *name, int type)
2273 {
2274 	di_path_prop_t prop = DI_PROP_NIL;
2275 
2276 	/*
2277 	 * Sanity check arguments
2278 	 */
2279 	if ((path == DI_PATH_NIL) || (name == NULL) || (strlen(name) == 0) ||
2280 	    !DI_PROP_TYPE_VALID(type)) {
2281 		errno = EINVAL;
2282 		return (DI_PROP_NIL);
2283 	}
2284 
2285 	while ((prop = di_path_prop_next(path, prop)) != DI_PROP_NIL) {
2286 		int prop_type = di_path_prop_type(prop);
2287 
2288 		DPRINTF((DI_TRACE1, "match path prop name %s, type %d\n",
2289 		    di_path_prop_name(prop), prop_type));
2290 
2291 		if (strcmp(name, di_path_prop_name(prop)) != 0)
2292 			continue;
2293 
2294 		if ((prop_type != DI_PROP_TYPE_UNKNOWN) && (prop_type != type))
2295 			continue;
2296 
2297 		return (prop);
2298 	}
2299 
2300 	return (DI_PROP_NIL);
2301 }
2302 
2303 int
2304 di_path_prop_lookup_bytes(di_path_t path, const char *prop_name,
2305     uchar_t **prop_data)
2306 {
2307 	di_path_prop_t prop;
2308 
2309 	if ((prop = di_path_prop_search(path, prop_name,
2310 	    DI_PROP_TYPE_BYTE)) == DI_PROP_NIL)
2311 		return (-1);
2312 
2313 	return (di_path_prop_bytes(prop, prop_data));
2314 }
2315 
2316 int
2317 di_path_prop_lookup_ints(di_path_t path, const char *prop_name,
2318     int **prop_data)
2319 {
2320 	di_path_prop_t prop;
2321 
2322 	if ((prop = di_path_prop_search(path, prop_name,
2323 	    DI_PROP_TYPE_INT)) == DI_PROP_NIL)
2324 		return (-1);
2325 
2326 	return (di_path_prop_ints(prop, prop_data));
2327 }
2328 
2329 int
2330 di_path_prop_lookup_int64s(di_path_t path, const char *prop_name,
2331     int64_t **prop_data)
2332 {
2333 	di_path_prop_t prop;
2334 
2335 	if ((prop = di_path_prop_search(path, prop_name,
2336 	    DI_PROP_TYPE_INT64)) == DI_PROP_NIL)
2337 		return (-1);
2338 
2339 	return (di_path_prop_int64s(prop, prop_data));
2340 }
2341 
2342 int di_path_prop_lookup_strings(di_path_t path, const char *prop_name,
2343     char **prop_data)
2344 {
2345 	di_path_prop_t prop;
2346 
2347 	if ((prop = di_path_prop_search(path, prop_name,
2348 	    DI_PROP_TYPE_STRING)) == DI_PROP_NIL)
2349 		return (-1);
2350 
2351 	return (di_path_prop_strings(prop, prop_data));
2352 }
2353 
2354 /*
2355  * Consolidation private interfaces for traversing vhci nodes.
2356  */
2357 di_node_t
2358 di_vhci_first_node(di_node_t root)
2359 {
2360 	struct di_all *dap;
2361 	caddr_t		pa;		/* starting address of map */
2362 
2363 	DPRINTF((DI_INFO, "Get first vhci node\n"));
2364 
2365 	if (root == DI_NODE_NIL) {
2366 		errno = EINVAL;
2367 		return (DI_NODE_NIL);
2368 	}
2369 
2370 	pa = (caddr_t)root - DI_NODE(root)->self;
2371 	dap = DI_ALL(pa);
2372 
2373 	if (dap->top_vhci_devinfo == NULL) {
2374 		errno = ENXIO;
2375 		return (DI_NODE_NIL);
2376 	}
2377 
2378 	return (DI_NODE(pa + dap->top_vhci_devinfo));
2379 }
2380 
2381 di_node_t
2382 di_vhci_next_node(di_node_t node)
2383 {
2384 	caddr_t		pa;		/* starting address of map */
2385 
2386 	if (node == DI_NODE_NIL) {
2387 		errno = EINVAL;
2388 		return (DI_NODE_NIL);
2389 	}
2390 
2391 	DPRINTF((DI_TRACE, "next vhci node on the snap shot:"
2392 	    " current=%s\n", di_node_name(node)));
2393 
2394 	if (DI_NODE(node)->next_vhci == NULL) {
2395 		errno = ENXIO;
2396 		return (DI_NODE_NIL);
2397 	}
2398 
2399 	pa = (caddr_t)node - DI_NODE(node)->self;
2400 
2401 	return (DI_NODE(pa + DI_NODE(node)->next_vhci));
2402 }
2403 
2404 /*
2405  * Consolidation private interfaces for traversing phci nodes.
2406  */
2407 di_node_t
2408 di_phci_first_node(di_node_t vhci_node)
2409 {
2410 	caddr_t		pa;		/* starting address of map */
2411 
2412 	DPRINTF((DI_INFO, "Get first phci node:\n"
2413 	    " current=%s", di_node_name(vhci_node)));
2414 
2415 	if (vhci_node == DI_NODE_NIL) {
2416 		errno = EINVAL;
2417 		return (DI_NODE_NIL);
2418 	}
2419 
2420 	pa = (caddr_t)vhci_node - DI_NODE(vhci_node)->self;
2421 
2422 	if (DI_NODE(vhci_node)->top_phci == NULL) {
2423 		errno = ENXIO;
2424 		return (DI_NODE_NIL);
2425 	}
2426 
2427 	return (DI_NODE(pa + DI_NODE(vhci_node)->top_phci));
2428 }
2429 
2430 di_node_t
2431 di_phci_next_node(di_node_t node)
2432 {
2433 	caddr_t		pa;		/* starting address of map */
2434 
2435 	if (node == DI_NODE_NIL) {
2436 		errno = EINVAL;
2437 		return (DI_NODE_NIL);
2438 	}
2439 
2440 	DPRINTF((DI_TRACE, "next phci node on the snap shot:"
2441 	    " current=%s\n", di_node_name(node)));
2442 
2443 	if (DI_NODE(node)->next_phci == NULL) {
2444 		errno = ENXIO;
2445 		return (DI_NODE_NIL);
2446 	}
2447 
2448 	pa = (caddr_t)node - DI_NODE(node)->self;
2449 
2450 	return (DI_NODE(pa + DI_NODE(node)->next_phci));
2451 }
2452 
2453 /*
2454  * Consolidation private interfaces for private data
2455  */
2456 void *
2457 di_parent_private_data(di_node_t node)
2458 {
2459 	caddr_t pa;
2460 
2461 	if (DI_NODE(node)->parent_data == 0) {
2462 		errno = ENXIO;
2463 		return (NULL);
2464 	}
2465 
2466 	if (DI_NODE(node)->parent_data == (di_off_t)-1) {
2467 		/*
2468 		 * Private data requested, but not obtained due to a memory
2469 		 * error (e.g. wrong format specified)
2470 		 */
2471 		errno = EFAULT;
2472 		return (NULL);
2473 	}
2474 
2475 	pa = (caddr_t)node - DI_NODE(node)->self;
2476 	if (DI_NODE(node)->parent_data)
2477 		return (pa + DI_NODE(node)->parent_data);
2478 
2479 	if (DI_ALL(pa)->command & DINFOPRIVDATA)
2480 		errno = ENXIO;
2481 	else
2482 		errno = ENOTSUP;
2483 
2484 	return (NULL);
2485 }
2486 
2487 void *
2488 di_driver_private_data(di_node_t node)
2489 {
2490 	caddr_t pa;
2491 
2492 	if (DI_NODE(node)->driver_data == 0) {
2493 		errno = ENXIO;
2494 		return (NULL);
2495 	}
2496 
2497 	if (DI_NODE(node)->driver_data == (di_off_t)-1) {
2498 		/*
2499 		 * Private data requested, but not obtained due to a memory
2500 		 * error (e.g. wrong format specified)
2501 		 */
2502 		errno = EFAULT;
2503 		return (NULL);
2504 	}
2505 
2506 	pa = (caddr_t)node - DI_NODE(node)->self;
2507 	if (DI_NODE(node)->driver_data)
2508 		return (pa + DI_NODE(node)->driver_data);
2509 
2510 	if (DI_ALL(pa)->command & DINFOPRIVDATA)
2511 		errno = ENXIO;
2512 	else
2513 		errno = ENOTSUP;
2514 
2515 	return (NULL);
2516 }
2517 
2518 /*
2519  * PROM property access
2520  */
2521 
2522 /*
2523  * openprom driver stuff:
2524  *	The maximum property length depends on the buffer size. We use
2525  *	OPROMMAXPARAM defined in <sys/openpromio.h>
2526  *
2527  *	MAXNAMESZ is max property name. obpdefs.h defines it as 32 based on 1275
2528  *	MAXVALSZ is maximum value size, which is whatever space left in buf
2529  */
2530 
2531 #define	OBP_MAXBUF	OPROMMAXPARAM - sizeof (int)
2532 #define	OBP_MAXPROPLEN	OBP_MAXBUF - OBP_MAXPROPNAME;
2533 
2534 struct di_prom_prop {
2535 	char *name;
2536 	int len;
2537 	uchar_t *data;
2538 	struct di_prom_prop *next;	/* form a linked list */
2539 };
2540 
2541 struct di_prom_handle { /* handle to prom */
2542 	mutex_t lock;	/* synchronize access to openprom fd */
2543 	int	fd;	/* /dev/openprom file descriptor */
2544 	struct di_prom_prop *list;	/* linked list of prop */
2545 	union {
2546 		char buf[OPROMMAXPARAM];
2547 		struct openpromio opp;
2548 	} oppbuf;
2549 };
2550 
2551 di_prom_handle_t
2552 di_prom_init()
2553 {
2554 	struct di_prom_handle *p;
2555 
2556 	if ((p = malloc(sizeof (struct di_prom_handle))) == NULL)
2557 		return (DI_PROM_HANDLE_NIL);
2558 
2559 	DPRINTF((DI_INFO, "di_prom_init: get prom handle 0x%p\n", p));
2560 
2561 	(void) mutex_init(&p->lock, USYNC_THREAD, NULL);
2562 	if ((p->fd = open("/dev/openprom", O_RDONLY)) < 0) {
2563 		free(p);
2564 		return (DI_PROM_HANDLE_NIL);
2565 	}
2566 	p->list = NULL;
2567 
2568 	return ((di_prom_handle_t)p);
2569 }
2570 
2571 static void
2572 di_prom_prop_free(struct di_prom_prop *list)
2573 {
2574 	struct di_prom_prop *tmp = list;
2575 
2576 	while (tmp != NULL) {
2577 		list = tmp->next;
2578 		if (tmp->name != NULL) {
2579 			free(tmp->name);
2580 		}
2581 		if (tmp->data != NULL) {
2582 			free(tmp->data);
2583 		}
2584 		free(tmp);
2585 		tmp = list;
2586 	}
2587 }
2588 
2589 void
2590 di_prom_fini(di_prom_handle_t ph)
2591 {
2592 	struct di_prom_handle *p = (struct di_prom_handle *)ph;
2593 
2594 	DPRINTF((DI_INFO, "di_prom_fini: free prom handle 0x%p\n", p));
2595 
2596 	(void) close(p->fd);
2597 	(void) mutex_destroy(&p->lock);
2598 	di_prom_prop_free(p->list);
2599 
2600 	free(p);
2601 }
2602 
2603 /*
2604  * Internal library interface for locating the property
2605  * XXX: ph->lock must be held for the duration of call.
2606  */
2607 static di_prom_prop_t
2608 di_prom_prop_found(di_prom_handle_t ph, int nodeid,
2609 	di_prom_prop_t prom_prop)
2610 {
2611 	struct di_prom_handle *p = (struct di_prom_handle *)ph;
2612 	struct openpromio *opp = &p->oppbuf.opp;
2613 	int *ip = (int *)((void *)opp->oprom_array);
2614 	struct di_prom_prop *prop = (struct di_prom_prop *)prom_prop;
2615 
2616 	DPRINTF((DI_TRACE1, "Looking for nodeid 0x%x\n", nodeid));
2617 
2618 	/*
2619 	 * Set "current" nodeid in the openprom driver
2620 	 */
2621 	opp->oprom_size = sizeof (int);
2622 	*ip = nodeid;
2623 	if (ioctl(p->fd, OPROMSETNODEID, opp) < 0) {
2624 		DPRINTF((DI_ERR, "*** Nodeid not found 0x%x\n", nodeid));
2625 		return (DI_PROM_PROP_NIL);
2626 	}
2627 
2628 	DPRINTF((DI_TRACE, "Found nodeid 0x%x\n", nodeid));
2629 
2630 	bzero(opp, OBP_MAXBUF);
2631 	opp->oprom_size = OBP_MAXPROPNAME;
2632 	if (prom_prop != DI_PROM_PROP_NIL)
2633 		(void) strcpy(opp->oprom_array, prop->name);
2634 
2635 	if ((ioctl(p->fd, OPROMNXTPROP, opp) < 0) || (opp->oprom_size == 0))
2636 		return (DI_PROM_PROP_NIL);
2637 
2638 	/*
2639 	 * Prom property found. Allocate struct for storing prop
2640 	 *   (reuse variable prop)
2641 	 */
2642 	if ((prop = malloc(sizeof (struct di_prom_prop))) == NULL)
2643 		return (DI_PROM_PROP_NIL);
2644 
2645 	/*
2646 	 * Get a copy of property name
2647 	 */
2648 	if ((prop->name = strdup(opp->oprom_array)) == NULL) {
2649 		free(prop);
2650 		return (DI_PROM_PROP_NIL);
2651 	}
2652 
2653 	/*
2654 	 * get property value and length
2655 	 */
2656 	opp->oprom_size = OBP_MAXPROPLEN;
2657 
2658 	if ((ioctl(p->fd, OPROMGETPROP, opp) < 0) ||
2659 	    (opp->oprom_size == (uint_t)-1)) {
2660 		free(prop->name);
2661 		free(prop);
2662 		return (DI_PROM_PROP_NIL);
2663 	}
2664 
2665 	/*
2666 	 * make a copy of the property value
2667 	 */
2668 	prop->len = opp->oprom_size;
2669 
2670 	if (prop->len == 0)
2671 		prop->data = NULL;
2672 	else if ((prop->data = malloc(prop->len)) == NULL) {
2673 		free(prop->name);
2674 		free(prop);
2675 		return (DI_PROM_PROP_NIL);
2676 	}
2677 
2678 	bcopy(opp->oprom_array, prop->data, prop->len);
2679 
2680 	/*
2681 	 * Prepend prop to list in prom handle
2682 	 */
2683 	prop->next = p->list;
2684 	p->list = prop;
2685 
2686 	return ((di_prom_prop_t)prop);
2687 }
2688 
2689 di_prom_prop_t
2690 di_prom_prop_next(di_prom_handle_t ph, di_node_t node, di_prom_prop_t prom_prop)
2691 {
2692 	struct di_prom_handle *p = (struct di_prom_handle *)ph;
2693 
2694 	DPRINTF((DI_TRACE1, "Search next prop for node 0x%p with ph 0x%p\n",
2695 	    node, p));
2696 
2697 	/*
2698 	 * paranoid check
2699 	 */
2700 	if ((ph == DI_PROM_HANDLE_NIL) || (node == DI_NODE_NIL)) {
2701 		errno = EINVAL;
2702 		return (DI_PROM_PROP_NIL);
2703 	}
2704 
2705 	if (di_nodeid(node) != DI_PROM_NODEID) {
2706 		errno = ENXIO;
2707 		return (DI_PROM_PROP_NIL);
2708 	}
2709 
2710 	/*
2711 	 * synchronize access to prom file descriptor
2712 	 */
2713 	(void) mutex_lock(&p->lock);
2714 
2715 	/*
2716 	 * look for next property
2717 	 */
2718 	prom_prop = di_prom_prop_found(ph, DI_NODE(node)->nodeid, prom_prop);
2719 
2720 	(void) mutex_unlock(&p->lock);
2721 
2722 	return (prom_prop);
2723 }
2724 
2725 char *
2726 di_prom_prop_name(di_prom_prop_t prom_prop)
2727 {
2728 	/*
2729 	 * paranoid check
2730 	 */
2731 	if (prom_prop == DI_PROM_PROP_NIL) {
2732 		errno = EINVAL;
2733 		return (NULL);
2734 	}
2735 
2736 	return (((struct di_prom_prop *)prom_prop)->name);
2737 }
2738 
2739 int
2740 di_prom_prop_data(di_prom_prop_t prom_prop, uchar_t **prom_prop_data)
2741 {
2742 	/*
2743 	 * paranoid check
2744 	 */
2745 	if (prom_prop == DI_PROM_PROP_NIL) {
2746 		errno = EINVAL;
2747 		return (NULL);
2748 	}
2749 
2750 	*prom_prop_data = ((struct di_prom_prop *)prom_prop)->data;
2751 
2752 	return (((struct di_prom_prop *)prom_prop)->len);
2753 }
2754 
2755 /*
2756  * Internal library interface for locating the property
2757  *    Returns length if found, -1 if prop doesn't exist.
2758  */
2759 static struct di_prom_prop *
2760 di_prom_prop_lookup_common(di_prom_handle_t ph, di_node_t node,
2761 	const char *prom_prop_name)
2762 {
2763 	struct openpromio *opp;
2764 	struct di_prom_prop *prop;
2765 	struct di_prom_handle *p = (struct di_prom_handle *)ph;
2766 
2767 	/*
2768 	 * paranoid check
2769 	 */
2770 	if ((ph == DI_PROM_HANDLE_NIL) || (node == DI_NODE_NIL)) {
2771 		errno = EINVAL;
2772 		return (NULL);
2773 	}
2774 
2775 	if (di_nodeid(node) != DI_PROM_NODEID) {
2776 		errno = ENXIO;
2777 		return (NULL);
2778 	}
2779 
2780 	opp = &p->oppbuf.opp;
2781 
2782 	(void) mutex_lock(&p->lock);
2783 
2784 	opp->oprom_size = sizeof (int);
2785 	opp->oprom_node = DI_NODE(node)->nodeid;
2786 	if (ioctl(p->fd, OPROMSETNODEID, opp) < 0) {
2787 		errno = ENXIO;
2788 		DPRINTF((DI_ERR, "*** Nodeid not found 0x%x\n",
2789 		    DI_NODE(node)->nodeid));
2790 		(void) mutex_unlock(&p->lock);
2791 		return (NULL);
2792 	}
2793 
2794 	/*
2795 	 * get property length
2796 	 */
2797 	bzero(opp, OBP_MAXBUF);
2798 	opp->oprom_size = OBP_MAXPROPLEN;
2799 	(void) strcpy(opp->oprom_array, prom_prop_name);
2800 
2801 	if ((ioctl(p->fd, OPROMGETPROPLEN, opp) < 0) ||
2802 	    (opp->oprom_len == -1)) {
2803 		/* no such property */
2804 		(void) mutex_unlock(&p->lock);
2805 		return (NULL);
2806 	}
2807 
2808 	/*
2809 	 * Prom property found. Allocate struct for storing prop
2810 	 */
2811 	if ((prop = malloc(sizeof (struct di_prom_prop))) == NULL) {
2812 		(void) mutex_unlock(&p->lock);
2813 		return (NULL);
2814 	}
2815 	prop->name = NULL;	/* we don't need the name */
2816 	prop->len = opp->oprom_len;
2817 
2818 	if (prop->len == 0) {	/* boolean property */
2819 		prop->data = NULL;
2820 		prop->next = p->list;
2821 		p->list = prop;
2822 		(void) mutex_unlock(&p->lock);
2823 		return (prop);
2824 	}
2825 
2826 	/*
2827 	 * retrieve the property value
2828 	 */
2829 	bzero(opp, OBP_MAXBUF);
2830 	opp->oprom_size = OBP_MAXPROPLEN;
2831 	(void) strcpy(opp->oprom_array, prom_prop_name);
2832 
2833 	if ((ioctl(p->fd, OPROMGETPROP, opp) < 0) ||
2834 	    (opp->oprom_size == (uint_t)-1)) {
2835 		/* error retrieving property value */
2836 		(void) mutex_unlock(&p->lock);
2837 		free(prop);
2838 		return (NULL);
2839 	}
2840 
2841 	/*
2842 	 * make a copy of the property value, stick in ph->list
2843 	 */
2844 	if ((prop->data = malloc(prop->len)) == NULL) {
2845 		(void) mutex_unlock(&p->lock);
2846 		free(prop);
2847 		return (NULL);
2848 	}
2849 
2850 	bcopy(opp->oprom_array, prop->data, prop->len);
2851 
2852 	prop->next = p->list;
2853 	p->list = prop;
2854 	(void) mutex_unlock(&p->lock);
2855 
2856 	return (prop);
2857 }
2858 
2859 int
2860 di_prom_prop_lookup_ints(di_prom_handle_t ph, di_node_t node,
2861 	const char *prom_prop_name, int **prom_prop_data)
2862 {
2863 	int len;
2864 	struct di_prom_prop *prop;
2865 
2866 	prop = di_prom_prop_lookup_common(ph, node, prom_prop_name);
2867 
2868 	if (prop == NULL) {
2869 		*prom_prop_data = NULL;
2870 		return (-1);
2871 	}
2872 
2873 	if (prop->len == 0) {	/* boolean property */
2874 		*prom_prop_data = NULL;
2875 		return (0);
2876 	}
2877 
2878 	len = di_prop_decode_common((void *)&prop->data, prop->len,
2879 	    DI_PROP_TYPE_INT, 1);
2880 	*prom_prop_data = (int *)((void *)prop->data);
2881 
2882 	return (len);
2883 }
2884 
2885 int
2886 di_prom_prop_lookup_strings(di_prom_handle_t ph, di_node_t node,
2887 	const char *prom_prop_name, char **prom_prop_data)
2888 {
2889 	int len;
2890 	struct di_prom_prop *prop;
2891 
2892 	prop = di_prom_prop_lookup_common(ph, node, prom_prop_name);
2893 
2894 	if (prop == NULL) {
2895 		*prom_prop_data = NULL;
2896 		return (-1);
2897 	}
2898 
2899 	if (prop->len == 0) {	/* boolean property */
2900 		*prom_prop_data = NULL;
2901 		return (0);
2902 	}
2903 
2904 	/*
2905 	 * Fix an openprom bug (OBP string not NULL terminated).
2906 	 * XXX This should really be fixed in promif.
2907 	 */
2908 	if (((char *)prop->data)[prop->len - 1] != '\0') {
2909 		uchar_t *tmp;
2910 		prop->len++;
2911 		if ((tmp = realloc(prop->data, prop->len)) == NULL)
2912 			return (-1);
2913 
2914 		prop->data = tmp;
2915 		((char *)prop->data)[prop->len - 1] = '\0';
2916 		DPRINTF((DI_INFO, "OBP string not NULL terminated: "
2917 		    "node=%s, prop=%s, val=%s\n",
2918 		    di_node_name(node), prom_prop_name, prop->data));
2919 	}
2920 
2921 	len = di_prop_decode_common((void *)&prop->data, prop->len,
2922 	    DI_PROP_TYPE_STRING, 1);
2923 	*prom_prop_data = (char *)prop->data;
2924 
2925 	return (len);
2926 }
2927 
2928 int
2929 di_prom_prop_lookup_bytes(di_prom_handle_t ph, di_node_t node,
2930 	const char *prom_prop_name, uchar_t **prom_prop_data)
2931 {
2932 	int len;
2933 	struct di_prom_prop *prop;
2934 
2935 	prop = di_prom_prop_lookup_common(ph, node, prom_prop_name);
2936 
2937 	if (prop == NULL) {
2938 		*prom_prop_data = NULL;
2939 		return (-1);
2940 	}
2941 
2942 	if (prop->len == 0) {	/* boolean property */
2943 		*prom_prop_data = NULL;
2944 		return (0);
2945 	}
2946 
2947 	len = di_prop_decode_common((void *)&prop->data, prop->len,
2948 	    DI_PROP_TYPE_BYTE, 1);
2949 	*prom_prop_data = prop->data;
2950 
2951 	return (len);
2952 }
2953 
2954 /*
2955  * returns an allocated array through <prop_data> only when its count > 0
2956  * and the number of entries (count) as the function return value;
2957  * use di_slot_names_free() to free the array
2958  */
2959 int
2960 di_prop_slot_names(di_prop_t prop, di_slot_name_t **prop_data)
2961 {
2962 	int rawlen, count;
2963 	uchar_t *rawdata;
2964 	char *nm = di_prop_name(prop);
2965 
2966 	if (nm == NULL || strcmp(DI_PROP_SLOT_NAMES, nm) != 0)
2967 		goto ERROUT;
2968 
2969 	rawlen = di_prop_rawdata(prop, &rawdata);
2970 	if (rawlen <= 0 || rawdata == NULL)
2971 		goto ERROUT;
2972 
2973 	count = di_slot_names_decode(rawdata, rawlen, prop_data);
2974 	if (count < 0 || *prop_data == NULL)
2975 		goto ERROUT;
2976 
2977 	return (count);
2978 	/*NOTREACHED*/
2979 ERROUT:
2980 	errno = EFAULT;
2981 	*prop_data = NULL;
2982 	return (-1);
2983 }
2984 
2985 int
2986 di_prop_lookup_slot_names(dev_t dev, di_node_t node,
2987     di_slot_name_t **prop_data)
2988 {
2989 	di_prop_t prop;
2990 
2991 	/*
2992 	 * change this if and when DI_PROP_TYPE_COMPOSITE is implemented
2993 	 * and slot-names is properly flagged as such
2994 	 */
2995 	if ((prop = di_prop_find(dev, node, DI_PROP_SLOT_NAMES)) ==
2996 	    DI_PROP_NIL) {
2997 		*prop_data = NULL;
2998 		return (-1);
2999 	}
3000 
3001 	return (di_prop_slot_names(prop, (void *)prop_data));
3002 }
3003 
3004 /*
3005  * returns an allocated array through <prop_data> only when its count > 0
3006  * and the number of entries (count) as the function return value;
3007  * use di_slot_names_free() to free the array
3008  */
3009 int
3010 di_prom_prop_slot_names(di_prom_prop_t prom_prop, di_slot_name_t **prop_data)
3011 {
3012 	int rawlen, count;
3013 	uchar_t *rawdata;
3014 
3015 	rawlen = di_prom_prop_data(prom_prop, &rawdata);
3016 	if (rawlen <= 0 || rawdata == NULL)
3017 		goto ERROUT;
3018 
3019 	count = di_slot_names_decode(rawdata, rawlen, prop_data);
3020 	if (count < 0 || *prop_data == NULL)
3021 		goto ERROUT;
3022 
3023 	return (count);
3024 	/*NOTREACHED*/
3025 ERROUT:
3026 	errno = EFAULT;
3027 	*prop_data = NULL;
3028 	return (-1);
3029 }
3030 
3031 int
3032 di_prom_prop_lookup_slot_names(di_prom_handle_t ph, di_node_t node,
3033     di_slot_name_t **prop_data)
3034 {
3035 	struct di_prom_prop *prom_prop;
3036 
3037 	prom_prop = di_prom_prop_lookup_common(ph, node, DI_PROP_SLOT_NAMES);
3038 	if (prom_prop == NULL) {
3039 		*prop_data = NULL;
3040 		return (-1);
3041 	}
3042 
3043 	return (di_prom_prop_slot_names(prom_prop, prop_data));
3044 }
3045 
3046 di_lnode_t
3047 di_link_to_lnode(di_link_t link, uint_t endpoint)
3048 {
3049 	struct di_all *di_all;
3050 
3051 	if ((link == DI_LINK_NIL) ||
3052 	    ((endpoint != DI_LINK_SRC) && (endpoint != DI_LINK_TGT))) {
3053 		errno = EINVAL;
3054 		return (DI_LNODE_NIL);
3055 	}
3056 
3057 	di_all = DI_ALL((caddr_t)link - DI_LINK(link)->self);
3058 
3059 	if (endpoint == DI_LINK_SRC) {
3060 		return (DI_LNODE((caddr_t)di_all + DI_LINK(link)->src_lnode));
3061 	} else {
3062 		return (DI_LNODE((caddr_t)di_all + DI_LINK(link)->tgt_lnode));
3063 	}
3064 	/* NOTREACHED */
3065 }
3066 
3067 char *
3068 di_lnode_name(di_lnode_t lnode)
3069 {
3070 	return (di_driver_name(di_lnode_devinfo(lnode)));
3071 }
3072 
3073 di_node_t
3074 di_lnode_devinfo(di_lnode_t lnode)
3075 {
3076 	struct di_all *di_all;
3077 
3078 	di_all = DI_ALL((caddr_t)lnode - DI_LNODE(lnode)->self);
3079 	return (DI_NODE((caddr_t)di_all + DI_LNODE(lnode)->node));
3080 }
3081 
3082 int
3083 di_lnode_devt(di_lnode_t lnode, dev_t *devt)
3084 {
3085 	if ((lnode == DI_LNODE_NIL) || (devt == NULL)) {
3086 		errno = EINVAL;
3087 		return (-1);
3088 	}
3089 	if ((DI_LNODE(lnode)->dev_major == (major_t)-1) &&
3090 	    (DI_LNODE(lnode)->dev_minor == (minor_t)-1))
3091 		return (-1);
3092 
3093 	*devt = makedev(DI_LNODE(lnode)->dev_major, DI_LNODE(lnode)->dev_minor);
3094 	return (0);
3095 }
3096 
3097 int
3098 di_link_spectype(di_link_t link)
3099 {
3100 	return (DI_LINK(link)->spec_type);
3101 }
3102 
3103 void
3104 di_minor_private_set(di_minor_t minor, void *data)
3105 {
3106 	DI_MINOR(minor)->user_private_data = (uintptr_t)data;
3107 }
3108 
3109 void *
3110 di_minor_private_get(di_minor_t minor)
3111 {
3112 	return ((void *)(uintptr_t)DI_MINOR(minor)->user_private_data);
3113 }
3114 
3115 void
3116 di_node_private_set(di_node_t node, void *data)
3117 {
3118 	DI_NODE(node)->user_private_data = (uintptr_t)data;
3119 }
3120 
3121 void *
3122 di_node_private_get(di_node_t node)
3123 {
3124 	return ((void *)(uintptr_t)DI_NODE(node)->user_private_data);
3125 }
3126 
3127 void
3128 di_path_private_set(di_path_t path, void *data)
3129 {
3130 	DI_PATH(path)->user_private_data = (uintptr_t)data;
3131 }
3132 
3133 void *
3134 di_path_private_get(di_path_t path)
3135 {
3136 	return ((void *)(uintptr_t)DI_PATH(path)->user_private_data);
3137 }
3138 
3139 void
3140 di_lnode_private_set(di_lnode_t lnode, void *data)
3141 {
3142 	DI_LNODE(lnode)->user_private_data = (uintptr_t)data;
3143 }
3144 
3145 void *
3146 di_lnode_private_get(di_lnode_t lnode)
3147 {
3148 	return ((void *)(uintptr_t)DI_LNODE(lnode)->user_private_data);
3149 }
3150 
3151 void
3152 di_link_private_set(di_link_t link, void *data)
3153 {
3154 	DI_LINK(link)->user_private_data = (uintptr_t)data;
3155 }
3156 
3157 void *
3158 di_link_private_get(di_link_t link)
3159 {
3160 	return ((void *)(uintptr_t)DI_LINK(link)->user_private_data);
3161 }
3162 
3163 di_lnode_t
3164 di_lnode_next(di_node_t node, di_lnode_t lnode)
3165 {
3166 	struct di_all *di_all;
3167 
3168 	/*
3169 	 * paranoid error checking
3170 	 */
3171 	if (node == DI_NODE_NIL) {
3172 		errno = EINVAL;
3173 		return (DI_LNODE_NIL);
3174 	}
3175 
3176 	di_all = DI_ALL((caddr_t)node - DI_NODE(node)->self);
3177 
3178 	if (lnode == DI_NODE_NIL) {
3179 		if (DI_NODE(node)->lnodes != NULL)
3180 			return (DI_LNODE((caddr_t)di_all +
3181 			    DI_NODE(node)->lnodes));
3182 	} else {
3183 		if (DI_LNODE(lnode)->node_next != NULL)
3184 			return (DI_LNODE((caddr_t)di_all +
3185 			    DI_LNODE(lnode)->node_next));
3186 	}
3187 
3188 	if (DINFOLYR & DI_ALL(di_all)->command)
3189 		errno = ENXIO;
3190 	else
3191 		errno = ENOTSUP;
3192 
3193 	return (DI_LNODE_NIL);
3194 }
3195 
3196 di_link_t
3197 di_link_next_by_node(di_node_t node, di_link_t link, uint_t endpoint)
3198 {
3199 	struct di_all *di_all;
3200 
3201 	/*
3202 	 * paranoid error checking
3203 	 */
3204 	if ((node == DI_NODE_NIL) ||
3205 	    ((endpoint != DI_LINK_SRC) && (endpoint != DI_LINK_TGT))) {
3206 		errno = EINVAL;
3207 		return (DI_LINK_NIL);
3208 	}
3209 
3210 	di_all = DI_ALL((caddr_t)node - DI_NODE(node)->self);
3211 
3212 	if (endpoint == DI_LINK_SRC) {
3213 		if (link == DI_LINK_NIL) {
3214 			if (DI_NODE(node)->src_links != NULL)
3215 				return (DI_LINK((caddr_t)di_all +
3216 				    DI_NODE(node)->src_links));
3217 		} else {
3218 			if (DI_LINK(link)->src_node_next != NULL)
3219 				return (DI_LINK((caddr_t)di_all +
3220 				    DI_LINK(link)->src_node_next));
3221 		}
3222 	} else {
3223 		if (link == DI_LINK_NIL) {
3224 			if (DI_NODE(node)->tgt_links != NULL)
3225 				return (DI_LINK((caddr_t)di_all +
3226 				    DI_NODE(node)->tgt_links));
3227 		} else {
3228 			if (DI_LINK(link)->tgt_node_next != NULL)
3229 				return (DI_LINK((caddr_t)di_all +
3230 				    DI_LINK(link)->tgt_node_next));
3231 		}
3232 	}
3233 
3234 	if (DINFOLYR & DI_ALL(di_all)->command)
3235 		errno = ENXIO;
3236 	else
3237 		errno = ENOTSUP;
3238 
3239 	return (DI_LINK_NIL);
3240 }
3241 
3242 di_link_t
3243 di_link_next_by_lnode(di_lnode_t lnode, di_link_t link, uint_t endpoint)
3244 {
3245 	struct di_all *di_all;
3246 
3247 	/*
3248 	 * paranoid error checking
3249 	 */
3250 	if ((lnode == DI_LNODE_NIL) ||
3251 	    ((endpoint != DI_LINK_SRC) && (endpoint != DI_LINK_TGT))) {
3252 		errno = EINVAL;
3253 		return (DI_LINK_NIL);
3254 	}
3255 
3256 	di_all = DI_ALL((caddr_t)lnode - DI_LNODE(lnode)->self);
3257 
3258 	if (endpoint == DI_LINK_SRC) {
3259 		if (link == DI_LINK_NIL) {
3260 			if (DI_LNODE(lnode)->link_out == NULL)
3261 				return (DI_LINK_NIL);
3262 			return (DI_LINK((caddr_t)di_all +
3263 			    DI_LNODE(lnode)->link_out));
3264 		} else {
3265 			if (DI_LINK(link)->src_link_next == NULL)
3266 				return (DI_LINK_NIL);
3267 			return (DI_LINK((caddr_t)di_all +
3268 			    DI_LINK(link)->src_link_next));
3269 		}
3270 	} else {
3271 		if (link == DI_LINK_NIL) {
3272 			if (DI_LNODE(lnode)->link_in == NULL)
3273 				return (DI_LINK_NIL);
3274 			return (DI_LINK((caddr_t)di_all +
3275 			    DI_LNODE(lnode)->link_in));
3276 		} else {
3277 			if (DI_LINK(link)->tgt_link_next == NULL)
3278 				return (DI_LINK_NIL);
3279 			return (DI_LINK((caddr_t)di_all +
3280 			    DI_LINK(link)->tgt_link_next));
3281 		}
3282 	}
3283 	/* NOTREACHED */
3284 }
3285 
3286 /*
3287  * Internal library function:
3288  *   Invoke callback for each link data on the link list of first node
3289  *   on node_list headp, and place children of first node on the list.
3290  *
3291  *   This is similar to walk_one_node, except we only walk in child
3292  *   first mode.
3293  */
3294 static void
3295 walk_one_link(struct node_list **headp, uint_t ep,
3296     void *arg, int (*callback)(di_link_t link, void *arg))
3297 {
3298 	int		action = DI_WALK_CONTINUE;
3299 	di_link_t	link = DI_LINK_NIL;
3300 	di_node_t	node = (*headp)->node;
3301 
3302 	while ((link = di_link_next_by_node(node, link, ep)) != DI_LINK_NIL) {
3303 		action = callback(link, arg);
3304 		if (action == DI_WALK_TERMINATE) {
3305 			break;
3306 		}
3307 	}
3308 
3309 	update_node_list(action, DI_WALK_LINKGEN, headp);
3310 }
3311 
3312 int
3313 di_walk_link(di_node_t root, uint_t flag, uint_t endpoint, void *arg,
3314     int (*link_callback)(di_link_t link, void *arg))
3315 {
3316 	struct node_list  *head;	/* node_list for tree walk */
3317 
3318 #ifdef DEBUG
3319 	char *devfspath = di_devfs_path(root);
3320 	DPRINTF((DI_INFO, "walking %s link data under %s\n",
3321 	    (endpoint == DI_LINK_SRC) ? "src" : "tgt", devfspath));
3322 	di_devfs_path_free(devfspath);
3323 #endif
3324 
3325 	/*
3326 	 * paranoid error checking
3327 	 */
3328 	if ((root == DI_NODE_NIL) || (link_callback == NULL) || (flag != 0) ||
3329 	    ((endpoint != DI_LINK_SRC) && (endpoint != DI_LINK_TGT))) {
3330 		errno = EINVAL;
3331 		return (-1);
3332 	}
3333 
3334 	if ((head = malloc(sizeof (struct node_list))) == NULL) {
3335 		DPRINTF((DI_ERR, "malloc of node_list failed\n"));
3336 		return (-1);
3337 	}
3338 
3339 	head->next = NULL;
3340 	head->node = root;
3341 
3342 	DPRINTF((DI_INFO, "Start link data walking from node %s\n",
3343 	    di_node_name(root)));
3344 
3345 	while (head != NULL)
3346 		walk_one_link(&head, endpoint, arg, link_callback);
3347 
3348 	return (0);
3349 }
3350 
3351 /*
3352  * Internal library function:
3353  *   Invoke callback for each link data on the link list of first node
3354  *   on node_list headp, and place children of first node on the list.
3355  *
3356  *   This is similar to walk_one_node, except we only walk in child
3357  *   first mode.
3358  */
3359 static void
3360 walk_one_lnode(struct node_list **headp, void *arg,
3361     int (*callback)(di_lnode_t lnode, void *arg))
3362 {
3363 	int		action = DI_WALK_CONTINUE;
3364 	di_lnode_t	lnode = DI_LNODE_NIL;
3365 	di_node_t	node = (*headp)->node;
3366 
3367 	while ((lnode = di_lnode_next(node, lnode)) != DI_LNODE_NIL) {
3368 		action = callback(lnode, arg);
3369 		if (action == DI_WALK_TERMINATE) {
3370 			break;
3371 		}
3372 	}
3373 
3374 	update_node_list(action, DI_WALK_LINKGEN, headp);
3375 }
3376 
3377 int
3378 di_walk_lnode(di_node_t root, uint_t flag, void *arg,
3379     int (*lnode_callback)(di_lnode_t lnode, void *arg))
3380 {
3381 	struct node_list  *head;	/* node_list for tree walk */
3382 
3383 #ifdef DEBUG
3384 	char *devfspath = di_devfs_path(root);
3385 	DPRINTF((DI_INFO, "walking lnode data under %s\n", devfspath));
3386 	di_devfs_path_free(devfspath);
3387 #endif
3388 
3389 	/*
3390 	 * paranoid error checking
3391 	 */
3392 	if ((root == DI_NODE_NIL) || (lnode_callback == NULL) || (flag != 0)) {
3393 		errno = EINVAL;
3394 		return (-1);
3395 	}
3396 
3397 	if ((head = malloc(sizeof (struct node_list))) == NULL) {
3398 		DPRINTF((DI_ERR, "malloc of node_list failed\n"));
3399 		return (-1);
3400 	}
3401 
3402 	head->next = NULL;
3403 	head->node = root;
3404 
3405 	DPRINTF((DI_INFO, "Start lnode data walking from node %s\n",
3406 	    di_node_name(root)));
3407 
3408 	while (head != NULL)
3409 		walk_one_lnode(&head, arg, lnode_callback);
3410 
3411 	return (0);
3412 }
3413 
3414 di_node_t
3415 di_lookup_node(di_node_t root, char *devfspath)
3416 {
3417 	struct di_all *dap;
3418 	di_node_t node;
3419 	char *copy, *slash, *pname, *paddr;
3420 
3421 	/*
3422 	 * Path must be absolute and musn't have duplicate slashes
3423 	 */
3424 	if (*devfspath != '/' || strstr(devfspath, "//")) {
3425 		DPRINTF((DI_ERR, "Invalid path: %s\n", devfspath));
3426 		return (DI_NODE_NIL);
3427 	}
3428 
3429 	if (root == DI_NODE_NIL) {
3430 		DPRINTF((DI_ERR, "root node is DI_NODE_NIL\n"));
3431 		return (DI_NODE_NIL);
3432 	}
3433 
3434 	dap = DI_ALL((caddr_t)root - DI_NODE(root)->self);
3435 	if (strcmp(dap->root_path, "/") != 0) {
3436 		DPRINTF((DI_ERR, "snapshot root not / : %s\n", dap->root_path));
3437 		return (DI_NODE_NIL);
3438 	}
3439 
3440 	if ((copy = strdup(devfspath)) == NULL) {
3441 		DPRINTF((DI_ERR, "strdup failed on: %s\n", devfspath));
3442 		return (DI_NODE_NIL);
3443 	}
3444 
3445 	for (slash = copy, node = root; slash; ) {
3446 
3447 		/*
3448 		 * Handle devfspath = "/" case as well as trailing '/'
3449 		 */
3450 		if (*(slash + 1) == '\0')
3451 			break;
3452 
3453 		/*
3454 		 * More path-components exist. Deal with the next one
3455 		 */
3456 		pname = slash + 1;
3457 		node = di_child_node(node);
3458 
3459 		if (slash = strchr(pname, '/'))
3460 			*slash = '\0';
3461 		if (paddr = strchr(pname, '@'))
3462 			*paddr++ = '\0';
3463 
3464 		for (; node != DI_NODE_NIL; node = di_sibling_node(node)) {
3465 			char *name, *baddr;
3466 
3467 			name = di_node_name(node);
3468 			baddr = di_bus_addr(node);
3469 
3470 			if (strcmp(pname, name) != 0)
3471 				continue;
3472 
3473 			/*
3474 			 * Mappings between a "path-address" and bus-addr
3475 			 *
3476 			 *	paddr		baddr
3477 			 *	---------------------
3478 			 *	NULL		NULL
3479 			 *	NULL		""
3480 			 *	""		N/A	(invalid paddr)
3481 			 */
3482 			if (paddr && baddr && strcmp(paddr, baddr) == 0)
3483 				break;
3484 			if (paddr == NULL && (baddr == NULL || *baddr == '\0'))
3485 				break;
3486 		}
3487 
3488 		/*
3489 		 * No nodes in the sibling list or there was no match
3490 		 */
3491 		if (node == DI_NODE_NIL) {
3492 			DPRINTF((DI_ERR, "%s@%s: no node\n", pname, paddr));
3493 			free(copy);
3494 			return (DI_NODE_NIL);
3495 		}
3496 	}
3497 
3498 	assert(node != DI_NODE_NIL);
3499 	free(copy);
3500 	return (node);
3501 }
3502 
3503 di_path_t
3504 di_lookup_path(di_node_t root, char *devfspath)
3505 {
3506 	di_node_t	phci_node;
3507 	di_path_t	path = DI_PATH_NIL;
3508 	char		*copy, *lastslash;
3509 	char		*pname, *paddr;
3510 	char		*path_name, *path_addr;
3511 
3512 	if ((copy = strdup(devfspath)) == NULL) {
3513 		DPRINTF((DI_ERR, "strdup failed on: %s\n", devfspath));
3514 		return (DI_NODE_NIL);
3515 	}
3516 
3517 	if ((lastslash = strrchr(copy, '/')) == NULL) {
3518 		DPRINTF((DI_ERR, "failed to find component: %s\n", devfspath));
3519 		goto out;
3520 	}
3521 
3522 	/* stop at pHCI and find the node for the phci */
3523 	*lastslash = '\0';
3524 	phci_node = di_lookup_node(root, copy);
3525 	if (phci_node == NULL) {
3526 		DPRINTF((DI_ERR, "failed to find component: %s\n", devfspath));
3527 		goto out;
3528 	}
3529 
3530 	/* set up pname and paddr for last component */
3531 	pname = lastslash + 1;
3532 	if ((paddr = strchr(pname, '@')) == NULL) {
3533 		DPRINTF((DI_ERR, "failed to find unit-addr: %s\n", devfspath));
3534 		goto out;
3535 	}
3536 	*paddr++ = '\0';
3537 
3538 	/* walk paths below phci looking for match */
3539 	for (path = di_path_phci_next_path(phci_node, DI_PATH_NIL);
3540 	    path != DI_PATH_NIL;
3541 	    path = di_path_phci_next_path(phci_node, path)) {
3542 
3543 		/* get name@addr of path */
3544 		path_name = di_path_node_name(path);
3545 		path_addr = di_path_bus_addr(path);
3546 		if ((path_name == NULL) || (path_addr == NULL))
3547 			continue;
3548 
3549 		/* break on match */
3550 		if ((strcmp(pname, path_name) == 0) &&
3551 		    (strcmp(paddr, path_addr) == 0))
3552 			break;
3553 	}
3554 
3555 out:	free(copy);
3556 	return (path);
3557 }
3558 
3559 static char *
3560 msglevel2str(di_debug_t msglevel)
3561 {
3562 	switch (msglevel) {
3563 		case DI_ERR:
3564 			return ("ERROR");
3565 		case DI_INFO:
3566 			return ("Info");
3567 		case DI_TRACE:
3568 			return ("Trace");
3569 		case DI_TRACE1:
3570 			return ("Trace1");
3571 		case DI_TRACE2:
3572 			return ("Trace2");
3573 		default:
3574 			return ("UNKNOWN");
3575 	}
3576 }
3577 
3578 void
3579 dprint(di_debug_t msglevel, const char *fmt, ...)
3580 {
3581 	va_list	ap;
3582 	char	*estr;
3583 
3584 	if (di_debug <= DI_QUIET)
3585 		return;
3586 
3587 	if (di_debug < msglevel)
3588 		return;
3589 
3590 	estr = msglevel2str(msglevel);
3591 
3592 	assert(estr);
3593 
3594 	va_start(ap, fmt);
3595 
3596 	(void) fprintf(stderr, "libdevinfo[%lu]: %s: ",
3597 	    (ulong_t)getpid(), estr);
3598 	(void) vfprintf(stderr, fmt, ap);
3599 
3600 	va_end(ap);
3601 }
3602 
3603 /* end of devinfo.c */
3604