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