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