/*++ Copyright (c) 1991 Microsoft Corporation Module Name: ncrnmi.c Abstract: Provides ncr x86 NMI handler Author: kenr Revision History: --*/ #include "halp.h" #include "bugcodes.h" #include "ncr.h" #include "ncrnls.h" #define SYSTEM_CONTROL_PORT_A 0x92 #define SYSTEM_CONTROL_PORT_B 0x61 #define EISA_EXTENDED_NMI_STATUS 0x461 extern ULONG NCRPlatform; VOID HalpDumpAsicPorts (UCHAR *p, ULONG arg, ...); VOID HalpDumpBitsField (UCHAR bits, char *BitMsgs[]); VOID HexSubPort (PUCHAR *o, ULONG l); VOID HalpDumpFred (UCHAR FredId); VOID HalpDumpVMC (UCHAR VMCId); VOID HalpDumpMCADDR (UCHAR MCADDRId); VOID HalpDumpArbiter (UCHAR ArbiterId); BOOLEAN HalpSUSSwNmi(); ULONG NmiLock; UCHAR HexString[] = "0123456789ABCDEF"; char *NmiStatusRegisterBits[] = { MSG_GLOBAL_NMI, // 0 MSG_SHUTDOWN_OCCURRED, // 1 MSG_LOCAL_EXTERNAL_ERROR, // 2 MSG_SB_READ_DATA_PARITY_ERROR, // 3 MSG_SB_ERROR_L_TERMINATION, // 4 MSG_P5_INTERNAL_ERROR, // 5 NULL, // 6 MSG_PROCESSOR_HALTED // 7 }; char *ParityErrorIntStatusBits[] = { MSG_SB_ADDRESS_PARITY_ERROR, // 0 MSG_SB_DATA_PARITY_ERROR, // 1 NULL, // 2 NULL, // 3 NULL, // 4 MSG_SHUTDOWN_ERROR_INT_L, // 5 MSG_EXTERNAL_ERROR_INT_L, // 6 MSG_P_IERR_L_ERROR_INT_L // 7 }; char *VMCErrorStatusZeroBits[] = { MSG_VDLC_DATA_ERROR, // 0 MSG_LST_ERROR, // 1 MSG_BUS_A_DATA_PARITY_ERROR, // 2 MSG_BUS_B_DATA_PARITY_ERROR, // 3 MSG_LST_UNCORRECTABLE_ERROR, // 4 NULL, // 5 NULL, // 6 NULL // 7 }; char *SBErrorIntStatusBits[] = { MSG_MC_MASTER_ERROR, // 0 MSG_SA_MASTER_ERROR, // 1 MSG_SA_MASTER_ERROR, // 2 MSG_MC_TOE_ERROR, // 3 MSG_ASYNC_ERROR, // 4 MSG_SYNC_ERROR, // 5 MSG_REFRESH_ERROR, // 6 MSG_SXERROR_L // 7 }; char *InterruptStatusOneBits[] = { "PAR_INT_B", // 0 "PAR_INT_A", // 1 "ELATCHD_B", // 2 "ELATCHD_A", // 3 "TOE_B", // 4 "TOE_A", // 5 "PROTO_ERR_B", // 6 "PROTO_ERR_A" // 7 }; char *InterruptStatusTwoBits[] = { "MC_TOE", // 0 "COUGAR_NMI", // 1 "EXP_MC_TOE", // 2 "EXP_COUGAR_NMI", // 3 "POWER_FAIL", // 4 "ERROR_INT", // 5 "SW_NMI", // 6 NULL // 7 }; VOID HalHandleNMI( IN OUT PVOID NmiInfo ) /*++ Routine Description: Called DURING an NMI. The system will BugCheck when an NMI occurs. This function can return the proper bugcheck code, bugcheck itself, or return success which will cause the system to iret from the nmi. This function is called during an NMI - no system services are available. In addition, you don't want to touch any spinlock which is normally used since we may have been interrupted while owning it, etc, etc... Warnings: Do NOT: Make any system calls Attempt to acquire any spinlock used by any code outside the NMI handler Change the interrupt state. Do not execute any IRET inside this code Passing data to non-NMI code must be done using manual interlocked functions. (xchg instructions). Arguments: NmiInfo - Pointer to NMI information structure (TBD) - NULL means no NMI information structure was passed Return Value: BugCheck code --*/ { UCHAR StatusByte; _asm { nmi00: lock bts NmiLock, 0 jnc nmilocked nmi10: test NmiLock, 1 jnz short nmi10 jmp short nmi00 nmilocked: } if (NmiLock & 2) { // some other processor has already processed the Nmi and // determined that the machine should crash - just stop // this processor NmiLock = 2; // free busy bit KeEnterKernelDebugger (); return; } if (NCRPlatform != NCR3360) { // // If is this is the Dump switch // if (HalpSUSSwNmi() == TRUE) { NmiLock = 2; KeEnterKernelDebugger(); return; } } HalDisplayString (MSG_HARDWARE_ERROR1); HalDisplayString (MSG_HARDWARE_ERROR3); StatusByte = READ_PORT_UCHAR((PUCHAR) SYSTEM_CONTROL_PORT_B); if (StatusByte & 0x80) { HalDisplayString (MSG_NMI_PARITY); } if (StatusByte & 0x40) { HalDisplayString (MSG_NMI_CHANNEL_CHECK); } if (NCRPlatform == NCR3360) { HalpDumpFred (0x41); // fred 1 HalpDumpFred (0x61); // fred 2 HalpDumpVMC (0x81); // memory 1 HalpDumpVMC (0x91); // memory 2 HalpDumpArbiter (0xC1); // arbiter HalpDumpMCADDR (0xC0); // mcaddr } else { // // For 3450 and 3550 let SUS go do FRU analysis and log the error to SUS // error log. After SUS logs the error the system will reboot and the // HwMon service will copy the SUS error log to the NT error log. // HalDisplayString ("NMI: 345x/35xx SUS Logging Error\n"); HalpSUSLogError(); } HalDisplayString (MSG_HALT); NmiLock = 2; KeEnterKernelDebugger (); } VOID HalpDumpFred (UCHAR FredId) { UCHAR c; WRITE_PORT_UCHAR ((PUCHAR) 0x97, FredId); c = READ_PORT_UCHAR ((PUCHAR) 0xf801); if (c & 0xf0 != 0x20) { return ; } HalpDumpAsicPorts (MSG_FRED, FredId, 0xf808, 0xf80d); c = READ_PORT_UCHAR ((PUCHAR) 0xf809); HalpDumpBitsField (c, NmiStatusRegisterBits); c = READ_PORT_UCHAR ((PUCHAR) 0xf80b); HalpDumpBitsField (c, ParityErrorIntStatusBits); HalDisplayString("\n"); } VOID HalpDumpVMC (UCHAR VMCId) { UCHAR c; WRITE_PORT_UCHAR ((PUCHAR) 0x97, VMCId); c = READ_PORT_UCHAR ((PUCHAR) 0xf800); if (c != 0x81 && c != VMCId) { return ; } HalpDumpAsicPorts ("VMC %x: S0 = %p S1 = %p S2 = %p BD/D = %p\n", VMCId, 0xf804, 0xf808, 0xf809, 0xf80a); c = READ_PORT_UCHAR ((PUCHAR) 0xf80d); HalpDumpBitsField (c, VMCErrorStatusZeroBits); if (c & 0x04) { HalpDumpAsicPorts (MSG_A_PARITY, 0x13, 0x14, 0x18, 0x19); } if (c & 0x08) { HalpDumpAsicPorts (MSG_B_PARITY, 0x23, 0x24, 0x28, 0x29); } WRITE_PORT_UCHAR ((PUCHAR) 0x97, 0xc1); // select arbiter c = READ_PORT_UCHAR ((PUCHAR) 0xf808); WRITE_PORT_UCHAR ((PUCHAR) 0x97, VMCId); if (c & 0x20) { // check for s_error HalpDumpAsicPorts (MSG_A_TOE, 0x1d, 0x1e); } if (c & 0x10) { // check for s_error HalpDumpAsicPorts (MSG_B_TOE, 0x2d, 0x2e); } HalDisplayString("\n"); } VOID HalpDumpMCADDR (UCHAR MCADDRId) { UCHAR c; WRITE_PORT_UCHAR ((PUCHAR) 0x97, MCADDRId); c = READ_PORT_UCHAR ((PUCHAR) 0xf800); if (c != MCADDRId) { return ; } WRITE_PORT_UCHAR ((PUCHAR) 0xF807, 0); HalpDumpAsicPorts ("MCADDR: %x\n", MCADDRId); HalpDumpAsicPorts (MSG_A_GOOD, 0x40, 0x43, 0x44); HalpDumpAsicPorts (MSG_B_GOOD, 0x45, 0x48, 0x49); HalpDumpAsicPorts (MSG_A_LAST, 0x4a, 0x4d); HalpDumpAsicPorts (MSG_B_LAST, 0x4e, 0x51); WRITE_PORT_UCHAR ((PUCHAR) 0xF806, 0x55); c = READ_PORT_UCHAR((PUCHAR) 0xF803); HalpDumpBitsField (c, SBErrorIntStatusBits); HalpDumpAsicPorts (MSG_MC_ADDRESS, 0x55, 0x5c, 0x5d); // status bits decode as: // 0x04 = 1 is memory, 0 is I/O if (c & 0x08) { HalpDumpAsicPorts (MSG_MC_TIMEOUT, 0x58); } HalpDumpAsicPorts (MSG_MC_PARITY, 0x5e, 0x5e); HalDisplayString ("\n"); } VOID HalpDumpArbiter (UCHAR ArbiterId) { UCHAR c; WRITE_PORT_UCHAR ((PUCHAR) 0x97, ArbiterId); c = READ_PORT_UCHAR ((PUCHAR) 0xf800); if (c != ArbiterId) { return ; } HalpDumpAsicPorts ("Aribter: %x\n", ArbiterId); c = READ_PORT_UCHAR((PUCHAR) 0xF808); HalpDumpBitsField (c, InterruptStatusOneBits); c = READ_PORT_UCHAR((PUCHAR) 0xF809); HalpDumpBitsField (c, InterruptStatusTwoBits); HalDisplayString ("\n"); } VOID HalpDumpBitsField (UCHAR bits, char *BitMsgs[]) { UCHAR i; for (i=0; i < 8; i++) { if (bits & 1 && BitMsgs[i]) { HalDisplayString (" "); HalDisplayString (BitMsgs[i]); HalDisplayString ("\n"); } bits >>= 1; } } VOID HalpDumpAsicPorts (PUCHAR p, ULONG arg, ...) { UCHAR c; PUCHAR o; UCHAR s[150]; PULONG stack; ULONG l; o = s; stack = &arg; while (*p) { if (*p == '%') { p++; l = *(stack++); switch (*(p++)) { // truncate sting if bit-0 is not set case '1': WRITE_PORT_UCHAR ((PUCHAR) 0xF806, (UCHAR) l); c = READ_PORT_UCHAR((PUCHAR) 0xF803); if ((c & 1) == 0) { return ; } break; case 'p': // port value c = READ_PORT_UCHAR((PUCHAR) l); *(o++) = HexString[c >> 4]; *(o++) = HexString[c & 0xF]; break; case 's': // sub-port value HexSubPort (&o, l); break; case 'A': // address at HexSubPort (&o, l); // sub-port HexSubPort (&o, l-1); HexSubPort (&o, l-2); HexSubPort (&o, l-3); break; case 'x': // hex byte *(o++) = HexString[l >> 4]; *(o++) = HexString[l & 0xF]; break; default: *(o++) = '?'; break; } } else { *(o++) = *(p++); } } *o = 0; HalDisplayString (s); } VOID HexSubPort (PUCHAR *o, ULONG l) { UCHAR c; WRITE_PORT_UCHAR ((PUCHAR) 0xF806, (UCHAR) l); c = READ_PORT_UCHAR((PUCHAR) 0xF803); o[0][0] = HexString[c >> 4]; o[0][1] = HexString[c & 0xF]; *o += 2; }