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