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