KD System Rewrite:
[reactos.git] / reactos / ntoskrnl / ke / bug.c
index ab79458..72ad4e3 100644 (file)
 /*
- *  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.34 2003/07/15 16:26:18 silverblade Exp $
- *
+ * COPYRIGHT:       See COPYING in the top level directory
  * PROJECT:         ReactOS kernel
  * 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
+ * 
+ * PROGRAMMERS:     Alex Ionescu - Rewrote Bugcheck Routines and implemented Reason Callbacks.
+ *                  David Welch (welch@cwcom.net)
+ *                  Phillip Susi
  */
 
 /* INCLUDES *****************************************************************/
 
-#include <roskrnl.h>
-#include <internal/kd.h>
-#include <internal/ke.h>
-#include <internal/ps.h>
-
+#include <ntoskrnl.h>
+#define NDEBUG
 #include <internal/debug.h>
 
-#include "../../hal/halx86/include/hal.h"
-
 /* GLOBALS ******************************************************************/
 
 static LIST_ENTRY BugcheckCallbackListHead = {NULL,NULL};
+static LIST_ENTRY BugcheckReasonCallbackListHead = {NULL,NULL};
 static ULONG InBugCheck;
-
-VOID PsDumpThreads(VOID);
+static PRTL_MESSAGE_RESOURCE_DATA KiBugCodeMessages;
+static ULONG KeBugCheckCount = 1;
 
 /* FUNCTIONS *****************************************************************/
 
 VOID
-KeInitializeBugCheck(VOID)
+INIT_FUNCTION
+KiInitializeBugCheck(VOID)
 {
-  InitializeListHead(&BugcheckCallbackListHead);
-  InBugCheck = 0;
+    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;
+        }
+    }
 }
 
 /*
- * @unimplemented
+ * @implemented
  */
-BOOLEAN STDCALL
+BOOLEAN 
+STDCALL
 KeDeregisterBugCheckCallback(PKBUGCHECK_CALLBACK_RECORD CallbackRecord)
 {
-  UNIMPLEMENTED;
-  return FALSE;
+    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
+BOOLEAN 
+STDCALL
 KeRegisterBugCheckCallback(PKBUGCHECK_CALLBACK_RECORD CallbackRecord,
-                          PKBUGCHECK_CALLBACK_ROUTINE  CallbackRoutine,
-                          PVOID Buffer,
-                          ULONG Length,
-                          PUCHAR Component)
+                           PKBUGCHECK_CALLBACK_ROUTINE CallbackRoutine,
+                           PVOID Buffer,
+                           ULONG Length,
+                           PUCHAR Component)
 {
-  InsertTailList(&BugcheckCallbackListHead, &CallbackRecord->Entry);
-  CallbackRecord->Length = Length;
-  CallbackRecord->Buffer = Buffer;
-  CallbackRecord->Component = Component;
-  CallbackRecord->CallbackRoutine = CallbackRoutine;
-  return(TRUE);
+    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 STDCALL
-KeBugCheckWithTf(ULONG BugCheckCode,        
-                ULONG BugCheckParameter1,
-                ULONG BugCheckParameter2,
-                ULONG BugCheckParameter3,
-                ULONG BugCheckParameter4,
-                PKTRAP_FRAME Tf)
+/*
+ * @implemented
+ */
+BOOLEAN
+STDCALL
+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(&BugcheckReasonCallbackListHead, &CallbackRecord->Entry);
+    
+        Status = TRUE;
+    }
+
+    /* Lower IRQL and return */
+    KeLowerIrql(OldIrql);
+    return Status;
+}
+
+VOID
+STDCALL
+KeGetBugMessageText(ULONG BugCheckCode, PANSI_STRING OutputString)
+{
+    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;
+            }
+        }
+    }
+}
+
+VOID
+STDCALL
+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))
-    {
-      if (Message->Flags == 0)
-       DbgPrint("  %s\n", Message->Text);
-      else
-       DbgPrint("  %S\n", (PWSTR)Message->Text);
+    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;
+        }
     }
-  else
-    {
-      DbgPrint("  No message text found!\n\n");
+}
+
+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);
 
-  if (InBugCheck == 1)
-    {
-      DbgPrint("Recursive bug check halting now\n");
-      for (;;)
-       {
-         __asm__ ("hlt\n\t");
-       }
+    /* 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);
     }
-  InBugCheck = 1;
-  KiDumpTrapFrame(Tf, BugCheckParameter1, BugCheckParameter2);
-  MmDumpToPagingFile(BugCheckCode, BugCheckParameter1, 
-                    BugCheckParameter2, BugCheckParameter3,
-                    BugCheckParameter4, Tf);
-
-  if (KdDebuggerEnabled)
-    {
-      __asm__("sti\n\t");
-      DbgBreakPoint();
-      __asm__("cli\n\t");
+
+    /* 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);
 
-  for (;;)
-    {
-      __asm__("hlt\n\t");
+        /* Wake up the Debugger */
+        if (KdDebuggerEnabled) {
+            Ke386EnableInterrupts();
+            DbgBreakPointWithStatus(DBG_STATUS_BUGCHECK_SECOND);
+            Ke386DisableInterrupts();
+        }
     }
+
+    /* Halt this CPU now */
+    for (;;) Ke386HaltProcessor();
 }
 
 /*
  * @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:
@@ -167,89 +444,34 @@ 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)
 {
-  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))
-    {
-      if (Message->Flags == 0)
-       DbgPrint("  %s\n", Message->Text);
-      else
-       DbgPrint("  %S\n", (PWSTR)Message->Text);
-    }
-  else
-    {
-      DbgPrint("  No message text found!\n\n");
-    }
-
-  if (InBugCheck == 1)
-    {
-      DbgPrint("Recursive bug check halting now\n");
-      for (;;)
-       {
-         __asm__("hlt\n\t");
-       }
-    }
-  InBugCheck = 1;
-  if (PsGetCurrentProcess() != NULL)
-    {
-      DbgPrint("Pid: %x <", PsGetCurrentProcess()->UniqueProcessId);
-      DbgPrint("%.8s> ", PsGetCurrentProcess()->ImageFileName);
-    }
-  if (PsGetCurrentThread() != NULL)
-    {
-      DbgPrint("Thrd: %x Tid: %x\n",
-              PsGetCurrentThread(),
-              PsGetCurrentThread()->Cid.UniqueThread);
-    }
-  KeDumpStackFrames((PULONG)__builtin_frame_address(0));
-  MmDumpToPagingFile(BugCheckCode, BugCheckParameter1, 
-                    BugCheckParameter2, BugCheckParameter3,
-                    BugCheckParameter4, NULL);
-
-  if (KdDebuggerEnabled)
-    {
-      __asm__("sti\n\t");
-      DbgBreakPoint();
-      __asm__("cli\n\t");
-    }
-
-  for (;;)
-    {
-      __asm__("hlt\n\t");
-    }
+    /* Call the Trap Frame version without a Trap Frame */
+    KeBugCheckWithTf(BugCheckCode, 
+                     BugCheckParameter1, 
+                     BugCheckParameter2,
+                     BugCheckParameter3, 
+                     BugCheckParameter4, 
+                     NULL);
 }
 
 /*
  * @implemented
- */
-VOID STDCALL
-KeBugCheck(ULONG BugCheckCode)
-/*
+ *
  * 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);
 }