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