xref: /reactos/ntoskrnl/kdbg/kdb.c (revision c2c66aff)
1 /*
2  * COPYRIGHT:       See COPYING in the top level directory
3  * PROJECT:         ReactOS kernel
4  * FILE:            ntoskrnl/kdbg/kdb.c
5  * PURPOSE:         Kernel Debugger
6  *
7  * PROGRAMMERS:     Gregor Anich
8  */
9 
10 /* INCLUDES ******************************************************************/
11 
12 #include <ntoskrnl.h>
13 #define NDEBUG
14 #include <debug.h>
15 
16 /* TYPES *********************************************************************/
17 
18 /* DEFINES *******************************************************************/
19 
20 #define KDB_STACK_SIZE                   (4096*3)
21 #define KDB_MAXIMUM_BREAKPOINT_COUNT     256
22 #define KDB_MAXIMUM_HW_BREAKPOINT_COUNT  4
23 #define KDB_MAXIMUM_SW_BREAKPOINT_COUNT  256
24 
25 #define __STRING(x) #x
26 #define _STRING(x) __STRING(x)
27 
28 /* GLOBALS *******************************************************************/
29 
30 static LONG KdbEntryCount = 0;
31 static CHAR KdbStack[KDB_STACK_SIZE];
32 
33 static ULONG KdbBreakPointCount = 0;  /* Number of used breakpoints in the array */
34 static KDB_BREAKPOINT KdbBreakPoints[KDB_MAXIMUM_BREAKPOINT_COUNT] = {{0}};  /* Breakpoint array */
35 static ULONG KdbSwBreakPointCount = 0;  /* Number of enabled software breakpoints */
36 static ULONG KdbHwBreakPointCount = 0;  /* Number of enabled hardware breakpoints */
37 static PKDB_BREAKPOINT KdbSwBreakPoints[KDB_MAXIMUM_SW_BREAKPOINT_COUNT]; /* Enabled software breakpoints, orderless */
38 static PKDB_BREAKPOINT KdbHwBreakPoints[KDB_MAXIMUM_HW_BREAKPOINT_COUNT]; /* Enabled hardware breakpoints, orderless */
39 static PKDB_BREAKPOINT KdbBreakPointToReenable = NULL; /* Set to a breakpoint struct when single stepping after
40                                                           a software breakpoint was hit, to reenable it */
41 static BOOLEAN KdbpEvenThoughWeHaveABreakPointToReenableWeAlsoHaveARealSingleStep;
42 LONG KdbLastBreakPointNr = -1;  /* Index of the breakpoint which cause KDB to be entered */
43 ULONG KdbNumSingleSteps = 0; /* How many single steps to do */
44 BOOLEAN KdbSingleStepOver = FALSE; /* Whether to step over calls/reps. */
45 ULONG KdbDebugState = 0; /* KDBG Settings (NOECHO, KDSERIAL) */
46 static BOOLEAN KdbEnteredOnSingleStep = FALSE; /* Set to true when KDB was entered because of single step */
47 PEPROCESS KdbCurrentProcess = NULL;  /* The current process context in which KDB runs */
48 PEPROCESS KdbOriginalProcess = NULL; /* The process in whichs context KDB was intered */
49 PETHREAD KdbCurrentThread = NULL;  /* The current thread context in which KDB runs */
50 PETHREAD KdbOriginalThread = NULL; /* The thread in whichs context KDB was entered */
51 PKDB_KTRAP_FRAME KdbCurrentTrapFrame = NULL; /* Pointer to the current trapframe */
52 static KDB_KTRAP_FRAME KdbTrapFrame = { { 0 } };  /* The trapframe which was passed to KdbEnterDebuggerException */
53 static KDB_KTRAP_FRAME KdbThreadTrapFrame = { { 0 } }; /* The trapframe of the current thread (KdbCurrentThread) */
54 static KAPC_STATE KdbApcState;
55 extern BOOLEAN KdbpBugCheckRequested;
56 
57 /* Array of conditions when to enter KDB */
58 static KDB_ENTER_CONDITION KdbEnterConditions[][2] =
59 {
60     /* First chance       Last chance */
61     { KdbDoNotEnter,      KdbEnterFromKmode },   /* 0: Zero divide */
62     { KdbEnterFromKmode,  KdbDoNotEnter },       /* 1: Debug trap */
63     { KdbDoNotEnter,      KdbEnterAlways },      /* 2: NMI */
64     { KdbEnterFromKmode,  KdbDoNotEnter },       /* 3: INT3 */
65     { KdbDoNotEnter,      KdbEnterFromKmode },   /* 4: Overflow */
66     { KdbDoNotEnter,      KdbEnterFromKmode },   /* 5: BOUND range exceeded */
67     { KdbDoNotEnter,      KdbEnterFromKmode },   /* 6: Invalid opcode */
68     { KdbDoNotEnter,      KdbEnterFromKmode },   /* 7: No math coprocessor fault */
69     { KdbEnterAlways,     KdbEnterAlways },      /* 8: Double Fault */
70     { KdbEnterAlways,     KdbEnterAlways },      /* 9: Unknown(9) */
71     { KdbDoNotEnter,      KdbEnterFromKmode },   /* 10: Invalid TSS */
72     { KdbDoNotEnter,      KdbEnterFromKmode },   /* 11: Segment Not Present */
73     { KdbDoNotEnter,      KdbEnterFromKmode },   /* 12: Stack fault */
74     { KdbDoNotEnter,      KdbEnterFromKmode },   /* 13: General protection fault */
75     { KdbDoNotEnter,      KdbEnterFromKmode },   /* 14: Page fault */
76     { KdbEnterAlways,     KdbEnterAlways },      /* 15: Reserved (15) */
77     { KdbDoNotEnter,      KdbEnterFromKmode },   /* 16: FPU fault */
78     { KdbDoNotEnter,      KdbEnterFromKmode },   /* 17: Alignment Check */
79     { KdbDoNotEnter,      KdbEnterFromKmode },   /* 18: Machine Check */
80     { KdbDoNotEnter,      KdbEnterFromKmode },   /* 19: SIMD fault */
81     { KdbEnterFromKmode,  KdbDoNotEnter },       /* 20: Assertion failure */
82     { KdbDoNotEnter,      KdbEnterFromKmode }    /* Last entry: used for unknown exceptions */
83 };
84 
85 /* Exception descriptions */
86 static const CHAR *ExceptionNrToString[] =
87 {
88     "Divide Error",
89     "Debug Trap",
90     "NMI",
91     "Breakpoint",
92     "Overflow",
93     "BOUND range exceeded",
94     "Invalid Opcode",
95     "No Math Coprocessor",
96     "Double Fault",
97     "Unknown(9)",
98     "Invalid TSS",
99     "Segment Not Present",
100     "Stack Segment Fault",
101     "General Protection",
102     "Page Fault",
103     "Reserved(15)",
104     "Math Fault",
105     "Alignment Check",
106     "Machine Check",
107     "SIMD Fault",
108     "Assertion Failure"
109 };
110 
111 ULONG
112 NTAPI
113 KiSsFromTrapFrame(
114     IN PKTRAP_FRAME TrapFrame);
115 
116 ULONG
117 NTAPI
118 KiEspFromTrapFrame(
119     IN PKTRAP_FRAME TrapFrame);
120 
121 VOID
122 NTAPI
123 KiSsToTrapFrame(
124     IN PKTRAP_FRAME TrapFrame,
125     IN ULONG Ss);
126 
127 VOID
128 NTAPI
129 KiEspToTrapFrame(
130     IN PKTRAP_FRAME TrapFrame,
131     IN ULONG Esp);
132 
133 /* FUNCTIONS *****************************************************************/
134 
135 static VOID
136 KdbpTrapFrameToKdbTrapFrame(
137     PKTRAP_FRAME TrapFrame,
138     PKDB_KTRAP_FRAME KdbTrapFrame)
139 {
140     ULONG TrapCr0, TrapCr2, TrapCr3, TrapCr4;
141 
142     /* Copy the TrapFrame only up to Eflags and zero the rest*/
143     RtlCopyMemory(&KdbTrapFrame->Tf, TrapFrame, FIELD_OFFSET(KTRAP_FRAME, HardwareEsp));
144     RtlZeroMemory((PVOID)((ULONG_PTR)&KdbTrapFrame->Tf + FIELD_OFFSET(KTRAP_FRAME, HardwareEsp)),
145                   sizeof(KTRAP_FRAME) - FIELD_OFFSET(KTRAP_FRAME, HardwareEsp));
146 
147 #ifndef _MSC_VER
148    asm volatile(
149       "movl %%cr0, %0"    "\n\t"
150       "movl %%cr2, %1"    "\n\t"
151       "movl %%cr3, %2"    "\n\t"
152       "movl %%cr4, %3"    "\n\t"
153       : "=r"(TrapCr0), "=r"(TrapCr2),
154         "=r"(TrapCr3), "=r"(TrapCr4));
155 #else
156    __asm
157    {
158        mov eax, cr0;
159        mov TrapCr0, eax;
160 
161        mov eax, cr2;
162        mov TrapCr2, eax;
163 
164        mov eax, cr3;
165        mov TrapCr3, eax;
166 /* FIXME: What's the problem with cr4? */
167        //mov eax, cr4;
168        //mov TrapCr4, eax;
169    }
170 #endif
171 
172     KdbTrapFrame->Cr0 = TrapCr0;
173     KdbTrapFrame->Cr2 = TrapCr2;
174     KdbTrapFrame->Cr3 = TrapCr3;
175     KdbTrapFrame->Cr4 = TrapCr4;
176 
177     KdbTrapFrame->Tf.HardwareEsp = KiEspFromTrapFrame(TrapFrame);
178     KdbTrapFrame->Tf.HardwareSegSs = (USHORT)(KiSsFromTrapFrame(TrapFrame) & 0xFFFF);
179 
180 
181     /* FIXME: copy v86 registers if TrapFrame is a V86 trapframe */
182 }
183 
184 static VOID
185 KdbpKdbTrapFrameToTrapFrame(
186     PKDB_KTRAP_FRAME KdbTrapFrame,
187     PKTRAP_FRAME TrapFrame)
188 {
189     /* Copy the TrapFrame only up to Eflags and zero the rest*/
190     RtlCopyMemory(TrapFrame, &KdbTrapFrame->Tf, FIELD_OFFSET(KTRAP_FRAME, HardwareEsp));
191 
192     /* FIXME: write cr0, cr2, cr3 and cr4 (not needed atm) */
193 
194     KiSsToTrapFrame(TrapFrame, KdbTrapFrame->Tf.HardwareSegSs);
195     KiEspToTrapFrame(TrapFrame, KdbTrapFrame->Tf.HardwareEsp);
196 
197     /* FIXME: copy v86 registers if TrapFrame is a V86 trapframe */
198 }
199 
200 static VOID
201 KdbpKdbTrapFrameFromKernelStack(
202     PVOID KernelStack,
203     PKDB_KTRAP_FRAME KdbTrapFrame)
204 {
205     ULONG_PTR *StackPtr;
206 
207     RtlZeroMemory(KdbTrapFrame, sizeof(KDB_KTRAP_FRAME));
208     StackPtr = (ULONG_PTR *) KernelStack;
209 #ifdef _M_IX86
210     KdbTrapFrame->Tf.Ebp = StackPtr[3];
211     KdbTrapFrame->Tf.Edi = StackPtr[4];
212     KdbTrapFrame->Tf.Esi = StackPtr[5];
213     KdbTrapFrame->Tf.Ebx = StackPtr[6];
214     KdbTrapFrame->Tf.Eip = StackPtr[7];
215     KdbTrapFrame->Tf.HardwareEsp = (ULONG) (StackPtr + 8);
216     KdbTrapFrame->Tf.HardwareSegSs = KGDT_R0_DATA;
217     KdbTrapFrame->Tf.SegCs = KGDT_R0_CODE;
218     KdbTrapFrame->Tf.SegDs = KGDT_R0_DATA;
219     KdbTrapFrame->Tf.SegEs = KGDT_R0_DATA;
220     KdbTrapFrame->Tf.SegGs = KGDT_R0_DATA;
221 #endif
222 
223     /* FIXME: what about the other registers??? */
224 }
225 
226 /*!\brief Overwrites the instruction at \a Address with \a NewInst and stores
227  *        the old instruction in *OldInst.
228  *
229  * \param Process  Process in which's context to overwrite the instruction.
230  * \param Address  Address at which to overwrite the instruction.
231  * \param NewInst  New instruction (written to \a Address)
232  * \param OldInst  Old instruction (read from \a Address)
233  *
234  * \returns NTSTATUS
235  */
236 static NTSTATUS
237 KdbpOverwriteInstruction(
238     IN  PEPROCESS Process,
239     IN  ULONG_PTR Address,
240     IN  UCHAR NewInst,
241     OUT PUCHAR OldInst  OPTIONAL)
242 {
243     NTSTATUS Status;
244     ULONG Protect;
245     PEPROCESS CurrentProcess = PsGetCurrentProcess();
246     KAPC_STATE ApcState;
247 
248     /* Get the protection for the address. */
249     Protect = MmGetPageProtect(Process, (PVOID)PAGE_ROUND_DOWN(Address));
250 
251     /* Return if that page isn't present. */
252     if (Protect & PAGE_NOACCESS)
253     {
254         return STATUS_MEMORY_NOT_ALLOCATED;
255     }
256 
257     /* Attach to the process */
258     if (CurrentProcess != Process)
259     {
260         KeStackAttachProcess(&Process->Pcb, &ApcState);
261     }
262 
263     /* Make the page writeable if it is read only. */
264     if (Protect & (PAGE_READONLY|PAGE_EXECUTE|PAGE_EXECUTE_READ))
265     {
266         MmSetPageProtect(Process, (PVOID)PAGE_ROUND_DOWN(Address),
267                          (Protect & ~(PAGE_READONLY|PAGE_EXECUTE|PAGE_EXECUTE_READ)) | PAGE_READWRITE);
268     }
269 
270     /* Copy the old instruction back to the caller. */
271     if (OldInst)
272     {
273         Status = KdbpSafeReadMemory(OldInst, (PUCHAR)Address, 1);
274         if (!NT_SUCCESS(Status))
275         {
276             if (Protect & (PAGE_READONLY|PAGE_EXECUTE|PAGE_EXECUTE_READ))
277             {
278                 MmSetPageProtect(Process, (PVOID)PAGE_ROUND_DOWN(Address), Protect);
279             }
280 
281             /* Detach from process */
282             if (CurrentProcess != Process)
283             {
284                 KeDetachProcess();
285             }
286 
287             return Status;
288         }
289     }
290 
291     /* Copy the new instruction in its place. */
292     Status = KdbpSafeWriteMemory((PUCHAR)Address, &NewInst, 1);
293 
294     /* Restore the page protection. */
295     if (Protect & (PAGE_READONLY|PAGE_EXECUTE|PAGE_EXECUTE_READ))
296     {
297         MmSetPageProtect(Process, (PVOID)PAGE_ROUND_DOWN(Address), Protect);
298     }
299 
300     /* Detach from process */
301     if (CurrentProcess != Process)
302     {
303         KeUnstackDetachProcess(&ApcState);
304     }
305 
306     return Status;
307 }
308 
309 /*!\brief Checks whether the given instruction can be single stepped or has to be
310  *        stepped over using a temporary breakpoint.
311  *
312  * \retval TRUE   Instruction is a call.
313  * \retval FALSE  Instruction is not a call.
314  */
315 BOOLEAN
316 KdbpShouldStepOverInstruction(
317     ULONG_PTR Eip)
318 {
319     UCHAR Mem[3];
320     ULONG i = 0;
321 
322     if (!NT_SUCCESS(KdbpSafeReadMemory(Mem, (PVOID)Eip, sizeof (Mem))))
323     {
324         KdbpPrint("Couldn't access memory at 0x%p\n", Eip);
325         return FALSE;
326     }
327 
328     /* Check if the current instruction is a call. */
329     while ((i < sizeof (Mem)) && (Mem[i] == 0x66 || Mem[i] == 0x67))
330         i++;
331 
332     if (i == sizeof (Mem))
333         return FALSE;
334 
335     if (Mem[i] == 0xE8 || Mem[i] == 0x9A || Mem[i] == 0xF2 || Mem[i] == 0xF3 ||
336         (((i + 1) < sizeof (Mem)) && Mem[i] == 0xFF && (Mem[i+1] & 0x38) == 0x10))
337     {
338         return TRUE;
339     }
340 
341     return FALSE;
342 }
343 
344 /*!\brief Steps over an instruction
345  *
346  * If the given instruction should be stepped over, this function inserts a
347  * temporary breakpoint after the instruction and returns TRUE, otherwise it
348  * returns FALSE.
349  *
350  * \retval TRUE   Temporary breakpoint set after instruction.
351  * \retval FALSE  No breakpoint was set.
352  */
353 BOOLEAN
354 KdbpStepOverInstruction(
355     ULONG_PTR Eip)
356 {
357     LONG InstLen;
358 
359     if (!KdbpShouldStepOverInstruction(Eip))
360         return FALSE;
361 
362     InstLen = KdbpGetInstLength(Eip);
363     if (InstLen < 1)
364         return FALSE;
365 
366     if (!NT_SUCCESS(KdbpInsertBreakPoint(Eip + InstLen, KdbBreakPointTemporary, 0, 0, NULL, FALSE, NULL)))
367         return FALSE;
368 
369     return TRUE;
370 }
371 
372 /*!\brief Steps into an instruction (interrupts)
373  *
374  * If the given instruction should be stepped into, this function inserts a
375  * temporary breakpoint at the target instruction and returns TRUE, otherwise it
376  * returns FALSE.
377  *
378  * \retval TRUE   Temporary breakpoint set at target instruction.
379  * \retval FALSE  No breakpoint was set.
380  */
381 BOOLEAN
382 KdbpStepIntoInstruction(
383     ULONG_PTR Eip)
384 {
385     KDESCRIPTOR Idtr = {0};
386     UCHAR Mem[2];
387     INT IntVect;
388     ULONG IntDesc[2];
389     ULONG_PTR TargetEip;
390 
391     /* Read memory */
392     if (!NT_SUCCESS(KdbpSafeReadMemory(Mem, (PVOID)Eip, sizeof (Mem))))
393     {
394         /*KdbpPrint("Couldn't access memory at 0x%p\n", Eip);*/
395         return FALSE;
396     }
397 
398     /* Check for INT instruction */
399     /* FIXME: Check for iret */
400     if (Mem[0] == 0xcc)
401         IntVect = 3;
402     else if (Mem[0] == 0xcd)
403         IntVect = Mem[1];
404     else if (Mem[0] == 0xce && KdbCurrentTrapFrame->Tf.EFlags & (1<<11)) /* 1 << 11 is the overflow flag */
405         IntVect = 4;
406     else
407         return FALSE;
408 
409     if (IntVect < 32) /* We should be informed about interrupts < 32 by the kernel, no need to breakpoint them */
410     {
411         return FALSE;
412     }
413 
414     /* Read the interrupt descriptor table register  */
415     __sidt(&Idtr.Limit);
416     if (IntVect >= (Idtr.Limit + 1) / 8)
417     {
418         /*KdbpPrint("IDT does not contain interrupt vector %d\n.", IntVect);*/
419         return TRUE;
420     }
421 
422     /* Get the interrupt descriptor */
423     if (!NT_SUCCESS(KdbpSafeReadMemory(IntDesc, (PVOID)(ULONG_PTR)(Idtr.Base + (IntVect * 8)), sizeof (IntDesc))))
424     {
425         /*KdbpPrint("Couldn't access memory at 0x%p\n", (ULONG_PTR)Idtr.Base + (IntVect * 8));*/
426         return FALSE;
427     }
428 
429     /* Check descriptor and get target eip (16 bit interrupt/trap gates not supported) */
430     if ((IntDesc[1] & (1 << 15)) == 0) /* not present */
431     {
432         return FALSE;
433     }
434     if ((IntDesc[1] & 0x1f00) == 0x0500) /* Task gate */
435     {
436         /* FIXME: Task gates not supported */
437         return FALSE;
438     }
439     else if (((IntDesc[1] & 0x1fe0) == 0x0e00) || /* 32 bit Interrupt gate */
440              ((IntDesc[1] & 0x1fe0) == 0x0f00))   /* 32 bit Trap gate */
441     {
442         /* FIXME: Should the segment selector of the interrupt gate be checked? */
443         TargetEip = (IntDesc[1] & 0xffff0000) | (IntDesc[0] & 0x0000ffff);
444     }
445     else
446     {
447         return FALSE;
448     }
449 
450     /* Insert breakpoint */
451     if (!NT_SUCCESS(KdbpInsertBreakPoint(TargetEip, KdbBreakPointTemporary, 0, 0, NULL, FALSE, NULL)))
452         return FALSE;
453 
454     return TRUE;
455 }
456 
457 /*!\brief Gets the number of the next breakpoint >= Start.
458  *
459  * \param Start   Breakpoint number to start searching at. -1 if no more breakpoints are found.
460  *
461  * \returns Breakpoint number (-1 if no more breakpoints are found)
462  */
463 LONG
464 KdbpGetNextBreakPointNr(
465     IN ULONG Start  OPTIONAL)
466 {
467     for (; Start < RTL_NUMBER_OF(KdbBreakPoints); Start++)
468     {
469         if (KdbBreakPoints[Start].Type != KdbBreakPointNone)
470             return Start;
471     }
472 
473     return -1;
474 }
475 
476 /*!\brief Returns information of the specified breakpoint.
477  *
478  * \param BreakPointNr         Number of the breakpoint to return information of.
479  * \param Address              Receives the address of the breakpoint.
480  * \param Type                 Receives the type of the breakpoint (hardware or software)
481  * \param Size                 Size - for memory breakpoints.
482  * \param AccessType           Access type - for hardware breakpoints.
483  * \param DebugReg             Debug register - for enabled hardware breakpoints.
484  * \param Enabled              Whether the breakpoint is enabled or not.
485  * \param Process              The owning process of the breakpoint.
486  * \param ConditionExpression  The expression which was given as condition for the bp.
487  *
488  * \returns NULL on failure, pointer to a KDB_BREAKPOINT struct on success.
489  */
490 BOOLEAN
491 KdbpGetBreakPointInfo(
492     IN  ULONG BreakPointNr,
493     OUT ULONG_PTR *Address  OPTIONAL,
494     OUT KDB_BREAKPOINT_TYPE *Type  OPTIONAL,
495     OUT UCHAR *Size  OPTIONAL,
496     OUT KDB_ACCESS_TYPE *AccessType  OPTIONAL,
497     OUT UCHAR *DebugReg  OPTIONAL,
498     OUT BOOLEAN *Enabled  OPTIONAL,
499     OUT BOOLEAN *Global  OPTIONAL,
500     OUT PEPROCESS *Process  OPTIONAL,
501     OUT PCHAR *ConditionExpression  OPTIONAL)
502 {
503     PKDB_BREAKPOINT bp;
504 
505     if (BreakPointNr >= RTL_NUMBER_OF(KdbBreakPoints) ||
506         KdbBreakPoints[BreakPointNr].Type == KdbBreakPointNone)
507     {
508         return FALSE;
509     }
510 
511     bp = KdbBreakPoints + BreakPointNr;
512     if (Address)
513         *Address = bp->Address;
514 
515     if (Type)
516         *Type = bp->Type;
517 
518     if (bp->Type == KdbBreakPointHardware)
519     {
520         if (Size)
521             *Size = bp->Data.Hw.Size;
522 
523         if (AccessType)
524             *AccessType = bp->Data.Hw.AccessType;
525 
526         if (DebugReg && bp->Enabled)
527             *DebugReg = bp->Data.Hw.DebugReg;
528     }
529 
530     if (Enabled)
531         *Enabled = bp->Enabled;
532 
533     if (Global)
534         *Global = bp->Global;
535 
536     if (Process)
537         *Process = bp->Process;
538 
539     if (ConditionExpression)
540         *ConditionExpression = bp->ConditionExpression;
541 
542     return TRUE;
543 }
544 
545 /*!\brief Inserts a breakpoint into the breakpoint array.
546  *
547  * The \a Process of the breakpoint is set to \a KdbCurrentProcess
548  *
549  * \param Address              Address at which to set the breakpoint.
550  * \param Type                 Type of breakpoint (hardware or software)
551  * \param Size                 Size of breakpoint (for hardware/memory breakpoints)
552  * \param AccessType           Access type (for hardware breakpoins)
553  * \param ConditionExpression  Expression which must evaluate to true for conditional breakpoints.
554  * \param Global               Wether the breakpoint is global or local to a process.
555  * \param BreakPointNumber     Receives the breakpoint number on success
556  *
557  * \returns NTSTATUS
558  */
559 NTSTATUS
560 KdbpInsertBreakPoint(
561     IN  ULONG_PTR Address,
562     IN  KDB_BREAKPOINT_TYPE Type,
563     IN  UCHAR Size  OPTIONAL,
564     IN  KDB_ACCESS_TYPE AccessType  OPTIONAL,
565     IN  PCHAR ConditionExpression  OPTIONAL,
566     IN  BOOLEAN Global,
567     OUT PLONG BreakPointNr  OPTIONAL)
568 {
569     LONG i;
570     PVOID Condition;
571     PCHAR ConditionExpressionDup;
572     LONG ErrOffset;
573     CHAR ErrMsg[128];
574 
575     ASSERT(Type != KdbBreakPointNone);
576 
577     if (Type == KdbBreakPointHardware)
578     {
579         if ((Address % Size) != 0)
580         {
581             KdbpPrint("Address (0x%p) must be aligned to a multiple of the size (%d)\n", Address, Size);
582             return STATUS_UNSUCCESSFUL;
583         }
584 
585         if (AccessType == KdbAccessExec && Size != 1)
586         {
587             KdbpPrint("Size must be 1 for execution breakpoints.\n");
588             return STATUS_UNSUCCESSFUL;
589         }
590     }
591 
592     if (KdbBreakPointCount == KDB_MAXIMUM_BREAKPOINT_COUNT)
593     {
594         return STATUS_UNSUCCESSFUL;
595     }
596 
597     /* Parse conditon expression string and duplicate it */
598     if (ConditionExpression)
599     {
600         Condition = KdbpRpnParseExpression(ConditionExpression, &ErrOffset, ErrMsg);
601         if (!Condition)
602         {
603             if (ErrOffset >= 0)
604                 KdbpPrint("Couldn't parse expression: %s at character %d\n", ErrMsg, ErrOffset);
605             else
606                 KdbpPrint("Couldn't parse expression: %s", ErrMsg);
607 
608             return STATUS_UNSUCCESSFUL;
609         }
610 
611         i = strlen(ConditionExpression) + 1;
612         ConditionExpressionDup = ExAllocatePoolWithTag(NonPagedPool, i, TAG_KDBG);
613         RtlCopyMemory(ConditionExpressionDup, ConditionExpression, i);
614     }
615     else
616     {
617         Condition = NULL;
618         ConditionExpressionDup = NULL;
619     }
620 
621     /* Find unused breakpoint */
622     if (Type == KdbBreakPointTemporary)
623     {
624         for (i = RTL_NUMBER_OF(KdbBreakPoints) - 1; i >= 0; i--)
625         {
626             if (KdbBreakPoints[i].Type == KdbBreakPointNone)
627                 break;
628         }
629     }
630     else
631     {
632         for (i = 0; i < (LONG)RTL_NUMBER_OF(KdbBreakPoints); i++)
633         {
634             if (KdbBreakPoints[i].Type == KdbBreakPointNone)
635                 break;
636         }
637     }
638 
639     ASSERT(i < (LONG)RTL_NUMBER_OF(KdbBreakPoints));
640 
641     /* Set the breakpoint */
642     ASSERT(KdbCurrentProcess);
643     KdbBreakPoints[i].Type = Type;
644     KdbBreakPoints[i].Address = Address;
645     KdbBreakPoints[i].Enabled = FALSE;
646     KdbBreakPoints[i].Global = Global;
647     KdbBreakPoints[i].Process = KdbCurrentProcess;
648     KdbBreakPoints[i].ConditionExpression = ConditionExpressionDup;
649     KdbBreakPoints[i].Condition = Condition;
650 
651     if (Type == KdbBreakPointHardware)
652     {
653         KdbBreakPoints[i].Data.Hw.Size = Size;
654         KdbBreakPoints[i].Data.Hw.AccessType = AccessType;
655     }
656 
657     KdbBreakPointCount++;
658 
659     if (Type != KdbBreakPointTemporary)
660         KdbpPrint("Breakpoint %d inserted.\n", i);
661 
662     /* Try to enable the breakpoint */
663     KdbpEnableBreakPoint(i, NULL);
664 
665     /* Return the breakpoint number */
666     if (BreakPointNr)
667         *BreakPointNr = i;
668 
669     return STATUS_SUCCESS;
670 }
671 
672 /*!\brief Deletes a breakpoint
673  *
674  * \param BreakPointNr  Number of the breakpoint to delete. Can be -1
675  * \param BreakPoint    Breakpoint to delete. Can be NULL.
676  *
677  * \retval TRUE   Success.
678  * \retval FALSE  Failure (invalid breakpoint number)
679  */
680 BOOLEAN
681 KdbpDeleteBreakPoint(
682     IN LONG BreakPointNr  OPTIONAL,
683     IN OUT PKDB_BREAKPOINT BreakPoint  OPTIONAL)
684 {
685     if (BreakPointNr < 0)
686     {
687         ASSERT(BreakPoint);
688         BreakPointNr = BreakPoint - KdbBreakPoints;
689     }
690 
691     if (BreakPointNr < 0 || BreakPointNr >= KDB_MAXIMUM_BREAKPOINT_COUNT)
692     {
693         KdbpPrint("Invalid breakpoint: %d\n", BreakPointNr);
694         return FALSE;
695     }
696 
697     if (!BreakPoint)
698     {
699         BreakPoint = KdbBreakPoints + BreakPointNr;
700     }
701 
702     if (BreakPoint->Type == KdbBreakPointNone)
703     {
704         KdbpPrint("Invalid breakpoint: %d\n", BreakPointNr);
705         return FALSE;
706     }
707 
708     if (BreakPoint->Enabled && !KdbpDisableBreakPoint(-1, BreakPoint))
709         return FALSE;
710 
711     if (BreakPoint->Type != KdbBreakPointTemporary)
712         KdbpPrint("Breakpoint %d deleted.\n", BreakPointNr);
713 
714     BreakPoint->Type = KdbBreakPointNone;
715     KdbBreakPointCount--;
716 
717     return TRUE;
718 }
719 
720 /*!\brief Checks if the breakpoint was set by the debugger
721  *
722  * Tries to find a breakpoint in the breakpoint array which caused
723  * the debug exception to happen.
724  *
725  * \param ExpNr      Exception Number (1 or 3)
726  * \param TrapFrame  Exception trapframe
727  *
728  * \returns Breakpoint number, -1 on error.
729  */
730 static LONG
731 KdbpIsBreakPointOurs(
732     IN NTSTATUS ExceptionCode,
733     IN PKTRAP_FRAME TrapFrame)
734 {
735     ULONG i;
736     ASSERT(ExceptionCode == STATUS_SINGLE_STEP || ExceptionCode == STATUS_BREAKPOINT);
737 
738     if (ExceptionCode == STATUS_BREAKPOINT) /* Software interrupt */
739     {
740         ULONG_PTR BpEip = (ULONG_PTR)TrapFrame->Eip - 1; /* Get EIP of INT3 instruction */
741         for (i = 0; i < KdbSwBreakPointCount; i++)
742         {
743             ASSERT((KdbSwBreakPoints[i]->Type == KdbBreakPointSoftware ||
744                    KdbSwBreakPoints[i]->Type == KdbBreakPointTemporary));
745             ASSERT(KdbSwBreakPoints[i]->Enabled);
746 
747             if (KdbSwBreakPoints[i]->Address == BpEip)
748             {
749                 return KdbSwBreakPoints[i] - KdbBreakPoints;
750             }
751         }
752     }
753     else if (ExceptionCode == STATUS_SINGLE_STEP) /* Hardware interrupt */
754     {
755         UCHAR DebugReg;
756 
757         for (i = 0; i < KdbHwBreakPointCount; i++)
758         {
759             ASSERT(KdbHwBreakPoints[i]->Type == KdbBreakPointHardware &&
760                    KdbHwBreakPoints[i]->Enabled);
761             DebugReg = KdbHwBreakPoints[i]->Data.Hw.DebugReg;
762 
763             if ((TrapFrame->Dr6 & (1 << DebugReg)) != 0)
764             {
765                 return KdbHwBreakPoints[i] - KdbBreakPoints;
766             }
767         }
768     }
769 
770     return -1;
771 }
772 
773 /*!\brief Enables a breakpoint.
774  *
775  * \param BreakPointNr  Number of the breakpoint to enable Can be -1.
776  * \param BreakPoint    Breakpoint to enable. Can be NULL.
777  *
778  * \retval TRUE   Success.
779  * \retval FALSE  Failure.
780  *
781  * \sa KdbpDisableBreakPoint
782  */
783 BOOLEAN
784 KdbpEnableBreakPoint(
785     IN LONG BreakPointNr  OPTIONAL,
786     IN OUT PKDB_BREAKPOINT BreakPoint  OPTIONAL)
787 {
788     NTSTATUS Status;
789     INT i;
790     ULONG ul;
791 
792     if (BreakPointNr < 0)
793     {
794         ASSERT(BreakPoint);
795         BreakPointNr = BreakPoint - KdbBreakPoints;
796     }
797 
798     if (BreakPointNr < 0 || BreakPointNr >= KDB_MAXIMUM_BREAKPOINT_COUNT)
799     {
800         KdbpPrint("Invalid breakpoint: %d\n", BreakPointNr);
801         return FALSE;
802     }
803 
804     if (!BreakPoint)
805     {
806         BreakPoint = KdbBreakPoints + BreakPointNr;
807     }
808 
809     if (BreakPoint->Type == KdbBreakPointNone)
810     {
811         KdbpPrint("Invalid breakpoint: %d\n", BreakPointNr);
812         return FALSE;
813     }
814 
815     if (BreakPoint->Enabled)
816     {
817         KdbpPrint("Breakpoint %d is already enabled.\n", BreakPointNr);
818         return TRUE;
819     }
820 
821     if (BreakPoint->Type == KdbBreakPointSoftware ||
822         BreakPoint->Type == KdbBreakPointTemporary)
823     {
824         if (KdbSwBreakPointCount >= KDB_MAXIMUM_SW_BREAKPOINT_COUNT)
825         {
826             KdbpPrint("Maximum number of SW breakpoints (%d) used. "
827                       "Disable another breakpoint in order to enable this one.\n",
828                       KDB_MAXIMUM_SW_BREAKPOINT_COUNT);
829             return FALSE;
830         }
831 
832         Status = KdbpOverwriteInstruction(BreakPoint->Process, BreakPoint->Address,
833                                           0xCC, &BreakPoint->Data.SavedInstruction);
834         if (!NT_SUCCESS(Status))
835         {
836             KdbpPrint("Couldn't access memory at 0x%p\n", BreakPoint->Address);
837             return FALSE;
838         }
839 
840         KdbSwBreakPoints[KdbSwBreakPointCount++] = BreakPoint;
841     }
842     else
843     {
844         if (BreakPoint->Data.Hw.AccessType == KdbAccessExec)
845             ASSERT(BreakPoint->Data.Hw.Size == 1);
846 
847         ASSERT((BreakPoint->Address % BreakPoint->Data.Hw.Size) == 0);
848 
849         if (KdbHwBreakPointCount >= KDB_MAXIMUM_HW_BREAKPOINT_COUNT)
850         {
851             KdbpPrint("Maximum number of HW breakpoints (%d) already used. "
852                       "Disable another breakpoint in order to enable this one.\n",
853                       KDB_MAXIMUM_HW_BREAKPOINT_COUNT);
854 
855             return FALSE;
856         }
857 
858         /* Find unused hw breakpoint */
859         ASSERT(KDB_MAXIMUM_HW_BREAKPOINT_COUNT == 4);
860         for (i = 0; i < KDB_MAXIMUM_HW_BREAKPOINT_COUNT; i++)
861         {
862             if ((KdbTrapFrame.Tf.Dr7 & (0x3 << (i * 2))) == 0)
863                 break;
864         }
865 
866         ASSERT(i < KDB_MAXIMUM_HW_BREAKPOINT_COUNT);
867 
868         /* Set the breakpoint address. */
869         switch (i)
870         {
871             case 0:
872                 KdbTrapFrame.Tf.Dr0 = BreakPoint->Address;
873                 break;
874             case 1:
875                 KdbTrapFrame.Tf.Dr1 = BreakPoint->Address;
876                 break;
877             case 2:
878                 KdbTrapFrame.Tf.Dr2 = BreakPoint->Address;
879                 break;
880             case 3:
881                 KdbTrapFrame.Tf.Dr3 = BreakPoint->Address;
882                 break;
883         }
884 
885         /* Enable the global breakpoint */
886         KdbTrapFrame.Tf.Dr7 |= (0x2 << (i * 2));
887 
888         /* Enable the exact match bits. */
889         KdbTrapFrame.Tf.Dr7 |= 0x00000300;
890 
891         /* Clear existing state. */
892         KdbTrapFrame.Tf.Dr7 &= ~(0xF << (16 + (i * 4)));
893 
894         /* Set the breakpoint type. */
895         switch (BreakPoint->Data.Hw.AccessType)
896         {
897             case KdbAccessExec:
898                 ul = 0;
899                 break;
900             case KdbAccessWrite:
901                 ul = 1;
902                 break;
903             case KdbAccessRead:
904             case KdbAccessReadWrite:
905                 ul = 3;
906                 break;
907             default:
908                 ASSERT(0);
909                 return TRUE;
910                 break;
911         }
912 
913         KdbTrapFrame.Tf.Dr7 |= (ul << (16 + (i * 4)));
914 
915         /* Set the breakpoint length. */
916         KdbTrapFrame.Tf.Dr7 |= ((BreakPoint->Data.Hw.Size - 1) << (18 + (i * 4)));
917 
918         /* Update KdbCurrentTrapFrame - values are taken from there by the CLI */
919         if (&KdbTrapFrame != KdbCurrentTrapFrame)
920         {
921             KdbCurrentTrapFrame->Tf.Dr0 = KdbTrapFrame.Tf.Dr0;
922             KdbCurrentTrapFrame->Tf.Dr1 = KdbTrapFrame.Tf.Dr1;
923             KdbCurrentTrapFrame->Tf.Dr2 = KdbTrapFrame.Tf.Dr2;
924             KdbCurrentTrapFrame->Tf.Dr3 = KdbTrapFrame.Tf.Dr3;
925             KdbCurrentTrapFrame->Tf.Dr6 = KdbTrapFrame.Tf.Dr6;
926             KdbCurrentTrapFrame->Tf.Dr7 = KdbTrapFrame.Tf.Dr7;
927         }
928 
929         BreakPoint->Data.Hw.DebugReg = i;
930         KdbHwBreakPoints[KdbHwBreakPointCount++] = BreakPoint;
931     }
932 
933     BreakPoint->Enabled = TRUE;
934     if (BreakPoint->Type != KdbBreakPointTemporary)
935         KdbpPrint("Breakpoint %d enabled.\n", BreakPointNr);
936 
937     return TRUE;
938 }
939 
940 /*!\brief Disables a breakpoint.
941  *
942  * \param BreakPointNr  Number of the breakpoint to disable. Can be -1
943  * \param BreakPoint    Breakpoint to disable. Can be NULL.
944  *
945  * \retval TRUE   Success.
946  * \retval FALSE  Failure.
947  *
948  * \sa KdbpEnableBreakPoint
949  */
950 BOOLEAN
951 KdbpDisableBreakPoint(
952     IN LONG BreakPointNr  OPTIONAL,
953     IN OUT PKDB_BREAKPOINT BreakPoint  OPTIONAL)
954 {
955     ULONG i;
956     NTSTATUS Status;
957 
958     if (BreakPointNr < 0)
959     {
960         ASSERT(BreakPoint);
961         BreakPointNr = BreakPoint - KdbBreakPoints;
962     }
963 
964     if (BreakPointNr < 0 || BreakPointNr >= KDB_MAXIMUM_BREAKPOINT_COUNT)
965     {
966         KdbpPrint("Invalid breakpoint: %d\n", BreakPointNr);
967         return FALSE;
968     }
969 
970     if (!BreakPoint)
971     {
972         BreakPoint = KdbBreakPoints + BreakPointNr;
973     }
974 
975     if (BreakPoint->Type == KdbBreakPointNone)
976     {
977         KdbpPrint("Invalid breakpoint: %d\n", BreakPointNr);
978         return FALSE;
979     }
980 
981     if (BreakPoint->Enabled == FALSE)
982     {
983         KdbpPrint("Breakpoint %d is not enabled.\n", BreakPointNr);
984         return TRUE;
985     }
986 
987     if (BreakPoint->Type == KdbBreakPointSoftware ||
988         BreakPoint->Type == KdbBreakPointTemporary)
989     {
990         ASSERT(KdbSwBreakPointCount > 0);
991         Status = KdbpOverwriteInstruction(BreakPoint->Process, BreakPoint->Address,
992                                           BreakPoint->Data.SavedInstruction, NULL);
993 
994         if (!NT_SUCCESS(Status))
995         {
996             KdbpPrint("Couldn't restore original instruction.\n");
997             return FALSE;
998         }
999 
1000         for (i = 0; i < KdbSwBreakPointCount; i++)
1001         {
1002             if (KdbSwBreakPoints[i] == BreakPoint)
1003             {
1004                 KdbSwBreakPoints[i] = KdbSwBreakPoints[--KdbSwBreakPointCount];
1005                 i = -1; /* if the last breakpoint is disabled dont break with i >= KdbSwBreakPointCount */
1006                 break;
1007             }
1008         }
1009 
1010         if (i != MAXULONG) /* not found */
1011             ASSERT(0);
1012     }
1013     else
1014     {
1015         ASSERT(BreakPoint->Type == KdbBreakPointHardware);
1016 
1017         /* Clear the breakpoint. */
1018         KdbTrapFrame.Tf.Dr7 &= ~(0x3 << (BreakPoint->Data.Hw.DebugReg * 2));
1019         if ((KdbTrapFrame.Tf.Dr7 & 0xFF) == 0)
1020         {
1021             /* If no breakpoints are enabled then clear the exact match flags. */
1022             KdbTrapFrame.Tf.Dr7 &= 0xFFFFFCFF;
1023         }
1024 
1025         for (i = 0; i < KdbHwBreakPointCount; i++)
1026         {
1027             if (KdbHwBreakPoints[i] == BreakPoint)
1028             {
1029                 KdbHwBreakPoints[i] = KdbHwBreakPoints[--KdbHwBreakPointCount];
1030                 i = -1; /* if the last breakpoint is disabled dont break with i >= KdbHwBreakPointCount */
1031                 break;
1032             }
1033         }
1034 
1035         if (i != MAXULONG) /* not found */
1036             ASSERT(0);
1037     }
1038 
1039     BreakPoint->Enabled = FALSE;
1040     if (BreakPoint->Type != KdbBreakPointTemporary)
1041         KdbpPrint("Breakpoint %d disabled.\n", BreakPointNr);
1042 
1043     return TRUE;
1044 }
1045 
1046 /*!\brief Gets the first or last chance enter-condition for exception nr. \a ExceptionNr
1047  *
1048  * \param ExceptionNr  Number of the exception to get condition of.
1049  * \param FirstChance  Whether to get first or last chance condition.
1050  * \param Condition    Receives the condition setting.
1051  *
1052  * \retval TRUE   Success.
1053  * \retval FALSE  Failure (invalid exception nr)
1054  */
1055 BOOLEAN
1056 KdbpGetEnterCondition(
1057     IN LONG ExceptionNr,
1058     IN BOOLEAN FirstChance,
1059     OUT KDB_ENTER_CONDITION *Condition)
1060 {
1061     if (ExceptionNr >= (LONG)RTL_NUMBER_OF(KdbEnterConditions))
1062         return FALSE;
1063 
1064     *Condition = KdbEnterConditions[ExceptionNr][FirstChance ? 0 : 1];
1065     return TRUE;
1066 }
1067 
1068 /*!\brief Sets the first or last chance enter-condition for exception nr. \a ExceptionNr
1069  *
1070  * \param ExceptionNr  Number of the exception to set condition of (-1 for all)
1071  * \param FirstChance  Whether to set first or last chance condition.
1072  * \param Condition    The new condition setting.
1073  *
1074  * \retval TRUE   Success.
1075  * \retval FALSE  Failure (invalid exception nr)
1076  */
1077 BOOLEAN
1078 KdbpSetEnterCondition(
1079     IN LONG ExceptionNr,
1080     IN BOOLEAN FirstChance,
1081     IN KDB_ENTER_CONDITION Condition)
1082 {
1083     if (ExceptionNr < 0)
1084     {
1085         for (ExceptionNr = 0; ExceptionNr < (LONG)RTL_NUMBER_OF(KdbEnterConditions); ExceptionNr++)
1086         {
1087             if (ExceptionNr == 1 || ExceptionNr == 8 ||
1088                 ExceptionNr == 9 || ExceptionNr == 15) /* Reserved exceptions */
1089             {
1090                 continue;
1091             }
1092 
1093             KdbEnterConditions[ExceptionNr][FirstChance ? 0 : 1] = Condition;
1094         }
1095     }
1096     else
1097     {
1098         if (ExceptionNr >= (LONG)RTL_NUMBER_OF(KdbEnterConditions) ||
1099             ExceptionNr == 1 || ExceptionNr == 8 || /* Do not allow changing of the debug */
1100             ExceptionNr == 9 || ExceptionNr == 15)  /* trap or reserved exceptions */
1101         {
1102             return FALSE;
1103         }
1104 
1105         KdbEnterConditions[ExceptionNr][FirstChance ? 0 : 1] = Condition;
1106     }
1107 
1108     return TRUE;
1109 }
1110 
1111 /*!\brief Switches to another thread context
1112  *
1113  * \param ThreadId  Id of the thread to switch to.
1114  *
1115  * \retval TRUE   Success.
1116  * \retval FALSE  Failure (i.e. invalid thread id)
1117  */
1118 BOOLEAN
1119 KdbpAttachToThread(
1120     PVOID ThreadId)
1121 {
1122     PETHREAD Thread = NULL;
1123     PEPROCESS Process;
1124 
1125     /* Get a pointer to the thread */
1126     if (!NT_SUCCESS(PsLookupThreadByThreadId(ThreadId, &Thread)))
1127     {
1128         KdbpPrint("Invalid thread id: 0x%08x\n", (ULONG_PTR)ThreadId);
1129         return FALSE;
1130     }
1131     Process = Thread->ThreadsProcess;
1132 
1133     if (KeIsExecutingDpc() && Process != KdbCurrentProcess)
1134     {
1135         KdbpPrint("Cannot attach to thread within another process while executing a DPC.\n");
1136         ObDereferenceObject(Thread);
1137         return FALSE;
1138     }
1139 
1140     /* Save the current thread's context (if we previously attached to a thread) */
1141     if (KdbCurrentThread != KdbOriginalThread)
1142     {
1143         ASSERT(KdbCurrentTrapFrame == &KdbThreadTrapFrame);
1144         /* Actually, we can't save the context, there's no guarantee that there was a trap frame */
1145     }
1146     else
1147     {
1148         ASSERT(KdbCurrentTrapFrame == &KdbTrapFrame);
1149     }
1150 
1151     /* Switch to the thread's context */
1152     if (Thread != KdbOriginalThread)
1153     {
1154         /* The thread we're attaching to isn't the thread on which we entered
1155          * kdb and so the thread we're attaching to is not running. There
1156          * is no guarantee that it actually has a trap frame. So we have to
1157          * peek directly at the registers which were saved on the stack when the
1158          * thread was preempted in the scheduler */
1159         KdbpKdbTrapFrameFromKernelStack(Thread->Tcb.KernelStack,
1160                                         &KdbThreadTrapFrame);
1161         KdbCurrentTrapFrame = &KdbThreadTrapFrame;
1162     }
1163     else /* Switching back to original thread */
1164     {
1165         KdbCurrentTrapFrame = &KdbTrapFrame;
1166     }
1167     KdbCurrentThread = Thread;
1168 
1169     /* Attach to the thread's process */
1170     ASSERT(KdbCurrentProcess == PsGetCurrentProcess());
1171     if (KdbCurrentProcess != Process)
1172     {
1173         if (KdbCurrentProcess != KdbOriginalProcess) /* detach from previously attached process */
1174         {
1175             KeUnstackDetachProcess(&KdbApcState);
1176         }
1177 
1178         if (KdbOriginalProcess != Process)
1179         {
1180             KeStackAttachProcess(&Process->Pcb, &KdbApcState);
1181         }
1182 
1183         KdbCurrentProcess = Process;
1184     }
1185 
1186     ObDereferenceObject(Thread);
1187     return TRUE;
1188 }
1189 
1190 /*!\brief Switches to another process/thread context
1191  *
1192  * This function switches to the first thread in the specified process.
1193  *
1194  * \param ProcessId  Id of the process to switch to.
1195  *
1196  * \retval TRUE   Success.
1197  * \retval FALSE  Failure (i.e. invalid process id)
1198  */
1199 BOOLEAN
1200 KdbpAttachToProcess(
1201     PVOID ProcessId)
1202 {
1203     PEPROCESS Process = NULL;
1204     PETHREAD Thread;
1205     PLIST_ENTRY Entry;
1206 
1207     /* Get a pointer to the process */
1208     if (!NT_SUCCESS(PsLookupProcessByProcessId(ProcessId, &Process)))
1209     {
1210         KdbpPrint("Invalid process id: 0x%08x\n", (ULONG_PTR)ProcessId);
1211         return FALSE;
1212     }
1213 
1214     Entry = Process->ThreadListHead.Flink;
1215     ObDereferenceObject(Process);
1216     if (Entry == &KdbCurrentProcess->ThreadListHead)
1217     {
1218         KdbpPrint("No threads in process 0x%p, cannot attach to process!\n", ProcessId);
1219         return FALSE;
1220     }
1221 
1222     Thread = CONTAINING_RECORD(Entry, ETHREAD, ThreadListEntry);
1223 
1224     return KdbpAttachToThread(Thread->Cid.UniqueThread);
1225 }
1226 
1227 /*!\brief Calls the main loop ...
1228  */
1229 static VOID
1230 KdbpCallMainLoop(VOID)
1231 {
1232     KdbpCliMainLoop(KdbEnteredOnSingleStep);
1233 }
1234 
1235 /*!\brief Internal function to enter KDB.
1236  *
1237  * Disables interrupts, releases display ownership, ...
1238  */
1239 static VOID
1240 KdbpInternalEnter(VOID)
1241 {
1242     PETHREAD Thread;
1243     PVOID SavedInitialStack, SavedStackBase, SavedKernelStack;
1244     ULONG SavedStackLimit;
1245 
1246     KbdDisableMouse();
1247 
1248     if (KdpDebugMode.Screen &&
1249         InbvIsBootDriverInstalled() &&
1250         !InbvCheckDisplayOwnership())
1251     {
1252         /* Acquire ownership and reset the display */
1253         InbvAcquireDisplayOwnership();
1254         InbvResetDisplay();
1255 
1256         /* Display debugger prompt */
1257         InbvSolidColorFill(0, 0, 639, 479, 0);
1258         InbvSetTextColor(15);
1259         InbvInstallDisplayStringFilter(NULL);
1260         InbvEnableDisplayString(TRUE);
1261         InbvSetScrollRegion(0, 0, 639, 479);
1262     }
1263 
1264     /* Call the interface's main loop on a different stack */
1265     Thread = PsGetCurrentThread();
1266     SavedInitialStack = Thread->Tcb.InitialStack;
1267     SavedStackBase = Thread->Tcb.StackBase;
1268     SavedStackLimit = Thread->Tcb.StackLimit;
1269     SavedKernelStack = Thread->Tcb.KernelStack;
1270     Thread->Tcb.InitialStack = Thread->Tcb.StackBase = (char*)KdbStack + KDB_STACK_SIZE;
1271     Thread->Tcb.StackLimit = (ULONG_PTR)KdbStack;
1272     Thread->Tcb.KernelStack = (char*)KdbStack + KDB_STACK_SIZE;
1273 
1274     /*KdbpPrint("Switching to KDB stack 0x%08x-0x%08x (Current Stack is 0x%08x)\n", Thread->Tcb.StackLimit, Thread->Tcb.StackBase, Esp);*/
1275 
1276     KdbpStackSwitchAndCall(KdbStack + KDB_STACK_SIZE - sizeof(ULONG), KdbpCallMainLoop);
1277 
1278     Thread->Tcb.InitialStack = SavedInitialStack;
1279     Thread->Tcb.StackBase = SavedStackBase;
1280     Thread->Tcb.StackLimit = SavedStackLimit;
1281     Thread->Tcb.KernelStack = SavedKernelStack;
1282     KbdEnableMouse();
1283 }
1284 
1285 static ULONG
1286 KdbpGetExceptionNumberFromStatus(
1287     IN NTSTATUS ExceptionCode)
1288 {
1289     ULONG Ret;
1290 
1291     switch (ExceptionCode)
1292     {
1293         case STATUS_INTEGER_DIVIDE_BY_ZERO:
1294             Ret = 0;
1295             break;
1296         case STATUS_SINGLE_STEP:
1297             Ret = 1;
1298             break;
1299         case STATUS_BREAKPOINT:
1300             Ret = 3;
1301             break;
1302         case STATUS_INTEGER_OVERFLOW:
1303             Ret = 4;
1304             break;
1305         case STATUS_ARRAY_BOUNDS_EXCEEDED:
1306             Ret = 5;
1307             break;
1308         case STATUS_ILLEGAL_INSTRUCTION:
1309             Ret = 6;
1310             break;
1311         case STATUS_FLOAT_INVALID_OPERATION:
1312             Ret = 7;
1313             break;
1314         case STATUS_STACK_OVERFLOW:
1315             Ret = 12;
1316             break;
1317         case STATUS_ACCESS_VIOLATION:
1318             Ret = 14;
1319             break;
1320         case STATUS_DATATYPE_MISALIGNMENT:
1321             Ret = 17;
1322             break;
1323         case STATUS_FLOAT_MULTIPLE_TRAPS:
1324             Ret = 18;
1325             break;
1326         case STATUS_ASSERTION_FAILURE:
1327             Ret = 20;
1328             break;
1329 
1330         default:
1331             Ret = RTL_NUMBER_OF(KdbEnterConditions) - 1;
1332             break;
1333     }
1334 
1335     return Ret;
1336 }
1337 
1338 /*!\brief KDB Exception filter
1339  *
1340  * Called by the exception dispatcher.
1341  *
1342  * \param ExceptionRecord  Unused.
1343  * \param PreviousMode     UserMode if the exception was raised from umode, otherwise KernelMode.
1344  * \param Context          Context, IN/OUT parameter.
1345  * \param TrapFrame        Exception TrapFrame.
1346  * \param FirstChance      TRUE when called before exception frames were serached,
1347  *                         FALSE for the second call.
1348  *
1349  * \returns KD_CONTINUE_TYPE
1350  */
1351 KD_CONTINUE_TYPE
1352 KdbEnterDebuggerException(
1353     IN PEXCEPTION_RECORD ExceptionRecord  OPTIONAL,
1354     IN KPROCESSOR_MODE PreviousMode,
1355     IN PCONTEXT Context,
1356     IN OUT PKTRAP_FRAME TrapFrame,
1357     IN BOOLEAN FirstChance)
1358 {
1359     KDB_ENTER_CONDITION EnterCondition;
1360     KD_CONTINUE_TYPE ContinueType = kdHandleException;
1361     PKDB_BREAKPOINT BreakPoint;
1362     ULONG ExpNr;
1363     ULONGLONG ull;
1364     BOOLEAN Resume = FALSE;
1365     BOOLEAN EnterConditionMet = TRUE;
1366     ULONG OldEflags;
1367     KIRQL OldIrql;
1368     NTSTATUS ExceptionCode;
1369 
1370     ExceptionCode = (ExceptionRecord ? ExceptionRecord->ExceptionCode : STATUS_BREAKPOINT);
1371 
1372     KdbCurrentProcess = PsGetCurrentProcess();
1373 
1374     /* Set continue type to kdContinue for single steps and breakpoints */
1375     if (ExceptionCode == STATUS_SINGLE_STEP ||
1376         ExceptionCode == STATUS_BREAKPOINT ||
1377         ExceptionCode == STATUS_ASSERTION_FAILURE)
1378     {
1379         ContinueType = kdContinue;
1380     }
1381 
1382     /* Check if we should handle the exception. */
1383     /* FIXME - won't get all exceptions here :( */
1384     ExpNr = KdbpGetExceptionNumberFromStatus(ExceptionCode);
1385     EnterCondition = KdbEnterConditions[ExpNr][FirstChance ? 0 : 1];
1386     if (EnterCondition == KdbDoNotEnter ||
1387         (EnterCondition == KdbEnterFromUmode && PreviousMode == KernelMode) ||
1388         (EnterCondition == KdbEnterFromKmode && PreviousMode != KernelMode))
1389     {
1390         EnterConditionMet = FALSE;
1391     }
1392 
1393     /* If we stopped on one of our breakpoints then let the user know. */
1394     KdbLastBreakPointNr = -1;
1395     KdbEnteredOnSingleStep = FALSE;
1396 
1397     if (FirstChance && (ExceptionCode == STATUS_SINGLE_STEP || ExceptionCode == STATUS_BREAKPOINT) &&
1398         (KdbLastBreakPointNr = KdbpIsBreakPointOurs(ExceptionCode, TrapFrame)) >= 0)
1399     {
1400         BreakPoint = KdbBreakPoints + KdbLastBreakPointNr;
1401 
1402         if (ExceptionCode == STATUS_BREAKPOINT)
1403         {
1404             /* ... and restore the original instruction. */
1405             if (!NT_SUCCESS(KdbpOverwriteInstruction(KdbCurrentProcess, BreakPoint->Address,
1406                                                      BreakPoint->Data.SavedInstruction, NULL)))
1407             {
1408                 KdbpPrint("Couldn't restore original instruction after INT3! Cannot continue execution.\n");
1409                 KeBugCheck(0); // FIXME: Proper bugcode!
1410             }
1411 
1412             /* Also since we are past the int3 now, decrement EIP in the
1413                TrapFrame. This is only needed because KDBG insists on working
1414                with the TrapFrame instead of with the Context, as it is supposed
1415                to do. The context has already EIP point to the int3, since
1416                KiDispatchException accounts for that. Whatever we do here with
1417                the TrapFrame does not matter anyway, since KiDispatchException
1418                will overwrite it with the values from the Context! */
1419             TrapFrame->Eip--;
1420         }
1421 
1422         if ((BreakPoint->Type == KdbBreakPointHardware) &&
1423             (BreakPoint->Data.Hw.AccessType == KdbAccessExec))
1424         {
1425             Resume = TRUE; /* Set the resume flag when continuing execution */
1426         }
1427 
1428         /*
1429          * When a temporary breakpoint is hit we have to make sure that we are
1430          * in the same context in which it was set, otherwise it could happen
1431          * that another process/thread hits it before and it gets deleted.
1432          */
1433         else if (BreakPoint->Type == KdbBreakPointTemporary &&
1434                  BreakPoint->Process == KdbCurrentProcess)
1435         {
1436             ASSERT((TrapFrame->EFlags & EFLAGS_TF) == 0);
1437 
1438             /* Delete the temporary breakpoint which was used to step over or into the instruction. */
1439             KdbpDeleteBreakPoint(-1, BreakPoint);
1440 
1441             if (--KdbNumSingleSteps > 0)
1442             {
1443                 if ((KdbSingleStepOver && !KdbpStepOverInstruction(TrapFrame->Eip)) ||
1444                     (!KdbSingleStepOver && !KdbpStepIntoInstruction(TrapFrame->Eip)))
1445                 {
1446                     Context->EFlags |= EFLAGS_TF;
1447                 }
1448 
1449                 goto continue_execution; /* return */
1450             }
1451 
1452             KdbEnteredOnSingleStep = TRUE;
1453         }
1454 
1455         /*
1456          * If we hit a breakpoint set by the debugger we set the single step flag,
1457          * ignore the next single step and reenable the breakpoint.
1458          */
1459         else if (BreakPoint->Type == KdbBreakPointSoftware ||
1460                  BreakPoint->Type == KdbBreakPointTemporary)
1461         {
1462             ASSERT(ExceptionCode == STATUS_BREAKPOINT);
1463             Context->EFlags |= EFLAGS_TF;
1464             KdbBreakPointToReenable = BreakPoint;
1465         }
1466 
1467         /* Make sure that the breakpoint should be triggered in this context */
1468         if (!BreakPoint->Global && BreakPoint->Process != KdbCurrentProcess)
1469         {
1470             goto continue_execution; /* return */
1471         }
1472 
1473         /* Check if the condition for the breakpoint is met. */
1474         if (BreakPoint->Condition)
1475         {
1476             /* Setup the KDB trap frame */
1477             KdbpTrapFrameToKdbTrapFrame(TrapFrame, &KdbTrapFrame);
1478 
1479             ull = 0;
1480             if (!KdbpRpnEvaluateParsedExpression(BreakPoint->Condition, &KdbTrapFrame, &ull, NULL, NULL))
1481             {
1482                 /* FIXME: Print warning? */
1483             }
1484             else if (ull == 0) /* condition is not met */
1485             {
1486                 goto continue_execution; /* return */
1487             }
1488         }
1489 
1490         if (BreakPoint->Type == KdbBreakPointSoftware)
1491         {
1492             KdbpPrint("\nEntered debugger on breakpoint #%d: EXEC 0x%04x:0x%08x\n",
1493                       KdbLastBreakPointNr, TrapFrame->SegCs & 0xffff, TrapFrame->Eip);
1494         }
1495         else if (BreakPoint->Type == KdbBreakPointHardware)
1496         {
1497             KdbpPrint("\nEntered debugger on breakpoint #%d: %s 0x%08x\n",
1498                       KdbLastBreakPointNr,
1499                      (BreakPoint->Data.Hw.AccessType == KdbAccessRead) ? "READ" :
1500                      ((BreakPoint->Data.Hw.AccessType == KdbAccessWrite) ? "WRITE" :
1501                      ((BreakPoint->Data.Hw.AccessType == KdbAccessReadWrite) ? "RDWR" : "EXEC")),
1502                      BreakPoint->Address);
1503         }
1504     }
1505     else if (ExceptionCode == STATUS_SINGLE_STEP)
1506     {
1507         /* Silently ignore a debugger initiated single step. */
1508         if ((TrapFrame->Dr6 & 0xf) == 0 && KdbBreakPointToReenable)
1509         {
1510             /* FIXME: Make sure that the breakpoint was really hit (check bp->Address vs. tf->Eip) */
1511             BreakPoint = KdbBreakPointToReenable;
1512             KdbBreakPointToReenable = NULL;
1513             ASSERT(BreakPoint->Type == KdbBreakPointSoftware ||
1514                    BreakPoint->Type == KdbBreakPointTemporary);
1515 
1516             /*
1517              * Reenable the breakpoint we disabled to execute the breakpointed
1518              * instruction.
1519              */
1520             if (!NT_SUCCESS(KdbpOverwriteInstruction(KdbCurrentProcess, BreakPoint->Address, 0xCC,
1521                                                      &BreakPoint->Data.SavedInstruction)))
1522             {
1523                 KdbpPrint("Warning: Couldn't reenable breakpoint %d\n",
1524                           BreakPoint - KdbBreakPoints);
1525             }
1526 
1527             /* Unset TF if we are no longer single stepping. */
1528             if (KdbNumSingleSteps == 0)
1529                 Context->EFlags &= ~EFLAGS_TF;
1530 
1531             if (!KdbpEvenThoughWeHaveABreakPointToReenableWeAlsoHaveARealSingleStep)
1532             {
1533                 goto continue_execution; /* return */
1534             }
1535         }
1536 
1537         /* Quoth the raven, 'Nevermore!' */
1538         KdbpEvenThoughWeHaveABreakPointToReenableWeAlsoHaveARealSingleStep = FALSE;
1539 
1540         /* Check if we expect a single step */
1541         if ((TrapFrame->Dr6 & 0xf) == 0 && KdbNumSingleSteps > 0)
1542         {
1543             /*ASSERT((Context->Eflags & EFLAGS_TF) != 0);*/
1544             if (--KdbNumSingleSteps > 0)
1545             {
1546                 if ((KdbSingleStepOver && KdbpStepOverInstruction(TrapFrame->Eip)) ||
1547                     (!KdbSingleStepOver && KdbpStepIntoInstruction(TrapFrame->Eip)))
1548                 {
1549                     Context->EFlags &= ~EFLAGS_TF;
1550                 }
1551                 else
1552                 {
1553                     Context->EFlags |= EFLAGS_TF;
1554                 }
1555 
1556                 goto continue_execution; /* return */
1557             }
1558             else
1559             {
1560                 Context->EFlags &= ~EFLAGS_TF;
1561                 KdbEnteredOnSingleStep = TRUE;
1562             }
1563         }
1564         else
1565         {
1566             if (!EnterConditionMet)
1567             {
1568                 return kdHandleException;
1569             }
1570 
1571             KdbpPrint("\nEntered debugger on unexpected debug trap!\n");
1572         }
1573     }
1574     else if (ExceptionCode == STATUS_BREAKPOINT)
1575     {
1576         if (KdbInitFileBuffer)
1577         {
1578             KdbpCliInterpretInitFile();
1579             EnterConditionMet = FALSE;
1580         }
1581         if (!EnterConditionMet)
1582         {
1583             return kdHandleException;
1584         }
1585 
1586         KdbpPrint("\nEntered debugger on embedded INT3 at 0x%04x:0x%08x.\n",
1587                   TrapFrame->SegCs & 0xffff, TrapFrame->Eip - 1);
1588     }
1589     else
1590     {
1591         const CHAR *ExceptionString = (ExpNr < RTL_NUMBER_OF(ExceptionNrToString)) ?
1592                                       (ExceptionNrToString[ExpNr]) :
1593                                       ("Unknown/User defined exception");
1594 
1595         if (!EnterConditionMet)
1596         {
1597             return ContinueType;
1598         }
1599 
1600         KdbpPrint("\nEntered debugger on %s-chance exception (Exception Code: 0x%x) (%s)\n",
1601                   FirstChance ? "first" : "last", ExceptionCode, ExceptionString);
1602 
1603         if (ExceptionCode == STATUS_ACCESS_VIOLATION &&
1604             ExceptionRecord && ExceptionRecord->NumberParameters != 0)
1605         {
1606             /* FIXME: Add noexec memory stuff */
1607             ULONG_PTR TrapCr2;
1608             ULONG Err;
1609 
1610             TrapCr2 = __readcr2();
1611 
1612             Err = TrapFrame->ErrCode;
1613             KdbpPrint("Memory at 0x%p could not be %s: ", TrapCr2, (Err & (1 << 1)) ? "written" : "read");
1614 
1615             if ((Err & (1 << 0)) == 0)
1616             {
1617                 KdbpPrint("Page not present.\n");
1618             }
1619             else
1620             {
1621                 if ((Err & (1 << 3)) != 0)
1622                     KdbpPrint("Reserved bits in page directory set.\n");
1623                 else
1624                     KdbpPrint("Page protection violation.\n");
1625             }
1626         }
1627     }
1628 
1629     /* Once we enter the debugger we do not expect any more single steps to happen */
1630     KdbNumSingleSteps = 0;
1631 
1632     /* Update the current process pointer */
1633     KdbCurrentProcess = KdbOriginalProcess = PsGetCurrentProcess();
1634     KdbCurrentThread = KdbOriginalThread = PsGetCurrentThread();
1635     KdbCurrentTrapFrame = &KdbTrapFrame;
1636 
1637     /* Setup the KDB trap frame */
1638     KdbpTrapFrameToKdbTrapFrame(TrapFrame, &KdbTrapFrame);
1639 
1640     /* Enter critical section */
1641     OldEflags = __readeflags();
1642     _disable();
1643 
1644     /* HACK: Save the current IRQL and pretend we are at passive level,
1645      * although interrupts are off. Needed because KDBG calls pageable code. */
1646     OldIrql = KeGetCurrentIrql();
1647     KeLowerIrql(PASSIVE_LEVEL);
1648 
1649     /* Exception inside the debugger? Game over. */
1650     if (InterlockedIncrement(&KdbEntryCount) > 1)
1651     {
1652         __writeeflags(OldEflags);
1653         return kdHandleException;
1654     }
1655 
1656     /* Call the main loop. */
1657     KdbpInternalEnter();
1658 
1659     /* Check if we should single step */
1660     if (KdbNumSingleSteps > 0)
1661     {
1662         /* Variable explains itself! */
1663         KdbpEvenThoughWeHaveABreakPointToReenableWeAlsoHaveARealSingleStep = TRUE;
1664 
1665         if ((KdbSingleStepOver && KdbpStepOverInstruction(KdbCurrentTrapFrame->Tf.Eip)) ||
1666             (!KdbSingleStepOver && KdbpStepIntoInstruction(KdbCurrentTrapFrame->Tf.Eip)))
1667         {
1668             ASSERT((KdbCurrentTrapFrame->Tf.EFlags & EFLAGS_TF) == 0);
1669             /*KdbCurrentTrapFrame->Tf.EFlags &= ~EFLAGS_TF;*/
1670         }
1671         else
1672         {
1673             Context->EFlags |= EFLAGS_TF;
1674         }
1675     }
1676 
1677     /* We can't update the current thread's trapframe 'cause it might not have one */
1678 
1679     /* Detach from attached process */
1680     if (KdbCurrentProcess != KdbOriginalProcess)
1681     {
1682         KeUnstackDetachProcess(&KdbApcState);
1683     }
1684 
1685     /* Update the exception TrapFrame */
1686     KdbpKdbTrapFrameToTrapFrame(&KdbTrapFrame, TrapFrame);
1687 
1688     /* Decrement the entry count */
1689     InterlockedDecrement(&KdbEntryCount);
1690 
1691     /* HACK: Raise back to old IRQL */
1692     KeRaiseIrql(OldIrql, &OldIrql);
1693 
1694     /* Leave critical section */
1695     __writeeflags(OldEflags);
1696 
1697     /* Check if user requested a bugcheck */
1698     if (KdbpBugCheckRequested)
1699     {
1700         /* Clear the flag and bugcheck the system */
1701         KdbpBugCheckRequested = FALSE;
1702         KeBugCheck(MANUALLY_INITIATED_CRASH);
1703     }
1704 
1705 continue_execution:
1706     /* Clear debug status */
1707     if (ExceptionCode == STATUS_BREAKPOINT) /* FIXME: Why clear DR6 on INT3? */
1708     {
1709         /* Set the RF flag so we don't trigger the same breakpoint again. */
1710         if (Resume)
1711         {
1712             TrapFrame->EFlags |= EFLAGS_RF;
1713         }
1714 
1715         /* Clear dr6 status flags. */
1716         TrapFrame->Dr6 &= ~0x0000e00f;
1717 
1718         if (!(KdbEnteredOnSingleStep && KdbSingleStepOver))
1719         {
1720             /* Skip the current instruction */
1721             Context->Eip++;
1722         }
1723     }
1724 
1725     return ContinueType;
1726 }
1727 
1728 VOID
1729 NTAPI
1730 INIT_FUNCTION
1731 KdbpGetCommandLineSettings(
1732     PCHAR p1)
1733 {
1734 #define CONST_STR_LEN(x) (sizeof(x)/sizeof(x[0]) - 1)
1735 
1736     while (p1 && (p1 = strchr(p1, ' ')))
1737     {
1738         /* Skip other spaces */
1739         while (*p1 == ' ') ++p1;
1740 
1741         if (!_strnicmp(p1, "KDSERIAL", CONST_STR_LEN("KDSERIAL")))
1742         {
1743             p1 += CONST_STR_LEN("KDSERIAL");
1744             KdbDebugState |= KD_DEBUG_KDSERIAL;
1745             KdpDebugMode.Serial = TRUE;
1746         }
1747         else if (!_strnicmp(p1, "KDNOECHO", CONST_STR_LEN("KDNOECHO")))
1748         {
1749             p1 += CONST_STR_LEN("KDNOECHO");
1750             KdbDebugState |= KD_DEBUG_KDNOECHO;
1751         }
1752         else if (!_strnicmp(p1, "FIRSTCHANCE", CONST_STR_LEN("FIRSTCHANCE")))
1753         {
1754             p1 += CONST_STR_LEN("FIRSTCHANCE");
1755             KdbpSetEnterCondition(-1, TRUE, KdbEnterAlways);
1756         }
1757     }
1758 }
1759 
1760 NTSTATUS
1761 KdbpSafeReadMemory(
1762     OUT PVOID Dest,
1763     IN PVOID Src,
1764     IN ULONG Bytes)
1765 {
1766     BOOLEAN Result = TRUE;
1767 
1768     switch (Bytes)
1769     {
1770         case 1:
1771         case 2:
1772         case 4:
1773         case 8:
1774             Result = KdpSafeReadMemory((ULONG_PTR)Src, Bytes, Dest);
1775             break;
1776 
1777         default:
1778         {
1779             ULONG_PTR Start, End, Write;
1780 
1781             for (Start = (ULONG_PTR)Src,
1782                     End = Start + Bytes,
1783                     Write = (ULONG_PTR)Dest;
1784                  Result && (Start < End);
1785                  Start++, Write++)
1786                 if (!KdpSafeReadMemory(Start, 1, (PVOID)Write))
1787                     Result = FALSE;
1788 
1789             break;
1790         }
1791     }
1792 
1793     return Result ? STATUS_SUCCESS : STATUS_ACCESS_VIOLATION;
1794 }
1795 
1796 NTSTATUS
1797 KdbpSafeWriteMemory(
1798     OUT PVOID Dest,
1799     IN PVOID Src,
1800     IN ULONG Bytes)
1801 {
1802     BOOLEAN Result = TRUE;
1803     ULONG_PTR Start, End, Write;
1804 
1805     for (Start = (ULONG_PTR)Src,
1806             End = Start + Bytes,
1807             Write = (ULONG_PTR)Dest;
1808          Result && (Start < End);
1809          Start++, Write++)
1810         if (!KdpSafeWriteMemory(Write, 1, *((PCHAR)Start)))
1811             Result = FALSE;
1812 
1813     return Result ? STATUS_SUCCESS : STATUS_ACCESS_VIOLATION;
1814 }
1815