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, Version 1.0 only 6 * (the "License"). You may not use this file except in compliance 7 * with the License. 8 * 9 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 10 * or http://www.opensolaris.org/os/licensing. 11 * See the License for the specific language governing permissions 12 * and limitations under the License. 13 * 14 * When distributing Covered Code, include this CDDL HEADER in each 15 * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 16 * If applicable, add the following below this CDDL HEADER, with the 17 * fields enclosed by brackets "[]" replaced with your own identifying 18 * information: Portions Copyright [yyyy] [name of copyright owner] 19 * 20 * CDDL HEADER END 21 */ 22 /* 23 * Copyright 2005 Sun Microsystems, Inc. All rights reserved. 24 * Use is subject to license terms. 25 */ 26 27 #pragma ident "%Z%%M% %I% %E% SMI" 28 29 #include <stdio.h> 30 #include <stdlib.h> 31 #include <string.h> 32 #include <fcntl.h> 33 #include <stdarg.h> 34 #include <errno.h> 35 #include <unistd.h> 36 #include <sys/utsname.h> 37 #include <sys/openpromio.h> 38 #include <libintl.h> 39 #include "pdevinfo.h" 40 #include "display.h" 41 #include "pdevinfo_sun4u.h" 42 43 /* 44 * For machines that support the openprom, fetch and print the list 45 * of devices that the kernel has fetched from the prom or conjured up. 46 * 47 */ 48 49 50 static int prom_fd; 51 extern char *progname; 52 extern char *promdev; 53 extern void getppdata(); 54 extern void printppdata(); 55 56 /* 57 * Define DPRINT for run-time debugging printf's... 58 * #define DPRINT 1 59 */ 60 61 #ifdef DPRINT 62 static char vdebug_flag = 1; 63 #define dprintf if (vdebug_flag) printf 64 static void dprint_dev_info(caddr_t, dev_info_t *); 65 #endif /* DPRINT */ 66 67 #if !defined(TEXT_DOMAIN) 68 #define TEXT_DOMAIN "SYS_TEST" 69 #endif 70 71 /*VARARGS1*/ 72 int 73 _error(char *fmt, ...) 74 { 75 int saved_errno; 76 va_list ap; 77 extern int errno; 78 saved_errno = errno; 79 80 if (progname) 81 (void) fprintf(stderr, "%s: ", progname); 82 83 va_start(ap, fmt); 84 85 (void) vfprintf(stderr, fmt, ap); 86 87 va_end(ap); 88 89 (void) fprintf(stderr, ": "); 90 errno = saved_errno; 91 perror(""); 92 93 return (2); 94 } 95 96 int 97 is_openprom(void) 98 { 99 Oppbuf oppbuf; 100 register struct openpromio *opp = &(oppbuf.opp); 101 register unsigned int i; 102 103 opp->oprom_size = MAXVALSIZE; 104 if (ioctl(prom_fd, OPROMGETCONS, opp) < 0) 105 exit(_error("OPROMGETCONS")); 106 107 i = (unsigned int)((unsigned char)opp->oprom_array[0]); 108 return ((i & OPROMCONS_OPENPROM) == OPROMCONS_OPENPROM); 109 } 110 111 /* 112 * Read all properties and values from nodes. 113 * Copy the properties read into the prom_node passsed in. 114 */ 115 void 116 dump_node(Prom_node *node) 117 { 118 Oppbuf oppbuf; 119 register struct openpromio *opp = &oppbuf.opp; 120 Prop *prop = NULL; /* tail of properties list */ 121 StaticProp *temp; 122 123 /* clear out pointers in pnode */ 124 node->props = NULL; 125 126 /* get first prop by asking for null string */ 127 (void) memset((void *) oppbuf.buf, 0, BUFSIZE); 128 129 /* allocate space for the property */ 130 if ((temp = malloc(sizeof (StaticProp))) == NULL) { 131 perror("malloc"); 132 exit(1); 133 } 134 135 opp->oprom_size = MAXPROPSIZE; 136 while (opp->oprom_size != 0) { 137 Prop *new; 138 int i; 139 char *tempp, *newp; 140 141 /* 142 * get property 143 */ 144 opp->oprom_size = MAXPROPSIZE; 145 146 if (ioctl(prom_fd, OPROMNXTPROP, opp) < 0) 147 exit(_error("OPROMNXTPROP")); 148 149 if (opp->oprom_size != 0) { 150 temp->name.opp.oprom_size = opp->oprom_size; 151 (void) strcpy(temp->name.opp.oprom_array, 152 opp->oprom_array); 153 154 (void) strcpy(temp->value.opp.oprom_array, 155 temp->name.opp.oprom_array); 156 getpropval(&temp->value.opp); 157 temp->size = temp->value.opp.oprom_size; 158 159 /* Now copy over temp's data to new. */ 160 if ((new = malloc(sizeof (Prop))) == NULL) { 161 perror("malloc"); 162 exit(1); 163 } 164 165 /* 166 * First copy over temp->name's data. The 167 * temp->name.opp.opio_u union always contains char[] 168 * (as opposed to an int or int []). 169 */ 170 new->name.opp.oprom_size = temp->name.opp.oprom_size; 171 172 if ((new->name.opp.oprom_array = 173 malloc(new->name.opp.oprom_size)) == NULL) { 174 perror("malloc"); 175 exit(1); 176 } 177 (void) strcpy(new->name.opp.oprom_array, 178 temp->name.opp.oprom_array); 179 180 new->name.opp.holds_array = 1; 181 182 /* 183 * Then copy over temp->value's data. 184 * temp->value.opp.opio_u could contain char[], int or 185 * int []. If *(temp->value.opp.oprom_array) is '\0', 186 * this indicates int or int []. int is the norm, but 187 * to be safe we assume int [] and copy over 188 * OPROM_NODE_SIZE int elements. 189 */ 190 new->value.opp.oprom_size = temp->value.opp.oprom_size; 191 192 if (*(temp->value.opp.oprom_array) == '\0') { 193 for (i = 0; i < OPROM_NODE_SIZE; i++) 194 new->value.opp.oprom_node[i] = 195 *(&temp->value.opp.oprom_node+i); 196 197 new->value.opp.holds_array = 0; 198 } else { 199 if ((new->value.opp.oprom_array = 200 malloc(new->value.opp.oprom_size)) 201 == NULL) { 202 perror("malloc"); 203 exit(1); 204 } 205 206 /* 207 * temp->value.opp.oprom_array can contain one 208 * or more embedded NULLs. These trip-up the 209 * standard string copying functions, so we do 210 * the copy by hand. temp->value.opp.oprom_array 211 * will be NULL-terminated. oprom_size includes 212 * this terminating NULL. 213 */ 214 newp = new->value.opp.oprom_array; 215 tempp = temp->value.opp.oprom_array; 216 for (i = new->value.opp.oprom_size; i > 0; i--) 217 *newp++ = *tempp++; 218 219 new->value.opp.holds_array = 1; 220 } 221 222 new->size = temp->size; 223 224 /* everything worked so link the property list */ 225 if (node->props == NULL) 226 node->props = new; 227 else if (prop != NULL) 228 prop->next = new; 229 prop = new; 230 prop->next = NULL; 231 } 232 } 233 free(temp); 234 } 235 236 int 237 promopen(int oflag) 238 { 239 /*CONSTCOND*/ 240 while (1) { 241 if ((prom_fd = open(promdev, oflag)) < 0) { 242 if (errno == EAGAIN) { 243 (void) sleep(5); 244 continue; 245 } 246 if (errno == ENXIO) 247 return (-1); 248 exit(_error(dgettext(TEXT_DOMAIN, "cannot open %s"), 249 promdev)); 250 } else 251 return (0); 252 } 253 /*NOTREACHED*/ 254 } 255 256 void 257 promclose(void) 258 { 259 if (close(prom_fd) < 0) 260 exit(_error(dgettext(TEXT_DOMAIN, "close error on %s"), 261 promdev)); 262 } 263 264 /* 265 * Read the value of the property from the PROM device tree 266 */ 267 void 268 getpropval(struct openpromio *opp) 269 { 270 opp->oprom_size = MAXVALSIZE; 271 272 if (ioctl(prom_fd, OPROMGETPROP, opp) < 0) 273 exit(_error("OPROMGETPROP")); 274 } 275 276 int 277 next(int id) 278 { 279 Oppbuf oppbuf; 280 register struct openpromio *opp = &(oppbuf.opp); 281 /* LINTED */ 282 int *ip = (int *)(opp->oprom_array); 283 284 (void) memset((void *) oppbuf.buf, 0, BUFSIZE); 285 286 opp->oprom_size = MAXVALSIZE; 287 *ip = id; 288 if (ioctl(prom_fd, OPROMNEXT, opp) < 0) 289 return (_error("OPROMNEXT")); 290 /* LINTED */ 291 return (*(int *)opp->oprom_array); 292 } 293 294 int 295 child(int id) 296 { 297 Oppbuf oppbuf; 298 register struct openpromio *opp = &(oppbuf.opp); 299 /* LINTED */ 300 int *ip = (int *)(opp->oprom_array); 301 302 (void) memset((void *) oppbuf.buf, 0, BUFSIZE); 303 opp->oprom_size = MAXVALSIZE; 304 *ip = id; 305 if (ioctl(prom_fd, OPROMCHILD, opp) < 0) 306 return (_error("OPROMCHILD")); 307 /* LINTED */ 308 return (*(int *)opp->oprom_array); 309 } 310 311 /* 312 * Check if the Prom node passed in contains a property called 313 * "board#". 314 */ 315 int 316 has_board_num(Prom_node *node) 317 { 318 Prop *prop = node->props; 319 320 /* 321 * walk thru all properties in this PROM node and look for 322 * board# prop 323 */ 324 while (prop != NULL) { 325 if (strcmp(prop->name.opp.oprom_array, "board#") == 0) 326 return (1); 327 328 prop = prop->next; 329 } 330 331 return (0); 332 } /* end of has_board_num() */ 333 334 /* 335 * Retrieve the value of the board number property from this Prom 336 * node. It has the type of int. 337 */ 338 int 339 get_board_num(Prom_node *node) 340 { 341 Prop *prop = node->props; 342 343 /* 344 * walk thru all properties in this PROM node and look for 345 * board# prop 346 */ 347 while (prop != NULL) { 348 if (strcmp(prop->name.opp.oprom_array, "board#") == 0) 349 return (prop->value.opp.oprom_node[0]); 350 351 prop = prop->next; 352 } 353 354 return (-1); 355 } /* end of get_board_num() */ 356 357 /* 358 * Find the requested board struct in the system device tree. 359 */ 360 Board_node * 361 find_board(Sys_tree *root, int board) 362 { 363 Board_node *bnode = root->bd_list; 364 365 while ((bnode != NULL) && (board != bnode->board_num)) 366 bnode = bnode->next; 367 368 return (bnode); 369 } /* end of find_board() */ 370 371 /* 372 * Add a board to the system list in order. Initialize all pointer 373 * fields to NULL. 374 */ 375 Board_node * 376 insert_board(Sys_tree *root, int board) 377 { 378 Board_node *bnode; 379 Board_node *temp = root->bd_list; 380 381 if ((bnode = (Board_node *) malloc(sizeof (Board_node))) == NULL) { 382 perror("malloc"); 383 exit(1); 384 } 385 bnode->nodes = NULL; 386 bnode->next = NULL; 387 bnode->board_num = board; 388 389 if (temp == NULL) 390 root->bd_list = bnode; 391 else if (temp->board_num > board) { 392 bnode->next = temp; 393 root->bd_list = bnode; 394 } else { 395 while ((temp->next != NULL) && (board > temp->next->board_num)) 396 temp = temp->next; 397 bnode->next = temp->next; 398 temp->next = bnode; 399 } 400 root->board_cnt++; 401 402 return (bnode); 403 } /* end of insert_board() */ 404 405 /* 406 * This function searches through the properties of the node passed in 407 * and returns a pointer to the value of the name property. 408 */ 409 char * 410 get_node_name(Prom_node *pnode) 411 { 412 Prop *prop; 413 414 if (pnode == NULL) { 415 return (NULL); 416 } 417 418 prop = pnode->props; 419 while (prop != NULL) { 420 if (strcmp("name", prop->name.opp.oprom_array) == 0) 421 return (prop->value.opp.oprom_array); 422 prop = prop->next; 423 } 424 return (NULL); 425 } /* end of get_node_name() */ 426 427 /* 428 * This function searches through the properties of the node passed in 429 * and returns a pointer to the value of the name property. 430 */ 431 char * 432 get_node_type(Prom_node *pnode) 433 { 434 Prop *prop; 435 436 if (pnode == NULL) { 437 return (NULL); 438 } 439 440 prop = pnode->props; 441 while (prop != NULL) { 442 if (strcmp("device_type", prop->name.opp.oprom_array) == 0) 443 return (prop->value.opp.oprom_array); 444 prop = prop->next; 445 } 446 return (NULL); 447 } /* end of get_node_type() */ 448 449 /* 450 * Do a depth-first walk of a device tree and 451 * return the first node with the name matching. 452 */ 453 454 Prom_node * 455 dev_find_node(Prom_node *root, char *name) 456 { 457 Prom_node *node; 458 459 node = dev_find_node_by_type(root, "name", name); 460 461 return (node); 462 } 463 464 Prom_node * 465 dev_next_node(Prom_node *root, char *name) 466 { 467 Prom_node *node; 468 469 node = dev_next_node_by_type(root, "name", name); 470 471 return (node); 472 } 473 474 /* 475 * Search for and return a node of the required type. If no node is found, 476 * then return NULL. 477 */ 478 Prom_node * 479 dev_find_type(Prom_node *root, char *type) 480 { 481 Prom_node *node; 482 483 node = dev_find_node_by_type(root, "device_type", type); 484 485 return (node); /* not found */ 486 } 487 488 /* 489 * Start from the current node and return the next node besides the 490 * current one which has the requested type property. 491 */ 492 Prom_node * 493 dev_next_type(Prom_node *root, char *type) 494 { 495 Prom_node *node; 496 497 node = dev_next_node_by_type(root, "device_type", type); 498 499 return (node); /* not found */ 500 } 501 502 /* 503 * Search a device tree and return the first failed node that is found. 504 * (has a 'status' property) 505 */ 506 Prom_node * 507 find_failed_node(Prom_node * root) 508 { 509 Prom_node *pnode; 510 511 if (root == NULL) 512 return (NULL); 513 514 if (node_failed(root)) { 515 return (root); 516 } 517 518 /* search the child */ 519 if ((pnode = find_failed_node(root->child)) != NULL) 520 return (pnode); 521 522 /* search the siblings */ 523 if ((pnode = find_failed_node(root->sibling)) != NULL) 524 return (pnode); 525 526 return (NULL); 527 } /* end of find_failed_node() */ 528 529 /* 530 * Start from the current node and return the next node besides 531 * the current one which is failed. (has a 'status' property) 532 */ 533 Prom_node * 534 next_failed_node(Prom_node * root) 535 { 536 Prom_node *pnode; 537 Prom_node *parent; 538 539 if (root == NULL) 540 return (NULL); 541 542 /* search the child */ 543 if ((pnode = find_failed_node(root->child)) != NULL) { 544 return (pnode); 545 } 546 547 /* search the siblings */ 548 if ((pnode = find_failed_node(root->sibling)) != NULL) { 549 return (pnode); 550 } 551 552 /* backtracking the search up through parents' siblings */ 553 parent = root->parent; 554 while (parent != NULL) { 555 if ((pnode = find_failed_node(parent->sibling)) != NULL) 556 return (pnode); 557 else 558 parent = parent->parent; 559 } 560 561 return (NULL); 562 } /* end of find_failed_node() */ 563 564 /* 565 * node_failed 566 * 567 * This function determines if the current Prom node is failed. This 568 * is defined by having a status property containing the token 'fail'. 569 */ 570 int 571 node_failed(Prom_node *node) 572 { 573 return (node_status(node, "fail")); 574 } 575 576 int 577 node_status(Prom_node *node, char *status) 578 { 579 void *value; 580 581 if (status == NULL) 582 return (0); 583 584 /* search the local node */ 585 if ((value = get_prop_val(find_prop(node, "status"))) != NULL) { 586 if ((value != NULL) && strstr((char *)value, status)) 587 return (1); 588 } 589 return (0); 590 } 591 592 /* 593 * Get a property's value. Must be void * since the property can 594 * be any data type. Caller must know the *PROPER* way to use this 595 * data. 596 */ 597 void * 598 get_prop_val(Prop *prop) 599 { 600 if (prop == NULL) 601 return (NULL); 602 603 if (prop->value.opp.holds_array) 604 return ((void *)(prop->value.opp.oprom_array)); 605 else 606 return ((void *)(&prop->value.opp.oprom_node[0])); 607 } /* end of get_prop_val() */ 608 609 /* 610 * Search a Prom node and retrieve the property with the correct 611 * name. 612 */ 613 Prop * 614 find_prop(Prom_node *pnode, char *name) 615 { 616 Prop *prop; 617 618 if (pnode == NULL) { 619 return (NULL); 620 } 621 622 if (pnode->props == NULL) { 623 (void) printf("%s", dgettext(TEXT_DOMAIN, "Prom node has " 624 "no properties\n")); 625 return (NULL); 626 } 627 628 prop = pnode->props; 629 while ((prop != NULL) && (strcmp(prop->name.opp.oprom_array, name))) 630 prop = prop->next; 631 632 return (prop); 633 } 634 635 /* 636 * This function adds a board node to the board structure where that 637 * that node's physical component lives. 638 */ 639 void 640 add_node(Sys_tree *root, Prom_node *pnode) 641 { 642 int board; 643 Board_node *bnode; 644 Prom_node *p; 645 646 /* add this node to the Board list of the appropriate board */ 647 if ((board = get_board_num(pnode)) == -1) { 648 /* board is 0 if not on Sunfire */ 649 board = 0; 650 } 651 652 /* find the node with the same board number */ 653 if ((bnode = find_board(root, board)) == NULL) { 654 bnode = insert_board(root, board); 655 bnode->board_type = UNKNOWN_BOARD; 656 } 657 658 /* now attach this prom node to the board list */ 659 /* Insert this node at the end of the list */ 660 pnode->sibling = NULL; 661 if (bnode->nodes == NULL) 662 bnode->nodes = pnode; 663 else { 664 p = bnode->nodes; 665 while (p->sibling != NULL) 666 p = p->sibling; 667 p->sibling = pnode; 668 } 669 670 } 671 672 /* 673 * Find the device on the current board with the requested device ID 674 * and name. If this rountine is passed a NULL pointer, it simply returns 675 * NULL. 676 */ 677 Prom_node * 678 find_device(Board_node *board, int id, char *name) 679 { 680 Prom_node *pnode; 681 int mask; 682 683 /* find the first cpu node */ 684 pnode = dev_find_node(board->nodes, name); 685 686 mask = 0x1F; 687 while (pnode != NULL) { 688 if ((get_id(pnode) & mask) == id) 689 return (pnode); 690 691 pnode = dev_next_node(pnode, name); 692 } 693 return (NULL); 694 } 695 696 Prom_node * 697 dev_find_node_by_type(Prom_node *root, char *type, char *property) 698 { 699 Prom_node *node; 700 char *type_prop; 701 702 if (root == NULL || property == NULL) 703 return (NULL); 704 705 type_prop = (char *)get_prop_val(find_prop(root, type)); 706 707 if (type_prop != NULL) { 708 if (strcmp(type_prop, property) == 0) { 709 return (root); 710 } 711 } 712 713 /* look at your children first */ 714 if ((node = dev_find_node_by_type(root->child, type, 715 property)) != NULL) 716 return (node); 717 718 /* now look at your siblings */ 719 if ((node = dev_find_node_by_type(root->sibling, type, 720 property)) != NULL) 721 return (node); 722 723 return (NULL); /* not found */ 724 } 725 726 Prom_node * 727 dev_next_node_by_type(Prom_node *root, char *type, char *property) 728 { 729 Prom_node *node; 730 731 if (root == NULL || property == NULL) 732 return (NULL); 733 734 /* look at your children first */ 735 if ((node = dev_find_node_by_type(root->child, type, 736 property)) != NULL) 737 return (node); 738 739 /* now look at your siblings */ 740 if ((node = dev_find_node_by_type(root->sibling, type, 741 property)) != NULL) 742 return (node); 743 744 /* now look at papa's siblings */ 745 if ((node = dev_find_node_by_type(root->parent->sibling, 746 type, property)) != NULL) 747 return (node); 748 749 return (NULL); /* not found */ 750 } 751 752 /* 753 * Do a depth-first walk of a device tree and 754 * return the first node with the matching compatible. 755 */ 756 Prom_node * 757 dev_find_node_by_compatible(Prom_node *root, char *compatible) 758 { 759 Prom_node *node; 760 Prop *prop; 761 char *compatible_array; 762 int size, nbytes; 763 764 if (root == NULL || compatible == NULL) 765 return (NULL); 766 767 if ((prop = find_prop(root, "compatible")) != NULL && 768 (compatible_array = (char *)get_prop_val(prop)) != NULL) { 769 /* 770 * The Prop structure returned by find_prop() is supposed 771 * to contain an indication of how big the value of the 772 * compatible property is. Since it is an array of strings 773 * this is our only means of determining just how many 774 * strings might be in this property. However, this size 775 * is often left as zero even though there is at least one 776 * string present. When this is the case, all we can do 777 * is examine the first string in the compatible property. 778 */ 779 780 for (size = prop->size; size >= 0; size -= nbytes) { 781 if (strcmp(compatible_array, compatible) == 0) 782 return (root); /* found a match */ 783 784 nbytes = strlen(compatible_array) + 1; 785 compatible_array += nbytes; 786 } 787 } 788 789 node = dev_find_node_by_compatible(root->child, compatible); 790 if (node != NULL) 791 return (node); 792 793 /* 794 * Note the very deliberate use of tail recursion here. A good 795 * compiler (such as Sun's) will recognize this and generate code 796 * that does not allocate another stack frame. Instead, it will 797 * overlay the existing stack frame with the new one, the only change 798 * having been to replace the original root with its sibling. 799 * This has the potential to create some confusion for anyone 800 * trying to debug this code from a core dump, since the stack 801 * trace will not reveal recursion on siblings, only on children. 802 */ 803 804 return (dev_find_node_by_compatible(root->sibling, compatible)); 805 } 806 807 /* 808 * Start from the current node and return the next node besides 809 * the current one which has the requested compatible property. 810 */ 811 Prom_node * 812 dev_next_node_by_compatible(Prom_node *root, char *compatible) 813 { 814 Prom_node *node; 815 816 if (root == NULL || compatible == NULL) 817 return (NULL); 818 819 node = dev_find_node_by_compatible(root->child, compatible); 820 if (node != NULL) 821 return (node); 822 823 /* 824 * More tail recursion. Even though it is a different function, 825 * this will overlay the current stack frame. Caveat exterminator. 826 */ 827 828 node = dev_find_node_by_compatible(root->sibling, compatible); 829 if (node != NULL) 830 return (node); 831 832 return (dev_find_node_by_compatible(root->parent->sibling, compatible)); 833 } 834