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