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