/*
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
* Common Development and Distribution License, Version 1.0 only
* (the "License"). You may not use this file except in compliance
* with the License.
*
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
* See the License for the specific language governing permissions
* and limitations under the License.
*
* When distributing Covered Code, include this CDDL HEADER in each
* file and include the License file at usr/src/OPENSOLARIS.LICENSE.
* If applicable, add the following below this CDDL HEADER, with the
* fields enclosed by brackets "[]" replaced with your own identifying
* information: Portions Copyright [yyyy] [name of copyright owner]
*
* CDDL HEADER END
*/
/*
* Copyright 2005 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*
* Sunfire Platform specific functions.
*
* called when :
* machine_type == MTYPE_SUNFIRE
*/
#pragma ident "%Z%%M% %I% %E% SMI"
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <ctype.h>
#include <string.h>
#include <kvm.h>
#include <varargs.h>
#include <time.h>
#include <dirent.h>
#include <fcntl.h>
#include <errno.h>
#include <sys/openpromio.h>
#include <libintl.h>
#include <syslog.h>
#include "pdevinfo.h"
#include "display.h"
#include "pdevinfo_sun4u.h"
#include "display_sun4u.h"
#include "libprtdiag.h"
#if !defined(TEXT_DOMAIN)
#endif
/* Macros for manipulating UPA IDs and board numbers on Sunfire. */
extern int print_flag;
/*
* these functions will overlay the symbol table of libprtdiag
* at runtime (sunfire systems only)
*/
struct system_kstat_data *kstats);
void display_mid(int mid);
void display_pci(Board_node *);
void display_ffb(Board_node *, int);
void resolve_board_types(Sys_tree *);
/* local functions */
struct grp_info *);
static int disp_err_log(struct system_kstat_data *);
static int disp_env_status(struct system_kstat_data *);
static int disp_keysw_and_leds(struct system_kstat_data *);
static void sunfire_disp_prom_versions(Sys_tree *);
static void erase_msgs(char **);
static void display_hp_boards(struct system_kstat_data *);
static int disp_parts(char **, u_longlong_t, int);
/*
* Error analysis routines. These routines decode data from specified
* error registers. They are meant to be used for decoding the fatal
* hardware reset data passed to the kernel by sun4u POST.
*/
static int analyze_cpu(char **, int, u_longlong_t);
static int analyze_ac(char **, u_longlong_t);
static int analyze_dc(int, char **, u_longlong_t);
/* Define special bits */
/*
* These defines comne from async.h, but it does not get exported from
*/
/* List of parts possible */
/* List of possible parts */
static char *part_str[] = {
"", /* 0, a placeholder for indexing */
"", /* 1, reserved strings shouldn't be printed */
"UPA devices", /* 2 */
"UPA Port A device", /* 3 */
"UPA Port B device", /* 4 */
"Software error", /* 5 */
"Address Controller", /* 6 */
"Undetermined Address Controller in system", /* 7 */
"Data Tags", /* 8 */
"Data Tags for UPA Port A", /* 9 */
"Data Tags for UPA Port B", /* 10 */
"Firehose Controller", /* 11 */
"This Board", /* 12 */
"Undetermined Board in system", /* 13 */
"Board Connector", /* 14 */
"Centerplane pins ", /* 15 */
"Centerplane terminators", /* 16 */
"CPU", /* 17 */
};
/* Ecache parity error messages. Tells which bits are bad. */
static char *ecache_parity[] = {
"Bits 7:0 ",
"Bits 15:8 ",
"Bits 21:16 ",
"Bits 24:22 "
};
struct ac_error {
char *error;
};
/*
* Hardware error register meanings, failed parts and FRUs. The
* following strings are indexed for the bit positions of the
* corresponding bits in the hardware. The code checks bit x of
* the hardware error register and prints out string[x] if the bit
* is turned on.
*
* This database of parts which are probably failed and which FRU's
* to replace was based on knowledge of the Sunfire Programmers Spec.
* and discussions with the hardware designers. The order of the part
* lists and consequently the FRU lists are in the order of most
* likely cause first.
*/
{ /* 0 */
"UPA Port A Error",
{ UPA_A_PART, 0, 0, 0, 0 },
},
{ /* 1 */
"UPA Port B Error",
{ UPA_B_PART, 0, 0, 0, 0 },
},
{ /* 2 */
NULL,
{ RSVD_PART, 0, 0, 0, 0 },
},
{ /* 3 */
NULL,
{ RSVD_PART, 0, 0, 0, 0 },
},
{ /* 4 */
"UPA Interrupt to unmapped destination",
{ BOARD_PART, 0, 0, 0, 0 },
},
{ /* 5 */
"UPA Non-cacheable write to unmapped destination",
{ BOARD_PART, 0, 0, 0, 0 },
},
{ /* 6 */
"UPA Cacheable write to unmapped destination",
{ BOARD_PART, 0, 0, 0, 0 },
},
{ /* 7 */
"Illegal Write Received",
{ BOARD_PART, 0, 0, 0, 0 },
},
{ /* 8 */
"Local Writeback match with line in state S",
},
{ /* 9 */
"Local Read match with valid line in Tags",
},
{ /* 10 */
NULL,
{ RSVD_PART, 0, 0, 0, 0 },
},
{ /* 11 */
NULL,
{ RSVD_PART, 0, 0, 0, 0 },
},
{ /* 12 */
"Tag and Victim were valid during lookup",
},
{ /* 13 */
"Local Writeback matches a victim in state S",
},
{ /* 14 */
"Local Read matches valid line in victim buffer",
},
{ /* 15 */
"Local Read victim bit set and victim is S state",
},
{ /* 16 */
"Local Read Victim bit set and Valid Victim Buffer",
},
{ /* 17 */
NULL,
{ RSVD_PART, 0, 0, 0, 0 },
},
{ /* 18 */
NULL,
{ RSVD_PART, 0, 0, 0, 0 },
},
{ /* 19 */
NULL,
{ RSVD_PART, 0, 0, 0, 0 },
},
{ /* 20 */
"UPA Transaction received in Sleep mode",
{ AC_PART, 0, 0, 0, 0 },
},
{ /* 21 */
"P_FERR error P_REPLY received from UPA Port",
},
{ /* 22 */
"Illegal P_REPLY received from UPA Port",
},
{ /* 23 */
NULL,
{ RSVD_PART, 0, 0, 0, 0 },
},
{ /* 24 */
"Timeout on a UPA Master Port",
{ AC_ANY_PART, BOARD_ANY_PART, 0, 0, 0 },
},
{ /* 25 */
NULL,
{ RSVD_PART, 0, 0, 0, 0 },
},
{ /* 26 */
NULL,
{ RSVD_PART, 0, 0, 0, 0 },
},
{ /* 27 */
NULL,
{ RSVD_PART, 0, 0, 0, 0 },
},
{ /* 28 */
"Coherent Transactions Queue Overflow Error",
},
{ /* 29 */
"Non-cacheable Request Queue Overflow Error",
{ AC_PART, AC_ANY_PART, 0, 0, 0 },
},
{ /* 30 */
"Non-cacheable Reply Queue Overflow Error",
{ AC_PART, 0, 0, 0, 0 },
},
{ /* 31 */
"PREQ Queue Overflow Error",
},
{ /* 32 */
"Foreign DID CAM Overflow Error",
{ AC_PART, AC_ANY_PART, 0, 0, 0 },
},
{ /* 33 */
"FT->UPA Queue Overflow Error",
},
{ /* 34 */
NULL,
{ RSVD_PART, 0, 0, 0, 0 },
},
{ /* 35 */
NULL,
{ RSVD_PART, 0, 0, 0, 0 },
},
{ /* 36 */
"UPA Port B Dtag Parity Error",
{ DTAG_B_PART, AC_PART, 0, 0, 0 },
},
{ /* 37 */
"UPA Port A Dtag Parity Error",
{ DTAG_A_PART, AC_PART, 0, 0, 0 },
},
{ /* 38 */
NULL,
{ RSVD_PART, 0, 0, 0, 0 },
},
{ /* 39 */
NULL,
{ RSVD_PART, 0, 0, 0, 0 },
},
{ /* 40 */
"UPA Bus Parity Error",
},
{ /* 41 */
"Data ID Line Mismatch",
},
{ /* 42 */
"Arbitration Line Mismatch",
},
{ /* 43 */
"Shared Line Parity Mismatch",
},
{ /* 44 */
"FireTruck Control Line Parity Error",
{ AC_PART, BACK_PIN_PART, 0, 0, 0 },
},
{ /* 45 */
"FireTruck Address Bus Parity Error",
{ AC_PART, BACK_PIN_PART, 0, 0, 0 },
},
{ /* 46 */
"Internal RAM Parity Error",
{ AC_PART, 0, 0, 0, 0 },
},
{ /* 47 */
NULL,
{ RSVD_PART, 0, 0, 0, 0 },
},
{ /* 48 */
"Internal Hardware Error",
{ AC_PART, 0, 0, 0, 0 },
},
{ /* 49 */
"FHC Communications Error",
},
/* Bits 50-63 are reserved in this implementation. */
};
/*
* There are only two error bits in the DC shadow chain that are
* important. They indicate an overflow error and a parity error,
* respectively. The other bits are not error bits and should not
* be checked for.
*/
/* defines for the sysio */
int
{
/*
* silently check for any types of machine errors
*/
print_flag = 0;
/* set exit_code to show failures */
exit_code = 1;
}
print_flag = 1;
return (exit_code);
}
/*
* disp_fail_parts
*
* Display the failed parts in the system. This function looks for
* the status property in all PROM nodes. On systems where
* the PROM does not supports passing diagnostic information
* thruogh the device tree, this routine will be silent.
*/
int
{
int exit_code;
int system_failed = 0;
exit_code = 0;
/* go through all of the boards looking for failed units. */
/* find failed chips */
system_failed = 1;
exit_code = 1;
if (print_flag == 0) {
return (exit_code);
}
log_printf("\n", 0);
"Failed Field Replaceable Units (FRU) "
"in System:\n"), 0);
log_printf("=========================="
"====================\n", 0);
}
void *value;
/* sanity check of data retreived from PROM */
continue;
}
/* Find the board type of this board */
board_type = "CPU";
} else {
board_type = "IO";
}
"%s unavailable on %s Board #%d\n"),
"\tPROM fault string: %s\n"), value, 0);
"\tFailed Field Replaceable Unit is "), 0);
/*
* Determine whether FRU is CPU module, system
* board, or SBus card.
*/
"SBus Card %d\n"),
get_sbus_slot(pnode), 0);
"PCI Card %d"),
get_pci_device(pnode), 0);
"UltraSPARC module "
"Board %d Module %d\n"),
} else {
"%s board %d\n"), board_type,
}
}
}
if (!system_failed) {
log_printf("\n", 0);
"No failures found in System\n"), 0);
log_printf("===========================\n", 0);
}
if (system_failed)
return (1);
else
return (0);
}
void
/* Build the memory group tables and interleave data */
/* display total usable installed memory */
/* We display the NVSIMM size totals separately. */
if (memory_total->nvsimm != 0) {
}
}
/*
* This routine displays the memory configuration for all boards in the
* system.
*/
void
{
int group;
" Spare " };
" Failed ", " Uninit. " };
#ifdef lint
#endif
/* Print the header for the memory section. */
log_printf("\n", 0);
log_printf("=========================", 0);
log_printf("=========================", 0);
log_printf("\n", 0);
log_printf("\n", 0);
log_printf(" Intrlv. "
"Intrlv.\n", 0);
log_printf("Brd Bank MB Status Condition Speed Factor "
" With\n", 0);
log_printf("--- ----- ---- ------- ---------- ----- ------- "
"-------\n", 0);
/* Print the Memory groups information. */
/* If this board is not a CPU or MEM board, skip it. */
continue;
}
}
log_printf("\n", 0);
}
}
}
void
{
/* Display Hot plugged, disabled and failed boards */
(void) display_hp_boards(kstats);
/* Display failed units */
(void) disp_fail_parts(tree);
/* Display fault info */
}
void
struct system_kstat_data *kstats)
{
/*
* Now display the last powerfail time and the fatal hardware
* reset information. We do this under a couple of conditions.
* First if the user asks for it. The second is iof the user
* told us to do logging, and we found a system failure.
*/
if (flag) {
/*
* display time of latest powerfail. Not all systems
* have this capability. For those that do not, this
* is just a no-op.
*/
/* Display system environmental conditions. */
(void) disp_env_status(kstats);
/* Display ASIC Chip revs for all boards. */
/* Print the PROM revisions here */
/*
* Display the latest system fatal hardware
* error data, if any. The system holds this
* data in SRAM, so it does not persist
* across power-on resets.
*/
(void) disp_err_log(kstats);
}
}
void
{
}
/*
* display_pci
* Call the generic psycho version of this function.
*/
void
{
}
/*
* display_ffb
* Display all FFBs on this board. It can either be in tabular format,
* or a more verbose format.
*/
void
{
void *value;
return;
/* Fill in common information */
if (table == 1) {
/* Print out in table format */
/* XXX - Get the slot number (hack) */
/* Find out if it's single or double buffered */
if ((*(int *)value) & FFB_B_BUFF)
"Double Buffered");
else
"Single Buffered");
/* Print model number */
(char *)value);
} else {
/* print in long format */
/* Find the device node using upa address */
continue;
*(int *)value);
continue;
break;
}
}
if (fd == -1)
continue;
continue;
log_printf("Board %d FFB Hardware Configuration:\n",
log_printf("-----------------------------------\n", 0);
log_printf("\tBoard rev: %d\n",
log_printf("\tDAC: %s\n",
log_printf("\t3DRAM: %s\n",
log_printf("\n", 0);
}
}
}
/*
* add_node
*
* This function adds a board node to the board structure where that
* that node's physical component lives.
*/
void
{
int board;
Prom_node *p;
/* add this node to the Board list of the appropriate board */
void *value;
/*
* if it is a server, pci nodes and ffb nodes never have
* board number properties and software can find the board
* number from the reg property. It is derived from the
* high word of the 'reg' property, which contains the
* mid.
*/
/* extract the board number from the 'reg' prop. */
"reg"))) == NULL) {
(void) printf("add_node() no reg property\n");
exit(2);
}
}
}
/* find the node with the same board number */
}
/* now attach this prom node to the board list */
/* Insert this node at the end of the list */
else {
p = p->sibling;
}
}
/*
* Function resolve_board_types
*
* After the tree is walked and all the information is gathered, this
* function is called to resolve the type of each board.
*/
void
{
char *type;
continue;
}
}
}
}
/*
* local functions
*/
static void
{
/* Display Prom revision header */
log_printf("System Board PROM revisions:\n", 0);
log_printf("----------------------------\n", 0);
/* For each board, print the POST and OBP versions */
/* find a flashprom node for this board */
/* If no flashprom node found, continue */
continue;
/* flashprom node found, display board# */
}
}
/*
* functions that are only needed inside this library
*/
/*
* build_mem_tables
*
* This routine builds the memory table which tells how much memory
* is present in each SIMM group of each board, what the interleave
* factors are, and the group ID of the interleave group.
*
* The algorithms used are:
* First fill in the sizes of groups.
* Next build lists of all groups with same physical base.
* From #of members in each list, interleave factor is
* determined.
* All members of a certain list get the same interleave
* group ID.
*/
static void
struct system_kstat_data *kstats,
{
int group;
int i;
/* initialize the interleave lists */
intrp++) {
}
int found;
int board;
/*
* Copy the board type field into the group record.
*/
} else {
continue;
}
/* Make sure we have kstats for this board */
if (bksp->ac_kstats_ok == 0) {
/* Mark this group as invalid and move to next one */
continue;
}
/* Find the bank status property */
if (bksp->ac_memstat_ok) {
} else {
}
case StBad:
case StActive:
case StSpare:
break;
default:
break;
}
case ConOK:
case ConFailing:
case ConFailed:
case ConTest:
case ConBad:
break;
default:
break;
}
/* base the group size off of the simmstat kstat. */
if (bksp->simmstat_kstats_ok == 0) {
continue;
}
/* Is it bank 0 or bank 1 */
} else {
}
/* Now decode the size field. */
switch (simm_reg & 0x1f) {
case MEM_SIZE_64M:
break;
case MEM_SIZE_256M:
break;
case MEM_SIZE_1G:
break;
case MEM_SIZE_2G:
break;
default:
continue;
}
/* Decode the speed field */
case MEM_SPEED_50ns:
break;
case MEM_SPEED_60ns:
break;
case MEM_SPEED_70ns:
break;
case MEM_SPEED_80ns:
break;
}
} else { /* assume it is group 1 */
}
/*
* find the interleave list this group belongs on. If the
* interleave list corresponding to this base address is
* not found, then create a new one.
*/
i = 0;
found = 0;
found = 1;
}
i++;
intrp++;
}
/*
* We did not find a matching base. So now i and intrp
* now point to the next interleave group in the list.
*/
if (!found) {
}
}
}
static void
{
int i;
/* Start with total of zero */
/* For now we ignore NVSIMMs. We might want to fix this later. */
}
}
}
static int
{
int i;
int result = 0;
time_t t;
if (!kstats->ft_kstat_ok) {
return (result);
}
if (!result) {
log_printf("\n", 0);
log_printf("Detected System Faults\n", 0);
log_printf("======================\n", 0);
}
result = 1;
/*
* If the fault on this board is PROM inherited, see
* if we can find some failed component information
* in the PROM device tree. The general solution
* would be to fix the fhc driver and have it put in
* more descriptive messages, but that's for another
* day.
*/
char *str;
/*
* If any nodes under this board have a
* status containing "fail", print it out.
*/
while (pn) {
"status"));
0);
}
}
}
} else {
}
log_printf("\tDetected %s",
}
if (!result) {
log_printf("\n", 0);
log_printf("No System Faults found\n", 0);
log_printf("======================\n", 0);
}
log_printf("\n", 0);
return (result);
}
/*
* disp_err_log
*
* Display the fatal hardware reset system error logs. These logs are
* collected by POST and passed up through the kernel to userland.
* They will not necessarily be present in all systems. Their form
* might also be different in different systems.
*
* NOTE - We are comparing POST defined board types here. Do not confuse
* them with kernel board types. The structure being analyzed in this
* function is created by POST. All the defines for it are in reset_info.h,
* which was ported from POST header files.
*/
static int
{
int exit_code = 0;
int i;
char **msgs;
/* start by initializing the err_msgs array to all NULLs */
for (i = 0; i < MAX_MSGS; i++) {
}
/* First check to see that the reset-info kstats are present. */
if (kstats->reset_kstats_ok == 0) {
return (exit_code);
}
log_printf("\n", 0);
"Analysis of most recent Fatal Hardware Watchdog:\n"),
0);
log_printf("======================================================\n",
0);
log_printf("Log Date: %s\n",
/* initialize the vector and the message index. */
msg_idx = 0;
/* Loop Through all of the boards. */
for (i = 0; i < MAX_BOARDS; i++, bdp++) {
/* Is there data for this board? */
continue;
}
/* If it is a CPU Board, look for CPU data. */
/* analyze CPU 0 if present */
}
/* analyze CPU1 if present. */
}
}
/* Always Analyze the AC and the DCs on a board. */
if (msg_idx != 0)
display_msgs(err_msgs, i);
/* If any messages are logged, we have errors */
if (msg_idx != 0) {
exit_code = 1;
}
/* reset the vector and the message index */
msg_idx = 0;
}
return (exit_code);
}
static void
{
int i;
}
}
static void
{
int i;
/* display the header for this board */
log_printf(*msgs, 0);
}
}
/*
* disp_keysw_and_leds
*
* This routine displays the position of the keyswitch and the front panel
* system LEDs. The keyswitch can be in either normal, diagnostic, or
* secure position. The three front panel LEDs are of importance because
* the center LED indicates component failure on the system.
*/
static int
{
int board;
int diag_mode = 0;
int secure_mode = 0;
int result = 0;
/* Check the first valid board to determeine the diag bit */
/* Find the first valid board */
/* If this was successful, break out of loop */
FHC_DIAG_MODE) == 0)
diag_mode = 1;
break;
}
}
/*
* Check the register on the clock-board to determine the
* secure bit.
*/
if (kstats->sys_kstats_ok) {
/* The secure bit is negative logic. */
secure_mode = 1;
}
}
/*
* The system cannot be in diag and secure mode. This is
* illegal.
*/
if (secure_mode && diag_mode) {
result = 2;
return (result);
}
/* Now print the keyswitch position. */
log_printf("Keyswitch position is in ", 0);
if (diag_mode) {
log_printf("Diagnostic Mode\n");
} else if (secure_mode) {
log_printf("Secure Mode\n", 0);
} else {
log_printf("Normal Mode\n");
}
/* display the redundant power status */
if (kstats->sys_kstats_ok) {
log_printf("System Power Status: ", 0);
switch (kstats->power_state) {
case REDUNDANT:
log_printf("Redundant\n", 0);
break;
case MINIMUM:
log_printf("Minimum Available\n", 0);
break;
case BELOW_MINIMUM:
log_printf("Insufficient Power Available\n", 0);
break;
default:
log_printf("Unknown\n", 0);
break;
}
}
if (kstats->sys_kstats_ok) {
/*
* If the center LED is on, then we return a non-zero
* result.
*/
log_printf("System LED Status: GREEN YELLOW "
"GREEN\n", 0);
log_printf("WARNING ", 0);
} else {
log_printf("Normal ", 0);
}
/*
* Left LED is negative logic, center and right LEDs
* are positive logic.
*/
log_printf("ON ", 0);
} else {
log_printf("OFF", 0);
}
log_printf(" ", 0);
log_printf("ON ", 0);
} else {
log_printf("OFF", 0);
}
log_printf(" BLINKING", 0);
}
log_printf("\n", 0);
return (result);
}
/*
* disp_env_status
*
* This routine displays the environmental status passed up from
* device drivers via kstats. The kstat names are defined in
* kernel header files included by this module.
*/
static int
{
int exit_code = 0;
int i;
int is4slot = 0;
/*
* Define some message arrays to make life simpler. These
* temperature trend (enum temp_trend) and temperature state
* (enum temp_state).
*/
"rapidly falling",
"falling",
"stable",
"rising",
"rapidly rising",
"unknown (noisy)"
};
"WARNING ",
" DANGER "
};
log_printf("\n", 0);
log_printf("=========================", 0);
log_printf("=========================", 0);
log_printf("\n", 0);
if (!kstats->sys_kstats_ok) {
"*** Error: Unavailable ***\n\n"));
return (1);
}
/*
* for purposes within this routine,
* 5 slot behaves the same as a 4 slot
*/
is4slot = 1;
log_printf("\n", 0);
log_printf("\nFans:\n", 0);
log_printf("-----\n", 0);
log_printf("Unit Status\n", 0);
log_printf("---- ------\n", 0);
/* Check the status of the Rack Fans */
log_printf("OK\n", 0);
} else {
log_printf("FAIL\n", 0);
exit_code = 1;
}
if (!is4slot) {
/*
* keyswitch and ac box are on 8 & 16 slot only
*/
/* Check the status of the Keyswitch Fan assembly. */
log_printf("OK\n", 0);
} else {
log_printf("FAIL\n", 0);
exit_code = 1;
}
log_printf("OK\n", 0);
} else {
log_printf("FAIL\n", 0);
exit_code = 1;
}
} else {
/*
* peripheral fan is on 4 slot only
* XXX might want to indicate transient states too
*/
if (kstats->psstat_kstat_ok) {
log_printf("PPS OK\n", 0);
PS_FAIL) {
log_printf("PPS FAIL\n", 0);
exit_code = 1;
}
}
}
log_printf("\n", 0);
log_printf("System Temperatures (Celsius):\n", 0);
log_printf("------------------------------\n", 0);
log_printf("Brd State Current Min Max Trend\n", 0);
log_printf("--- ------- ------- --- --- -----\n", 0);
i++, bksp++) {
/* Make sure we have kstats for this board first */
if (!bksp->temp_kstat_ok) {
continue;
}
log_printf("%2d ", i, 0);
/* Print the current state of the temperature */
/* Set exit code for WARNING and DANGER */
exit_code = 1;
/* Print the current temperature */
/* Print the minimum recorded temperature */
/* Print the maximum recorded temperature */
/* Print the current trend in temperature (if available) */
log_printf("unknown\n", 0);
else
}
if (kstats->temp_kstat_ok) {
log_printf("CLK ", 0);
/* Print the current state of the temperature */
/* Set exit code for WARNING or DANGER */
exit_code = 1;
/* Print the current temperature */
/* Print the minimum recorded temperature */
/* Print the maximum recorded temperature */
/* Print the current trend in temperature (if available) */
log_printf("unknown\n\n", 0);
else
log_printf("%s\n\n",
} else {
log_printf("\n");
}
log_printf("\n", 0);
log_printf("Power Supplies:\n", 0);
log_printf("---------------\n", 0);
log_printf("Supply Status\n", 0);
log_printf("--------- ------\n", 0);
if (kstats->psstat_kstat_ok) {
for (i = 0; i < SYS_PS_COUNT; i++) {
/* skip core power supplies that are not present */
continue;
/* Display the unit Number */
switch (i) {
case 0: ps = "0"; break;
case SYS_V5_P_PCH_INDEX: ps =
" Peripheral 5.0v precharge";
break;
case SYS_V12_P_PCH_INDEX: ps =
" Peripheral 12v precharge";
break;
case SYS_V3_PCH_INDEX: ps =
" System 3.3v precharge"; break;
case SYS_V5_PCH_INDEX: ps =
" System 5.0v precharge"; break;
/* skip the peripheral fan here */
case SYS_P_FAN_INDEX:
continue;
}
/* what is the state? */
case PS_OK:
state = "OK";
break;
case PS_FAIL:
state = "FAIL";
exit_code = 1;
break;
/* XXX is this an exit_code condition? */
case PS_OUT:
state = "PPS Out";
exit_code = 1;
break;
case PS_UNKNOWN:
state = "Unknown";
break;
default:
state = "Illegal State";
break;
}
}
}
/* Check status of the system AC Power Source */
log_printf("OK\n", 0);
} else {
log_printf("failed\n", 0);
exit_code = 1;
}
log_printf("\n", 0);
return (exit_code);
}
/*
* Many of the ASICs present in fusion machines have implementation and
* version numbers stored in the OBP device tree. These codes are displayed
* in this routine in an effort to aid Engineering and Field service
* in detecting old ASICs which may have bugs in them.
*/
static void
{
int isplusbrd;
"Memory", "Dual-SBus", "UPA-SBus",
"Dual-PCI", "Disk", "Clock",
"Dual-SBus-SOC+", "UPA-SBus-SOC+"};
/* Print the header */
log_printf("\n", 0);
log_printf("=========================", 0);
log_printf(" HW Revisions ", 0);
log_printf("=========================", 0);
log_printf("\n", 0);
log_printf("\n", 0);
/* Else this is a Sunfire or campfire */
log_printf("ASIC Revisions:\n", 0);
log_printf("---------------\n", 0);
/* Display Firetruck ASIC Revisions first */
log_printf("Brd FHC AC SBus0 SBus1 PCI0 PCI1 FEPS", 0);
log_printf(" Board Type Attributes", 0);
log_printf("\n", 0);
log_printf("--- --- -- ----- ----- ---- ---- ----", 0);
log_printf(" ---------- ----------", 0);
log_printf("\n", 0);
/*
* Display all of the FHC, AC, and chip revisions for the entire
* machine. The AC anf FHC chip revs are available from the device
* tree that was read out of the PROM, but the DC chip revs will be
* read via a kstat. The interfaces for this are not completely
* available at this time.
*/
int *version;
/* Display the header with the board number */
/* display the FHC version */
log_printf(" ", 0);
} else {
"version#"))) == NULL) {
log_printf(" ", 0);
} else {
}
}
/* display the AC version */
log_printf(" ", 0);
} else {
"version#"))) == NULL) {
log_printf(" ", 0);
} else {
}
}
/* Find sysio 0 on board and print rev */
log_printf(" ", 0);
} else {
"version#"))) == NULL) {
log_printf(" ", 0);
} else {
}
}
/* Find sysio 1 on board and print rev */
log_printf(" ", 0);
} else {
"version#"))) == NULL) {
log_printf(" ", 0);
} else {
}
}
/* Find Psycho 0 on board and print rev */
log_printf(" ", 0);
} else {
"version#"))) == NULL) {
log_printf(" ", 0);
} else {
}
}
/* Find Psycho 1 on board and print rev */
log_printf(" ", 0);
} else {
"version#"))) == NULL) {
log_printf(" ", 0);
} else {
}
}
/* Find the FEPS on board and print rev */
"hm-rev"))) != NULL) {
if (*version == 0xa0) {
log_printf(" 2.0 ", 0);
} else if (*version == 0x20) {
log_printf(" 2.1 ", 0);
} else {
}
}
} else
log_printf(" ", 0);
/* print out the board type */
if (isplusbrd)
log_printf("100MHz Capable", 0);
else
log_printf("84MHz Capable", 0);
log_printf("\n", 0);
}
log_printf("\n", 0);
/* Now display the FFB board component revisions */
display_ffb(bnode, 0);
}
}
static void
{
int i;
int j;
int hp_found = 0;
char *state;
continue;
}
hp_found = 1;
}
/* return if there are no hotplug boards in the system. */
if (!hp_found) {
return;
}
if (hp_found != 0) {
log_printf("\n", 0);
log_printf("Detached Boards\n", 0);
log_printf("===============\n", 0);
log_printf(" Slot State Type Info\n", 0);
log_printf(" ---- --------- ------ ----"
"-------------------------------------\n", 0);
}
/* Display all detached boards */
continue;
}
case UNKNOWN_STATE:
state = "unknown";
break;
case ACTIVE_STATE:
state = "active";
break;
case LOWPOWER_STATE:
state = "low-power";
break;
case HOTPLUG_STATE:
state = "hot-plug";
break;
case DISABLED_STATE:
state = "disabled";
break;
case FAILED_STATE:
state = "failed";
break;
default:
state = "unknown";
break;
}
case MEM_BOARD:
break;
case CPU_BOARD:
/* Cannot display CPU info for disabled boards */
break;
}
/* Display both CPUs if present */
for (j = 0; j < 2; j++, cpu++) {
log_printf("CPU %d: ", j, 0);
/* Print the rated speed of the CPU. */
0);
} else {
log_printf("no CPU ", 0);
continue;
}
/* Display the size of the cache */
if (cpu->cache_size != 0) {
log_printf(" %0.1fM ",
(float)cpu->cache_size /
(float)(1024*1024), 0);
} else {
log_printf(" ", 0);
}
}
break;
case IO_2SBUS_BOARD:
break;
case IO_2SBUS_SOCPLUS_BOARD:
break;
case IO_SBUS_FFB_BOARD:
case FFB_SINGLE:
log_printf("Single buffered FFB", 0);
break;
case FFB_DOUBLE:
log_printf("Double buffered FFB", 0);
break;
case FFB_NOT_FOUND:
log_printf("No FFB installed", 0);
break;
default:
log_printf("Illegal FFB size", 0);
break;
}
break;
case FFB_SINGLE:
log_printf("Single buffered FFB", 0);
break;
case FFB_DOUBLE:
log_printf("Double buffered FFB", 0);
break;
case FFB_NOT_FOUND:
log_printf("No FFB installed", 0);
break;
default:
log_printf("Illegal FFB size", 0);
break;
}
break;
case IO_PCI_BOARD:
break;
case DISK_BOARD:
for (j = 0; j < 2; j++) {
log_printf("Disk %d:", j, 0);
log_printf(" Target: %2d ",
0);
} else {
log_printf(" no disk ", 0);
}
}
break;
case UNKNOWN_BOARD:
case UNINIT_BOARD:
default:
log_printf("UNKNOWN ", 0);
break;
}
log_printf("\n");
}
}
/*
* Analysis functions:
*
* Most of the Fatal error data analyzed from error registers is not
* very complicated. This is because the FRUs for errors detected by
* most parts is either a CPU module, a FFB, or the system board
* itself.
* The analysis of the Address Controller errors is the most complicated.
* These errors can be caused by other boards as well as the local board.
*/
/*
* analyze_cpu
*
* Analyze the CPU MFSR passed in and determine what type of fatal
* hardware errors occurred at the time of the crash. This function
* returns a pointer to a string to the calling routine.
*/
static int
{
int count = 0;
int i;
int syndrome;
return (count);
}
if (afsr & P_AFSR_ETP) {
cpu_id);
/* extract syndrome for afsr */
/* now concat the parity syndrome msg */
for (i = 0; i < 4; i++) {
if ((0x1 << i) & syndrome) {
}
}
count++;
}
if (afsr & P_AFSR_ISAP) {
"CPU %d Incoming System Address Parity Error\n",
cpu_id);
count++;
}
return (count);
}
/*
* analyze_ac
*
* This function checks the AC error register passed in and checks
* for any errors that occured during the fatal hardware reset.
*/
static int
{
int i;
int count = 0;
int tmp_cnt;
return (count);
}
for (i = 2; i < MAX_BITS; i++) {
count++;
/* display the part that might cause this */
}
}
}
return (count);
}
/*
* analyze_dc
*
* This routine checks the DC shdow chain and tries to determine
* what type of error might have caused the fatal hardware reset
* error.
*/
static int
{
int i;
int count = 0;
return (count);
}
/*
* The DC scan data is contained in 8 bytes, one byte per
* DC. There are 8 DCs on a system board.
*/
for (i = 0; i < 8; i++) {
if (dc_error & DC_OVERFLOW) {
count++;
}
count++;
}
}
return (count);
}
static int
{
int count = 0;
int part;
int i;
return (count);
}
count++;
if (ac_error & UPA_PORT_A) {
part = UPA_A_PART;
} else if (ac_error & UPA_PORT_B) {
part = UPA_B_PART;
}
}
if (ac_error & UPA_PORT_A) {
part = DTAG_A_PART;
} else if (ac_error & UPA_PORT_B) {
part = DTAG_B_PART;
}
}
count++;
}
return (count);
}