[KDGDB]
[reactos.git] / reactos / drivers / base / kdgdb / gdb_input.c
1 /*
2 * COPYRIGHT: GPL, see COPYING in the top level directory
3 * PROJECT: ReactOS kernel
4 * FILE: drivers/base/kddll/gdb_input.c
5 * PURPOSE: Base functions for the kernel debugger.
6 */
7
8 #include "kdgdb.h"
9
10 /* LOCALS *********************************************************************/
11 static HANDLE gdb_run_thread;
12 static HANDLE gdb_dbg_process;
13 HANDLE gdb_dbg_thread;
14 CONTEXT CurrentContext;
15
16 /* PRIVATE FUNCTIONS **********************************************************/
17 static
18 HANDLE
19 hex_to_thread(char* buffer)
20 {
21 ULONG_PTR ret = 0;
22 char hex;
23 while (*buffer)
24 {
25 hex = hex_value(*buffer++);
26 if (hex < 0)
27 return (HANDLE)ret;
28 ret <<= 4;
29 ret += hex;
30 }
31 return (HANDLE)ret;
32 }
33
34 static
35 ULONG64
36 hex_to_address(char* buffer)
37 {
38 ULONG64 ret = 0;
39 char hex;
40 while (*buffer)
41 {
42 hex = hex_value(*buffer++);
43 if (hex < 0)
44 return ret;
45 ret <<= 4;
46 ret += hex;
47 }
48 return ret;
49 }
50
51 /* H* packets */
52 static
53 void
54 handle_gdb_set_thread(void)
55 {
56 switch (gdb_input[1])
57 {
58 case 'c':
59 if (strcmp(&gdb_input[2], "-1") == 0)
60 gdb_run_thread = (HANDLE)-1;
61 else
62 gdb_run_thread = hex_to_thread(&gdb_input[2]);
63 send_gdb_packet("OK");
64 break;
65 case 'g':
66 if (strncmp(&gdb_input[2], "p-1", 3) == 0)
67 {
68 gdb_dbg_process = (HANDLE)-1;
69 gdb_dbg_thread = (HANDLE)-1;
70 }
71 else
72 {
73 char* ptr = strstr(gdb_input, ".") + 1;
74 gdb_dbg_process = hex_to_thread(&gdb_input[3]);
75 if (strncmp(ptr, "-1", 2) == 0)
76 gdb_dbg_thread = (HANDLE)-1;
77 else
78 gdb_dbg_thread = hex_to_thread(ptr);
79 }
80 send_gdb_packet("OK");
81 break;
82 default:
83 KDDBGPRINT("KDGBD: Unknown 'H' command: %s\n", gdb_input);
84 send_gdb_packet("");
85 }
86 }
87
88 static
89 void
90 handle_gdb_thread_alive(void)
91 {
92 char* ptr = strstr(gdb_input, ".") + 1;
93 CLIENT_ID ClientId;
94 PETHREAD Thread;
95 NTSTATUS Status;
96
97 ClientId.UniqueProcess = hex_to_thread(&gdb_input[2]);
98 ClientId.UniqueThread = hex_to_thread(ptr);
99
100 Status = PsLookupProcessThreadByCid(&ClientId, NULL, &Thread);
101
102 if (!NT_SUCCESS(Status))
103 {
104 /* Thread doesn't exist */
105 send_gdb_packet("E03");
106 return;
107 }
108
109 /* It's OK */
110 ObDereferenceObject(Thread);
111 send_gdb_packet("OK");
112 }
113
114 /* q* packets */
115 static
116 void
117 handle_gdb_query(void)
118 {
119 if (strncmp(gdb_input, "qSupported:", 11) == 0)
120 {
121 send_gdb_packet("PacketSize=4096;multiprocess+;");
122 return;
123 }
124
125 if (strncmp(gdb_input, "qAttached", 9) == 0)
126 {
127 /* Say yes: the remote server didn't create the process, ReactOS did! */
128 send_gdb_packet("0");
129 return;
130 }
131
132 if (strncmp(gdb_input, "qRcmd,", 6) == 0)
133 {
134 send_gdb_packet("OK");
135 return;
136 }
137
138 if (strcmp(gdb_input, "qC") == 0)
139 {
140 char gdb_out[64];
141 sprintf(gdb_out, "QC:p%p.%p;",
142 PsGetThreadProcessId((PETHREAD)(ULONG_PTR)CurrentStateChange.Thread),
143 PsGetThreadId((PETHREAD)(ULONG_PTR)CurrentStateChange.Thread));
144 send_gdb_packet(gdb_out);
145 return;
146 }
147
148 if (strncmp(gdb_input, "qTStatus", 8) == 0)
149 {
150 /* We don't support tracepoints. */
151 send_gdb_packet("T0");
152 return;
153 }
154
155 KDDBGPRINT("KDGDB: Unknown query: %s\n", gdb_input);
156 send_gdb_packet("");
157 }
158
159 #if 0
160 static
161 KDSTATUS
162 handle_gdb_registers(
163 _Out_ DBGKD_MANIPULATE_STATE64* State,
164 _Out_ PSTRING MessageData,
165 _Out_ PULONG MessageLength)
166 {
167 /*
168 if (gdb_dbg_thread)
169 KDDBGPRINT("Should get registers from other thread!\n");
170 */
171
172 State->ApiNumber = DbgKdGetContextApi;
173 State->ReturnStatus = STATUS_SUCCESS; /* ? */
174 State->Processor = CurrentStateChange.Processor;
175 State->ProcessorLevel = CurrentStateChange.ProcessorLevel;
176 if (MessageData)
177 MessageData->Length = 0;
178 *MessageLength = 0;
179 return KdPacketReceived;
180 }
181 #endif
182
183 static
184 KDSTATUS
185 handle_gdb_read_mem(
186 _Out_ DBGKD_MANIPULATE_STATE64* State,
187 _Out_ PSTRING MessageData,
188 _Out_ PULONG MessageLength)
189 {
190 State->ApiNumber = DbgKdReadVirtualMemoryApi;
191 State->ReturnStatus = STATUS_SUCCESS; /* ? */
192 State->Processor = CurrentStateChange.Processor;
193 State->ProcessorLevel = CurrentStateChange.ProcessorLevel;
194 if (MessageData)
195 MessageData->Length = 0;
196 *MessageLength = 0;
197
198 State->u.ReadMemory.TargetBaseAddress = hex_to_address(&gdb_input[1]);
199 State->u.ReadMemory.TransferCount = hex_to_address(strstr(&gdb_input[1], ",") + 1);
200 return KdPacketReceived;
201 }
202
203 static
204 VOID
205 GetCurrentContextSendHandler(
206 _In_ ULONG PacketType,
207 _In_ PSTRING MessageHeader,
208 _In_ PSTRING MessageData
209 )
210 {
211 DBGKD_MANIPULATE_STATE64* State = (DBGKD_MANIPULATE_STATE64*)MessageHeader->Buffer;
212 const CONTEXT* Context = (const CONTEXT*)MessageData->Buffer;
213
214 if ((PacketType != PACKET_TYPE_KD_STATE_MANIPULATE)
215 || (State->ApiNumber != DbgKdGetContextApi)
216 || (MessageData->Length < sizeof(*Context)))
217 {
218 /* Should we bugcheck ? */
219 while (1);
220 }
221
222 /* Just copy it */
223 RtlCopyMemory(&CurrentContext, Context, sizeof(*Context));
224 }
225
226 static
227 VOID
228 GetCurrentContext(
229 _Out_ DBGKD_MANIPULATE_STATE64* State,
230 _Out_ PSTRING MessageData,
231 _Out_ PULONG MessageLength,
232 _Inout_ PKD_CONTEXT KdContext,
233 _In_opt_ KDP_MANIPULATESTATE_HANDLER ManipulateStateHandler
234 )
235 {
236 State->ApiNumber = DbgKdGetContextApi;
237 State->Processor = CurrentStateChange.Processor;
238 State->ReturnStatus = STATUS_SUCCESS;
239 State->ProcessorLevel = CurrentStateChange.ProcessorLevel;
240 MessageData->Length = 0;
241
242 /* Update the send <-> receive loop handler */
243 KdpSendPacketHandler = GetCurrentContextSendHandler;
244 KdpManipulateStateHandler = ManipulateStateHandler;
245 }
246
247 static
248 VOID
249 SetContextSendHandler(
250 _In_ ULONG PacketType,
251 _In_ PSTRING MessageHeader,
252 _In_ PSTRING MessageData
253 )
254 {
255 DBGKD_MANIPULATE_STATE64* State = (DBGKD_MANIPULATE_STATE64*)MessageHeader->Buffer;
256
257 /* We just confirm that all went well */
258 if ((PacketType != PACKET_TYPE_KD_STATE_MANIPULATE)
259 || (State->ApiNumber != DbgKdSetContextApi)
260 || (State->ReturnStatus != STATUS_SUCCESS))
261 {
262 /* Should we bugcheck ? */
263 while (1);
264 }
265 }
266
267 static
268 KDSTATUS
269 SetContext(
270 _Out_ DBGKD_MANIPULATE_STATE64* State,
271 _Out_ PSTRING MessageData,
272 _Out_ PULONG MessageLength,
273 _Inout_ PKD_CONTEXT KdContext,
274 _In_opt_ KDP_MANIPULATESTATE_HANDLER ManipulateStateHandler
275 )
276 {
277 State->ApiNumber = DbgKdSetContextApi;
278 State->Processor = CurrentStateChange.Processor;
279 State->ReturnStatus = STATUS_SUCCESS;
280 State->ProcessorLevel = CurrentStateChange.ProcessorLevel;
281 MessageData->Length = sizeof(CurrentContext);
282
283 if (MessageData->MaximumLength < sizeof(CurrentContext))
284 {
285 while (1);
286 }
287
288 RtlCopyMemory(MessageData->Buffer, &CurrentContext, sizeof(CurrentContext));
289
290 /* Update the send <-> receive loop handlers */
291 KdpSendPacketHandler = SetContextSendHandler;
292 KdpManipulateStateHandler = ManipulateStateHandler;
293
294 return KdPacketReceived;
295 }
296
297 static
298 KDSTATUS
299 SendContinue(
300 _Out_ DBGKD_MANIPULATE_STATE64* State,
301 _Out_ PSTRING MessageData,
302 _Out_ PULONG MessageLength,
303 _Inout_ PKD_CONTEXT KdContext
304 )
305 {
306 /* Let's go on */
307 State->ApiNumber = DbgKdContinueApi;
308 State->ReturnStatus = STATUS_SUCCESS; /* ? */
309 State->Processor = CurrentStateChange.Processor;
310 State->ProcessorLevel = CurrentStateChange.ProcessorLevel;
311 if (MessageData)
312 MessageData->Length = 0;
313 *MessageLength = 0;
314 State->u.Continue.ContinueStatus = STATUS_SUCCESS;
315
316 /* We definitely are at the end of the send <-> receive loop, if any */
317 KdpSendPacketHandler = NULL;
318 KdpManipulateStateHandler = NULL;
319
320 /* Tell GDB we are fine */
321 send_gdb_packet("OK");
322 return KdPacketReceived;
323 }
324
325 static
326 KDSTATUS
327 UpdateProgramCounterSendContinue(
328 _Out_ DBGKD_MANIPULATE_STATE64* State,
329 _Out_ PSTRING MessageData,
330 _Out_ PULONG MessageLength,
331 _Inout_ PKD_CONTEXT KdContext)
332 {
333 ULONG_PTR ProgramCounter;
334
335 /* So we must get past the breakpoint instruction */
336 ProgramCounter = KdpGetContextPc(&CurrentContext);
337 KdpSetContextPc(&CurrentContext, ProgramCounter + KD_BREAKPOINT_SIZE);
338
339 /* Set the context and continue */
340 SetContext(State, MessageData, MessageLength, KdContext, SendContinue);
341 return KdPacketReceived;
342 }
343
344 static
345 KDSTATUS
346 handle_gdb_v(
347 _Out_ DBGKD_MANIPULATE_STATE64* State,
348 _Out_ PSTRING MessageData,
349 _Out_ PULONG MessageLength,
350 _Inout_ PKD_CONTEXT KdContext)
351 {
352 if (strncmp(gdb_input, "vCont", 5) == 0)
353 {
354 if (gdb_input[5] == '?')
355 {
356 KDSTATUS Status;
357 /* Report what we support */
358 send_gdb_packet("vCont;c;C;s;S");
359 Status = gdb_receive_packet(KdContext);
360 if (Status != KdPacketReceived)
361 return Status;
362 return gdb_interpret_input(State, MessageData, MessageLength, KdContext);
363 }
364
365 if (strcmp(gdb_input, "vCont;c") == 0)
366 {
367 DBGKM_EXCEPTION64* Exception = NULL;
368
369 if (CurrentStateChange.NewState == DbgKdExceptionStateChange)
370 Exception = &CurrentStateChange.u.Exception;
371
372 /* See if we should update the program counter (unlike windbg, gdb doesn't do it for us) */
373 if (Exception && (Exception->ExceptionRecord.ExceptionCode == STATUS_BREAKPOINT)
374 && (Exception->ExceptionRecord.ExceptionInformation[0] == 0))
375 {
376 /* So we get the context, update it and send it back */
377 GetCurrentContext(State, MessageData, MessageLength, KdContext, UpdateProgramCounterSendContinue);
378 return KdPacketReceived;
379 }
380
381 return SendContinue(State, MessageData, MessageLength, KdContext);
382 }
383 }
384
385 return KdPacketReceived;
386 }
387
388 /* GLOBAL FUNCTIONS ***********************************************************/
389 KDSTATUS
390 gdb_interpret_input(
391 _Out_ DBGKD_MANIPULATE_STATE64* State,
392 _Out_ PSTRING MessageData,
393 _Out_ PULONG MessageLength,
394 _Inout_ PKD_CONTEXT KdContext)
395 {
396 KDSTATUS Status;
397 switch (gdb_input[0])
398 {
399 case '?':
400 /* Send the Status */
401 gdb_send_exception();
402 break;
403 case 'g':
404 gdb_send_registers();
405 break;
406 case 'H':
407 handle_gdb_set_thread();
408 break;
409 case 'm':
410 return handle_gdb_read_mem(State, MessageData, MessageLength);
411 case 'q':
412 handle_gdb_query();
413 break;
414 case 'T':
415 handle_gdb_thread_alive();
416 break;
417 case 'v':
418 return handle_gdb_v(State, MessageData, MessageLength, KdContext);
419 default:
420 /* We don't know how to handle this request. Maybe this is something for KD */
421 State->ReturnStatus = STATUS_NOT_SUPPORTED;
422 return KdPacketReceived;
423 }
424 /* Get the answer from GDB */
425 Status = gdb_receive_packet(KdContext);
426 if (Status != KdPacketReceived)
427 return Status;
428 /* Try interpreting this new packet */
429 return gdb_interpret_input(State, MessageData, MessageLength, KdContext);
430 }