xref: /illumos-gate/usr/src/cmd/prtconf/pdevinfo.c (revision 2a1fd0ffe121888d44fdec321c25b53dcfaa9118)
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  * Copyright (c) 2012, Joyent, Inc. All rights reserved.
27  */
28 /*
29  * Copyright (c) 2019 Peter Tribble.
30  */
31 
32 /*
33  * For machines that support the openprom, fetch and print the list
34  * of devices that the kernel has fetched from the prom or conjured up.
35  */
36 
37 #include <stdio.h>
38 #include <stdarg.h>
39 #include <stdlib.h>
40 #include <fcntl.h>
41 #include <ctype.h>
42 #include <strings.h>
43 #include <unistd.h>
44 #include <stropts.h>
45 #include <sys/types.h>
46 #include <sys/mkdev.h>
47 #include <sys/sunddi.h>
48 #include <sys/openpromio.h>
49 #include <sys/modctl.h>
50 #include <sys/stat.h>
51 #include <zone.h>
52 #include <libnvpair.h>
53 #include <pcidb.h>
54 #include "prtconf.h"
55 
56 
57 typedef char *(*dump_propname_t)(void *);
58 typedef int (*dump_proptype_t)(void *);
59 typedef int (*dump_propints_t)(void *, int **);
60 typedef int (*dump_propint64_t)(void *, int64_t **);
61 typedef int (*dump_propstrings_t)(void *, char **);
62 typedef int (*dump_propbytes_t)(void *, uchar_t **);
63 typedef int (*dump_proprawdata_t)(void *, uchar_t **);
64 
65 typedef struct dumpops_common {
66 	dump_propname_t doc_propname;
67 	dump_proptype_t doc_proptype;
68 	dump_propints_t doc_propints;
69 	dump_propint64_t doc_propint64;
70 	dump_propstrings_t doc_propstrings;
71 	dump_propbytes_t doc_propbytes;
72 	dump_proprawdata_t doc_proprawdata;
73 } dumpops_common_t;
74 
75 static const dumpops_common_t prop_dumpops = {
76 	(dump_propname_t)di_prop_name,
77 	(dump_proptype_t)di_prop_type,
78 	(dump_propints_t)di_prop_ints,
79 	(dump_propint64_t)di_prop_int64,
80 	(dump_propstrings_t)di_prop_strings,
81 	(dump_propbytes_t)di_prop_bytes,
82 	(dump_proprawdata_t)di_prop_rawdata
83 }, pathprop_common_dumpops = {
84 	(dump_propname_t)di_path_prop_name,
85 	(dump_proptype_t)di_path_prop_type,
86 	(dump_propints_t)di_path_prop_ints,
87 	(dump_propint64_t)di_path_prop_int64s,
88 	(dump_propstrings_t)di_path_prop_strings,
89 	(dump_propbytes_t)di_path_prop_bytes,
90 	(dump_proprawdata_t)di_path_prop_bytes
91 };
92 
93 typedef void *(*dump_nextprop_t)(void *, void *);
94 typedef dev_t (*dump_propdevt_t)(void *);
95 
96 typedef struct dumpops {
97 	const dumpops_common_t *dop_common;
98 	dump_nextprop_t dop_nextprop;
99 	dump_propdevt_t dop_propdevt;
100 } dumpops_t;
101 
102 typedef struct di_args {
103 	di_prom_handle_t	prom_hdl;
104 	di_devlink_handle_t	devlink_hdl;
105 	pcidb_hdl_t 		*pcidb_hdl;
106 } di_arg_t;
107 
108 static const dumpops_t sysprop_dumpops = {
109 	&prop_dumpops,
110 	(dump_nextprop_t)di_prop_sys_next,
111 	NULL
112 }, globprop_dumpops = {
113 	&prop_dumpops,
114 	(dump_nextprop_t)di_prop_global_next,
115 	NULL
116 }, drvprop_dumpops = {
117 	&prop_dumpops,
118 	(dump_nextprop_t)di_prop_drv_next,
119 	(dump_propdevt_t)di_prop_devt
120 }, hwprop_dumpops = {
121 	&prop_dumpops,
122 	(dump_nextprop_t)di_prop_hw_next,
123 	NULL
124 }, pathprop_dumpops = {
125 	&pathprop_common_dumpops,
126 	(dump_nextprop_t)di_path_prop_next,
127 	NULL
128 };
129 
130 #define	PROPNAME(ops) (ops->dop_common->doc_propname)
131 #define	PROPTYPE(ops) (ops->dop_common->doc_proptype)
132 #define	PROPINTS(ops) (ops->dop_common->doc_propints)
133 #define	PROPINT64(ops) (ops->dop_common->doc_propint64)
134 #define	PROPSTRINGS(ops) (ops->dop_common->doc_propstrings)
135 #define	PROPBYTES(ops) (ops->dop_common->doc_propbytes)
136 #define	PROPRAWDATA(ops) (ops->dop_common->doc_proprawdata)
137 #define	NEXTPROP(ops) (ops->dop_nextprop)
138 #define	PROPDEVT(ops) (ops->dop_propdevt)
139 #define	NUM_ELEMENTS(A) (sizeof (A) / sizeof (A[0]))
140 
141 static int prop_type_guess(const dumpops_t *, void *, void **, int *);
142 static void walk_driver(di_node_t, di_arg_t *);
143 static int dump_devs(di_node_t, void *);
144 static int dump_prop_list(const dumpops_t *, const char *,
145 				int, void *, dev_t, int *);
146 static int _error(const char *, ...);
147 static int is_openprom();
148 static void walk(uchar_t *, uint_t, int);
149 static void dump_node(nvlist_t *, int);
150 static void dump_prodinfo(di_prom_handle_t, di_node_t, const char **,
151 				char *, int);
152 static di_node_t find_node_by_name(di_prom_handle_t, di_node_t, char *);
153 static int get_propval_by_name(di_prom_handle_t, di_node_t,
154 				const char *, uchar_t **);
155 static int dump_compatible(char *, int, di_node_t);
156 static void dump_pathing_data(int, di_node_t);
157 static void dump_minor_data(int, di_node_t, di_devlink_handle_t);
158 static void dump_link_data(int, di_node_t, di_devlink_handle_t);
159 static int print_composite_string(const char *, char *, int);
160 static void print_one(nvpair_t *, int);
161 static int unprintable(char *, int);
162 static int promopen(int);
163 static void promclose();
164 static di_node_t find_target_node(di_node_t);
165 static void node_display_set(di_node_t);
166 static int dump_pciid(char *, int, di_node_t, pcidb_hdl_t *);
167 
168 void
169 prtconf_devinfo(void)
170 {
171 	struct di_priv_data	fetch;
172 	di_arg_t		di_arg;
173 	di_prom_handle_t	prom_hdl = DI_PROM_HANDLE_NIL;
174 	di_devlink_handle_t	devlink_hdl = NULL;
175 	pcidb_hdl_t		*pcidb_hdl = NULL;
176 	di_node_t		root_node;
177 	uint_t			flag;
178 	char			*rootpath;
179 
180 	dprintf("verbosemode %s\n", opts.o_verbose ? "on" : "off");
181 
182 	/* determine what info we need to get from kernel */
183 	flag = DINFOSUBTREE;
184 	rootpath = "/";
185 
186 	if (opts.o_target) {
187 		flag |= (DINFOMINOR | DINFOPATH);
188 	}
189 
190 	if (opts.o_pciid) {
191 		flag |= DINFOPROP;
192 		if ((prom_hdl = di_prom_init()) == DI_PROM_HANDLE_NIL)
193 			exit(_error("di_prom_init() failed."));
194 	}
195 
196 	if (opts.o_forcecache) {
197 		if (dbg.d_forceload) {
198 			exit(_error(NULL, "option combination not supported"));
199 		}
200 		if (strcmp(rootpath, "/") != 0) {
201 			exit(_error(NULL, "invalid root path for option"));
202 		}
203 		flag = DINFOCACHE;
204 	} else if (opts.o_verbose) {
205 		flag |= (DINFOPROP | DINFOMINOR |
206 		    DINFOPRIVDATA | DINFOPATH | DINFOLYR);
207 	}
208 
209 	if (dbg.d_forceload) {
210 		flag |= DINFOFORCE;
211 	}
212 
213 	if (opts.o_verbose) {
214 		init_priv_data(&fetch);
215 		root_node = di_init_impl(rootpath, flag, &fetch);
216 
217 		/* get devlink (aka aliases) data */
218 		if ((devlink_hdl = di_devlink_init(NULL, 0)) == NULL)
219 			exit(_error("di_devlink_init() failed."));
220 	} else
221 		root_node = di_init(rootpath, flag);
222 
223 	if (root_node == DI_NODE_NIL) {
224 		(void) _error(NULL, "devinfo facility not available");
225 		/* not an error if this isn't the global zone */
226 		if (getzoneid() == GLOBAL_ZONEID)
227 			exit(-1);
228 		else
229 			exit(0);
230 	}
231 
232 	if (opts.o_verbose || opts.o_pciid) {
233 		pcidb_hdl = pcidb_open(PCIDB_VERSION);
234 		if (pcidb_hdl == NULL)
235 			(void) _error(NULL, "pcidb facility not available, "
236 			    "continuing anyways");
237 	}
238 
239 	di_arg.prom_hdl = prom_hdl;
240 	di_arg.devlink_hdl = devlink_hdl;
241 	di_arg.pcidb_hdl = pcidb_hdl;
242 
243 	/*
244 	 * ...and walk all nodes to report them out...
245 	 */
246 	if (dbg.d_bydriver) {
247 		opts.o_target = 0;
248 		walk_driver(root_node, &di_arg);
249 		if (prom_hdl != DI_PROM_HANDLE_NIL)
250 			di_prom_fini(prom_hdl);
251 		if (devlink_hdl != NULL)
252 			(void) di_devlink_fini(&devlink_hdl);
253 		di_fini(root_node);
254 		return;
255 	}
256 
257 	if (opts.o_target) {
258 		di_node_t target_node, node;
259 
260 		target_node = find_target_node(root_node);
261 		if (target_node == DI_NODE_NIL) {
262 			(void) fprintf(stderr, "%s: "
263 			    "invalid device path specified\n",
264 			    opts.o_progname);
265 			exit(1);
266 		}
267 
268 		/* mark the target node so we display it */
269 		node_display_set(target_node);
270 
271 		if (opts.o_ancestors) {
272 			/*
273 			 * mark the ancestors of this node so we display
274 			 * them as well
275 			 */
276 			node = target_node;
277 			while (node = di_parent_node(node))
278 				node_display_set(node);
279 		} else {
280 			/*
281 			 * when we display device tree nodes the indentation
282 			 * level is based off of tree depth.
283 			 *
284 			 * here we increment o_target to reflect the
285 			 * depth of the target node in the tree.  we do
286 			 * this so that when we calculate the indentation
287 			 * level we can subtract o_target so that the
288 			 * target node starts with an indentation of zero.
289 			 */
290 			node = target_node;
291 			while (node = di_parent_node(node))
292 				opts.o_target++;
293 		}
294 
295 		if (opts.o_children) {
296 			/*
297 			 * mark the children of this node so we display
298 			 * them as well
299 			 */
300 			(void) di_walk_node(target_node, DI_WALK_CLDFIRST,
301 			    (void *)1,
302 			    (int (*)(di_node_t, void *))
303 			    node_display_set);
304 		}
305 	}
306 
307 	(void) di_walk_node(root_node, DI_WALK_CLDFIRST, &di_arg,
308 	    dump_devs);
309 
310 	if (prom_hdl != DI_PROM_HANDLE_NIL)
311 		di_prom_fini(prom_hdl);
312 	if (devlink_hdl != NULL)
313 		(void) di_devlink_fini(&devlink_hdl);
314 	if (pcidb_hdl != NULL)
315 		pcidb_close(pcidb_hdl);
316 	di_fini(root_node);
317 }
318 
319 /*
320  * utility routines
321  */
322 static int
323 i_find_target_node(di_node_t node, void *arg)
324 {
325 	di_node_t *target = (di_node_t *)arg;
326 
327 	if (opts.o_devices_path != NULL) {
328 		char *path;
329 
330 		if ((path = di_devfs_path(node)) == NULL)
331 			exit(_error("failed to allocate memory"));
332 
333 		if (strcmp(opts.o_devices_path, path) == 0) {
334 			di_devfs_path_free(path);
335 			*target = node;
336 			return (DI_WALK_TERMINATE);
337 		}
338 
339 		di_devfs_path_free(path);
340 	} else if (opts.o_devt != DDI_DEV_T_NONE) {
341 		di_minor_t	minor = DI_MINOR_NIL;
342 
343 		while ((minor = di_minor_next(node, minor)) != DI_MINOR_NIL) {
344 			if (opts.o_devt == di_minor_devt(minor)) {
345 				*target = node;
346 				return (DI_WALK_TERMINATE);
347 			}
348 		}
349 	} else {
350 		/* we should never get here */
351 		exit(_error(NULL, "internal error"));
352 	}
353 	return (DI_WALK_CONTINUE);
354 }
355 
356 static di_node_t
357 find_target_node(di_node_t root_node)
358 {
359 	di_node_t target = DI_NODE_NIL;
360 
361 	/* special case to allow displaying of the root node */
362 	if (opts.o_devices_path != NULL) {
363 		if (strlen(opts.o_devices_path) == 0)
364 			return (root_node);
365 		if (strcmp(opts.o_devices_path, ".") == 0)
366 			return (root_node);
367 	}
368 
369 	(void) di_walk_node(root_node, DI_WALK_CLDFIRST, &target,
370 	    i_find_target_node);
371 	return (target);
372 }
373 
374 #define	NODE_DISPLAY		(1<<0)
375 
376 static long
377 node_display(di_node_t node)
378 {
379 	long data = (long)di_node_private_get(node);
380 	return (data & NODE_DISPLAY);
381 }
382 
383 static void
384 node_display_set(di_node_t node)
385 {
386 	long data = (long)di_node_private_get(node);
387 	data |= NODE_DISPLAY;
388 	di_node_private_set(node, (void *)data);
389 }
390 
391 #define	LNODE_DISPLAYED		(1<<0)
392 
393 static long
394 lnode_displayed(di_lnode_t lnode)
395 {
396 	long data = (long)di_lnode_private_get(lnode);
397 	return (data & LNODE_DISPLAYED);
398 }
399 
400 static void
401 lnode_displayed_set(di_lnode_t lnode)
402 {
403 	long data = (long)di_lnode_private_get(lnode);
404 	data |= LNODE_DISPLAYED;
405 	di_lnode_private_set(lnode, (void *)data);
406 }
407 
408 static void
409 lnode_displayed_clear(di_lnode_t lnode)
410 {
411 	long data = (long)di_lnode_private_get(lnode);
412 	data &= ~LNODE_DISPLAYED;
413 	di_lnode_private_set(lnode, (void *)data);
414 }
415 
416 #define	MINOR_DISPLAYED		(1<<0)
417 #define	MINOR_PTR		(~(0x3))
418 
419 static long
420 minor_displayed(di_minor_t minor)
421 {
422 	long data = (long)di_minor_private_get(minor);
423 	return (data & MINOR_DISPLAYED);
424 }
425 
426 static void
427 minor_displayed_set(di_minor_t minor)
428 {
429 	long data = (long)di_minor_private_get(minor);
430 	data |= MINOR_DISPLAYED;
431 	di_minor_private_set(minor, (void *)data);
432 }
433 
434 static void
435 minor_displayed_clear(di_minor_t minor)
436 {
437 	long data = (long)di_minor_private_get(minor);
438 	data &= ~MINOR_DISPLAYED;
439 	di_minor_private_set(minor, (void *)data);
440 }
441 
442 static void *
443 minor_ptr(di_minor_t minor)
444 {
445 	long data = (long)di_minor_private_get(minor);
446 	return ((void *)(data & MINOR_PTR));
447 }
448 
449 static void
450 minor_ptr_set(di_minor_t minor, void *ptr)
451 {
452 	long data = (long)di_minor_private_get(minor);
453 	data = (data & ~MINOR_PTR) | (((long)ptr) & MINOR_PTR);
454 	di_minor_private_set(minor, (void *)data);
455 }
456 
457 /*
458  * In this comment typed properties are those of type DI_PROP_TYPE_UNDEF_IT,
459  * DI_PROP_TYPE_BOOLEAN, DI_PROP_TYPE_INT, DI_PROP_TYPE_INT64,
460  * DI_PROP_TYPE_BYTE, and DI_PROP_TYPE_STRING.
461  *
462  * The guessing algorithm is:
463  * 1. If the property is typed and the type is consistent with the value of
464  *    the property, then the property is of that type. If the type is not
465  *    consistent with value of the property, then the type is treated as
466  *    alien to prtconf.
467  * 2. If the property is of type DI_PROP_TYPE_UNKNOWN the following steps
468  *    are carried out.
469  *    a. If the value of the property is consistent with a string property,
470  *       the type of the property is DI_PROP_TYPE_STRING.
471  *    b. Otherwise, if the value of the property is consistent with an integer
472  *       property, the type of the property is DI_PROP_TYPE_INT.
473  *    c. Otherwise, the property type is treated as alien to prtconf.
474  * 3. If the property type is alien to prtconf, then the property value is
475  *    read by the appropriate routine for untyped properties and the following
476  *    steps are carried out.
477  *    a. If the length that the property routine returned is zero, the
478  *       property is of type DI_PROP_TYPE_BOOLEAN.
479  *    b. Otherwise, if the length that the property routine returned is
480  *       positive, then the property value is treated as raw data of type
481  *       DI_PROP_TYPE_UNKNOWN.
482  *    c. Otherwise, if the length that the property routine returned is
483  *       negative, then there is some internal inconsistency and this is
484  *       treated as an error and no type is determined.
485  */
486 static int
487 prop_type_guess(const dumpops_t *propops, void *prop, void **prop_data,
488     int *prop_type)
489 {
490 	int len, type;
491 
492 	type = PROPTYPE(propops)(prop);
493 	switch (type) {
494 	case DI_PROP_TYPE_UNDEF_IT:
495 	case DI_PROP_TYPE_BOOLEAN:
496 		*prop_data = NULL;
497 		*prop_type = type;
498 		return (0);
499 	case DI_PROP_TYPE_INT:
500 		len = PROPINTS(propops)(prop, (int **)prop_data);
501 		break;
502 	case DI_PROP_TYPE_INT64:
503 		len = PROPINT64(propops)(prop, (int64_t **)prop_data);
504 		break;
505 	case DI_PROP_TYPE_BYTE:
506 		len = PROPBYTES(propops)(prop, (uchar_t **)prop_data);
507 		break;
508 	case DI_PROP_TYPE_STRING:
509 		len = PROPSTRINGS(propops)(prop, (char **)prop_data);
510 		break;
511 	case DI_PROP_TYPE_UNKNOWN:
512 		len = PROPSTRINGS(propops)(prop, (char **)prop_data);
513 		if ((len > 0) && ((*(char **)prop_data)[0] != 0)) {
514 			*prop_type = DI_PROP_TYPE_STRING;
515 			return (len);
516 		}
517 
518 		len = PROPINTS(propops)(prop, (int **)prop_data);
519 		type = DI_PROP_TYPE_INT;
520 
521 		break;
522 	default:
523 		len = -1;
524 	}
525 
526 	if (len > 0) {
527 		*prop_type = type;
528 		return (len);
529 	}
530 
531 	len = PROPRAWDATA(propops)(prop, (uchar_t **)prop_data);
532 	if (len < 0) {
533 		return (-1);
534 	} else if (len == 0) {
535 		*prop_type = DI_PROP_TYPE_BOOLEAN;
536 		return (0);
537 	}
538 
539 	*prop_type = DI_PROP_TYPE_UNKNOWN;
540 	return (len);
541 }
542 
543 /*
544  * Returns 0 if nothing is printed, 1 otherwise
545  */
546 static int
547 dump_prop_list(const dumpops_t *dumpops, const char *name, int ilev,
548     void *node, dev_t dev, int *compat_printed)
549 {
550 	void		*prop = DI_PROP_NIL, *prop_data;
551 	di_minor_t	minor;
552 	char		*p;
553 	int		i, prop_type, nitems;
554 	dev_t		pdev;
555 	int		nprop = 0;
556 
557 	if (compat_printed)
558 		*compat_printed = 0;
559 
560 	while ((prop = NEXTPROP(dumpops)(node, prop)) != DI_PROP_NIL) {
561 
562 		/* Skip properties a dev_t oriented caller is not requesting */
563 		if (PROPDEVT(dumpops)) {
564 			pdev = PROPDEVT(dumpops)(prop);
565 
566 			if (dev == DDI_DEV_T_ANY) {
567 				/*
568 				 * Caller requesting print all properties
569 				 */
570 				goto print;
571 			} else if (dev == DDI_DEV_T_NONE) {
572 				/*
573 				 * Caller requesting print of properties
574 				 * associated with devinfo (not minor).
575 				 */
576 				if ((pdev == DDI_DEV_T_ANY) ||
577 				    (pdev == DDI_DEV_T_NONE))
578 					goto print;
579 
580 				/*
581 				 * Property has a minor association, see if
582 				 * we have a minor with this dev_t. If there
583 				 * is no such minor we print the property now
584 				 * so it gets displayed.
585 				 */
586 				minor = DI_MINOR_NIL;
587 				while ((minor = di_minor_next((di_node_t)node,
588 				    minor)) != DI_MINOR_NIL) {
589 					if (di_minor_devt(minor) == pdev)
590 						break;
591 				}
592 				if (minor == DI_MINOR_NIL)
593 					goto print;
594 			} else if (dev == pdev) {
595 				/*
596 				 * Caller requesting print of properties
597 				 * associated with a specific matching minor
598 				 * node.
599 				 */
600 				goto print;
601 			}
602 
603 			/* otherwise skip print */
604 			continue;
605 		}
606 
607 print:		nitems = prop_type_guess(dumpops, prop, &prop_data, &prop_type);
608 		if (nitems < 0)
609 			continue;
610 
611 		if (nprop == 0) {
612 			if (name) {
613 				indent_to_level(ilev);
614 				(void) printf("%s properties:\n", name);
615 			}
616 			ilev++;
617 		}
618 		nprop++;
619 
620 		indent_to_level(ilev);
621 		(void) printf("name='%s' type=", PROPNAME(dumpops)(prop));
622 
623 		/* report 'compatible' as processed */
624 		if (compat_printed &&
625 		    (strcmp(PROPNAME(dumpops)(prop), "compatible") == 0))
626 			*compat_printed = 1;
627 
628 		switch (prop_type) {
629 		case DI_PROP_TYPE_UNDEF_IT:
630 			(void) printf("undef");
631 			break;
632 		case DI_PROP_TYPE_BOOLEAN:
633 			(void) printf("boolean");
634 			break;
635 		case DI_PROP_TYPE_INT:
636 			(void) printf("int");
637 			break;
638 		case DI_PROP_TYPE_INT64:
639 			(void) printf("int64");
640 			break;
641 		case DI_PROP_TYPE_BYTE:
642 			(void) printf("byte");
643 			break;
644 		case DI_PROP_TYPE_STRING:
645 			(void) printf("string");
646 			break;
647 		case DI_PROP_TYPE_UNKNOWN:
648 			(void) printf("unknown");
649 			break;
650 		default:
651 			/* Should never be here */
652 			(void) printf("0x%x", prop_type);
653 		}
654 
655 		if (nitems != 0)
656 			(void) printf(" items=%i", nitems);
657 
658 		/* print the major and minor numbers for a device property */
659 		if (PROPDEVT(dumpops)) {
660 			if ((pdev == DDI_DEV_T_NONE) ||
661 			    (pdev == DDI_DEV_T_ANY)) {
662 				(void) printf(" dev=none");
663 			} else {
664 				(void) printf(" dev=(%u,%u)",
665 				    (uint_t)major(pdev), (uint_t)minor(pdev));
666 			}
667 		}
668 
669 		(void) putchar('\n');
670 
671 		if (nitems == 0)
672 			continue;
673 
674 		indent_to_level(ilev);
675 
676 		(void) printf("    value=");
677 
678 		switch (prop_type) {
679 		case DI_PROP_TYPE_INT:
680 			for (i = 0; i < nitems - 1; i++)
681 				(void) printf("%8.8x.", ((int *)prop_data)[i]);
682 			(void) printf("%8.8x", ((int *)prop_data)[i]);
683 			break;
684 		case DI_PROP_TYPE_INT64:
685 			for (i = 0; i < nitems - 1; i++)
686 				(void) printf("%16.16llx.",
687 				    ((long long *)prop_data)[i]);
688 			(void) printf("%16.16llx", ((long long *)prop_data)[i]);
689 			break;
690 		case DI_PROP_TYPE_STRING:
691 			p = (char *)prop_data;
692 			for (i = 0; i < nitems - 1; i++) {
693 				(void) printf("'%s' + ", p);
694 				p += strlen(p) + 1;
695 			}
696 			(void) printf("'%s'", p);
697 			break;
698 		default:
699 			for (i = 0; i < nitems - 1; i++)
700 				(void) printf("%2.2x.",
701 				    ((uint8_t *)prop_data)[i]);
702 			(void) printf("%2.2x", ((uint8_t *)prop_data)[i]);
703 		}
704 
705 		(void) putchar('\n');
706 	}
707 
708 	return (nprop ? 1 : 0);
709 }
710 
711 /*
712  * walk_driver is a debugging facility.
713  */
714 static void
715 walk_driver(di_node_t root, di_arg_t *di_arg)
716 {
717 	di_node_t node;
718 
719 	node = di_drv_first_node(dbg.d_drivername, root);
720 
721 	while (node != DI_NODE_NIL) {
722 		(void) dump_devs(node, di_arg);
723 		node = di_drv_next_node(node);
724 	}
725 }
726 
727 /*
728  * print out information about this node, returns appropriate code.
729  */
730 /*ARGSUSED1*/
731 static int
732 dump_devs(di_node_t node, void *arg)
733 {
734 	di_arg_t		*di_arg = arg;
735 	di_devlink_handle_t	devlink_hdl = di_arg->devlink_hdl;
736 	int			ilev = 0;	/* indentation level */
737 	char			*driver_name;
738 	di_node_t		root_node, tmp;
739 	int			compat_printed;
740 	int			printed;
741 
742 	if (dbg.d_debug) {
743 		char *path = di_devfs_path(node);
744 		dprintf("Dump node %s\n", path);
745 		di_devfs_path_free(path);
746 	}
747 
748 	if (dbg.d_bydriver) {
749 		ilev = 1;
750 	} else {
751 		/* figure out indentation level */
752 		tmp = node;
753 		while ((tmp = di_parent_node(tmp)) != DI_NODE_NIL)
754 			ilev++;
755 
756 		if (opts.o_target && !opts.o_ancestors) {
757 			ilev -= opts.o_target - 1;
758 		}
759 	}
760 
761 	if (opts.o_target && !node_display(node)) {
762 		/*
763 		 * if we're only displaying certain nodes and this one
764 		 * isn't flagged, skip it.
765 		 */
766 		return (DI_WALK_CONTINUE);
767 	}
768 
769 	indent_to_level(ilev);
770 
771 	(void) printf("%s", di_node_name(node));
772 	if (opts.o_pciid)
773 		(void) print_pciid(node, di_arg->prom_hdl, di_arg->pcidb_hdl);
774 
775 	/*
776 	 * if this node does not have an instance number or is the
777 	 * root node (1229946), we don't print an instance number
778 	 */
779 	root_node = tmp = node;
780 	while ((tmp = di_parent_node(tmp)) != DI_NODE_NIL)
781 		root_node = tmp;
782 	if ((di_instance(node) >= 0) && (node != root_node))
783 		(void) printf(", instance #%d", di_instance(node));
784 
785 	if (opts.o_drv_name) {
786 		driver_name = di_driver_name(node);
787 		if (driver_name != NULL)
788 			(void) printf(" (driver name: %s)", driver_name);
789 	} else if (di_retired(node)) {
790 		(void) printf(" (retired)");
791 	} else if (di_state(node) & DI_DRIVER_DETACHED)
792 		(void) printf(" (driver not attached)");
793 	(void) printf("\n");
794 
795 	if (opts.o_verbose)  {
796 		if (dump_prop_list(&sysprop_dumpops, "System", ilev + 1,
797 		    node, DDI_DEV_T_ANY, NULL)) {
798 			(void) dump_prop_list(&globprop_dumpops, NULL, ilev + 1,
799 			    node, DDI_DEV_T_ANY, NULL);
800 		} else {
801 			(void) dump_prop_list(&globprop_dumpops,
802 			    "System software", ilev + 1,
803 			    node, DDI_DEV_T_ANY, NULL);
804 		}
805 		(void) dump_prop_list(&drvprop_dumpops, "Driver", ilev + 1,
806 		    node, DDI_DEV_T_NONE, NULL);
807 
808 		printed = dump_prop_list(&hwprop_dumpops, "Hardware",
809 		    ilev + 1, node, DDI_DEV_T_ANY, &compat_printed);
810 
811 		/* Ensure that 'compatible' is printed under Hardware header */
812 		if (!compat_printed)
813 			printed |= dump_compatible(printed ? NULL : "Hardware",
814 			    ilev + 1, node);
815 
816 		/* Ensure that pci id information is printed under Hardware */
817 		(void) dump_pciid(printed ? NULL : "Hardware",
818 		    ilev + 1, node, di_arg->pcidb_hdl);
819 
820 		dump_priv_data(ilev + 1, node);
821 		dump_pathing_data(ilev + 1, node);
822 		dump_link_data(ilev + 1, node, devlink_hdl);
823 		dump_minor_data(ilev + 1, node, devlink_hdl);
824 	}
825 
826 	if (opts.o_target)
827 		return (DI_WALK_CONTINUE);
828 
829 	if (!opts.o_pseudodevs && (strcmp(di_node_name(node), "pseudo") == 0))
830 		return (DI_WALK_PRUNECHILD);
831 
832 	return (DI_WALK_CONTINUE);
833 }
834 
835 /* _error([no_perror, ] fmt [, arg ...]) */
836 static int
837 _error(const char *opt_noperror, ...)
838 {
839 	int saved_errno;
840 	va_list ap;
841 	int no_perror = 0;
842 	const char *fmt;
843 
844 	saved_errno = errno;
845 
846 	(void) fprintf(stderr, "%s: ", opts.o_progname);
847 
848 	va_start(ap, opt_noperror);
849 	if (opt_noperror == NULL) {
850 		no_perror = 1;
851 		fmt = va_arg(ap, char *);
852 	} else
853 		fmt = opt_noperror;
854 	(void) vfprintf(stderr, fmt, ap);
855 	va_end(ap);
856 
857 	if (no_perror)
858 		(void) fprintf(stderr, "\n");
859 	else {
860 		(void) fprintf(stderr, ": ");
861 		errno = saved_errno;
862 		perror("");
863 	}
864 
865 	return (-1);
866 }
867 
868 
869 /*
870  * The rest of the routines handle printing the raw prom devinfo (-p option).
871  *
872  * 128 is the size of the largest (currently) property name
873  * 16k - MAXNAMESZ - sizeof (int) is the size of the largest
874  * (currently) property value that is allowed.
875  * the sizeof (uint_t) is from struct openpromio
876  */
877 
878 #define	MAXNAMESZ	128
879 #define	MAXVALSIZE	(16384 - MAXNAMESZ - sizeof (uint_t))
880 #define	BUFSIZE		(MAXNAMESZ + MAXVALSIZE + sizeof (uint_t))
881 typedef union {
882 	char buf[BUFSIZE];
883 	struct openpromio opp;
884 } Oppbuf;
885 
886 static int prom_fd;
887 static uchar_t *prom_snapshot;
888 
889 static int
890 is_openprom(void)
891 {
892 	Oppbuf	oppbuf;
893 	struct openpromio *opp = &(oppbuf.opp);
894 	unsigned int i;
895 
896 	opp->oprom_size = MAXVALSIZE;
897 	if (ioctl(prom_fd, OPROMGETCONS, opp) < 0)
898 		exit(_error("OPROMGETCONS"));
899 
900 	i = (unsigned int)((unsigned char)opp->oprom_array[0]);
901 	return ((i & OPROMCONS_OPENPROM) == OPROMCONS_OPENPROM);
902 }
903 
904 int
905 do_prominfo(void)
906 {
907 	uint_t arg = opts.o_verbose;
908 
909 	if (promopen(O_RDONLY))  {
910 		exit(_error("openeepr device open failed"));
911 	}
912 
913 	if (is_openprom() == 0)  {
914 		(void) fprintf(stderr, "System architecture does not "
915 		    "support this option of this command.\n");
916 		return (1);
917 	}
918 
919 	/* OPROMSNAPSHOT returns size in arg */
920 	if (ioctl(prom_fd, OPROMSNAPSHOT, &arg) < 0)
921 		exit(_error("OPROMSNAPSHOT"));
922 
923 	if (arg == 0)
924 		return (1);
925 
926 	if ((prom_snapshot = malloc(arg)) == NULL)
927 		exit(_error("failed to allocate memory"));
928 
929 	/* copy out the snapshot for printing */
930 	/*LINTED*/
931 	*(uint_t *)prom_snapshot = arg;
932 	if (ioctl(prom_fd, OPROMCOPYOUT, prom_snapshot) < 0)
933 		exit(_error("OPROMCOPYOUT"));
934 
935 	promclose();
936 
937 	/* print out information */
938 	walk(prom_snapshot, arg, 0);
939 	free(prom_snapshot);
940 
941 	return (0);
942 }
943 
944 static void
945 walk(uchar_t *buf, uint_t size, int level)
946 {
947 	int error;
948 	nvlist_t *nvl, *cnvl;
949 	nvpair_t *child = NULL;
950 	uchar_t *cbuf = NULL;
951 	uint_t csize;
952 
953 	/* Expand to an nvlist */
954 	if (nvlist_unpack((char *)buf, size, &nvl, 0))
955 		exit(_error("error processing snapshot"));
956 
957 	/* print current node */
958 	dump_node(nvl, level);
959 
960 	/* print children */
961 	error = nvlist_lookup_byte_array(nvl, "@child", &cbuf, &csize);
962 	if ((error == ENOENT) || (cbuf == NULL))
963 		return;		/* no child exists */
964 
965 	if (error || nvlist_unpack((char *)cbuf, csize, &cnvl, 0))
966 		exit(_error("error processing snapshot"));
967 
968 	while (child = nvlist_next_nvpair(cnvl, child)) {
969 		char *name = nvpair_name(child);
970 		data_type_t type = nvpair_type(child);
971 		uchar_t *nodebuf;
972 		uint_t nodesize;
973 		if (strcmp("node", name) != 0) {
974 			dprintf("unexpected nvpair name %s != name\n", name);
975 			continue;
976 		}
977 		if (type != DATA_TYPE_BYTE_ARRAY) {
978 			dprintf("unexpected nvpair type %d, not byte array \n",
979 			    type);
980 			continue;
981 		}
982 
983 		(void) nvpair_value_byte_array(child,
984 		    (uchar_t **)&nodebuf, &nodesize);
985 		walk(nodebuf, nodesize, level + 1);
986 	}
987 
988 	nvlist_free(nvl);
989 }
990 
991 /*
992  * Print all properties and values
993  */
994 static void
995 dump_node(nvlist_t *nvl, int level)
996 {
997 	int id = 0;
998 	char *name = NULL;
999 	nvpair_t *nvp = NULL;
1000 
1001 	indent_to_level(level);
1002 	(void) printf("Node");
1003 	if (!opts.o_verbose) {
1004 		if (nvlist_lookup_string(nvl, "name", &name))
1005 			(void) printf("data not available");
1006 		else
1007 			(void) printf(" '%s'", name);
1008 		(void) putchar('\n');
1009 		return;
1010 	}
1011 	(void) nvlist_lookup_int32(nvl, "@nodeid", &id);
1012 	(void) printf(" %#08x\n", id);
1013 
1014 	while (nvp = nvlist_next_nvpair(nvl, nvp)) {
1015 		name = nvpair_name(nvp);
1016 		if (name[0] == '@')
1017 			continue;
1018 
1019 		print_one(nvp, level + 1);
1020 	}
1021 	(void) putchar('\n');
1022 }
1023 
1024 static const char *
1025 path_state_name(di_path_state_t st)
1026 {
1027 	switch (st) {
1028 		case DI_PATH_STATE_ONLINE:
1029 			return ("online");
1030 		case DI_PATH_STATE_STANDBY:
1031 			return ("standby");
1032 		case DI_PATH_STATE_OFFLINE:
1033 			return ("offline");
1034 		case DI_PATH_STATE_FAULT:
1035 			return ("faulted");
1036 	}
1037 	return ("unknown");
1038 }
1039 
1040 /*
1041  * Print all phci's each client is connected to.
1042  */
1043 static void
1044 dump_pathing_data(int ilev, di_node_t node)
1045 {
1046 	di_path_t	pi = DI_PATH_NIL;
1047 	di_node_t	phci_node;
1048 	char		*phci_path;
1049 	int		path_instance;
1050 	int		firsttime = 1;
1051 
1052 	if (node == DI_PATH_NIL)
1053 		return;
1054 
1055 	while ((pi = di_path_client_next_path(node, pi)) != DI_PATH_NIL) {
1056 
1057 		/* It is not really a path if we failed to capture the pHCI */
1058 		phci_node = di_path_phci_node(pi);
1059 		if (phci_node == DI_NODE_NIL)
1060 			continue;
1061 
1062 		/* Print header for the first path */
1063 		if (firsttime) {
1064 			indent_to_level(ilev);
1065 			firsttime = 0;
1066 			ilev++;
1067 			(void) printf("Paths from multipath bus adapters:\n");
1068 		}
1069 
1070 		/*
1071 		 * Print the path instance and full "pathinfo" path, which is
1072 		 * the same as the /devices devifo path had the device been
1073 		 * enumerated under pHCI.
1074 		 */
1075 		phci_path = di_devfs_path(phci_node);
1076 		if (phci_path) {
1077 			path_instance = di_path_instance(pi);
1078 			if (path_instance > 0) {
1079 				indent_to_level(ilev);
1080 				(void) printf("Path %d: %s/%s@%s\n",
1081 				    path_instance, phci_path,
1082 				    di_node_name(node),
1083 				    di_path_bus_addr(pi));
1084 			}
1085 			di_devfs_path_free(phci_path);
1086 		}
1087 
1088 		/* print phci driver, instance, and path state information */
1089 		indent_to_level(ilev);
1090 		(void) printf("%s#%d (%s)\n", di_driver_name(phci_node),
1091 		    di_instance(phci_node), path_state_name(di_path_state(pi)));
1092 
1093 		(void) dump_prop_list(&pathprop_dumpops, NULL, ilev + 1,
1094 		    pi, DDI_DEV_T_ANY, NULL);
1095 	}
1096 }
1097 
1098 static int
1099 dump_minor_data_links(di_devlink_t devlink, void *arg)
1100 {
1101 	int ilev = (intptr_t)arg;
1102 	indent_to_level(ilev);
1103 	(void) printf("dev_link=%s\n", di_devlink_path(devlink));
1104 	return (DI_WALK_CONTINUE);
1105 }
1106 
1107 static void
1108 dump_minor_data_paths(int ilev, di_minor_t minor,
1109     di_devlink_handle_t devlink_hdl)
1110 {
1111 	char	*path, *type;
1112 	int	spec_type;
1113 
1114 	/* get the path to the device and the minor node name */
1115 	if ((path = di_devfs_minor_path(minor)) == NULL)
1116 		exit(_error("failed to allocate memory"));
1117 
1118 	/* display the path to this minor node */
1119 	indent_to_level(ilev);
1120 	(void) printf("dev_path=%s\n", path);
1121 
1122 	if (devlink_hdl != NULL) {
1123 
1124 		/* get the device minor node information */
1125 		spec_type = di_minor_spectype(minor);
1126 		switch (di_minor_type(minor)) {
1127 			case DDM_MINOR:
1128 				type = "minor";
1129 				break;
1130 			case DDM_ALIAS:
1131 				type = "alias";
1132 				break;
1133 			case DDM_DEFAULT:
1134 				type = "default";
1135 				break;
1136 			case DDM_INTERNAL_PATH:
1137 				type = "internal";
1138 				break;
1139 			default:
1140 				type = "unknown";
1141 				break;
1142 		}
1143 
1144 		/* display the device minor node information */
1145 		indent_to_level(ilev + 1);
1146 		(void) printf("spectype=%s type=%s\n",
1147 		    (spec_type == S_IFBLK) ? "blk" : "chr", type);
1148 
1149 		/* display all the devlinks for this device minor node */
1150 		(void) di_devlink_walk(devlink_hdl, NULL, path,
1151 		    0, (void *)(intptr_t)(ilev + 1), dump_minor_data_links);
1152 	}
1153 
1154 	di_devfs_path_free(path);
1155 }
1156 
1157 static void
1158 create_minor_list(di_node_t node)
1159 {
1160 	di_minor_t	minor, minor_head, minor_tail, minor_prev, minor_walk;
1161 	int		major;
1162 
1163 	/* if there are no minor nodes, bail */
1164 	if (di_minor_next(node, DI_MINOR_NIL) == DI_MINOR_NIL)
1165 		return;
1166 
1167 	/*
1168 	 * here we want to create lists of minor nodes with the same
1169 	 * dev_t.  to do this we first sort all the minor nodes by devt.
1170 	 *
1171 	 * the algorithm used here is a bubble sort, so performance sucks.
1172 	 * but it's probably ok here because most device instances don't
1173 	 * have that many minor nodes.  also we're doing this as we're
1174 	 * displaying each node so it doesn't look like we're pausing
1175 	 * output for a long time.
1176 	 */
1177 	major = di_driver_major(node);
1178 	minor_head = minor_tail = minor = DI_MINOR_NIL;
1179 	while ((minor = di_minor_next(node, minor)) != DI_MINOR_NIL) {
1180 		dev_t	dev = di_minor_devt(minor);
1181 
1182 		/* skip /pseudo/clone@0 minor nodes */
1183 		if (major != major(dev))
1184 			continue;
1185 
1186 		minor_ptr_set(minor, DI_MINOR_NIL);
1187 		if (minor_head == DI_MINOR_NIL) {
1188 			/* this is the first minor node we're looking at */
1189 			minor_head = minor_tail = minor;
1190 			continue;
1191 		}
1192 
1193 		/*
1194 		 * if the new dev is less than the old dev, update minor_head
1195 		 * so it points to the beginning of the list.  ie it points
1196 		 * to the node with the lowest dev value
1197 		 */
1198 		if (dev <= di_minor_devt(minor_head)) {
1199 			minor_ptr_set(minor, minor_head);
1200 			minor_head = minor;
1201 			continue;
1202 		}
1203 
1204 		minor_prev = minor_head;
1205 		minor_walk = minor_ptr(minor_head);
1206 		while ((minor_walk != DI_MINOR_NIL) &&
1207 		    (dev > di_minor_devt(minor_walk))) {
1208 			minor_prev = minor_walk;
1209 			minor_walk = minor_ptr(minor_walk);
1210 		}
1211 		minor_ptr_set(minor, minor_walk);
1212 		minor_ptr_set(minor_prev, minor);
1213 		if (minor_walk == NULL)
1214 			minor_tail = minor;
1215 	}
1216 
1217 	/* check if there were any non /pseudo/clone@0 nodes.  if not, bail */
1218 	if (minor_head == DI_MINOR_NIL)
1219 		return;
1220 
1221 	/*
1222 	 * now that we have a list of minor nodes sorted by devt
1223 	 * we walk through the list and break apart the entire list
1224 	 * to create circular lists of minor nodes with matching devts.
1225 	 */
1226 	minor_prev = minor_head;
1227 	minor_walk = minor_ptr(minor_head);
1228 	while (minor_walk != DI_MINOR_NIL) {
1229 		if (di_minor_devt(minor_prev) != di_minor_devt(minor_walk)) {
1230 			minor_ptr_set(minor_prev, minor_head);
1231 			minor_head = minor_walk;
1232 		}
1233 		minor_prev = minor_walk;
1234 		minor_walk = minor_ptr(minor_walk);
1235 	}
1236 	minor_ptr_set(minor_tail, minor_head);
1237 }
1238 
1239 static void
1240 link_lnode_disp(di_link_t link, uint_t endpoint, int ilev,
1241     di_devlink_handle_t devlink_hdl)
1242 {
1243 	di_lnode_t	lnode;
1244 	char		*name, *path;
1245 	int		displayed_path, spec_type;
1246 	di_node_t	node = DI_NODE_NIL;
1247 	dev_t		devt = DDI_DEV_T_NONE;
1248 
1249 	lnode = di_link_to_lnode(link, endpoint);
1250 
1251 	indent_to_level(ilev);
1252 	name = di_lnode_name(lnode);
1253 	spec_type = di_link_spectype(link);
1254 
1255 	(void) printf("mod=%s", name);
1256 
1257 	/*
1258 	 * if we're displaying the source of a link, we should display
1259 	 * the target access mode.  (either block or char.)
1260 	 */
1261 	if (endpoint == DI_LINK_SRC)
1262 		(void) printf(" accesstype=%s",
1263 		    (spec_type == S_IFBLK) ? "blk" : "chr");
1264 
1265 	/*
1266 	 * check if the lnode is bound to a specific device
1267 	 * minor node (i.e.  if it's bound to a dev_t) and
1268 	 * if so display the dev_t value and any possible
1269 	 * minor node pathing information.
1270 	 */
1271 	displayed_path = 0;
1272 	if (di_lnode_devt(lnode, &devt) == 0) {
1273 		di_minor_t	minor = DI_MINOR_NIL;
1274 
1275 		(void) printf(" dev=(%u,%u)\n",
1276 		    (uint_t)major(devt), (uint_t)minor(devt));
1277 
1278 		/* display paths to the src devt minor node */
1279 		while (minor = di_minor_next(node, minor)) {
1280 			if (devt != di_minor_devt(minor))
1281 				continue;
1282 
1283 			if ((endpoint == DI_LINK_TGT) &&
1284 			    (spec_type != di_minor_spectype(minor)))
1285 				continue;
1286 
1287 			dump_minor_data_paths(ilev + 1, minor, devlink_hdl);
1288 			displayed_path = 1;
1289 		}
1290 	} else {
1291 		(void) printf("\n");
1292 	}
1293 
1294 	if (displayed_path)
1295 		return;
1296 
1297 	/*
1298 	 * This device lnode is not did not have any minor node
1299 	 * pathing information so display the path to device node.
1300 	 */
1301 	node = di_lnode_devinfo(lnode);
1302 	if ((path = di_devfs_path(node)) == NULL)
1303 		exit(_error("failed to allocate memory"));
1304 
1305 	indent_to_level(ilev + 1);
1306 	(void) printf("dev_path=%s\n", path);
1307 	di_devfs_path_free(path);
1308 }
1309 
1310 static void
1311 dump_minor_link_data(int ilev, di_node_t node, dev_t devt,
1312     di_devlink_handle_t devlink_hdl)
1313 {
1314 	int		first = 1;
1315 	di_link_t	link;
1316 
1317 	link = DI_LINK_NIL;
1318 	while (link = di_link_next_by_node(node, link, DI_LINK_TGT)) {
1319 		di_lnode_t	tgt_lnode;
1320 		dev_t		tgt_devt = DDI_DEV_T_NONE;
1321 
1322 		tgt_lnode = di_link_to_lnode(link, DI_LINK_TGT);
1323 
1324 		if (di_lnode_devt(tgt_lnode, &tgt_devt) != 0)
1325 			continue;
1326 
1327 		if (devt != tgt_devt)
1328 			continue;
1329 
1330 		if (first) {
1331 			first = 0;
1332 			indent_to_level(ilev);
1333 			(void) printf("Device Minor Layered Under:\n");
1334 		}
1335 
1336 		/* displayed this lnode */
1337 		lnode_displayed_set(tgt_lnode);
1338 		link_lnode_disp(link, DI_LINK_SRC, ilev + 1, devlink_hdl);
1339 	}
1340 
1341 	link = DI_LINK_NIL;
1342 	while (link = di_link_next_by_node(node, link, DI_LINK_SRC)) {
1343 		di_lnode_t	src_lnode;
1344 		dev_t		src_devt = DDI_DEV_T_NONE;
1345 
1346 		src_lnode = di_link_to_lnode(link, DI_LINK_SRC);
1347 
1348 		if (di_lnode_devt(src_lnode, &src_devt) != 0)
1349 			continue;
1350 
1351 		if (devt != src_devt)
1352 			continue;
1353 
1354 		if (first) {
1355 			first = 0;
1356 			indent_to_level(ilev);
1357 			(void) printf("Device Minor Layered Over:\n");
1358 		}
1359 
1360 		/* displayed this lnode */
1361 		lnode_displayed_set(src_lnode);
1362 		link_lnode_disp(link, DI_LINK_TGT, ilev + 1, devlink_hdl);
1363 	}
1364 }
1365 
1366 static void
1367 dump_minor_data(int ilev, di_node_t node, di_devlink_handle_t devlink_hdl)
1368 {
1369 	di_minor_t	minor, minor_next;
1370 	di_lnode_t	lnode;
1371 	di_link_t	link;
1372 	int		major, firstminor = 1;
1373 
1374 	/*
1375 	 * first go through and mark all lnodes and minor nodes for this
1376 	 * node as undisplayed
1377 	 */
1378 	lnode = DI_LNODE_NIL;
1379 	while (lnode = di_lnode_next(node, lnode))
1380 		lnode_displayed_clear(lnode);
1381 	minor = DI_MINOR_NIL;
1382 	while (minor = di_minor_next(node, minor)) {
1383 		minor_displayed_clear(minor);
1384 	}
1385 
1386 	/*
1387 	 * when we display the minor nodes we want to coalesce nodes
1388 	 * that have the same dev_t.  we do this by creating circular
1389 	 * lists of minor nodes with the same devt.
1390 	 */
1391 	create_minor_list(node);
1392 
1393 	/* now we display the driver defined minor nodes */
1394 	major = di_driver_major(node);
1395 	minor = DI_MINOR_NIL;
1396 	while ((minor = di_minor_next(node, minor)) != DI_MINOR_NIL) {
1397 		dev_t	devt;
1398 
1399 		/*
1400 		 * skip /pseudo/clone@0 minor nodes.
1401 		 * these are only created for DLPIv2 network devices.
1402 		 * since these minor nodes are associated with a driver
1403 		 * and are only bound to a device instance after they
1404 		 * are opened and attached we don't print them out
1405 		 * here.
1406 		 */
1407 		devt = di_minor_devt(minor);
1408 		if (major != major(devt))
1409 			continue;
1410 
1411 		/* skip nodes that may have already been displayed */
1412 		if (minor_displayed(minor))
1413 			continue;
1414 
1415 		if (firstminor) {
1416 			firstminor = 0;
1417 			indent_to_level(ilev++);
1418 			(void) printf("Device Minor Nodes:\n");
1419 		}
1420 
1421 		/* display the device minor node information */
1422 		indent_to_level(ilev);
1423 		(void) printf("dev=(%u,%u)\n",
1424 		    (uint_t)major(devt), (uint_t)minor(devt));
1425 
1426 		minor_next = minor;
1427 		do {
1428 			/* display device minor node path info */
1429 			minor_displayed_set(minor_next);
1430 			dump_minor_data_paths(ilev + 1, minor_next,
1431 			    devlink_hdl);
1432 
1433 			/* get a pointer to the next node */
1434 			minor_next = minor_ptr(minor_next);
1435 		} while (minor_next != minor);
1436 
1437 		/* display who has this device minor node open */
1438 		dump_minor_link_data(ilev + 1, node, devt, devlink_hdl);
1439 
1440 		/* display properties associated with this devt */
1441 		(void) dump_prop_list(&drvprop_dumpops, "Minor",
1442 		    ilev + 1, node, devt, NULL);
1443 	}
1444 
1445 	/*
1446 	 * now go through all the target lnodes for this node and
1447 	 * if they haven't yet been displayed, display them now.
1448 	 *
1449 	 * this happens in the case of clone opens when an "official"
1450 	 * minor node does not exist for the opened devt
1451 	 */
1452 	link = DI_LINK_NIL;
1453 	while (link = di_link_next_by_node(node, link, DI_LINK_TGT)) {
1454 		dev_t		devt;
1455 
1456 		lnode = di_link_to_lnode(link, DI_LINK_TGT);
1457 
1458 		/* if we've already displayed this target lnode, skip it */
1459 		if (lnode_displayed(lnode))
1460 			continue;
1461 
1462 		if (firstminor) {
1463 			firstminor = 0;
1464 			indent_to_level(ilev++);
1465 			(void) printf("Device Minor Nodes:\n");
1466 		}
1467 
1468 		/* display the device minor node information */
1469 		indent_to_level(ilev);
1470 		(void) di_lnode_devt(lnode, &devt);
1471 		(void) printf("dev=(%u,%u)\n",
1472 		    (uint_t)major(devt), (uint_t)minor(devt));
1473 
1474 		indent_to_level(ilev + 1);
1475 		(void) printf("dev_path=<clone>\n");
1476 
1477 		/* display who has this cloned device minor node open */
1478 		dump_minor_link_data(ilev + 1, node, devt, devlink_hdl);
1479 
1480 		/* mark node as displayed */
1481 		lnode_displayed_set(lnode);
1482 	}
1483 }
1484 
1485 static void
1486 dump_link_data(int ilev, di_node_t node, di_devlink_handle_t devlink_hdl)
1487 {
1488 	int		first = 1;
1489 	di_link_t	link;
1490 
1491 	link = DI_LINK_NIL;
1492 	while (link = di_link_next_by_node(node, link, DI_LINK_SRC)) {
1493 		di_lnode_t	src_lnode;
1494 		dev_t		src_devt = DDI_DEV_T_NONE;
1495 
1496 		src_lnode = di_link_to_lnode(link, DI_LINK_SRC);
1497 
1498 		/*
1499 		 * here we only want to print out layering information
1500 		 * if we are the source and our source lnode is not
1501 		 * associated with any particular dev_t.  (which means
1502 		 * we won't display this link while dumping minor node
1503 		 * info.)
1504 		 */
1505 		if (di_lnode_devt(src_lnode, &src_devt) != -1)
1506 			continue;
1507 
1508 		if (first) {
1509 			first = 0;
1510 			indent_to_level(ilev);
1511 			(void) printf("Device Layered Over:\n");
1512 		}
1513 
1514 		/* displayed this lnode */
1515 		link_lnode_disp(link, DI_LINK_TGT, ilev + 1, devlink_hdl);
1516 	}
1517 }
1518 
1519 /*
1520  * certain 'known' property names may contain 'composite' strings.
1521  * Handle them here, and print them as 'string1' + 'string2' ...
1522  */
1523 static int
1524 print_composite_string(const char *var, char *value, int size)
1525 {
1526 	char *p, *q;
1527 	char *firstp;
1528 
1529 	if ((strcmp(var, "version") != 0) &&
1530 	    (strcmp(var, "compatible") != 0))
1531 		return (0);	/* Not a known composite string */
1532 
1533 	/*
1534 	 * Verify that each string in the composite string is non-NULL,
1535 	 * is within the bounds of the property length, and contains
1536 	 * printable characters or white space. Otherwise let the
1537 	 * caller deal with it.
1538 	 */
1539 	for (firstp = p = value; p < (value + size); p += strlen(p) + 1) {
1540 		if (strlen(p) == 0)
1541 			return (0);		/* NULL string */
1542 		for (q = p; *q; q++) {
1543 			if (!(isascii(*q) && (isprint(*q) || isspace(*q))))
1544 				return (0);	/* Not printable or space */
1545 		}
1546 		if (q > (firstp + size))
1547 			return (0);		/* Out of bounds */
1548 	}
1549 
1550 	for (firstp = p = value; p < (value + size); p += strlen(p) + 1) {
1551 		if (p == firstp)
1552 			(void) printf("'%s'", p);
1553 		else
1554 			(void) printf(" + '%s'", p);
1555 	}
1556 	(void) putchar('\n');
1557 	return (1);
1558 }
1559 
1560 /*
1561  * Print one property and its value. Handle the verbose case.
1562  */
1563 static void
1564 print_one(nvpair_t *nvp, int level)
1565 {
1566 	int i;
1567 	int endswap = 0;
1568 	uint_t valsize;
1569 	char *value;
1570 	char *var = nvpair_name(nvp);
1571 
1572 	indent_to_level(level);
1573 	(void) printf("%s: ", var);
1574 
1575 	switch (nvpair_type(nvp)) {
1576 	case DATA_TYPE_BOOLEAN:
1577 		(void) printf(" \n");
1578 		return;
1579 	case DATA_TYPE_BYTE_ARRAY:
1580 		if (nvpair_value_byte_array(nvp, (uchar_t **)&value,
1581 		    &valsize)) {
1582 			(void) printf("data not available.\n");
1583 			return;
1584 		}
1585 		valsize--;	/* take out null added by driver */
1586 
1587 		/*
1588 		 * Do not print valsize > MAXVALSIZE, to be compatible
1589 		 * with old behavior. E.g. intel's eisa-nvram property
1590 		 * has a size of 65 K.
1591 		 */
1592 		if (valsize > MAXVALSIZE) {
1593 			(void) printf(" \n");
1594 			return;
1595 		}
1596 		break;
1597 	default:
1598 		(void) printf("data type unexpected.\n");
1599 		return;
1600 	}
1601 
1602 	/*
1603 	 * Handle printing verbosely
1604 	 */
1605 	if (print_composite_string(var, value, valsize)) {
1606 		return;
1607 	}
1608 
1609 	if (!unprintable(value, valsize)) {
1610 		(void) printf(" '%s'\n", value);
1611 		return;
1612 	}
1613 
1614 	(void) printf(" ");
1615 #ifdef	__x86
1616 	/*
1617 	 * Due to backwards compatibility constraints x86 int
1618 	 * properties are not in big-endian (ieee 1275) byte order.
1619 	 * If we have a property that is a multiple of 4 bytes,
1620 	 * let's assume it is an array of ints and print the bytes
1621 	 * in little endian order to make things look nicer for
1622 	 * the user.
1623 	 */
1624 	endswap = (valsize % 4) == 0;
1625 #endif	/* __x86 */
1626 	for (i = 0; i < valsize; i++) {
1627 		int out;
1628 		if (i && (i % 4 == 0))
1629 			(void) putchar('.');
1630 		if (endswap)
1631 			out = value[i + (3 - 2 * (i % 4))] & 0xff;
1632 		else
1633 			out = value[i] & 0xff;
1634 
1635 		(void) printf("%02x", out);
1636 	}
1637 	(void) putchar('\n');
1638 }
1639 
1640 static int
1641 unprintable(char *value, int size)
1642 {
1643 	int i;
1644 
1645 	/*
1646 	 * Is this just a zero?
1647 	 */
1648 	if (size == 0 || value[0] == '\0')
1649 		return (1);
1650 	/*
1651 	 * If any character is unprintable, or if a null appears
1652 	 * anywhere except at the end of a string, the whole
1653 	 * property is "unprintable".
1654 	 */
1655 	for (i = 0; i < size; ++i) {
1656 		if (value[i] == '\0')
1657 			return (i != (size - 1));
1658 		if (!isascii(value[i]) || iscntrl(value[i]))
1659 			return (1);
1660 	}
1661 	return (0);
1662 }
1663 
1664 static int
1665 promopen(int oflag)
1666 {
1667 	for (;;)  {
1668 		if ((prom_fd = open(opts.o_promdev, oflag)) < 0)  {
1669 			if (errno == EAGAIN)   {
1670 				(void) sleep(5);
1671 				continue;
1672 			}
1673 			if (errno == ENXIO)
1674 				return (-1);
1675 			if (getzoneid() == GLOBAL_ZONEID) {
1676 				_exit(_error("cannot open %s",
1677 				    opts.o_promdev));
1678 			}
1679 			/* not an error if this isn't the global zone */
1680 			(void) _error(NULL, "openprom facility not available");
1681 			exit(0);
1682 		} else
1683 			return (0);
1684 	}
1685 }
1686 
1687 static void
1688 promclose(void)
1689 {
1690 	if (close(prom_fd) < 0)
1691 		exit(_error("close error on %s", opts.o_promdev));
1692 }
1693 
1694 /*
1695  * Get and print the name of the frame buffer device.
1696  */
1697 int
1698 do_fbname(void)
1699 {
1700 	int	retval;
1701 	char fbuf_path[MAXPATHLEN];
1702 
1703 	retval =  modctl(MODGETFBNAME, (caddr_t)fbuf_path);
1704 
1705 	if (retval == 0) {
1706 		(void) printf("%s\n", fbuf_path);
1707 	} else {
1708 		if (retval == EFAULT) {
1709 			(void) fprintf(stderr,
1710 			"Error copying fb path to userland\n");
1711 		} else {
1712 			(void) fprintf(stderr,
1713 			"Console output device is not a frame buffer\n");
1714 		}
1715 		return (1);
1716 	}
1717 	return (0);
1718 }
1719 
1720 /*
1721  * Get and print the PROM version.
1722  */
1723 int
1724 do_promversion(void)
1725 {
1726 	Oppbuf	oppbuf;
1727 	struct openpromio *opp = &(oppbuf.opp);
1728 
1729 	if (promopen(O_RDONLY))  {
1730 		(void) fprintf(stderr, "Cannot open openprom device\n");
1731 		return (1);
1732 	}
1733 
1734 	opp->oprom_size = MAXVALSIZE;
1735 	if (ioctl(prom_fd, OPROMGETVERSION, opp) < 0)
1736 		exit(_error("OPROMGETVERSION"));
1737 
1738 	(void) printf("%s\n", opp->oprom_array);
1739 	promclose();
1740 	return (0);
1741 }
1742 
1743 int
1744 do_productinfo(void)
1745 {
1746 	di_node_t root, next_node;
1747 	di_prom_handle_t promh;
1748 	static const char *root_prop[] = { "name", "model", "banner-name",
1749 					"compatible" };
1750 	static const char *root_propv[] = { "name", "model", "banner-name",
1751 					"compatible", "idprom" };
1752 	static const char *oprom_prop[] = { "model", "version" };
1753 
1754 
1755 	root = di_init("/", DINFOCPYALL);
1756 
1757 	if (root == DI_NODE_NIL) {
1758 		(void) fprintf(stderr, "di_init() failed\n");
1759 		return (1);
1760 	}
1761 
1762 	promh = di_prom_init();
1763 
1764 	if (promh == DI_PROM_HANDLE_NIL) {
1765 		(void) fprintf(stderr, "di_prom_init() failed\n");
1766 		return (1);
1767 	}
1768 
1769 	if (opts.o_verbose) {
1770 		dump_prodinfo(promh, root, root_propv, "root",
1771 		    NUM_ELEMENTS(root_propv));
1772 
1773 		/* Get model and version properties under node "openprom" */
1774 		next_node = find_node_by_name(promh, root, "openprom");
1775 		if (next_node != DI_NODE_NIL)
1776 			dump_prodinfo(promh, next_node, oprom_prop,
1777 			    "openprom", NUM_ELEMENTS(oprom_prop));
1778 
1779 	} else
1780 		dump_prodinfo(promh, root, root_prop, "root",
1781 		    NUM_ELEMENTS(root_prop));
1782 	di_prom_fini(promh);
1783 	di_fini(root);
1784 	return (0);
1785 }
1786 
1787 di_node_t
1788 find_node_by_name(di_prom_handle_t promh, di_node_t parent,
1789     char *node_name)
1790 {
1791 	di_node_t next_node;
1792 	uchar_t *prop_valp;
1793 
1794 	for (next_node = di_child_node(parent); next_node != DI_NODE_NIL;
1795 	    next_node = di_sibling_node(next_node)) {
1796 		int len;
1797 
1798 		len = get_propval_by_name(promh, next_node, "name", &prop_valp);
1799 		if ((len != -1) && (strcmp((char *)prop_valp, node_name) == 0))
1800 			return (next_node);
1801 	}
1802 	return (DI_NODE_NIL);
1803 }
1804 
1805 
1806 int
1807 get_propval_by_name(di_prom_handle_t promh, di_node_t node, const char *name,
1808     uchar_t **valp)
1809 {
1810 	int len;
1811 	uchar_t *bufp;
1812 
1813 	len = di_prom_prop_lookup_bytes(promh, node, name,
1814 	    (uchar_t **)&bufp);
1815 	if (len != -1) {
1816 		*valp = (uchar_t *)malloc(len);
1817 		(void) memcpy(*valp, bufp, len);
1818 	}
1819 	return (len);
1820 }
1821 
1822 
1823 static void
1824 dump_prodinfo(di_prom_handle_t promh, di_node_t node, const char **propstr,
1825     char *node_name, int num)
1826 {
1827 	int out, len, index1, index, endswap = 0;
1828 	uchar_t *prop_valp;
1829 
1830 	for (index1 = 0; index1 < num; index1++) {
1831 		len = get_propval_by_name(promh, node, propstr[index1],
1832 		    &prop_valp);
1833 		if (len != -1) {
1834 			if (strcmp(node_name, "root"))
1835 				(void) printf("%s ", node_name);
1836 
1837 			(void) printf("%s: ", propstr[index1]);
1838 
1839 			if (print_composite_string((const char *)
1840 			    propstr[index1], (char *)prop_valp, len)) {
1841 				free(prop_valp);
1842 				continue;
1843 			}
1844 
1845 			if (!unprintable((char *)prop_valp, len)) {
1846 				(void) printf(" %s\n", (char *)prop_valp);
1847 				free(prop_valp);
1848 				continue;
1849 			}
1850 
1851 			(void) printf(" ");
1852 #ifdef  __x86
1853 			endswap = (len % 4) == 0;
1854 #endif  /* __x86 */
1855 			for (index = 0; index < len; index++) {
1856 				if (index && (index % 4 == 0))
1857 					(void) putchar('.');
1858 				if (endswap)
1859 					out = prop_valp[index +
1860 					    (3 - 2 * (index % 4))] & 0xff;
1861 				else
1862 					out = prop_valp[index] & 0xff;
1863 				(void) printf("%02x", out);
1864 			}
1865 			(void) putchar('\n');
1866 			free(prop_valp);
1867 		}
1868 	}
1869 }
1870 
1871 static int
1872 dump_compatible(char *name, int ilev, di_node_t node)
1873 {
1874 	int	ncompat;
1875 	char	*compat_array;
1876 	char	*p, *q;
1877 	int	i;
1878 
1879 	if (node == DI_PATH_NIL)
1880 		return (0);
1881 
1882 	ncompat = di_compatible_names(node, &compat_array);
1883 	if (ncompat <= 0)
1884 		return (0);	/* no 'compatible' available */
1885 
1886 	/* verify integrety of compat_array */
1887 	for (i = 0, p = compat_array; i < ncompat; i++, p += strlen(p) + 1) {
1888 		if (strlen(p) == 0)
1889 			return (0);		/* NULL string */
1890 		for (q = p; *q; q++) {
1891 			if (!(isascii(*q) && (isprint(*q) || isspace(*q))))
1892 				return (0);	/* Not printable or space */
1893 		}
1894 	}
1895 
1896 	/* If name is non-NULL, produce header */
1897 	if (name) {
1898 		indent_to_level(ilev);
1899 		(void) printf("%s properties:\n", name);
1900 	}
1901 	ilev++;
1902 
1903 	/* process like a string array property */
1904 	indent_to_level(ilev);
1905 	(void) printf("name='compatible' type=string items=%d\n", ncompat);
1906 	indent_to_level(ilev);
1907 	(void) printf("    value=");
1908 	for (i = 0, p = compat_array; i < (ncompat - 1);
1909 	    i++, p += strlen(p) + 1)
1910 		(void) printf("'%s' + ", p);
1911 	(void) printf("'%s'", p);
1912 	(void) putchar('\n');
1913 	return (1);
1914 }
1915 
1916 static int
1917 dump_pciid(char *name, int ilev, di_node_t node, pcidb_hdl_t *pci)
1918 {
1919 	char *t = NULL;
1920 	int *vid, *did, *svid, *sdid;
1921 	const char *vname, *dname, *sname;
1922 	pcidb_vendor_t *pciv;
1923 	pcidb_device_t *pcid;
1924 	pcidb_subvd_t *pcis;
1925 	di_node_t pnode = di_parent_node(node);
1926 
1927 	const char *unov = "unknown vendor";
1928 	const char *unod = "unknown device";
1929 	const char *unos = "unknown subsystem";
1930 
1931 	if (pci == NULL)
1932 		return (0);
1933 
1934 	vname = unov;
1935 	dname = unod;
1936 	sname = unos;
1937 
1938 	if (di_prop_lookup_strings(DDI_DEV_T_ANY, pnode,
1939 	    "device_type", &t) <= 0)
1940 		return (0);
1941 
1942 	if (t == NULL || (strcmp(t, "pci") != 0 &&
1943 	    strcmp(t, "pciex") != 0))
1944 		return (0);
1945 
1946 	/*
1947 	 * All devices should have a vendor and device id, if we fail to find
1948 	 * one, then we're going to return right here and not print anything.
1949 	 *
1950 	 * We're going to also check for the subsystem-vendor-id and
1951 	 * subsystem-id. If we don't find one of them, we're going to assume
1952 	 * that this device does not have one. In that case, we will never
1953 	 * attempt to try and print anything related to that. If it does have
1954 	 * both, then we are going to look them up and print the appropriate
1955 	 * string if we find it or not.
1956 	 */
1957 	if (di_prop_lookup_ints(DDI_DEV_T_ANY, node, "vendor-id", &vid) <= 0)
1958 		return (0);
1959 
1960 	if (di_prop_lookup_ints(DDI_DEV_T_ANY, node, "device-id", &did) <= 0)
1961 		return (0);
1962 
1963 	if (di_prop_lookup_ints(DDI_DEV_T_ANY, node, "subsystem-vendor-id",
1964 	    &svid) <= 0 || di_prop_lookup_ints(DDI_DEV_T_ANY, node,
1965 	    "subsystem-id", &sdid) <= 0) {
1966 		svid = NULL;
1967 		sdid = NULL;
1968 		sname = NULL;
1969 	}
1970 
1971 	pciv = pcidb_lookup_vendor(pci, vid[0]);
1972 	if (pciv == NULL)
1973 		goto print;
1974 	vname = pcidb_vendor_name(pciv);
1975 
1976 	pcid = pcidb_lookup_device_by_vendor(pciv, did[0]);
1977 	if (pcid == NULL)
1978 		goto print;
1979 	dname = pcidb_device_name(pcid);
1980 
1981 	if (svid != NULL) {
1982 		pcis = pcidb_lookup_subvd_by_device(pcid, svid[0], sdid[0]);
1983 		if (pcis == NULL)
1984 			goto print;
1985 		sname = pcidb_subvd_name(pcis);
1986 	}
1987 
1988 print:
1989 	/* If name is non-NULL, produce header */
1990 	if (name) {
1991 		indent_to_level(ilev);
1992 		(void) printf("%s properties:\n", name);
1993 	}
1994 	ilev++;
1995 
1996 	/* These are all going to be single string properties */
1997 	indent_to_level(ilev);
1998 	(void) printf("name='vendor-name' type=string items=1\n");
1999 	indent_to_level(ilev);
2000 	(void) printf("    value='%s'\n", vname);
2001 
2002 	indent_to_level(ilev);
2003 	(void) printf("name='device-name' type=string items=1\n");
2004 	indent_to_level(ilev);
2005 	(void) printf("    value='%s'\n", dname);
2006 
2007 	if (sname != NULL) {
2008 		indent_to_level(ilev);
2009 		(void) printf("name='subsystem-name' type=string items=1\n");
2010 		indent_to_level(ilev);
2011 		(void) printf("    value='%s'\n", sname);
2012 	}
2013 
2014 	return (0);
2015 }
2016