1 /****************************************************************************
3 THIS SOFTWARE IS NOT COPYRIGHTED
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.
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.
13 ****************************************************************************/
14 /****************************************************************************
15 * Contributor: Lake Stevens Instrument Division$
17 * Description: low level support for gdb debugger. $
19 * Written by: Glenn Engel $
20 * ModuleState: Experimental $
22 * Modified for 386 by Jim Kingdon, Cygnus Support.
23 * Modified for ReactOS by Casper S. Hornstrup <chorns@users.sourceforge.net>
25 ****************************************************************************/
31 /************************************************************************/
33 static BOOLEAN GspInitialized
;
34 static BOOLEAN GspRemoteDebug
;
36 static CONST CHAR HexChars
[] = "0123456789abcdef";
38 static PETHREAD GspRunThread
; /* NULL means run all threads */
39 static PETHREAD GspDbgThread
;
40 static PETHREAD GspEnumThread
;
42 static FAST_MUTEX GspLock
;
44 extern LIST_ENTRY PsActiveProcessHead
;
46 /* FIXME hardcoded for COM2, 115200 baud */
47 KD_PORT_INFORMATION GdbPortInfo
= { 2, 115200, 0 };
49 static CHAR GspInBuffer
[1000];
50 static CHAR GspOutBuffer
[1000];
52 /* Number of Registers. */
57 EAX
, ECX
, EDX
, EBX
, ESP
, EBP
, ESI
, EDI
,
58 PC
/* also known as eip */ ,
59 PS
/* also known as eflags */ ,
60 CS
, SS
, DS
, ES
, FS
, GS
63 typedef struct _CPU_REGISTER
67 ULONG OffsetInContext
;
69 } CPU_REGISTER
, *PCPU_REGISTER
;
71 static CPU_REGISTER GspRegisters
[NUMREGS
] =
73 { 4, FIELD_OFFSET(KTRAP_FRAME
, Eax
), FIELD_OFFSET(CONTEXT
, Eax
), TRUE
},
74 { 4, FIELD_OFFSET(KTRAP_FRAME
, Ecx
), FIELD_OFFSET(CONTEXT
, Ecx
), TRUE
},
75 { 4, FIELD_OFFSET(KTRAP_FRAME
, Edx
), FIELD_OFFSET(CONTEXT
, Edx
), FALSE
},
76 { 4, FIELD_OFFSET(KTRAP_FRAME
, Ebx
), FIELD_OFFSET(CONTEXT
, Ebx
), TRUE
},
77 { 4, FIELD_OFFSET(KTRAP_FRAME
, HardwareEsp
), FIELD_OFFSET(CONTEXT
, Esp
), TRUE
},
78 { 4, FIELD_OFFSET(KTRAP_FRAME
, DbgEbp
), FIELD_OFFSET(CONTEXT
, Ebp
), TRUE
},
79 { 4, FIELD_OFFSET(KTRAP_FRAME
, Esi
), FIELD_OFFSET(CONTEXT
, Esi
), TRUE
},
80 { 4, FIELD_OFFSET(KTRAP_FRAME
, Edi
), FIELD_OFFSET(CONTEXT
, Edi
), TRUE
},
81 { 4, FIELD_OFFSET(KTRAP_FRAME
, DbgEip
), FIELD_OFFSET(CONTEXT
, Eip
), TRUE
},
82 { 4, FIELD_OFFSET(KTRAP_FRAME
, EFlags
), FIELD_OFFSET(CONTEXT
, EFlags
), TRUE
},
83 { 4, FIELD_OFFSET(KTRAP_FRAME
, SegCs
), FIELD_OFFSET(CONTEXT
, SegCs
), TRUE
},
84 { 4, FIELD_OFFSET(KTRAP_FRAME
, HardwareSegSs
), FIELD_OFFSET(CONTEXT
, SegSs
), TRUE
},
85 { 4, FIELD_OFFSET(KTRAP_FRAME
, SegDs
), FIELD_OFFSET(CONTEXT
, SegDs
), TRUE
},
86 { 4, FIELD_OFFSET(KTRAP_FRAME
, SegEs
), FIELD_OFFSET(CONTEXT
, SegEs
), TRUE
},
87 { 4, FIELD_OFFSET(KTRAP_FRAME
, SegFs
), FIELD_OFFSET(CONTEXT
, SegFs
), TRUE
},
88 { 4, FIELD_OFFSET(KTRAP_FRAME
, SegGs
), FIELD_OFFSET(CONTEXT
, SegGs
), TRUE
}
91 static PCHAR GspThreadStates
[DeferredReady
+ 1] =
106 if ((ch
>= '0') && (ch
<= '9'))
109 if ((ch
>= 'a') && (ch
<= 'f'))
110 return (ch
- 'a' + 10);
112 if ((ch
>= 'A') && (ch
<= 'F'))
113 return (ch
- 'A' + 10);
119 GdbPutChar(UCHAR Value
)
121 KdPortPutByteEx(&GdbPortInfo
, Value
);
129 while (!KdPortGetByteEx(&GdbPortInfo
, &Value
))
135 /* scan for the sequence $<data>#<Checksum> */
139 PCHAR Buffer
= &GspInBuffer
[0];
147 /* wait around for the start character, ignore all other characters */
148 while ((ch
= GdbGetChar()) != '$')
156 /* now, read until a # or end of Buffer is found */
157 while (Count
< sizeof(GspInBuffer
) - 1)
166 Checksum
= Checksum
+ ch
;
175 XmitChecksum
= (CHAR
)(HexValue(ch
) << 4);
177 XmitChecksum
+= (CHAR
)(HexValue(ch
));
179 if (Checksum
!= XmitChecksum
)
181 GdbPutChar('-'); /* failed checksum */
185 GdbPutChar('+'); /* successful transfer */
192 /* send the packet in Buffer. */
194 GspPutPacket(PCHAR Buffer
)
200 /* $<packet info>#<Checksum>. */
207 while ((ch
= Buffer
[Count
]))
215 GdbPutChar(HexChars
[(Checksum
>> 4) & 0xf]);
216 GdbPutChar(HexChars
[Checksum
& 0xf]);
218 while (GdbGetChar() != '+');
222 GspPutPacketNoWait(PCHAR Buffer
)
228 /* $<packet info>#<Checksum>. */
233 while ((ch
= Buffer
[Count
]))
241 GdbPutChar(HexChars
[(Checksum
>> 4) & 0xf]);
242 GdbPutChar(HexChars
[Checksum
& 0xf]);
245 /* Indicate to caller of GspMem2Hex or GspHex2Mem that there has been an error. */
246 static volatile BOOLEAN GspMemoryError
= FALSE
;
249 GspReadMemSafe(PCHAR Address
)
253 if (!KdpSafeReadMemory((ULONG_PTR
)Address
, 1, &ch
))
254 GspMemoryError
= TRUE
;
260 GspWriteMemSafe(PCHAR Address
, CHAR Ch
)
262 if (!KdpSafeWriteMemory((ULONG_PTR
)Address
, 1, Ch
))
263 GspMemoryError
= TRUE
;
266 /* Convert the memory pointed to by Address into hex, placing result in Buffer
267 * Return a pointer to the last char put in Buffer (null)
268 * If MayFault is TRUE, then we should set GspMemoryError in response to
269 * a fault; if FALSE treat a fault like any other fault in the stub.
272 GspMem2Hex(PCHAR Address
, PCHAR Buffer
, LONG Count
, BOOLEAN MayFault
)
277 for (i
= 0; i
< (ULONG
)Count
; i
++)
281 ch
= GspReadMemSafe(Address
);
289 *Buffer
++ = HexChars
[(ch
>> 4) & 0xf];
290 *Buffer
++ = HexChars
[ch
& 0xf];
299 GspWriteMem(PCHAR Address
, ULONG Count
, BOOLEAN MayFault
,
300 CHAR (*GetContent
)(PVOID Context
, ULONG Offset
), PVOID Context
)
309 while (Current
< Address
+ Count
)
311 Page
= (PCHAR
)PAGE_ROUND_DOWN(Current
);
312 if (Address
+ Count
<= Page
+ PAGE_SIZE
)
314 /* Fits in this page */
319 /* Flows into next page, handle only current page in this iteration */
320 CountInPage
= PAGE_SIZE
- (Address
- Page
);
323 for (i
= 0; i
< CountInPage
&& !GspMemoryError
; i
++)
325 ch
= (*GetContent
)(Context
, Current
- Address
);
328 GspWriteMemSafe(Current
, ch
);
337 return Current
- Address
;
341 return Current
- Address
;
345 GspHex2MemGetContent(PVOID Context
, ULONG Offset
)
347 return (CHAR
)((HexValue(*((PCHAR
)Context
+ 2 * Offset
)) << 4) +
348 HexValue(*((PCHAR
)Context
+ 2 * Offset
+ 1)));
351 /* Convert the hex array pointed to by Buffer into binary to be placed at Address
352 * Return a pointer to the character AFTER the last byte read from Buffer */
354 GspHex2Mem(PCHAR Buffer
, PCHAR Address
, ULONG Count
, BOOLEAN MayFault
)
356 Count
= GspWriteMem(Address
, Count
, MayFault
, GspHex2MemGetContent
, Buffer
);
357 return Buffer
+ 2 * Count
;
360 /**********************************************/
361 /* WHILE WE FIND NICE HEX CHARS, BUILD A LONG */
362 /* RETURN NUMBER OF CHARS PROCESSED */
363 /**********************************************/
365 GspHex2Long(PCHAR
*Address
, PLONG Value
)
374 Hex
= HexValue(**Address
);
377 *Value
= (*Value
<< 4) | Hex
;
392 GspLong2Hex(PCHAR
*Address
, LONG Value
)
396 Save
= (((Value
>> 0) & 0xff) << 24) | (((Value
>> 8) & 0xff) << 16) |
397 (((Value
>> 16) & 0xff) << 8) | (((Value
>> 24) & 0xff) << 0);
399 *Address
= GspMem2Hex((PCHAR
)&Save
, *Address
, 4, FALSE
);
403 * When coming from kernel mode, Esp is not stored in the trap frame.
404 * Instead, it was pointing to the location of the TrapFrame Esp member
405 * when the exception occured. When coming from user mode, Esp is just
406 * stored in the TrapFrame Esp member.
409 GspGetEspFromTrapFrame(PKTRAP_FRAME TrapFrame
)
411 return KeGetPreviousMode() == KernelMode
?
412 (LONG
)&TrapFrame
->HardwareEsp
: (LONG
)TrapFrame
->HardwareEsp
;
416 GspGetRegisters(PCHAR Address
, PKTRAP_FRAME TrapFrame
)
422 ULONG_PTR
*KernelStack
;
424 if (NULL
== GspDbgThread
)
426 Thread
= PsGetCurrentThread();
430 TrapFrame
= GspDbgThread
->Tcb
.TrapFrame
;
431 Thread
= GspDbgThread
;
434 if (Waiting
== Thread
->Tcb
.State
)
436 KernelStack
= Thread
->Tcb
.KernelStack
;
437 for (i
= 0; i
< sizeof(GspRegisters
) / sizeof(GspRegisters
[0]); i
++)
442 Value
= KernelStack
[3];
445 Value
= KernelStack
[4];
448 Value
= KernelStack
[5];
451 Value
= KernelStack
[6];
454 Value
= KernelStack
[7];
457 Value
= (ULONG_PTR
)(KernelStack
+ 8);
460 Value
= KGDT_R0_CODE
;
463 Value
= KGDT_R0_DATA
;
470 Address
= GspMem2Hex((PCHAR
)&Value
, Address
, GspRegisters
[i
].Size
, FALSE
);
475 for (i
= 0; i
< sizeof(GspRegisters
) / sizeof(GspRegisters
[0]); i
++)
481 Value
= GspGetEspFromTrapFrame(TrapFrame
);
485 p
= (PULONG
)((ULONG_PTR
)TrapFrame
+ GspRegisters
[i
].OffsetInTF
);
492 * This thread has not been sheduled yet so assume it
493 * is still in PsBeginThreadWithContextInternal().
495 Value
= (ULONG
)KiThreadStartup
;
502 Address
= GspMem2Hex((PCHAR
)&Value
, Address
, GspRegisters
[i
].Size
, FALSE
);
508 GspSetRegistersInTrapFrame(PCHAR Address
, PCONTEXT Context
, PKTRAP_FRAME TrapFrame
)
519 for (i
= 0; i
< NUMREGS
; i
++)
521 if (GspRegisters
[i
].SetInContext
)
523 p
= (PULONG
)((ULONG_PTR
)Context
+ GspRegisters
[i
].OffsetInContext
);
527 p
= (PULONG
)((ULONG_PTR
)TrapFrame
+ GspRegisters
[i
].OffsetInTF
);
531 Buffer
= GspHex2Mem(Buffer
, (PCHAR
)&Value
, GspRegisters
[i
].Size
, FALSE
);
537 GspSetSingleRegisterInTrapFrame(PCHAR Address
, LONG Number
, PCONTEXT Context
, PKTRAP_FRAME TrapFrame
)
545 if (GspRegisters
[Number
].SetInContext
)
547 p
= (PULONG
)((ULONG_PTR
)Context
+ GspRegisters
[Number
].OffsetInContext
);
551 p
= (PULONG
)((ULONG_PTR
)TrapFrame
+ GspRegisters
[Number
].OffsetInTF
);
555 GspHex2Mem(Address
, (PCHAR
)&Value
, GspRegisters
[Number
].Size
, FALSE
);
560 GspFindThread(PCHAR Data
, PETHREAD
*Thread
)
562 PETHREAD ThreadInfo
= NULL
;
564 if (strcmp(Data
, "-1") == 0)
573 PCHAR ptr
= &Data
[0];
575 GspHex2Long(&ptr
, (PLONG
)&uThreadId
);
576 ThreadId
= (HANDLE
)uThreadId
;
578 if (!NT_SUCCESS(PsLookupThreadByThreadId(ThreadId
, &ThreadInfo
)))
585 *Thread
= ThreadInfo
;
590 GspSetThread(PCHAR Request
)
593 PCHAR ptr
= &Request
[1];
597 case 'c': /* Run thread */
598 if (GspFindThread(ptr
, &ThreadInfo
))
600 GspOutBuffer
[0] = 'O';
601 GspOutBuffer
[1] = 'K';
603 if (NULL
!= GspRunThread
)
604 ObDereferenceObject(GspRunThread
);
606 GspRunThread
= ThreadInfo
;
608 if (NULL
!= GspRunThread
)
609 ObReferenceObject(GspRunThread
);
613 GspOutBuffer
[0] = 'E';
617 case 'g': /* Debug thread */
618 if (GspFindThread(ptr
, &ThreadInfo
))
620 GspOutBuffer
[0] = 'O';
621 GspOutBuffer
[1] = 'K';
623 if (NULL
!= GspDbgThread
)
624 ObDereferenceObject(GspDbgThread
);
626 if (ThreadInfo
== PsGetCurrentThread())
629 ObDereferenceObject(ThreadInfo
);
633 GspDbgThread
= ThreadInfo
;
638 GspOutBuffer
[0] = 'E';
648 GspQuery(PCHAR Request
)
652 if (strncmp(Request
, "C", 1) == 0)
654 PCHAR ptr
= &GspOutBuffer
[2];
656 /* Get current thread id */
657 GspOutBuffer
[0] = 'Q';
658 GspOutBuffer
[1] = 'C';
660 if (NULL
!= GspDbgThread
)
661 Value
= (ULONG
)GspDbgThread
->Cid
.UniqueThread
;
663 Value
= (ULONG
)PsGetCurrentThread()->Cid
.UniqueThread
;
665 GspLong2Hex(&ptr
, Value
);
667 else if (strncmp(Request
, "fThreadInfo", 11) == 0)
670 PLIST_ENTRY AThread
, AProcess
;
671 PCHAR ptr
= &GspOutBuffer
[1];
673 /* Get first thread id */
674 GspEnumThread
= NULL
;
675 AProcess
= PsActiveProcessHead
.Flink
;
676 while (AProcess
!= &PsActiveProcessHead
)
678 Process
= CONTAINING_RECORD(AProcess
, EPROCESS
, ActiveProcessLinks
);
679 AThread
= Process
->ThreadListHead
.Flink
;
680 if (AThread
!= &Process
->ThreadListHead
)
682 GspEnumThread
= CONTAINING_RECORD(Process
->ThreadListHead
.Flink
,
683 ETHREAD
, ThreadListEntry
);
686 AProcess
= AProcess
->Flink
;
688 if (GspEnumThread
!= NULL
)
690 GspOutBuffer
[0] = 'm';
691 Value
= (ULONG
)GspEnumThread
->Cid
.UniqueThread
;
692 GspLong2Hex(&ptr
, Value
);
696 /* FIXME - what to do here? This case should never happen though, there
697 should always be at least one thread on the system... */
698 /* GspOutBuffer[0] = 'l'; */
701 else if (strncmp(Request
, "sThreadInfo", 11) == 0)
704 PLIST_ENTRY AThread
, AProcess
;
705 PCHAR ptr
= &GspOutBuffer
[1];
707 /* Get next thread id */
708 if (GspEnumThread
!= NULL
)
710 /* find the next thread */
711 Process
= GspEnumThread
->ThreadsProcess
;
712 if (GspEnumThread
->ThreadListEntry
.Flink
!= &Process
->ThreadListHead
)
714 GspEnumThread
= CONTAINING_RECORD(GspEnumThread
->ThreadListEntry
.Flink
,
715 ETHREAD
, ThreadListEntry
);
719 PETHREAD Thread
= NULL
;
720 AProcess
= Process
->ActiveProcessLinks
.Flink
;
721 while (AProcess
!= &PsActiveProcessHead
)
723 Process
= CONTAINING_RECORD(AProcess
, EPROCESS
, ActiveProcessLinks
);
724 AThread
= Process
->ThreadListHead
.Flink
;
725 if (AThread
!= &Process
->ThreadListHead
)
727 Thread
= CONTAINING_RECORD(Process
->ThreadListHead
.Flink
,
728 ETHREAD
, ThreadListEntry
);
731 AProcess
= AProcess
->Flink
;
733 GspEnumThread
= Thread
;
736 if (GspEnumThread
!= NULL
)
739 GspOutBuffer
[0] = 'm';
740 Value
= (ULONG
)GspEnumThread
->Cid
.UniqueThread
;
741 GspLong2Hex(&ptr
, Value
);
745 GspOutBuffer
[0] = 'l';
750 GspOutBuffer
[0] = 'l';
753 else if (strncmp(Request
, "ThreadExtraInfo", 15) == 0)
757 /* Get thread information */
758 if (GspFindThread(Request
+ 16, &ThreadInfo
))
763 Proc
= (PEPROCESS
)ThreadInfo
->ThreadsProcess
;
768 sprintf(Buffer
, "%s [%d:0x%x], ",
770 (int)Proc
->UniqueProcessId
,
771 (int)ThreadInfo
->Cid
.UniqueThread
);
773 strcpy(Buffer
+ strlen(Buffer
), GspThreadStates
[ThreadInfo
->Tcb
.State
]);
775 ObDereferenceObject(ThreadInfo
);
777 GspMem2Hex(Buffer
, &GspOutBuffer
[0], strlen(Buffer
), FALSE
);
780 else if (strncmp(Request
, "Supported", 9) == 0)
782 /* tell maximum incoming packet size */
783 sprintf(GspOutBuffer
, "PacketSize=%u", sizeof(GspInBuffer
) - 1);
785 else if (strncmp(Request
, "Rcmd,", 5) == 0)
792 GspQueryThreadStatus(PCHAR Request
)
795 PCHAR ptr
= &Request
[0];
797 if (GspFindThread(ptr
, &ThreadInfo
))
799 ObDereferenceObject(ThreadInfo
);
801 GspOutBuffer
[0] = 'O';
802 GspOutBuffer
[1] = 'K';
803 GspOutBuffer
[2] = '\0';
807 GspOutBuffer
[0] = 'E';
808 GspOutBuffer
[1] = '\0';
812 #define DR6_BS 0x00004000 /* Single step */
814 #define DR7_L0 0x00000001 /* Local breakpoint 0 enable */
815 #define DR7_G0 0x00000002 /* Global breakpoint 0 enable */
816 #define DR7_L1 0x00000004 /* Local breakpoint 1 enable */
817 #define DR7_G1 0x00000008 /* Global breakpoint 1 enable */
818 #define DR7_L2 0x00000010 /* Local breakpoint 2 enable */
819 #define DR7_G2 0x00000020 /* Global breakpoint 2 enable */
820 #define DR7_L3 0x00000040 /* Local breakpoint 3 enable */
821 #define DR7_G3 0x00000080 /* Global breakpoint 3 enable */
822 #define DR7_LE 0x00000100 /* Local exact breakpoint enable (old) */
823 #define DR7_GE 0x00000200 /* Global exact breakpoint enable (old) */
824 #define DR7_GD 0x00002000 /* General detect enable */
825 #define DR7_TYPE0_MASK 0x00030000 /* Breakpoint 0 condition */
826 #define DR7_LEN0_MASK 0x000c0000 /* Breakpoint 0 length */
827 #define DR7_TYPE1_MASK 0x00300000 /* Breakpoint 1 condition */
828 #define DR7_LEN1_MASK 0x00c00000 /* Breakpoint 1 length */
829 #define DR7_TYPE2_MASK 0x03000000 /* Breakpoint 2 condition */
830 #define DR7_LEN2_MASK 0x0c000000 /* Breakpoint 2 length */
831 #define DR7_TYPE3_MASK 0x30000000 /* Breakpoint 3 condition */
832 #define DR7_LEN3_MASK 0xc0000000 /* Breakpoint 3 length */
833 #define DR7_GLOBAL_ENABLE(Bp) (2 << (2 * (Bp)))
834 #define DR7_TYPE(Bp, Type) ((Type) << (16 + 4 * (Bp)))
835 #define DR7_LEN(Bp, Len) ((Len) << (18 + 4 * (Bp)))
837 #define I386_BP_TYPE_EXECUTE 0
838 #define I386_BP_TYPE_DATA_WRITE 1
839 #define I386_BP_TYPE_DATA_READWRITE 3
841 #define I386_OPCODE_INT3 0xcc
843 #define GDB_ZTYPE_MEMORY_BREAKPOINT 0
844 #define GDB_ZTYPE_HARDWARE_BREAKPOINT 1
845 #define GDB_ZTYPE_WRITE_WATCHPOINT 2
846 #define GDB_ZTYPE_READ_WATCHPOINT 3
847 #define GDB_ZTYPE_ACCESS_WATCHPOINT 4
849 typedef struct _GSPHWBREAKPOINT
856 #define MAX_HW_BREAKPOINTS 4
857 static unsigned GspHwBreakpointCount
= 0;
858 static GSPHWBREAKPOINT GspHwBreakpoints
[MAX_HW_BREAKPOINTS
];
860 typedef struct _GSPSWBREAKPOINT
867 #define MAX_SW_BREAKPOINTS 64
868 static unsigned GspSwBreakpointCount
= 0;
869 static GSPSWBREAKPOINT GspSwBreakpoints
[MAX_SW_BREAKPOINTS
];
872 GspSetHwBreakpoint(ULONG Type
, ULONG_PTR Address
, ULONG Length
)
874 DPRINT("GspSetHwBreakpoint(%lu, 0x%p, %lu)\n", Type
, Address
, Length
);
876 if (GDB_ZTYPE_READ_WATCHPOINT
== Type
)
878 DPRINT1("Read watchpoint not supported\n");
879 strcpy(GspOutBuffer
, "E22");
881 else if (GDB_ZTYPE_HARDWARE_BREAKPOINT
== Type
&& 1 != Length
)
883 DPRINT1("Invalid length %lu for hardware breakpoint\n", Length
);
884 strcpy(GspOutBuffer
, "E22");
886 else if (1 != Length
&& 2 != Length
&& 4 != Length
)
888 DPRINT1("Invalid length %lu for GDB Z type %lu\n", Length
, Type
);
889 strcpy(GspOutBuffer
, "E22");
891 else if (0 != (Address
& (Length
- 1)))
893 DPRINT1("Invalid alignment for address 0x%p and length %d\n", Address
, Length
);
894 strcpy(GspOutBuffer
, "E22");
896 else if (MAX_HW_BREAKPOINTS
== GspHwBreakpointCount
)
898 DPRINT1("Trying to set too many hardware breakpoints\n");
899 strcpy(GspOutBuffer
, "E22");
903 DPRINT("Stored at index %u\n", GspHwBreakpointCount
);
904 GspHwBreakpoints
[GspHwBreakpointCount
].Type
= Type
;
905 GspHwBreakpoints
[GspHwBreakpointCount
].Address
= Address
;
906 GspHwBreakpoints
[GspHwBreakpointCount
].Length
= Length
;
907 GspHwBreakpointCount
++;
908 strcpy(GspOutBuffer
, "OK");
913 GspRemoveHwBreakpoint(ULONG Type
, ULONG_PTR Address
, ULONG Length
)
917 DPRINT("GspRemoveHwBreakpoint(%lu, 0x%p, %lu)\n", Type
, Address
, Length
);
918 for (Index
= 0; Index
< GspHwBreakpointCount
; Index
++)
920 if (GspHwBreakpoints
[Index
].Type
== Type
&&
921 GspHwBreakpoints
[Index
].Address
== Address
&&
922 GspHwBreakpoints
[Index
].Length
== Length
)
924 DPRINT("Found match at index %u\n", Index
);
925 if (Index
+ 1 < GspHwBreakpointCount
)
926 memmove(GspHwBreakpoints
+ Index
,
927 GspHwBreakpoints
+ (Index
+ 1),
928 (GspHwBreakpointCount
- Index
- 1) * sizeof(GSPHWBREAKPOINT
));
930 GspHwBreakpointCount
--;
931 strcpy(GspOutBuffer
, "OK");
936 DPRINT1("Not found\n");
937 strcpy(GspOutBuffer
, "E22");
941 GspFindSwBreakpoint(ULONG_PTR Address
, PULONG PIndex
)
945 for (Index
= 0; Index
< GspSwBreakpointCount
; Index
++)
946 if (GspSwBreakpoints
[Index
].Address
== Address
)
957 GspSetSwBreakpoint(ULONG_PTR Address
)
959 DPRINT("GspSetSwBreakpoint(0x%p)\n", Address
);
961 if (MAX_SW_BREAKPOINTS
== GspSwBreakpointCount
)
963 DPRINT1("Trying to set too many software breakpoints\n");
964 strcpy(GspOutBuffer
, "E22");
968 if (GspFindSwBreakpoint(Address
, NULL
))
970 strcpy(GspOutBuffer
, "E22");
974 DPRINT("Stored at index %u\n", GspSwBreakpointCount
);
975 GspSwBreakpoints
[GspSwBreakpointCount
].Address
= Address
;
976 GspSwBreakpoints
[GspSwBreakpointCount
].Active
= FALSE
;
977 GspSwBreakpointCount
++;
978 strcpy(GspOutBuffer
, "OK");
982 GspRemoveSwBreakpoint(ULONG_PTR Address
)
986 DPRINT("GspRemoveSwBreakpoint(0x%p)\n", Address
);
988 if (GspFindSwBreakpoint(Address
, &Index
))
990 DPRINT("Found match at index %u\n", Index
);
991 ASSERT(!GspSwBreakpoints
[Index
].Active
);
993 if (Index
+ 1 < GspSwBreakpointCount
)
994 memmove(GspSwBreakpoints
+ Index
,
995 GspSwBreakpoints
+ (Index
+ 1),
996 (GspSwBreakpointCount
- Index
- 1) * sizeof(GSPSWBREAKPOINT
));
998 GspSwBreakpointCount
--;
999 strcpy(GspOutBuffer
, "OK");
1003 DPRINT1("Not found\n");
1004 strcpy(GspOutBuffer
, "E22");
1008 GspLoadHwBreakpoint(PKTRAP_FRAME TrapFrame
, unsigned BpIndex
,
1009 ULONG_PTR Address
, ULONG Length
, ULONG Type
)
1011 DPRINT("GspLoadHwBreakpoint(0x%p, %d, 0x%p, %d)\n",
1012 TrapFrame
, BpIndex
, Address
, Type
);
1014 /* Set the DR7_Gx bit to globally enable the breakpoint */
1015 TrapFrame
->Dr7
|= DR7_GLOBAL_ENABLE(BpIndex
) |
1016 DR7_LEN(BpIndex
, Length
) |
1017 DR7_TYPE(BpIndex
, Type
);
1022 DPRINT("Setting DR0 to 0x%p\n", Address
);
1023 TrapFrame
->Dr0
= Address
;
1027 DPRINT("Setting DR1 to 0x%p\n", Address
);
1028 TrapFrame
->Dr1
= Address
;
1032 DPRINT("Setting DR2 to 0x%p\n", Address
);
1033 TrapFrame
->Dr2
= Address
;
1037 DPRINT("Setting DR3 to 0x%p\n", Address
);
1038 TrapFrame
->Dr3
= Address
;
1044 GspLoadSwBreakpoint(ULONG Index
)
1046 GspMemoryError
= FALSE
;
1048 GspSwBreakpoints
[Index
].PrevContent
= GspReadMemSafe((PCHAR
)GspSwBreakpoints
[Index
].Address
);
1050 if (!GspMemoryError
)
1051 GspWriteMemSafe((PCHAR
)GspSwBreakpoints
[Index
].Address
, I386_OPCODE_INT3
);
1053 GspSwBreakpoints
[Index
].Active
= !GspMemoryError
;
1056 DPRINT1("Failed to set software breakpoint at 0x%p\n", GspSwBreakpoints
[Index
].Address
);
1058 DPRINT("Successfully set software breakpoint at 0x%p\n", GspSwBreakpoints
[Index
].Address
);
1062 GspLoadBreakpoints(PKTRAP_FRAME TrapFrame
)
1067 DPRINT("GspLoadBreakpoints\n");
1068 DPRINT("DR7 on entry: 0x%08x\n", TrapFrame
->Dr7
);
1070 /* Remove all breakpoints */
1071 TrapFrame
->Dr7
&= ~(DR7_L0
| DR7_L1
| DR7_L2
| DR7_L3
|
1072 DR7_G0
| DR7_G1
| DR7_G2
| DR7_G3
|
1073 DR7_TYPE0_MASK
| DR7_LEN0_MASK
|
1074 DR7_TYPE1_MASK
| DR7_LEN1_MASK
|
1075 DR7_TYPE2_MASK
| DR7_LEN2_MASK
|
1076 DR7_TYPE3_MASK
| DR7_LEN3_MASK
);
1078 for (Index
= 0; Index
< GspHwBreakpointCount
; Index
++)
1080 switch (GspHwBreakpoints
[Index
].Type
)
1082 case GDB_ZTYPE_HARDWARE_BREAKPOINT
:
1083 i386Type
= I386_BP_TYPE_EXECUTE
;
1085 case GDB_ZTYPE_WRITE_WATCHPOINT
:
1086 i386Type
= I386_BP_TYPE_DATA_WRITE
;
1088 case GDB_ZTYPE_ACCESS_WATCHPOINT
:
1089 i386Type
= I386_BP_TYPE_DATA_READWRITE
;
1093 i386Type
= I386_BP_TYPE_EXECUTE
;
1097 GspLoadHwBreakpoint(TrapFrame
, Index
, GspHwBreakpoints
[Index
].Address
,
1098 GspHwBreakpoints
[Index
].Length
- 1, i386Type
);
1101 for (Index
= 0; Index
< GspSwBreakpointCount
; Index
++)
1103 DPRINT("Using real software breakpoint\n");
1104 GspLoadSwBreakpoint(Index
);
1107 DPRINT("Final DR7 value 0x%08x\n", TrapFrame
->Dr7
);
1111 GspUnloadBreakpoints(void)
1115 DPRINT("GspUnloadBreakpoints\n");
1117 /* Disable hardware debugging while we are inside the stub */
1120 for (Index
= 0; Index
< GspSwBreakpointCount
; Index
++)
1122 if (GspSwBreakpoints
[Index
].Active
)
1124 GspMemoryError
= FALSE
;
1125 GspWriteMemSafe((PCHAR
)GspSwBreakpoints
[Index
].Address
,
1126 GspSwBreakpoints
[Index
].PrevContent
);
1127 GspSwBreakpoints
[Index
].Active
= FALSE
;
1130 DPRINT1("Failed to remove software breakpoint from 0x%p\n",
1131 GspSwBreakpoints
[Index
].Address
);
1135 DPRINT("Successfully removed software breakpoint from 0x%p\n",
1136 GspSwBreakpoints
[Index
].Address
);
1143 GspStopReply(NTSTATUS ExceptionCode
, PKTRAP_FRAME TrapFrame
)
1145 PCHAR ptr
= GspOutBuffer
;
1149 switch (ExceptionCode
)
1151 case STATUS_INTEGER_DIVIDE_BY_ZERO
:
1152 SigVal
= 8; /* divide by zero */
1154 case STATUS_SINGLE_STEP
:
1155 case STATUS_BREAKPOINT
:
1156 SigVal
= 5; /* breakpoint */
1158 case STATUS_INTEGER_OVERFLOW
:
1159 case STATUS_ARRAY_BOUNDS_EXCEEDED
:
1160 SigVal
= 16; /* bound instruction */
1162 case STATUS_ILLEGAL_INSTRUCTION
:
1163 SigVal
= 4; /* Invalid opcode */
1165 case STATUS_STACK_OVERFLOW
:
1166 case STATUS_DATATYPE_MISALIGNMENT
:
1167 case STATUS_ACCESS_VIOLATION
:
1168 SigVal
= 11; /* access violation */
1171 SigVal
= 7; /* "software generated" */
1176 *ptr
++ = 'T'; /* notify gdb with signo, PC, FP and SP */
1177 *ptr
++ = HexChars
[(SigVal
>> 4) & 0xf];
1178 *ptr
++ = HexChars
[SigVal
& 0xf];
1180 *ptr
++ = HexChars
[ESP
];
1183 Esp
= GspGetEspFromTrapFrame(TrapFrame
); /* SP */
1184 ptr
= GspMem2Hex((PCHAR
)&Esp
, ptr
, 4, 0);
1187 *ptr
++ = HexChars
[EBP
];
1189 ptr
= GspMem2Hex((PCHAR
)&TrapFrame
->Ebp
, ptr
, 4, 0); /* FP */
1192 *ptr
++ = HexChars
[PC
];
1194 ptr
= GspMem2Hex((PCHAR
)&TrapFrame
->Eip
, ptr
, 4, 0); /* PC */
1201 * This function does all command procesing for interfacing to GDB.
1205 KdpGdbEnterDebuggerException(PEXCEPTION_RECORD ExceptionRecord
,
1207 PKTRAP_FRAME TrapFrame
)
1209 static BOOLEAN GdbAttached
= FALSE
;
1210 BOOLEAN Stepping
= FALSE
;
1211 NTSTATUS ExceptionCode
;
1216 /* FIXME: Stop on other CPUs too */
1218 DPRINT("Thread %p entering stub\n", PsGetCurrentThread());
1219 ExceptionCode
= (NTSTATUS
)ExceptionRecord
->ExceptionCode
;
1221 /* Can only debug 1 thread at a time... */
1222 ExAcquireFastMutex(&GspLock
);
1223 DPRINT("Thread %p acquired mutex\n", PsGetCurrentThread());
1225 GspUnloadBreakpoints();
1227 /* Make sure we're debugging the current thread. */
1228 if (NULL
!= GspDbgThread
)
1230 DPRINT1("Internal error: entering stub with non-NULL GspDbgThread\n");
1231 ObDereferenceObject(GspDbgThread
);
1232 GspDbgThread
= NULL
;
1237 GspStopReply(ExceptionCode
, TrapFrame
);
1238 GspPutPacket(GspOutBuffer
);
1239 // DbgPrint(">>> (%s) >>>\n", GspOutBuffer);
1248 /* Zero the buffer now so we don't have to worry about the terminating zero character */
1249 memset(GspOutBuffer
, 0, sizeof(GspOutBuffer
));
1250 ptr
= GspGetPacket();
1251 // DbgPrint("<<< (%s) <<<\n", ptr);
1256 /* a little hack to send more complete status information */
1257 GspStopReply(ExceptionCode
, TrapFrame
);
1261 GspRemoteDebug
= !GspRemoteDebug
; /* toggle debug flag */
1264 case 'g': /* return the value of the CPU Registers */
1265 GspGetRegisters(GspOutBuffer
, TrapFrame
);
1268 case 'G': /* set the value of the CPU Registers - return OK */
1269 if (NULL
!= GspDbgThread
)
1270 GspSetRegistersInTrapFrame(ptr
, Context
, GspDbgThread
->Tcb
.TrapFrame
);
1272 GspSetRegistersInTrapFrame(ptr
, Context
, TrapFrame
);
1274 strcpy(GspOutBuffer
, "OK");
1277 case 'P': /* set the value of a single CPU register - return OK */
1281 if ((GspHex2Long(&ptr
, &Register
)) && (*ptr
++ == '='))
1283 if ((Register
>= 0) && (Register
< NUMREGS
))
1287 GspSetSingleRegisterInTrapFrame(ptr
, Register
, Context
,
1288 GspDbgThread
->Tcb
.TrapFrame
);
1292 GspSetSingleRegisterInTrapFrame(ptr
, Register
, Context
, TrapFrame
);
1294 strcpy(GspOutBuffer
, "OK");
1299 strcpy(GspOutBuffer
, "E01");
1303 /* mAA..AA,LLLL Read LLLL bytes at address AA..AA */
1306 /* TRY TO READ %x,%x. IF SUCCEED, SET PTR = 0 */
1307 if (GspHex2Long(&ptr
, &Address
) && *(ptr
++) == ',' && GspHex2Long(&ptr
, &Length
))
1309 PEPROCESS DbgProcess
= NULL
;
1312 if (NULL
!= GspDbgThread
&&
1313 PsGetCurrentProcess() != GspDbgThread
->ThreadsProcess
)
1315 DbgProcess
= GspDbgThread
->ThreadsProcess
;
1316 KeAttachProcess(&DbgProcess
->Pcb
);
1319 GspMemoryError
= FALSE
;
1320 GspMem2Hex((PCHAR
)Address
, GspOutBuffer
, Length
, 1);
1322 if (NULL
!= DbgProcess
)
1327 strcpy(GspOutBuffer
, "E03");
1328 DPRINT1("Fault during memory read\n");
1334 strcpy(GspOutBuffer
, "E01");
1339 /* MAA..AA,LLLL: Write LLLL bytes at address AA.AA return OK */
1342 /* TRY TO READ '%x,%x:'. IF SUCCEED, SET PTR = 0 */
1343 if (GspHex2Long(&ptr
, &Address
))
1345 if (*(ptr
++) == ',' && GspHex2Long(&ptr
, &Length
) && *(ptr
++) == ':')
1347 PEPROCESS DbgProcess
= NULL
;
1349 if (NULL
!= GspDbgThread
&&
1350 PsGetCurrentProcess() != GspDbgThread
->ThreadsProcess
)
1352 DbgProcess
= GspDbgThread
->ThreadsProcess
;
1353 KeAttachProcess(&DbgProcess
->Pcb
);
1355 GspMemoryError
= FALSE
;
1356 GspHex2Mem(ptr
, (PCHAR
)Address
, Length
, TRUE
);
1357 if (NULL
!= DbgProcess
)
1362 strcpy(GspOutBuffer
, "E03");
1363 DPRINT1("Fault during memory write\n");
1367 strcpy(GspOutBuffer
, "OK");
1374 strcpy(GspOutBuffer
, "E02");
1379 /* cAA..AA Continue at address AA..AA */
1380 /* sAA..AA Step one instruction from AA..AA */
1385 ULONG BreakpointNumber
;
1388 /* try to read optional parameter, pc changed if param */
1389 if (GspHex2Long(&ptr
, &Address
))
1391 Context
->Eip
= Address
;
1393 else if (ExceptionCode
== STATUS_BREAKPOINT
)
1395 if (GspReadMemSafe((PCHAR
)Context
->Eip
) == (CHAR
)I386_OPCODE_INT3
)
1399 /* clear the trace bit */
1400 Context
->EFlags
&= ~EFLAGS_TF
;
1402 /* set the trace bit if we're Stepping */
1404 Context
->EFlags
|= EFLAGS_TF
;
1407 if (!(Dr6
& DR6_BS
))
1409 for (BreakpointNumber
= 0;
1410 BreakpointNumber
< MAX_HW_BREAKPOINTS
;
1413 if (Dr6
& (1 << BreakpointNumber
))
1415 if (GspHwBreakpoints
[BreakpointNumber
].Type
== I386_BP_TYPE_EXECUTE
)
1417 /* Set restore flag */
1418 Context
->EFlags
|= EFLAGS_RF
;
1425 GspLoadBreakpoints(TrapFrame
);
1428 if (NULL
!= GspDbgThread
)
1430 ObDereferenceObject(GspDbgThread
);
1431 GspDbgThread
= NULL
;
1434 DPRINT("Thread %p releasing mutex\n", PsGetCurrentThread());
1435 ExReleaseFastMutex(&GspLock
);
1436 DPRINT("Thread %p leaving stub\n", PsGetCurrentThread());
1438 if (ExceptionCode
== STATUS_BREAKPOINT
||
1439 ExceptionCode
== STATUS_SINGLE_STEP
)
1442 return kdHandleException
;
1445 case 'k': /* kill the program */
1446 strcpy(GspOutBuffer
, "OK");
1449 case 'H': /* Set thread */
1453 case 'q': /* Query */
1457 case 'T': /* Query thread status */
1458 GspQueryThreadStatus(ptr
);
1467 GspHex2Long(&ptr
, &Type
);
1469 GspHex2Long(&ptr
, &Address
);
1471 GspHex2Long(&ptr
, &Length
);
1474 GspSetSwBreakpoint((ULONG_PTR
)Address
);
1476 GspSetHwBreakpoint(Type
, (ULONG_PTR
)Address
, Length
);
1487 GspHex2Long(&ptr
, &Type
);
1489 GspHex2Long(&ptr
, &Address
);
1491 GspHex2Long(&ptr
, &Length
);
1494 GspRemoveSwBreakpoint((ULONG_PTR
)Address
);
1496 GspRemoveHwBreakpoint(Type
, (ULONG_PTR
)Address
, Length
);
1505 /* reply to the request */
1506 GspPutPacket(GspOutBuffer
);
1507 // DbgPrint(">>> (%s) >>>\n", GspOutBuffer);
1513 GspBreakIn(PKINTERRUPT Interrupt
, PVOID ServiceContext
)
1515 PKTRAP_FRAME TrapFrame
;
1521 DPRINT("Break In\n");
1524 while (KdPortGetByteEx(&GdbPortInfo
, &Value
))
1533 KeRaiseIrql(HIGH_LEVEL
, &OldIrql
);
1535 TrapFrame
= PsGetCurrentThread()->Tcb
.TrapFrame
;
1537 KeTrapFrameToContext(TrapFrame
, NULL
, &Context
);
1539 KdpGdbEnterDebuggerException(NULL
, &Context
, TrapFrame
);
1541 KeContextToTrapFrame(&Context
, NULL
, TrapFrame
, Context
.ContextFlags
, KernelMode
);
1543 KeLowerIrql(OldIrql
);
1550 KdpGdbDebugPrint(PCH Message
, ULONG Length
)
1554 /* Initialize the GDB stub */
1557 KdpGdbStubInit(PKD_DISPATCH_TABLE WrapperTable
, ULONG BootPhase
)
1559 if (!KdDebuggerEnabled
|| !KdpDebugMode
.Gdb
)
1564 ExInitializeFastMutex(&GspLock
);
1566 /* Write out the functions that we support for now */
1567 WrapperTable
->KdpInitRoutine
= KdpGdbStubInit
;
1568 WrapperTable
->KdpPrintRoutine
= KdpGdbDebugPrint
;
1569 WrapperTable
->KdpExceptionRoutine
= KdpGdbEnterDebuggerException
;
1571 /* Initialize the Port */
1572 KdPortInitializeEx(&GdbPortInfo
, 0, 0);
1574 else if (BootPhase
== 1)
1576 GspInitialized
= TRUE
;
1578 GspRunThread
= NULL
;
1579 GspDbgThread
= NULL
;
1580 GspEnumThread
= NULL
;
1582 HalDisplayString("Waiting for GDB to attach\n");
1583 DbgBreakPointWithStatus(DBG_STATUS_CONTROL_C
);
1585 else if (BootPhase
== 2)
1587 HalDisplayString("\n GDB debugging enabled\n\n");