729 lines
17 KiB
C
729 lines
17 KiB
C
/*++
|
||
|
||
Copyright (c) 1989-1993 Microsoft Corporation
|
||
|
||
Module Name:
|
||
|
||
stubs.c
|
||
|
||
Abstract:
|
||
|
||
This module implements bug check and system shutdown code.
|
||
|
||
Author:
|
||
|
||
Mark Lucovsky (markl) 30-Aug-1990
|
||
|
||
Environment:
|
||
|
||
Kernel mode only.
|
||
|
||
Revision History:
|
||
|
||
--*/
|
||
|
||
#include "ki.h"
|
||
|
||
//
|
||
// Define forward referenced prototypes.
|
||
//
|
||
|
||
VOID
|
||
KiScanBugCheckCallbackList (
|
||
VOID
|
||
);
|
||
|
||
//
|
||
// Define bug count recursion counter and a context buffer.
|
||
//
|
||
|
||
ULONG KeBugCheckCount = 1;
|
||
|
||
|
||
VOID
|
||
KeBugCheck (
|
||
IN ULONG BugCheckCode
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This function crashes the system in a controlled manner.
|
||
|
||
Arguments:
|
||
|
||
BugCheckCode - Supplies the reason for the bug check.
|
||
|
||
Return Value:
|
||
|
||
None.
|
||
|
||
--*/
|
||
{
|
||
KeBugCheckEx(BugCheckCode,0,0,0,0);
|
||
}
|
||
|
||
ULONG KiBugCheckData[5];
|
||
|
||
BOOLEAN
|
||
KeGetBugMessageText(
|
||
IN ULONG MessageId,
|
||
IN PANSI_STRING ReturnedString OPTIONAL
|
||
)
|
||
{
|
||
ULONG i;
|
||
PUCHAR s;
|
||
PMESSAGE_RESOURCE_BLOCK MessageBlock;
|
||
PUCHAR Buffer;
|
||
BOOLEAN Result;
|
||
|
||
Result = FALSE;
|
||
try {
|
||
if (KiBugCodeMessages != NULL) {
|
||
MessageBlock = &KiBugCodeMessages->Blocks[0];
|
||
for (i = KiBugCodeMessages->NumberOfBlocks; i; i--) {
|
||
if (MessageId >= MessageBlock->LowId &&
|
||
MessageId <= MessageBlock->HighId) {
|
||
|
||
s = (PCHAR)KiBugCodeMessages + MessageBlock->OffsetToEntries;
|
||
for (i = MessageId - MessageBlock->LowId; i; i--) {
|
||
s += ((PMESSAGE_RESOURCE_ENTRY)s)->Length;
|
||
}
|
||
|
||
Buffer = ((PMESSAGE_RESOURCE_ENTRY)s)->Text;
|
||
|
||
i = strlen(Buffer) - 1;
|
||
while (i > 0 && (Buffer[i] == '\n' ||
|
||
Buffer[i] == '\r' ||
|
||
Buffer[i] == 0
|
||
)
|
||
) {
|
||
if (!ARGUMENT_PRESENT( ReturnedString )) {
|
||
Buffer[i] = 0;
|
||
}
|
||
i -= 1;
|
||
}
|
||
|
||
if (!ARGUMENT_PRESENT( ReturnedString )) {
|
||
HalDisplayString(Buffer);
|
||
}
|
||
else {
|
||
ReturnedString->Buffer = Buffer;
|
||
ReturnedString->Length = (USHORT)(i+1);
|
||
ReturnedString->MaximumLength = (USHORT)(i+1);
|
||
}
|
||
Result = TRUE;
|
||
break;
|
||
}
|
||
MessageBlock++;
|
||
}
|
||
}
|
||
} except ( EXCEPTION_EXECUTE_HANDLER ) {
|
||
;
|
||
}
|
||
|
||
return Result;
|
||
}
|
||
|
||
|
||
|
||
PCHAR
|
||
KeBugCheckUnicodeToAnsi(
|
||
IN PUNICODE_STRING UnicodeString,
|
||
OUT PCHAR AnsiBuffer,
|
||
IN ULONG MaxAnsiLength
|
||
)
|
||
{
|
||
PCHAR Dst;
|
||
PWSTR Src;
|
||
ULONG Length;
|
||
|
||
Length = UnicodeString->Length / sizeof( WCHAR );
|
||
if (Length >= MaxAnsiLength) {
|
||
Length = MaxAnsiLength - 1;
|
||
}
|
||
Src = UnicodeString->Buffer;
|
||
Dst = AnsiBuffer;
|
||
while (Length--) {
|
||
*Dst++ = (UCHAR)*Src++;
|
||
}
|
||
*Dst = '\0';
|
||
return AnsiBuffer;
|
||
}
|
||
|
||
|
||
VOID
|
||
KeBugCheckEx (
|
||
IN ULONG BugCheckCode,
|
||
IN ULONG BugCheckParameter1,
|
||
IN ULONG BugCheckParameter2,
|
||
IN ULONG BugCheckParameter3,
|
||
IN ULONG BugCheckParameter4
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This function crashes the system in a controlled manner.
|
||
|
||
Arguments:
|
||
|
||
BugCheckCode - Supplies the reason for the bug check.
|
||
|
||
BugCheckParameter1-4 - Supplies additional bug check information
|
||
|
||
Return Value:
|
||
|
||
None.
|
||
|
||
--*/
|
||
|
||
{
|
||
UCHAR Buffer[100];
|
||
ULONG BugCheckParameters[4];
|
||
CONTEXT ContextSave;
|
||
#if !defined(i386)
|
||
KIRQL OldIrql;
|
||
#endif
|
||
|
||
#if !defined(NT_UP)
|
||
|
||
ULONG TargetSet;
|
||
|
||
#endif
|
||
BOOLEAN hardErrorCalled;
|
||
|
||
//
|
||
// Capture the callers context as closely as possible into the debugger's
|
||
// processor state area of the Prcb
|
||
//
|
||
// N.B. There may be some prologue code that shuffles registers such that
|
||
// they get destroyed.
|
||
//
|
||
|
||
#if defined(i386)
|
||
KiSetHardwareTrigger();
|
||
#else
|
||
KiHardwareTrigger = 1;
|
||
#endif
|
||
|
||
RtlCaptureContext(&KeGetCurrentPrcb()->ProcessorState.ContextFrame);
|
||
KiSaveProcessorControlState(&KeGetCurrentPrcb()->ProcessorState);
|
||
|
||
//
|
||
// this is necessary on machines where the
|
||
// virtual unwind that happens during KeDumpMachineState()
|
||
// destroys the context record
|
||
//
|
||
|
||
ContextSave = KeGetCurrentPrcb()->ProcessorState.ContextFrame;
|
||
|
||
//
|
||
// if we are called by hard error then we don't want to dump the
|
||
// processor state on the machine.
|
||
//
|
||
// We know that we are called by hard error because the bug check
|
||
// code will be FATAL_UNHANDLED_HARD_ERROR. If this is so then the
|
||
// error status passed to harderr is the second parameter, and a pointer
|
||
// to the parameter array from hard error is passed as the third
|
||
// argument.
|
||
//
|
||
|
||
if (BugCheckCode == FATAL_UNHANDLED_HARD_ERROR) {
|
||
|
||
PULONG parameterArray;
|
||
|
||
hardErrorCalled = TRUE;
|
||
|
||
parameterArray = (PULONG)BugCheckParameter2;
|
||
BugCheckCode = BugCheckParameter1;
|
||
BugCheckParameter1 = parameterArray[0];
|
||
BugCheckParameter2 = parameterArray[1];
|
||
BugCheckParameter3 = parameterArray[2];
|
||
BugCheckParameter4 = parameterArray[3];
|
||
|
||
|
||
} else {
|
||
|
||
hardErrorCalled = FALSE;
|
||
|
||
}
|
||
|
||
KiBugCheckData[0] = BugCheckCode;
|
||
KiBugCheckData[1] = BugCheckParameter1;
|
||
KiBugCheckData[2] = BugCheckParameter2;
|
||
KiBugCheckData[3] = BugCheckParameter3;
|
||
KiBugCheckData[4] = BugCheckParameter4;
|
||
|
||
BugCheckParameters[0] = BugCheckParameter1;
|
||
BugCheckParameters[1] = BugCheckParameter2;
|
||
BugCheckParameters[2] = BugCheckParameter3;
|
||
BugCheckParameters[3] = BugCheckParameter4;
|
||
|
||
#if DBG
|
||
|
||
//
|
||
// Don't clear screen if debugger is available.
|
||
//
|
||
|
||
if (KdDebuggerEnabled != FALSE) {
|
||
try {
|
||
DbgPrint("\n*** Fatal System Error: 0x%08lX (0x%08lX,0x%08lX,0x%08lX,0x%08lX)\n\n",
|
||
BugCheckCode,
|
||
BugCheckParameter1,
|
||
BugCheckParameter2,
|
||
BugCheckParameter3,
|
||
BugCheckParameter4
|
||
);
|
||
DbgBreakPointWithStatus(DBG_STATUS_BUGCHECK_FIRST);
|
||
|
||
} except(EXCEPTION_EXECUTE_HANDLER) {
|
||
for (;;) {
|
||
}
|
||
}
|
||
}
|
||
|
||
#endif //DBG
|
||
|
||
//
|
||
// Freeze execution of the system by disabling interrupts and looping
|
||
//
|
||
|
||
KiDisableInterrupts();
|
||
|
||
#if !defined(i386)
|
||
KeRaiseIrql(HIGH_LEVEL, &OldIrql);
|
||
#endif
|
||
|
||
//
|
||
// Don't attempt to display message more than once.
|
||
//
|
||
|
||
if (InterlockedDecrement (&KeBugCheckCount) == 0) {
|
||
|
||
#if !defined(NT_UP)
|
||
|
||
//
|
||
// Attempt to get the other processors frozen now, but don't wait
|
||
// for them to freeze (in case someone is stuck)
|
||
//
|
||
|
||
TargetSet = KeActiveProcessors & ~KeGetCurrentPrcb()->SetMember;
|
||
if (TargetSet != 0) {
|
||
KiIpiSend((KAFFINITY) TargetSet, IPI_FREEZE);
|
||
|
||
//
|
||
// Give the other processors one second to flush their data caches.
|
||
//
|
||
// N.B. This cannot be synchronized since the reason for the bug
|
||
// may be one of the other processors failed.
|
||
//
|
||
|
||
KeStallExecutionProcessor(1000 * 1000);
|
||
}
|
||
|
||
#endif
|
||
|
||
if (!hardErrorCalled) {
|
||
sprintf((char *)Buffer,
|
||
"\n*** STOP: 0x%08lX (0x%08lX,0x%08lX,0x%08lX,0x%08lX)\n",
|
||
BugCheckCode,
|
||
BugCheckParameter1,
|
||
BugCheckParameter2,
|
||
BugCheckParameter3,
|
||
BugCheckParameter4
|
||
);
|
||
|
||
HalDisplayString((char *)Buffer);
|
||
KeGetBugMessageText(BugCheckCode, NULL);
|
||
}
|
||
|
||
//
|
||
// Process the bug check callback list.
|
||
//
|
||
|
||
KiScanBugCheckCallbackList();
|
||
|
||
//
|
||
// If the debugger is not enabled, then dump the machine state and
|
||
// attempt to enable the debbugger.
|
||
//
|
||
|
||
if (!hardErrorCalled) {
|
||
|
||
KeDumpMachineState(
|
||
&KeGetCurrentPrcb()->ProcessorState,
|
||
(char *)Buffer,
|
||
BugCheckParameters,
|
||
4,
|
||
KeBugCheckUnicodeToAnsi);
|
||
|
||
}
|
||
|
||
if (KdDebuggerEnabled == FALSE && KdPitchDebugger == FALSE ) {
|
||
KdInitSystem(NULL, FALSE);
|
||
|
||
} else {
|
||
HalDisplayString("\n");
|
||
}
|
||
|
||
//
|
||
// Write a crash dump and optionally reboot if the system has been
|
||
// so configured.
|
||
//
|
||
|
||
KeGetCurrentPrcb()->ProcessorState.ContextFrame = ContextSave;
|
||
if (!IoWriteCrashDump(BugCheckCode,
|
||
BugCheckParameter1,
|
||
BugCheckParameter2,
|
||
BugCheckParameter3,
|
||
BugCheckParameter4,
|
||
&ContextSave
|
||
)) {
|
||
//
|
||
// If no crashdump take, display the PSS message
|
||
//
|
||
|
||
KeGetBugMessageText(BUGCODE_PSS_MESSAGE, NULL);
|
||
}
|
||
}
|
||
|
||
//
|
||
// Attempt to enter the kernel debugger.
|
||
//
|
||
|
||
while(TRUE) {
|
||
try {
|
||
DbgBreakPointWithStatus(DBG_STATUS_BUGCHECK_SECOND);
|
||
|
||
} except(EXCEPTION_EXECUTE_HANDLER) {
|
||
for (;;) {
|
||
}
|
||
}
|
||
};
|
||
|
||
return;
|
||
}
|
||
|
||
VOID
|
||
KeEnterKernelDebugger (
|
||
VOID
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This function crashes the system in a controlled manner attempting
|
||
to invoke the kernel debugger.
|
||
|
||
Arguments:
|
||
|
||
None.
|
||
|
||
Return Value:
|
||
|
||
None.
|
||
|
||
--*/
|
||
|
||
{
|
||
|
||
#if !defined(i386)
|
||
KIRQL OldIrql;
|
||
#endif
|
||
|
||
//
|
||
// Freeze execution of the system by disabling interrupts and looping
|
||
//
|
||
|
||
KiHardwareTrigger = 1;
|
||
KiDisableInterrupts();
|
||
#if !defined(i386)
|
||
KeRaiseIrql(HIGH_LEVEL, &OldIrql);
|
||
#endif
|
||
if (InterlockedDecrement (&KeBugCheckCount) == 0) {
|
||
if (KdDebuggerEnabled == FALSE) {
|
||
if ( KdPitchDebugger == FALSE ) {
|
||
KdInitSystem(NULL, FALSE);
|
||
}
|
||
}
|
||
}
|
||
|
||
while(TRUE) {
|
||
try {
|
||
DbgBreakPointWithStatus(DBG_STATUS_FATAL);
|
||
|
||
} except(EXCEPTION_EXECUTE_HANDLER) {
|
||
for (;;) {
|
||
}
|
||
}
|
||
};
|
||
}
|
||
|
||
NTKERNELAPI
|
||
BOOLEAN
|
||
KeDeregisterBugCheckCallback (
|
||
IN PKBUGCHECK_CALLBACK_RECORD CallbackRecord
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This function deregisters a bug check callback record.
|
||
|
||
Arguments:
|
||
|
||
CallbackRecord - Supplies a pointer to a bug check callback record.
|
||
|
||
Return Value:
|
||
|
||
If the specified bug check callback record is successfully deregistered,
|
||
then a value of TRUE is returned. Otherwise, a value of FALSE is returned.
|
||
|
||
--*/
|
||
|
||
{
|
||
|
||
BOOLEAN Deregister;
|
||
KIRQL OldIrql;
|
||
|
||
//
|
||
// Raise IRQL to HIGH_LEVEL and acquire the bug check callback list
|
||
// spinlock.
|
||
//
|
||
|
||
KeRaiseIrql(HIGH_LEVEL, &OldIrql);
|
||
KiAcquireSpinLock(&KeBugCheckCallbackLock);
|
||
|
||
//
|
||
// If the specified callback record is currently registered, then
|
||
// deregister the callback record.
|
||
//
|
||
|
||
Deregister = FALSE;
|
||
if (CallbackRecord->State == BufferInserted) {
|
||
CallbackRecord->State = BufferEmpty;
|
||
RemoveEntryList(&CallbackRecord->Entry);
|
||
Deregister = TRUE;
|
||
}
|
||
|
||
//
|
||
// Release the bug check callback spinlock, lower IRQL to its previous
|
||
// value, and return whether the callback record was successfully
|
||
// deregistered.
|
||
//
|
||
|
||
KiReleaseSpinLock(&KeBugCheckCallbackLock);
|
||
KeLowerIrql(OldIrql);
|
||
return Deregister;
|
||
}
|
||
|
||
NTKERNELAPI
|
||
BOOLEAN
|
||
KeRegisterBugCheckCallback (
|
||
IN PKBUGCHECK_CALLBACK_RECORD CallbackRecord,
|
||
IN PKBUGCHECK_CALLBACK_ROUTINE CallbackRoutine,
|
||
IN PVOID Buffer,
|
||
IN ULONG Length,
|
||
IN PUCHAR Component
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This function registers a bug check callback record. If the system
|
||
crashes, then the specified function will be called during bug check
|
||
processing so it may dump additional state in the specified bug check
|
||
buffer.
|
||
|
||
N.B. Bug check callback routines are called in reverse order of
|
||
registration, i.e., in LIFO order.
|
||
|
||
Arguments:
|
||
|
||
CallbackRecord - Supplies a pointer to a callback record.
|
||
|
||
CallbackRoutine - Supplies a pointer to the callback routine.
|
||
|
||
Buffer - Supplies a pointer to the bug check buffer.
|
||
|
||
Length - Supplies the length of the bug check buffer in bytes.
|
||
|
||
Component - Supplies a pointer to a zero terminated component
|
||
identifier.
|
||
|
||
Return Value:
|
||
|
||
If the specified bug check callback record is successfully registered,
|
||
then a value of TRUE is returned. Otherwise, a value of FALSE is returned.
|
||
|
||
--*/
|
||
|
||
{
|
||
|
||
BOOLEAN Inserted;
|
||
KIRQL OldIrql;
|
||
|
||
//
|
||
// Raise IRQL to HIGH_LEVEL and acquire the bug check callback list
|
||
// spinlock.
|
||
//
|
||
|
||
KeRaiseIrql(HIGH_LEVEL, &OldIrql);
|
||
KiAcquireSpinLock(&KeBugCheckCallbackLock);
|
||
|
||
//
|
||
// If the specified callback record is currently not registered, then
|
||
// register the callback record.
|
||
//
|
||
|
||
Inserted = FALSE;
|
||
if (CallbackRecord->State == BufferEmpty) {
|
||
CallbackRecord->CallbackRoutine = CallbackRoutine;
|
||
CallbackRecord->Buffer = Buffer;
|
||
CallbackRecord->Length = Length;
|
||
CallbackRecord->Component = Component;
|
||
CallbackRecord->Checksum =
|
||
(ULONG)CallbackRoutine + (ULONG)Buffer + Length + (ULONG)Component;
|
||
|
||
CallbackRecord->State = BufferInserted;
|
||
InsertHeadList(&KeBugCheckCallbackListHead, &CallbackRecord->Entry);
|
||
Inserted = TRUE;
|
||
}
|
||
|
||
//
|
||
// Release the bug check callback spinlock, lower IRQL to its previous
|
||
// value, and return whether the callback record was successfully
|
||
// registered.
|
||
//
|
||
|
||
KiReleaseSpinLock(&KeBugCheckCallbackLock);
|
||
KeLowerIrql(OldIrql);
|
||
return Inserted;
|
||
}
|
||
|
||
VOID
|
||
KiScanBugCheckCallbackList (
|
||
VOID
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This function scans the bug check callback list and calls each bug
|
||
check callback routine so it can dump component specific information
|
||
that may identify the cause of the bug check.
|
||
|
||
N.B. The scan of the bug check callback list is performed VERY
|
||
carefully. Bug check callback routines are called at HIGH_LEVEL
|
||
and may not acquire ANY resources.
|
||
|
||
Arguments:
|
||
|
||
None.
|
||
|
||
Return Value:
|
||
|
||
None.
|
||
|
||
--*/
|
||
|
||
{
|
||
|
||
PKBUGCHECK_CALLBACK_RECORD CallbackRecord;
|
||
ULONG Checksum;
|
||
ULONG Index;
|
||
PLIST_ENTRY LastEntry;
|
||
PLIST_ENTRY ListHead;
|
||
PLIST_ENTRY NextEntry;
|
||
PUCHAR Source;
|
||
|
||
//
|
||
// If the bug check callback listhead is not initialized, then the
|
||
// bug check has occured before the system has gotten far enough
|
||
// in the initialization code to enable anyone to register a callback.
|
||
//
|
||
|
||
ListHead = &KeBugCheckCallbackListHead;
|
||
if ((ListHead->Flink != NULL) && (ListHead->Blink != NULL)) {
|
||
|
||
//
|
||
// Scan the bug check callback list.
|
||
//
|
||
|
||
LastEntry = ListHead;
|
||
NextEntry = ListHead->Flink;
|
||
while (NextEntry != ListHead) {
|
||
|
||
//
|
||
// The next entry address must be aligned properly, the
|
||
// callback record must be readable, and the callback record
|
||
// must have back link to the last entry.
|
||
//
|
||
|
||
if (((ULONG)NextEntry & (sizeof(ULONG) - 1)) != 0) {
|
||
return;
|
||
|
||
} else {
|
||
CallbackRecord = CONTAINING_RECORD(NextEntry,
|
||
KBUGCHECK_CALLBACK_RECORD,
|
||
Entry);
|
||
|
||
Source = (PUCHAR)CallbackRecord;
|
||
for (Index = 0; Index < sizeof(KBUGCHECK_CALLBACK_RECORD); Index += 1) {
|
||
if (MmDbgReadCheck((PVOID)Source) == NULL) {
|
||
return;
|
||
}
|
||
|
||
Source += 1;
|
||
}
|
||
|
||
if (CallbackRecord->Entry.Blink != LastEntry) {
|
||
return;
|
||
}
|
||
|
||
//
|
||
// If the callback record has a state of inserted and the
|
||
// computed checksum matches the callback record checksum,
|
||
// then call the specified bug check callback routine.
|
||
//
|
||
|
||
Checksum = (ULONG)CallbackRecord->CallbackRoutine;
|
||
Checksum += (ULONG)CallbackRecord->Buffer;
|
||
Checksum += CallbackRecord->Length;
|
||
Checksum += (ULONG)CallbackRecord->Component;
|
||
if ((CallbackRecord->State == BufferInserted) &&
|
||
(CallbackRecord->Checksum == Checksum)) {
|
||
|
||
//
|
||
// Call the specified bug check callback routine and
|
||
// handle any exceptions that occur.
|
||
//
|
||
|
||
CallbackRecord->State = BufferStarted;
|
||
try {
|
||
(CallbackRecord->CallbackRoutine)(CallbackRecord->Buffer,
|
||
CallbackRecord->Length);
|
||
|
||
CallbackRecord->State = BufferFinished;
|
||
|
||
} except(EXCEPTION_EXECUTE_HANDLER) {
|
||
CallbackRecord->State = BufferIncomplete;
|
||
}
|
||
}
|
||
}
|
||
|
||
LastEntry = NextEntry;
|
||
NextEntry = NextEntry->Flink;
|
||
}
|
||
}
|
||
|
||
return;
|
||
}
|