reactos/ntoskrnl/ke/bug.c
Hermès Bélusca-Maïto f694d12f0c
[NTOS:KE/MM] Some bug-fixes in the bugcheck code.
- Introduce the MmMakeKernelResourceSectionWritable() helper for
  making the kernel resource memory section writable, and use it
  in KeGetBugMessageText(). Indeed, this latter function patches
  in place the bugcheck resource message to trim any trailing
  newlines before displaying the message on screen.

  See also https://github.com/osresearch/uxen/blob/83bad53/dm/introspection-win7.c#L286
  that mentions it too.

  This fixes bugcheck text display (e.g. the MANUALLY_INITIATED_CRASH one)
  when using (at least) MSVC-built ReactOS, avoiding a Page-Fault
  exception during the bugcheck.

- Cover KeGetBugMessageText() in SEH since we are accessing kernel
  resources that could also be corrupted in bugcheck scenarii, and we
  don't want to further bugcheck.

- Fix newline trimming loop.

- KiDoBugCheckCallbacks():
  * Wrap the bugcheck CallbackRoutine call in SEH.
  * Add a FIXME concerning the need of further memory validation of CurrentRecord.

- Add a FIXME concerning the need to run the bugcheck-reason callbacks
  with the KbCallbackReserved1 reason, in KeBugCheckWithTf().
  Mentioned in http://blog.ptsecurity.com/2012/06/customizing-blue-screen-of-death.html
2019-06-25 21:01:49 +02:00

1539 lines
44 KiB
C

/*
* PROJECT: ReactOS Kernel
* LICENSE: GPL - See COPYING in the top level directory
* FILE: ntoskrnl/ke/bug.c
* PURPOSE: Bugcheck Support
* PROGRAMMERS: Alex Ionescu (alex.ionescu@reactos.org)
*/
/* INCLUDES ******************************************************************/
#include <ntoskrnl.h>
#define NDEBUG
#include <debug.h>
#if defined (ALLOC_PRAGMA)
#pragma alloc_text(INIT, KiInitializeBugCheck)
#endif
/* GLOBALS *******************************************************************/
LIST_ENTRY KeBugcheckCallbackListHead;
LIST_ENTRY KeBugcheckReasonCallbackListHead;
KSPIN_LOCK BugCheckCallbackLock;
ULONG KeBugCheckActive, KeBugCheckOwner;
LONG KeBugCheckOwnerRecursionCount;
PMESSAGE_RESOURCE_DATA KiBugCodeMessages;
ULONG KeBugCheckCount = 1;
ULONG KiHardwareTrigger;
PUNICODE_STRING KiBugCheckDriver;
ULONG_PTR KiBugCheckData[5];
PKNMI_HANDLER_CALLBACK KiNmiCallbackListHead = NULL;
KSPIN_LOCK KiNmiCallbackListLock;
#define TAG_KNMI 'IMNK'
/* Bugzilla Reporting */
UNICODE_STRING KeRosProcessorName, KeRosBiosDate, KeRosBiosVersion;
UNICODE_STRING KeRosVideoBiosDate, KeRosVideoBiosVersion;
/* PRIVATE FUNCTIONS *********************************************************/
PVOID
NTAPI
KiPcToFileHeader(IN PVOID Pc,
OUT PLDR_DATA_TABLE_ENTRY *LdrEntry,
IN BOOLEAN DriversOnly,
OUT PBOOLEAN InKernel)
{
ULONG i = 0;
PVOID ImageBase, PcBase = NULL;
PLDR_DATA_TABLE_ENTRY Entry;
PLIST_ENTRY ListHead, NextEntry;
/* Check which list we should use */
ListHead = (KeLoaderBlock) ? &KeLoaderBlock->LoadOrderListHead :
&PsLoadedModuleList;
/* Assume no */
*InKernel = FALSE;
/* Set list pointers and make sure it's valid */
NextEntry = ListHead->Flink;
if (NextEntry)
{
/* Start loop */
while (NextEntry != ListHead)
{
/* Increase entry */
i++;
/* Check if this is a kernel entry and we only want drivers */
if ((i <= 2) && (DriversOnly != FALSE))
{
/* Skip it */
NextEntry = NextEntry->Flink;
continue;
}
/* Get the loader entry */
Entry = CONTAINING_RECORD(NextEntry,
LDR_DATA_TABLE_ENTRY,
InLoadOrderLinks);
/* Move to the next entry */
NextEntry = NextEntry->Flink;
ImageBase = Entry->DllBase;
/* Check if this is the right one */
if (((ULONG_PTR)Pc >= (ULONG_PTR)Entry->DllBase) &&
((ULONG_PTR)Pc < ((ULONG_PTR)Entry->DllBase + Entry->SizeOfImage)))
{
/* Return this entry */
*LdrEntry = Entry;
PcBase = ImageBase;
/* Check if this was a kernel or HAL entry */
if (i <= 2) *InKernel = TRUE;
break;
}
}
}
/* Return the base address */
return PcBase;
}
PVOID
NTAPI
KiRosPcToUserFileHeader(IN PVOID Pc,
OUT PLDR_DATA_TABLE_ENTRY *LdrEntry)
{
PVOID ImageBase, PcBase = NULL;
PLDR_DATA_TABLE_ENTRY Entry;
PLIST_ENTRY ListHead, NextEntry;
/*
* We know this is valid because we should only be called after a
* succesfull address from RtlWalkFrameChain for UserMode, which
* validates everything for us.
*/
ListHead = &KeGetCurrentThread()->
Teb->ProcessEnvironmentBlock->Ldr->InLoadOrderModuleList;
/* Set list pointers and make sure it's valid */
NextEntry = ListHead->Flink;
if (NextEntry)
{
/* Start loop */
while (NextEntry != ListHead)
{
/* Get the loader entry */
Entry = CONTAINING_RECORD(NextEntry,
LDR_DATA_TABLE_ENTRY,
InLoadOrderLinks);
/* Move to the next entry */
NextEntry = NextEntry->Flink;
ImageBase = Entry->DllBase;
/* Check if this is the right one */
if (((ULONG_PTR)Pc >= (ULONG_PTR)Entry->DllBase) &&
((ULONG_PTR)Pc < ((ULONG_PTR)Entry->DllBase + Entry->SizeOfImage)))
{
/* Return this entry */
*LdrEntry = Entry;
PcBase = ImageBase;
break;
}
}
}
/* Return the base address */
return PcBase;
}
USHORT
NTAPI
KeRosCaptureUserStackBackTrace(IN ULONG FramesToSkip,
IN ULONG FramesToCapture,
OUT PVOID *BackTrace,
OUT PULONG BackTraceHash OPTIONAL)
{
PVOID Frames[2 * 64];
ULONG FrameCount;
ULONG Hash = 0, i;
/* Skip a frame for the caller */
FramesToSkip++;
/* Don't go past the limit */
if ((FramesToCapture + FramesToSkip) >= 128) return 0;
/* Do the back trace */
FrameCount = RtlWalkFrameChain(Frames, FramesToCapture + FramesToSkip, 1);
/* Make sure we're not skipping all of them */
if (FrameCount <= FramesToSkip) return 0;
/* Loop all the frames */
for (i = 0; i < FramesToCapture; i++)
{
/* Don't go past the limit */
if ((FramesToSkip + i) >= FrameCount) break;
/* Save this entry and hash it */
BackTrace[i] = Frames[FramesToSkip + i];
Hash += PtrToUlong(BackTrace[i]);
}
/* Write the hash */
if (BackTraceHash) *BackTraceHash = Hash;
/* Clear the other entries and return count */
RtlFillMemoryUlong(Frames, 128, 0);
return (USHORT)i;
}
VOID
FASTCALL
KeRosDumpStackFrameArray(IN PULONG_PTR Frames,
IN ULONG FrameCount)
{
ULONG i;
ULONG_PTR Addr;
BOOLEAN InSystem;
PVOID p;
/* GCC complaints that it may be used uninitialized */
PLDR_DATA_TABLE_ENTRY LdrEntry = NULL;
/* Loop them */
for (i = 0; i < FrameCount; i++)
{
/* Get the EIP */
Addr = Frames[i];
if (!Addr)
{
break;
}
/* Get the base for this file */
if (Addr > (ULONG_PTR)MmHighestUserAddress)
{
/* We are in kernel */
p = KiPcToFileHeader((PVOID)Addr, &LdrEntry, FALSE, &InSystem);
}
else
{
/* We are in user land */
p = KiRosPcToUserFileHeader((PVOID)Addr, &LdrEntry);
}
if (p)
{
#ifdef KDBG
if (!KdbSymPrintAddress((PVOID)Addr, NULL))
#endif
{
CHAR AnsiName[64];
/* Convert module name to ANSI and print it */
KeBugCheckUnicodeToAnsi(&LdrEntry->BaseDllName,
AnsiName,
sizeof(AnsiName));
Addr -= (ULONG_PTR)LdrEntry->DllBase;
DbgPrint("<%s: %p>", AnsiName, (PVOID)Addr);
}
}
else
{
/* Print only the address */
DbgPrint("<%p>", (PVOID)Addr);
}
/* Go to the next frame */
DbgPrint("\n");
}
}
VOID
NTAPI
KeRosDumpStackFrames(IN PULONG_PTR Frame OPTIONAL,
IN ULONG FrameCount OPTIONAL)
{
ULONG_PTR Frames[32];
ULONG RealFrameCount;
/* If the caller didn't ask, assume 32 frames */
if (!FrameCount || FrameCount > 32) FrameCount = 32;
if (Frame)
{
/* Dump them */
KeRosDumpStackFrameArray(Frame, FrameCount);
}
else
{
/* Get the current frames (skip the two. One for the dumper, one for the caller) */
RealFrameCount = RtlCaptureStackBackTrace(2, FrameCount, (PVOID*)Frames, NULL);
DPRINT1("RealFrameCount =%lu\n", RealFrameCount);
/* Dump them */
KeRosDumpStackFrameArray(Frames, RealFrameCount);
/* Count left for user mode? */
if (FrameCount - RealFrameCount > 0)
{
/* Get the current frames */
RealFrameCount = KeRosCaptureUserStackBackTrace(-1, FrameCount - RealFrameCount, (PVOID*)Frames, NULL);
/* Dump them */
KeRosDumpStackFrameArray(Frames, RealFrameCount);
}
}
}
VOID
NTAPI
KeRosDumpTriageForBugZillaReport(VOID)
{
#if 0
extern BOOLEAN KiFastSystemCallDisable, KiSMTProcessorsPresent;
extern ULONG KeI386MachineType, MxcsrFeatureMask;
extern BOOLEAN Ke386Pae, Ke386NoExecute;
DbgPrint("ReactOS has crashed! Please go to http://jira.reactos.org/ to file a bug!\n");
DbgPrint("\nHardware Information\n");
DbgPrint("Processor Architecture: %d\n"
"Feature Bits: %d\n"
"System Call Disabled: %d\n"
"NPX Present: %d\n"
"MXCsr Mask: %d\n"
"MXCsr Feature Mask: %d\n"
"XMMI Present: %d\n"
"FXSR Present: %d\n"
"Machine Type: %d\n"
"PAE: %d\n"
"NX: %d\n"
"Processors: %d\n"
"Active Processors: %d\n"
"Pentium LOCK Bug: %d\n"
"Hyperthreading: %d\n"
"CPU Manufacturer: %s\n"
"CPU Name: %wZ\n"
"CPUID: %d\n"
"CPU Type: %d\n"
"CPU Stepping: %d\n"
"CPU Speed: %d\n"
"CPU L2 Cache: %d\n"
"BIOS Date: %wZ\n"
"BIOS Version: %wZ\n"
"Video BIOS Date: %wZ\n"
"Video BIOS Version: %wZ\n"
"Memory: %d\n",
KeProcessorArchitecture,
KeFeatureBits,
KiFastSystemCallDisable,
KeI386NpxPresent,
KiMXCsrMask,
MxcsrFeatureMask,
KeI386XMMIPresent,
KeI386FxsrPresent,
KeI386MachineType,
Ke386Pae,
Ke386NoExecute,
KeNumberProcessors,
KeActiveProcessors,
KiI386PentiumLockErrataPresent,
KiSMTProcessorsPresent,
KeGetCurrentPrcb()->VendorString,
&KeRosProcessorName,
KeGetCurrentPrcb()->CpuID,
KeGetCurrentPrcb()->CpuType,
KeGetCurrentPrcb()->CpuStep,
KeGetCurrentPrcb()->MHz,
((PKIPCR)KeGetPcr())->SecondLevelCacheSize,
&KeRosBiosDate,
&KeRosBiosVersion,
&KeRosVideoBiosDate,
&KeRosVideoBiosVersion,
MmNumberOfPhysicalPages * PAGE_SIZE);
#endif
}
VOID
INIT_FUNCTION
NTAPI
KiInitializeBugCheck(VOID)
{
PMESSAGE_RESOURCE_DATA BugCheckData;
LDR_RESOURCE_INFO ResourceInfo;
PIMAGE_RESOURCE_DATA_ENTRY ResourceDataEntry;
NTSTATUS Status;
PLDR_DATA_TABLE_ENTRY LdrEntry;
/* Get the kernel entry */
LdrEntry = CONTAINING_RECORD(KeLoaderBlock->LoadOrderListHead.Flink,
LDR_DATA_TABLE_ENTRY,
InLoadOrderLinks);
/* Cache the Bugcheck Message Strings. Prepare the Lookup Data */
ResourceInfo.Type = 11;
ResourceInfo.Name = 1;
ResourceInfo.Language = 9;
/* Do the lookup. */
Status = LdrFindResource_U(LdrEntry->DllBase,
&ResourceInfo,
RESOURCE_DATA_LEVEL,
&ResourceDataEntry);
/* Make sure it worked */
if (NT_SUCCESS(Status))
{
/* Now actually get a pointer to it */
Status = LdrAccessResource(LdrEntry->DllBase,
ResourceDataEntry,
(PVOID*)&BugCheckData,
NULL);
if (NT_SUCCESS(Status)) KiBugCodeMessages = BugCheckData;
}
}
BOOLEAN
NTAPI
KeGetBugMessageText(IN ULONG BugCheckCode,
OUT PANSI_STRING OutputString OPTIONAL)
{
ULONG i;
ULONG IdOffset;
PMESSAGE_RESOURCE_ENTRY MessageEntry;
PCHAR BugCode;
USHORT Length;
BOOLEAN Result = FALSE;
/* Make sure we're not bugchecking too early */
if (!KiBugCodeMessages) return Result;
/*
* Globally protect in SEH as we are trying to access data in
* dire situations, and potentially going to patch it (see below).
*/
_SEH2_TRY
{
/*
* Make the kernel resource section writable, as we are going to manually
* trim the trailing newlines in the bugcheck resource message in place,
* when OutputString is NULL and before displaying it on screen.
*/
MmMakeKernelResourceSectionWritable();
/* Find the message. This code is based on RtlFindMesssage */
for (i = 0; i < KiBugCodeMessages->NumberOfBlocks; i++)
{
/* Check if the ID matches */
if ((BugCheckCode >= KiBugCodeMessages->Blocks[i].LowId) &&
(BugCheckCode <= KiBugCodeMessages->Blocks[i].HighId))
{
/* Get offset to entry */
MessageEntry = (PMESSAGE_RESOURCE_ENTRY)
((ULONG_PTR)KiBugCodeMessages + KiBugCodeMessages->Blocks[i].OffsetToEntries);
IdOffset = BugCheckCode - KiBugCodeMessages->Blocks[i].LowId;
/* Advance in the entries until finding it */
while (IdOffset--)
{
MessageEntry = (PMESSAGE_RESOURCE_ENTRY)
((ULONG_PTR)MessageEntry + MessageEntry->Length);
}
/* Make sure it's not Unicode */
ASSERT(!(MessageEntry->Flags & MESSAGE_RESOURCE_UNICODE));
/* Get the final code */
BugCode = (PCHAR)MessageEntry->Text;
Length = (USHORT)strlen(BugCode);
/* Handle trailing newlines */
while ((Length > 0) && ((BugCode[Length - 1] == '\n') ||
(BugCode[Length - 1] == '\r') ||
(BugCode[Length - 1] == ANSI_NULL)))
{
/* Directly trim the newline in place if we don't return the string */
if (!OutputString) BugCode[Length - 1] = ANSI_NULL;
/* Skip the trailing newline */
Length--;
}
/* Check if caller wants an output string */
if (OutputString)
{
/* Return it in the OutputString */
OutputString->Buffer = BugCode;
OutputString->Length = Length;
OutputString->MaximumLength = Length;
}
else
{
/* Direct output to screen */
InbvDisplayString(BugCode);
InbvDisplayString("\r");
}
/* We're done */
Result = TRUE;
break;
}
}
}
_SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
{
}
_SEH2_END;
/* Return the result */
return Result;
}
VOID
NTAPI
KiDoBugCheckCallbacks(VOID)
{
PKBUGCHECK_CALLBACK_RECORD CurrentRecord;
PLIST_ENTRY ListHead, NextEntry, LastEntry;
ULONG_PTR Checksum;
/* First make sure that the list is initialized... it might not be */
ListHead = &KeBugcheckCallbackListHead;
if ((!ListHead->Flink) || (!ListHead->Blink))
return;
/* Loop the list */
LastEntry = ListHead;
NextEntry = ListHead->Flink;
while (NextEntry != ListHead)
{
/* Get the reord */
CurrentRecord = CONTAINING_RECORD(NextEntry,
KBUGCHECK_CALLBACK_RECORD,
Entry);
/* Validate it */
// TODO/FIXME: Check whether the memory CurrentRecord points to
// is still accessible and valid!
if (CurrentRecord->Entry.Blink != LastEntry) return;
Checksum = (ULONG_PTR)CurrentRecord->CallbackRoutine;
Checksum += (ULONG_PTR)CurrentRecord->Buffer;
Checksum += (ULONG_PTR)CurrentRecord->Length;
Checksum += (ULONG_PTR)CurrentRecord->Component;
/* Make sure it's inserted and validated */
if ((CurrentRecord->State == BufferInserted) &&
(CurrentRecord->Checksum == Checksum))
{
/* Call the routine */
CurrentRecord->State = BufferStarted;
_SEH2_TRY
{
(CurrentRecord->CallbackRoutine)(CurrentRecord->Buffer,
CurrentRecord->Length);
CurrentRecord->State = BufferFinished;
}
_SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
{
CurrentRecord->State = BufferIncomplete;
}
_SEH2_END;
}
/* Go to the next entry */
LastEntry = NextEntry;
NextEntry = NextEntry->Flink;
}
}
VOID
NTAPI
KiBugCheckDebugBreak(IN ULONG StatusCode)
{
/*
* Wrap this in SEH so we don't crash if
* there is no debugger or if it disconnected
*/
DoBreak:
_SEH2_TRY
{
/* Breakpoint */
DbgBreakPointWithStatus(StatusCode);
}
_SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
{
/* No debugger, halt the CPU */
HalHaltSystem();
}
_SEH2_END;
/* Break again if this wasn't first try */
if (StatusCode != DBG_STATUS_BUGCHECK_FIRST) goto DoBreak;
}
PCHAR
NTAPI
KeBugCheckUnicodeToAnsi(IN PUNICODE_STRING Unicode,
OUT PCHAR Ansi,
IN ULONG Length)
{
PCHAR p;
PWCHAR pw;
ULONG i;
/* Set length and normalize it */
i = Unicode->Length / sizeof(WCHAR);
i = min(i, Length - 1);
/* Set source and destination, and copy */
pw = Unicode->Buffer;
p = Ansi;
while (i--) *p++ = (CHAR)*pw++;
/* Null terminate and return */
*p = ANSI_NULL;
return Ansi;
}
VOID
NTAPI
KiDumpParameterImages(IN PCHAR Message,
IN PULONG_PTR Parameters,
IN ULONG ParameterCount,
IN PKE_BUGCHECK_UNICODE_TO_ANSI ConversionRoutine)
{
ULONG i;
BOOLEAN InSystem;
PLDR_DATA_TABLE_ENTRY LdrEntry;
PVOID ImageBase;
PUNICODE_STRING DriverName;
CHAR AnsiName[32];
PIMAGE_NT_HEADERS NtHeader;
ULONG TimeStamp;
BOOLEAN FirstRun = TRUE;
/* Loop parameters */
for (i = 0; i < ParameterCount; i++)
{
/* Get the base for this parameter */
ImageBase = KiPcToFileHeader((PVOID)Parameters[i],
&LdrEntry,
FALSE,
&InSystem);
if (!ImageBase)
{
/* FIXME: Add code to check for unloaded drivers */
DPRINT1("Potentially unloaded driver!\n");
continue;
}
else
{
/* Get the NT Headers and Timestamp */
NtHeader = RtlImageNtHeader(LdrEntry->DllBase);
TimeStamp = NtHeader->FileHeader.TimeDateStamp;
/* Convert the driver name */
DriverName = &LdrEntry->BaseDllName;
ConversionRoutine(&LdrEntry->BaseDllName,
AnsiName,
sizeof(AnsiName));
}
/* Format driver name */
sprintf(Message,
"%s** %12s - Address %p base at %p, DateStamp %08lx\r\n",
FirstRun ? "\r\n*":"*",
AnsiName,
(PVOID)Parameters[i],
ImageBase,
TimeStamp);
/* Check if we only had one parameter */
if (ParameterCount <= 1)
{
/* Then just save the name */
KiBugCheckDriver = DriverName;
}
else
{
/* Otherwise, display the message */
InbvDisplayString(Message);
}
/* Loop again */
FirstRun = FALSE;
}
}
VOID
NTAPI
KiDisplayBlueScreen(IN ULONG MessageId,
IN BOOLEAN IsHardError,
IN PCHAR HardErrCaption OPTIONAL,
IN PCHAR HardErrMessage OPTIONAL,
IN PCHAR Message)
{
CHAR AnsiName[75];
/* Check if bootvid is installed */
if (InbvIsBootDriverInstalled())
{
/* Acquire ownership and reset the display */
InbvAcquireDisplayOwnership();
InbvResetDisplay();
/* Display blue screen */
InbvSolidColorFill(0, 0, 639, 479, 4);
InbvSetTextColor(15);
InbvInstallDisplayStringFilter(NULL);
InbvEnableDisplayString(TRUE);
InbvSetScrollRegion(0, 0, 639, 479);
}
/* Check if this is a hard error */
if (IsHardError)
{
/* Display caption and message */
if (HardErrCaption) InbvDisplayString(HardErrCaption);
if (HardErrMessage) InbvDisplayString(HardErrMessage);
}
/* Begin the display */
InbvDisplayString("\r\n");
/* Print out initial message */
KeGetBugMessageText(BUGCHECK_MESSAGE_INTRO, NULL);
InbvDisplayString("\r\n\r\n");
/* Check if we have a driver */
if (KiBugCheckDriver)
{
/* Print out into to driver name */
KeGetBugMessageText(BUGCODE_ID_DRIVER, NULL);
/* Convert and print out driver name */
KeBugCheckUnicodeToAnsi(KiBugCheckDriver, AnsiName, sizeof(AnsiName));
InbvDisplayString(" ");
InbvDisplayString(AnsiName);
InbvDisplayString("\r\n\r\n");
}
/* Check if this is the generic message */
if (MessageId == BUGCODE_PSS_MESSAGE)
{
/* It is, so get the bug code string as well */
KeGetBugMessageText((ULONG)KiBugCheckData[0], NULL);
InbvDisplayString("\r\n\r\n");
}
/* Print second introduction message */
KeGetBugMessageText(PSS_MESSAGE_INTRO, NULL);
InbvDisplayString("\r\n\r\n");
/* Get the bug code string */
KeGetBugMessageText(MessageId, NULL);
InbvDisplayString("\r\n\r\n");
/* Print message for technical information */
KeGetBugMessageText(BUGCHECK_TECH_INFO, NULL);
/* Show the technical Data */
sprintf(AnsiName,
"\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(AnsiName);
/* Check if we have a driver*/
if (KiBugCheckDriver)
{
/* Display technical driver data */
InbvDisplayString(Message);
}
else
{
/* Dump parameter information */
KiDumpParameterImages(Message,
(PVOID)&KiBugCheckData[1],
4,
KeBugCheckUnicodeToAnsi);
}
}
VOID
NTAPI
KeBugCheckWithTf(IN ULONG BugCheckCode,
IN ULONG_PTR BugCheckParameter1,
IN ULONG_PTR BugCheckParameter2,
IN ULONG_PTR BugCheckParameter3,
IN ULONG_PTR BugCheckParameter4,
IN PKTRAP_FRAME TrapFrame)
{
PKPRCB Prcb = KeGetCurrentPrcb();
CONTEXT Context;
ULONG MessageId;
CHAR AnsiName[128];
BOOLEAN IsSystem, IsHardError = FALSE, Reboot = FALSE;
PCHAR HardErrCaption = NULL, HardErrMessage = NULL;
PVOID Pc = NULL, Memory;
PVOID DriverBase;
PLDR_DATA_TABLE_ENTRY LdrEntry;
PULONG_PTR HardErrorParameters;
KIRQL OldIrql;
#ifdef CONFIG_SMP
LONG i = 0;
#endif
/* Set active bugcheck */
KeBugCheckActive = TRUE;
KiBugCheckDriver = NULL;
/* Check if this is power failure simulation */
if (BugCheckCode == POWER_FAILURE_SIMULATE)
{
/* Call the Callbacks and reboot */
KiDoBugCheckCallbacks();
HalReturnToFirmware(HalRebootRoutine);
}
/* Save the IRQL and set hardware trigger */
Prcb->DebuggerSavedIRQL = KeGetCurrentIrql();
InterlockedIncrement((PLONG)&KiHardwareTrigger);
/* Capture the CPU Context */
RtlCaptureContext(&Prcb->ProcessorState.ContextFrame);
KiSaveProcessorControlState(&Prcb->ProcessorState);
Context = Prcb->ProcessorState.ContextFrame;
/* FIXME: Call the Watchdog if it's registered */
/* Check which bugcode this is */
switch (BugCheckCode)
{
/* These bug checks already have detailed messages, keep them */
case UNEXPECTED_KERNEL_MODE_TRAP:
case DRIVER_CORRUPTED_EXPOOL:
case ACPI_BIOS_ERROR:
case ACPI_BIOS_FATAL_ERROR:
case THREAD_STUCK_IN_DEVICE_DRIVER:
case DATA_BUS_ERROR:
case FAT_FILE_SYSTEM:
case NO_MORE_SYSTEM_PTES:
case INACCESSIBLE_BOOT_DEVICE:
/* Keep the same code */
MessageId = BugCheckCode;
break;
/* Check if this is a kernel-mode exception */
case KERNEL_MODE_EXCEPTION_NOT_HANDLED:
case SYSTEM_THREAD_EXCEPTION_NOT_HANDLED:
case KMODE_EXCEPTION_NOT_HANDLED:
/* Use the generic text message */
MessageId = KMODE_EXCEPTION_NOT_HANDLED;
break;
/* File-system errors */
case NTFS_FILE_SYSTEM:
/* Use the generic message for FAT */
MessageId = FAT_FILE_SYSTEM;
break;
/* Check if this is a coruption of the Mm's Pool */
case DRIVER_CORRUPTED_MMPOOL:
/* Use generic corruption message */
MessageId = DRIVER_CORRUPTED_EXPOOL;
break;
/* Check if this is a signature check failure */
case STATUS_SYSTEM_IMAGE_BAD_SIGNATURE:
/* Use the generic corruption message */
MessageId = BUGCODE_PSS_MESSAGE_SIGNATURE;
break;
/* All other codes */
default:
/* Use the default bugcheck message */
MessageId = BUGCODE_PSS_MESSAGE;
break;
}
/* Save bugcheck data */
KiBugCheckData[0] = BugCheckCode;
KiBugCheckData[1] = BugCheckParameter1;
KiBugCheckData[2] = BugCheckParameter2;
KiBugCheckData[3] = BugCheckParameter3;
KiBugCheckData[4] = BugCheckParameter4;
/* Now check what bugcheck this is */
switch (BugCheckCode)
{
/* Invalid access to R/O memory or Unhandled KM Exception */
case KERNEL_MODE_EXCEPTION_NOT_HANDLED:
case ATTEMPTED_WRITE_TO_READONLY_MEMORY:
case ATTEMPTED_EXECUTE_OF_NOEXECUTE_MEMORY:
{
/* Check if we have a trap frame */
if (!TrapFrame)
{
/* Use parameter 3 as a trap frame, if it exists */
if (BugCheckParameter3) TrapFrame = (PVOID)BugCheckParameter3;
}
/* Check if we got one now and if we need to get the Program Counter */
if ((TrapFrame) &&
(BugCheckCode != KERNEL_MODE_EXCEPTION_NOT_HANDLED))
{
/* Get the Program Counter */
Pc = (PVOID)KeGetTrapFramePc(TrapFrame);
}
break;
}
/* Wrong IRQL */
case IRQL_NOT_LESS_OR_EQUAL:
{
/*
* The NT kernel has 3 special sections:
* MISYSPTE, POOLMI and POOLCODE. The bug check code can
* determine in which of these sections this bugcode happened
* and provide a more detailed analysis. For now, we don't.
*/
/* Program Counter is in parameter 4 */
Pc = (PVOID)BugCheckParameter4;
/* Get the driver base */
DriverBase = KiPcToFileHeader(Pc,
&LdrEntry,
FALSE,
&IsSystem);
if (IsSystem)
{
/*
* The error happened inside the kernel or HAL.
* Get the memory address that was being referenced.
*/
Memory = (PVOID)BugCheckParameter1;
/* Find to which driver it belongs */
DriverBase = KiPcToFileHeader(Memory,
&LdrEntry,
TRUE,
&IsSystem);
if (DriverBase)
{
/* Get the driver name and update the bug code */
KiBugCheckDriver = &LdrEntry->BaseDllName;
KiBugCheckData[0] = DRIVER_PORTION_MUST_BE_NONPAGED;
}
else
{
/* Find the driver that unloaded at this address */
KiBugCheckDriver = NULL; // FIXME: ROS can't locate
/* Check if the cause was an unloaded driver */
if (KiBugCheckDriver)
{
/* Update bug check code */
KiBugCheckData[0] =
SYSTEM_SCAN_AT_RAISED_IRQL_CAUGHT_IMPROPER_DRIVER_UNLOAD;
}
}
}
else
{
/* Update the bug check code */
KiBugCheckData[0] = DRIVER_IRQL_NOT_LESS_OR_EQUAL;
}
/* Clear Pc so we don't look it up later */
Pc = NULL;
break;
}
/* Hard error */
case FATAL_UNHANDLED_HARD_ERROR:
{
/* Copy bug check data from hard error */
HardErrorParameters = (PULONG_PTR)BugCheckParameter2;
KiBugCheckData[0] = BugCheckParameter1;
KiBugCheckData[1] = HardErrorParameters[0];
KiBugCheckData[2] = HardErrorParameters[1];
KiBugCheckData[3] = HardErrorParameters[2];
KiBugCheckData[4] = HardErrorParameters[3];
/* Remember that this is hard error and set the caption/message */
IsHardError = TRUE;
HardErrCaption = (PCHAR)BugCheckParameter3;
HardErrMessage = (PCHAR)BugCheckParameter4;
break;
}
/* Page fault */
case PAGE_FAULT_IN_NONPAGED_AREA:
{
/* Assume no driver */
DriverBase = NULL;
/* Check if we have a trap frame */
if (!TrapFrame)
{
/* We don't, use parameter 3 if possible */
if (BugCheckParameter3) TrapFrame = (PVOID)BugCheckParameter3;
}
/* Check if we have a frame now */
if (TrapFrame)
{
/* Get the Program Counter */
Pc = (PVOID)KeGetTrapFramePc(TrapFrame);
KiBugCheckData[3] = (ULONG_PTR)Pc;
/* Find out if was in the kernel or drivers */
DriverBase = KiPcToFileHeader(Pc,
&LdrEntry,
FALSE,
&IsSystem);
}
else
{
/* Can't blame a driver, assume system */
IsSystem = TRUE;
}
/* FIXME: Check for session pool in addition to special pool */
/* Special pool has its own bug check codes */
if (MmIsSpecialPoolAddress((PVOID)BugCheckParameter1))
{
if (MmIsSpecialPoolAddressFree((PVOID)BugCheckParameter1))
{
KiBugCheckData[0] = IsSystem
? PAGE_FAULT_IN_FREED_SPECIAL_POOL
: DRIVER_PAGE_FAULT_IN_FREED_SPECIAL_POOL;
}
else
{
KiBugCheckData[0] = IsSystem
? PAGE_FAULT_BEYOND_END_OF_ALLOCATION
: DRIVER_PAGE_FAULT_BEYOND_END_OF_ALLOCATION;
}
}
else if (!DriverBase)
{
/* Find the driver that unloaded at this address */
KiBugCheckDriver = NULL; // FIXME: ROS can't locate
/* Check if the cause was an unloaded driver */
if (KiBugCheckDriver)
{
KiBugCheckData[0] =
DRIVER_UNLOADED_WITHOUT_CANCELLING_PENDING_OPERATIONS;
}
}
break;
}
/* Check if the driver forgot to unlock pages */
case DRIVER_LEFT_LOCKED_PAGES_IN_PROCESS:
/* Program Counter is in parameter 1 */
Pc = (PVOID)BugCheckParameter1;
break;
/* Check if the driver consumed too many PTEs */
case DRIVER_USED_EXCESSIVE_PTES:
/* Loader entry is in parameter 1 */
LdrEntry = (PVOID)BugCheckParameter1;
KiBugCheckDriver = &LdrEntry->BaseDllName;
break;
/* Check if the driver has a stuck thread */
case THREAD_STUCK_IN_DEVICE_DRIVER:
/* The name is in Parameter 3 */
KiBugCheckDriver = (PVOID)BugCheckParameter3;
break;
/* Anything else */
default:
break;
}
/* Do we have a driver name? */
if (KiBugCheckDriver)
{
/* Convert it to ANSI */
KeBugCheckUnicodeToAnsi(KiBugCheckDriver, AnsiName, sizeof(AnsiName));
}
else
{
/* Do we have a Program Counter? */
if (Pc)
{
/* Dump image name */
KiDumpParameterImages(AnsiName,
(PULONG_PTR)&Pc,
1,
KeBugCheckUnicodeToAnsi);
}
}
/* Check if we need to save the context for KD */
if (!KdPitchDebugger) KdDebuggerDataBlock.SavedContext = (ULONG_PTR)&Context;
/* Check if a debugger is connected */
if ((BugCheckCode != MANUALLY_INITIATED_CRASH) && (KdDebuggerEnabled))
{
/* Crash on the debugger console */
DbgPrint("\n*** Fatal System Error: 0x%08lx\n"
" (0x%p,0x%p,0x%p,0x%p)\n\n",
KiBugCheckData[0],
KiBugCheckData[1],
KiBugCheckData[2],
KiBugCheckData[3],
KiBugCheckData[4]);
/* Check if the debugger isn't currently connected */
if (!KdDebuggerNotPresent)
{
/* Check if we have a driver to blame */
if (KiBugCheckDriver)
{
/* Dump it */
DbgPrint("Driver at fault: %s.\n", AnsiName);
}
/* Check if this was a hard error */
if (IsHardError)
{
/* Print caption and message */
if (HardErrCaption) DbgPrint(HardErrCaption);
if (HardErrMessage) DbgPrint(HardErrMessage);
}
/* Break in the debugger */
KiBugCheckDebugBreak(DBG_STATUS_BUGCHECK_FIRST);
}
else
{
/*
* ROS HACK.
* Ok, so debugging is enabled, but KDBG isn't there.
* We'll manually dump the stack for the user.
*/
KeRosDumpStackFrames(NULL, 0);
/* ROS HACK 2: Generate something useful for Bugzilla */
KeRosDumpTriageForBugZillaReport();
}
}
/* Raise IRQL to HIGH_LEVEL */
_disable();
KeRaiseIrql(HIGH_LEVEL, &OldIrql);
/* Avoid recursion */
if (!InterlockedDecrement((PLONG)&KeBugCheckCount))
{
#ifdef CONFIG_SMP
/* Set CPU that is bug checking now */
KeBugCheckOwner = Prcb->Number;
/* Freeze the other CPUs */
for (i = 0; i < KeNumberProcessors; i++)
{
if (i != (LONG)KeGetCurrentProcessorNumber())
{
/* Send the IPI and give them one second to catch up */
KiIpiSend(1 << i, IPI_FREEZE);
KeStallExecutionProcessor(1000000);
}
}
#endif
/* Display the BSOD */
KiDisplayBlueScreen(MessageId,
IsHardError,
HardErrCaption,
HardErrMessage,
AnsiName);
// TODO/FIXME: Run the registered reason-callbacks from
// the KeBugcheckReasonCallbackListHead list with the
// KbCallbackReserved1 reason.
/* Check if the debugger is disabled but we can enable it */
if (!(KdDebuggerEnabled) && !(KdPitchDebugger))
{
/* Enable it */
KdEnableDebuggerWithLock(FALSE);
}
else
{
/* Otherwise, print the last line */
InbvDisplayString("\r\n");
}
/* Save the context */
Prcb->ProcessorState.ContextFrame = Context;
/* FIXME: Support Triage Dump */
/* FIXME: Write the crash dump */
}
else
{
/* Increase recursion count */
KeBugCheckOwnerRecursionCount++;
if (KeBugCheckOwnerRecursionCount == 2)
{
/* Break in the debugger */
KiBugCheckDebugBreak(DBG_STATUS_BUGCHECK_SECOND);
}
else if (KeBugCheckOwnerRecursionCount > 2)
{
/* Halt execution */
while (TRUE);
}
}
/* Call the Callbacks */
KiDoBugCheckCallbacks();
/* FIXME: Call Watchdog if enabled */
/* Check if we have to reboot */
if (Reboot)
{
/* Unload symbols */
DbgUnLoadImageSymbols(NULL, (PVOID)MAXULONG_PTR, 0);
HalReturnToFirmware(HalRebootRoutine);
}
/* Attempt to break in the debugger (otherwise halt CPU) */
KiBugCheckDebugBreak(DBG_STATUS_BUGCHECK_SECOND);
/* Shouldn't get here */
ASSERT(FALSE);
while (TRUE);
}
BOOLEAN
NTAPI
KiHandleNmi(VOID)
{
BOOLEAN Handled = FALSE;
PKNMI_HANDLER_CALLBACK NmiData;
/* Parse the list of callbacks */
NmiData = KiNmiCallbackListHead;
while (NmiData)
{
/* Save if this callback has handled it -- all it takes is one */
Handled |= NmiData->Callback(NmiData->Context, Handled);
NmiData = NmiData->Next;
}
/* Has anyone handled this? */
return Handled;
}
/* PUBLIC FUNCTIONS **********************************************************/
/*
* @unimplemented
*/
NTSTATUS
NTAPI
KeInitializeCrashDumpHeader(IN ULONG Type,
IN ULONG Flags,
OUT PVOID Buffer,
IN ULONG BufferSize,
OUT ULONG BufferNeeded OPTIONAL)
{
UNIMPLEMENTED;
return STATUS_UNSUCCESSFUL;
}
/*
* @implemented
*/
BOOLEAN
NTAPI
KeDeregisterBugCheckCallback(IN PKBUGCHECK_CALLBACK_RECORD CallbackRecord)
{
KIRQL OldIrql;
BOOLEAN Status = FALSE;
/* Raise IRQL to High */
KeRaiseIrql(HIGH_LEVEL, &OldIrql);
/* Check the Current State */
if (CallbackRecord->State == BufferInserted)
{
/* Reset state and remove from list */
CallbackRecord->State = BufferEmpty;
RemoveEntryList(&CallbackRecord->Entry);
Status = TRUE;
}
/* Lower IRQL and return */
KeLowerIrql(OldIrql);
return Status;
}
/*
* @implemented
*/
BOOLEAN
NTAPI
KeDeregisterBugCheckReasonCallback(
IN PKBUGCHECK_REASON_CALLBACK_RECORD CallbackRecord)
{
KIRQL OldIrql;
BOOLEAN Status = FALSE;
/* Raise IRQL to High */
KeRaiseIrql(HIGH_LEVEL, &OldIrql);
/* Check the Current State */
if (CallbackRecord->State == BufferInserted)
{
/* Reset state and remove from list */
CallbackRecord->State = BufferEmpty;
RemoveEntryList(&CallbackRecord->Entry);
Status = TRUE;
}
/* Lower IRQL and return */
KeLowerIrql(OldIrql);
return Status;
}
/*
* @implemented
*/
BOOLEAN
NTAPI
KeRegisterBugCheckCallback(IN PKBUGCHECK_CALLBACK_RECORD CallbackRecord,
IN PKBUGCHECK_CALLBACK_ROUTINE CallbackRoutine,
IN PVOID Buffer,
IN ULONG Length,
IN PUCHAR Component)
{
KIRQL OldIrql;
BOOLEAN Status = FALSE;
/* Raise IRQL to High */
KeRaiseIrql(HIGH_LEVEL, &OldIrql);
/* Check the Current State first so we don't double-register */
if (CallbackRecord->State == BufferEmpty)
{
/* Set the Callback Settings and insert into the list */
CallbackRecord->Length = Length;
CallbackRecord->Buffer = Buffer;
CallbackRecord->Component = Component;
CallbackRecord->CallbackRoutine = CallbackRoutine;
CallbackRecord->State = BufferInserted;
InsertTailList(&KeBugcheckCallbackListHead, &CallbackRecord->Entry);
Status = TRUE;
}
/* Lower IRQL and return */
KeLowerIrql(OldIrql);
return Status;
}
/*
* @implemented
*/
BOOLEAN
NTAPI
KeRegisterBugCheckReasonCallback(
IN PKBUGCHECK_REASON_CALLBACK_RECORD CallbackRecord,
IN PKBUGCHECK_REASON_CALLBACK_ROUTINE CallbackRoutine,
IN KBUGCHECK_CALLBACK_REASON Reason,
IN PUCHAR Component)
{
KIRQL OldIrql;
BOOLEAN Status = FALSE;
/* Raise IRQL to High */
KeRaiseIrql(HIGH_LEVEL, &OldIrql);
/* Check the Current State first so we don't double-register */
if (CallbackRecord->State == BufferEmpty)
{
/* Set the Callback Settings and insert into the list */
CallbackRecord->Component = Component;
CallbackRecord->CallbackRoutine = CallbackRoutine;
CallbackRecord->State = BufferInserted;
CallbackRecord->Reason = Reason;
InsertTailList(&KeBugcheckReasonCallbackListHead,
&CallbackRecord->Entry);
Status = TRUE;
}
/* Lower IRQL and return */
KeLowerIrql(OldIrql);
return Status;
}
/*
* @implemented
*/
PVOID
NTAPI
KeRegisterNmiCallback(IN PNMI_CALLBACK CallbackRoutine,
IN PVOID Context)
{
KIRQL OldIrql;
PKNMI_HANDLER_CALLBACK NmiData, Next;
ASSERT_IRQL_LESS_OR_EQUAL(DISPATCH_LEVEL);
/* Allocate NMI callback data */
NmiData = ExAllocatePoolWithTag(NonPagedPool, sizeof(*NmiData), TAG_KNMI);
if (!NmiData) return NULL;
/* Fill in the information */
NmiData->Callback = CallbackRoutine;
NmiData->Context = Context;
NmiData->Handle = NmiData;
/* Insert it into NMI callback list */
KiAcquireNmiListLock(&OldIrql);
NmiData->Next = KiNmiCallbackListHead;
Next = InterlockedCompareExchangePointer((PVOID*)&KiNmiCallbackListHead,
NmiData,
NmiData->Next);
ASSERT(Next == NmiData->Next);
KiReleaseNmiListLock(OldIrql);
/* Return the opaque "handle" */
return NmiData->Handle;
}
/*
* @implemented
*/
NTSTATUS
NTAPI
KeDeregisterNmiCallback(IN PVOID Handle)
{
KIRQL OldIrql;
PKNMI_HANDLER_CALLBACK NmiData;
PKNMI_HANDLER_CALLBACK* Previous;
ASSERT_IRQL_LESS_OR_EQUAL(DISPATCH_LEVEL);
/* Find in the list the NMI callback corresponding to the handle */
KiAcquireNmiListLock(&OldIrql);
Previous = &KiNmiCallbackListHead;
NmiData = *Previous;
while (NmiData)
{
if (NmiData->Handle == Handle)
{
/* The handle is the pointer to the callback itself */
ASSERT(Handle == NmiData);
/* Found it, remove from the list */
*Previous = NmiData->Next;
break;
}
/* Not found; try again */
Previous = &NmiData->Next;
NmiData = *Previous;
}
KiReleaseNmiListLock(OldIrql);
/* If we have found the entry, free it */
if (NmiData)
{
ExFreePoolWithTag(NmiData, TAG_KNMI);
return STATUS_SUCCESS;
}
return STATUS_INVALID_HANDLE;
}
/*
* @implemented
*/
VOID
NTAPI
KeBugCheckEx(IN ULONG BugCheckCode,
IN ULONG_PTR BugCheckParameter1,
IN ULONG_PTR BugCheckParameter2,
IN ULONG_PTR BugCheckParameter3,
IN ULONG_PTR BugCheckParameter4)
{
/* Call the internal API */
KeBugCheckWithTf(BugCheckCode,
BugCheckParameter1,
BugCheckParameter2,
BugCheckParameter3,
BugCheckParameter4,
NULL);
}
/*
* @implemented
*/
VOID
NTAPI
KeBugCheck(ULONG BugCheckCode)
{
/* Call the internal API */
KeBugCheckWithTf(BugCheckCode, 0, 0, 0, 0, NULL);
}
/*
* @implemented
*/
VOID
NTAPI
KeEnterKernelDebugger(VOID)
{
/* Disable interrupts */
KiHardwareTrigger = 1;
_disable();
/* Check the bugcheck count */
if (!InterlockedDecrement((PLONG)&KeBugCheckCount))
{
/* There was only one, is the debugger disabled? */
if (!(KdDebuggerEnabled) && !(KdPitchDebugger))
{
/* Enable the debugger */
KdInitSystem(0, NULL);
}
}
/* Break in the debugger */
KiBugCheckDebugBreak(DBG_STATUS_FATAL);
}
/* EOF */