/*
- * ReactOS kernel
- * Copyright (C) 1998, 1999, 2000, 2001, 2002 ReactOS Team
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
- */
-/* $Id: bug.c,v 1.31 2003/07/10 17:44:06 royce Exp $
- *
- * PROJECT: ReactOS kernel
+ * PROJECT: ReactOS Kernel
+ * LICENSE: GPL - See COPYING in the top level directory
* FILE: ntoskrnl/ke/bug.c
- * PURPOSE: Graceful system shutdown if a bug is detected
- * PROGRAMMER: David Welch (welch@cwcom.net)
- * PORTABILITY: Unchecked
- * UPDATE HISTORY:
- * Created 22/05/98
- * Phillip Susi: 12/8/99: Minor fix
+ * PURPOSE: Bugcheck Support
+ * PROGRAMMERS: Alex Ionescu (alex.ionescu@reactos.org)
*/
-/* INCLUDES *****************************************************************/
-
-#include <ddk/ntddk.h>
-#include <internal/kd.h>
-#include <internal/ke.h>
-#include <internal/ps.h>
+/* INCLUDES ******************************************************************/
+#include <ntoskrnl.h>
+#define NDEBUG
#include <internal/debug.h>
-/* GLOBALS ******************************************************************/
+#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;
+PRTL_MESSAGE_RESOURCE_DATA KiBugCodeMessages;
+ULONG KeBugCheckCount = 1;
+ULONG KiHardwareTrigger;
+PUNICODE_STRING KiBugCheckDriver;
+ULONG_PTR KiBugCheckData[5];
-static LIST_ENTRY BugcheckCallbackListHead = {NULL,NULL};
-static ULONG InBugCheck;
+/* PRIVATE FUNCTIONS *********************************************************/
+
+BOOLEAN
+NTAPI
+KiRosPrintAddress(PVOID address)
+{
+ PLIST_ENTRY current_entry;
+ PLDR_DATA_TABLE_ENTRY current;
+ extern LIST_ENTRY PsLoadedModuleList;
+ ULONG_PTR RelativeAddress;
+ ULONG i = 0;
+
+ do
+ {
+ current_entry = PsLoadedModuleList.Flink;
-VOID PsDumpThreads(VOID);
+ while (current_entry != &PsLoadedModuleList)
+ {
+ current = CONTAINING_RECORD(current_entry,
+ LDR_DATA_TABLE_ENTRY,
+ InLoadOrderLinks);
-/* FUNCTIONS *****************************************************************/
+ if (address >= (PVOID)current->DllBase &&
+ address < (PVOID)((ULONG_PTR)current->DllBase +
+ current->SizeOfImage))
+ {
+ RelativeAddress = (ULONG_PTR)address -
+ (ULONG_PTR)current->DllBase;
+ DbgPrint("<%wZ: %x>", ¤t->FullDllName, RelativeAddress);
+ return(TRUE);
+ }
+ current_entry = current_entry->Flink;
+ }
+ } while(++i <= 1);
+
+ return(FALSE);
+}
VOID
-KeInitializeBugCheck(VOID)
+NTAPI
+KeRosDumpStackFrames(IN PULONG Frame OPTIONAL,
+ IN ULONG FrameCount OPTIONAL)
{
- InitializeListHead(&BugcheckCallbackListHead);
- InBugCheck = 0;
+ ULONG Frames[32];
+ ULONG i, Addr;
+
+ /* If the caller didn't ask, assume 32 frames */
+ if (!FrameCount) FrameCount = 32;
+
+ /* Get the current frames */
+ FrameCount = RtlCaptureStackBackTrace(2, FrameCount, (PVOID*)Frames, NULL);
+
+ /* Now loop them (skip the two. One for the dumper, one for the caller) */
+ for (i = 0; i < FrameCount; i++)
+ {
+ /* Get the EIP */
+ Addr = Frames[i];
+
+ /* If we had a custom frame, make sure we've reached it first */
+ if ((Frame) && (Frame[1] == Addr))
+ {
+ Frame = NULL;
+ }
+ else if (Frame)
+ {
+ /* Skip this entry */
+ continue;
+ }
+
+ /* Print it out */
+ if (!KeRosPrintAddress((PVOID)Addr)) DbgPrint("<%X>", Addr);
+
+ /* Go to the next frame */
+ DbgPrint("\n");
+ }
+
+ /* Finish the output */
+ DbgPrint("\n");
}
-/*
- * @unimplemented
- */
-BOOLEAN STDCALL
-KeDeregisterBugCheckCallback(PKBUGCHECK_CALLBACK_RECORD CallbackRecord)
+VOID
+INIT_FUNCTION
+NTAPI
+KiInitializeBugCheck(VOID)
{
- UNIMPLEMENTED;
- return FALSE;
+ PRTL_MESSAGE_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;
+ }
}
-/*
- * @implemented
- */
-BOOLEAN STDCALL
-KeRegisterBugCheckCallback(PKBUGCHECK_CALLBACK_RECORD CallbackRecord,
- PKBUGCHECK_CALLBACK_ROUTINE CallbackRoutine,
- PVOID Buffer,
- ULONG Length,
- PUCHAR Component)
+VOID
+NTAPI
+KeGetBugMessageText(IN ULONG BugCheckCode,
+ OUT PANSI_STRING OutputString OPTIONAL)
{
- InsertTailList(&BugcheckCallbackListHead, &CallbackRecord->Entry);
- CallbackRecord->Length = Length;
- CallbackRecord->Buffer = Buffer;
- CallbackRecord->Component = Component;
- CallbackRecord->CallbackRoutine = CallbackRoutine;
- return(TRUE);
+ ULONG i;
+ ULONG IdOffset;
+ ULONG_PTR MessageEntry;
+ PCHAR BugCode;
+
+ /* 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 = KiBugCodeMessages->Blocks[i].OffsetToEntries +
+ (ULONG_PTR)KiBugCodeMessages;
+ IdOffset = BugCheckCode - KiBugCodeMessages->Blocks[i].LowId;
+
+ /* Get offset to ID */
+ for (i = 0; i < IdOffset; i++)
+ {
+ /* Advance in the Entries */
+ MessageEntry += ((PRTL_MESSAGE_RESOURCE_ENTRY)MessageEntry)->
+ Length;
+ }
+
+ /* Get the final Code */
+ BugCode = ((PRTL_MESSAGE_RESOURCE_ENTRY)MessageEntry)->Text;
+ i = strlen(BugCode);
+
+ /* Return it in the OutputString */
+ if (OutputString)
+ {
+ OutputString->Buffer = BugCode;
+ OutputString->Length = i + 1;
+ OutputString->MaximumLength = i + 1;
+ }
+ else
+ {
+ /* Direct Output to Screen */
+ InbvDisplayString(BugCode);
+ InbvDisplayString("\r");
+ break;
+ }
+ }
+ }
}
-/*
- * @implemented
- */
-VOID STDCALL
-KeBugCheckWithTf(ULONG BugCheckCode,
- ULONG BugCheckParameter1,
- ULONG BugCheckParameter2,
- ULONG BugCheckParameter3,
- ULONG BugCheckParameter4,
- PKTRAP_FRAME Tf)
+VOID
+NTAPI
+KiDoBugCheckCallbacks(VOID)
{
- PRTL_MESSAGE_RESOURCE_ENTRY Message;
- NTSTATUS Status;
-
- /* Make sure we're switching back to the blue screen and print messages on it */
- HalReleaseDisplayOwnership();
- KdDebugState |= KD_DEBUG_SCREEN;
-
- __asm__("cli\n\t");
- DbgPrint("Bug detected (code %x param %x %x %x %x)\n",
- BugCheckCode,
- BugCheckParameter1,
- BugCheckParameter2,
- BugCheckParameter3,
- BugCheckParameter4);
-
- Status = RtlFindMessage((PVOID)KERNEL_BASE, //0xC0000000,
- 11, //RT_MESSAGETABLE,
- 0x09, //0x409,
- BugCheckCode,
- &Message);
- if (NT_SUCCESS(Status))
+ 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))
{
- if (Message->Flags == 0)
- DbgPrint(" %s\n", Message->Text);
- else
- DbgPrint(" %S\n", (PWSTR)Message->Text);
+ /* Loop the list */
+ LastEntry = ListHead;
+ NextEntry = ListHead->Flink;
+ while (NextEntry != ListHead)
+ {
+ /* Get the reord */
+ CurrentRecord = CONTAINING_RECORD(NextEntry,
+ KBUGCHECK_CALLBACK_RECORD,
+ Entry);
+
+ /* Validate it */
+ 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 valitdated */
+ if ((CurrentRecord->State == BufferInserted) &&
+ (CurrentRecord->Checksum == Checksum))
+ {
+ /* Call the routine */
+ CurrentRecord->State = BufferStarted;
+ (CurrentRecord->CallbackRoutine)(CurrentRecord->Buffer,
+ CurrentRecord->Length);
+ CurrentRecord->State = BufferFinished;
+ }
+
+ /* Go to the next entry */
+ LastEntry = NextEntry;
+ NextEntry = NextEntry->Flink;
+ }
}
- else
+}
+
+VOID
+NTAPI
+KiBugCheckDebugBreak(IN ULONG StatusCode)
+{
+ /* If KDBG isn't connected, freeze the CPU, otherwise, break */
+ if (KdDebuggerNotPresent) for (;;) Ke386HaltProcessor();
+ DbgBreakPointWithStatus(StatusCode);
+}
+
+PVOID
+NTAPI
+KiPcToFileHeader(IN PVOID Eip,
+ OUT PLDR_DATA_TABLE_ENTRY *LdrEntry,
+ IN BOOLEAN DriversOnly,
+ OUT PBOOLEAN InKernel)
+{
+ ULONG i = 0;
+ PVOID ImageBase, EipBase = NULL;
+ PLDR_DATA_TABLE_ENTRY Entry;
+ PLIST_ENTRY ListHead, NextEntry;
+ extern LIST_ENTRY PsLoadedModuleList;
+
+ /* Assume no */
+ *InKernel = FALSE;
+
+ /* Set list pointers and make sure it's valid */
+ ListHead = &PsLoadedModuleList;
+ NextEntry = ListHead->Flink;
+ if (NextEntry)
{
- DbgPrint(" No message text found!\n\n");
+ /* Start loop */
+ while (NextEntry != ListHead)
+ {
+ /* Increase entry */
+ i++;
+
+ /* Check if this is a kernel entry and we only want drivers */
+ if ((i <= 2) && (DriversOnly == TRUE))
+ {
+ /* 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)Eip >= (ULONG_PTR)Entry->DllBase) &&
+ ((ULONG_PTR)Eip < ((ULONG_PTR)Entry->DllBase + Entry->SizeOfImage)))
+ {
+ /* Return this entry */
+ *LdrEntry = Entry;
+ EipBase = ImageBase;
+
+ /* Check if this was a kernel or HAL entry */
+ if (i <= 2) *InKernel = TRUE;
+ break;
+ }
+ }
+ }
+
+ /* Return the base address */
+ return EipBase;
+}
+
+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)
+ {
+ /* Driver wasn't found, check for unloaded driver */
+ DriverName = NULL; // FIXME: ROS can't
+ if (!DriverName) continue;
+
+ /* Convert the driver name */
+ ImageBase = (PVOID)Parameters[i];
+ ConversionRoutine(DriverName, AnsiName, sizeof(AnsiName));
+ }
+ 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\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);
}
- if (InBugCheck == 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)
{
- DbgPrint("Recursive bug check halting now\n");
- for (;;)
- {
- __asm__ ("hlt\n\t");
- }
+ /* 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");
}
- InBugCheck = 1;
- KiDumpTrapFrame(Tf, BugCheckParameter1, BugCheckParameter2);
- MmDumpToPagingFile(BugCheckCode, BugCheckParameter1,
- BugCheckParameter2, BugCheckParameter3,
- BugCheckParameter4, Tf);
- if (KdDebuggerEnabled)
+ /* Check if this is the generic message */
+ if (MessageId == BUGCODE_PSS_MESSAGE)
{
- __asm__("sti\n\t");
- DbgBreakPoint();
- __asm__("cli\n\t");
+ /* It is, so get the bug code string as well */
+ KeGetBugMessageText(KiBugCheckData[0], NULL);
+ InbvDisplayString("\r\n\r\n");
}
- for (;;)
+ /* 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 techincal Data */
+ sprintf(AnsiName,
+ "\r\n\r\n*** STOP: 0x%08lX (0x%p,0x%p,0x%p,0x%p)\r\n\r\n",
+ 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
{
- __asm__("hlt\n\t");
+ /* Dump parameter information */
+ KiDumpParameterImages(Message,
+ (PVOID)&KiBugCheckData[1],
+ 4,
+ KeBugCheckUnicodeToAnsi);
}
}
-/*
- * @implemented
- */
-VOID STDCALL
-KeBugCheckEx(ULONG BugCheckCode,
- ULONG BugCheckParameter1,
- ULONG BugCheckParameter2,
- ULONG BugCheckParameter3,
- ULONG BugCheckParameter4)
-/*
- * FUNCTION: Brings the system down in a controlled manner when an
- * inconsistency that might otherwise cause corruption has been detected
- * ARGUMENTS:
- * BugCheckCode = Specifies the reason for the bug check
- * BugCheckParameter[1-4] = Additional information about bug
- * RETURNS: Doesn't
- */
+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)
{
- PRTL_MESSAGE_RESOURCE_ENTRY Message;
- NTSTATUS Status;
-
- /* Make sure we're switching back to the blue screen and print messages on it */
- HalReleaseDisplayOwnership();
- KdDebugState |= KD_DEBUG_SCREEN;
-
- __asm__("cli\n\t");
- DbgPrint("Bug detected (code %x param %x %x %x %x)\n",
- BugCheckCode,
- BugCheckParameter1,
- BugCheckParameter2,
- BugCheckParameter3,
- BugCheckParameter4);
-
- Status = RtlFindMessage((PVOID)KERNEL_BASE, //0xC0000000,
- 11, //RT_MESSAGETABLE,
- 0x09, //0x409,
- BugCheckCode,
- &Message);
- if (NT_SUCCESS(Status))
+ PKPRCB Prcb = KeGetCurrentPrcb();
+ CONTEXT Context;
+ ULONG MessageId;
+ CHAR AnsiName[128];
+ BOOLEAN IsSystem, IsHardError = FALSE, Reboot = FALSE;
+ PCHAR HardErrCaption = NULL, HardErrMessage = NULL;
+ PVOID Eip = 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)
{
- if (Message->Flags == 0)
- DbgPrint(" %s\n", Message->Text);
- else
- DbgPrint(" %S\n", (PWSTR)Message->Text);
+ /* 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;
}
- else
+
+ /* 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)
{
- DbgPrint(" No message text found!\n\n");
+ /* 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 EIP */
+ if ((TrapFrame) &&
+ (BugCheckCode != KERNEL_MODE_EXCEPTION_NOT_HANDLED))
+ {
+ /* Get EIP */
+ Eip = (PVOID)TrapFrame->Eip;
+ }
+ 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.
+ */
+
+ /* Eip is in parameter 4 */
+ Eip = (PVOID)BugCheckParameter4;
+
+ /* Get the driver base */
+ DriverBase = KiPcToFileHeader(Eip, &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 EIP so we don't look it up later */
+ Eip = 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 EIP */
+ Eip = (PVOID)TrapFrame->Eip;
+ KiBugCheckData[3] = (ULONG)Eip;
+
+ /* Find out if was in the kernel or drivers */
+ DriverBase = KiPcToFileHeader(Eip,
+ &LdrEntry,
+ FALSE,
+ &IsSystem);
+ }
+
+ /*
+ * Now we should check if this happened in:
+ * 1) Special Pool 2) Free Special Pool 3) Session Pool
+ * and update the bugcheck code appropriately.
+ */
+
+ /* Check if we didn't have a driver base */
+ 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:
+
+ /* EIP is in parameter 1 */
+ Eip = (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;
}
- if (InBugCheck == 1)
+ /* Do we have a driver name? */
+ if (KiBugCheckDriver)
{
- DbgPrint("Recursive bug check halting now\n");
- for (;;)
- {
- __asm__("hlt\n\t");
- }
+ /* Convert it to ANSI */
+ KeBugCheckUnicodeToAnsi(KiBugCheckDriver, AnsiName, sizeof(AnsiName));
}
- InBugCheck = 1;
- if (PsGetCurrentProcess() != NULL)
+ else
{
- DbgPrint("Pid: %x <", PsGetCurrentProcess()->UniqueProcessId);
- DbgPrint("%.8s> ", PsGetCurrentProcess()->ImageFileName);
+ /* Do we have an EIP? */
+ if (Eip)
+ {
+ /* Dump image name */
+ KiDumpParameterImages(AnsiName,
+ (PULONG_PTR)&Eip,
+ 1,
+ KeBugCheckUnicodeToAnsi);
+ }
}
- if (PsGetCurrentThread() != NULL)
+
+ /* Check if we need to save the context for KD */
+
+ /* Check if a debugger is connected */
+ if ((BugCheckCode != MANUALLY_INITIATED_CRASH) && (KdDebuggerEnabled))
{
- DbgPrint("Thrd: %x Tid: %x\n",
- PsGetCurrentThread(),
- PsGetCurrentThread()->Cid.UniqueThread);
+ /* 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);
+ }
}
- KeDumpStackFrames((PULONG)__builtin_frame_address(0));
- MmDumpToPagingFile(BugCheckCode, BugCheckParameter1,
- BugCheckParameter2, BugCheckParameter3,
- BugCheckParameter4, NULL);
- if (KdDebuggerEnabled)
+ /* Raise IRQL to HIGH_LEVEL */
+ _disable();
+ KeRaiseIrql(HIGH_LEVEL, &OldIrql);
+
+ /* ROS HACK: Unlock the Kernel Address Space if we own it */
+ if (KernelAddressSpaceLock.Owner == KeGetCurrentThread())
{
- __asm__("sti\n\t");
- DbgBreakPoint();
- __asm__("cli\n\t");
+ MmUnlockAddressSpace(MmGetKernelAddressSpace());
}
- for (;;)
+ /* 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 */
+ KiIpiSendRequest(1 << i, IPI_FREEZE);
+ KeStallExecutionProcessor(1000000);
+ }
+ }
+#endif
+
+ /* Display the BSOD */
+ KiDisplayBlueScreen(MessageId,
+ IsHardError,
+ HardErrCaption,
+ HardErrMessage,
+ AnsiName);
+
+ /* 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 */
+
+ /* Write the crash dump */
+ MmDumpToPagingFile(KiBugCheckData[4],
+ KiBugCheckData[0],
+ KiBugCheckData[1],
+ KiBugCheckData[2],
+ KiBugCheckData[3],
+ TrapFrame);
+ }
+ else
{
- __asm__("hlt\n\t");
+ /* Increase recursion count */
+ KeBugCheckOwnerRecursionCount++;
+ if (KeBugCheckOwnerRecursionCount == 2)
+ {
+ /* Break in the debugger */
+ KiBugCheckDebugBreak(DBG_STATUS_BUGCHECK_SECOND);
+ }
+ else if (KeBugCheckOwnerRecursionCount > 2)
+ {
+ /* Halt the CPU */
+ for (;;) Ke386HaltProcessor();
+ }
}
+
+ /* Call the Callbacks */
+ KiDoBugCheckCallbacks();
+
+ /* FIXME: Call Watchdog if enabled */
+
+ /* Check if we have to reboot */
+ if (Reboot)
+ {
+ /* Unload symbols */
+ DbgUnLoadImageSymbols(NULL, NtCurrentProcess(), 0);
+ HalReturnToFirmware(HalRebootRoutine);
+ }
+
+ /* Attempt to break in the debugger (otherwise halt CPU) */
+ KiBugCheckDebugBreak(DBG_STATUS_BUGCHECK_SECOND);
}
+/* PUBLIC FUNCTIONS **********************************************************/
+
/*
* @implemented
*/
-VOID STDCALL
-KeBugCheck(ULONG BugCheckCode)
+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
+ */
+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);
+}
+
/*
- * FUNCTION: Brings the system down in a controlled manner when an
- * inconsistency that might otherwise cause corruption has been detected
- * ARGUMENTS:
- * BugCheckCode = Specifies the reason for the bug check
- * RETURNS: Doesn't
+ * @implemented
*/
+VOID
+NTAPI
+KeBugCheck(ULONG BugCheckCode)
{
- KeBugCheckEx(BugCheckCode, 0, 0, 0, 0);
+ /* Call the internal API */
+ KeBugCheckWithTf(BugCheckCode, 0, 0, 0, 0, NULL);
}
/* EOF */