KD System Rewrite:
[reactos.git] / reactos / ntoskrnl / ke / bug.c
index 4419076..72ad4e3 100644 (file)
  * PROJECT:         ReactOS kernel
  * FILE:            ntoskrnl/ke/bug.c
  * PURPOSE:         Graceful system shutdown if a bug is detected
- * PROGRAMMER:      David Welch (welch@cwcom.net)
- * UPDATE HISTORY:
- *                  Created 22/05/98
+ * 
+ * PROGRAMMERS:     Alex Ionescu - Rewrote Bugcheck Routines and implemented Reason Callbacks.
+ *                  David Welch (welch@cwcom.net)
+ *                  Phillip Susi
  */
 
 /* INCLUDES *****************************************************************/
 
-#include <ddk/ntddk.h>
-#include <internal/ke.h>
-
+#include <ntoskrnl.h>
+#define NDEBUG
 #include <internal/debug.h>
 
 /* GLOBALS ******************************************************************/
 
 static LIST_ENTRY BugcheckCallbackListHead = {NULL,NULL};
-
-VOID PsDumpThreads(VOID);
+static LIST_ENTRY BugcheckReasonCallbackListHead = {NULL,NULL};
+static ULONG InBugCheck;
+static PRTL_MESSAGE_RESOURCE_DATA KiBugCodeMessages;
+static ULONG KeBugCheckCount = 1;
 
 /* FUNCTIONS *****************************************************************/
 
-BOOLEAN KeDeregisterBugCheckCallback(PKBUGCHECK_CALLBACK_RECORD CallbackRecord)
+VOID
+INIT_FUNCTION
+KiInitializeBugCheck(VOID)
+{
+    PRTL_MESSAGE_RESOURCE_DATA BugCheckData;
+    LDR_RESOURCE_INFO ResourceInfo;
+    PIMAGE_RESOURCE_DATA_ENTRY ResourceDataEntry;
+    NTSTATUS Status;
+
+    /* Initialize Callbadk Listhead and State */
+    InitializeListHead(&BugcheckCallbackListHead);
+    InitializeListHead(&BugcheckReasonCallbackListHead);
+    InBugCheck = 0;
+
+    /* Cache the Bugcheck Message Strings. Prepare the Lookup Data */
+    ResourceInfo.Type = 11;
+    ResourceInfo.Name = 1;
+    ResourceInfo.Language = 9;
+    
+    /* Do the lookup. */
+    Status = LdrFindResource_U((PVOID)KERNEL_BASE,
+                               &ResourceInfo,
+                               RESOURCE_DATA_LEVEL,
+                               &ResourceDataEntry);
+    
+    /* Make sure it worked */
+    if (NT_SUCCESS(Status)) {
+        
+        DPRINT1("Found Bugcheck Resource Data!\n");
+        
+        /* Now actually get a pointer to it */
+        Status = LdrAccessResource((PVOID)KERNEL_BASE,
+                                   ResourceDataEntry,
+                                   (PVOID*)&BugCheckData,
+                                   NULL);
+        
+        /* Make sure it worked */
+        if (NT_SUCCESS(Status)) {
+
+            DPRINT1("Got Pointer to Bugcheck Resource Data!\n");
+            KiBugCodeMessages = BugCheckData;
+        }
+    }
+}
+
+/*
+ * @implemented
+ */
+BOOLEAN 
+STDCALL
+KeDeregisterBugCheckCallback(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
+STDCALL
+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 
+STDCALL
+KeRegisterBugCheckCallback(PKBUGCHECK_CALLBACK_RECORD CallbackRecord,
+                           PKBUGCHECK_CALLBACK_ROUTINE CallbackRoutine,
+                           PVOID Buffer,
+                           ULONG Length,
+                           PUCHAR Component)
 {
-   UNIMPLEMENTED;
+    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(&BugcheckCallbackListHead, &CallbackRecord->Entry);
+    
+        Status = TRUE;
+    }
+
+    /* Lower IRQL and return */
+    KeLowerIrql(OldIrql);
+    return Status;
 }
 
-VOID KeInitializeBugCheck(VOID)
+/*
+ * @implemented
+ */
+BOOLEAN
+STDCALL
+KeRegisterBugCheckReasonCallback(IN PKBUGCHECK_REASON_CALLBACK_RECORD CallbackRecord,
+                                 IN PKBUGCHECK_REASON_CALLBACK_ROUTINE CallbackRoutine,
+                                 IN KBUGCHECK_CALLBACK_REASON Reason,
+                                 IN PUCHAR Component)
 {
-   InitializeListHead(&BugcheckCallbackListHead);
+    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(&BugcheckReasonCallbackListHead, &CallbackRecord->Entry);
+    
+        Status = TRUE;
+    }
+
+    /* Lower IRQL and return */
+    KeLowerIrql(OldIrql);
+    return Status;
 }
 
-VOID KeInitializeCallbackRecord(PKBUGCHECK_CALLBACK_RECORD CallbackRecord)
+VOID
+STDCALL
+KeGetBugMessageText(ULONG BugCheckCode, PANSI_STRING OutputString)
 {
-   UNIMPLEMENTED;
+    ULONG i;
+    ULONG IdOffset;
+    ULONG_PTR MessageEntry;
+    PCHAR BugCode;
+    
+    /* Find the message. This code is based on RtlFindMesssage -- Alex */
+    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 = (ULONG_PTR)KiBugCodeMessages + KiBugCodeMessages->Blocks[i].OffsetToEntries;
+            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;
+            
+            /* Return it in the OutputString */
+            if (OutputString) {
+            
+                OutputString->Buffer = BugCode;
+                OutputString->Length = strlen(BugCode) + 1;
+                OutputString->MaximumLength = strlen(BugCode) + 1;
+            
+            } else {
+            
+                /* Direct Output to Screen */
+                DbgPrint("%s\n", BugCode);
+                break;
+            }
+        }
+    }
 }
 
-BOOLEAN KeRegisterBugCheckCallback(PKBUGCHECK_CALLBACK_RECORD CallbackRecord,
-                                  PKBUGCHECK_CALLBACK_ROUTINE CallbackRoutine,
-                                  PVOID Buffer,
-                                  ULONG Length,
-                                  PUCHAR Component)
+VOID
+STDCALL
+KiDoBugCheckCallbacks(VOID)
 {
-   InsertTailList(&BugcheckCallbackListHead,&CallbackRecord->Entry);
-   CallbackRecord->Length=Length;
-   CallbackRecord->Buffer=Buffer;
-   CallbackRecord->Component=Component;
-   CallbackRecord->CallbackRoutine=CallbackRoutine;
-   return(TRUE);
+    PKBUGCHECK_CALLBACK_RECORD CurrentRecord;
+    PLIST_ENTRY ListHead;
+    PLIST_ENTRY NextEntry;
+    
+    /* FIXME: Check Checksum and add support for WithReason Callbacks */
+    
+    /* First make sure that the list is Initialized... it might not be */
+    ListHead = &BugcheckCallbackListHead;
+    if (ListHead->Flink && ListHead->Blink) {
+    
+        /* Loop the list */
+        NextEntry = ListHead->Flink;
+        while (NextEntry != ListHead) {
+        
+            /* Get the Callback Record */
+            CurrentRecord = CONTAINING_RECORD(NextEntry, 
+                                              KBUGCHECK_CALLBACK_RECORD,
+                                              Entry);
+            
+            /* Make sure it's inserted */
+            if (CurrentRecord->State == BufferInserted) {
+            
+                /* Call the routine */
+                CurrentRecord->State = BufferStarted;
+                (CurrentRecord->CallbackRoutine)(CurrentRecord->Buffer, 
+                                                 CurrentRecord->Length);
+                CurrentRecord->State = BufferFinished;
+            }
+            
+            /* Move to next Entry */
+            NextEntry = NextEntry->Flink;
+        }
+    }
+}
+
+VOID 
+STDCALL
+KeBugCheckWithTf(ULONG BugCheckCode,
+                 ULONG BugCheckParameter1,
+                 ULONG BugCheckParameter2,
+                 ULONG BugCheckParameter3,
+                 ULONG BugCheckParameter4,
+                 PKTRAP_FRAME Tf)
+{
+    KIRQL OldIrql;
+    BOOLEAN GotExtendedCrashInfo = FALSE;
+    PVOID Address = 0;
+    PLIST_ENTRY CurrentEntry;
+    MODULE_TEXT_SECTION* CurrentSection = NULL;
+    extern LIST_ENTRY ModuleTextListHead;
+     
+    /* Make sure we're switching back to the blue screen and print messages on it */
+    HalReleaseDisplayOwnership();
+    if (KdpDebugMode.Gdb) KdpDebugMode.Screen = TRUE;
+    /* Try to find out who did this. For this, we need a Trap Frame.
+     * Note: Some special BSODs pass the Frame/EIP as a Param. MSDN has the
+     * info so it eventually needs to be supported. 
+     */
+    if (Tf) {
+        
+        /* For now, get Address from EIP */
+        Address = (PVOID)Tf->Eip;
+        
+        /* Try to get information on the module */
+        CurrentEntry = ModuleTextListHead.Flink;
+        while (CurrentEntry != &ModuleTextListHead && CurrentEntry != NULL) {
+            
+            /* Get the current Section */
+            CurrentSection = CONTAINING_RECORD(CurrentEntry, 
+                                               MODULE_TEXT_SECTION, 
+                                               ListEntry);
+
+            /* Check if this is the right one */
+            if ((Address != NULL && (Address >= (PVOID)CurrentSection->Base &&
+                 Address < (PVOID)(CurrentSection->Base + CurrentSection->Length)))) {
+
+                /* We got it */            
+                GotExtendedCrashInfo = TRUE;
+                break;
+            }
+            
+            /* Loop again */
+            CurrentEntry = CurrentEntry->Flink;
+        }
+    }
+       
+    /* Raise IRQL to HIGH_LEVEL */
+    Ke386DisableInterrupts();
+    KeRaiseIrql(HIGH_LEVEL, &OldIrql);
+
+    /* Unload the Kernel Adress Space if we own it */
+    if (MmGetKernelAddressSpace()->Lock.Owner == KeGetCurrentThread())
+        MmUnlockAddressSpace(MmGetKernelAddressSpace());
+     
+    /* FIXMEs: Use inbv to clear, fill and write to screen. */
+    
+    /* Show the STOP Message */
+    DbgPrint("A problem has been detected and ReactOS has been shut down to prevent "
+             "damage to your computer.\n\n");
+    /* Show the module name of who caused this */
+    if (GotExtendedCrashInfo) {
+    
+        DbgPrint("The problem seems to be caused by the following file: %S\n\n", CurrentSection->Name);
+    }
+
+    /* Find the Bug Code String */
+    KeGetBugMessageText(BugCheckCode, NULL);
+
+    /* Show the techincal Data */
+    DbgPrint("Technical information:\n\n*** STOP: 0x%08lX (0x%p,0x%p,0x%p,0x%p)\n\n",
+             BugCheckCode,
+             BugCheckParameter1,
+             BugCheckParameter2,
+             BugCheckParameter3,
+             BugCheckParameter4);
+
+    /* Show the module name and more data of who caused this */
+    if (GotExtendedCrashInfo) {
+    
+        DbgPrint("***    %S - Address 0x%p base at 0x%p, DateStamp 0x%x\n\n", 
+                 CurrentSection->Name,
+                 Address,
+                 CurrentSection->Base,
+                 0);
+    }
+    
+    /* There can only be one Bugcheck per Bootup */
+    if (!InterlockedDecrement(&KeBugCheckCount)) {
+
+#ifdef CONFIG_SMP
+        ULONG i;
+        /* Freeze the other CPUs */
+        for (i = 0; i < KeNumberProcessors; i++) {    
+            if (i != KeGetCurrentProcessorNumber()) {
+                
+                /* Send the IPI and give them one second to catch up */
+                KiIpiSendRequest(1 << i, IPI_REQUEST_FREEZE);
+                KeStallExecutionProcessor(1000000);
+            }
+        }
+#endif
+
+        /* Check if we got a Trap Frame */
+        if (Tf) {
+        
+            /* Dump it */
+            KiDumpTrapFrame(Tf, BugCheckParameter1, BugCheckParameter2);
+    
+        } else {
+        
+            /* We can only dump the frames */
+#if defined(__GNUC__)
+            KeDumpStackFrames((PULONG)__builtin_frame_address(0));
+#elif defined(_MSC_VER)
+            __asm push ebp
+            __asm call KeDumpStackFrames
+            __asm add esp, 4
+#else
+#error Unknown compiler for inline assembler
+#endif
+        }
+        
+        /* Call the Callbacks */;
+        KiDoBugCheckCallbacks();
+
+        /* Dump the BSOD to the Paging File */
+        MmDumpToPagingFile(BugCheckCode, 
+                           BugCheckParameter1, 
+                           BugCheckParameter2, 
+                           BugCheckParameter3,
+                           BugCheckParameter4, 
+                           Tf);
+
+        /* Wake up the Debugger */
+        if (KdDebuggerEnabled) {
+            Ke386EnableInterrupts();
+            DbgBreakPointWithStatus(DBG_STATUS_BUGCHECK_SECOND);
+            Ke386DisableInterrupts();
+        }
+    }
+
+    /* Halt this CPU now */
+    for (;;) Ke386HaltProcessor();
 }
 
-VOID KeBugCheckEx(ULONG BugCheckCode,
-                 ULONG BugCheckParameter1,
-                 ULONG BugCheckParameter2,
-                 ULONG BugCheckParameter3,
-                 ULONG BugCheckParameter4)
 /*
+ * @implemented
+ *
  * FUNCTION: Brings the system down in a controlled manner when an 
  * inconsistency that might otherwise cause corruption has been detected
  * ARGUMENTS:
@@ -65,25 +444,36 @@ VOID KeBugCheckEx(ULONG BugCheckCode,
  *           BugCheckParameter[1-4] = Additional information about bug
  * RETURNS: Doesn't
  */
+VOID 
+STDCALL
+KeBugCheckEx(ULONG BugCheckCode,
+             ULONG BugCheckParameter1,
+             ULONG BugCheckParameter2,
+             ULONG BugCheckParameter3,
+             ULONG BugCheckParameter4)
 {
-   DbgPrint("Bug detected (code %x param %x %x %x %x)\n",BugCheckCode,
-         BugCheckParameter1,BugCheckParameter2,BugCheckParameter3,
-         BugCheckParameter4);
-   PsDumpThreads();
-   KeDumpStackFrames(0,64);
-   __asm__("cli\n\t");
-   for(;;);
+    /* Call the Trap Frame version without a Trap Frame */
+    KeBugCheckWithTf(BugCheckCode, 
+                     BugCheckParameter1, 
+                     BugCheckParameter2,
+                     BugCheckParameter3, 
+                     BugCheckParameter4, 
+                     NULL);
 }
 
-VOID KeBugCheck(ULONG BugCheckCode)
 /*
+ * @implemented
+ *
  * 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
  */
+VOID STDCALL
+KeBugCheck(ULONG BugCheckCode)
 {
-   KeBugCheckEx(BugCheckCode,0,0,0,0);
+  KeBugCheckEx(BugCheckCode, 0, 0, 0, 0);
 }
 
+/* EOF */