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