Merge my current work done on the kd++ branch:
[reactos.git] / reactos / ntoskrnl / kd / wrappers / gdbstub.c
1 /****************************************************************************
2
3 THIS SOFTWARE IS NOT COPYRIGHTED
4
5 HP offers the following for use in the public domain. HP makes no
6 warranty with regard to the software or it's performance and the
7 user accepts the software "AS IS" with all faults.
8
9 HP DISCLAIMS ANY WARRANTIES, EXPRESS OR IMPLIED, WITH REGARD
10 TO THIS SOFTWARE INCLUDING BUT NOT LIMITED TO THE WARRANTIES
11 OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
12
13 ****************************************************************************/
14 /****************************************************************************
15 * Contributor: Lake Stevens Instrument Division$
16 *
17 * Description: low level support for gdb debugger. $
18 *
19 * Written by: Glenn Engel $
20 * ModuleState: Experimental $
21 *
22 * Modified for 386 by Jim Kingdon, Cygnus Support.
23 * Modified for ReactOS by Casper S. Hornstrup <chorns@users.sourceforge.net>
24 *
25 ****************************************************************************/
26
27 #include <ntoskrnl.h>
28 #define NDEBUG
29 #include <debug.h>
30
31 /************************************************************************/
32
33 static BOOLEAN GspInitialized;
34 static BOOLEAN GspRemoteDebug;
35
36 static CONST CHAR HexChars[] = "0123456789abcdef";
37
38 static PETHREAD GspRunThread; /* NULL means run all threads */
39 static PETHREAD GspDbgThread;
40 static PETHREAD GspEnumThread;
41
42 static FAST_MUTEX GspLock;
43
44 extern LIST_ENTRY PsActiveProcessHead;
45
46 /* FIXME hardcoded for COM2, 115200 baud */
47 ULONG GdbPortNumber = DEFAULT_DEBUG_PORT;
48 CPPORT GdbPortInfo = {0, DEFAULT_DEBUG_BAUD_RATE, 0};
49
50 static CHAR GspInBuffer[1000];
51 static CHAR GspOutBuffer[1000];
52
53 /* Number of Registers. */
54 #define NUMREGS 16
55
56 enum REGISTER_NAMES
57 {
58 EAX, ECX, EDX, EBX, ESP, EBP, ESI, EDI,
59 PC /* also known as eip */ ,
60 PS /* also known as eflags */ ,
61 CS, SS, DS, ES, FS, GS
62 };
63
64 typedef struct _CPU_REGISTER
65 {
66 ULONG Size;
67 ULONG OffsetInTF;
68 ULONG OffsetInContext;
69 BOOLEAN SetInContext;
70 } CPU_REGISTER, *PCPU_REGISTER;
71
72 static CPU_REGISTER GspRegisters[NUMREGS] =
73 {
74 { 4, FIELD_OFFSET(KTRAP_FRAME, Eax), FIELD_OFFSET(CONTEXT, Eax), TRUE },
75 { 4, FIELD_OFFSET(KTRAP_FRAME, Ecx), FIELD_OFFSET(CONTEXT, Ecx), TRUE },
76 { 4, FIELD_OFFSET(KTRAP_FRAME, Edx), FIELD_OFFSET(CONTEXT, Edx), FALSE },
77 { 4, FIELD_OFFSET(KTRAP_FRAME, Ebx), FIELD_OFFSET(CONTEXT, Ebx), TRUE },
78 { 4, FIELD_OFFSET(KTRAP_FRAME, HardwareEsp), FIELD_OFFSET(CONTEXT, Esp), TRUE },
79 { 4, FIELD_OFFSET(KTRAP_FRAME, DbgEbp), FIELD_OFFSET(CONTEXT, Ebp), TRUE },
80 { 4, FIELD_OFFSET(KTRAP_FRAME, Esi), FIELD_OFFSET(CONTEXT, Esi), TRUE },
81 { 4, FIELD_OFFSET(KTRAP_FRAME, Edi), FIELD_OFFSET(CONTEXT, Edi), TRUE },
82 { 4, FIELD_OFFSET(KTRAP_FRAME, DbgEip), FIELD_OFFSET(CONTEXT, Eip), TRUE },
83 { 4, FIELD_OFFSET(KTRAP_FRAME, EFlags), FIELD_OFFSET(CONTEXT, EFlags), TRUE },
84 { 4, FIELD_OFFSET(KTRAP_FRAME, SegCs), FIELD_OFFSET(CONTEXT, SegCs), TRUE },
85 { 4, FIELD_OFFSET(KTRAP_FRAME, HardwareSegSs), FIELD_OFFSET(CONTEXT, SegSs), TRUE },
86 { 4, FIELD_OFFSET(KTRAP_FRAME, SegDs), FIELD_OFFSET(CONTEXT, SegDs), TRUE },
87 { 4, FIELD_OFFSET(KTRAP_FRAME, SegEs), FIELD_OFFSET(CONTEXT, SegEs), TRUE },
88 { 4, FIELD_OFFSET(KTRAP_FRAME, SegFs), FIELD_OFFSET(CONTEXT, SegFs), TRUE },
89 { 4, FIELD_OFFSET(KTRAP_FRAME, SegGs), FIELD_OFFSET(CONTEXT, SegGs), TRUE }
90 };
91
92 static PCHAR GspThreadStates[DeferredReady + 1] =
93 {
94 "Initialized",
95 "Ready",
96 "Running",
97 "Standby",
98 "Terminated",
99 "Waiting",
100 "Transition",
101 "DeferredReady"
102 };
103
104 LONG
105 HexValue(CHAR ch)
106 {
107 if ((ch >= '0') && (ch <= '9'))
108 return (ch - '0');
109
110 if ((ch >= 'a') && (ch <= 'f'))
111 return (ch - 'a' + 10);
112
113 if ((ch >= 'A') && (ch <= 'F'))
114 return (ch - 'A' + 10);
115
116 return -1;
117 }
118
119 VOID
120 GdbPutChar(UCHAR Value)
121 {
122 KdPortPutByteEx(&GdbPortInfo, Value);
123 }
124
125 UCHAR
126 GdbGetChar(VOID)
127 {
128 UCHAR Value;
129
130 while (!KdPortGetByteEx(&GdbPortInfo, &Value)) ;
131 return Value;
132 }
133
134 /* scan for the sequence $<data>#<Checksum> */
135 PCHAR
136 GspGetPacket()
137 {
138 PCHAR Buffer = &GspInBuffer[0];
139 CHAR Checksum;
140 CHAR XmitChecksum;
141 ULONG Count;
142 CHAR ch;
143
144 while (TRUE)
145 {
146 /* wait around for the start character, ignore all other characters */
147 while ((ch = GdbGetChar()) != '$') ;
148
149 retry:
150 Checksum = 0;
151 XmitChecksum = -1;
152 Count = 0;
153
154 /* now, read until a # or end of Buffer is found */
155 while (Count < sizeof(GspInBuffer) - 1)
156 {
157 ch = GdbGetChar();
158 if (ch == '$')
159 goto retry;
160
161 if (ch == '#')
162 break;
163
164 Checksum = Checksum + ch;
165 Buffer[Count] = ch;
166 Count = Count + 1;
167 }
168 Buffer[Count] = 0;
169
170 if (ch == '#')
171 {
172 ch = GdbGetChar();
173 XmitChecksum = (CHAR)(HexValue(ch) << 4);
174 ch = GdbGetChar();
175 XmitChecksum += (CHAR)(HexValue(ch));
176
177 if (Checksum != XmitChecksum)
178 {
179 GdbPutChar('-'); /* failed checksum */
180 }
181 else
182 {
183 GdbPutChar('+'); /* successful transfer */
184 return &Buffer[0];
185 }
186 }
187 }
188 }
189
190 /* send the packet in Buffer. */
191 VOID
192 GspPutPacket(PCHAR Buffer)
193 {
194 CHAR Checksum;
195 LONG Count;
196 CHAR ch;
197
198 /* $<packet info>#<Checksum>. */
199 do
200 {
201 GdbPutChar('$');
202 Checksum = 0;
203 Count = 0;
204
205 while ((ch = Buffer[Count]))
206 {
207 GdbPutChar(ch);
208 Checksum += ch;
209 Count += 1;
210 }
211
212 GdbPutChar('#');
213 GdbPutChar(HexChars[(Checksum >> 4) & 0xf]);
214 GdbPutChar(HexChars[Checksum & 0xf]);
215 }
216 while (GdbGetChar() != '+');
217 }
218
219 VOID
220 GspPutPacketNoWait(PCHAR Buffer)
221 {
222 CHAR Checksum;
223 LONG Count;
224 CHAR ch;
225
226 /* $<packet info>#<Checksum>. */
227 GdbPutChar('$');
228 Checksum = 0;
229 Count = 0;
230
231 while ((ch = Buffer[Count]))
232 {
233 GdbPutChar(ch);
234 Checksum += ch;
235 Count += 1;
236 }
237
238 GdbPutChar('#');
239 GdbPutChar(HexChars[(Checksum >> 4) & 0xf]);
240 GdbPutChar(HexChars[Checksum & 0xf]);
241 }
242
243 /* Indicate to caller of GspMem2Hex or GspHex2Mem that there has been an error. */
244 static volatile BOOLEAN GspMemoryError = FALSE;
245
246 static CHAR
247 GspReadMemSafe(PCHAR Address)
248 {
249 CHAR ch = 0;
250
251 if (!KdpSafeReadMemory((ULONG_PTR)Address, 1, &ch))
252 GspMemoryError = TRUE;
253
254 return ch;
255 }
256
257 static void
258 GspWriteMemSafe(PCHAR Address, CHAR Ch)
259 {
260 if (!KdpSafeWriteMemory((ULONG_PTR)Address, 1, Ch))
261 GspMemoryError = TRUE;
262 }
263
264 /* Convert the memory pointed to by Address into hex, placing result in Buffer
265 * Return a pointer to the last char put in Buffer (null)
266 * If MayFault is TRUE, then we should set GspMemoryError in response to
267 * a fault; if FALSE treat a fault like any other fault in the stub.
268 */
269 static PCHAR
270 GspMem2Hex(PCHAR Address, PCHAR Buffer, LONG Count, BOOLEAN MayFault)
271 {
272 ULONG i;
273 CHAR ch;
274
275 for (i = 0; i < (ULONG)Count; i++)
276 {
277 if (MayFault)
278 {
279 ch = GspReadMemSafe(Address);
280 if (GspMemoryError)
281 return Buffer;
282 }
283 else
284 {
285 ch = *Address;
286 }
287 *Buffer++ = HexChars[(ch >> 4) & 0xf];
288 *Buffer++ = HexChars[ch & 0xf];
289 Address++;
290 }
291
292 *Buffer = 0;
293 return Buffer;
294 }
295
296 static ULONG
297 GspWriteMem(PCHAR Address, ULONG Count, BOOLEAN MayFault,
298 CHAR (*GetContent)(PVOID Context, ULONG Offset), PVOID Context)
299 {
300 PCHAR Current;
301 PCHAR Page;
302 ULONG CountInPage;
303 ULONG i;
304 CHAR ch;
305
306 Current = Address;
307 while (Current < Address + Count)
308 {
309 Page = (PCHAR)PAGE_ROUND_DOWN(Current);
310 if (Address + Count <= Page + PAGE_SIZE)
311 {
312 /* Fits in this page */
313 CountInPage = Count;
314 }
315 else
316 {
317 /* Flows into next page, handle only current page in this iteration */
318 CountInPage = PAGE_SIZE - (Address - Page);
319 }
320
321 for (i = 0; i < CountInPage && !GspMemoryError; i++)
322 {
323 ch = (*GetContent)(Context, Current - Address);
324
325 if (MayFault)
326 GspWriteMemSafe(Current, ch);
327 else
328 *Current = ch;
329
330 Current++;
331 }
332 if (MayFault)
333 {
334 if (GspMemoryError)
335 return Current - Address;
336 }
337 }
338
339 return Current - Address;
340 }
341
342 static CHAR
343 GspHex2MemGetContent(PVOID Context, ULONG Offset)
344 {
345 return (CHAR)((HexValue(*((PCHAR)Context + 2 * Offset)) << 4) +
346 HexValue(*((PCHAR)Context + 2 * Offset + 1)));
347 }
348
349 /* Convert the hex array pointed to by Buffer into binary to be placed at Address
350 * Return a pointer to the character AFTER the last byte read from Buffer */
351 static PCHAR
352 GspHex2Mem(PCHAR Buffer, PCHAR Address, ULONG Count, BOOLEAN MayFault)
353 {
354 Count = GspWriteMem(Address, Count, MayFault, GspHex2MemGetContent, Buffer);
355 return Buffer + 2 * Count;
356 }
357
358 /**********************************************/
359 /* WHILE WE FIND NICE HEX CHARS, BUILD A LONG */
360 /* RETURN NUMBER OF CHARS PROCESSED */
361 /**********************************************/
362 LONG
363 GspHex2Long(PCHAR *Address, PLONG Value)
364 {
365 LONG NumChars = 0;
366 LONG Hex;
367
368 *Value = 0;
369
370 while (**Address)
371 {
372 Hex = HexValue(**Address);
373 if (Hex >= 0)
374 {
375 *Value = (*Value << 4) | Hex;
376 NumChars++;
377 }
378 else
379 {
380 break;
381 }
382
383 (*Address)++;
384 }
385
386 return NumChars;
387 }
388
389 VOID
390 GspLong2Hex(PCHAR *Address, LONG Value)
391 {
392 LONG Save;
393
394 Save = (((Value >> 0) & 0xff) << 24) | (((Value >> 8) & 0xff) << 16) |
395 (((Value >> 16) & 0xff) << 8) | (((Value >> 24) & 0xff) << 0);
396
397 *Address = GspMem2Hex((PCHAR)&Save, *Address, 4, FALSE);
398 }
399
400 /*
401 * When coming from kernel mode, Esp is not stored in the trap frame.
402 * Instead, it was pointing to the location of the TrapFrame Esp member
403 * when the exception occured. When coming from user mode, Esp is just
404 * stored in the TrapFrame Esp member.
405 */
406 static LONG
407 GspGetEspFromTrapFrame(PKTRAP_FRAME TrapFrame)
408 {
409 return KeGetPreviousMode() == KernelMode ?
410 (LONG)&TrapFrame->HardwareEsp : (LONG)TrapFrame->HardwareEsp;
411 }
412
413 static VOID
414 GspGetRegisters(PCHAR Address, PKTRAP_FRAME TrapFrame)
415 {
416 ULONG_PTR Value;
417 PULONG p;
418 ULONG i;
419 PETHREAD Thread;
420 ULONG_PTR *KernelStack;
421
422 if (NULL == GspDbgThread)
423 {
424 Thread = PsGetCurrentThread();
425 }
426 else
427 {
428 TrapFrame = GspDbgThread->Tcb.TrapFrame;
429 Thread = GspDbgThread;
430 }
431
432 if (Waiting == Thread->Tcb.State)
433 {
434 KernelStack = Thread->Tcb.KernelStack;
435 for (i = 0; i < sizeof(GspRegisters) / sizeof(GspRegisters[0]); i++)
436 {
437 switch (i)
438 {
439 case EBP:
440 Value = KernelStack[3];
441 break;
442 case EDI:
443 Value = KernelStack[4];
444 break;
445 case ESI:
446 Value = KernelStack[5];
447 break;
448 case EBX:
449 Value = KernelStack[6];
450 break;
451 case PC:
452 Value = KernelStack[7];
453 break;
454 case ESP:
455 Value = (ULONG_PTR)(KernelStack + 8);
456 break;
457 case CS:
458 Value = KGDT_R0_CODE;
459 break;
460 case DS:
461 Value = KGDT_R0_DATA;
462 break;
463 default:
464 Value = 0;
465 break;
466 }
467
468 Address = GspMem2Hex((PCHAR)&Value, Address, GspRegisters[i].Size, FALSE);
469 }
470 }
471 else
472 {
473 for (i = 0; i < sizeof(GspRegisters) / sizeof(GspRegisters[0]); i++)
474 {
475 if (TrapFrame)
476 {
477 if (ESP == i)
478 {
479 Value = GspGetEspFromTrapFrame(TrapFrame);
480 }
481 else
482 {
483 p = (PULONG)((ULONG_PTR)TrapFrame + GspRegisters[i].OffsetInTF);
484 Value = *p;
485 }
486 }
487 else if (i == PC)
488 {
489 /*
490 * This thread has not been sheduled yet so assume it
491 * is still in PsBeginThreadWithContextInternal().
492 */
493 Value = (ULONG)KiThreadStartup;
494 }
495 else
496 {
497 Value = 0;
498 }
499
500 Address = GspMem2Hex((PCHAR)&Value, Address, GspRegisters[i].Size, FALSE);
501 }
502 }
503 }
504
505 VOID
506 GspSetRegistersInTrapFrame(PCHAR Address, PCONTEXT Context, PKTRAP_FRAME TrapFrame)
507 {
508 ULONG Value;
509 PCHAR Buffer;
510 PULONG p;
511 ULONG i;
512
513 if (!TrapFrame)
514 return;
515
516 Buffer = Address;
517 for (i = 0; i < NUMREGS; i++)
518 {
519 if (GspRegisters[i].SetInContext)
520 {
521 p = (PULONG)((ULONG_PTR)Context + GspRegisters[i].OffsetInContext);
522 }
523 else
524 {
525 p = (PULONG)((ULONG_PTR)TrapFrame + GspRegisters[i].OffsetInTF);
526 }
527
528 Value = 0;
529 Buffer = GspHex2Mem(Buffer, (PCHAR)&Value, GspRegisters[i].Size, FALSE);
530 *p = Value;
531 }
532 }
533
534 VOID
535 GspSetSingleRegisterInTrapFrame(PCHAR Address, LONG Number, PCONTEXT Context, PKTRAP_FRAME TrapFrame)
536 {
537 ULONG Value;
538 PULONG p;
539
540 if (!TrapFrame)
541 return;
542
543 if (GspRegisters[Number].SetInContext)
544 {
545 p = (PULONG)((ULONG_PTR)Context + GspRegisters[Number].OffsetInContext);
546 }
547 else
548 {
549 p = (PULONG)((ULONG_PTR)TrapFrame + GspRegisters[Number].OffsetInTF);
550 }
551
552 Value = 0;
553 GspHex2Mem(Address, (PCHAR)&Value, GspRegisters[Number].Size, FALSE);
554 *p = Value;
555 }
556
557 BOOLEAN
558 GspFindThread(PCHAR Data, PETHREAD *Thread)
559 {
560 PETHREAD ThreadInfo = NULL;
561
562 if (strcmp(Data, "-1") == 0)
563 {
564 /* All threads */
565 ThreadInfo = NULL;
566 }
567 else
568 {
569 ULONG uThreadId;
570 HANDLE ThreadId;
571 PCHAR ptr = &Data[0];
572
573 GspHex2Long(&ptr, (PLONG)&uThreadId);
574 ThreadId = (HANDLE)uThreadId;
575
576 if (!NT_SUCCESS(PsLookupThreadByThreadId(ThreadId, &ThreadInfo)))
577 {
578 *Thread = NULL;
579 return FALSE;
580 }
581 }
582
583 *Thread = ThreadInfo;
584 return TRUE;
585 }
586
587 VOID
588 GspSetThread(PCHAR Request)
589 {
590 PETHREAD ThreadInfo;
591 PCHAR ptr = &Request[1];
592
593 switch (Request[0])
594 {
595 case 'c': /* Run thread */
596 if (GspFindThread(ptr, &ThreadInfo))
597 {
598 GspOutBuffer[0] = 'O';
599 GspOutBuffer[1] = 'K';
600
601 if (NULL != GspRunThread)
602 ObDereferenceObject(GspRunThread);
603
604 GspRunThread = ThreadInfo;
605
606 if (NULL != GspRunThread)
607 ObReferenceObject(GspRunThread);
608 }
609 else
610 {
611 GspOutBuffer[0] = 'E';
612 }
613 break;
614
615 case 'g': /* Debug thread */
616 if (GspFindThread(ptr, &ThreadInfo))
617 {
618 GspOutBuffer[0] = 'O';
619 GspOutBuffer[1] = 'K';
620
621 if (NULL != GspDbgThread)
622 ObDereferenceObject(GspDbgThread);
623
624 if (ThreadInfo == PsGetCurrentThread())
625 {
626 GspDbgThread = NULL;
627 ObDereferenceObject(ThreadInfo);
628 }
629 else
630 {
631 GspDbgThread = ThreadInfo;
632 }
633 }
634 else
635 {
636 GspOutBuffer[0] = 'E';
637 }
638 break;
639
640 default:
641 break;
642 }
643 }
644
645 VOID
646 GspQuery(PCHAR Request)
647 {
648 ULONG Value;
649
650 if (strncmp(Request, "C", 1) == 0)
651 {
652 PCHAR ptr = &GspOutBuffer[2];
653
654 /* Get current thread id */
655 GspOutBuffer[0] = 'Q';
656 GspOutBuffer[1] = 'C';
657
658 if (NULL != GspDbgThread)
659 Value = (ULONG)GspDbgThread->Cid.UniqueThread;
660 else
661 Value = (ULONG)PsGetCurrentThread()->Cid.UniqueThread;
662
663 GspLong2Hex(&ptr, Value);
664 }
665 else if (strncmp(Request, "fThreadInfo", 11) == 0)
666 {
667 PEPROCESS Process;
668 PLIST_ENTRY AThread, AProcess;
669 PCHAR ptr = &GspOutBuffer[1];
670
671 /* Get first thread id */
672 GspEnumThread = NULL;
673 AProcess = PsActiveProcessHead.Flink;
674 while (AProcess != &PsActiveProcessHead)
675 {
676 Process = CONTAINING_RECORD(AProcess, EPROCESS, ActiveProcessLinks);
677 AThread = Process->ThreadListHead.Flink;
678 if (AThread != &Process->ThreadListHead)
679 {
680 GspEnumThread = CONTAINING_RECORD(Process->ThreadListHead.Flink,
681 ETHREAD, ThreadListEntry);
682 break;
683 }
684 AProcess = AProcess->Flink;
685 }
686 if (GspEnumThread != NULL)
687 {
688 GspOutBuffer[0] = 'm';
689 Value = (ULONG)GspEnumThread->Cid.UniqueThread;
690 GspLong2Hex(&ptr, Value);
691 }
692 else
693 {
694 /* FIXME - what to do here? This case should never happen though, there
695 should always be at least one thread on the system... */
696 /* GspOutBuffer[0] = 'l'; */
697 }
698 }
699 else if (strncmp(Request, "sThreadInfo", 11) == 0)
700 {
701 PEPROCESS Process;
702 PLIST_ENTRY AThread, AProcess;
703 PCHAR ptr = &GspOutBuffer[1];
704
705 /* Get next thread id */
706 if (GspEnumThread != NULL)
707 {
708 /* find the next thread */
709 Process = GspEnumThread->ThreadsProcess;
710 if (GspEnumThread->ThreadListEntry.Flink != &Process->ThreadListHead)
711 {
712 GspEnumThread = CONTAINING_RECORD(GspEnumThread->ThreadListEntry.Flink,
713 ETHREAD, ThreadListEntry);
714 }
715 else
716 {
717 PETHREAD Thread = NULL;
718 AProcess = Process->ActiveProcessLinks.Flink;
719 while (AProcess != &PsActiveProcessHead)
720 {
721 Process = CONTAINING_RECORD(AProcess, EPROCESS, ActiveProcessLinks);
722 AThread = Process->ThreadListHead.Flink;
723 if (AThread != &Process->ThreadListHead)
724 {
725 Thread = CONTAINING_RECORD(Process->ThreadListHead.Flink,
726 ETHREAD, ThreadListEntry);
727 break;
728 }
729 AProcess = AProcess->Flink;
730 }
731 GspEnumThread = Thread;
732 }
733
734 if (GspEnumThread != NULL)
735 {
736 /* return the ID */
737 GspOutBuffer[0] = 'm';
738 Value = (ULONG)GspEnumThread->Cid.UniqueThread;
739 GspLong2Hex(&ptr, Value);
740 }
741 else
742 {
743 GspOutBuffer[0] = 'l';
744 }
745 }
746 else
747 {
748 GspOutBuffer[0] = 'l';
749 }
750 }
751 else if (strncmp(Request, "ThreadExtraInfo", 15) == 0)
752 {
753 PETHREAD ThreadInfo;
754
755 /* Get thread information */
756 if (GspFindThread(Request + 16, &ThreadInfo))
757 {
758 char Buffer[64];
759 PEPROCESS Proc;
760
761 Proc = (PEPROCESS)ThreadInfo->ThreadsProcess;
762
763 Buffer[0] = '\0';
764
765 if (NULL != Proc)
766 sprintf(Buffer, "%s [%d:0x%x], ",
767 Proc->ImageFileName,
768 (int)Proc->UniqueProcessId,
769 (int)ThreadInfo->Cid.UniqueThread);
770
771 strcpy(Buffer + strlen(Buffer), GspThreadStates[ThreadInfo->Tcb.State]);
772
773 ObDereferenceObject(ThreadInfo);
774
775 GspMem2Hex(Buffer, &GspOutBuffer[0], strlen(Buffer), FALSE);
776 }
777 }
778 else if (strncmp(Request, "Supported", 9) == 0)
779 {
780 /* tell maximum incoming packet size */
781 sprintf(GspOutBuffer, "PacketSize=%u", sizeof(GspInBuffer) - 1);
782 }
783 else if (strncmp(Request, "Rcmd,", 5) == 0)
784 {
785 }
786 }
787
788 VOID
789 GspQueryThreadStatus(PCHAR Request)
790 {
791 PETHREAD ThreadInfo;
792 PCHAR ptr = &Request[0];
793
794 if (GspFindThread(ptr, &ThreadInfo))
795 {
796 ObDereferenceObject(ThreadInfo);
797
798 GspOutBuffer[0] = 'O';
799 GspOutBuffer[1] = 'K';
800 GspOutBuffer[2] = '\0';
801 }
802 else
803 {
804 GspOutBuffer[0] = 'E';
805 GspOutBuffer[1] = '\0';
806 }
807 }
808
809 #define DR6_BS 0x00004000 /* Single step */
810
811 #define DR7_L0 0x00000001 /* Local breakpoint 0 enable */
812 #define DR7_G0 0x00000002 /* Global breakpoint 0 enable */
813 #define DR7_L1 0x00000004 /* Local breakpoint 1 enable */
814 #define DR7_G1 0x00000008 /* Global breakpoint 1 enable */
815 #define DR7_L2 0x00000010 /* Local breakpoint 2 enable */
816 #define DR7_G2 0x00000020 /* Global breakpoint 2 enable */
817 #define DR7_L3 0x00000040 /* Local breakpoint 3 enable */
818 #define DR7_G3 0x00000080 /* Global breakpoint 3 enable */
819 #define DR7_LE 0x00000100 /* Local exact breakpoint enable (old) */
820 #define DR7_GE 0x00000200 /* Global exact breakpoint enable (old) */
821 #define DR7_GD 0x00002000 /* General detect enable */
822 #define DR7_TYPE0_MASK 0x00030000 /* Breakpoint 0 condition */
823 #define DR7_LEN0_MASK 0x000c0000 /* Breakpoint 0 length */
824 #define DR7_TYPE1_MASK 0x00300000 /* Breakpoint 1 condition */
825 #define DR7_LEN1_MASK 0x00c00000 /* Breakpoint 1 length */
826 #define DR7_TYPE2_MASK 0x03000000 /* Breakpoint 2 condition */
827 #define DR7_LEN2_MASK 0x0c000000 /* Breakpoint 2 length */
828 #define DR7_TYPE3_MASK 0x30000000 /* Breakpoint 3 condition */
829 #define DR7_LEN3_MASK 0xc0000000 /* Breakpoint 3 length */
830 #define DR7_GLOBAL_ENABLE(Bp) (2 << (2 * (Bp)))
831 #define DR7_TYPE(Bp, Type) ((Type) << (16 + 4 * (Bp)))
832 #define DR7_LEN(Bp, Len) ((Len) << (18 + 4 * (Bp)))
833
834 #define I386_BP_TYPE_EXECUTE 0
835 #define I386_BP_TYPE_DATA_WRITE 1
836 #define I386_BP_TYPE_DATA_READWRITE 3
837
838 #define I386_OPCODE_INT3 0xcc
839
840 #define GDB_ZTYPE_MEMORY_BREAKPOINT 0
841 #define GDB_ZTYPE_HARDWARE_BREAKPOINT 1
842 #define GDB_ZTYPE_WRITE_WATCHPOINT 2
843 #define GDB_ZTYPE_READ_WATCHPOINT 3
844 #define GDB_ZTYPE_ACCESS_WATCHPOINT 4
845
846 typedef struct _GSPHWBREAKPOINT
847 {
848 ULONG Type;
849 ULONG_PTR Address;
850 ULONG Length;
851 } GSPHWBREAKPOINT;
852
853 #define MAX_HW_BREAKPOINTS 4
854 static unsigned GspHwBreakpointCount = 0;
855 static GSPHWBREAKPOINT GspHwBreakpoints[MAX_HW_BREAKPOINTS];
856
857 typedef struct _GSPSWBREAKPOINT
858 {
859 ULONG_PTR Address;
860 CHAR PrevContent;
861 BOOLEAN Active;
862 } GSPSWBREAKPOINT;
863
864 #define MAX_SW_BREAKPOINTS 64
865 static unsigned GspSwBreakpointCount = 0;
866 static GSPSWBREAKPOINT GspSwBreakpoints[MAX_SW_BREAKPOINTS];
867
868 static void
869 GspSetHwBreakpoint(ULONG Type, ULONG_PTR Address, ULONG Length)
870 {
871 DPRINT("GspSetHwBreakpoint(%lu, 0x%p, %lu)\n", Type, Address, Length);
872
873 if (GDB_ZTYPE_READ_WATCHPOINT == Type)
874 {
875 DPRINT1("Read watchpoint not supported\n");
876 strcpy(GspOutBuffer, "E22");
877 }
878 else if (GDB_ZTYPE_HARDWARE_BREAKPOINT == Type && 1 != Length)
879 {
880 DPRINT1("Invalid length %lu for hardware breakpoint\n", Length);
881 strcpy(GspOutBuffer, "E22");
882 }
883 else if (1 != Length && 2 != Length && 4 != Length)
884 {
885 DPRINT1("Invalid length %lu for GDB Z type %lu\n", Length, Type);
886 strcpy(GspOutBuffer, "E22");
887 }
888 else if (0 != (Address & (Length - 1)))
889 {
890 DPRINT1("Invalid alignment for address 0x%p and length %d\n", Address, Length);
891 strcpy(GspOutBuffer, "E22");
892 }
893 else if (MAX_HW_BREAKPOINTS == GspHwBreakpointCount)
894 {
895 DPRINT1("Trying to set too many hardware breakpoints\n");
896 strcpy(GspOutBuffer, "E22");
897 }
898 else
899 {
900 DPRINT("Stored at index %u\n", GspHwBreakpointCount);
901 GspHwBreakpoints[GspHwBreakpointCount].Type = Type;
902 GspHwBreakpoints[GspHwBreakpointCount].Address = Address;
903 GspHwBreakpoints[GspHwBreakpointCount].Length = Length;
904 GspHwBreakpointCount++;
905 strcpy(GspOutBuffer, "OK");
906 }
907 }
908
909 static void
910 GspRemoveHwBreakpoint(ULONG Type, ULONG_PTR Address, ULONG Length)
911 {
912 unsigned Index;
913
914 DPRINT("GspRemoveHwBreakpoint(%lu, 0x%p, %lu)\n", Type, Address, Length);
915 for (Index = 0; Index < GspHwBreakpointCount; Index++)
916 {
917 if (GspHwBreakpoints[Index].Type == Type &&
918 GspHwBreakpoints[Index].Address == Address &&
919 GspHwBreakpoints[Index].Length == Length)
920 {
921 DPRINT("Found match at index %u\n", Index);
922 if (Index + 1 < GspHwBreakpointCount)
923 memmove(GspHwBreakpoints + Index,
924 GspHwBreakpoints + (Index + 1),
925 (GspHwBreakpointCount - Index - 1) * sizeof(GSPHWBREAKPOINT));
926
927 GspHwBreakpointCount--;
928 strcpy(GspOutBuffer, "OK");
929 return;
930 }
931 }
932
933 DPRINT1("Not found\n");
934 strcpy(GspOutBuffer, "E22");
935 }
936
937 static BOOLEAN
938 GspFindSwBreakpoint(ULONG_PTR Address, PULONG PIndex)
939 {
940 ULONG Index;
941
942 for (Index = 0; Index < GspSwBreakpointCount; Index++)
943 {
944 if (GspSwBreakpoints[Index].Address == Address)
945 {
946 if (PIndex)
947 *PIndex = Index;
948 return TRUE;
949 }
950 }
951
952 return FALSE;
953 }
954
955 static void
956 GspSetSwBreakpoint(ULONG_PTR Address)
957 {
958 DPRINT("GspSetSwBreakpoint(0x%p)\n", Address);
959
960 if (MAX_SW_BREAKPOINTS == GspSwBreakpointCount)
961 {
962 DPRINT1("Trying to set too many software breakpoints\n");
963 strcpy(GspOutBuffer, "E22");
964 return;
965 }
966
967 if (GspFindSwBreakpoint(Address, NULL))
968 {
969 strcpy(GspOutBuffer, "E22");
970 return;
971 }
972
973 DPRINT("Stored at index %u\n", GspSwBreakpointCount);
974 GspSwBreakpoints[GspSwBreakpointCount].Address = Address;
975 GspSwBreakpoints[GspSwBreakpointCount].Active = FALSE;
976 GspSwBreakpointCount++;
977 strcpy(GspOutBuffer, "OK");
978 }
979
980 static void
981 GspRemoveSwBreakpoint(ULONG_PTR Address)
982 {
983 ULONG Index;
984
985 DPRINT("GspRemoveSwBreakpoint(0x%p)\n", Address);
986
987 if (GspFindSwBreakpoint(Address, &Index))
988 {
989 DPRINT("Found match at index %u\n", Index);
990 ASSERT(!GspSwBreakpoints[Index].Active);
991
992 if (Index + 1 < GspSwBreakpointCount)
993 memmove(GspSwBreakpoints + Index,
994 GspSwBreakpoints + (Index + 1),
995 (GspSwBreakpointCount - Index - 1) * sizeof(GSPSWBREAKPOINT));
996
997 GspSwBreakpointCount--;
998 strcpy(GspOutBuffer, "OK");
999 return;
1000 }
1001
1002 DPRINT1("Not found\n");
1003 strcpy(GspOutBuffer, "E22");
1004 }
1005
1006 static void
1007 GspLoadHwBreakpoint(PKTRAP_FRAME TrapFrame, unsigned BpIndex,
1008 ULONG_PTR Address, ULONG Length, ULONG Type)
1009 {
1010 DPRINT("GspLoadHwBreakpoint(0x%p, %d, 0x%p, %d)\n",
1011 TrapFrame, BpIndex, Address, Type);
1012
1013 /* Set the DR7_Gx bit to globally enable the breakpoint */
1014 TrapFrame->Dr7 |= DR7_GLOBAL_ENABLE(BpIndex) |
1015 DR7_LEN(BpIndex, Length) |
1016 DR7_TYPE(BpIndex, Type);
1017
1018 switch (BpIndex)
1019 {
1020 case 0:
1021 DPRINT("Setting DR0 to 0x%p\n", Address);
1022 TrapFrame->Dr0 = Address;
1023 break;
1024
1025 case 1:
1026 DPRINT("Setting DR1 to 0x%p\n", Address);
1027 TrapFrame->Dr1 = Address;
1028 break;
1029
1030 case 2:
1031 DPRINT("Setting DR2 to 0x%p\n", Address);
1032 TrapFrame->Dr2 = Address;
1033 break;
1034
1035 case 3:
1036 DPRINT("Setting DR3 to 0x%p\n", Address);
1037 TrapFrame->Dr3 = Address;
1038 break;
1039 }
1040 }
1041
1042 static void
1043 GspLoadSwBreakpoint(ULONG Index)
1044 {
1045 GspMemoryError = FALSE;
1046
1047 GspSwBreakpoints[Index].PrevContent = GspReadMemSafe((PCHAR)GspSwBreakpoints[Index].Address);
1048
1049 if (!GspMemoryError)
1050 GspWriteMemSafe((PCHAR)GspSwBreakpoints[Index].Address, I386_OPCODE_INT3);
1051
1052 GspSwBreakpoints[Index].Active = !GspMemoryError;
1053
1054 if (GspMemoryError)
1055 DPRINT1("Failed to set software breakpoint at 0x%p\n", GspSwBreakpoints[Index].Address);
1056 else
1057 DPRINT("Successfully set software breakpoint at 0x%p\n", GspSwBreakpoints[Index].Address);
1058 }
1059
1060 static void
1061 GspLoadBreakpoints(PKTRAP_FRAME TrapFrame)
1062 {
1063 unsigned Index;
1064 ULONG i386Type;
1065
1066 DPRINT("GspLoadBreakpoints\n");
1067 DPRINT("DR7 on entry: 0x%08x\n", TrapFrame->Dr7);
1068
1069 /* Remove all breakpoints */
1070 TrapFrame->Dr7 &= ~(DR7_L0 | DR7_L1 | DR7_L2 | DR7_L3 |
1071 DR7_G0 | DR7_G1 | DR7_G2 | DR7_G3 |
1072 DR7_TYPE0_MASK | DR7_LEN0_MASK |
1073 DR7_TYPE1_MASK | DR7_LEN1_MASK |
1074 DR7_TYPE2_MASK | DR7_LEN2_MASK |
1075 DR7_TYPE3_MASK | DR7_LEN3_MASK);
1076
1077 for (Index = 0; Index < GspHwBreakpointCount; Index++)
1078 {
1079 switch (GspHwBreakpoints[Index].Type)
1080 {
1081 case GDB_ZTYPE_HARDWARE_BREAKPOINT:
1082 i386Type = I386_BP_TYPE_EXECUTE;
1083 break;
1084 case GDB_ZTYPE_WRITE_WATCHPOINT:
1085 i386Type = I386_BP_TYPE_DATA_WRITE;
1086 break;
1087 case GDB_ZTYPE_ACCESS_WATCHPOINT:
1088 i386Type = I386_BP_TYPE_DATA_READWRITE;
1089 break;
1090 default:
1091 ASSERT(FALSE);
1092 i386Type = I386_BP_TYPE_EXECUTE;
1093 break;
1094 }
1095
1096 GspLoadHwBreakpoint(TrapFrame, Index, GspHwBreakpoints[Index].Address,
1097 GspHwBreakpoints[Index].Length - 1, i386Type);
1098 }
1099
1100 for (Index = 0; Index < GspSwBreakpointCount; Index++)
1101 {
1102 DPRINT("Using real software breakpoint\n");
1103 GspLoadSwBreakpoint(Index);
1104 }
1105
1106 DPRINT("Final DR7 value 0x%08x\n", TrapFrame->Dr7);
1107 }
1108
1109 static void
1110 GspUnloadBreakpoints(void)
1111 {
1112 unsigned Index;
1113
1114 DPRINT("GspUnloadBreakpoints\n");
1115
1116 /* Disable hardware debugging while we are inside the stub */
1117 __writedr(7, 0);
1118
1119 for (Index = 0; Index < GspSwBreakpointCount; Index++)
1120 {
1121 if (GspSwBreakpoints[Index].Active)
1122 {
1123 GspMemoryError = FALSE;
1124 GspWriteMemSafe((PCHAR)GspSwBreakpoints[Index].Address,
1125 GspSwBreakpoints[Index].PrevContent);
1126 GspSwBreakpoints[Index].Active = FALSE;
1127 if (GspMemoryError)
1128 {
1129 DPRINT1("Failed to remove software breakpoint from 0x%p\n",
1130 GspSwBreakpoints[Index].Address);
1131 }
1132 else
1133 {
1134 DPRINT("Successfully removed software breakpoint from 0x%p\n",
1135 GspSwBreakpoints[Index].Address);
1136 }
1137 }
1138 }
1139 }
1140
1141 static void
1142 GspStopReply(NTSTATUS ExceptionCode, PKTRAP_FRAME TrapFrame)
1143 {
1144 PCHAR ptr = GspOutBuffer;
1145 ULONG SigVal;
1146 LONG Esp;
1147
1148 switch (ExceptionCode)
1149 {
1150 case STATUS_INTEGER_DIVIDE_BY_ZERO:
1151 SigVal = 8; /* divide by zero */
1152 break;
1153 case STATUS_SINGLE_STEP:
1154 case STATUS_BREAKPOINT:
1155 SigVal = 5; /* breakpoint */
1156 break;
1157 case STATUS_INTEGER_OVERFLOW:
1158 case STATUS_ARRAY_BOUNDS_EXCEEDED:
1159 SigVal = 16; /* bound instruction */
1160 break;
1161 case STATUS_ILLEGAL_INSTRUCTION:
1162 SigVal = 4; /* Invalid opcode */
1163 break;
1164 case STATUS_STACK_OVERFLOW:
1165 case STATUS_DATATYPE_MISALIGNMENT:
1166 case STATUS_ACCESS_VIOLATION:
1167 SigVal = 11; /* access violation */
1168 break;
1169 default:
1170 SigVal = 7; /* "software generated" */
1171 }
1172
1173 ptr = GspOutBuffer;
1174
1175 *ptr++ = 'T'; /* notify gdb with signo, PC, FP and SP */
1176 *ptr++ = HexChars[(SigVal >> 4) & 0xf];
1177 *ptr++ = HexChars[SigVal & 0xf];
1178
1179 *ptr++ = HexChars[ESP];
1180 *ptr++ = ':';
1181
1182 Esp = GspGetEspFromTrapFrame(TrapFrame); /* SP */
1183 ptr = GspMem2Hex((PCHAR)&Esp, ptr, 4, 0);
1184 *ptr++ = ';';
1185
1186 *ptr++ = HexChars[EBP];
1187 *ptr++ = ':';
1188 ptr = GspMem2Hex((PCHAR)&TrapFrame->Ebp, ptr, 4, 0); /* FP */
1189 *ptr++ = ';';
1190
1191 *ptr++ = HexChars[PC];
1192 *ptr++ = ':';
1193 ptr = GspMem2Hex((PCHAR)&TrapFrame->Eip, ptr, 4, 0); /* PC */
1194 *ptr++ = ';';
1195
1196 *ptr = '\0';
1197 }
1198
1199 /*
1200 * This function does all command procesing for interfacing to GDB.
1201 */
1202 KD_CONTINUE_TYPE
1203 NTAPI
1204 KdpGdbEnterDebuggerException(PEXCEPTION_RECORD ExceptionRecord,
1205 PCONTEXT Context,
1206 PKTRAP_FRAME TrapFrame)
1207 {
1208 static BOOLEAN GdbAttached = FALSE;
1209 BOOLEAN Stepping = FALSE;
1210 NTSTATUS ExceptionCode;
1211 LONG Address;
1212 LONG Length;
1213 PCHAR ptr;
1214
1215 /* FIXME: Stop on other CPUs too */
1216
1217 DPRINT("Thread %p entering stub\n", PsGetCurrentThread());
1218 ExceptionCode = (NTSTATUS)ExceptionRecord->ExceptionCode;
1219
1220 /* Can only debug 1 thread at a time... */
1221 ExAcquireFastMutex(&GspLock);
1222 DPRINT("Thread %p acquired mutex\n", PsGetCurrentThread());
1223
1224 GspUnloadBreakpoints();
1225
1226 /* Make sure we're debugging the current thread. */
1227 if (NULL != GspDbgThread)
1228 {
1229 DPRINT1("Internal error: entering stub with non-NULL GspDbgThread\n");
1230 ObDereferenceObject(GspDbgThread);
1231 GspDbgThread = NULL;
1232 }
1233
1234 if (GdbAttached)
1235 {
1236 GspStopReply(ExceptionCode, TrapFrame);
1237 GspPutPacket(GspOutBuffer);
1238 // DbgPrint(">>> (%s) >>>\n", GspOutBuffer);
1239 }
1240 else
1241 {
1242 GdbAttached = TRUE;
1243 }
1244
1245 while (TRUE)
1246 {
1247 /* Zero the buffer now so we don't have to worry about the terminating zero character */
1248 memset(GspOutBuffer, 0, sizeof(GspOutBuffer));
1249 ptr = GspGetPacket();
1250 // DbgPrint("<<< (%s) <<<\n", ptr);
1251
1252 switch (*ptr++)
1253 {
1254 case '?':
1255 /* a little hack to send more complete status information */
1256 GspStopReply(ExceptionCode, TrapFrame);
1257 break;
1258
1259 case 'd':
1260 GspRemoteDebug = !GspRemoteDebug; /* toggle debug flag */
1261 break;
1262
1263 case 'g': /* return the value of the CPU Registers */
1264 GspGetRegisters(GspOutBuffer, TrapFrame);
1265 break;
1266
1267 case 'G': /* set the value of the CPU Registers - return OK */
1268 if (NULL != GspDbgThread)
1269 GspSetRegistersInTrapFrame(ptr, Context, GspDbgThread->Tcb.TrapFrame);
1270 else
1271 GspSetRegistersInTrapFrame(ptr, Context, TrapFrame);
1272
1273 strcpy(GspOutBuffer, "OK");
1274 break;
1275
1276 case 'P': /* set the value of a single CPU register - return OK */
1277 {
1278 LONG Register;
1279
1280 if ((GspHex2Long(&ptr, &Register)) && (*ptr++ == '='))
1281 {
1282 if ((Register >= 0) && (Register < NUMREGS))
1283 {
1284 if (GspDbgThread)
1285 {
1286 GspSetSingleRegisterInTrapFrame(ptr, Register, Context,
1287 GspDbgThread->Tcb.TrapFrame);
1288 }
1289 else
1290 {
1291 GspSetSingleRegisterInTrapFrame(ptr, Register, Context, TrapFrame);
1292 }
1293 strcpy(GspOutBuffer, "OK");
1294 break;
1295 }
1296 }
1297
1298 strcpy(GspOutBuffer, "E01");
1299 break;
1300 }
1301
1302 /* mAA..AA,LLLL Read LLLL bytes at address AA..AA */
1303 case 'm':
1304 {
1305 /* TRY TO READ %x,%x. IF SUCCEED, SET PTR = 0 */
1306 if (GspHex2Long(&ptr, &Address) && *(ptr++) == ',' && GspHex2Long(&ptr, &Length))
1307 {
1308 PEPROCESS DbgProcess = NULL;
1309
1310 ptr = NULL;
1311 if (NULL != GspDbgThread &&
1312 PsGetCurrentProcess() != GspDbgThread->ThreadsProcess)
1313 {
1314 DbgProcess = GspDbgThread->ThreadsProcess;
1315 KeAttachProcess(&DbgProcess->Pcb);
1316 }
1317
1318 GspMemoryError = FALSE;
1319 GspMem2Hex((PCHAR)Address, GspOutBuffer, Length, 1);
1320
1321 if (NULL != DbgProcess)
1322 KeDetachProcess();
1323
1324 if (GspMemoryError)
1325 {
1326 strcpy(GspOutBuffer, "E03");
1327 DPRINT1("Fault during memory read\n");
1328 }
1329 ptr = NULL;
1330 }
1331
1332 if (NULL != ptr)
1333 strcpy(GspOutBuffer, "E01");
1334
1335 break;
1336 }
1337
1338 /* MAA..AA,LLLL: Write LLLL bytes at address AA.AA return OK */
1339 case 'M':
1340 {
1341 /* TRY TO READ '%x,%x:'. IF SUCCEED, SET PTR = 0 */
1342 if (GspHex2Long(&ptr, &Address))
1343 {
1344 if (*(ptr++) == ',' && GspHex2Long(&ptr, &Length) && *(ptr++) == ':')
1345 {
1346 PEPROCESS DbgProcess = NULL;
1347
1348 if (NULL != GspDbgThread &&
1349 PsGetCurrentProcess() != GspDbgThread->ThreadsProcess)
1350 {
1351 DbgProcess = GspDbgThread->ThreadsProcess;
1352 KeAttachProcess(&DbgProcess->Pcb);
1353 }
1354 GspMemoryError = FALSE;
1355 GspHex2Mem(ptr, (PCHAR)Address, Length, TRUE);
1356 if (NULL != DbgProcess)
1357 KeDetachProcess();
1358
1359 if (GspMemoryError)
1360 {
1361 strcpy(GspOutBuffer, "E03");
1362 DPRINT1("Fault during memory write\n");
1363 }
1364 else
1365 {
1366 strcpy(GspOutBuffer, "OK");
1367 }
1368 ptr = NULL;
1369 }
1370 }
1371
1372 if (NULL != ptr)
1373 strcpy(GspOutBuffer, "E02");
1374
1375 break;
1376 }
1377
1378 /* cAA..AA Continue at address AA..AA */
1379 /* sAA..AA Step one instruction from AA..AA */
1380 case 's':
1381 Stepping = TRUE;
1382 case 'c':
1383 {
1384 ULONG BreakpointNumber;
1385 ULONG Dr6;
1386
1387 /* try to read optional parameter, pc changed if param */
1388 if (GspHex2Long(&ptr, &Address))
1389 {
1390 Context->Eip = Address;
1391 }
1392 else if (ExceptionCode == STATUS_BREAKPOINT)
1393 {
1394 if (GspReadMemSafe((PCHAR)Context->Eip) == (CHAR)I386_OPCODE_INT3)
1395 Context->Eip++;
1396 }
1397
1398 /* clear the trace bit */
1399 Context->EFlags &= ~EFLAGS_TF;
1400
1401 /* set the trace bit if we're Stepping */
1402 if (Stepping)
1403 Context->EFlags |= EFLAGS_TF;
1404
1405 Dr6 = __readdr(6);
1406 if (!(Dr6 & DR6_BS))
1407 {
1408 for (BreakpointNumber = 0;
1409 BreakpointNumber < MAX_HW_BREAKPOINTS;
1410 BreakpointNumber++)
1411 {
1412 if (Dr6 & (1 << BreakpointNumber))
1413 {
1414 if (GspHwBreakpoints[BreakpointNumber].Type == I386_BP_TYPE_EXECUTE)
1415 {
1416 /* Set restore flag */
1417 Context->EFlags |= EFLAGS_RF;
1418 break;
1419 }
1420 }
1421 }
1422 }
1423
1424 GspLoadBreakpoints(TrapFrame);
1425 __writedr(6, 0);
1426
1427 if (NULL != GspDbgThread)
1428 {
1429 ObDereferenceObject(GspDbgThread);
1430 GspDbgThread = NULL;
1431 }
1432
1433 DPRINT("Thread %p releasing mutex\n", PsGetCurrentThread());
1434 ExReleaseFastMutex(&GspLock);
1435 DPRINT("Thread %p leaving stub\n", PsGetCurrentThread());
1436
1437 if (ExceptionCode == STATUS_BREAKPOINT ||
1438 ExceptionCode == STATUS_SINGLE_STEP)
1439 return kdContinue;
1440
1441 return kdHandleException;
1442 }
1443
1444 case 'k': /* kill the program */
1445 strcpy(GspOutBuffer, "OK");
1446 break;
1447
1448 case 'H': /* Set thread */
1449 GspSetThread(ptr);
1450 break;
1451
1452 case 'q': /* Query */
1453 GspQuery(ptr);
1454 break;
1455
1456 case 'T': /* Query thread status */
1457 GspQueryThreadStatus(ptr);
1458 break;
1459
1460 case 'Z':
1461 {
1462 LONG Type;
1463 LONG Address;
1464 LONG Length;
1465
1466 GspHex2Long(&ptr, &Type);
1467 ptr++;
1468 GspHex2Long(&ptr, &Address);
1469 ptr++;
1470 GspHex2Long(&ptr, &Length);
1471
1472 if (0 == Type)
1473 GspSetSwBreakpoint((ULONG_PTR)Address);
1474 else
1475 GspSetHwBreakpoint(Type, (ULONG_PTR)Address, Length);
1476
1477 break;
1478 }
1479
1480 case 'z':
1481 {
1482 LONG Type;
1483 LONG Address;
1484 LONG Length;
1485
1486 GspHex2Long(&ptr, &Type);
1487 ptr++;
1488 GspHex2Long(&ptr, &Address);
1489 ptr++;
1490 GspHex2Long(&ptr, &Length);
1491
1492 if (0 == Type)
1493 GspRemoveSwBreakpoint((ULONG_PTR)Address);
1494 else
1495 GspRemoveHwBreakpoint(Type, (ULONG_PTR)Address, Length);
1496
1497 break;
1498 }
1499
1500 default:
1501 break;
1502 }
1503
1504 /* reply to the request */
1505 GspPutPacket(GspOutBuffer);
1506 // DbgPrint(">>> (%s) >>>\n", GspOutBuffer);
1507 }
1508 }
1509
1510 BOOLEAN
1511 NTAPI
1512 GspBreakIn(PKINTERRUPT Interrupt, PVOID ServiceContext)
1513 {
1514 PKTRAP_FRAME TrapFrame;
1515 BOOLEAN DoBreakIn;
1516 CONTEXT Context;
1517 KIRQL OldIrql;
1518 UCHAR Value;
1519
1520 DPRINT("Break In\n");
1521
1522 DoBreakIn = FALSE;
1523 while (KdPortGetByteEx(&GdbPortInfo, &Value))
1524 {
1525 if (Value == 0x03)
1526 DoBreakIn = TRUE;
1527 }
1528
1529 if (!DoBreakIn)
1530 return TRUE;
1531
1532 KeRaiseIrql(HIGH_LEVEL, &OldIrql);
1533
1534 TrapFrame = PsGetCurrentThread()->Tcb.TrapFrame;
1535
1536 KeTrapFrameToContext(TrapFrame, NULL, &Context);
1537
1538 KdpGdbEnterDebuggerException(NULL, &Context, TrapFrame);
1539
1540 KeContextToTrapFrame(&Context, NULL, TrapFrame, Context.ContextFlags, KernelMode);
1541
1542 KeLowerIrql(OldIrql);
1543
1544 return TRUE;
1545 }
1546
1547 VOID
1548 NTAPI
1549 KdpGdbDebugPrint(PCH Message, ULONG Length)
1550 {
1551 }
1552
1553 /* Initialize the GDB stub */
1554 VOID
1555 NTAPI
1556 KdpGdbStubInit(PKD_DISPATCH_TABLE WrapperTable, ULONG BootPhase)
1557 {
1558 if (!KdDebuggerEnabled || !KdpDebugMode.Gdb)
1559 return;
1560
1561 if (BootPhase == 0)
1562 {
1563 ExInitializeFastMutex(&GspLock);
1564
1565 /* Write out the functions that we support for now */
1566 WrapperTable->KdpInitRoutine = KdpGdbStubInit;
1567 WrapperTable->KdpPrintRoutine = KdpGdbDebugPrint;
1568 WrapperTable->KdpExceptionRoutine = KdpGdbEnterDebuggerException;
1569
1570 /* Initialize the Port */
1571 KdPortInitializeEx(&GdbPortInfo, GdbPortNumber);
1572 }
1573 else if (BootPhase == 1)
1574 {
1575 GspInitialized = TRUE;
1576
1577 GspRunThread = NULL;
1578 GspDbgThread = NULL;
1579 GspEnumThread = NULL;
1580
1581 HalDisplayString("Waiting for GDB to attach\n");
1582 DbgBreakPointWithStatus(DBG_STATUS_CONTROL_C);
1583 }
1584 else if (BootPhase == 2)
1585 {
1586 HalDisplayString("\n GDB debugging enabled\n\n");
1587 }
1588 }
1589
1590 /* EOF */