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