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