[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 #include <pstypes.h>
11
12 /* LOCALS *********************************************************************/
13 static HANDLE gdb_run_thread;
14 static HANDLE gdb_dbg_process;
15 HANDLE gdb_dbg_thread;
16 CONTEXT CurrentContext;
17
18 /* PRIVATE FUNCTIONS **********************************************************/
19 static
20 HANDLE
21 hex_to_thread(char* buffer)
22 {
23 ULONG_PTR ret = 0;
24 char hex;
25 while (*buffer)
26 {
27 hex = hex_value(*buffer++);
28 if (hex < 0)
29 return (HANDLE)ret;
30 ret <<= 4;
31 ret += hex;
32 }
33 return (HANDLE)ret;
34 }
35
36 static
37 ULONG64
38 hex_to_address(char* buffer)
39 {
40 ULONG64 ret = 0;
41 char hex;
42 while (*buffer)
43 {
44 hex = hex_value(*buffer++);
45 if (hex < 0)
46 return ret;
47 ret <<= 4;
48 ret += hex;
49 }
50 return ret;
51 }
52
53 /* H* packets */
54 static
55 void
56 handle_gdb_set_thread(void)
57 {
58 switch (gdb_input[1])
59 {
60 case 'c':
61 if (strcmp(&gdb_input[2], "-1") == 0)
62 gdb_run_thread = (HANDLE)-1;
63 else
64 gdb_run_thread = hex_to_thread(&gdb_input[2]);
65 send_gdb_packet("OK");
66 break;
67 case 'g':
68 if (strncmp(&gdb_input[2], "p-1", 3) == 0)
69 {
70 gdb_dbg_process = (HANDLE)-1;
71 gdb_dbg_thread = (HANDLE)-1;
72 }
73 else
74 {
75 char* ptr = strstr(gdb_input, ".") + 1;
76 gdb_dbg_process = hex_to_thread(&gdb_input[3]);
77 if (strncmp(ptr, "-1", 2) == 0)
78 gdb_dbg_thread = (HANDLE)-1;
79 else
80 gdb_dbg_thread = hex_to_thread(ptr);
81 }
82 send_gdb_packet("OK");
83 break;
84 default:
85 KDDBGPRINT("KDGBD: Unknown 'H' command: %s\n", gdb_input);
86 send_gdb_packet("");
87 }
88 }
89
90 static
91 void
92 handle_gdb_thread_alive(void)
93 {
94 char* ptr = strstr(gdb_input, ".") + 1;
95 CLIENT_ID ClientId;
96 PETHREAD Thread;
97 NTSTATUS Status;
98
99 ClientId.UniqueProcess = hex_to_thread(&gdb_input[2]);
100 ClientId.UniqueThread = hex_to_thread(ptr);
101
102 Status = PsLookupProcessThreadByCid(&ClientId, NULL, &Thread);
103
104 if (!NT_SUCCESS(Status))
105 {
106 /* Thread doesn't exist */
107 send_gdb_packet("E03");
108 return;
109 }
110
111 /* It's OK */
112 ObDereferenceObject(Thread);
113 send_gdb_packet("OK");
114 }
115
116 /* q* packets */
117 static
118 void
119 handle_gdb_query(_Inout_ PKD_CONTEXT KdContext)
120 {
121 if (strncmp(gdb_input, "qSupported:", 11) == 0)
122 {
123 send_gdb_packet("PacketSize=4096;multiprocess+;");
124 return;
125 }
126
127 if (strncmp(gdb_input, "qAttached", 9) == 0)
128 {
129 /* Say yes: the remote server didn't create the process, ReactOS did! */
130 send_gdb_packet("0");
131 return;
132 }
133
134 if (strncmp(gdb_input, "qRcmd,", 6) == 0)
135 {
136 send_gdb_packet("OK");
137 return;
138 }
139
140 if (strcmp(gdb_input, "qC") == 0)
141 {
142 char gdb_out[64];
143 sprintf(gdb_out, "QC:p%p.%p;",
144 PsGetThreadProcessId((PETHREAD)(ULONG_PTR)CurrentStateChange.Thread),
145 PsGetThreadId((PETHREAD)(ULONG_PTR)CurrentStateChange.Thread));
146 send_gdb_packet(gdb_out);
147 return;
148 }
149
150 if (strncmp(gdb_input, "qfThreadInfo", 12) == 0)
151 {
152 LIST_ENTRY* ProcessListHead = (LIST_ENTRY*)KdDebuggerDataBlock->PsActiveProcessHead.Pointer;
153 LIST_ENTRY* ProcessEntry;
154 PEPROCESS Process;
155
156 KDDBGPRINT("ProcessListHead: %p.\n", ProcessListHead);
157
158 /* Maybe this was not initialized yet */
159 if (!ProcessListHead->Flink)
160 {
161 char gdb_out[64];
162 /* Just tell GDB about the current thread */
163 sprintf(gdb_out, "mp%p.%p", PsGetCurrentProcessId(), PsGetCurrentThreadId());
164 send_gdb_packet(gdb_out);
165 gdb_receive_packet(KdContext);
166 if (strncmp(gdb_input, "qsThreadInfo", 12) != 0)
167 {
168 // KdAssert
169 KDDBGPRINT("Received %s instead of qsThreadInfo!\n", gdb_input);
170 while(1);
171 }
172 send_gdb_packet("l");
173 return;
174 }
175
176 /* List all processes */
177 for (ProcessEntry = ProcessListHead->Flink;
178 ProcessEntry != ProcessListHead;
179 ProcessEntry = ProcessEntry->Flink)
180 {
181 BOOLEAN FirstThread = TRUE;
182 LIST_ENTRY* ThreadEntry;
183 PETHREAD Thread;
184 static char gdb_out[1024];
185 char* ptr;
186
187 ptr = gdb_out;
188 Process = CONTAINING_RECORD(ProcessEntry, EPROCESS, ActiveProcessLinks);
189
190 KDDBGPRINT("gdb_out %p.\n", gdb_out);
191
192 *ptr++ = 'm';
193 /* List threads from this process */
194 for (ThreadEntry = Process->ThreadListHead.Flink;
195 ThreadEntry != &Process->ThreadListHead;
196 ThreadEntry = ThreadEntry->Flink)
197 {
198 Thread = CONTAINING_RECORD(ThreadEntry, ETHREAD, ThreadListEntry);
199
200 KDDBGPRINT("ptr %p.\n", ptr);
201
202 /* See if we should add a comma */
203 if (FirstThread)
204 {
205 FirstThread = FALSE;
206 }
207 else
208 {
209 *ptr++ = ',';
210 }
211
212 ptr += _snprintf(ptr, 1024 - (ptr - gdb_out),
213 "p%p.%p", PsGetProcessId(Process), PsGetThreadId(Thread));
214 if (ptr > (gdb_out + 1024))
215 {
216 /* send what we got */
217 KDDBGPRINT("Sending %s.\n", gdb_out);
218 send_gdb_packet(gdb_out);
219 gdb_receive_packet(KdContext);
220 if (strncmp(gdb_input, "qsThreadInfo", 12) != 0)
221 {
222 // KdAssert
223 KDDBGPRINT("Received %s instead of qsThreadInfo!\n", gdb_input);
224 while(1);
225 }
226 /* Start anew */
227 ptr = gdb_out;
228 *ptr++ = 'm';
229 FirstThread = TRUE;
230 }
231 }
232
233 /* send the list for this process */
234 KDDBGPRINT("Sending %s.\n", gdb_out);
235 send_gdb_packet(gdb_out);
236 gdb_receive_packet(KdContext);
237 if (strncmp(gdb_input, "qsThreadInfo", 12) != 0)
238 {
239 // KdAssert
240 KDDBGPRINT("Received %s instead of qsThreadInfo!\n", gdb_input);
241 while(1);
242 }
243 }
244
245 /* We're done. Send end-of-list packet */
246 send_gdb_packet("l");
247 return;
248 }
249
250 KDDBGPRINT("KDGDB: Unknown query: %s\n", gdb_input);
251 send_gdb_packet("");
252 }
253
254 #if 0
255 static
256 KDSTATUS
257 handle_gdb_registers(
258 _Out_ DBGKD_MANIPULATE_STATE64* State,
259 _Out_ PSTRING MessageData,
260 _Out_ PULONG MessageLength)
261 {
262 /*
263 if (gdb_dbg_thread)
264 KDDBGPRINT("Should get registers from other thread!\n");
265 */
266
267 State->ApiNumber = DbgKdGetContextApi;
268 State->ReturnStatus = STATUS_SUCCESS; /* ? */
269 State->Processor = CurrentStateChange.Processor;
270 State->ProcessorLevel = CurrentStateChange.ProcessorLevel;
271 if (MessageData)
272 MessageData->Length = 0;
273 *MessageLength = 0;
274 return KdPacketReceived;
275 }
276 #endif
277
278 static
279 KDSTATUS
280 handle_gdb_read_mem(
281 _Out_ DBGKD_MANIPULATE_STATE64* State,
282 _Out_ PSTRING MessageData,
283 _Out_ PULONG MessageLength)
284 {
285 State->ApiNumber = DbgKdReadVirtualMemoryApi;
286 State->ReturnStatus = STATUS_SUCCESS; /* ? */
287 State->Processor = CurrentStateChange.Processor;
288 State->ProcessorLevel = CurrentStateChange.ProcessorLevel;
289 if (MessageData)
290 MessageData->Length = 0;
291 *MessageLength = 0;
292
293 State->u.ReadMemory.TargetBaseAddress = hex_to_address(&gdb_input[1]);
294 State->u.ReadMemory.TransferCount = hex_to_address(strstr(&gdb_input[1], ",") + 1);
295 return KdPacketReceived;
296 }
297
298 static
299 VOID
300 GetCurrentContextSendHandler(
301 _In_ ULONG PacketType,
302 _In_ PSTRING MessageHeader,
303 _In_ PSTRING MessageData
304 )
305 {
306 DBGKD_MANIPULATE_STATE64* State = (DBGKD_MANIPULATE_STATE64*)MessageHeader->Buffer;
307 const CONTEXT* Context = (const CONTEXT*)MessageData->Buffer;
308
309 if ((PacketType != PACKET_TYPE_KD_STATE_MANIPULATE)
310 || (State->ApiNumber != DbgKdGetContextApi)
311 || (MessageData->Length < sizeof(*Context)))
312 {
313 /* Should we bugcheck ? */
314 while (1);
315 }
316
317 /* Just copy it */
318 RtlCopyMemory(&CurrentContext, Context, sizeof(*Context));
319 }
320
321 static
322 VOID
323 GetCurrentContext(
324 _Out_ DBGKD_MANIPULATE_STATE64* State,
325 _Out_ PSTRING MessageData,
326 _Out_ PULONG MessageLength,
327 _Inout_ PKD_CONTEXT KdContext,
328 _In_opt_ KDP_MANIPULATESTATE_HANDLER ManipulateStateHandler
329 )
330 {
331 State->ApiNumber = DbgKdGetContextApi;
332 State->Processor = CurrentStateChange.Processor;
333 State->ReturnStatus = STATUS_SUCCESS;
334 State->ProcessorLevel = CurrentStateChange.ProcessorLevel;
335 MessageData->Length = 0;
336
337 /* Update the send <-> receive loop handler */
338 KdpSendPacketHandler = GetCurrentContextSendHandler;
339 KdpManipulateStateHandler = ManipulateStateHandler;
340 }
341
342 static
343 VOID
344 SetContextSendHandler(
345 _In_ ULONG PacketType,
346 _In_ PSTRING MessageHeader,
347 _In_ PSTRING MessageData
348 )
349 {
350 DBGKD_MANIPULATE_STATE64* State = (DBGKD_MANIPULATE_STATE64*)MessageHeader->Buffer;
351
352 /* We just confirm that all went well */
353 if ((PacketType != PACKET_TYPE_KD_STATE_MANIPULATE)
354 || (State->ApiNumber != DbgKdSetContextApi)
355 || (State->ReturnStatus != STATUS_SUCCESS))
356 {
357 /* Should we bugcheck ? */
358 while (1);
359 }
360 }
361
362 static
363 KDSTATUS
364 SetContext(
365 _Out_ DBGKD_MANIPULATE_STATE64* State,
366 _Out_ PSTRING MessageData,
367 _Out_ PULONG MessageLength,
368 _Inout_ PKD_CONTEXT KdContext,
369 _In_opt_ KDP_MANIPULATESTATE_HANDLER ManipulateStateHandler
370 )
371 {
372 State->ApiNumber = DbgKdSetContextApi;
373 State->Processor = CurrentStateChange.Processor;
374 State->ReturnStatus = STATUS_SUCCESS;
375 State->ProcessorLevel = CurrentStateChange.ProcessorLevel;
376 MessageData->Length = sizeof(CurrentContext);
377
378 if (MessageData->MaximumLength < sizeof(CurrentContext))
379 {
380 while (1);
381 }
382
383 RtlCopyMemory(MessageData->Buffer, &CurrentContext, sizeof(CurrentContext));
384
385 /* Update the send <-> receive loop handlers */
386 KdpSendPacketHandler = SetContextSendHandler;
387 KdpManipulateStateHandler = ManipulateStateHandler;
388
389 return KdPacketReceived;
390 }
391
392 static
393 KDSTATUS
394 SendContinue(
395 _Out_ DBGKD_MANIPULATE_STATE64* State,
396 _Out_ PSTRING MessageData,
397 _Out_ PULONG MessageLength,
398 _Inout_ PKD_CONTEXT KdContext
399 )
400 {
401 /* Let's go on */
402 State->ApiNumber = DbgKdContinueApi;
403 State->ReturnStatus = STATUS_SUCCESS; /* ? */
404 State->Processor = CurrentStateChange.Processor;
405 State->ProcessorLevel = CurrentStateChange.ProcessorLevel;
406 if (MessageData)
407 MessageData->Length = 0;
408 *MessageLength = 0;
409 State->u.Continue.ContinueStatus = STATUS_SUCCESS;
410
411 /* We definitely are at the end of the send <-> receive loop, if any */
412 KdpSendPacketHandler = NULL;
413 KdpManipulateStateHandler = NULL;
414
415 /* Tell GDB we are fine */
416 send_gdb_packet("OK");
417 return KdPacketReceived;
418 }
419
420 static
421 KDSTATUS
422 UpdateProgramCounterSendContinue(
423 _Out_ DBGKD_MANIPULATE_STATE64* State,
424 _Out_ PSTRING MessageData,
425 _Out_ PULONG MessageLength,
426 _Inout_ PKD_CONTEXT KdContext)
427 {
428 ULONG_PTR ProgramCounter;
429
430 /* So we must get past the breakpoint instruction */
431 ProgramCounter = KdpGetContextPc(&CurrentContext);
432 KdpSetContextPc(&CurrentContext, ProgramCounter + KD_BREAKPOINT_SIZE);
433
434 /* Set the context and continue */
435 SetContext(State, MessageData, MessageLength, KdContext, SendContinue);
436 return KdPacketReceived;
437 }
438
439 static
440 KDSTATUS
441 handle_gdb_v(
442 _Out_ DBGKD_MANIPULATE_STATE64* State,
443 _Out_ PSTRING MessageData,
444 _Out_ PULONG MessageLength,
445 _Inout_ PKD_CONTEXT KdContext)
446 {
447 if (strncmp(gdb_input, "vCont", 5) == 0)
448 {
449 if (gdb_input[5] == '?')
450 {
451 KDSTATUS Status;
452 /* Report what we support */
453 send_gdb_packet("vCont;c;C;s;S");
454 Status = gdb_receive_packet(KdContext);
455 if (Status != KdPacketReceived)
456 return Status;
457 return gdb_interpret_input(State, MessageData, MessageLength, KdContext);
458 }
459
460 if (strcmp(gdb_input, "vCont;c") == 0)
461 {
462 DBGKM_EXCEPTION64* Exception = NULL;
463
464 if (CurrentStateChange.NewState == DbgKdExceptionStateChange)
465 Exception = &CurrentStateChange.u.Exception;
466
467 /* See if we should update the program counter (unlike windbg, gdb doesn't do it for us) */
468 if (Exception && (Exception->ExceptionRecord.ExceptionCode == STATUS_BREAKPOINT)
469 && (Exception->ExceptionRecord.ExceptionInformation[0] == 0))
470 {
471 /* So we get the context, update it and send it back */
472 GetCurrentContext(State, MessageData, MessageLength, KdContext, UpdateProgramCounterSendContinue);
473 return KdPacketReceived;
474 }
475
476 return SendContinue(State, MessageData, MessageLength, KdContext);
477 }
478 }
479
480 return KdPacketReceived;
481 }
482
483 /* GLOBAL FUNCTIONS ***********************************************************/
484 KDSTATUS
485 gdb_interpret_input(
486 _Out_ DBGKD_MANIPULATE_STATE64* State,
487 _Out_ PSTRING MessageData,
488 _Out_ PULONG MessageLength,
489 _Inout_ PKD_CONTEXT KdContext)
490 {
491 KDSTATUS Status;
492 switch (gdb_input[0])
493 {
494 case '?':
495 /* Send the Status */
496 gdb_send_exception();
497 break;
498 case 'g':
499 gdb_send_registers();
500 break;
501 case 'H':
502 handle_gdb_set_thread();
503 break;
504 case 'm':
505 return handle_gdb_read_mem(State, MessageData, MessageLength);
506 case 'q':
507 handle_gdb_query(KdContext);
508 break;
509 case 'T':
510 handle_gdb_thread_alive();
511 break;
512 case 'v':
513 return handle_gdb_v(State, MessageData, MessageLength, KdContext);
514 default:
515 /* We don't know how to handle this request. Maybe this is something for KD */
516 State->ReturnStatus = STATUS_NOT_SUPPORTED;
517 return KdPacketReceived;
518 }
519 /* Get the answer from GDB */
520 Status = gdb_receive_packet(KdContext);
521 if (Status != KdPacketReceived)
522 return Status;
523 /* Try interpreting this new packet */
524 return gdb_interpret_input(State, MessageData, MessageLength, KdContext);
525 }