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