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