You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
2007 lines
56 KiB
2007 lines
56 KiB
/*++
|
|
|
|
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 NOEXTAPI
|
|
#include "wdbgexts.h"
|
|
#include <inbv.h>
|
|
#include <hdlsblk.h>
|
|
#include <hdlsterm.h>
|
|
|
|
//
|
|
//
|
|
//
|
|
|
|
extern KDDEBUGGER_DATA64 KdDebuggerDataBlock;
|
|
|
|
extern PVOID ExPoolCodeStart;
|
|
extern PVOID ExPoolCodeEnd;
|
|
extern PVOID MmPoolCodeStart;
|
|
extern PVOID MmPoolCodeEnd;
|
|
extern PVOID MmPteCodeStart;
|
|
extern PVOID MmPteCodeEnd;
|
|
|
|
extern PWD_HANDLER ExpWdHandler;
|
|
extern PVOID ExpWdHandlerContext;
|
|
|
|
#if defined(_AMD64_)
|
|
|
|
#define PROGRAM_COUNTER(_trapframe) ((_trapframe)->Rip)
|
|
|
|
#elif defined(_X86_)
|
|
|
|
#define PROGRAM_COUNTER(_trapframe) ((_trapframe)->Eip)
|
|
|
|
#elif defined(_IA64_)
|
|
|
|
#define PROGRAM_COUNTER(_trapframe) ((_trapframe)->StIIP)
|
|
|
|
#else
|
|
|
|
#error "no target architecture"
|
|
|
|
#endif
|
|
|
|
//
|
|
// Define forward referenced prototypes.
|
|
//
|
|
|
|
VOID
|
|
KiScanBugCheckCallbackList (
|
|
VOID
|
|
);
|
|
|
|
VOID
|
|
KiInvokeBugCheckEntryCallbacks (
|
|
VOID
|
|
);
|
|
|
|
//
|
|
// Define bug count recursion counter and a context buffer.
|
|
//
|
|
|
|
LONG KeBugCheckCount = 1;
|
|
|
|
|
|
VOID
|
|
KeBugCheck (
|
|
IN ULONG BugCheckCode
|
|
)
|
|
{
|
|
KeBugCheck2(BugCheckCode,0,0,0,0,NULL);
|
|
}
|
|
|
|
VOID
|
|
KeBugCheckEx (
|
|
IN ULONG BugCheckCode,
|
|
IN ULONG_PTR P1,
|
|
IN ULONG_PTR P2,
|
|
IN ULONG_PTR P3,
|
|
IN ULONG_PTR P4
|
|
)
|
|
{
|
|
KeBugCheck2(BugCheckCode,P1,P2,P3,P4,NULL);
|
|
}
|
|
|
|
ULONG_PTR KiBugCheckData[5];
|
|
PUNICODE_STRING KiBugCheckDriver;
|
|
|
|
BOOLEAN
|
|
KeGetBugMessageText(
|
|
IN ULONG MessageId,
|
|
IN PANSI_STRING ReturnedString OPTIONAL
|
|
)
|
|
{
|
|
SIZE_T i;
|
|
PCHAR s;
|
|
PMESSAGE_RESOURCE_BLOCK MessageBlock;
|
|
PCHAR Buffer;
|
|
BOOLEAN Result;
|
|
|
|
Result = FALSE;
|
|
try {
|
|
if (KiBugCodeMessages != NULL) {
|
|
MmMakeKernelResourceSectionWritable ();
|
|
MessageBlock = &KiBugCodeMessages->Blocks[0];
|
|
for (i = KiBugCodeMessages->NumberOfBlocks; i; i -= 1) {
|
|
if (MessageId >= MessageBlock->LowId &&
|
|
MessageId <= MessageBlock->HighId) {
|
|
|
|
s = (PCHAR)KiBugCodeMessages + MessageBlock->OffsetToEntries;
|
|
for (i = MessageId - MessageBlock->LowId; i; i -= 1) {
|
|
s += ((PMESSAGE_RESOURCE_ENTRY)s)->Length;
|
|
}
|
|
|
|
Buffer = (PCHAR)((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 )) {
|
|
InbvDisplayString((PUCHAR)Buffer);
|
|
InbvDisplayString((PUCHAR)"\r");
|
|
}
|
|
else {
|
|
ReturnedString->Buffer = Buffer;
|
|
ReturnedString->Length = (USHORT)(i+1);
|
|
ReturnedString->MaximumLength = (USHORT)(i+1);
|
|
}
|
|
Result = TRUE;
|
|
break;
|
|
}
|
|
MessageBlock += 1;
|
|
}
|
|
}
|
|
} 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
|
|
KiBugCheckDebugBreak (
|
|
IN ULONG BreakStatus
|
|
)
|
|
{
|
|
do {
|
|
|
|
try {
|
|
|
|
//
|
|
// Issue a breakpoint
|
|
//
|
|
|
|
DbgBreakPointWithStatus (BreakStatus);
|
|
|
|
} except(EXCEPTION_EXECUTE_HANDLER) {
|
|
|
|
HEADLESS_RSP_QUERY_INFO Response;
|
|
NTSTATUS Status;
|
|
SIZE_T Length;
|
|
|
|
//
|
|
// Failed to issue the breakpoint, must be no debugger. Now, give
|
|
// the headless terminal a chance to reboot the system, if there is one.
|
|
//
|
|
Length = sizeof(HEADLESS_RSP_QUERY_INFO);
|
|
Status = HeadlessDispatch(HeadlessCmdQueryInformation,
|
|
NULL,
|
|
0,
|
|
&Response,
|
|
&Length
|
|
);
|
|
|
|
if (NT_SUCCESS(Status) &&
|
|
(Response.PortType == HeadlessSerialPort) &&
|
|
Response.Serial.TerminalAttached) {
|
|
|
|
HeadlessDispatch(HeadlessCmdPutString,
|
|
"\r\n",
|
|
sizeof("\r\n"),
|
|
NULL,
|
|
NULL
|
|
);
|
|
|
|
for (;;) {
|
|
HeadlessDispatch(HeadlessCmdDoBugCheckProcessing, NULL, 0, NULL, NULL);
|
|
}
|
|
|
|
}
|
|
|
|
//
|
|
// No terminal, or it failed, halt the system
|
|
//
|
|
|
|
try {
|
|
|
|
HalHaltSystem();
|
|
|
|
} except(EXCEPTION_EXECUTE_HANDLER) {
|
|
|
|
for (;;) {
|
|
}
|
|
|
|
}
|
|
}
|
|
} while (BreakStatus != DBG_STATUS_BUGCHECK_FIRST);
|
|
}
|
|
|
|
PVOID
|
|
KiPcToFileHeader(
|
|
IN PVOID PcValue,
|
|
OUT PLDR_DATA_TABLE_ENTRY *DataTableEntry,
|
|
IN LOGICAL DriversOnly,
|
|
OUT PBOOLEAN InKernelOrHal
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This function returns the base of an image that contains the
|
|
specified PcValue. An image contains the PcValue if the PcValue
|
|
is within the ImageBase, and the ImageBase plus the size of the
|
|
virtual image.
|
|
|
|
Arguments:
|
|
|
|
PcValue - Supplies a PcValue.
|
|
|
|
DataTableEntry - Supplies a pointer to a variable that receives the
|
|
address of the data table entry that describes the image.
|
|
|
|
DriversOnly - Supplies TRUE if the kernel and HAL should be skipped.
|
|
|
|
InKernelOrHal - Set to TRUE if the PcValue is in the kernel or the HAL.
|
|
This only has meaning if DriversOnly is FALSE.
|
|
|
|
Return Value:
|
|
|
|
NULL - No image was found that contains the PcValue.
|
|
|
|
NON-NULL - Returns the base address of the image that contains the
|
|
PcValue.
|
|
|
|
--*/
|
|
|
|
{
|
|
ULONG i;
|
|
PLIST_ENTRY ModuleListHead;
|
|
PLDR_DATA_TABLE_ENTRY Entry;
|
|
PLIST_ENTRY Next;
|
|
ULONG_PTR Bounds;
|
|
PVOID ReturnBase, Base;
|
|
|
|
//
|
|
// If the module list has been initialized, then scan the list to
|
|
// locate the appropriate entry.
|
|
//
|
|
|
|
if (KeLoaderBlock != NULL) {
|
|
ModuleListHead = &KeLoaderBlock->LoadOrderListHead;
|
|
|
|
} else {
|
|
ModuleListHead = &PsLoadedModuleList;
|
|
}
|
|
|
|
*InKernelOrHal = FALSE;
|
|
|
|
ReturnBase = NULL;
|
|
Next = ModuleListHead->Flink;
|
|
if (Next != NULL) {
|
|
i = 0;
|
|
while (Next != ModuleListHead) {
|
|
if (MmIsAddressValid(Next) == FALSE) {
|
|
return NULL;
|
|
}
|
|
i += 1;
|
|
if ((i <= 2) && (DriversOnly == TRUE)) {
|
|
Next = Next->Flink;
|
|
continue;
|
|
}
|
|
|
|
Entry = CONTAINING_RECORD(Next,
|
|
LDR_DATA_TABLE_ENTRY,
|
|
InLoadOrderLinks);
|
|
|
|
Next = Next->Flink;
|
|
Base = Entry->DllBase;
|
|
Bounds = (ULONG_PTR)Base + Entry->SizeOfImage;
|
|
if ((ULONG_PTR)PcValue >= (ULONG_PTR)Base && (ULONG_PTR)PcValue < Bounds) {
|
|
*DataTableEntry = Entry;
|
|
ReturnBase = Base;
|
|
if (i <= 2) {
|
|
*InKernelOrHal = TRUE;
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
return ReturnBase;
|
|
}
|
|
|
|
|
|
|
|
VOID
|
|
KiDumpParameterImages(
|
|
IN PCHAR Buffer,
|
|
IN PULONG_PTR BugCheckParameters,
|
|
IN ULONG NumberOfParameters,
|
|
IN PKE_BUGCHECK_UNICODE_TO_ANSI UnicodeToAnsiRoutine
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This function formats and displays the image names of boogcheck parameters
|
|
that happen to match an address in an image.
|
|
|
|
Arguments:
|
|
|
|
Buffer - Supplies a pointer to a buffer to be used to output machine
|
|
state information.
|
|
|
|
BugCheckParameters - Supplies additional bugcheck information.
|
|
|
|
NumberOfParameters - sizeof BugCheckParameters array.
|
|
if just 1 parameter is passed in, just save the string.
|
|
|
|
UnicodeToAnsiRoutine - Supplies a pointer to a routine to convert Unicode
|
|
strings to Ansi strings without touching paged translation tables.
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
|
|
{
|
|
PUNICODE_STRING BugCheckDriver;
|
|
ULONG i;
|
|
PLDR_DATA_TABLE_ENTRY DataTableEntry;
|
|
PVOID ImageBase;
|
|
CHAR AnsiBuffer[ 32 ];
|
|
ULONG DateStamp;
|
|
PIMAGE_NT_HEADERS NtHeaders;
|
|
BOOLEAN FirstPrint = TRUE;
|
|
BOOLEAN InKernelOrHal;
|
|
PUNICODE_STRING DriverName;
|
|
|
|
//
|
|
// At this point the context record contains the machine state at the
|
|
// call to bug check.
|
|
//
|
|
// Put out the system version and the title line with the PSR and FSR.
|
|
//
|
|
|
|
//
|
|
// Check to see if any BugCheckParameters are valid code addresses.
|
|
// If so, print them for the user.
|
|
//
|
|
|
|
DriverName = NULL;
|
|
|
|
for (i = 0; i < NumberOfParameters; i += 1) {
|
|
|
|
DateStamp = 0;
|
|
ImageBase = KiPcToFileHeader((PVOID) BugCheckParameters[i],
|
|
&DataTableEntry,
|
|
TRUE,
|
|
&InKernelOrHal);
|
|
if (ImageBase == NULL) {
|
|
BugCheckDriver = MmLocateUnloadedDriver ((PVOID)BugCheckParameters[i]);
|
|
|
|
if (BugCheckDriver == NULL){
|
|
continue;
|
|
}
|
|
|
|
DriverName = BugCheckDriver;
|
|
ImageBase = (PVOID)BugCheckParameters[i];
|
|
(*UnicodeToAnsiRoutine) (BugCheckDriver,
|
|
AnsiBuffer,
|
|
sizeof (AnsiBuffer));
|
|
} else {
|
|
|
|
if (MmIsAddressValid(DataTableEntry->DllBase) == TRUE) {
|
|
|
|
NtHeaders = RtlImageNtHeader(DataTableEntry->DllBase);
|
|
if (NtHeaders) {
|
|
DateStamp = NtHeaders->FileHeader.TimeDateStamp;
|
|
}
|
|
}
|
|
DriverName = &DataTableEntry->BaseDllName;
|
|
(*UnicodeToAnsiRoutine)( DriverName,
|
|
AnsiBuffer,
|
|
sizeof( AnsiBuffer ));
|
|
}
|
|
|
|
sprintf(Buffer, "%s** %12s - Address %p base at %p, DateStamp %08lx\r\n",
|
|
FirstPrint ? "\r\n*":"*",
|
|
AnsiBuffer,
|
|
(PVOID)BugCheckParameters[i],
|
|
ImageBase,
|
|
DateStamp);
|
|
|
|
//
|
|
// Only print the string if we are called to print multiple.
|
|
//
|
|
|
|
if (NumberOfParameters > 1) {
|
|
InbvDisplayString((PUCHAR)Buffer);
|
|
} else {
|
|
KiBugCheckDriver = DriverName;
|
|
}
|
|
|
|
FirstPrint = FALSE;
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
|
|
//
|
|
// Enable terminal output and turn on bugcheck processing.
|
|
//
|
|
VOID
|
|
KiEnableHeadlessBlueScreen(
|
|
)
|
|
{
|
|
HEADLESS_CMD_ENABLE_TERMINAL HeadlessCmd;
|
|
HEADLESS_CMD_SEND_BLUE_SCREEN_DATA HeadlessCmdBlueScreen;
|
|
|
|
HeadlessCmdBlueScreen.BugcheckCode = (ULONG)KiBugCheckData[0];
|
|
|
|
HeadlessCmd.Enable = TRUE;
|
|
|
|
HeadlessDispatch(HeadlessCmdStartBugCheck, NULL, 0, NULL, NULL);
|
|
|
|
HeadlessDispatch(HeadlessCmdEnableTerminal,
|
|
&HeadlessCmd,
|
|
sizeof(HEADLESS_CMD_ENABLE_TERMINAL),
|
|
NULL,
|
|
NULL
|
|
);
|
|
|
|
HeadlessDispatch(HeadlessCmdSendBlueScreenData,
|
|
&HeadlessCmdBlueScreen,
|
|
sizeof(HEADLESS_CMD_SEND_BLUE_SCREEN_DATA),
|
|
NULL,
|
|
NULL
|
|
);
|
|
|
|
}
|
|
|
|
VOID
|
|
KiDisplayBlueScreen(
|
|
IN ULONG PssMessage,
|
|
IN BOOLEAN HardErrorCalled,
|
|
IN PCHAR HardErrorCaption,
|
|
IN PCHAR HardErrorMessage,
|
|
IN PCHAR StateString
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Display the "Blue Screen of Death" with associated bluescreen information.
|
|
The function is headless aware.
|
|
|
|
Arguments:
|
|
|
|
PssMessage - PSS message id.
|
|
|
|
HardErrorCalled - Supplies a flag specifying whether the bluescreen
|
|
was generated as a result of a harderror or not.
|
|
|
|
HardErrorCaption - If HardErrorCalled is TRUE, supplies the harderror
|
|
caption.
|
|
|
|
HardErrorMessage - If HardErrorCalled is TRUE, supplies the harderror
|
|
message.
|
|
|
|
StateString - String containing the bugcheck driver name or state
|
|
information about the crash.
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
{
|
|
CHAR Buffer [103];
|
|
|
|
|
|
KiEnableHeadlessBlueScreen();
|
|
|
|
//
|
|
// Enable InbvDisplayString calls to make it through to bootvid driver.
|
|
//
|
|
|
|
if (InbvIsBootDriverInstalled()) {
|
|
|
|
InbvAcquireDisplayOwnership();
|
|
|
|
InbvResetDisplay();
|
|
InbvSolidColorFill(0,0,639,479,4); // make the screen blue
|
|
InbvSetTextColor(15);
|
|
InbvInstallDisplayStringFilter((INBV_DISPLAY_STRING_FILTER)NULL);
|
|
InbvEnableDisplayString(TRUE); // enable display string
|
|
InbvSetScrollRegion(0,0,639,475); // set to use entire screen
|
|
}
|
|
|
|
if (!HardErrorCalled) {
|
|
|
|
InbvDisplayString((PUCHAR)"\r\n");
|
|
KeGetBugMessageText(BUGCHECK_MESSAGE_INTRO, NULL);
|
|
InbvDisplayString((PUCHAR)"\r\n\r\n");
|
|
|
|
if (KiBugCheckDriver) {
|
|
|
|
//
|
|
// Output the driver name.
|
|
//
|
|
|
|
KeGetBugMessageText(BUGCODE_ID_DRIVER, NULL);
|
|
|
|
KeBugCheckUnicodeToAnsi (KiBugCheckDriver,
|
|
Buffer,
|
|
sizeof (Buffer));
|
|
InbvDisplayString((PUCHAR)" ");
|
|
InbvDisplayString ((PUCHAR)Buffer);
|
|
InbvDisplayString((PUCHAR)"\r\n\r\n");
|
|
}
|
|
|
|
//
|
|
// Display the PSS message.
|
|
// If we have no special text, get the text for the bugcode
|
|
// which will be the bugcode name.
|
|
//
|
|
|
|
if (PssMessage == BUGCODE_PSS_MESSAGE) {
|
|
KeGetBugMessageText((ULONG)KiBugCheckData[0], NULL);
|
|
InbvDisplayString((PUCHAR)"\r\n\r\n");
|
|
|
|
}
|
|
|
|
KeGetBugMessageText(PSS_MESSAGE_INTRO, NULL);
|
|
InbvDisplayString((PUCHAR)"\r\n\r\n");
|
|
KeGetBugMessageText(PssMessage, NULL);
|
|
InbvDisplayString((PUCHAR)"\r\n\r\n");
|
|
|
|
KeGetBugMessageText(BUGCHECK_TECH_INFO, NULL);
|
|
|
|
sprintf(Buffer,
|
|
"\r\n\r\n*** STOP: 0x%08lX (0x%p,0x%p,0x%p,0x%p)\r\n\r\n",
|
|
(ULONG)KiBugCheckData[0],
|
|
(PVOID)KiBugCheckData[1],
|
|
(PVOID)KiBugCheckData[2],
|
|
(PVOID)KiBugCheckData[3],
|
|
(PVOID)KiBugCheckData[4]);
|
|
|
|
InbvDisplayString((PUCHAR)Buffer);
|
|
|
|
if (KiBugCheckDriver) {
|
|
InbvDisplayString((PUCHAR)StateString);
|
|
}
|
|
|
|
if (!KiBugCheckDriver) {
|
|
KiDumpParameterImages(StateString,
|
|
&(KiBugCheckData[1]),
|
|
4,
|
|
KeBugCheckUnicodeToAnsi);
|
|
}
|
|
|
|
} else {
|
|
|
|
if (HardErrorCaption) {
|
|
InbvDisplayString((PUCHAR)HardErrorCaption);
|
|
}
|
|
|
|
if (HardErrorMessage) {
|
|
InbvDisplayString((PUCHAR)HardErrorMessage);
|
|
}
|
|
}
|
|
}
|
|
|
|
#ifdef _X86_
|
|
#pragma optimize("y", off) // RtlCaptureContext needs EBP to be correct
|
|
#endif
|
|
|
|
VOID
|
|
KeBugCheck2 (
|
|
IN ULONG BugCheckCode,
|
|
IN ULONG_PTR BugCheckParameter1,
|
|
IN ULONG_PTR BugCheckParameter2,
|
|
IN ULONG_PTR BugCheckParameter3,
|
|
IN ULONG_PTR BugCheckParameter4,
|
|
IN PVOID SaveDataPage
|
|
)
|
|
|
|
/*++
|
|
|
|
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.
|
|
|
|
--*/
|
|
|
|
{
|
|
// CHAR Buffer[103];
|
|
CONTEXT ContextSave;
|
|
ULONG PssMessage;
|
|
PCHAR HardErrorCaption;
|
|
PCHAR HardErrorMessage;
|
|
KIRQL OldIrql;
|
|
PKTRAP_FRAME TrapInformation;
|
|
PVOID ExecutionAddress;
|
|
PVOID ImageBase;
|
|
PVOID VirtualAddress;
|
|
PLDR_DATA_TABLE_ENTRY DataTableEntry;
|
|
CHAR AnsiBuffer[100];
|
|
PKTHREAD Thread;
|
|
BOOLEAN InKernelOrHal;
|
|
BOOLEAN Reboot;
|
|
BOOLEAN HardErrorCalled;
|
|
|
|
#if !defined(NT_UP)
|
|
|
|
KAFFINITY TargetSet;
|
|
|
|
#endif
|
|
|
|
HardErrorCalled = FALSE;
|
|
HardErrorCaption = NULL;
|
|
HardErrorMessage = NULL;
|
|
ExecutionAddress = NULL;
|
|
Thread = KeGetCurrentThread();
|
|
|
|
//
|
|
// Initialization
|
|
//
|
|
|
|
Reboot = FALSE;
|
|
KiBugCheckDriver = NULL;
|
|
|
|
//
|
|
// Try to simulate a power failure for Cluster testing
|
|
//
|
|
|
|
if (BugCheckCode == POWER_FAILURE_SIMULATE) {
|
|
KiScanBugCheckCallbackList();
|
|
HalReturnToFirmware(HalRebootRoutine);
|
|
}
|
|
|
|
//
|
|
// Save the current IRQL in the Prcb so the debugger can extract it
|
|
// later on for debugging purposes.
|
|
//
|
|
|
|
KeGetCurrentPrcb()->DebuggerSavedIRQL = KeGetCurrentIrql();
|
|
|
|
//
|
|
// 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(_X86_)
|
|
|
|
KiSetHardwareTrigger();
|
|
|
|
#else
|
|
|
|
InterlockedIncrement64((LONGLONG volatile *)&KiHardwareTrigger);
|
|
|
|
#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;
|
|
|
|
//
|
|
// Stop the watchdog timer
|
|
//
|
|
|
|
if (ExpWdHandler != NULL) {
|
|
ExpWdHandler( WdActionStopTimer, ExpWdHandlerContext, NULL, TRUE );
|
|
}
|
|
|
|
//
|
|
// Get the correct string for bugchecks
|
|
//
|
|
|
|
|
|
switch (BugCheckCode) {
|
|
|
|
case SYSTEM_THREAD_EXCEPTION_NOT_HANDLED:
|
|
case KERNEL_MODE_EXCEPTION_NOT_HANDLED:
|
|
case KMODE_EXCEPTION_NOT_HANDLED:
|
|
PssMessage = KMODE_EXCEPTION_NOT_HANDLED;
|
|
break;
|
|
|
|
case DATA_BUS_ERROR:
|
|
case NO_MORE_SYSTEM_PTES:
|
|
case INACCESSIBLE_BOOT_DEVICE:
|
|
case UNEXPECTED_KERNEL_MODE_TRAP:
|
|
case ACPI_BIOS_ERROR:
|
|
case ACPI_BIOS_FATAL_ERROR:
|
|
case FAT_FILE_SYSTEM:
|
|
case DRIVER_CORRUPTED_EXPOOL:
|
|
case THREAD_STUCK_IN_DEVICE_DRIVER:
|
|
PssMessage = BugCheckCode;
|
|
break;
|
|
|
|
case DRIVER_CORRUPTED_MMPOOL:
|
|
PssMessage = DRIVER_CORRUPTED_EXPOOL;
|
|
break;
|
|
|
|
case NTFS_FILE_SYSTEM:
|
|
PssMessage = FAT_FILE_SYSTEM;
|
|
break;
|
|
|
|
case STATUS_SYSTEM_IMAGE_BAD_SIGNATURE:
|
|
PssMessage = BUGCODE_PSS_MESSAGE_SIGNATURE;
|
|
break;
|
|
default:
|
|
PssMessage = BUGCODE_PSS_MESSAGE;
|
|
break;
|
|
}
|
|
|
|
|
|
//
|
|
// Do further processing on bugcheck codes
|
|
//
|
|
|
|
|
|
KiBugCheckData[0] = BugCheckCode;
|
|
KiBugCheckData[1] = BugCheckParameter1;
|
|
KiBugCheckData[2] = BugCheckParameter2;
|
|
KiBugCheckData[3] = BugCheckParameter3;
|
|
KiBugCheckData[4] = BugCheckParameter4;
|
|
|
|
|
|
switch (BugCheckCode) {
|
|
|
|
case FATAL_UNHANDLED_HARD_ERROR:
|
|
//
|
|
// 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 first parameter, and a pointer
|
|
// to the parameter array from hard error is passed as the second
|
|
// argument.
|
|
//
|
|
// The third argument is the OemCaption to be printed.
|
|
// The last argument is the OemMessage to be printed.
|
|
//
|
|
{
|
|
PULONG_PTR parameterArray;
|
|
|
|
HardErrorCalled = TRUE;
|
|
|
|
HardErrorCaption = (PCHAR)BugCheckParameter3;
|
|
HardErrorMessage = (PCHAR)BugCheckParameter4;
|
|
parameterArray = (PULONG_PTR)BugCheckParameter2;
|
|
KiBugCheckData[0] = (ULONG)BugCheckParameter1;
|
|
KiBugCheckData[1] = parameterArray[0];
|
|
KiBugCheckData[2] = parameterArray[1];
|
|
KiBugCheckData[3] = parameterArray[2];
|
|
KiBugCheckData[4] = parameterArray[3];
|
|
}
|
|
break;
|
|
|
|
case IRQL_NOT_LESS_OR_EQUAL:
|
|
|
|
ExecutionAddress = (PVOID)BugCheckParameter4;
|
|
|
|
if (ExecutionAddress >= ExPoolCodeStart && ExecutionAddress < ExPoolCodeEnd) {
|
|
KiBugCheckData[0] = DRIVER_CORRUPTED_EXPOOL;
|
|
}
|
|
else if (ExecutionAddress >= MmPoolCodeStart && ExecutionAddress < MmPoolCodeEnd) {
|
|
KiBugCheckData[0] = DRIVER_CORRUPTED_MMPOOL;
|
|
}
|
|
else if (ExecutionAddress >= MmPteCodeStart && ExecutionAddress < MmPteCodeEnd) {
|
|
KiBugCheckData[0] = DRIVER_CORRUPTED_SYSPTES;
|
|
}
|
|
else {
|
|
ImageBase = KiPcToFileHeader (ExecutionAddress,
|
|
&DataTableEntry,
|
|
FALSE,
|
|
&InKernelOrHal);
|
|
if (InKernelOrHal == TRUE) {
|
|
|
|
//
|
|
// The kernel faulted at raised IRQL. Quite often this
|
|
// is a driver that has unloaded without deleting its
|
|
// lookaside lists or other resources. Or its resources
|
|
// are marked pagable and shouldn't be. Detect both
|
|
// cases here and identify the offending driver
|
|
// whenever possible.
|
|
//
|
|
|
|
VirtualAddress = (PVOID)BugCheckParameter1;
|
|
|
|
ImageBase = KiPcToFileHeader (VirtualAddress,
|
|
&DataTableEntry,
|
|
TRUE,
|
|
&InKernelOrHal);
|
|
|
|
if (ImageBase != NULL) {
|
|
KiBugCheckDriver = &DataTableEntry->BaseDllName;
|
|
KiBugCheckData[0] = DRIVER_PORTION_MUST_BE_NONPAGED;
|
|
}
|
|
else {
|
|
KiBugCheckDriver = MmLocateUnloadedDriver (VirtualAddress);
|
|
if (KiBugCheckDriver != NULL) {
|
|
KiBugCheckData[0] = SYSTEM_SCAN_AT_RAISED_IRQL_CAUGHT_IMPROPER_DRIVER_UNLOAD;
|
|
}
|
|
}
|
|
}
|
|
else {
|
|
KiBugCheckData[0] = DRIVER_IRQL_NOT_LESS_OR_EQUAL;
|
|
}
|
|
}
|
|
|
|
ExecutionAddress = NULL;
|
|
break;
|
|
|
|
case ATTEMPTED_WRITE_TO_READONLY_MEMORY:
|
|
case ATTEMPTED_EXECUTE_OF_NOEXECUTE_MEMORY:
|
|
|
|
TrapInformation = (PKTRAP_FRAME)BugCheckParameter3;
|
|
|
|
//
|
|
// Extract the execution address from the trap frame to
|
|
// identify the component.
|
|
//
|
|
|
|
if (TrapInformation != NULL) {
|
|
ExecutionAddress = (PVOID) PROGRAM_COUNTER (TrapInformation);
|
|
}
|
|
|
|
break;
|
|
|
|
case DRIVER_LEFT_LOCKED_PAGES_IN_PROCESS:
|
|
|
|
ExecutionAddress = (PVOID)BugCheckParameter1;
|
|
break;
|
|
|
|
case DRIVER_USED_EXCESSIVE_PTES:
|
|
|
|
DataTableEntry = (PLDR_DATA_TABLE_ENTRY)BugCheckParameter1;
|
|
KiBugCheckDriver = &DataTableEntry->BaseDllName;
|
|
|
|
break;
|
|
|
|
case PAGE_FAULT_IN_NONPAGED_AREA:
|
|
|
|
ImageBase = NULL;
|
|
|
|
//
|
|
// Extract the execution address from the trap frame to
|
|
// identify the component.
|
|
//
|
|
|
|
if (BugCheckParameter3) {
|
|
|
|
ExecutionAddress = (PVOID)PROGRAM_COUNTER
|
|
((PKTRAP_FRAME)BugCheckParameter3);
|
|
|
|
KiBugCheckData[3] = (ULONG_PTR)ExecutionAddress;
|
|
|
|
ImageBase = KiPcToFileHeader (ExecutionAddress,
|
|
&DataTableEntry,
|
|
FALSE,
|
|
&InKernelOrHal);
|
|
}
|
|
else {
|
|
|
|
//
|
|
// No trap frame, so no execution address either.
|
|
//
|
|
|
|
InKernelOrHal = TRUE;
|
|
}
|
|
|
|
VirtualAddress = (PVOID)BugCheckParameter1;
|
|
|
|
if (MmIsSpecialPoolAddress (VirtualAddress) == TRUE) {
|
|
|
|
//
|
|
// Update the bugcheck number so the administrator gets
|
|
// useful feedback that enabling special pool has enabled
|
|
// the system to locate the corruptor.
|
|
//
|
|
|
|
if (MmIsSpecialPoolAddressFree (VirtualAddress) == TRUE) {
|
|
if (InKernelOrHal == TRUE) {
|
|
KiBugCheckData[0] = PAGE_FAULT_IN_FREED_SPECIAL_POOL;
|
|
}
|
|
else {
|
|
KiBugCheckData[0] = DRIVER_PAGE_FAULT_IN_FREED_SPECIAL_POOL;
|
|
}
|
|
}
|
|
else {
|
|
if (InKernelOrHal == TRUE) {
|
|
KiBugCheckData[0] = PAGE_FAULT_BEYOND_END_OF_ALLOCATION;
|
|
}
|
|
else {
|
|
KiBugCheckData[0] = DRIVER_PAGE_FAULT_BEYOND_END_OF_ALLOCATION;
|
|
}
|
|
}
|
|
}
|
|
else if ((ExecutionAddress == VirtualAddress) &&
|
|
(MmIsSessionAddress (VirtualAddress) == TRUE) &&
|
|
((Thread->Teb == NULL) || (IS_SYSTEM_ADDRESS(Thread->Teb)))) {
|
|
//
|
|
// This is a driver reference to session space from a
|
|
// worker thread. Since the system process has no session
|
|
// space this is illegal and the driver must be fixed.
|
|
//
|
|
|
|
KiBugCheckData[0] = TERMINAL_SERVER_DRIVER_MADE_INCORRECT_MEMORY_REFERENCE;
|
|
}
|
|
else if (ImageBase == NULL) {
|
|
KiBugCheckDriver = MmLocateUnloadedDriver (VirtualAddress);
|
|
if (KiBugCheckDriver != NULL) {
|
|
KiBugCheckData[0] = DRIVER_UNLOADED_WITHOUT_CANCELLING_PENDING_OPERATIONS;
|
|
}
|
|
}
|
|
|
|
break;
|
|
|
|
case THREAD_STUCK_IN_DEVICE_DRIVER:
|
|
|
|
KiBugCheckDriver = (PUNICODE_STRING) BugCheckParameter3;
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
|
|
if (KiBugCheckDriver) {
|
|
KeBugCheckUnicodeToAnsi(KiBugCheckDriver,
|
|
AnsiBuffer,
|
|
sizeof(AnsiBuffer));
|
|
} else {
|
|
|
|
//
|
|
// This will set KiBugCheckDriver to 1 if successful.
|
|
//
|
|
|
|
if (ExecutionAddress) {
|
|
KiDumpParameterImages(AnsiBuffer,
|
|
(PULONG_PTR)&ExecutionAddress,
|
|
1,
|
|
KeBugCheckUnicodeToAnsi);
|
|
}
|
|
}
|
|
|
|
if (KdPitchDebugger == FALSE ) {
|
|
KdDebuggerDataBlock.SavedContext = (ULONG_PTR) &ContextSave;
|
|
}
|
|
|
|
//
|
|
// If the user manually crashed the machine, skips the DbgPrints and
|
|
// go to the crashdump.
|
|
// Trying to do DbgPrint causes us to reeeter the debugger which causes
|
|
// some problems.
|
|
//
|
|
// Otherwise, if the debugger is enabled, print out the information and
|
|
// stop.
|
|
//
|
|
|
|
if ((BugCheckCode != MANUALLY_INITIATED_CRASH) &&
|
|
(KdDebuggerEnabled)) {
|
|
|
|
DbgPrint("\n*** Fatal System Error: 0x%08lx\n"
|
|
" (0x%p,0x%p,0x%p,0x%p)\n\n",
|
|
(ULONG)KiBugCheckData[0],
|
|
KiBugCheckData[1],
|
|
KiBugCheckData[2],
|
|
KiBugCheckData[3],
|
|
KiBugCheckData[4]);
|
|
|
|
//
|
|
// If the debugger is not actually connected, or the user manually
|
|
// crashed the machine by typing .crash in the debugger, proceed to
|
|
// "blue screen" the system.
|
|
//
|
|
// The call to DbgPrint above will have set the state of
|
|
// KdDebuggerNotPresent if the debugger has become disconnected
|
|
// since the system was booted.
|
|
//
|
|
|
|
if (KdDebuggerNotPresent == FALSE) {
|
|
|
|
if (KiBugCheckDriver != NULL) {
|
|
DbgPrint("Driver at fault: %s.\n", AnsiBuffer);
|
|
}
|
|
|
|
if (HardErrorCalled != FALSE) {
|
|
if (HardErrorCaption) {
|
|
DbgPrint(HardErrorCaption);
|
|
}
|
|
if (HardErrorMessage) {
|
|
DbgPrint(HardErrorMessage);
|
|
}
|
|
}
|
|
|
|
KiBugCheckDebugBreak (DBG_STATUS_BUGCHECK_FIRST);
|
|
}
|
|
}
|
|
|
|
//
|
|
// Freeze execution of the system by disabling interrupts and looping.
|
|
//
|
|
|
|
KeDisableInterrupts();
|
|
KeRaiseIrql(HIGH_LEVEL, &OldIrql);
|
|
|
|
|
|
//
|
|
// 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
|
|
|
|
//
|
|
// Display the blue screen.
|
|
//
|
|
|
|
KiDisplayBlueScreen (PssMessage,
|
|
HardErrorCalled,
|
|
HardErrorCaption,
|
|
HardErrorMessage,
|
|
AnsiBuffer);
|
|
|
|
//
|
|
// Invoke bugcheck callbacks.
|
|
//
|
|
|
|
KiInvokeBugCheckEntryCallbacks();
|
|
|
|
//
|
|
// If the debugger is not enabled, attempt to enable it.
|
|
//
|
|
|
|
if (KdDebuggerEnabled == FALSE && KdPitchDebugger == FALSE ) {
|
|
KdInitSystem(0, NULL);
|
|
|
|
} else {
|
|
InbvDisplayString((PUCHAR)"\r\n");
|
|
}
|
|
|
|
// Restore the original Context frame
|
|
KeGetCurrentPrcb()->ProcessorState.ContextFrame = ContextSave;
|
|
|
|
//
|
|
// For some bugchecks we want to change the thread and context before
|
|
// it is written to the dump file IFF it is a minidump.
|
|
// Look at the original bugcheck data, not the processed data from
|
|
// above
|
|
//
|
|
|
|
#define MINIDUMP_BUGCHECK 0x10000000
|
|
|
|
if (IoIsTriageDumpEnabled()) {
|
|
|
|
switch (BugCheckCode) {
|
|
|
|
//
|
|
// System thread stores a context record as the 4th parameter.
|
|
// use that.
|
|
// Also save the context record in case someone needs to look
|
|
// at it.
|
|
//
|
|
|
|
case SYSTEM_THREAD_EXCEPTION_NOT_HANDLED:
|
|
if (BugCheckParameter4) {
|
|
ContextSave = *((PCONTEXT)BugCheckParameter4);
|
|
|
|
KiBugCheckData[0] |= MINIDUMP_BUGCHECK;
|
|
}
|
|
break;
|
|
|
|
#if defined (_X86_)
|
|
|
|
//
|
|
// 3rd parameter is a trap frame.
|
|
//
|
|
// Build a context record out of that only if it's a kernel mode
|
|
// failure because esp may be wrong in that case ???.
|
|
//
|
|
|
|
case ATTEMPTED_WRITE_TO_READONLY_MEMORY:
|
|
case ATTEMPTED_EXECUTE_OF_NOEXECUTE_MEMORY:
|
|
case KERNEL_MODE_EXCEPTION_NOT_HANDLED:
|
|
case PAGE_FAULT_IN_NONPAGED_AREA:
|
|
|
|
if (BugCheckParameter3)
|
|
{
|
|
PKTRAP_FRAME Trap = (PKTRAP_FRAME) BugCheckParameter3;
|
|
|
|
if ((Trap->SegCs & 1) ||
|
|
(Trap->EFlags & EFLAGS_V86_MASK))
|
|
{
|
|
ContextSave.Esp = Trap->HardwareEsp;
|
|
}
|
|
else
|
|
{
|
|
ContextSave.Esp = (ULONG)Trap +
|
|
FIELD_OFFSET(KTRAP_FRAME, HardwareEsp);
|
|
}
|
|
if (Trap->EFlags & EFLAGS_V86_MASK)
|
|
{
|
|
ContextSave.SegSs = Trap->HardwareSegSs & 0xffff;
|
|
}
|
|
else if (Trap->SegCs & 1)
|
|
{
|
|
//
|
|
// It's user mode.
|
|
// The HardwareSegSs contains R3 data selector.
|
|
//
|
|
|
|
ContextSave.SegSs =
|
|
(Trap->HardwareSegSs | 3) & 0xffff;
|
|
}
|
|
else
|
|
{
|
|
ContextSave.SegSs = KGDT_R0_DATA;
|
|
}
|
|
|
|
ContextSave.SegGs = Trap->SegGs & 0xffff;
|
|
ContextSave.SegFs = Trap->SegFs & 0xffff;
|
|
ContextSave.SegEs = Trap->SegEs & 0xffff;
|
|
ContextSave.SegDs = Trap->SegDs & 0xffff;
|
|
ContextSave.SegCs = Trap->SegCs & 0xffff;
|
|
ContextSave.Eip = Trap->Eip;
|
|
ContextSave.Ebp = Trap->Ebp;
|
|
ContextSave.Eax = Trap->Eax;
|
|
ContextSave.Ebx = Trap->Ebx;
|
|
ContextSave.Ecx = Trap->Ecx;
|
|
ContextSave.Edx = Trap->Edx;
|
|
ContextSave.Edi = Trap->Edi;
|
|
ContextSave.Esi = Trap->Esi;
|
|
ContextSave.EFlags = Trap->EFlags;
|
|
|
|
KiBugCheckData[0] |= MINIDUMP_BUGCHECK;
|
|
}
|
|
break;
|
|
|
|
case THREAD_STUCK_IN_DEVICE_DRIVER:
|
|
|
|
// Extract the address of the spinning code from the thread
|
|
// object, so the dump is based off this thread.
|
|
|
|
Thread = (PKTHREAD) BugCheckParameter1;
|
|
|
|
if (Thread->State == Running)
|
|
{
|
|
//
|
|
// If the thread was running, the thread is now in a
|
|
// frozen state and the registers are in the PRCB
|
|
// context
|
|
//
|
|
ULONG Processor = Thread->NextProcessor;
|
|
ASSERT(Processor < (ULONG) KeNumberProcessors);
|
|
ContextSave =
|
|
KiProcessorBlock[Processor]->ProcessorState.ContextFrame;
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// This should be a uniproc machine, and the thread
|
|
// should be suspended. Just get the data off the
|
|
// switch frame.
|
|
//
|
|
|
|
PKSWITCHFRAME SwitchFrame = (PKSWITCHFRAME)Thread->KernelStack;
|
|
|
|
ASSERT(Thread->State == Ready);
|
|
|
|
ContextSave.Esp = (ULONG)Thread->KernelStack + sizeof(KSWITCHFRAME);
|
|
ContextSave.Ebp = *((PULONG)(ContextSave.Esp));
|
|
ContextSave.Eip = SwitchFrame->RetAddr;
|
|
}
|
|
|
|
KiBugCheckData[0] |= MINIDUMP_BUGCHECK;
|
|
break;
|
|
|
|
case UNEXPECTED_KERNEL_MODE_TRAP:
|
|
|
|
//
|
|
// Double fault
|
|
//
|
|
|
|
if (BugCheckParameter1 == 0x8)
|
|
{
|
|
// The thread is correct in this case.
|
|
// Second parameter is the TSS. If we have a TSS, convert
|
|
// the context and mark the bugcheck as converted.
|
|
|
|
PKTSS Tss = (PKTSS) BugCheckParameter2;
|
|
|
|
if (Tss)
|
|
{
|
|
if (Tss->EFlags & EFLAGS_V86_MASK)
|
|
{
|
|
ContextSave.SegSs = Tss->Ss & 0xffff;
|
|
}
|
|
else if (Tss->Cs & 1)
|
|
{
|
|
//
|
|
// It's user mode.
|
|
// The HardwareSegSs contains R3 data selector.
|
|
//
|
|
|
|
ContextSave.SegSs = (Tss->Ss | 3) & 0xffff;
|
|
}
|
|
else
|
|
{
|
|
ContextSave.SegSs = KGDT_R0_DATA;
|
|
}
|
|
|
|
ContextSave.SegGs = Tss->Gs & 0xffff;
|
|
ContextSave.SegFs = Tss->Fs & 0xffff;
|
|
ContextSave.SegEs = Tss->Es & 0xffff;
|
|
ContextSave.SegDs = Tss->Ds & 0xffff;
|
|
ContextSave.SegCs = Tss->Cs & 0xffff;
|
|
ContextSave.Esp = Tss->Esp;
|
|
ContextSave.Eip = Tss->Eip;
|
|
ContextSave.Ebp = Tss->Ebp;
|
|
ContextSave.Eax = Tss->Eax;
|
|
ContextSave.Ebx = Tss->Ebx;
|
|
ContextSave.Ecx = Tss->Ecx;
|
|
ContextSave.Edx = Tss->Edx;
|
|
ContextSave.Edi = Tss->Edi;
|
|
ContextSave.Esi = Tss->Esi;
|
|
ContextSave.EFlags = Tss->EFlags;
|
|
}
|
|
|
|
KiBugCheckData[0] |= MINIDUMP_BUGCHECK;
|
|
break;
|
|
}
|
|
#endif
|
|
default:
|
|
break;
|
|
}
|
|
|
|
//
|
|
// Write a crash dump and optionally reboot if the system has been
|
|
// so configured.
|
|
//
|
|
|
|
IoAddTriageDumpDataBlock(PAGE_ALIGN(KiBugCheckData[1]), PAGE_SIZE);
|
|
IoAddTriageDumpDataBlock(PAGE_ALIGN(KiBugCheckData[2]), PAGE_SIZE);
|
|
IoAddTriageDumpDataBlock(PAGE_ALIGN(KiBugCheckData[3]), PAGE_SIZE);
|
|
IoAddTriageDumpDataBlock(PAGE_ALIGN(KiBugCheckData[4]), PAGE_SIZE);
|
|
IoAddTriageDumpDataBlock(PAGE_ALIGN(SaveDataPage), PAGE_SIZE);
|
|
|
|
//
|
|
// If the DPC stack is active, save that data page as well.
|
|
//
|
|
|
|
#if defined (_X86_)
|
|
if (KeGetCurrentPrcb()->DpcRoutineActive)
|
|
{
|
|
IoAddTriageDumpDataBlock(PAGE_ALIGN(KeGetCurrentPrcb()->DpcRoutineActive), PAGE_SIZE);
|
|
}
|
|
#endif
|
|
}
|
|
|
|
IoWriteCrashDump((ULONG)KiBugCheckData[0],
|
|
KiBugCheckData[1],
|
|
KiBugCheckData[2],
|
|
KiBugCheckData[3],
|
|
KiBugCheckData[4],
|
|
&ContextSave,
|
|
Thread,
|
|
&Reboot);
|
|
}
|
|
|
|
//
|
|
// Invoke bugcheck callbacks after crashdump, so the callbacks will
|
|
// not prevent us from crashdumping.
|
|
//
|
|
|
|
KiScanBugCheckCallbackList();
|
|
|
|
//
|
|
// Start the watchdog timer
|
|
//
|
|
|
|
if (ExpWdHandler != NULL) {
|
|
ExpWdHandler( WdActionStartTimer, ExpWdHandlerContext, NULL, TRUE );
|
|
}
|
|
|
|
//
|
|
// Reboot the machine if necessary.
|
|
//
|
|
|
|
if (Reboot) {
|
|
DbgUnLoadImageSymbols (NULL, (PVOID)-1, 0);
|
|
HalReturnToFirmware (HalRebootRoutine);
|
|
}
|
|
|
|
|
|
//
|
|
// Attempt to enter the kernel debugger.
|
|
//
|
|
|
|
KiBugCheckDebugBreak (DBG_STATUS_BUGCHECK_SECOND);
|
|
}
|
|
#ifdef _X86_
|
|
#pragma optimize("", on)
|
|
#endif
|
|
|
|
|
|
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;
|
|
KeDisableInterrupts();
|
|
#if !defined(i386)
|
|
KeRaiseIrql(HIGH_LEVEL, &OldIrql);
|
|
#endif
|
|
if (InterlockedDecrement (&KeBugCheckCount) == 0) {
|
|
if (KdDebuggerEnabled == FALSE) {
|
|
if ( KdPitchDebugger == FALSE ) {
|
|
KdInitSystem(0, NULL);
|
|
}
|
|
}
|
|
}
|
|
|
|
KiBugCheckDebugBreak (DBG_STATUS_FATAL);
|
|
}
|
|
|
|
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_PTR)CallbackRoutine + (ULONG_PTR)Buffer + Length + (ULONG_PTR)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_PTR 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_PTR)NextEntry & (sizeof(ULONG_PTR) - 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 (MmIsAddressValid((PVOID)Source) == FALSE) {
|
|
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_PTR)CallbackRecord->CallbackRoutine;
|
|
Checksum += (ULONG_PTR)CallbackRecord->Buffer;
|
|
Checksum += CallbackRecord->Length;
|
|
Checksum += (ULONG_PTR)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;
|
|
}
|
|
|
|
NTKERNELAPI
|
|
BOOLEAN
|
|
KeDeregisterBugCheckReasonCallback (
|
|
IN PKBUGCHECK_REASON_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
|
|
KeRegisterBugCheckReasonCallback (
|
|
IN PKBUGCHECK_REASON_CALLBACK_RECORD CallbackRecord,
|
|
IN PKBUGCHECK_REASON_CALLBACK_ROUTINE CallbackRoutine,
|
|
IN KBUGCHECK_CALLBACK_REASON Reason,
|
|
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.
|
|
|
|
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.
|
|
|
|
Reason - Specifies the conditions under which the callback
|
|
should be called.
|
|
|
|
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->Reason = Reason;
|
|
CallbackRecord->Component = Component;
|
|
CallbackRecord->Checksum =
|
|
((ULONG_PTR)CallbackRoutine + Reason + (ULONG_PTR)Component);
|
|
|
|
CallbackRecord->State = BufferInserted;
|
|
InsertHeadList(&KeBugCheckReasonCallbackListHead,
|
|
&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
|
|
KiInvokeBugCheckEntryCallbacks (
|
|
VOID
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This function scans the bug check reason callback list and calls
|
|
each bug check entry callback routine.
|
|
|
|
This may seem like a duplication of KiScanBugCheckCallbackList
|
|
but the critical difference is that the bug check entry callbacks
|
|
are called immediately upon entry to KeBugCheck2 whereas
|
|
KSBCCL does not invoke its callbacks until after all bug check
|
|
processing has finished.
|
|
|
|
In order to avoid people from abusing this callback it's
|
|
semi-private and the reason -- KbCallbackReserved1 -- has
|
|
an obscure name.
|
|
|
|
N.B. The scan of the bug check callback list is performed VERY
|
|
carefully. Bug check callback routines may be called at HIGH_LEVEL
|
|
and may not acquire ANY resources.
|
|
|
|
Arguments:
|
|
|
|
None.
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
|
|
{
|
|
PKBUGCHECK_REASON_CALLBACK_RECORD CallbackRecord;
|
|
ULONG_PTR Checksum;
|
|
PLIST_ENTRY LastEntry;
|
|
PLIST_ENTRY ListHead;
|
|
PLIST_ENTRY NextEntry;
|
|
PUCHAR Va;
|
|
ULONG Pages;
|
|
|
|
//
|
|
// 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 = &KeBugCheckReasonCallbackListHead;
|
|
if (ListHead->Flink == NULL || ListHead->Blink == NULL) {
|
|
return;
|
|
}
|
|
|
|
//
|
|
// 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_PTR)NextEntry & (sizeof(ULONG_PTR) - 1)) != 0) {
|
|
return;
|
|
}
|
|
|
|
CallbackRecord = CONTAINING_RECORD(NextEntry,
|
|
KBUGCHECK_REASON_CALLBACK_RECORD,
|
|
Entry);
|
|
|
|
//
|
|
// Verify that the callback record is still valid.
|
|
//
|
|
|
|
Va = (PUCHAR) PAGE_ALIGN (CallbackRecord);
|
|
Pages = ADDRESS_AND_SIZE_TO_SPAN_PAGES (CallbackRecord,
|
|
sizeof (*CallbackRecord));
|
|
|
|
while (Pages) {
|
|
if (!MmIsAddressValid (Va)) {
|
|
return;
|
|
}
|
|
Va += PAGE_SIZE;
|
|
Pages--;
|
|
}
|
|
|
|
if (CallbackRecord->Entry.Blink != LastEntry) {
|
|
return;
|
|
}
|
|
|
|
LastEntry = NextEntry;
|
|
NextEntry = NextEntry->Flink;
|
|
|
|
//
|
|
// 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_PTR)CallbackRecord->CallbackRoutine;
|
|
Checksum += (ULONG_PTR)CallbackRecord->Reason;
|
|
Checksum += (ULONG_PTR)CallbackRecord->Component;
|
|
if ((CallbackRecord->State != BufferInserted) ||
|
|
(CallbackRecord->Checksum != Checksum) ||
|
|
(CallbackRecord->Reason != KbCallbackReserved1) ||
|
|
MmIsAddressValid((PVOID)(ULONG_PTR)CallbackRecord->
|
|
CallbackRoutine) == FALSE) {
|
|
|
|
continue;
|
|
}
|
|
|
|
//
|
|
// Call the specified bug check callback routine and
|
|
// handle any exceptions that occur.
|
|
//
|
|
|
|
try {
|
|
(CallbackRecord->CallbackRoutine)(KbCallbackReserved1,
|
|
CallbackRecord,
|
|
NULL, 0);
|
|
CallbackRecord->State = BufferFinished;
|
|
} except(EXCEPTION_EXECUTE_HANDLER) {
|
|
CallbackRecord->State = BufferIncomplete;
|
|
}
|
|
}
|
|
}
|