xref: /reactos/drivers/base/kdgdb/amd64_sup.c (revision 34593d93)
1 /*
2  * PROJECT:     ReactOS KD dll - GDB stub
3  * LICENSE:     GPL-2.0-or-later (https://spdx.org/licenses/GPL-2.0-or-later)
4  * PURPOSE:     Base functions for the kernel debugger
5  * COPYRIGHT:   Copyright 2021 Jérôme Gardou
6  */
7 
8 #include "kdgdb.h"
9 
10 enum reg_name
11 {
12     RAX, RBX, RCX, RDX, RSI, RDI, RBP, RSP,
13     R8, R9, R10, R11, R12, R13, R14, R15,
14     RIP,
15     EFLAGS,
16     CS, SS, DS, ES, FS, GS,
17     ST0, ST1, ST2, ST3, ST4, ST5, ST6, ST7,
18     FCTRL, FSTAT, FTAG, FISEG, FIOFF, FOSEG, FOOFF, FOP
19 };
20 
21 static const unsigned char reg_size[] =
22 {
23     8, 8, 8, 8, 8, 8, 8, 8,
24     8, 8, 8, 8, 8, 8, 8, 8,
25     8,
26     4,
27     4, 4, 4, 4, 4, 4,
28     10, 10, 10, 10, 10, 10, 10, 10,
29     8, 8, 8, 8, 8, 8, 8, 8
30 };
31 
32 static
33 void*
34 ctx_to_reg(CONTEXT* ctx, enum reg_name name)
35 {
36     switch (name)
37     {
38         case RAX: return &ctx->Rax;
39         case RBX: return &ctx->Rbx;
40         case RCX: return &ctx->Rcx;
41         case RDX: return &ctx->Rdx;
42         case RSP: return &ctx->Rsp;
43         case RBP: return &ctx->Rbp;
44         case RSI: return &ctx->Rsi;
45         case RDI: return &ctx->Rdi;
46         case RIP: return &ctx->Rip;
47         case R8: return &ctx->R8;
48         case R9: return &ctx->R9;
49         case R10: return &ctx->R10;
50         case R11: return &ctx->R11;
51         case R12: return &ctx->R12;
52         case R13: return &ctx->R13;
53         case R14: return &ctx->R14;
54         case R15: return &ctx->R15;
55         case EFLAGS: return &ctx->EFlags;
56         case CS: return &ctx->SegCs;
57         case DS: return &ctx->SegSs;
58         case ES: return &ctx->SegEs;
59         case FS: return &ctx->SegFs;
60         case GS: return &ctx->SegGs;
61         case SS: return &ctx->SegSs;
62     }
63 #undef return_reg
64     return 0;
65 }
66 
67 static
68 void*
69 thread_to_reg(PETHREAD Thread, enum reg_name reg_name)
70 {
71     static const void* NullValue = NULL;
72 
73 #if 0
74     if (Thread->Tcb.TrapFrame)
75     {
76         PKTRAP_FRAME TrapFrame = Thread->Tcb.TrapFrame;
77 
78         switch (reg_name)
79         {
80             case RAX: return &TrapFrame->Rax;
81             case RBX: return &TrapFrame->Rbx;
82             case RCX: return &TrapFrame->Rcx;
83             case RDX: return &TrapFrame->Rdx;
84             case RSP: return &TrapFrame->Rsp;
85             case RBP: return &TrapFrame->Rbp;
86             case RSI: return &TrapFrame->Rsi;
87             case RDI: return &TrapFrame->Rdi;
88             case RIP: return &TrapFrame->Rip;
89             case R8: return &TrapFrame->R8;
90             case R9: return &TrapFrame->R9;
91             case R10: return &TrapFrame->R10;
92             case R11: return &TrapFrame->R11;
93             case EFLAGS: return &TrapFrame->EFlags;
94             case CS: return &TrapFrame->SegCs;
95             case DS: return &TrapFrame->SegSs;
96             case ES: return &TrapFrame->SegEs;
97             case FS: return &TrapFrame->SegFs;
98             case GS: return &TrapFrame->SegGs;
99             case SS: return &TrapFrame->SegSs;
100             default:
101                 KDDBGPRINT("Unhandled regname: %d.\n", reg_name);
102         }
103     }
104     else
105 #endif
106     if (!Thread->Tcb.InitialStack)
107     {
108         /* Terminated thread ? */
109         switch (reg_name)
110         {
111             case RSP:
112             case RBP:
113             case RIP:
114                 KDDBGPRINT("Returning NULL for register %d.\n", reg_name);
115                 return &NullValue;
116             default:
117                 return NULL;
118         }
119     }
120     else
121     {
122         switch(reg_name)
123         {
124             case RSP: return &Thread->Tcb.KernelStack;
125             case RIP:
126             {
127                 PULONG_PTR Rsp = Thread->Tcb.KernelStack;
128                 return &Rsp[3];
129             }
130             case RBP:
131             {
132                 PULONG_PTR Rsp = Thread->Tcb.KernelStack;
133                 return &Rsp[4];
134             }
135             default:
136                 return NULL;
137         }
138     }
139 
140     return NULL;
141 }
142 
143 KDSTATUS
144 gdb_send_registers(void)
145 {
146     CHAR RegisterStr[17];
147     UCHAR* RegisterPtr;
148     unsigned short i;
149     unsigned short size;
150 
151     start_gdb_packet();
152 
153     KDDBGPRINT("Sending registers of thread %" PRIxPTR ".\n", gdb_dbg_tid);
154     KDDBGPRINT("Current thread_id: %p.\n", PsGetThreadId((PETHREAD)(ULONG_PTR)CurrentStateChange.Thread));
155     if (((gdb_dbg_pid == 0) && (gdb_dbg_tid == 0)) ||
156             gdb_tid_to_handle(gdb_dbg_tid) == PsGetThreadId((PETHREAD)(ULONG_PTR)CurrentStateChange.Thread))
157     {
158         for (i = 0; i < 24; i++)
159         {
160             RegisterPtr = ctx_to_reg(&CurrentContext, i);
161             size = reg_size[i] * 2;
162             RegisterStr[size] = 0;
163             while (size)
164             {
165                 size--;
166                 RegisterStr[size] = hex_chars[RegisterPtr[size/2] & 0xF];
167                 size--;
168                 RegisterStr[size] = hex_chars[RegisterPtr[size/2] >> 4];
169             }
170 
171             send_gdb_partial_packet(RegisterStr);
172         }
173     }
174     else
175     {
176         PETHREAD DbgThread;
177 
178         DbgThread = find_thread(gdb_dbg_pid, gdb_dbg_tid);
179 
180         if (DbgThread == NULL)
181         {
182             /* Thread is dead */
183             send_gdb_partial_packet("E03");
184             return finish_gdb_packet();
185         }
186 
187         for (i = 0; i < 24; i++)
188         {
189             RegisterPtr = thread_to_reg(DbgThread, i);
190             size = reg_size[i] * 2;
191             RegisterStr[size] = 0;
192 
193             while (size)
194             {
195                 if (RegisterPtr)
196                 {
197                     size--;
198                     RegisterStr[size] = hex_chars[RegisterPtr[size/2] & 0xF];
199                     size--;
200                     RegisterStr[size] = hex_chars[RegisterPtr[size/2] >> 4];
201                 }
202                 else
203                 {
204                     size--;
205                     RegisterStr[size] = 'x';
206                     size--;
207                     RegisterStr[size] = 'x';
208                 }
209             }
210 
211             send_gdb_partial_packet(RegisterStr);
212         }
213     }
214 
215     return finish_gdb_packet();
216 }
217 
218 KDSTATUS
219 gdb_send_register(void)
220 {
221     enum reg_name reg_name;
222     void *ptr;
223 
224     /* Get the GDB register name (gdb_input = "pXX") */
225     reg_name = (hex_value(gdb_input[1]) << 4) | hex_value(gdb_input[2]);
226 
227     if (((gdb_dbg_pid == 0) && (gdb_dbg_tid == 0)) ||
228             gdb_tid_to_handle(gdb_dbg_tid) == PsGetThreadId((PETHREAD)(ULONG_PTR)CurrentStateChange.Thread))
229     {
230         /* We can get it from the context of the current exception */
231         ptr = ctx_to_reg(&CurrentContext, reg_name);
232     }
233     else
234     {
235         PETHREAD DbgThread;
236 
237         DbgThread = find_thread(gdb_dbg_pid, gdb_dbg_tid);
238 
239         if (DbgThread == NULL)
240         {
241             /* Thread is dead */
242             return send_gdb_packet("E03");
243         }
244 
245         ptr = thread_to_reg(DbgThread, reg_name);
246     }
247 
248     if (!ptr)
249     {
250         unsigned char size = reg_size[reg_name];
251         start_gdb_packet();
252         while (size--)
253             send_gdb_partial_packet("xx");
254         return finish_gdb_packet();
255     }
256     else
257     {
258         KDDBGPRINT("KDDBG : Sending registers as memory.\n");
259         return send_gdb_memory(ptr, reg_size[reg_name]);
260     }
261 }
262 
263 
264