xref: /reactos/ntoskrnl/ke/bug.c (revision bbccad0e)
1 /*
2  * PROJECT:         ReactOS Kernel
3  * LICENSE:         GPL - See COPYING in the top level directory
4  * FILE:            ntoskrnl/ke/bug.c
5  * PURPOSE:         Bugcheck Support
6  * PROGRAMMERS:     Alex Ionescu (alex.ionescu@reactos.org)
7  */
8 
9 /* INCLUDES ******************************************************************/
10 
11 #include <ntoskrnl.h>
12 
13 #define NDEBUG
14 #include <debug.h>
15 
16 /* GLOBALS *******************************************************************/
17 
18 LIST_ENTRY KeBugcheckCallbackListHead;
19 LIST_ENTRY KeBugcheckReasonCallbackListHead;
20 KSPIN_LOCK BugCheckCallbackLock;
21 ULONG KeBugCheckActive, KeBugCheckOwner;
22 LONG KeBugCheckOwnerRecursionCount;
23 PMESSAGE_RESOURCE_DATA KiBugCodeMessages;
24 ULONG KeBugCheckCount = 1;
25 ULONG KiHardwareTrigger;
26 PUNICODE_STRING KiBugCheckDriver;
27 ULONG_PTR KiBugCheckData[5];
28 
29 PKNMI_HANDLER_CALLBACK KiNmiCallbackListHead = NULL;
30 KSPIN_LOCK KiNmiCallbackListLock;
31 
32 /* Bugzilla Reporting */
33 UNICODE_STRING KeRosProcessorName, KeRosBiosDate, KeRosBiosVersion;
34 UNICODE_STRING KeRosVideoBiosDate, KeRosVideoBiosVersion;
35 
36 /* PRIVATE FUNCTIONS *********************************************************/
37 
38 PVOID
39 NTAPI
40 KiPcToFileHeader(IN PVOID Pc,
41                  OUT PLDR_DATA_TABLE_ENTRY *LdrEntry,
42                  IN BOOLEAN DriversOnly,
43                  OUT PBOOLEAN InKernel)
44 {
45     ULONG i = 0;
46     PVOID ImageBase, PcBase = NULL;
47     PLDR_DATA_TABLE_ENTRY Entry;
48     PLIST_ENTRY ListHead, NextEntry;
49 
50     /* Check which list we should use */
51     ListHead = (KeLoaderBlock) ? &KeLoaderBlock->LoadOrderListHead :
52                                  &PsLoadedModuleList;
53 
54     /* Assume no */
55     *InKernel = FALSE;
56 
57     /* Set list pointers and make sure it's valid */
58     NextEntry = ListHead->Flink;
59     if (NextEntry)
60     {
61         /* Start loop */
62         while (NextEntry != ListHead)
63         {
64             /* Increase entry */
65             i++;
66 
67             /* Check if this is a kernel entry and we only want drivers */
68             if ((i <= 2) && (DriversOnly != FALSE))
69             {
70                 /* Skip it */
71                 NextEntry = NextEntry->Flink;
72                 continue;
73             }
74 
75             /* Get the loader entry */
76             Entry = CONTAINING_RECORD(NextEntry,
77                                       LDR_DATA_TABLE_ENTRY,
78                                       InLoadOrderLinks);
79 
80             /* Move to the next entry */
81             NextEntry = NextEntry->Flink;
82             ImageBase = Entry->DllBase;
83 
84             /* Check if this is the right one */
85             if (((ULONG_PTR)Pc >= (ULONG_PTR)Entry->DllBase) &&
86                 ((ULONG_PTR)Pc < ((ULONG_PTR)Entry->DllBase + Entry->SizeOfImage)))
87             {
88                 /* Return this entry */
89                 *LdrEntry = Entry;
90                 PcBase = ImageBase;
91 
92                 /* Check if this was a kernel or HAL entry */
93                 if (i <= 2) *InKernel = TRUE;
94                 break;
95             }
96         }
97     }
98 
99     /* Return the base address */
100     return PcBase;
101 }
102 
103 PVOID
104 NTAPI
105 KiRosPcToUserFileHeader(IN PVOID Pc,
106                         OUT PLDR_DATA_TABLE_ENTRY *LdrEntry)
107 {
108     PVOID ImageBase, PcBase = NULL;
109     PLDR_DATA_TABLE_ENTRY Entry;
110     PLIST_ENTRY ListHead, NextEntry;
111 
112     /*
113      * We know this is valid because we should only be called after a
114      * succesfull address from RtlWalkFrameChain for UserMode, which
115      * validates everything for us.
116      */
117     ListHead = &KeGetCurrentThread()->
118                Teb->ProcessEnvironmentBlock->Ldr->InLoadOrderModuleList;
119 
120     /* Set list pointers and make sure it's valid */
121     NextEntry = ListHead->Flink;
122     if (NextEntry)
123     {
124         /* Start loop */
125         while (NextEntry != ListHead)
126         {
127             /* Get the loader entry */
128             Entry = CONTAINING_RECORD(NextEntry,
129                                       LDR_DATA_TABLE_ENTRY,
130                                       InLoadOrderLinks);
131 
132             /* Move to the next entry */
133             NextEntry = NextEntry->Flink;
134             ImageBase = Entry->DllBase;
135 
136             /* Check if this is the right one */
137             if (((ULONG_PTR)Pc >= (ULONG_PTR)Entry->DllBase) &&
138                 ((ULONG_PTR)Pc < ((ULONG_PTR)Entry->DllBase + Entry->SizeOfImage)))
139             {
140                 /* Return this entry */
141                 *LdrEntry = Entry;
142                 PcBase = ImageBase;
143                 break;
144             }
145         }
146     }
147 
148     /* Return the base address */
149     return PcBase;
150 }
151 
152 USHORT
153 NTAPI
154 KeRosCaptureUserStackBackTrace(IN ULONG FramesToSkip,
155                                IN ULONG FramesToCapture,
156                                OUT PVOID *BackTrace,
157                                OUT PULONG BackTraceHash OPTIONAL)
158 {
159     PVOID Frames[2 * 64];
160     ULONG FrameCount;
161     ULONG Hash = 0, i;
162 
163     /* Skip a frame for the caller */
164     FramesToSkip++;
165 
166     /* Don't go past the limit */
167     if ((FramesToCapture + FramesToSkip) >= 128) return 0;
168 
169     /* Do the back trace */
170     FrameCount = RtlWalkFrameChain(Frames, FramesToCapture + FramesToSkip, 1);
171 
172     /* Make sure we're not skipping all of them */
173     if (FrameCount <= FramesToSkip) return 0;
174 
175     /* Loop all the frames */
176     for (i = 0; i < FramesToCapture; i++)
177     {
178         /* Don't go past the limit */
179         if ((FramesToSkip + i) >= FrameCount) break;
180 
181         /* Save this entry and hash it */
182         BackTrace[i] = Frames[FramesToSkip + i];
183         Hash += PtrToUlong(BackTrace[i]);
184     }
185 
186     /* Write the hash */
187     if (BackTraceHash) *BackTraceHash = Hash;
188 
189     /* Clear the other entries and return count */
190     RtlFillMemoryUlong(Frames, 128, 0);
191     return (USHORT)i;
192 }
193 
194 
195 VOID
196 FASTCALL
197 KeRosDumpStackFrameArray(IN PULONG_PTR Frames,
198                          IN ULONG FrameCount)
199 {
200     ULONG i;
201     ULONG_PTR Addr;
202     BOOLEAN InSystem;
203     PVOID p;
204 
205     /* GCC complaints that it may be used uninitialized */
206     PLDR_DATA_TABLE_ENTRY LdrEntry = NULL;
207 
208     /* Loop them */
209     for (i = 0; i < FrameCount; i++)
210     {
211         /* Get the EIP */
212         Addr = Frames[i];
213         if (!Addr)
214         {
215         	break;
216         }
217 
218         /* Get the base for this file */
219         if (Addr > (ULONG_PTR)MmHighestUserAddress)
220         {
221             /* We are in kernel */
222             p = KiPcToFileHeader((PVOID)Addr, &LdrEntry, FALSE, &InSystem);
223         }
224         else
225         {
226             /* We are in user land */
227             p = KiRosPcToUserFileHeader((PVOID)Addr, &LdrEntry);
228         }
229         if (p)
230         {
231 #ifdef KDBG
232             if (!KdbSymPrintAddress((PVOID)Addr, NULL))
233 #endif
234             {
235                 CHAR AnsiName[64];
236 
237                 /* Convert module name to ANSI and print it */
238                 KeBugCheckUnicodeToAnsi(&LdrEntry->BaseDllName,
239                                         AnsiName,
240                                         sizeof(AnsiName));
241                 Addr -= (ULONG_PTR)LdrEntry->DllBase;
242                 DbgPrint("<%s: %p>", AnsiName, (PVOID)Addr);
243             }
244         }
245         else
246         {
247             /* Print only the address */
248             DbgPrint("<%p>", (PVOID)Addr);
249         }
250 
251         /* Go to the next frame */
252         DbgPrint("\n");
253     }
254 }
255 
256 VOID
257 NTAPI
258 KeRosDumpStackFrames(IN PULONG_PTR Frame OPTIONAL,
259                      IN ULONG FrameCount OPTIONAL)
260 {
261     ULONG_PTR Frames[32];
262     ULONG RealFrameCount;
263 
264     /* If the caller didn't ask, assume 32 frames */
265     if (!FrameCount || FrameCount > 32) FrameCount = 32;
266 
267     if (Frame)
268     {
269         /* Dump them */
270         KeRosDumpStackFrameArray(Frame, FrameCount);
271     }
272     else
273     {
274         /* Get the current frames (skip the two. One for the dumper, one for the caller) */
275         RealFrameCount = RtlCaptureStackBackTrace(2, FrameCount, (PVOID*)Frames, NULL);
276         DPRINT1("RealFrameCount =%lu\n", RealFrameCount);
277 
278         /* Dump them */
279         KeRosDumpStackFrameArray(Frames, RealFrameCount);
280 
281         /* Count left for user mode? */
282         if (FrameCount - RealFrameCount > 0)
283         {
284             /* Get the current frames */
285             RealFrameCount = KeRosCaptureUserStackBackTrace(-1, FrameCount - RealFrameCount, (PVOID*)Frames, NULL);
286 
287             /* Dump them */
288             KeRosDumpStackFrameArray(Frames, RealFrameCount);
289         }
290     }
291 }
292 
293 CODE_SEG("INIT")
294 VOID
295 NTAPI
296 KiInitializeBugCheck(VOID)
297 {
298     PMESSAGE_RESOURCE_DATA BugCheckData;
299     LDR_RESOURCE_INFO ResourceInfo;
300     PIMAGE_RESOURCE_DATA_ENTRY ResourceDataEntry;
301     NTSTATUS Status;
302     PLDR_DATA_TABLE_ENTRY LdrEntry;
303 
304     /* Get the kernel entry */
305     LdrEntry = CONTAINING_RECORD(KeLoaderBlock->LoadOrderListHead.Flink,
306                                  LDR_DATA_TABLE_ENTRY,
307                                  InLoadOrderLinks);
308 
309     /* Cache the Bugcheck Message Strings. Prepare the Lookup Data */
310     ResourceInfo.Type = 11;
311     ResourceInfo.Name = 1;
312     ResourceInfo.Language = 9;
313 
314     /* Do the lookup. */
315     Status = LdrFindResource_U(LdrEntry->DllBase,
316                                &ResourceInfo,
317                                RESOURCE_DATA_LEVEL,
318                                &ResourceDataEntry);
319 
320     /* Make sure it worked */
321     if (NT_SUCCESS(Status))
322     {
323         /* Now actually get a pointer to it */
324         Status = LdrAccessResource(LdrEntry->DllBase,
325                                    ResourceDataEntry,
326                                    (PVOID*)&BugCheckData,
327                                    NULL);
328         if (NT_SUCCESS(Status)) KiBugCodeMessages = BugCheckData;
329     }
330 }
331 
332 BOOLEAN
333 NTAPI
334 KeGetBugMessageText(IN ULONG BugCheckCode,
335                     OUT PANSI_STRING OutputString OPTIONAL)
336 {
337     ULONG i;
338     ULONG IdOffset;
339     PMESSAGE_RESOURCE_ENTRY MessageEntry;
340     PCHAR BugCode;
341     USHORT Length;
342     BOOLEAN Result = FALSE;
343 
344     /* Make sure we're not bugchecking too early */
345     if (!KiBugCodeMessages) return Result;
346 
347     /*
348      * Globally protect in SEH as we are trying to access data in
349      * dire situations, and potentially going to patch it (see below).
350      */
351     _SEH2_TRY
352     {
353 
354     /*
355      * Make the kernel resource section writable, as we are going to manually
356      * trim the trailing newlines in the bugcheck resource message in place,
357      * when OutputString is NULL and before displaying it on screen.
358      */
359     MmMakeKernelResourceSectionWritable();
360 
361     /* Find the message. This code is based on RtlFindMesssage */
362     for (i = 0; i < KiBugCodeMessages->NumberOfBlocks; i++)
363     {
364         /* Check if the ID matches */
365         if ((BugCheckCode >= KiBugCodeMessages->Blocks[i].LowId) &&
366             (BugCheckCode <= KiBugCodeMessages->Blocks[i].HighId))
367         {
368             /* Get offset to entry */
369             MessageEntry = (PMESSAGE_RESOURCE_ENTRY)
370                 ((ULONG_PTR)KiBugCodeMessages + KiBugCodeMessages->Blocks[i].OffsetToEntries);
371             IdOffset = BugCheckCode - KiBugCodeMessages->Blocks[i].LowId;
372 
373             /* Advance in the entries until finding it */
374             while (IdOffset--)
375             {
376                 MessageEntry = (PMESSAGE_RESOURCE_ENTRY)
377                     ((ULONG_PTR)MessageEntry + MessageEntry->Length);
378             }
379 
380             /* Make sure it's not Unicode */
381             ASSERT(!(MessageEntry->Flags & MESSAGE_RESOURCE_UNICODE));
382 
383             /* Get the final code */
384             BugCode = (PCHAR)MessageEntry->Text;
385             Length = (USHORT)strlen(BugCode);
386 
387             /* Handle trailing newlines */
388             while ((Length > 0) && ((BugCode[Length - 1] == '\n') ||
389                                     (BugCode[Length - 1] == '\r') ||
390                                     (BugCode[Length - 1] == ANSI_NULL)))
391             {
392                 /* Directly trim the newline in place if we don't return the string */
393                 if (!OutputString) BugCode[Length - 1] = ANSI_NULL;
394 
395                 /* Skip the trailing newline */
396                 Length--;
397             }
398 
399             /* Check if caller wants an output string */
400             if (OutputString)
401             {
402                 /* Return it in the OutputString */
403                 OutputString->Buffer = BugCode;
404                 OutputString->Length = Length;
405                 OutputString->MaximumLength = Length;
406             }
407             else
408             {
409                 /* Direct output to screen */
410                 InbvDisplayString(BugCode);
411                 InbvDisplayString("\r");
412             }
413 
414             /* We're done */
415             Result = TRUE;
416             break;
417         }
418     }
419 
420     }
421     _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
422     {
423     }
424     _SEH2_END;
425 
426     /* Return the result */
427     return Result;
428 }
429 
430 VOID
431 NTAPI
432 KiDoBugCheckCallbacks(VOID)
433 {
434     PKBUGCHECK_CALLBACK_RECORD CurrentRecord;
435     PLIST_ENTRY ListHead, NextEntry, LastEntry;
436     ULONG_PTR Checksum;
437 
438     /* First make sure that the list is initialized... it might not be */
439     ListHead = &KeBugcheckCallbackListHead;
440     if ((!ListHead->Flink) || (!ListHead->Blink))
441         return;
442 
443     /* Loop the list */
444     LastEntry = ListHead;
445     NextEntry = ListHead->Flink;
446     while (NextEntry != ListHead)
447     {
448         /* Get the reord */
449         CurrentRecord = CONTAINING_RECORD(NextEntry,
450                                           KBUGCHECK_CALLBACK_RECORD,
451                                           Entry);
452 
453         /* Validate it */
454         // TODO/FIXME: Check whether the memory CurrentRecord points to
455         // is still accessible and valid!
456         if (CurrentRecord->Entry.Blink != LastEntry) return;
457         Checksum = (ULONG_PTR)CurrentRecord->CallbackRoutine;
458         Checksum += (ULONG_PTR)CurrentRecord->Buffer;
459         Checksum += (ULONG_PTR)CurrentRecord->Length;
460         Checksum += (ULONG_PTR)CurrentRecord->Component;
461 
462         /* Make sure it's inserted and validated */
463         if ((CurrentRecord->State == BufferInserted) &&
464             (CurrentRecord->Checksum == Checksum))
465         {
466             /* Call the routine */
467             CurrentRecord->State = BufferStarted;
468             _SEH2_TRY
469             {
470                 (CurrentRecord->CallbackRoutine)(CurrentRecord->Buffer,
471                                                  CurrentRecord->Length);
472                 CurrentRecord->State = BufferFinished;
473             }
474             _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
475             {
476                 CurrentRecord->State = BufferIncomplete;
477             }
478             _SEH2_END;
479         }
480 
481         /* Go to the next entry */
482         LastEntry = NextEntry;
483         NextEntry = NextEntry->Flink;
484     }
485 }
486 
487 VOID
488 NTAPI
489 KiBugCheckDebugBreak(IN ULONG StatusCode)
490 {
491     /*
492      * Wrap this in SEH so we don't crash if
493      * there is no debugger or if it disconnected
494      */
495 DoBreak:
496     _SEH2_TRY
497     {
498         /* Breakpoint */
499         DbgBreakPointWithStatus(StatusCode);
500     }
501     _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
502     {
503         /* No debugger, halt the CPU */
504         HalHaltSystem();
505     }
506     _SEH2_END;
507 
508     /* Break again if this wasn't first try */
509     if (StatusCode != DBG_STATUS_BUGCHECK_FIRST) goto DoBreak;
510 }
511 
512 PCHAR
513 NTAPI
514 KeBugCheckUnicodeToAnsi(IN PUNICODE_STRING Unicode,
515                         OUT PCHAR Ansi,
516                         IN ULONG Length)
517 {
518     PCHAR p;
519     PWCHAR pw;
520     ULONG i;
521 
522     /* Set length and normalize it */
523     i = Unicode->Length / sizeof(WCHAR);
524     i = min(i, Length - 1);
525 
526     /* Set source and destination, and copy */
527     pw = Unicode->Buffer;
528     p = Ansi;
529     while (i--) *p++ = (CHAR)*pw++;
530 
531     /* Null terminate and return */
532     *p = ANSI_NULL;
533     return Ansi;
534 }
535 
536 VOID
537 NTAPI
538 KiDumpParameterImages(IN PCHAR Message,
539                       IN PULONG_PTR Parameters,
540                       IN ULONG ParameterCount,
541                       IN PKE_BUGCHECK_UNICODE_TO_ANSI ConversionRoutine)
542 {
543     ULONG i;
544     BOOLEAN InSystem;
545     PLDR_DATA_TABLE_ENTRY LdrEntry;
546     PVOID ImageBase;
547     PUNICODE_STRING DriverName;
548     CHAR AnsiName[32];
549     PIMAGE_NT_HEADERS NtHeader;
550     ULONG TimeStamp;
551     BOOLEAN FirstRun = TRUE;
552 
553     /* Loop parameters */
554     for (i = 0; i < ParameterCount; i++)
555     {
556         /* Get the base for this parameter */
557         ImageBase = KiPcToFileHeader((PVOID)Parameters[i],
558                                      &LdrEntry,
559                                      FALSE,
560                                      &InSystem);
561         if (!ImageBase)
562         {
563             /* FIXME: Add code to check for unloaded drivers */
564             DPRINT1("Potentially unloaded driver!\n");
565             continue;
566         }
567         else
568         {
569             /* Get the NT Headers and Timestamp */
570             NtHeader = RtlImageNtHeader(LdrEntry->DllBase);
571             TimeStamp = NtHeader->FileHeader.TimeDateStamp;
572 
573             /* Convert the driver name */
574             DriverName = &LdrEntry->BaseDllName;
575             ConversionRoutine(&LdrEntry->BaseDllName,
576                               AnsiName,
577                               sizeof(AnsiName));
578         }
579 
580         /* Format driver name */
581         sprintf(Message,
582                 "%s**  %12s - Address %p base at %p, DateStamp %08lx\r\n",
583                 FirstRun ? "\r\n*":"*",
584                 AnsiName,
585                 (PVOID)Parameters[i],
586                 ImageBase,
587                 TimeStamp);
588 
589         /* Check if we only had one parameter */
590         if (ParameterCount <= 1)
591         {
592             /* Then just save the name */
593             KiBugCheckDriver = DriverName;
594         }
595         else
596         {
597             /* Otherwise, display the message */
598             InbvDisplayString(Message);
599         }
600 
601         /* Loop again */
602         FirstRun = FALSE;
603     }
604 }
605 
606 VOID
607 NTAPI
608 KiDisplayBlueScreen(IN ULONG MessageId,
609                     IN BOOLEAN IsHardError,
610                     IN PCHAR HardErrCaption OPTIONAL,
611                     IN PCHAR HardErrMessage OPTIONAL,
612                     IN PCHAR Message)
613 {
614     CHAR AnsiName[107];
615 
616     /* Check if bootvid is installed */
617     if (InbvIsBootDriverInstalled())
618     {
619         /* Acquire ownership and reset the display */
620         InbvAcquireDisplayOwnership();
621         InbvResetDisplay();
622 
623         /* Display blue screen */
624         InbvSolidColorFill(0, 0, SCREEN_WIDTH - 1, SCREEN_HEIGHT - 1, BV_COLOR_BLUE);
625         InbvSetTextColor(BV_COLOR_WHITE);
626         InbvInstallDisplayStringFilter(NULL);
627         InbvEnableDisplayString(TRUE);
628         InbvSetScrollRegion(0, 0, SCREEN_WIDTH - 1, SCREEN_HEIGHT - 1);
629     }
630 
631     /* Check if this is a hard error */
632     if (IsHardError)
633     {
634         /* Display caption and message */
635         if (HardErrCaption) InbvDisplayString(HardErrCaption);
636         if (HardErrMessage) InbvDisplayString(HardErrMessage);
637     }
638 
639     /* Begin the display */
640     InbvDisplayString("\r\n");
641 
642     /* Print out initial message */
643     KeGetBugMessageText(BUGCHECK_MESSAGE_INTRO, NULL);
644     InbvDisplayString("\r\n\r\n");
645 
646     /* Check if we have a driver */
647     if (KiBugCheckDriver)
648     {
649         /* Print out into to driver name */
650         KeGetBugMessageText(BUGCODE_ID_DRIVER, NULL);
651 
652         /* Convert and print out driver name */
653         KeBugCheckUnicodeToAnsi(KiBugCheckDriver, AnsiName, sizeof(AnsiName));
654         InbvDisplayString(" ");
655         InbvDisplayString(AnsiName);
656         InbvDisplayString("\r\n\r\n");
657     }
658 
659     /* Check if this is the generic message */
660     if (MessageId == BUGCODE_PSS_MESSAGE)
661     {
662         /* It is, so get the bug code string as well */
663         KeGetBugMessageText((ULONG)KiBugCheckData[0], NULL);
664         InbvDisplayString("\r\n\r\n");
665     }
666 
667     /* Print second introduction message */
668     KeGetBugMessageText(PSS_MESSAGE_INTRO, NULL);
669     InbvDisplayString("\r\n\r\n");
670 
671     /* Get the bug code string */
672     KeGetBugMessageText(MessageId, NULL);
673     InbvDisplayString("\r\n\r\n");
674 
675     /* Print message for technical information */
676     KeGetBugMessageText(BUGCHECK_TECH_INFO, NULL);
677 
678     /* Show the technical Data */
679     RtlStringCbPrintfA(AnsiName,
680                        sizeof(AnsiName),
681                        "\r\n\r\n*** STOP: 0x%08lX (0x%p,0x%p,0x%p,0x%p)\r\n\r\n",
682                        (ULONG)KiBugCheckData[0],
683                        (PVOID)KiBugCheckData[1],
684                        (PVOID)KiBugCheckData[2],
685                        (PVOID)KiBugCheckData[3],
686                        (PVOID)KiBugCheckData[4]);
687     InbvDisplayString(AnsiName);
688 
689     /* Check if we have a driver*/
690     if (KiBugCheckDriver)
691     {
692         /* Display technical driver data */
693         InbvDisplayString(Message);
694     }
695     else
696     {
697         /* Dump parameter information */
698         KiDumpParameterImages(Message,
699                               (PVOID)&KiBugCheckData[1],
700                               4,
701                               KeBugCheckUnicodeToAnsi);
702     }
703 }
704 
705 DECLSPEC_NORETURN
706 VOID
707 NTAPI
708 KeBugCheckWithTf(IN ULONG BugCheckCode,
709                  IN ULONG_PTR BugCheckParameter1,
710                  IN ULONG_PTR BugCheckParameter2,
711                  IN ULONG_PTR BugCheckParameter3,
712                  IN ULONG_PTR BugCheckParameter4,
713                  IN PKTRAP_FRAME TrapFrame)
714 {
715     PKPRCB Prcb = KeGetCurrentPrcb();
716     CONTEXT Context;
717     ULONG MessageId;
718     CHAR AnsiName[128];
719     BOOLEAN IsSystem, IsHardError = FALSE, Reboot = FALSE;
720     PCHAR HardErrCaption = NULL, HardErrMessage = NULL;
721     PVOID Pc = NULL, Memory;
722     PVOID DriverBase;
723     PLDR_DATA_TABLE_ENTRY LdrEntry;
724     PULONG_PTR HardErrorParameters;
725     KIRQL OldIrql;
726 #ifdef CONFIG_SMP
727     LONG i = 0;
728 #endif
729 
730     /* Set active bugcheck */
731     KeBugCheckActive = TRUE;
732     KiBugCheckDriver = NULL;
733 
734     /* Check if this is power failure simulation */
735     if (BugCheckCode == POWER_FAILURE_SIMULATE)
736     {
737         /* Call the Callbacks and reboot */
738         KiDoBugCheckCallbacks();
739         HalReturnToFirmware(HalRebootRoutine);
740     }
741 
742     /* Save the IRQL and set hardware trigger */
743     Prcb->DebuggerSavedIRQL = KeGetCurrentIrql();
744     InterlockedIncrement((PLONG)&KiHardwareTrigger);
745 
746     /* Capture the CPU Context */
747     RtlCaptureContext(&Prcb->ProcessorState.ContextFrame);
748     KiSaveProcessorControlState(&Prcb->ProcessorState);
749     Context = Prcb->ProcessorState.ContextFrame;
750 
751     /* FIXME: Call the Watchdog if it's registered */
752 
753     /* Check which bugcode this is */
754     switch (BugCheckCode)
755     {
756         /* These bug checks already have detailed messages, keep them */
757         case UNEXPECTED_KERNEL_MODE_TRAP:
758         case DRIVER_CORRUPTED_EXPOOL:
759         case ACPI_BIOS_ERROR:
760         case ACPI_BIOS_FATAL_ERROR:
761         case THREAD_STUCK_IN_DEVICE_DRIVER:
762         case DATA_BUS_ERROR:
763         case FAT_FILE_SYSTEM:
764         case NO_MORE_SYSTEM_PTES:
765         case INACCESSIBLE_BOOT_DEVICE:
766 
767             /* Keep the same code */
768             MessageId = BugCheckCode;
769             break;
770 
771         /* Check if this is a kernel-mode exception */
772         case KERNEL_MODE_EXCEPTION_NOT_HANDLED:
773         case SYSTEM_THREAD_EXCEPTION_NOT_HANDLED:
774         case KMODE_EXCEPTION_NOT_HANDLED:
775 
776             /* Use the generic text message */
777             MessageId = KMODE_EXCEPTION_NOT_HANDLED;
778             break;
779 
780         /* File-system errors */
781         case NTFS_FILE_SYSTEM:
782 
783             /* Use the generic message for FAT */
784             MessageId = FAT_FILE_SYSTEM;
785             break;
786 
787         /* Check if this is a coruption of the Mm's Pool */
788         case DRIVER_CORRUPTED_MMPOOL:
789 
790             /* Use generic corruption message */
791             MessageId = DRIVER_CORRUPTED_EXPOOL;
792             break;
793 
794         /* Check if this is a signature check failure */
795         case STATUS_SYSTEM_IMAGE_BAD_SIGNATURE:
796 
797             /* Use the generic corruption message */
798             MessageId = BUGCODE_PSS_MESSAGE_SIGNATURE;
799             break;
800 
801         /* All other codes */
802         default:
803 
804             /* Use the default bugcheck message */
805             MessageId = BUGCODE_PSS_MESSAGE;
806             break;
807     }
808 
809     /* Save bugcheck data */
810     KiBugCheckData[0] = BugCheckCode;
811     KiBugCheckData[1] = BugCheckParameter1;
812     KiBugCheckData[2] = BugCheckParameter2;
813     KiBugCheckData[3] = BugCheckParameter3;
814     KiBugCheckData[4] = BugCheckParameter4;
815 
816     /* Now check what bugcheck this is */
817     switch (BugCheckCode)
818     {
819         /* Invalid access to R/O memory or Unhandled KM Exception */
820         case KERNEL_MODE_EXCEPTION_NOT_HANDLED:
821         case ATTEMPTED_WRITE_TO_READONLY_MEMORY:
822         case ATTEMPTED_EXECUTE_OF_NOEXECUTE_MEMORY:
823         {
824             /* Check if we have a trap frame */
825             if (!TrapFrame)
826             {
827                 /* Use parameter 3 as a trap frame, if it exists */
828                 if (BugCheckParameter3) TrapFrame = (PVOID)BugCheckParameter3;
829             }
830 
831             /* Check if we got one now and if we need to get the Program Counter */
832             if ((TrapFrame) &&
833                 (BugCheckCode != KERNEL_MODE_EXCEPTION_NOT_HANDLED))
834             {
835                 /* Get the Program Counter */
836                 Pc = (PVOID)KeGetTrapFramePc(TrapFrame);
837             }
838             break;
839         }
840 
841         /* Wrong IRQL */
842         case IRQL_NOT_LESS_OR_EQUAL:
843         {
844             /*
845              * The NT kernel has 3 special sections:
846              * MISYSPTE, POOLMI and POOLCODE. The bug check code can
847              * determine in which of these sections this bugcode happened
848              * and provide a more detailed analysis. For now, we don't.
849              */
850 
851             /* Program Counter is in parameter 4 */
852             Pc = (PVOID)BugCheckParameter4;
853 
854             /* Get the driver base */
855             DriverBase = KiPcToFileHeader(Pc,
856                                           &LdrEntry,
857                                           FALSE,
858                                           &IsSystem);
859             if (IsSystem)
860             {
861                 /*
862                  * The error happened inside the kernel or HAL.
863                  * Get the memory address that was being referenced.
864                  */
865                 Memory = (PVOID)BugCheckParameter1;
866 
867                 /* Find to which driver it belongs */
868                 DriverBase = KiPcToFileHeader(Memory,
869                                               &LdrEntry,
870                                               TRUE,
871                                               &IsSystem);
872                 if (DriverBase)
873                 {
874                     /* Get the driver name and update the bug code */
875                     KiBugCheckDriver = &LdrEntry->BaseDllName;
876                     KiBugCheckData[0] = DRIVER_PORTION_MUST_BE_NONPAGED;
877                 }
878                 else
879                 {
880                     /* Find the driver that unloaded at this address */
881                     KiBugCheckDriver = NULL; // FIXME: ROS can't locate
882 
883                     /* Check if the cause was an unloaded driver */
884                     if (KiBugCheckDriver)
885                     {
886                         /* Update bug check code */
887                         KiBugCheckData[0] =
888                             SYSTEM_SCAN_AT_RAISED_IRQL_CAUGHT_IMPROPER_DRIVER_UNLOAD;
889                     }
890                 }
891             }
892             else
893             {
894                 /* Update the bug check code */
895                 KiBugCheckData[0] = DRIVER_IRQL_NOT_LESS_OR_EQUAL;
896             }
897 
898             /* Clear Pc so we don't look it up later */
899             Pc = NULL;
900             break;
901         }
902 
903         /* Hard error */
904         case FATAL_UNHANDLED_HARD_ERROR:
905         {
906             /* Copy bug check data from hard error */
907             HardErrorParameters = (PULONG_PTR)BugCheckParameter2;
908             KiBugCheckData[0] = BugCheckParameter1;
909             KiBugCheckData[1] = HardErrorParameters[0];
910             KiBugCheckData[2] = HardErrorParameters[1];
911             KiBugCheckData[3] = HardErrorParameters[2];
912             KiBugCheckData[4] = HardErrorParameters[3];
913 
914             /* Remember that this is hard error and set the caption/message */
915             IsHardError = TRUE;
916             HardErrCaption = (PCHAR)BugCheckParameter3;
917             HardErrMessage = (PCHAR)BugCheckParameter4;
918             break;
919         }
920 
921         /* Page fault */
922         case PAGE_FAULT_IN_NONPAGED_AREA:
923         {
924             /* Assume no driver */
925             DriverBase = NULL;
926 
927             /* Check if we have a trap frame */
928             if (!TrapFrame)
929             {
930                 /* We don't, use parameter 3 if possible */
931                 if (BugCheckParameter3) TrapFrame = (PVOID)BugCheckParameter3;
932             }
933 
934             /* Check if we have a frame now */
935             if (TrapFrame)
936             {
937                 /* Get the Program Counter */
938                 Pc = (PVOID)KeGetTrapFramePc(TrapFrame);
939                 KiBugCheckData[3] = (ULONG_PTR)Pc;
940 
941                 /* Find out if was in the kernel or drivers */
942                 DriverBase = KiPcToFileHeader(Pc,
943                                               &LdrEntry,
944                                               FALSE,
945                                               &IsSystem);
946             }
947             else
948             {
949                 /* Can't blame a driver, assume system */
950                 IsSystem = TRUE;
951             }
952 
953             /* FIXME: Check for session pool in addition to special pool */
954 
955             /* Special pool has its own bug check codes */
956             if (MmIsSpecialPoolAddress((PVOID)BugCheckParameter1))
957             {
958                 if (MmIsSpecialPoolAddressFree((PVOID)BugCheckParameter1))
959                 {
960                     KiBugCheckData[0] = IsSystem
961                         ? PAGE_FAULT_IN_FREED_SPECIAL_POOL
962                         : DRIVER_PAGE_FAULT_IN_FREED_SPECIAL_POOL;
963                 }
964                 else
965                 {
966                     KiBugCheckData[0] = IsSystem
967                         ? PAGE_FAULT_BEYOND_END_OF_ALLOCATION
968                         : DRIVER_PAGE_FAULT_BEYOND_END_OF_ALLOCATION;
969                 }
970             }
971             else if (!DriverBase)
972             {
973                 /* Find the driver that unloaded at this address */
974                 KiBugCheckDriver = NULL; // FIXME: ROS can't locate
975 
976                 /* Check if the cause was an unloaded driver */
977                 if (KiBugCheckDriver)
978                 {
979                     KiBugCheckData[0] =
980                         DRIVER_UNLOADED_WITHOUT_CANCELLING_PENDING_OPERATIONS;
981                 }
982             }
983             break;
984         }
985 
986         /* Check if the driver forgot to unlock pages */
987         case DRIVER_LEFT_LOCKED_PAGES_IN_PROCESS:
988 
989             /* Program Counter is in parameter 1 */
990             Pc = (PVOID)BugCheckParameter1;
991             break;
992 
993         /* Check if the driver consumed too many PTEs */
994         case DRIVER_USED_EXCESSIVE_PTES:
995 
996             /* Loader entry is in parameter 1 */
997             LdrEntry = (PVOID)BugCheckParameter1;
998             KiBugCheckDriver = &LdrEntry->BaseDllName;
999             break;
1000 
1001         /* Check if the driver has a stuck thread */
1002         case THREAD_STUCK_IN_DEVICE_DRIVER:
1003 
1004             /* The name is in Parameter 3 */
1005             KiBugCheckDriver = (PVOID)BugCheckParameter3;
1006             break;
1007 
1008         /* Anything else */
1009         default:
1010             break;
1011     }
1012 
1013     /* Do we have a driver name? */
1014     if (KiBugCheckDriver)
1015     {
1016         /* Convert it to ANSI */
1017         KeBugCheckUnicodeToAnsi(KiBugCheckDriver, AnsiName, sizeof(AnsiName));
1018     }
1019     else
1020     {
1021         /* Do we have a Program Counter? */
1022         if (Pc)
1023         {
1024             /* Dump image name */
1025             KiDumpParameterImages(AnsiName,
1026                                   (PULONG_PTR)&Pc,
1027                                   1,
1028                                   KeBugCheckUnicodeToAnsi);
1029         }
1030     }
1031 
1032     /* Check if we need to save the context for KD */
1033     if (!KdPitchDebugger) KdDebuggerDataBlock.SavedContext = (ULONG_PTR)&Context;
1034 
1035     /* Check if a debugger is connected */
1036     if ((BugCheckCode != MANUALLY_INITIATED_CRASH) && (KdDebuggerEnabled))
1037     {
1038         /* Crash on the debugger console */
1039         DbgPrint("\n*** Fatal System Error: 0x%08lx\n"
1040                  "                       (0x%p,0x%p,0x%p,0x%p)\n\n",
1041                  KiBugCheckData[0],
1042                  KiBugCheckData[1],
1043                  KiBugCheckData[2],
1044                  KiBugCheckData[3],
1045                  KiBugCheckData[4]);
1046 
1047         /* Check if the debugger isn't currently connected */
1048         if (!KdDebuggerNotPresent)
1049         {
1050             /* Check if we have a driver to blame */
1051             if (KiBugCheckDriver)
1052             {
1053                 /* Dump it */
1054                 DbgPrint("Driver at fault: %s.\n", AnsiName);
1055             }
1056 
1057             /* Check if this was a hard error */
1058             if (IsHardError)
1059             {
1060                 /* Print caption and message */
1061                 if (HardErrCaption) DbgPrint(HardErrCaption);
1062                 if (HardErrMessage) DbgPrint(HardErrMessage);
1063             }
1064 
1065             /* Break in the debugger */
1066             KiBugCheckDebugBreak(DBG_STATUS_BUGCHECK_FIRST);
1067         }
1068     }
1069 
1070     /* Raise IRQL to HIGH_LEVEL */
1071     _disable();
1072     KeRaiseIrql(HIGH_LEVEL, &OldIrql);
1073 
1074     /* Avoid recursion */
1075     if (!InterlockedDecrement((PLONG)&KeBugCheckCount))
1076     {
1077 #ifdef CONFIG_SMP
1078         /* Set CPU that is bug checking now */
1079         KeBugCheckOwner = Prcb->Number;
1080 
1081         /* Freeze the other CPUs */
1082         for (i = 0; i < KeNumberProcessors; i++)
1083         {
1084             if (i != (LONG)KeGetCurrentProcessorNumber())
1085             {
1086                 /* Send the IPI and give them one second to catch up */
1087                 KiIpiSend(1 << i, IPI_FREEZE);
1088                 KeStallExecutionProcessor(1000000);
1089             }
1090         }
1091 #endif
1092 
1093         /* Display the BSOD */
1094         KiDisplayBlueScreen(MessageId,
1095                             IsHardError,
1096                             HardErrCaption,
1097                             HardErrMessage,
1098                             AnsiName);
1099 
1100         // TODO/FIXME: Run the registered reason-callbacks from
1101         // the KeBugcheckReasonCallbackListHead list with the
1102         // KbCallbackReserved1 reason.
1103 
1104         /* Check if the debugger is disabled but we can enable it */
1105         if (!(KdDebuggerEnabled) && !(KdPitchDebugger))
1106         {
1107             /* Enable it */
1108             KdEnableDebuggerWithLock(FALSE);
1109         }
1110         else
1111         {
1112             /* Otherwise, print the last line */
1113             InbvDisplayString("\r\n");
1114         }
1115 
1116         /* Save the context */
1117         Prcb->ProcessorState.ContextFrame = Context;
1118 
1119         /* FIXME: Support Triage Dump */
1120 
1121         /* FIXME: Write the crash dump */
1122     }
1123     else
1124     {
1125         /* Increase recursion count */
1126         KeBugCheckOwnerRecursionCount++;
1127         if (KeBugCheckOwnerRecursionCount == 2)
1128         {
1129             /* Break in the debugger */
1130             KiBugCheckDebugBreak(DBG_STATUS_BUGCHECK_SECOND);
1131         }
1132         else if (KeBugCheckOwnerRecursionCount > 2)
1133         {
1134             /* Halt execution */
1135             while (TRUE);
1136         }
1137     }
1138 
1139     /* Call the Callbacks */
1140     KiDoBugCheckCallbacks();
1141 
1142     /* FIXME: Call Watchdog if enabled */
1143 
1144     /* Check if we have to reboot */
1145     if (Reboot)
1146     {
1147         /* Unload symbols */
1148         DbgUnLoadImageSymbols(NULL, (PVOID)MAXULONG_PTR, 0);
1149         HalReturnToFirmware(HalRebootRoutine);
1150     }
1151 
1152     /* Attempt to break in the debugger (otherwise halt CPU) */
1153     KiBugCheckDebugBreak(DBG_STATUS_BUGCHECK_SECOND);
1154 
1155     /* Shouldn't get here */
1156     ASSERT(FALSE);
1157     while (TRUE);
1158 }
1159 
1160 BOOLEAN
1161 NTAPI
1162 KiHandleNmi(VOID)
1163 {
1164     BOOLEAN Handled = FALSE;
1165     PKNMI_HANDLER_CALLBACK NmiData;
1166 
1167     /* Parse the list of callbacks */
1168     NmiData = KiNmiCallbackListHead;
1169     while (NmiData)
1170     {
1171         /* Save if this callback has handled it -- all it takes is one */
1172         Handled |= NmiData->Callback(NmiData->Context, Handled);
1173         NmiData = NmiData->Next;
1174     }
1175 
1176     /* Has anyone handled this? */
1177     return Handled;
1178 }
1179 
1180 /* PUBLIC FUNCTIONS **********************************************************/
1181 
1182 /*
1183  * @unimplemented
1184  */
1185 NTSTATUS
1186 NTAPI
1187 KeInitializeCrashDumpHeader(IN ULONG Type,
1188                             IN ULONG Flags,
1189                             OUT PVOID Buffer,
1190                             IN ULONG BufferSize,
1191                             OUT ULONG BufferNeeded OPTIONAL)
1192 {
1193     UNIMPLEMENTED;
1194     return STATUS_UNSUCCESSFUL;
1195 }
1196 
1197 /*
1198  * @implemented
1199  */
1200 BOOLEAN
1201 NTAPI
1202 KeDeregisterBugCheckCallback(IN PKBUGCHECK_CALLBACK_RECORD CallbackRecord)
1203 {
1204     KIRQL OldIrql;
1205     BOOLEAN Status = FALSE;
1206 
1207     /* Raise IRQL to High */
1208     KeRaiseIrql(HIGH_LEVEL, &OldIrql);
1209 
1210     /* Check the Current State */
1211     if (CallbackRecord->State == BufferInserted)
1212     {
1213         /* Reset state and remove from list */
1214         CallbackRecord->State = BufferEmpty;
1215         RemoveEntryList(&CallbackRecord->Entry);
1216         Status = TRUE;
1217     }
1218 
1219     /* Lower IRQL and return */
1220     KeLowerIrql(OldIrql);
1221     return Status;
1222 }
1223 
1224 /*
1225  * @implemented
1226  */
1227 BOOLEAN
1228 NTAPI
1229 KeDeregisterBugCheckReasonCallback(
1230     IN PKBUGCHECK_REASON_CALLBACK_RECORD CallbackRecord)
1231 {
1232     KIRQL OldIrql;
1233     BOOLEAN Status = FALSE;
1234 
1235     /* Raise IRQL to High */
1236     KeRaiseIrql(HIGH_LEVEL, &OldIrql);
1237 
1238     /* Check the Current State */
1239     if (CallbackRecord->State == BufferInserted)
1240     {
1241         /* Reset state and remove from list */
1242         CallbackRecord->State = BufferEmpty;
1243         RemoveEntryList(&CallbackRecord->Entry);
1244         Status = TRUE;
1245     }
1246 
1247     /* Lower IRQL and return */
1248     KeLowerIrql(OldIrql);
1249     return Status;
1250 }
1251 
1252 /*
1253  * @implemented
1254  */
1255 BOOLEAN
1256 NTAPI
1257 KeRegisterBugCheckCallback(IN PKBUGCHECK_CALLBACK_RECORD CallbackRecord,
1258                            IN PKBUGCHECK_CALLBACK_ROUTINE CallbackRoutine,
1259                            IN PVOID Buffer,
1260                            IN ULONG Length,
1261                            IN PUCHAR Component)
1262 {
1263     KIRQL OldIrql;
1264     BOOLEAN Status = FALSE;
1265 
1266     /* Raise IRQL to High */
1267     KeRaiseIrql(HIGH_LEVEL, &OldIrql);
1268 
1269     /* Check the Current State first so we don't double-register */
1270     if (CallbackRecord->State == BufferEmpty)
1271     {
1272         /* Set the Callback Settings and insert into the list */
1273         CallbackRecord->Length = Length;
1274         CallbackRecord->Buffer = Buffer;
1275         CallbackRecord->Component = Component;
1276         CallbackRecord->CallbackRoutine = CallbackRoutine;
1277         CallbackRecord->State = BufferInserted;
1278         InsertTailList(&KeBugcheckCallbackListHead, &CallbackRecord->Entry);
1279         Status = TRUE;
1280     }
1281 
1282     /* Lower IRQL and return */
1283     KeLowerIrql(OldIrql);
1284     return Status;
1285 }
1286 
1287 /*
1288  * @implemented
1289  */
1290 BOOLEAN
1291 NTAPI
1292 KeRegisterBugCheckReasonCallback(
1293     IN PKBUGCHECK_REASON_CALLBACK_RECORD CallbackRecord,
1294     IN PKBUGCHECK_REASON_CALLBACK_ROUTINE CallbackRoutine,
1295     IN KBUGCHECK_CALLBACK_REASON Reason,
1296     IN PUCHAR Component)
1297 {
1298     KIRQL OldIrql;
1299     BOOLEAN Status = FALSE;
1300 
1301     /* Raise IRQL to High */
1302     KeRaiseIrql(HIGH_LEVEL, &OldIrql);
1303 
1304     /* Check the Current State first so we don't double-register */
1305     if (CallbackRecord->State == BufferEmpty)
1306     {
1307         /* Set the Callback Settings and insert into the list */
1308         CallbackRecord->Component = Component;
1309         CallbackRecord->CallbackRoutine = CallbackRoutine;
1310         CallbackRecord->State = BufferInserted;
1311         CallbackRecord->Reason = Reason;
1312         InsertTailList(&KeBugcheckReasonCallbackListHead,
1313                        &CallbackRecord->Entry);
1314         Status = TRUE;
1315     }
1316 
1317     /* Lower IRQL and return */
1318     KeLowerIrql(OldIrql);
1319     return Status;
1320 }
1321 
1322 /*
1323  * @implemented
1324  */
1325 PVOID
1326 NTAPI
1327 KeRegisterNmiCallback(IN PNMI_CALLBACK CallbackRoutine,
1328                       IN PVOID Context)
1329 {
1330     KIRQL OldIrql;
1331     PKNMI_HANDLER_CALLBACK NmiData, Next;
1332     ASSERT_IRQL_LESS_OR_EQUAL(DISPATCH_LEVEL);
1333 
1334     /* Allocate NMI callback data */
1335     NmiData = ExAllocatePoolWithTag(NonPagedPool, sizeof(*NmiData), TAG_KNMI);
1336     if (!NmiData) return NULL;
1337 
1338     /* Fill in the information */
1339     NmiData->Callback = CallbackRoutine;
1340     NmiData->Context = Context;
1341     NmiData->Handle = NmiData;
1342 
1343     /* Insert it into NMI callback list */
1344     KiAcquireNmiListLock(&OldIrql);
1345     NmiData->Next = KiNmiCallbackListHead;
1346     Next = InterlockedCompareExchangePointer((PVOID*)&KiNmiCallbackListHead,
1347                                              NmiData,
1348                                              NmiData->Next);
1349     ASSERT(Next == NmiData->Next);
1350     KiReleaseNmiListLock(OldIrql);
1351 
1352     /* Return the opaque "handle" */
1353     return NmiData->Handle;
1354 }
1355 
1356 /*
1357  * @implemented
1358  */
1359 NTSTATUS
1360 NTAPI
1361 KeDeregisterNmiCallback(IN PVOID Handle)
1362 {
1363     KIRQL OldIrql;
1364     PKNMI_HANDLER_CALLBACK NmiData;
1365     PKNMI_HANDLER_CALLBACK* Previous;
1366     ASSERT_IRQL_LESS_OR_EQUAL(DISPATCH_LEVEL);
1367 
1368     /* Find in the list the NMI callback corresponding to the handle */
1369     KiAcquireNmiListLock(&OldIrql);
1370     Previous = &KiNmiCallbackListHead;
1371     NmiData = *Previous;
1372     while (NmiData)
1373     {
1374         if (NmiData->Handle == Handle)
1375         {
1376             /* The handle is the pointer to the callback itself */
1377             ASSERT(Handle == NmiData);
1378 
1379             /* Found it, remove from the list */
1380             *Previous = NmiData->Next;
1381             break;
1382         }
1383 
1384         /* Not found; try again */
1385         Previous = &NmiData->Next;
1386         NmiData = *Previous;
1387     }
1388     KiReleaseNmiListLock(OldIrql);
1389 
1390     /* If we have found the entry, free it */
1391     if (NmiData)
1392     {
1393         ExFreePoolWithTag(NmiData, TAG_KNMI);
1394         return STATUS_SUCCESS;
1395     }
1396 
1397     return STATUS_INVALID_HANDLE;
1398 }
1399 
1400 /*
1401  * @implemented
1402  */
1403 DECLSPEC_NORETURN
1404 VOID
1405 NTAPI
1406 KeBugCheckEx(IN ULONG BugCheckCode,
1407              IN ULONG_PTR BugCheckParameter1,
1408              IN ULONG_PTR BugCheckParameter2,
1409              IN ULONG_PTR BugCheckParameter3,
1410              IN ULONG_PTR BugCheckParameter4)
1411 {
1412     /* Call the internal API */
1413     KeBugCheckWithTf(BugCheckCode,
1414                      BugCheckParameter1,
1415                      BugCheckParameter2,
1416                      BugCheckParameter3,
1417                      BugCheckParameter4,
1418                      NULL);
1419 }
1420 
1421 /*
1422  * @implemented
1423  */
1424 DECLSPEC_NORETURN
1425 VOID
1426 NTAPI
1427 KeBugCheck(ULONG BugCheckCode)
1428 {
1429     /* Call the internal API */
1430     KeBugCheckWithTf(BugCheckCode, 0, 0, 0, 0, NULL);
1431 }
1432 
1433 /*
1434  * @implemented
1435  */
1436 VOID
1437 NTAPI
1438 KeEnterKernelDebugger(VOID)
1439 {
1440     /* Disable interrupts */
1441     KiHardwareTrigger = 1;
1442     _disable();
1443 
1444     /* Check the bugcheck count */
1445     if (!InterlockedDecrement((PLONG)&KeBugCheckCount))
1446     {
1447         /* There was only one, is the debugger disabled? */
1448         if (!(KdDebuggerEnabled) && !(KdPitchDebugger))
1449         {
1450             /* Enable the debugger */
1451             KdInitSystem(0, NULL);
1452         }
1453     }
1454 
1455     /* Break in the debugger */
1456     KiBugCheckDebugBreak(DBG_STATUS_FATAL);
1457 }
1458 
1459 /* EOF */
1460