Something went wrong. Try again.
Reactos
Something went wrong. Try again.
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467/* * 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>
#ifdef KDBG#include <kdbg/kdb.h>#endif
#define NDEBUG#include <debug.h>
/* 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;
/* Jira Reporting */UNICODE_STRING KeRosProcessorName, KeRosBiosDate, KeRosBiosVersion;UNICODE_STRING KeRosVideoBiosDate, KeRosVideoBiosVersion;
/* PRIVATE FUNCTIONS *********************************************************/
PVOIDNTAPIKiPcToFileHeader(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;}
PVOIDNTAPIKiRosPcToUserFileHeader(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;}
USHORTNTAPIKeRosCaptureUserStackBackTrace(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;}
VOIDFASTCALLKeRosDumpStackFrameArray(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"); }}
VOIDNTAPIKeRosDumpStackFrames(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); } }}
CODE_SEG("INIT")VOIDNTAPIKiInitializeBugCheck(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 = RT_MESSAGETABLE; ResourceInfo.Name = 1; ResourceInfo.Language = MAKELANGID(LANG_NEUTRAL, SUBLANG_NEUTRAL);
/* 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; }}
BOOLEANNTAPIKeGetBugMessageText(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;}
VOIDNTAPIKiDoBugCheckCallbacks(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 record */ 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; }}
VOIDNTAPIKiBugCheckDebugBreak(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;}
PCHARNTAPIKeBugCheckUnicodeToAnsi(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;}
VOIDNTAPIKiDumpParameterImages(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; }}
VOIDNTAPIKiDisplayBlueScreen(IN ULONG MessageId, IN BOOLEAN IsHardError, IN PCHAR HardErrCaption OPTIONAL, IN PCHAR HardErrMessage OPTIONAL, IN PCHAR Message){ ULONG BugCheckCode = (ULONG)KiBugCheckData[0]; BOOLEAN Enable = TRUE; CHAR AnsiName[107];
/* Enable headless support for bugcheck */ HeadlessDispatch(HeadlessCmdStartBugCheck, NULL, 0, NULL, NULL); HeadlessDispatch(HeadlessCmdEnableTerminal, &Enable, sizeof(Enable), NULL, NULL); HeadlessDispatch(HeadlessCmdSendBlueScreenData, &BugCheckCode, sizeof(BugCheckCode), NULL, NULL);
/* Check if bootvid is installed */ if (InbvIsBootDriverInstalled()) { /* Acquire ownership and reset the display */ InbvAcquireDisplayOwnership(); InbvResetDisplay();
/* Display blue screen */ InbvSolidColorFill(0, 0, SCREEN_WIDTH - 1, SCREEN_HEIGHT - 1, BV_COLOR_BLUE); InbvSetTextColor(BV_COLOR_WHITE); InbvInstallDisplayStringFilter(NULL); InbvEnableDisplayString(TRUE); InbvSetScrollRegion(0, 0, SCREEN_WIDTH - 1, SCREEN_HEIGHT - 1); }
/* 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(BugCheckCode, 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 */ RtlStringCbPrintfA(AnsiName, sizeof(AnsiName), "\r\n\r\n*** STOP: 0x%08lX (0x%p,0x%p,0x%p,0x%p)\r\n\r\n", BugCheckCode, (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); }}
DECLSPEC_NORETURNVOIDNTAPIKeBugCheckWithTf(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;
/* 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); } }
/* 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 */ KxFreezeExecution();#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 */ // TODO: The crash-dump helper must set the Reboot variable. Reboot = !!IopAutoReboot; } 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);}
BOOLEANNTAPIKiHandleNmi(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 */NTSTATUSNTAPIKeInitializeCrashDumpHeader(IN ULONG Type, IN ULONG Flags, OUT PVOID Buffer, IN ULONG BufferSize, OUT ULONG BufferNeeded OPTIONAL){ UNIMPLEMENTED; return STATUS_UNSUCCESSFUL;}
/* * @implemented */BOOLEANNTAPIKeDeregisterBugCheckCallback(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 */BOOLEANNTAPIKeDeregisterBugCheckReasonCallback( 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 */BOOLEANNTAPIKeRegisterBugCheckCallback(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 */BOOLEANNTAPIKeRegisterBugCheckReasonCallback( 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 */PVOIDNTAPIKeRegisterNmiCallback(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 */NTSTATUSNTAPIKeDeregisterNmiCallback(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 */DECLSPEC_NORETURNVOIDNTAPIKeBugCheckEx(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 */DECLSPEC_NORETURNVOIDNTAPIKeBugCheck(ULONG BugCheckCode){ /* Call the internal API */ KeBugCheckWithTf(BugCheckCode, 0, 0, 0, 0, NULL);}
/* * @implemented */VOIDNTAPIKeEnterKernelDebugger(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 */