xref: /reactos/ntoskrnl/ke/bug.c (revision 81db5e1d)
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[75];
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     sprintf(AnsiName,
680             "\r\n\r\n*** STOP: 0x%08lX (0x%p,0x%p,0x%p,0x%p)\r\n\r\n",
681             (ULONG)KiBugCheckData[0],
682             (PVOID)KiBugCheckData[1],
683             (PVOID)KiBugCheckData[2],
684             (PVOID)KiBugCheckData[3],
685             (PVOID)KiBugCheckData[4]);
686     InbvDisplayString(AnsiName);
687 
688     /* Check if we have a driver*/
689     if (KiBugCheckDriver)
690     {
691         /* Display technical driver data */
692         InbvDisplayString(Message);
693     }
694     else
695     {
696         /* Dump parameter information */
697         KiDumpParameterImages(Message,
698                               (PVOID)&KiBugCheckData[1],
699                               4,
700                               KeBugCheckUnicodeToAnsi);
701     }
702 }
703 
704 DECLSPEC_NORETURN
705 VOID
706 NTAPI
707 KeBugCheckWithTf(IN ULONG BugCheckCode,
708                  IN ULONG_PTR BugCheckParameter1,
709                  IN ULONG_PTR BugCheckParameter2,
710                  IN ULONG_PTR BugCheckParameter3,
711                  IN ULONG_PTR BugCheckParameter4,
712                  IN PKTRAP_FRAME TrapFrame)
713 {
714     PKPRCB Prcb = KeGetCurrentPrcb();
715     CONTEXT Context;
716     ULONG MessageId;
717     CHAR AnsiName[128];
718     BOOLEAN IsSystem, IsHardError = FALSE, Reboot = FALSE;
719     PCHAR HardErrCaption = NULL, HardErrMessage = NULL;
720     PVOID Pc = NULL, Memory;
721     PVOID DriverBase;
722     PLDR_DATA_TABLE_ENTRY LdrEntry;
723     PULONG_PTR HardErrorParameters;
724     KIRQL OldIrql;
725 #ifdef CONFIG_SMP
726     LONG i = 0;
727 #endif
728 
729     /* Set active bugcheck */
730     KeBugCheckActive = TRUE;
731     KiBugCheckDriver = NULL;
732 
733     /* Check if this is power failure simulation */
734     if (BugCheckCode == POWER_FAILURE_SIMULATE)
735     {
736         /* Call the Callbacks and reboot */
737         KiDoBugCheckCallbacks();
738         HalReturnToFirmware(HalRebootRoutine);
739     }
740 
741     /* Save the IRQL and set hardware trigger */
742     Prcb->DebuggerSavedIRQL = KeGetCurrentIrql();
743     InterlockedIncrement((PLONG)&KiHardwareTrigger);
744 
745     /* Capture the CPU Context */
746     RtlCaptureContext(&Prcb->ProcessorState.ContextFrame);
747     KiSaveProcessorControlState(&Prcb->ProcessorState);
748     Context = Prcb->ProcessorState.ContextFrame;
749 
750     /* FIXME: Call the Watchdog if it's registered */
751 
752     /* Check which bugcode this is */
753     switch (BugCheckCode)
754     {
755         /* These bug checks already have detailed messages, keep them */
756         case UNEXPECTED_KERNEL_MODE_TRAP:
757         case DRIVER_CORRUPTED_EXPOOL:
758         case ACPI_BIOS_ERROR:
759         case ACPI_BIOS_FATAL_ERROR:
760         case THREAD_STUCK_IN_DEVICE_DRIVER:
761         case DATA_BUS_ERROR:
762         case FAT_FILE_SYSTEM:
763         case NO_MORE_SYSTEM_PTES:
764         case INACCESSIBLE_BOOT_DEVICE:
765 
766             /* Keep the same code */
767             MessageId = BugCheckCode;
768             break;
769 
770         /* Check if this is a kernel-mode exception */
771         case KERNEL_MODE_EXCEPTION_NOT_HANDLED:
772         case SYSTEM_THREAD_EXCEPTION_NOT_HANDLED:
773         case KMODE_EXCEPTION_NOT_HANDLED:
774 
775             /* Use the generic text message */
776             MessageId = KMODE_EXCEPTION_NOT_HANDLED;
777             break;
778 
779         /* File-system errors */
780         case NTFS_FILE_SYSTEM:
781 
782             /* Use the generic message for FAT */
783             MessageId = FAT_FILE_SYSTEM;
784             break;
785 
786         /* Check if this is a coruption of the Mm's Pool */
787         case DRIVER_CORRUPTED_MMPOOL:
788 
789             /* Use generic corruption message */
790             MessageId = DRIVER_CORRUPTED_EXPOOL;
791             break;
792 
793         /* Check if this is a signature check failure */
794         case STATUS_SYSTEM_IMAGE_BAD_SIGNATURE:
795 
796             /* Use the generic corruption message */
797             MessageId = BUGCODE_PSS_MESSAGE_SIGNATURE;
798             break;
799 
800         /* All other codes */
801         default:
802 
803             /* Use the default bugcheck message */
804             MessageId = BUGCODE_PSS_MESSAGE;
805             break;
806     }
807 
808     /* Save bugcheck data */
809     KiBugCheckData[0] = BugCheckCode;
810     KiBugCheckData[1] = BugCheckParameter1;
811     KiBugCheckData[2] = BugCheckParameter2;
812     KiBugCheckData[3] = BugCheckParameter3;
813     KiBugCheckData[4] = BugCheckParameter4;
814 
815     /* Now check what bugcheck this is */
816     switch (BugCheckCode)
817     {
818         /* Invalid access to R/O memory or Unhandled KM Exception */
819         case KERNEL_MODE_EXCEPTION_NOT_HANDLED:
820         case ATTEMPTED_WRITE_TO_READONLY_MEMORY:
821         case ATTEMPTED_EXECUTE_OF_NOEXECUTE_MEMORY:
822         {
823             /* Check if we have a trap frame */
824             if (!TrapFrame)
825             {
826                 /* Use parameter 3 as a trap frame, if it exists */
827                 if (BugCheckParameter3) TrapFrame = (PVOID)BugCheckParameter3;
828             }
829 
830             /* Check if we got one now and if we need to get the Program Counter */
831             if ((TrapFrame) &&
832                 (BugCheckCode != KERNEL_MODE_EXCEPTION_NOT_HANDLED))
833             {
834                 /* Get the Program Counter */
835                 Pc = (PVOID)KeGetTrapFramePc(TrapFrame);
836             }
837             break;
838         }
839 
840         /* Wrong IRQL */
841         case IRQL_NOT_LESS_OR_EQUAL:
842         {
843             /*
844              * The NT kernel has 3 special sections:
845              * MISYSPTE, POOLMI and POOLCODE. The bug check code can
846              * determine in which of these sections this bugcode happened
847              * and provide a more detailed analysis. For now, we don't.
848              */
849 
850             /* Program Counter is in parameter 4 */
851             Pc = (PVOID)BugCheckParameter4;
852 
853             /* Get the driver base */
854             DriverBase = KiPcToFileHeader(Pc,
855                                           &LdrEntry,
856                                           FALSE,
857                                           &IsSystem);
858             if (IsSystem)
859             {
860                 /*
861                  * The error happened inside the kernel or HAL.
862                  * Get the memory address that was being referenced.
863                  */
864                 Memory = (PVOID)BugCheckParameter1;
865 
866                 /* Find to which driver it belongs */
867                 DriverBase = KiPcToFileHeader(Memory,
868                                               &LdrEntry,
869                                               TRUE,
870                                               &IsSystem);
871                 if (DriverBase)
872                 {
873                     /* Get the driver name and update the bug code */
874                     KiBugCheckDriver = &LdrEntry->BaseDllName;
875                     KiBugCheckData[0] = DRIVER_PORTION_MUST_BE_NONPAGED;
876                 }
877                 else
878                 {
879                     /* Find the driver that unloaded at this address */
880                     KiBugCheckDriver = NULL; // FIXME: ROS can't locate
881 
882                     /* Check if the cause was an unloaded driver */
883                     if (KiBugCheckDriver)
884                     {
885                         /* Update bug check code */
886                         KiBugCheckData[0] =
887                             SYSTEM_SCAN_AT_RAISED_IRQL_CAUGHT_IMPROPER_DRIVER_UNLOAD;
888                     }
889                 }
890             }
891             else
892             {
893                 /* Update the bug check code */
894                 KiBugCheckData[0] = DRIVER_IRQL_NOT_LESS_OR_EQUAL;
895             }
896 
897             /* Clear Pc so we don't look it up later */
898             Pc = NULL;
899             break;
900         }
901 
902         /* Hard error */
903         case FATAL_UNHANDLED_HARD_ERROR:
904         {
905             /* Copy bug check data from hard error */
906             HardErrorParameters = (PULONG_PTR)BugCheckParameter2;
907             KiBugCheckData[0] = BugCheckParameter1;
908             KiBugCheckData[1] = HardErrorParameters[0];
909             KiBugCheckData[2] = HardErrorParameters[1];
910             KiBugCheckData[3] = HardErrorParameters[2];
911             KiBugCheckData[4] = HardErrorParameters[3];
912 
913             /* Remember that this is hard error and set the caption/message */
914             IsHardError = TRUE;
915             HardErrCaption = (PCHAR)BugCheckParameter3;
916             HardErrMessage = (PCHAR)BugCheckParameter4;
917             break;
918         }
919 
920         /* Page fault */
921         case PAGE_FAULT_IN_NONPAGED_AREA:
922         {
923             /* Assume no driver */
924             DriverBase = NULL;
925 
926             /* Check if we have a trap frame */
927             if (!TrapFrame)
928             {
929                 /* We don't, use parameter 3 if possible */
930                 if (BugCheckParameter3) TrapFrame = (PVOID)BugCheckParameter3;
931             }
932 
933             /* Check if we have a frame now */
934             if (TrapFrame)
935             {
936                 /* Get the Program Counter */
937                 Pc = (PVOID)KeGetTrapFramePc(TrapFrame);
938                 KiBugCheckData[3] = (ULONG_PTR)Pc;
939 
940                 /* Find out if was in the kernel or drivers */
941                 DriverBase = KiPcToFileHeader(Pc,
942                                               &LdrEntry,
943                                               FALSE,
944                                               &IsSystem);
945             }
946             else
947             {
948                 /* Can't blame a driver, assume system */
949                 IsSystem = TRUE;
950             }
951 
952             /* FIXME: Check for session pool in addition to special pool */
953 
954             /* Special pool has its own bug check codes */
955             if (MmIsSpecialPoolAddress((PVOID)BugCheckParameter1))
956             {
957                 if (MmIsSpecialPoolAddressFree((PVOID)BugCheckParameter1))
958                 {
959                     KiBugCheckData[0] = IsSystem
960                         ? PAGE_FAULT_IN_FREED_SPECIAL_POOL
961                         : DRIVER_PAGE_FAULT_IN_FREED_SPECIAL_POOL;
962                 }
963                 else
964                 {
965                     KiBugCheckData[0] = IsSystem
966                         ? PAGE_FAULT_BEYOND_END_OF_ALLOCATION
967                         : DRIVER_PAGE_FAULT_BEYOND_END_OF_ALLOCATION;
968                 }
969             }
970             else if (!DriverBase)
971             {
972                 /* Find the driver that unloaded at this address */
973                 KiBugCheckDriver = NULL; // FIXME: ROS can't locate
974 
975                 /* Check if the cause was an unloaded driver */
976                 if (KiBugCheckDriver)
977                 {
978                     KiBugCheckData[0] =
979                         DRIVER_UNLOADED_WITHOUT_CANCELLING_PENDING_OPERATIONS;
980                 }
981             }
982             break;
983         }
984 
985         /* Check if the driver forgot to unlock pages */
986         case DRIVER_LEFT_LOCKED_PAGES_IN_PROCESS:
987 
988             /* Program Counter is in parameter 1 */
989             Pc = (PVOID)BugCheckParameter1;
990             break;
991 
992         /* Check if the driver consumed too many PTEs */
993         case DRIVER_USED_EXCESSIVE_PTES:
994 
995             /* Loader entry is in parameter 1 */
996             LdrEntry = (PVOID)BugCheckParameter1;
997             KiBugCheckDriver = &LdrEntry->BaseDllName;
998             break;
999 
1000         /* Check if the driver has a stuck thread */
1001         case THREAD_STUCK_IN_DEVICE_DRIVER:
1002 
1003             /* The name is in Parameter 3 */
1004             KiBugCheckDriver = (PVOID)BugCheckParameter3;
1005             break;
1006 
1007         /* Anything else */
1008         default:
1009             break;
1010     }
1011 
1012     /* Do we have a driver name? */
1013     if (KiBugCheckDriver)
1014     {
1015         /* Convert it to ANSI */
1016         KeBugCheckUnicodeToAnsi(KiBugCheckDriver, AnsiName, sizeof(AnsiName));
1017     }
1018     else
1019     {
1020         /* Do we have a Program Counter? */
1021         if (Pc)
1022         {
1023             /* Dump image name */
1024             KiDumpParameterImages(AnsiName,
1025                                   (PULONG_PTR)&Pc,
1026                                   1,
1027                                   KeBugCheckUnicodeToAnsi);
1028         }
1029     }
1030 
1031     /* Check if we need to save the context for KD */
1032     if (!KdPitchDebugger) KdDebuggerDataBlock.SavedContext = (ULONG_PTR)&Context;
1033 
1034     /* Check if a debugger is connected */
1035     if ((BugCheckCode != MANUALLY_INITIATED_CRASH) && (KdDebuggerEnabled))
1036     {
1037         /* Crash on the debugger console */
1038         DbgPrint("\n*** Fatal System Error: 0x%08lx\n"
1039                  "                       (0x%p,0x%p,0x%p,0x%p)\n\n",
1040                  KiBugCheckData[0],
1041                  KiBugCheckData[1],
1042                  KiBugCheckData[2],
1043                  KiBugCheckData[3],
1044                  KiBugCheckData[4]);
1045 
1046         /* Check if the debugger isn't currently connected */
1047         if (!KdDebuggerNotPresent)
1048         {
1049             /* Check if we have a driver to blame */
1050             if (KiBugCheckDriver)
1051             {
1052                 /* Dump it */
1053                 DbgPrint("Driver at fault: %s.\n", AnsiName);
1054             }
1055 
1056             /* Check if this was a hard error */
1057             if (IsHardError)
1058             {
1059                 /* Print caption and message */
1060                 if (HardErrCaption) DbgPrint(HardErrCaption);
1061                 if (HardErrMessage) DbgPrint(HardErrMessage);
1062             }
1063 
1064             /* Break in the debugger */
1065             KiBugCheckDebugBreak(DBG_STATUS_BUGCHECK_FIRST);
1066         }
1067     }
1068 
1069     /* Raise IRQL to HIGH_LEVEL */
1070     _disable();
1071     KeRaiseIrql(HIGH_LEVEL, &OldIrql);
1072 
1073     /* Avoid recursion */
1074     if (!InterlockedDecrement((PLONG)&KeBugCheckCount))
1075     {
1076 #ifdef CONFIG_SMP
1077         /* Set CPU that is bug checking now */
1078         KeBugCheckOwner = Prcb->Number;
1079 
1080         /* Freeze the other CPUs */
1081         for (i = 0; i < KeNumberProcessors; i++)
1082         {
1083             if (i != (LONG)KeGetCurrentProcessorNumber())
1084             {
1085                 /* Send the IPI and give them one second to catch up */
1086                 KiIpiSend(1 << i, IPI_FREEZE);
1087                 KeStallExecutionProcessor(1000000);
1088             }
1089         }
1090 #endif
1091 
1092         /* Display the BSOD */
1093         KiDisplayBlueScreen(MessageId,
1094                             IsHardError,
1095                             HardErrCaption,
1096                             HardErrMessage,
1097                             AnsiName);
1098 
1099         // TODO/FIXME: Run the registered reason-callbacks from
1100         // the KeBugcheckReasonCallbackListHead list with the
1101         // KbCallbackReserved1 reason.
1102 
1103         /* Check if the debugger is disabled but we can enable it */
1104         if (!(KdDebuggerEnabled) && !(KdPitchDebugger))
1105         {
1106             /* Enable it */
1107             KdEnableDebuggerWithLock(FALSE);
1108         }
1109         else
1110         {
1111             /* Otherwise, print the last line */
1112             InbvDisplayString("\r\n");
1113         }
1114 
1115         /* Save the context */
1116         Prcb->ProcessorState.ContextFrame = Context;
1117 
1118         /* FIXME: Support Triage Dump */
1119 
1120         /* FIXME: Write the crash dump */
1121     }
1122     else
1123     {
1124         /* Increase recursion count */
1125         KeBugCheckOwnerRecursionCount++;
1126         if (KeBugCheckOwnerRecursionCount == 2)
1127         {
1128             /* Break in the debugger */
1129             KiBugCheckDebugBreak(DBG_STATUS_BUGCHECK_SECOND);
1130         }
1131         else if (KeBugCheckOwnerRecursionCount > 2)
1132         {
1133             /* Halt execution */
1134             while (TRUE);
1135         }
1136     }
1137 
1138     /* Call the Callbacks */
1139     KiDoBugCheckCallbacks();
1140 
1141     /* FIXME: Call Watchdog if enabled */
1142 
1143     /* Check if we have to reboot */
1144     if (Reboot)
1145     {
1146         /* Unload symbols */
1147         DbgUnLoadImageSymbols(NULL, (PVOID)MAXULONG_PTR, 0);
1148         HalReturnToFirmware(HalRebootRoutine);
1149     }
1150 
1151     /* Attempt to break in the debugger (otherwise halt CPU) */
1152     KiBugCheckDebugBreak(DBG_STATUS_BUGCHECK_SECOND);
1153 
1154     /* Shouldn't get here */
1155     ASSERT(FALSE);
1156     while (TRUE);
1157 }
1158 
1159 BOOLEAN
1160 NTAPI
1161 KiHandleNmi(VOID)
1162 {
1163     BOOLEAN Handled = FALSE;
1164     PKNMI_HANDLER_CALLBACK NmiData;
1165 
1166     /* Parse the list of callbacks */
1167     NmiData = KiNmiCallbackListHead;
1168     while (NmiData)
1169     {
1170         /* Save if this callback has handled it -- all it takes is one */
1171         Handled |= NmiData->Callback(NmiData->Context, Handled);
1172         NmiData = NmiData->Next;
1173     }
1174 
1175     /* Has anyone handled this? */
1176     return Handled;
1177 }
1178 
1179 /* PUBLIC FUNCTIONS **********************************************************/
1180 
1181 /*
1182  * @unimplemented
1183  */
1184 NTSTATUS
1185 NTAPI
1186 KeInitializeCrashDumpHeader(IN ULONG Type,
1187                             IN ULONG Flags,
1188                             OUT PVOID Buffer,
1189                             IN ULONG BufferSize,
1190                             OUT ULONG BufferNeeded OPTIONAL)
1191 {
1192     UNIMPLEMENTED;
1193     return STATUS_UNSUCCESSFUL;
1194 }
1195 
1196 /*
1197  * @implemented
1198  */
1199 BOOLEAN
1200 NTAPI
1201 KeDeregisterBugCheckCallback(IN PKBUGCHECK_CALLBACK_RECORD CallbackRecord)
1202 {
1203     KIRQL OldIrql;
1204     BOOLEAN Status = FALSE;
1205 
1206     /* Raise IRQL to High */
1207     KeRaiseIrql(HIGH_LEVEL, &OldIrql);
1208 
1209     /* Check the Current State */
1210     if (CallbackRecord->State == BufferInserted)
1211     {
1212         /* Reset state and remove from list */
1213         CallbackRecord->State = BufferEmpty;
1214         RemoveEntryList(&CallbackRecord->Entry);
1215         Status = TRUE;
1216     }
1217 
1218     /* Lower IRQL and return */
1219     KeLowerIrql(OldIrql);
1220     return Status;
1221 }
1222 
1223 /*
1224  * @implemented
1225  */
1226 BOOLEAN
1227 NTAPI
1228 KeDeregisterBugCheckReasonCallback(
1229     IN PKBUGCHECK_REASON_CALLBACK_RECORD CallbackRecord)
1230 {
1231     KIRQL OldIrql;
1232     BOOLEAN Status = FALSE;
1233 
1234     /* Raise IRQL to High */
1235     KeRaiseIrql(HIGH_LEVEL, &OldIrql);
1236 
1237     /* Check the Current State */
1238     if (CallbackRecord->State == BufferInserted)
1239     {
1240         /* Reset state and remove from list */
1241         CallbackRecord->State = BufferEmpty;
1242         RemoveEntryList(&CallbackRecord->Entry);
1243         Status = TRUE;
1244     }
1245 
1246     /* Lower IRQL and return */
1247     KeLowerIrql(OldIrql);
1248     return Status;
1249 }
1250 
1251 /*
1252  * @implemented
1253  */
1254 BOOLEAN
1255 NTAPI
1256 KeRegisterBugCheckCallback(IN PKBUGCHECK_CALLBACK_RECORD CallbackRecord,
1257                            IN PKBUGCHECK_CALLBACK_ROUTINE CallbackRoutine,
1258                            IN PVOID Buffer,
1259                            IN ULONG Length,
1260                            IN PUCHAR Component)
1261 {
1262     KIRQL OldIrql;
1263     BOOLEAN Status = FALSE;
1264 
1265     /* Raise IRQL to High */
1266     KeRaiseIrql(HIGH_LEVEL, &OldIrql);
1267 
1268     /* Check the Current State first so we don't double-register */
1269     if (CallbackRecord->State == BufferEmpty)
1270     {
1271         /* Set the Callback Settings and insert into the list */
1272         CallbackRecord->Length = Length;
1273         CallbackRecord->Buffer = Buffer;
1274         CallbackRecord->Component = Component;
1275         CallbackRecord->CallbackRoutine = CallbackRoutine;
1276         CallbackRecord->State = BufferInserted;
1277         InsertTailList(&KeBugcheckCallbackListHead, &CallbackRecord->Entry);
1278         Status = TRUE;
1279     }
1280 
1281     /* Lower IRQL and return */
1282     KeLowerIrql(OldIrql);
1283     return Status;
1284 }
1285 
1286 /*
1287  * @implemented
1288  */
1289 BOOLEAN
1290 NTAPI
1291 KeRegisterBugCheckReasonCallback(
1292     IN PKBUGCHECK_REASON_CALLBACK_RECORD CallbackRecord,
1293     IN PKBUGCHECK_REASON_CALLBACK_ROUTINE CallbackRoutine,
1294     IN KBUGCHECK_CALLBACK_REASON Reason,
1295     IN PUCHAR Component)
1296 {
1297     KIRQL OldIrql;
1298     BOOLEAN Status = FALSE;
1299 
1300     /* Raise IRQL to High */
1301     KeRaiseIrql(HIGH_LEVEL, &OldIrql);
1302 
1303     /* Check the Current State first so we don't double-register */
1304     if (CallbackRecord->State == BufferEmpty)
1305     {
1306         /* Set the Callback Settings and insert into the list */
1307         CallbackRecord->Component = Component;
1308         CallbackRecord->CallbackRoutine = CallbackRoutine;
1309         CallbackRecord->State = BufferInserted;
1310         CallbackRecord->Reason = Reason;
1311         InsertTailList(&KeBugcheckReasonCallbackListHead,
1312                        &CallbackRecord->Entry);
1313         Status = TRUE;
1314     }
1315 
1316     /* Lower IRQL and return */
1317     KeLowerIrql(OldIrql);
1318     return Status;
1319 }
1320 
1321 /*
1322  * @implemented
1323  */
1324 PVOID
1325 NTAPI
1326 KeRegisterNmiCallback(IN PNMI_CALLBACK CallbackRoutine,
1327                       IN PVOID Context)
1328 {
1329     KIRQL OldIrql;
1330     PKNMI_HANDLER_CALLBACK NmiData, Next;
1331     ASSERT_IRQL_LESS_OR_EQUAL(DISPATCH_LEVEL);
1332 
1333     /* Allocate NMI callback data */
1334     NmiData = ExAllocatePoolWithTag(NonPagedPool, sizeof(*NmiData), TAG_KNMI);
1335     if (!NmiData) return NULL;
1336 
1337     /* Fill in the information */
1338     NmiData->Callback = CallbackRoutine;
1339     NmiData->Context = Context;
1340     NmiData->Handle = NmiData;
1341 
1342     /* Insert it into NMI callback list */
1343     KiAcquireNmiListLock(&OldIrql);
1344     NmiData->Next = KiNmiCallbackListHead;
1345     Next = InterlockedCompareExchangePointer((PVOID*)&KiNmiCallbackListHead,
1346                                              NmiData,
1347                                              NmiData->Next);
1348     ASSERT(Next == NmiData->Next);
1349     KiReleaseNmiListLock(OldIrql);
1350 
1351     /* Return the opaque "handle" */
1352     return NmiData->Handle;
1353 }
1354 
1355 /*
1356  * @implemented
1357  */
1358 NTSTATUS
1359 NTAPI
1360 KeDeregisterNmiCallback(IN PVOID Handle)
1361 {
1362     KIRQL OldIrql;
1363     PKNMI_HANDLER_CALLBACK NmiData;
1364     PKNMI_HANDLER_CALLBACK* Previous;
1365     ASSERT_IRQL_LESS_OR_EQUAL(DISPATCH_LEVEL);
1366 
1367     /* Find in the list the NMI callback corresponding to the handle */
1368     KiAcquireNmiListLock(&OldIrql);
1369     Previous = &KiNmiCallbackListHead;
1370     NmiData = *Previous;
1371     while (NmiData)
1372     {
1373         if (NmiData->Handle == Handle)
1374         {
1375             /* The handle is the pointer to the callback itself */
1376             ASSERT(Handle == NmiData);
1377 
1378             /* Found it, remove from the list */
1379             *Previous = NmiData->Next;
1380             break;
1381         }
1382 
1383         /* Not found; try again */
1384         Previous = &NmiData->Next;
1385         NmiData = *Previous;
1386     }
1387     KiReleaseNmiListLock(OldIrql);
1388 
1389     /* If we have found the entry, free it */
1390     if (NmiData)
1391     {
1392         ExFreePoolWithTag(NmiData, TAG_KNMI);
1393         return STATUS_SUCCESS;
1394     }
1395 
1396     return STATUS_INVALID_HANDLE;
1397 }
1398 
1399 /*
1400  * @implemented
1401  */
1402 DECLSPEC_NORETURN
1403 VOID
1404 NTAPI
1405 KeBugCheckEx(IN ULONG BugCheckCode,
1406              IN ULONG_PTR BugCheckParameter1,
1407              IN ULONG_PTR BugCheckParameter2,
1408              IN ULONG_PTR BugCheckParameter3,
1409              IN ULONG_PTR BugCheckParameter4)
1410 {
1411     /* Call the internal API */
1412     KeBugCheckWithTf(BugCheckCode,
1413                      BugCheckParameter1,
1414                      BugCheckParameter2,
1415                      BugCheckParameter3,
1416                      BugCheckParameter4,
1417                      NULL);
1418 }
1419 
1420 /*
1421  * @implemented
1422  */
1423 DECLSPEC_NORETURN
1424 VOID
1425 NTAPI
1426 KeBugCheck(ULONG BugCheckCode)
1427 {
1428     /* Call the internal API */
1429     KeBugCheckWithTf(BugCheckCode, 0, 0, 0, 0, NULL);
1430 }
1431 
1432 /*
1433  * @implemented
1434  */
1435 VOID
1436 NTAPI
1437 KeEnterKernelDebugger(VOID)
1438 {
1439     /* Disable interrupts */
1440     KiHardwareTrigger = 1;
1441     _disable();
1442 
1443     /* Check the bugcheck count */
1444     if (!InterlockedDecrement((PLONG)&KeBugCheckCount))
1445     {
1446         /* There was only one, is the debugger disabled? */
1447         if (!(KdDebuggerEnabled) && !(KdPitchDebugger))
1448         {
1449             /* Enable the debugger */
1450             KdInitSystem(0, NULL);
1451         }
1452     }
1453 
1454     /* Break in the debugger */
1455     KiBugCheckDebugBreak(DBG_STATUS_FATAL);
1456 }
1457 
1458 /* EOF */
1459