[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 void
280 ReadMemorySendHandler(
281 _In_ ULONG PacketType,
282 _In_ PSTRING MessageHeader,
283 _In_ PSTRING MessageData)
284 {
285 DBGKD_MANIPULATE_STATE64* State = (DBGKD_MANIPULATE_STATE64*)MessageHeader->Buffer;
286
287 if (PacketType != PACKET_TYPE_KD_STATE_MANIPULATE)
288 {
289 // KdAssert
290 KDDBGPRINT("Wrong packet type (%lu) received after DbgKdReadVirtualMemoryApi request.\n", PacketType);
291 while (1);
292 }
293
294 if (State->ApiNumber != DbgKdReadVirtualMemoryApi)
295 {
296 KDDBGPRINT("Wrong API number (%lu) after DbgKdReadVirtualMemoryApi request.\n", State->ApiNumber);
297 }
298
299 /* Check status */
300 if (!NT_SUCCESS(State->ReturnStatus))
301 send_gdb_ntstatus(State->ReturnStatus);
302 else
303 send_gdb_memory(MessageData->Buffer, MessageData->Length);
304 KdpSendPacketHandler = NULL;
305 }
306
307 static
308 KDSTATUS
309 handle_gdb_read_mem(
310 _Out_ DBGKD_MANIPULATE_STATE64* State,
311 _Out_ PSTRING MessageData,
312 _Out_ PULONG MessageLength)
313 {
314 State->ApiNumber = DbgKdReadVirtualMemoryApi;
315 State->ReturnStatus = STATUS_SUCCESS; /* ? */
316 State->Processor = CurrentStateChange.Processor;
317 State->ProcessorLevel = CurrentStateChange.ProcessorLevel;
318 if (MessageData)
319 MessageData->Length = 0;
320 *MessageLength = 0;
321
322 State->u.ReadMemory.TargetBaseAddress = hex_to_address(&gdb_input[1]);
323 State->u.ReadMemory.TransferCount = hex_to_address(strstr(&gdb_input[1], ",") + 1);
324
325 /* KD will reply with KdSendPacket. Catch it */
326 KdpSendPacketHandler = ReadMemorySendHandler;
327
328 return KdPacketReceived;
329 }
330
331 static
332 VOID
333 GetCurrentContextSendHandler(
334 _In_ ULONG PacketType,
335 _In_ PSTRING MessageHeader,
336 _In_ PSTRING MessageData
337 )
338 {
339 DBGKD_MANIPULATE_STATE64* State = (DBGKD_MANIPULATE_STATE64*)MessageHeader->Buffer;
340 const CONTEXT* Context = (const CONTEXT*)MessageData->Buffer;
341
342 if ((PacketType != PACKET_TYPE_KD_STATE_MANIPULATE)
343 || (State->ApiNumber != DbgKdGetContextApi)
344 || (MessageData->Length < sizeof(*Context)))
345 {
346 /* Should we bugcheck ? */
347 while (1);
348 }
349
350 /* Just copy it */
351 RtlCopyMemory(&CurrentContext, Context, sizeof(*Context));
352 }
353
354 static
355 VOID
356 GetCurrentContext(
357 _Out_ DBGKD_MANIPULATE_STATE64* State,
358 _Out_ PSTRING MessageData,
359 _Out_ PULONG MessageLength,
360 _Inout_ PKD_CONTEXT KdContext,
361 _In_opt_ KDP_MANIPULATESTATE_HANDLER ManipulateStateHandler
362 )
363 {
364 State->ApiNumber = DbgKdGetContextApi;
365 State->Processor = CurrentStateChange.Processor;
366 State->ReturnStatus = STATUS_SUCCESS;
367 State->ProcessorLevel = CurrentStateChange.ProcessorLevel;
368 MessageData->Length = 0;
369
370 /* Update the send <-> receive loop handler */
371 KdpSendPacketHandler = GetCurrentContextSendHandler;
372 KdpManipulateStateHandler = ManipulateStateHandler;
373 }
374
375 static
376 VOID
377 SetContextSendHandler(
378 _In_ ULONG PacketType,
379 _In_ PSTRING MessageHeader,
380 _In_ PSTRING MessageData
381 )
382 {
383 DBGKD_MANIPULATE_STATE64* State = (DBGKD_MANIPULATE_STATE64*)MessageHeader->Buffer;
384
385 /* We just confirm that all went well */
386 if ((PacketType != PACKET_TYPE_KD_STATE_MANIPULATE)
387 || (State->ApiNumber != DbgKdSetContextApi)
388 || (State->ReturnStatus != STATUS_SUCCESS))
389 {
390 /* Should we bugcheck ? */
391 while (1);
392 }
393 }
394
395 static
396 KDSTATUS
397 SetContext(
398 _Out_ DBGKD_MANIPULATE_STATE64* State,
399 _Out_ PSTRING MessageData,
400 _Out_ PULONG MessageLength,
401 _Inout_ PKD_CONTEXT KdContext,
402 _In_opt_ KDP_MANIPULATESTATE_HANDLER ManipulateStateHandler
403 )
404 {
405 State->ApiNumber = DbgKdSetContextApi;
406 State->Processor = CurrentStateChange.Processor;
407 State->ReturnStatus = STATUS_SUCCESS;
408 State->ProcessorLevel = CurrentStateChange.ProcessorLevel;
409 MessageData->Length = sizeof(CurrentContext);
410
411 if (MessageData->MaximumLength < sizeof(CurrentContext))
412 {
413 while (1);
414 }
415
416 RtlCopyMemory(MessageData->Buffer, &CurrentContext, sizeof(CurrentContext));
417
418 /* Update the send <-> receive loop handlers */
419 KdpSendPacketHandler = SetContextSendHandler;
420 KdpManipulateStateHandler = ManipulateStateHandler;
421
422 return KdPacketReceived;
423 }
424
425 static
426 KDSTATUS
427 SendContinue(
428 _Out_ DBGKD_MANIPULATE_STATE64* State,
429 _Out_ PSTRING MessageData,
430 _Out_ PULONG MessageLength,
431 _Inout_ PKD_CONTEXT KdContext
432 )
433 {
434 /* Let's go on */
435 State->ApiNumber = DbgKdContinueApi;
436 State->ReturnStatus = STATUS_SUCCESS; /* ? */
437 State->Processor = CurrentStateChange.Processor;
438 State->ProcessorLevel = CurrentStateChange.ProcessorLevel;
439 if (MessageData)
440 MessageData->Length = 0;
441 *MessageLength = 0;
442 State->u.Continue.ContinueStatus = STATUS_SUCCESS;
443
444 /* We definitely are at the end of the send <-> receive loop, if any */
445 KdpSendPacketHandler = NULL;
446 KdpManipulateStateHandler = NULL;
447
448 /* Tell GDB we are fine */
449 send_gdb_packet("OK");
450 return KdPacketReceived;
451 }
452
453 static
454 KDSTATUS
455 UpdateProgramCounterSendContinue(
456 _Out_ DBGKD_MANIPULATE_STATE64* State,
457 _Out_ PSTRING MessageData,
458 _Out_ PULONG MessageLength,
459 _Inout_ PKD_CONTEXT KdContext)
460 {
461 ULONG_PTR ProgramCounter;
462
463 /* So we must get past the breakpoint instruction */
464 ProgramCounter = KdpGetContextPc(&CurrentContext);
465 KdpSetContextPc(&CurrentContext, ProgramCounter + KD_BREAKPOINT_SIZE);
466
467 /* Set the context and continue */
468 SetContext(State, MessageData, MessageLength, KdContext, SendContinue);
469 return KdPacketReceived;
470 }
471
472 static
473 KDSTATUS
474 handle_gdb_v(
475 _Out_ DBGKD_MANIPULATE_STATE64* State,
476 _Out_ PSTRING MessageData,
477 _Out_ PULONG MessageLength,
478 _Inout_ PKD_CONTEXT KdContext)
479 {
480 if (strncmp(gdb_input, "vCont", 5) == 0)
481 {
482 if (gdb_input[5] == '?')
483 {
484 KDSTATUS Status;
485 /* Report what we support */
486 send_gdb_packet("vCont;c;C;s;S");
487 Status = gdb_receive_packet(KdContext);
488 if (Status != KdPacketReceived)
489 return Status;
490 return gdb_interpret_input(State, MessageData, MessageLength, KdContext);
491 }
492
493 if (strcmp(gdb_input, "vCont;c") == 0)
494 {
495 DBGKM_EXCEPTION64* Exception = NULL;
496
497 if (CurrentStateChange.NewState == DbgKdExceptionStateChange)
498 Exception = &CurrentStateChange.u.Exception;
499
500 /* See if we should update the program counter (unlike windbg, gdb doesn't do it for us) */
501 if (Exception && (Exception->ExceptionRecord.ExceptionCode == STATUS_BREAKPOINT)
502 && (Exception->ExceptionRecord.ExceptionInformation[0] == 0))
503 {
504 /* So we get the context, update it and send it back */
505 GetCurrentContext(State, MessageData, MessageLength, KdContext, UpdateProgramCounterSendContinue);
506 return KdPacketReceived;
507 }
508
509 return SendContinue(State, MessageData, MessageLength, KdContext);
510 }
511 }
512
513 return KdPacketReceived;
514 }
515
516 /* GLOBAL FUNCTIONS ***********************************************************/
517 KDSTATUS
518 gdb_interpret_input(
519 _Out_ DBGKD_MANIPULATE_STATE64* State,
520 _Out_ PSTRING MessageData,
521 _Out_ PULONG MessageLength,
522 _Inout_ PKD_CONTEXT KdContext)
523 {
524 KDSTATUS Status;
525 switch (gdb_input[0])
526 {
527 case '?':
528 /* Send the Status */
529 gdb_send_exception();
530 break;
531 case 'g':
532 gdb_send_registers();
533 break;
534 case 'H':
535 handle_gdb_set_thread();
536 break;
537 case 'm':
538 return handle_gdb_read_mem(State, MessageData, MessageLength);
539 case 'q':
540 handle_gdb_query(KdContext);
541 break;
542 case 'T':
543 handle_gdb_thread_alive();
544 break;
545 case 'v':
546 return handle_gdb_v(State, MessageData, MessageLength, KdContext);
547 default:
548 /* We don't know how to handle this request. Maybe this is something for KD */
549 State->ReturnStatus = STATUS_NOT_SUPPORTED;
550 return KdPacketReceived;
551 }
552 /* Get the answer from GDB */
553 Status = gdb_receive_packet(KdContext);
554 if (Status != KdPacketReceived)
555 return Status;
556 /* Try interpreting this new packet */
557 return gdb_interpret_input(State, MessageData, MessageLength, KdContext);
558 }