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 ****************************************************************************/
15 /****************************************************************************
16 * Header: remcom.c,v 1.34 91/03/09 12:29:49 glenne Exp $
18 * Module name: remcom.c $
20 * Date: 91/03/09 12:29:49 $
21 * Contributor: Lake Stevens Instrument Division$
23 * Description: low level support for gdb debugger. $
25 * Considerations: only works on target hardware $
27 * Written by: Glenn Engel $
28 * ModuleState: Experimental $
32 * Modified for 386 by Jim Kingdon, Cygnus Support.
33 * Modified for ReactOS by Casper S. Hornstrup <chorns@users.sourceforge.net>
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.
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.
47 * The following gdb commands are supported:
49 * command function Return value
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
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
57 * c Resume at current address SNN ( signal NN)
58 * cAA..AA Continue at address AA..AA SNN
60 * s Step one instruction SNN
61 * sAA..AA Step one instruction from AA..AA SNN
65 * ? What was the last sigval ? SNN (signal NN)
67 * All commands and responses are sent with a packet which includes a
68 * Checksum. A packet consists of
70 * $<packet info>#<Checksum>.
73 * <packet info> :: <characters representing the command or response>
74 * <Checksum> :: < two hex digits computed as modulo 256 sum of <packetinfo>>
76 * When a packet is received, it is first acknowledged with either '+' or '-'.
77 * '+' indicates a successful transfer. '-' indicates a failed transfer.
82 * $m0,10#2a +$00010203040506070809101112131415#42
84 ****************************************************************************/
90 /************************************************************************/
91 /* BUFMAX defines the maximum number of characters in inbound/outbound buffers*/
92 /* at least NUMREGBYTES*2 are needed for register packets */
95 static BOOLEAN GspInitialized
;
97 static BOOLEAN GspRemoteDebug
;
99 static CONST CHAR HexChars
[]="0123456789abcdef";
101 static PETHREAD GspRunThread
; /* NULL means run all threads */
102 static PETHREAD GspDbgThread
;
103 static PETHREAD GspEnumThread
;
105 static FAST_MUTEX GspLock
;
107 extern LIST_ENTRY PsActiveProcessHead
;
108 KD_PORT_INFORMATION GdbPortInfo
= { 2, 115200, 0 }; /* FIXME hardcoded for COM2, 115200 baud */
110 /* Number of Registers. */
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
121 typedef struct _CPU_REGISTER
125 ULONG OffsetInContext
;
126 BOOLEAN SetInContext
;
127 } CPU_REGISTER
, *PCPU_REGISTER
;
129 static CPU_REGISTER GspRegisters
[NUMREGS
] =
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
}
149 static PCHAR GspThreadStates
[DeferredReady
+1] =
165 if ((ch
>= '0') && (ch
<= '9'))
169 if ((ch
>= 'a') && (ch
<= 'f'))
171 return (ch
- 'a' + 10);
173 if ((ch
>= 'A') && (ch
<= 'F'))
175 return (ch
- 'A' + 10);
181 static CHAR GspInBuffer
[BUFMAX
];
182 static CHAR GspOutBuffer
[BUFMAX
];
185 GdbPutChar(UCHAR Value
)
187 KdPortPutByteEx(&GdbPortInfo
, Value
);
195 while (!KdPortGetByteEx(&GdbPortInfo
, &Value
))
201 /* scan for the sequence $<data>#<Checksum> */
206 PCHAR Buffer
= &GspInBuffer
[0];
214 /* wait around for the start character, ignore all other characters */
215 while ((ch
= GdbGetChar ()) != '$')
223 /* now, read until a # or end of Buffer is found */
224 while (Count
< BUFMAX
)
235 Checksum
= Checksum
+ ch
;
244 XmitChecksum
= (CHAR
)(HexValue(ch
) << 4);
246 XmitChecksum
+= (CHAR
)(HexValue(ch
));
248 if (Checksum
!= XmitChecksum
)
250 GdbPutChar('-'); /* failed checksum */
254 GdbPutChar('+'); /* successful transfer */
262 /* send the packet in Buffer. */
265 GspPutPacket(PCHAR Buffer
)
271 /* $<packet info>#<Checksum>. */
278 while ((ch
= Buffer
[Count
]))
286 GdbPutChar(HexChars
[(Checksum
>> 4) & 0xf]);
287 GdbPutChar(HexChars
[Checksum
& 0xf]);
289 while (GdbGetChar() != '+');
294 GspPutPacketNoWait(PCHAR Buffer
)
300 /* $<packet info>#<Checksum>. */
305 while ((ch
= Buffer
[Count
]))
313 GdbPutChar(HexChars
[(Checksum
>> 4) & 0xf]);
314 GdbPutChar(HexChars
[Checksum
& 0xf]);
317 /* Indicate to caller of GspMem2Hex or GspHex2Mem that there has been an
319 static volatile BOOLEAN GspMemoryError
= FALSE
;
320 static volatile void *GspAccessLocation
= NULL
;
323 GspReadMemSafe(PCHAR Address
)
326 KdpSafeReadMemory((ULONG_PTR
)Address
, 1, &ch
);
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. */
335 GspMem2Hex(PCHAR Address
,
343 for (i
= 0; i
< (ULONG
) Count
; i
++)
347 ch
= GspReadMemSafe(Address
);
357 *Buffer
++ = HexChars
[(ch
>> 4) & 0xf];
358 *Buffer
++ = HexChars
[ch
& 0xf];
367 GspWriteMem(PCHAR Address
,
370 CHAR (*GetContent
)(PVOID Context
, ULONG Offset
),
381 while (Current
< Address
+ Count
)
383 Page
= (PCHAR
)PAGE_ROUND_DOWN(Current
);
384 if (Address
+ Count
<= Page
+ PAGE_SIZE
)
386 /* Fits in this page */
391 /* Flows into next page, handle only current page in this iteration */
392 CountInPage
= PAGE_SIZE
- (Address
- Page
);
396 OldProt
= MmGetPageProtect(NULL
, Address
);
397 MmSetPageProtect(NULL
, Address
, PAGE_EXECUTE_READWRITE
);
400 for (i
= 0; i
< CountInPage
&& ! GspMemoryError
; i
++)
402 ch
= (*GetContent
)(Context
, Current
- Address
);
406 GspAccessLocation
= Current
;
411 GspAccessLocation
= NULL
;
417 MmSetPageProtect(NULL
, Page
, OldProt
);
420 return Current
- Address
;
425 return Current
- Address
;
429 GspHex2MemGetContent(PVOID Context
, ULONG Offset
)
431 return (CHAR
)((HexValue(*((PCHAR
) Context
+ 2 * Offset
)) << 4) +
432 HexValue(*((PCHAR
) Context
+ 2 * Offset
+ 1)));
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 */
438 GspHex2Mem(PCHAR Buffer
,
443 Count
= GspWriteMem(Address
, Count
, MayFault
, GspHex2MemGetContent
, Buffer
);
445 return Buffer
+ 2 * Count
;
449 GspWriteMemSafe(PCHAR Address
,
452 KdpSafeWriteMemory((ULONG_PTR
)Address
, 1, Ch
);
456 /* This function takes the 386 exception vector and attempts to
457 translate this number into a unix compatible signal value */
459 GspComputeSignal(NTSTATUS ExceptionCode
)
463 switch (ExceptionCode
)
465 case STATUS_INTEGER_DIVIDE_BY_ZERO
:
466 SigVal
= 8; /* divide by zero */
468 case STATUS_SINGLE_STEP
:
469 case STATUS_BREAKPOINT
:
470 SigVal
= 5; /* breakpoint */
472 case STATUS_INTEGER_OVERFLOW
:
473 case STATUS_ARRAY_BOUNDS_EXCEEDED
:
474 SigVal
= 16; /* bound instruction */
476 case STATUS_ILLEGAL_INSTRUCTION
:
477 SigVal
= 4; /* Invalid opcode */
479 case STATUS_STACK_OVERFLOW
:
480 case STATUS_DATATYPE_MISALIGNMENT
:
481 case STATUS_ACCESS_VIOLATION
:
482 SigVal
= 11; /* access violation */
485 SigVal
= 7; /* "software generated" */
491 /**********************************************/
492 /* WHILE WE FIND NICE HEX CHARS, BUILD A LONG */
493 /* RETURN NUMBER OF CHARS PROCESSED */
494 /**********************************************/
496 GspHex2Long(PCHAR
*Address
,
506 Hex
= HexValue(**Address
);
509 *Value
= (*Value
<< 4) | Hex
;
525 GspLong2Hex(PCHAR
*Address
,
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
);
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.
545 GspGetEspFromTrapFrame(PKTRAP_FRAME TrapFrame
)
547 return KeGetPreviousMode() == KernelMode
548 ? (LONG
) &TrapFrame
->HardwareEsp
: (LONG
)TrapFrame
->HardwareEsp
;
553 GspGetRegisters(PCHAR Address
,
554 PKTRAP_FRAME TrapFrame
)
560 ULONG_PTR
*KernelStack
;
562 if (NULL
== GspDbgThread
)
564 Thread
= PsGetCurrentThread();
568 TrapFrame
= GspDbgThread
->Tcb
.TrapFrame
;
569 Thread
= GspDbgThread
;
572 if (Waiting
== Thread
->Tcb
.State
)
574 KernelStack
= Thread
->Tcb
.KernelStack
;
575 for (i
= 0; i
< sizeof(GspRegisters
) / sizeof(GspRegisters
[0]); i
++)
580 Value
= KernelStack
[3];
583 Value
= KernelStack
[4];
586 Value
= KernelStack
[5];
589 Value
= KernelStack
[6];
592 Value
= KernelStack
[7];
595 Value
= (ULONG_PTR
) (KernelStack
+ 8);
598 Value
= KGDT_R0_CODE
;
601 Value
= KGDT_R0_DATA
;
607 Address
= GspMem2Hex((PCHAR
) &Value
, Address
, GspRegisters
[i
].Size
,
613 for (i
= 0; i
< sizeof(GspRegisters
) / sizeof(GspRegisters
[0]); i
++)
619 Value
= GspGetEspFromTrapFrame(TrapFrame
);
623 p
= (PULONG
)((ULONG_PTR
) TrapFrame
+
624 GspRegisters
[i
].OffsetInTF
);
631 * This thread has not been sheduled yet so assume it
632 * is still in PsBeginThreadWithContextInternal().
634 Value
= (ULONG
)KiThreadStartup
;
640 Address
= GspMem2Hex((PCHAR
) &Value
, Address
,
641 GspRegisters
[i
].Size
, FALSE
);
648 GspSetRegistersInTrapFrame(PCHAR Address
,
650 PKTRAP_FRAME TrapFrame
)
663 for (i
= 0; i
< NUMREGS
; i
++)
665 if (GspRegisters
[i
].SetInContext
)
667 p
= (PULONG
) ((ULONG_PTR
) Context
+ GspRegisters
[i
].OffsetInContext
);
671 p
= (PULONG
) ((ULONG_PTR
) TrapFrame
+ GspRegisters
[i
].OffsetInTF
);
674 Buffer
= GspHex2Mem(Buffer
, (PCHAR
) &Value
, GspRegisters
[i
].Size
, FALSE
);
681 GspSetSingleRegisterInTrapFrame(PCHAR Address
,
684 PKTRAP_FRAME TrapFrame
)
694 if (GspRegisters
[Number
].SetInContext
)
696 p
= (PULONG
) ((ULONG_PTR
) Context
+ GspRegisters
[Number
].OffsetInContext
);
700 p
= (PULONG
) ((ULONG_PTR
) TrapFrame
+ GspRegisters
[Number
].OffsetInTF
);
703 GspHex2Mem(Address
, (PCHAR
) &Value
, GspRegisters
[Number
].Size
, FALSE
);
709 GspFindThread(PCHAR Data
,
712 PETHREAD ThreadInfo
= NULL
;
714 if (strcmp (Data
, "-1") == 0)
723 PCHAR ptr
= &Data
[0];
725 GspHex2Long(&ptr
, (PLONG
) &uThreadId
);
726 ThreadId
= (HANDLE
)uThreadId
;
728 if (!NT_SUCCESS(PsLookupThreadByThreadId(ThreadId
, &ThreadInfo
)))
734 *Thread
= ThreadInfo
;
740 GspSetThread(PCHAR Request
)
743 PCHAR ptr
= &Request
[1];
747 case 'c': /* Run thread */
748 if (GspFindThread(ptr
, &ThreadInfo
))
750 GspOutBuffer
[0] = 'O';
751 GspOutBuffer
[1] = 'K';
753 if (NULL
!= GspRunThread
)
755 ObDereferenceObject(GspRunThread
);
757 GspRunThread
= ThreadInfo
;
758 if (NULL
!= GspRunThread
)
760 ObReferenceObject(GspRunThread
);
765 GspOutBuffer
[0] = 'E';
768 case 'g': /* Debug thread */
769 if (GspFindThread(ptr
, &ThreadInfo
))
771 GspOutBuffer
[0] = 'O';
772 GspOutBuffer
[1] = 'K';
774 if (NULL
!= GspDbgThread
)
776 ObDereferenceObject(GspDbgThread
);
779 if (ThreadInfo
== PsGetCurrentThread())
782 ObDereferenceObject(ThreadInfo
);
786 GspDbgThread
= ThreadInfo
;
791 GspOutBuffer
[0] = 'E';
801 GspQuery(PCHAR Request
)
805 if (strncmp(Request
, "C", 1) == 0)
807 PCHAR ptr
= &GspOutBuffer
[2];
809 /* Get current thread id */
810 GspOutBuffer
[0] = 'Q';
811 GspOutBuffer
[1] = 'C';
812 if (NULL
!= GspDbgThread
)
814 Value
= (ULONG
) GspDbgThread
->Cid
.UniqueThread
;
818 Value
= (ULONG
) PsGetCurrentThread()->Cid
.UniqueThread
;
820 GspLong2Hex(&ptr
, Value
);
822 else if (strncmp(Request
, "fThreadInfo", 11) == 0)
825 PLIST_ENTRY AThread
, AProcess
;
826 PCHAR ptr
= &GspOutBuffer
[1];
828 /* Get first thread id */
829 GspEnumThread
= NULL
;
830 AProcess
= PsActiveProcessHead
.Flink
;
831 while(AProcess
!= &PsActiveProcessHead
)
833 Process
= CONTAINING_RECORD(AProcess
, EPROCESS
, ActiveProcessLinks
);
834 AThread
= Process
->ThreadListHead
.Flink
;
835 if (AThread
!= &Process
->ThreadListHead
)
837 GspEnumThread
= CONTAINING_RECORD(Process
->ThreadListHead
.Flink
,
838 ETHREAD
, ThreadListEntry
);
841 AProcess
= AProcess
->Flink
;
843 if(GspEnumThread
!= NULL
)
845 GspOutBuffer
[0] = 'm';
846 Value
= (ULONG
) GspEnumThread
->Cid
.UniqueThread
;
847 GspLong2Hex(&ptr
, Value
);
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'; */
856 else if (strncmp(Request
, "sThreadInfo", 11) == 0)
859 PLIST_ENTRY AThread
, AProcess
;
860 PCHAR ptr
= &GspOutBuffer
[1];
862 /* Get next thread id */
863 if (GspEnumThread
!= NULL
)
865 /* find the next thread */
866 Process
= GspEnumThread
->ThreadsProcess
;
867 if(GspEnumThread
->ThreadListEntry
.Flink
!= &Process
->ThreadListHead
)
869 GspEnumThread
= CONTAINING_RECORD(GspEnumThread
->ThreadListEntry
.Flink
,
870 ETHREAD
, ThreadListEntry
);
874 PETHREAD Thread
= NULL
;
875 AProcess
= Process
->ActiveProcessLinks
.Flink
;
876 while(AProcess
!= &PsActiveProcessHead
)
878 Process
= CONTAINING_RECORD(AProcess
, EPROCESS
, ActiveProcessLinks
);
879 AThread
= Process
->ThreadListHead
.Flink
;
880 if (AThread
!= &Process
->ThreadListHead
)
882 Thread
= CONTAINING_RECORD(Process
->ThreadListHead
.Flink
,
883 ETHREAD
, ThreadListEntry
);
886 AProcess
= AProcess
->Flink
;
888 GspEnumThread
= Thread
;
891 if (GspEnumThread
!= NULL
)
894 GspOutBuffer
[0] = 'm';
895 Value
= (ULONG
) GspEnumThread
->Cid
.UniqueThread
;
896 GspLong2Hex(&ptr
, Value
);
900 GspOutBuffer
[0] = 'l';
905 GspOutBuffer
[0] = 'l';
908 else if (strncmp(Request
, "ThreadExtraInfo", 15) == 0)
912 /* Get thread information */
913 if (GspFindThread(Request
+ 16, &ThreadInfo
))
918 Proc
= (PEPROCESS
) ThreadInfo
->ThreadsProcess
;
923 sprintf(Buffer
, "%s [%d:0x%x], ", Proc
->ImageFileName
,
924 (int) Proc
->UniqueProcessId
,
925 (int) ThreadInfo
->Cid
.UniqueThread
);
927 strcpy(Buffer
+ strlen(Buffer
),
928 GspThreadStates
[ThreadInfo
->Tcb
.State
]);
930 ObDereferenceObject(ThreadInfo
);
932 GspMem2Hex(Buffer
, &GspOutBuffer
[0], strlen(Buffer
), FALSE
);
938 GspQueryThreadStatus(PCHAR Request
)
941 PCHAR ptr
= &Request
[0];
943 if (GspFindThread(ptr
, &ThreadInfo
))
945 ObDereferenceObject(ThreadInfo
);
947 GspOutBuffer
[0] = 'O';
948 GspOutBuffer
[1] = 'K';
949 GspOutBuffer
[2] = '\0';
953 GspOutBuffer
[0] = 'E';
954 GspOutBuffer
[1] = '\0';
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)))
981 #define I386_BP_TYPE_EXECUTE 0
982 #define I386_BP_TYPE_DATA_WRITE 1
983 #define I386_BP_TYPE_DATA_READWRITE 3
985 #define I386_OPCODE_INT3 0xcc
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
993 typedef struct _GSPHWBREAKPOINT
1000 #define MAX_HW_BREAKPOINTS 4
1001 static unsigned GspHwBreakpointCount
= 0;
1002 static GSPHWBREAKPOINT GspHwBreakpoints
[MAX_HW_BREAKPOINTS
];
1004 typedef struct _GSPSWBREAKPOINT
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
];
1017 GspSetHwBreakpoint(ULONG Type
, ULONG_PTR Address
, ULONG Length
)
1019 DPRINT("GspSetHwBreakpoint(%lu, 0x%p, %lu)\n", Type
, Address
, Length
);
1021 if (GDB_ZTYPE_READ_WATCHPOINT
== Type
)
1023 DPRINT1("Read watchpoint not supported\n");
1024 strcpy(GspOutBuffer
, "E22");
1026 else if (GDB_ZTYPE_HARDWARE_BREAKPOINT
== Type
&& 1 != Length
)
1028 DPRINT1("Invalid length %lu for hardware breakpoint\n", Length
);
1029 strcpy(GspOutBuffer
, "E22");
1031 else if (1 != Length
&& 2 != Length
&& 4 != Length
)
1033 DPRINT1("Invalid length %lu for GDB Z type %lu\n", Length
, Type
);
1034 strcpy(GspOutBuffer
, "E22");
1036 else if (0 != (Address
& (Length
- 1)))
1038 DPRINT1("Invalid alignment for address 0x%p and length %d\n",
1040 strcpy(GspOutBuffer
, "E22");
1042 else if (MAX_HW_BREAKPOINTS
== GspHwBreakpointCount
)
1044 DPRINT1("Trying to set too many hardware breakpoints\n");
1045 strcpy(GspOutBuffer
, "E22");
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");
1059 GspRemoveHwBreakpoint(ULONG Type
, ULONG_PTR Address
, ULONG Length
)
1063 DPRINT("GspRemoveHwBreakpoint(%lu, 0x%p, %lu)\n", Type
, Address
, Length
);
1064 for (Index
= 0; Index
< GspHwBreakpointCount
; Index
++)
1066 if (GspHwBreakpoints
[Index
].Type
== Type
&&
1067 GspHwBreakpoints
[Index
].Address
== Address
&&
1068 GspHwBreakpoints
[Index
].Length
== Length
)
1070 DPRINT("Found match at index %u\n", Index
);
1071 if (Index
+ 1 < GspHwBreakpointCount
)
1073 memmove(GspHwBreakpoints
+ Index
,
1074 GspHwBreakpoints
+ (Index
+ 1),
1075 (GspHwBreakpointCount
- Index
- 1) *
1076 sizeof(GSPHWBREAKPOINT
));
1078 GspHwBreakpointCount
--;
1079 strcpy(GspOutBuffer
, "OK");
1084 DPRINT1("Not found\n");
1085 strcpy(GspOutBuffer
, "E22");
1089 GspSetSwBreakpoint(ULONG_PTR Address
)
1091 DPRINT("GspSetSwBreakpoint(0x%p)\n", Address
);
1093 if (MAX_SW_BREAKPOINTS
== GspSwBreakpointCount
)
1095 DPRINT1("Trying to set too many software breakpoints\n");
1096 strcpy(GspOutBuffer
, "E22");
1102 for (Index
= 0; Index
< GspSwBreakpointCount
; Index
++)
1104 if (GspSwBreakpoints
[Index
].Address
== Address
)
1106 strcpy(GspOutBuffer
, "E22");
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");
1123 GspRemoveSwBreakpoint(ULONG_PTR Address
)
1127 DPRINT("GspRemoveSwBreakpoint(0x%p)\n", Address
);
1128 for (Index
= 0; Index
< GspSwBreakpointCount
; Index
++)
1130 if (GspSwBreakpoints
[Index
].Address
== Address
)
1132 DPRINT("Found match at index %u\n", Index
);
1133 ASSERT(! GspSwBreakpoints
[Index
].Active
);
1135 GspWriteMemSafe((PCHAR
)Address
,
1136 GspSwBreakpointsInstructions
[Index
]);
1138 if (Index
+ 1 < GspSwBreakpointCount
)
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
));
1148 GspSwBreakpointCount
--;
1149 strcpy(GspOutBuffer
, "OK");
1154 DPRINT1("Not found\n");
1155 strcpy(GspOutBuffer
, "E22");
1159 GspLoadHwBreakpoint(PKTRAP_FRAME TrapFrame
,
1165 DPRINT("GspLoadHwBreakpoint(0x%p, %d, 0x%p, %d)\n", TrapFrame
, BpIndex
,
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
);
1176 DPRINT("Setting DR0 to 0x%p\n", Address
);
1177 TrapFrame
->Dr0
= Address
;
1181 DPRINT("Setting DR1 to 0x%p\n", Address
);
1182 TrapFrame
->Dr1
= Address
;
1186 DPRINT("Setting DR2 to 0x%p\n", Address
);
1187 TrapFrame
->Dr2
= Address
;
1191 DPRINT("Setting DR3 to 0x%p\n", Address
);
1192 TrapFrame
->Dr3
= Address
;
1198 GspLoadBreakpoints(PKTRAP_FRAME TrapFrame
)
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
);
1213 for (Index
= 0; Index
< GspHwBreakpointCount
; Index
++)
1215 switch(GspHwBreakpoints
[Index
].Type
)
1217 case GDB_ZTYPE_HARDWARE_BREAKPOINT
:
1218 i386Type
= I386_BP_TYPE_EXECUTE
;
1220 case GDB_ZTYPE_WRITE_WATCHPOINT
:
1221 i386Type
= I386_BP_TYPE_DATA_WRITE
;
1223 case GDB_ZTYPE_ACCESS_WATCHPOINT
:
1224 i386Type
= I386_BP_TYPE_DATA_READWRITE
;
1228 i386Type
= I386_BP_TYPE_EXECUTE
;
1232 GspLoadHwBreakpoint(TrapFrame
, Index
, GspHwBreakpoints
[Index
].Address
,
1233 GspHwBreakpoints
[Index
].Length
- 1, i386Type
);
1236 for (Index
= 0; Index
< GspSwBreakpointCount
; Index
++)
1238 if (GspHwBreakpointCount
+ Index
< MAX_HW_BREAKPOINTS
)
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
;
1248 DPRINT("Using real software breakpoint\n");
1249 GspMemoryError
= FALSE
;
1250 GspSwBreakpoints
[Index
].PrevContent
= GspReadMemSafe((PCHAR
) GspSwBreakpoints
[Index
].Address
);
1251 if (! GspMemoryError
)
1253 GspWriteMemSafe((PCHAR
) GspSwBreakpoints
[Index
].Address
, I386_OPCODE_INT3
);
1255 GspSwBreakpoints
[Index
].Active
= ! GspMemoryError
;
1258 DPRINT1("Failed to set software breakpoint at 0x%p\n",
1259 GspSwBreakpoints
[Index
].Address
);
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
);
1270 DPRINT("Final DR7 value 0x%08x\n", TrapFrame
->Dr7
);
1274 GspUnloadBreakpoints(PKTRAP_FRAME TrapFrame
)
1278 DPRINT("GspUnloadHwBreakpoints\n");
1280 for (Index
= 0; Index
< GspSwBreakpointCount
; Index
++)
1282 if (GspSwBreakpoints
[Index
].Active
)
1284 GspMemoryError
= FALSE
;
1285 GspWriteMemSafe((PCHAR
) GspSwBreakpoints
[Index
].Address
,
1286 GspSwBreakpoints
[Index
].PrevContent
);
1287 GspSwBreakpoints
[Index
].Active
= FALSE
;
1290 DPRINT1("Failed to remove software breakpoint from 0x%p\n",
1291 GspSwBreakpoints
[Index
].Address
);
1295 DPRINT("Successfully removed software breakpoint from 0x%p\n",
1296 GspSwBreakpoints
[Index
].Address
);
1302 static BOOLEAN gdb_attached_yet
= FALSE
;
1304 * This function does all command procesing for interfacing to gdb.
1308 KdpGdbEnterDebuggerException(PEXCEPTION_RECORD ExceptionRecord
,
1310 PKTRAP_FRAME TrapFrame
)
1319 /* FIXME: Stop on other CPUs too */
1321 if (STATUS_ACCESS_VIOLATION
== (NTSTATUS
) ExceptionRecord
->ExceptionCode
&&
1322 NULL
!= GspAccessLocation
&&
1323 (ULONG_PTR
) GspAccessLocation
==
1324 (ULONG_PTR
) ExceptionRecord
->ExceptionInformation
[1])
1326 GspAccessLocation
= NULL
;
1327 GspMemoryError
= TRUE
;
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());
1337 /* Disable hardware debugging while we are inside the stub */
1339 GspUnloadBreakpoints(TrapFrame
);
1341 /* Make sure we're debugging the current thread. */
1342 if (NULL
!= GspDbgThread
)
1344 DPRINT1("Internal error: entering stub with non-NULL GspDbgThread\n");
1345 ObDereferenceObject(GspDbgThread
);
1346 GspDbgThread
= NULL
;
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
)
1356 /* reply to host that an exception has occurred */
1357 SigVal
= GspComputeSignal(ExceptionRecord
->ExceptionCode
);
1361 *ptr
++ = 'T'; /* notify gdb with signo, PC, FP and SP */
1362 *ptr
++ = HexChars
[(SigVal
>> 4) & 0xf];
1363 *ptr
++ = HexChars
[SigVal
& 0xf];
1365 *ptr
++ = HexChars
[ESP
];
1368 Esp
= GspGetEspFromTrapFrame(TrapFrame
); /* SP */
1369 ptr
= GspMem2Hex((PCHAR
) &Esp
, ptr
, 4, 0);
1372 *ptr
++ = HexChars
[EBP
];
1374 ptr
= GspMem2Hex((PCHAR
) &TrapFrame
->Ebp
, ptr
, 4, 0); /* FP */
1377 *ptr
++ = HexChars
[PC
];
1379 ptr
= GspMem2Hex((PCHAR
) &TrapFrame
->Eip
, ptr
, 4, 0); /* PC */
1384 GspPutPacket(&GspOutBuffer
[0]);
1385 /* DPRINT("------- replied status: (%s) -------\n", GspOutBuffer); */
1389 gdb_attached_yet
= 1;
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); */
1404 /* a little hack to send more complete status information */
1406 GspOutBuffer
[0] = 'S';
1407 GspOutBuffer
[1] = HexChars
[SigVal
>> 4];
1408 GspOutBuffer
[2] = HexChars
[SigVal
% 16];
1409 GspOutBuffer
[3] = 0;
1412 GspRemoteDebug
= !GspRemoteDebug
; /* toggle debug flag */
1414 case 'g': /* return the value of the CPU Registers */
1415 GspGetRegisters(GspOutBuffer
, TrapFrame
);
1417 case 'G': /* set the value of the CPU Registers - return OK */
1418 if (NULL
!= GspDbgThread
)
1420 GspSetRegistersInTrapFrame(ptr
, Context
, GspDbgThread
->Tcb
.TrapFrame
);
1424 GspSetRegistersInTrapFrame(ptr
, Context
, TrapFrame
);
1426 strcpy(GspOutBuffer
, "OK");
1428 case 'P': /* set the value of a single CPU register - return OK */
1432 if ((GspHex2Long(&ptr
, &Register
)) && (*ptr
++ == '='))
1434 if ((Register
>= 0) && (Register
< NUMREGS
))
1438 GspSetSingleRegisterInTrapFrame(ptr
, Register
,
1440 GspDbgThread
->Tcb
.TrapFrame
);
1444 GspSetSingleRegisterInTrapFrame(ptr
, Register
,
1445 Context
, TrapFrame
);
1447 strcpy(GspOutBuffer
, "OK");
1452 strcpy(GspOutBuffer
, "E01");
1456 /* mAA..AA,LLLL Read LLLL bytes at address AA..AA */
1458 /* TRY TO READ %x,%x. IF SUCCEED, SET PTR = 0 */
1459 if (GspHex2Long(&ptr
, &Address
) &&
1461 GspHex2Long(&ptr
, &Length
))
1463 PEPROCESS DbgProcess
= NULL
;
1466 if (NULL
!= GspDbgThread
&&
1467 PsGetCurrentProcess() != GspDbgThread
->ThreadsProcess
)
1469 DbgProcess
= GspDbgThread
->ThreadsProcess
;
1470 KeAttachProcess(&DbgProcess
->Pcb
);
1472 GspMemoryError
= FALSE
;
1473 GspMem2Hex((PCHAR
) Address
, GspOutBuffer
, Length
, 1);
1474 if (NULL
!= DbgProcess
)
1480 strcpy(GspOutBuffer
, "E03");
1481 DPRINT("Fault during memory read\n");
1487 strcpy(GspOutBuffer
, "E01");
1491 /* MAA..AA,LLLL: Write LLLL bytes at address AA.AA return OK */
1493 /* TRY TO READ '%x,%x:'. IF SUCCEED, SET PTR = 0 */
1494 if (GspHex2Long(&ptr
, &Address
))
1496 if (*(ptr
++) == ',' &&
1497 GspHex2Long(&ptr
, &Length
) &&
1500 PEPROCESS DbgProcess
= NULL
;
1502 if (NULL
!= GspDbgThread
&&
1503 PsGetCurrentProcess() != GspDbgThread
->ThreadsProcess
)
1505 DbgProcess
= GspDbgThread
->ThreadsProcess
;
1506 KeAttachProcess(&DbgProcess
->Pcb
);
1508 GspMemoryError
= FALSE
;
1509 GspHex2Mem(ptr
, (PCHAR
) Address
, Length
, TRUE
);
1510 if (NULL
!= DbgProcess
)
1516 strcpy(GspOutBuffer
, "E03");
1517 DPRINT("Fault during memory write\n");
1521 strcpy(GspOutBuffer
, "OK");
1529 strcpy(GspOutBuffer
, "E02");
1533 /* cAA..AA Continue at address AA..AA(optional) */
1534 /* sAA..AA Step one instruction from AA..AA(optional) */
1539 ULONG BreakpointNumber
;
1542 /* try to read optional parameter, pc unchanged if no parm */
1543 if (GspHex2Long (&ptr
, &Address
))
1545 Context
->Eip
= Address
;
1548 NewPC
= Context
->Eip
;
1550 /* clear the trace bit */
1551 Context
->EFlags
&= 0xfffffeff;
1553 /* set the trace bit if we're Stepping */
1556 Context
->EFlags
|= 0x100;
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
;
1564 #error Unknown compiler for inline assembler
1566 if (!(dr6_
& 0x4000))
1568 for (BreakpointNumber
= 0; BreakpointNumber
< 4; ++BreakpointNumber
)
1570 if (dr6_
& (1 << BreakpointNumber
))
1572 if (GspHwBreakpoints
[BreakpointNumber
].Type
== 0)
1574 /* Set restore flag */
1575 Context
->EFlags
|= 0x10000;
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
;
1587 #error Unknown compiler for inline assembler
1590 if (NULL
!= GspDbgThread
)
1592 ObDereferenceObject(GspDbgThread
);
1593 GspDbgThread
= NULL
;
1596 DPRINT("Thread %p releasing mutex\n", PsGetCurrentThread());
1597 ExReleaseFastMutex(&GspLock
);
1598 DPRINT("Thread %p leaving stub\n", PsGetCurrentThread());
1603 case 'k': /* kill the program */
1604 strcpy(GspOutBuffer
, "OK");
1606 /* kill the program */
1608 case 'H': /* Set thread */
1612 case 'q': /* Query */
1616 case 'T': /* Query thread status */
1617 GspQueryThreadStatus(ptr
);
1626 GspHex2Long(&ptr
, &Type
);
1628 GspHex2Long(&ptr
, &Address
);
1630 GspHex2Long(&ptr
, &Length
);
1633 GspSetSwBreakpoint((ULONG_PTR
) Address
);
1637 GspSetHwBreakpoint(Type
, (ULONG_PTR
) Address
, Length
);
1648 GspHex2Long(&ptr
, &Type
);
1650 GspHex2Long(&ptr
, &Address
);
1652 GspHex2Long(&ptr
, &Length
);
1655 GspRemoveSwBreakpoint((ULONG_PTR
) Address
);
1659 GspRemoveHwBreakpoint(Type
, (ULONG_PTR
) Address
, Length
);
1668 /* reply to the request */
1669 GspPutPacket(GspOutBuffer
);
1670 /* DPRINT("------- reply command (%s) -------\n", GspOutBuffer); */
1677 if (NULL
!= GspDbgThread
)
1679 ObDereferenceObject(GspDbgThread
);
1680 GspDbgThread
= NULL
;
1689 GspBreakIn(PKINTERRUPT Interrupt
,
1690 PVOID ServiceContext
)
1692 PKTRAP_FRAME TrapFrame
;
1698 DPRINT("Break In\n");
1701 while (KdPortGetByteEx(&GdbPortInfo
, &Value
))
1714 KeRaiseIrql(HIGH_LEVEL
, &OldIrql
);
1716 TrapFrame
= PsGetCurrentThread()->Tcb
.TrapFrame
;
1718 KeTrapFrameToContext(TrapFrame
, NULL
, &Context
);
1720 KdpGdbEnterDebuggerException(NULL
, &Context
, TrapFrame
);
1722 KeContextToTrapFrame(&Context
, NULL
, TrapFrame
, Context
.ContextFlags
, KernelMode
);
1724 KeLowerIrql(OldIrql
);
1731 KdpGdbDebugPrint(PCH Message
, ULONG Length
)
1735 /* Initialize the GDB stub */
1738 KdpGdbStubInit(PKD_DISPATCH_TABLE WrapperTable
,
1741 if (!KdDebuggerEnabled
|| !KdpDebugMode
.Gdb
)
1748 ExInitializeFastMutex(&GspLock
);
1750 /* Write out the functions that we support for now */
1751 WrapperTable
->KdpInitRoutine
= KdpGdbStubInit
;
1752 WrapperTable
->KdpPrintRoutine
= KdpGdbDebugPrint
;
1753 WrapperTable
->KdpExceptionRoutine
= KdpGdbEnterDebuggerException
;
1755 /* Initialize the Port */
1756 KdPortInitializeEx(&GdbPortInfo
, 0, 0);
1758 KdpPort
= GdbPortInfo
.ComPort
;
1760 else if (BootPhase
== 1)
1762 GspInitialized
= TRUE
;
1764 GspRunThread
= NULL
;
1765 GspDbgThread
= NULL
;
1766 GspEnumThread
= NULL
;
1768 HalDisplayString("Waiting for GDB to attach\n");
1769 DbgBreakPointWithStatus(DBG_STATUS_CONTROL_C
);
1771 else if (BootPhase
== 2)
1773 HalDisplayString("\n GDB debugging enabled\n\n");