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