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