/*++
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;
}