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 1999-2002 Sun Microsystems, Inc. All rights reserved. 24 * Use is subject to license terms. 25 * 26 * Desktop Platform specific functions. 27 * 28 * Called when: 29 * machine_type == MTYPE_DARWIN && 30 * machine_type == MTYPE_DEFAULT 31 * 32 */ 33 34 #pragma ident "%Z%%M% %I% %E% SMI" 35 36 #include <stdio.h> 37 #include <stdlib.h> 38 #include <unistd.h> 39 #include <ctype.h> 40 #include <string.h> 41 #include <kvm.h> 42 #include <varargs.h> 43 #include <errno.h> 44 #include <time.h> 45 #include <dirent.h> 46 #include <fcntl.h> 47 #include <sys/param.h> 48 #include <sys/stat.h> 49 #include <sys/types.h> 50 #include <sys/systeminfo.h> 51 #include <sys/utsname.h> 52 #include <sys/openpromio.h> 53 #include <kstat.h> 54 #include <libintl.h> 55 #include <syslog.h> 56 #include <sys/dkio.h> 57 #include "pdevinfo.h" 58 #include "display.h" 59 #include "pdevinfo_sun4u.h" 60 #include "display_sun4u.h" 61 #include "libprtdiag.h" 62 63 #if !defined(TEXT_DOMAIN) 64 #define TEXT_DOMAIN "SYS_TEST" 65 #endif 66 67 68 #define PCI_BUS(x) ((x >> 16) & 0xff) 69 70 /* 71 * State variable to signify the type of machine we're currently 72 * running on. Since prtdiag has come to be the dumping ground 73 * for lots of platform-specific routines, and machine architecture 74 * alone is not enough to determine our course of action, we need 75 * to enumerate the different machine types that we should worry 76 * about. 77 */ 78 enum machine_type { 79 MTYPE_DEFAULT = 0, /* Desktop-class machine */ 80 MTYPE_DARWIN = 1 81 }; 82 83 enum machine_type machine_type = MTYPE_DEFAULT; 84 85 extern int print_flag; 86 87 /* 88 * these functions will overlay the symbol table of libprtdiag 89 * at runtime (desktop systems only) 90 */ 91 int error_check(Sys_tree *tree, struct system_kstat_data *kstats); 92 void display_memoryconf(Sys_tree *tree, struct grp_info *grps); 93 int disp_fail_parts(Sys_tree *tree); 94 void display_hp_fail_fault(Sys_tree *tree, struct system_kstat_data *kstats); 95 void display_diaginfo(int flag, Prom_node *root, Sys_tree *tree, 96 struct system_kstat_data *kstats); 97 void display_pci(Board_node *bnode); 98 void read_platform_kstats(Sys_tree *tree, 99 struct system_kstat_data *sys_kstat, 100 struct bd_kstat_data *bdp, struct envctrl_kstat_data *ep); 101 void display_sbus(Board_node *); 102 103 104 /* local functions */ 105 static void dt_disp_asic_revs(Sys_tree *); 106 static void display_sabre_pci(Board_node *); 107 static void display_dev_node(Prom_node *np, int depth); 108 static void get_machine_type(void); 109 110 int 111 error_check(Sys_tree *tree, struct system_kstat_data *kstats) 112 { 113 int exit_code = 0; /* init to all OK */ 114 115 #ifdef lint 116 kstats = kstats; 117 #endif 118 119 /* 120 * silently check for any types of machine errors 121 */ 122 print_flag = 0; 123 if (disp_fail_parts(tree)) { 124 /* set exit_code to show failures */ 125 exit_code = 1; 126 } 127 print_flag = 1; 128 129 return (exit_code); 130 } 131 132 133 void 134 display_memoryconf(Sys_tree *tree, struct grp_info *grps) 135 { 136 #ifdef lint 137 tree = tree; 138 grps = grps; 139 #endif 140 } 141 142 /* 143 * disp_fail_parts 144 * 145 * Display the failed parts in the system. This function looks for 146 * the status property in all PROM nodes. On systems where 147 * the PROM does not supports passing diagnostic information 148 * thruogh the device tree, this routine will be silent. 149 */ 150 int 151 disp_fail_parts(Sys_tree *tree) 152 { 153 int exit_code; 154 int system_failed = 0; 155 Board_node *bnode = tree->bd_list; 156 Prom_node *pnode; 157 158 exit_code = 0; 159 160 /* go through all of the boards looking for failed units. */ 161 while (bnode != NULL) { 162 /* find failed chips */ 163 pnode = find_failed_node(bnode->nodes); 164 if ((pnode != NULL) && !system_failed) { 165 system_failed = 1; 166 exit_code = 1; 167 if (print_flag == 0) { 168 return (exit_code); 169 } 170 log_printf("\n", 0); 171 log_printf(dgettext(TEXT_DOMAIN, "Failed Field " 172 "Replaceable Units (FRU) in System:\n"), 0); 173 log_printf("==========================" 174 "====================\n", 0); 175 } 176 177 while (pnode != NULL) { 178 void *value; 179 char *name; /* node name string */ 180 char *type; /* node type string */ 181 char *board_type = NULL; 182 183 value = get_prop_val(find_prop(pnode, "status")); 184 name = get_node_name(pnode); 185 186 /* sanity check of data retreived from PROM */ 187 if ((value == NULL) || (name == NULL)) { 188 pnode = next_failed_node(pnode); 189 continue; 190 } 191 192 /* Find the board type of this board */ 193 if (bnode->board_type == CPU_BOARD) { 194 board_type = "CPU"; 195 } else { 196 board_type = "IO"; 197 } 198 199 log_printf(dgettext(TEXT_DOMAIN, "%s unavailable " 200 "on %s Board #%d\n"), name, board_type, 201 bnode->board_num, 0); 202 203 log_printf(dgettext(TEXT_DOMAIN, 204 "\tPROM fault string: %s\n"), value, 0); 205 206 log_printf(dgettext(TEXT_DOMAIN, 207 "\tFailed Field Replaceable Unit is "), 0); 208 209 /* 210 * Determine whether FRU is CPU module, system 211 * board, or SBus card. 212 */ 213 if ((name != NULL) && (strstr(name, "sbus"))) { 214 215 log_printf(dgettext(TEXT_DOMAIN, 216 "SBus Card %d\n"), 217 get_sbus_slot(pnode), 0); 218 219 } else if (((name = get_node_name(pnode->parent)) != 220 NULL) && (strstr(name, "pci"))) { 221 222 log_printf(dgettext(TEXT_DOMAIN, 223 "PCI Card %d"), 224 get_pci_device(pnode), 0); 225 226 } else if (((type = get_node_type(pnode)) != NULL) && 227 (strstr(type, "cpu"))) { 228 229 log_printf(dgettext(TEXT_DOMAIN, "UltraSPARC " 230 "module Board %d Module %d\n"), 0, 231 get_id(pnode)); 232 233 } else { 234 log_printf(dgettext(TEXT_DOMAIN, 235 "%s board %d\n"), board_type, 236 bnode->board_num, 0); 237 } 238 pnode = next_failed_node(pnode); 239 } 240 bnode = bnode->next; 241 } 242 243 if (!system_failed) { 244 log_printf("\n", 0); 245 log_printf(dgettext(TEXT_DOMAIN, 246 "No failures found in System\n"), 0); 247 log_printf("===========================\n", 0); 248 } 249 250 if (system_failed) 251 return (1); 252 else 253 return (0); 254 } 255 256 257 void 258 display_hp_fail_fault(Sys_tree *tree, struct system_kstat_data *kstats) 259 { 260 261 #ifdef lint 262 kstats = kstats; 263 #endif 264 /* Display failed units */ 265 (void) disp_fail_parts(tree); 266 } 267 268 void 269 display_diaginfo(int flag, Prom_node *root, Sys_tree *tree, 270 struct system_kstat_data *kstats) 271 { 272 273 #ifdef lint 274 kstats = kstats; 275 #endif 276 /* 277 * Now display the last powerfail time and the fatal hardware 278 * reset information. We do this under a couple of conditions. 279 * First if the user asks for it. The second is iof the user 280 * told us to do logging, and we found a system failure. 281 */ 282 if (flag) { 283 /* 284 * display time of latest powerfail. Not all systems 285 * have this capability. For those that do not, this 286 * is just a no-op. 287 */ 288 disp_powerfail(root); 289 290 dt_disp_asic_revs(tree); 291 292 platform_disp_prom_version(tree); 293 } 294 return; 295 296 } 297 298 void 299 display_pci(Board_node *bnode) 300 { 301 Prom_node *pci; 302 303 /* 304 * We have different routines for walking/displaying PCI 305 * devices depending on whether the PCI device is a 306 * Psycho or a Sabre. 307 */ 308 pci = dev_find_node_by_type(bnode->nodes, "model", "SUNW,psycho"); 309 if (pci != NULL) { 310 display_psycho_pci(bnode); 311 return; 312 } 313 314 pci = dev_find_node_by_type(bnode->nodes, "model", "SUNW,sabre"); 315 if (pci != NULL) { 316 display_sabre_pci(bnode); 317 return; 318 } 319 } 320 321 void 322 read_platform_kstats(Sys_tree *tree, struct system_kstat_data *sys_kstat, 323 struct bd_kstat_data *bdp, struct envctrl_kstat_data *ep) 324 325 { 326 #ifdef lint 327 tree = tree; 328 sys_kstat = sys_kstat; 329 bdp = bdp; 330 ep = ep; 331 #endif 332 } 333 334 335 /* 336 * local functions 337 */ 338 339 void 340 dt_disp_asic_revs(Sys_tree *tree) 341 { 342 Board_node *bnode; 343 Prom_node *pnode; 344 char *name; 345 int *version; 346 347 /* Print the header */ 348 log_printf("\n", 0); 349 log_printf("=========================", 0); 350 log_printf(" HW Revisions ", 0); 351 log_printf("=========================", 0); 352 log_printf("\n", 0); 353 log_printf("\n", 0); 354 355 bnode = tree->bd_list; 356 357 log_printf("ASIC Revisions:\n", 0); 358 log_printf("---------------\n", 0); 359 360 /* Find sysio and print rev */ 361 for (pnode = dev_find_node(bnode->nodes, "sbus"); pnode != NULL; 362 pnode = dev_next_node(pnode, "sbus")) { 363 version = (int *)get_prop_val(find_prop(pnode, "version#")); 364 name = get_prop_val(find_prop(pnode, "name")); 365 366 if ((version != NULL) && (name != NULL)) { 367 log_printf("SBus: %s Rev %d\n", 368 name, *version, 0); 369 } 370 } 371 372 /* Find Psycho and print rev */ 373 for (pnode = dev_find_node(bnode->nodes, "pci"); pnode != NULL; 374 pnode = dev_next_node(pnode, "pci")) { 375 version = (int *)get_prop_val(find_prop(pnode, "version#")); 376 name = get_prop_val(find_prop(pnode, "name")); 377 378 if ((version != NULL) && (name != NULL)) 379 log_printf("PCI: %s Rev %d\n", 380 name, *version, 0); 381 } 382 383 /* Find Cheerio and print rev */ 384 for (pnode = dev_find_node(bnode->nodes, "ebus"); pnode != NULL; 385 pnode = dev_next_node(pnode, "ebus")) { 386 version = (int *)get_prop_val(find_prop(pnode, "revision-id")); 387 name = get_prop_val(find_prop(pnode, "name")); 388 389 if ((version != NULL) && (name != NULL)) 390 log_printf("Cheerio: %s Rev %d\n", name, *version, 0); 391 } 392 393 394 /* Find the FEPS and print rev */ 395 for (pnode = dev_find_node(bnode->nodes, "SUNW,hme"); pnode != NULL; 396 pnode = dev_next_node(pnode, "SUNW,hme")) { 397 version = (int *)get_prop_val(find_prop(pnode, "hm-rev")); 398 name = get_prop_val(find_prop(pnode, "name")); 399 400 if ((version != NULL) && (name != NULL)) { 401 log_printf("FEPS: %s Rev ", name); 402 if (*version == 0xa0) { 403 log_printf("2.0\n", 0); 404 } else if (*version == 0x20) { 405 log_printf("2.1\n", 0); 406 } else { 407 log_printf("%x\n", *version, 0); 408 } 409 } 410 } 411 log_printf("\n", 0); 412 413 display_ffb(bnode, 0); 414 } 415 416 /* 417 * print the header and call display_dev_node() to walk the device 418 * tree (darwin platform only). 419 */ 420 static void 421 display_sabre_pci(Board_node *board) 422 { 423 if (board == NULL) 424 return; 425 426 log_printf(" Bus# Freq\n", 0); 427 log_printf("Brd Type MHz Slot " 428 "Name Model", 0); 429 log_printf("\n", 0); 430 log_printf("--- ---- ---- ---- " 431 "-------------------------------- ----------------------", 0); 432 log_printf("\n", 0); 433 display_dev_node(board->nodes, 0); 434 log_printf("\n", 0); 435 } 436 437 438 /* 439 * Recursively traverse the device tree and use tree depth as filter. 440 * called by: display_sabre_pci() 441 */ 442 static void 443 display_dev_node(Prom_node *np, int depth) 444 { 445 char *name, *model, *compat, *regval; 446 unsigned int reghi; 447 448 if (!np) 449 return; 450 if (depth > 2) 451 return; 452 453 name = get_prop_val(find_prop(np, "name")); 454 model = get_prop_val(find_prop(np, "model")); 455 compat = get_prop_val(find_prop(np, "compatible")); 456 regval = get_prop_val(find_prop(np, "reg")); 457 458 if (!regval) 459 return; 460 else 461 reghi = *(int *)regval; 462 463 if (!model) 464 model = ""; 465 if (!name) 466 name = ""; 467 468 if (depth == 2) { 469 char buf[256]; 470 if (compat) 471 (void) sprintf(buf, "%s-%s", name, compat); 472 else 473 (void) sprintf(buf, "%s", name); 474 475 log_printf(" 0 PCI-%d 33 ", PCI_BUS(reghi), 0); 476 log_printf("%3d ", PCI_DEVICE(reghi), 0); 477 log_printf("%-32.32s", buf, 0); 478 log_printf(strlen(buf) > 32 ? "+ " : " ", 0); 479 log_printf("%-22.22s", model, 0); 480 log_printf(strlen(model) > 22 ? "+" : "", 0); 481 log_printf("\n", 0); 482 483 #ifdef DEBUG 484 if (!compat) 485 compat = ""; 486 printf("bus=%d slot=%d name=%s model=%s compat=%s\n", 487 PCI_BUS(reghi), PCI_DEVICE(reghi), name, model, compat); 488 #endif 489 } 490 491 if ((!strstr(name, "ebus")) && (!strstr(name, "ide"))) 492 display_dev_node(np->child, depth+1); 493 display_dev_node(np->sibling, depth); 494 } 495 496 /* 497 * display_sbus 498 * Display all the SBus IO cards on this board. 499 */ 500 void 501 display_sbus(Board_node *board) 502 { 503 struct io_card card; 504 struct io_card *card_list = NULL; 505 int freq; 506 int card_num; 507 void *value; 508 Prom_node *sbus; 509 Prom_node *card_node; 510 511 if (board == NULL) 512 return; 513 514 for (sbus = dev_find_node(board->nodes, SBUS_NAME); sbus != NULL; 515 sbus = dev_next_node(sbus, SBUS_NAME)) { 516 517 /* Skip failed nodes for now */ 518 if (node_failed(sbus)) 519 continue; 520 521 /* Calculate SBus frequency in MHz */ 522 value = get_prop_val(find_prop(sbus, "clock-frequency")); 523 if (value != NULL) 524 freq = ((*(int *)value) + 500000) / 1000000; 525 else 526 freq = -1; 527 528 for (card_node = sbus->child; card_node != NULL; 529 card_node = card_node->sibling) { 530 char *model; 531 char *name; 532 char *child_name; 533 534 card_num = get_sbus_slot(card_node); 535 if (card_num == -1) 536 continue; 537 538 /* Fill in card information */ 539 card.display = 1; 540 card.freq = freq; 541 card.board = board->board_num; 542 (void) sprintf(card.bus_type, "SBus"); 543 card.slot = card_num; 544 card.status[0] = '\0'; 545 546 /* Try and get card status */ 547 value = get_prop_val(find_prop(card_node, "status")); 548 if (value != NULL) 549 (void) strncpy(card.status, (char *)value, 550 MAXSTRLEN); 551 552 /* XXX - For now, don't display failed cards */ 553 if (strstr(card.status, "fail") != NULL) 554 continue; 555 556 /* 557 * sets the machine_type var if not already set 558 */ 559 get_machine_type(); 560 561 /* 562 * For desktops, the only high slot number that 563 * needs to be displayed is the # 14 slot. 564 */ 565 if (machine_type == MTYPE_DEFAULT && 566 card_num >= MX_SBUS_SLOTS && card_num != 14) { 567 continue; 568 } 569 570 /* Now gather all of the node names for that card */ 571 model = (char *)get_prop_val(find_prop(card_node, 572 "model")); 573 name = get_node_name(card_node); 574 575 if (name == NULL) 576 continue; 577 578 card.name[0] = '\0'; 579 card.model[0] = '\0'; 580 581 /* Figure out how we want to display the name */ 582 child_name = get_node_name(card_node->child); 583 if ((card_node->child != NULL) && 584 (child_name != NULL)) { 585 value = get_prop_val(find_prop(card_node->child, 586 "device_type")); 587 if (value != NULL) 588 (void) sprintf(card.name, "%s/%s (%s)", 589 name, child_name, 590 (char *)value); 591 else 592 (void) sprintf(card.name, "%s/%s", name, 593 child_name); 594 } else { 595 (void) strncpy(card.name, name, MAXSTRLEN); 596 } 597 598 if (model != NULL) 599 (void) strncpy(card.model, model, MAXSTRLEN); 600 601 card_list = insert_io_card(card_list, &card); 602 } 603 } 604 605 /* We're all done gathering card info, now print it out */ 606 display_io_cards(card_list); 607 free_io_cards(card_list); 608 } 609 610 static void 611 get_machine_type(void) 612 { 613 char name[MAXSTRLEN]; 614 615 machine_type = MTYPE_DEFAULT; 616 617 /* Figure out what kind of machine we're on */ 618 if (sysinfo(SI_PLATFORM, name, MAXSTRLEN) != -1) { 619 if (strcmp(name, "SUNW,Ultra-5_10") == 0) 620 machine_type = MTYPE_DARWIN; 621 } 622 } 623