423 lines
11 KiB
C
423 lines
11 KiB
C
|
/*++
|
|||
|
|
|||
|
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;
|
|||
|
}
|