[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 /* Keep track of where we are for qfThreadInfo/qsThreadInfo */
15 static LIST_ENTRY* CurrentProcessEntry;
16 static LIST_ENTRY* CurrentThreadEntry;
17
18 /* GLOBALS ********************************************************************/
19 HANDLE gdb_dbg_process;
20 HANDLE gdb_dbg_thread;
21
22 /* PRIVATE FUNCTIONS **********************************************************/
23 static
24 HANDLE
25 hex_to_thread(char* buffer)
26 {
27 ULONG_PTR ret = 0;
28 char hex;
29 while (*buffer)
30 {
31 hex = hex_value(*buffer++);
32 if (hex < 0)
33 return (HANDLE)ret;
34 ret <<= 4;
35 ret += hex;
36 }
37 return (HANDLE)ret;
38 }
39
40 static
41 ULONG64
42 hex_to_address(char* buffer)
43 {
44 ULONG64 ret = 0;
45 char hex;
46 while (*buffer)
47 {
48 hex = hex_value(*buffer++);
49 if (hex < 0)
50 return ret;
51 ret <<= 4;
52 ret += hex;
53 }
54 return ret;
55 }
56
57 /* H* packets */
58 static
59 void
60 handle_gdb_set_thread(void)
61 {
62 switch (gdb_input[1])
63 {
64 case 'c':
65 if (strcmp(&gdb_input[2], "-1") == 0)
66 gdb_run_thread = (HANDLE)-1;
67 else
68 gdb_run_thread = hex_to_thread(&gdb_input[2]);
69 send_gdb_packet("OK");
70 break;
71 case 'g':
72 KDDBGPRINT("Setting debug thread: %s.\n", gdb_input);
73 if (strncmp(&gdb_input[2], "p-1", 3) == 0)
74 {
75 gdb_dbg_process = (HANDLE)-1;
76 gdb_dbg_thread = (HANDLE)-1;
77 }
78 else
79 {
80 char* ptr = strstr(gdb_input, ".") + 1;
81 gdb_dbg_process = hex_to_thread(&gdb_input[3]);
82 if (strncmp(ptr, "-1", 2) == 0)
83 gdb_dbg_thread = (HANDLE)-1;
84 else
85 gdb_dbg_thread = hex_to_thread(ptr);
86 }
87 send_gdb_packet("OK");
88 break;
89 default:
90 KDDBGPRINT("KDGBD: Unknown 'H' command: %s\n", gdb_input);
91 send_gdb_packet("");
92 }
93 }
94
95 KDSTATUS
96 gdb_receive_and_interpret_packet(
97 _Out_ DBGKD_MANIPULATE_STATE64* State,
98 _Out_ PSTRING MessageData,
99 _Out_ PULONG MessageLength,
100 _Inout_ PKD_CONTEXT KdContext)
101 {
102 KDSTATUS Status = gdb_receive_packet(KdContext);
103
104 if (Status != KdPacketReceived)
105 return Status;
106 return gdb_interpret_input(State, MessageData, MessageLength, KdContext);
107 }
108
109 static
110 void
111 handle_gdb_thread_alive(void)
112 {
113 char* ptr = strstr(gdb_input, ".") + 1;
114 CLIENT_ID ClientId;
115 PETHREAD Thread;
116 NTSTATUS Status;
117
118 ClientId.UniqueProcess = hex_to_thread(&gdb_input[2]);
119 ClientId.UniqueThread = hex_to_thread(ptr);
120
121 Status = PsLookupProcessThreadByCid(&ClientId, NULL, &Thread);
122
123 if (!NT_SUCCESS(Status))
124 {
125 /* Thread doesn't exist */
126 send_gdb_packet("E03");
127 return;
128 }
129
130 /* It's OK */
131 ObDereferenceObject(Thread);
132 send_gdb_packet("OK");
133 }
134
135 /* q* packets */
136 static
137 KDSTATUS
138 handle_gdb_query(
139 _Out_ DBGKD_MANIPULATE_STATE64* State,
140 _Out_ PSTRING MessageData,
141 _Out_ PULONG MessageLength,
142 _Inout_ PKD_CONTEXT KdContext)
143 {
144 if (strncmp(gdb_input, "qSupported:", 11) == 0)
145 {
146 send_gdb_packet("PacketSize=4096;multiprocess+;");
147 return gdb_receive_and_interpret_packet(State, MessageData, MessageLength, KdContext);
148 }
149
150 if (strncmp(gdb_input, "qAttached", 9) == 0)
151 {
152 /* Say no: We didn't attach, we create the process! */
153 send_gdb_packet("0");
154 return gdb_receive_and_interpret_packet(State, MessageData, MessageLength, KdContext);
155 }
156
157 if (strncmp(gdb_input, "qRcmd,", 6) == 0)
158 {
159 send_gdb_packet("OK");
160 return gdb_receive_and_interpret_packet(State, MessageData, MessageLength, KdContext);
161 }
162
163 if (strcmp(gdb_input, "qC") == 0)
164 {
165 char gdb_out[64];
166 sprintf(gdb_out, "QC:p%p.%p;",
167 PsGetThreadProcessId((PETHREAD)(ULONG_PTR)CurrentStateChange.Thread),
168 PsGetThreadId((PETHREAD)(ULONG_PTR)CurrentStateChange.Thread));
169 send_gdb_packet(gdb_out);
170 return gdb_receive_and_interpret_packet(State, MessageData, MessageLength, KdContext);
171 }
172
173 if ((strncmp(gdb_input, "qfThreadInfo", 12) == 0)
174 || (strncmp(gdb_input, "qsThreadInfo", 12) == 0))
175 {
176 LIST_ENTRY* ProcessListHead = (LIST_ENTRY*)KdDebuggerDataBlock->PsActiveProcessHead.Pointer;
177 BOOLEAN FirstThread = TRUE;
178 PEPROCESS Process;
179 PETHREAD Thread;
180 char gdb_out[1024];
181 char* ptr;
182 BOOLEAN Resuming = strncmp(gdb_input, "qsThreadInfo", 12) == 0;
183
184 /* Maybe this was not initialized yet */
185 if (!ProcessListHead->Flink)
186 {
187 char gdb_out[64];
188
189 if (Resuming)
190 {
191 /* there is only one thread to tell about */
192 send_gdb_packet("l");
193 return gdb_receive_and_interpret_packet(State, MessageData, MessageLength, KdContext);
194 }
195 /* Just tell GDB about the current thread */
196 sprintf(gdb_out, "mp%p.%p", PsGetCurrentProcessId(), PsGetCurrentThreadId());
197 send_gdb_packet(gdb_out);
198 /* GDB can ask anything at this point, it isn't necessarily a qsThreadInfo packet */
199 return gdb_receive_and_interpret_packet(State, MessageData, MessageLength, KdContext);
200 }
201
202 if (Resuming)
203 {
204 if (CurrentThreadEntry == NULL)
205 CurrentProcessEntry = CurrentProcessEntry->Flink;
206 }
207 else
208 CurrentProcessEntry = ProcessListHead->Flink;
209
210 if (CurrentProcessEntry == ProcessListHead)
211 {
212 /* We're done */
213 send_gdb_packet("l");
214 return gdb_receive_and_interpret_packet(State, MessageData, MessageLength, KdContext);
215 }
216
217 Process = CONTAINING_RECORD(CurrentProcessEntry, EPROCESS, ActiveProcessLinks);
218
219 if (Resuming && CurrentThreadEntry != NULL)
220 CurrentThreadEntry = CurrentThreadEntry->Flink;
221 else
222 CurrentThreadEntry = Process->ThreadListHead.Flink;
223
224 ptr = gdb_out;
225
226 *ptr++ = 'm';
227 /* List threads from this process */
228 for ( ;
229 CurrentThreadEntry != &Process->ThreadListHead;
230 CurrentThreadEntry = CurrentThreadEntry->Flink)
231 {
232 Thread = CONTAINING_RECORD(CurrentThreadEntry, ETHREAD, ThreadListEntry);
233
234 /* See if we should add a comma */
235 if (FirstThread)
236 {
237 FirstThread = FALSE;
238 }
239 else
240 {
241 *ptr++ = ',';
242 }
243
244 ptr += _snprintf(ptr, 1024 - (ptr - gdb_out),
245 "p%p.%p", PsGetProcessId(Process), PsGetThreadId(Thread));
246 if (ptr > (gdb_out + 1024))
247 {
248 /* send what we got */
249 send_gdb_packet(gdb_out);
250 /* GDB can ask anything at this point, it isn't necessarily a qsThreadInfo packet */
251 return gdb_receive_and_interpret_packet(State, MessageData, MessageLength, KdContext);
252 }
253 }
254
255 /* send the list for this process */
256 send_gdb_packet(gdb_out);
257 CurrentThreadEntry = NULL;
258 /* GDB can ask anything at this point, it isn't necessarily a qsThreadInfo packet */
259 return gdb_receive_and_interpret_packet(State, MessageData, MessageLength, KdContext);
260 }
261
262 KDDBGPRINT("KDGDB: Unknown query: %s\n", gdb_input);
263 send_gdb_packet("");
264 return gdb_receive_and_interpret_packet(State, MessageData, MessageLength, KdContext);
265 }
266
267 #if 0
268 static
269 KDSTATUS
270 handle_gdb_registers(
271 _Out_ DBGKD_MANIPULATE_STATE64* State,
272 _Out_ PSTRING MessageData,
273 _Out_ PULONG MessageLength)
274 {
275 /*
276 if (gdb_dbg_thread)
277 KDDBGPRINT("Should get registers from other thread!\n");
278 */
279
280 State->ApiNumber = DbgKdGetContextApi;
281 State->ReturnStatus = STATUS_SUCCESS; /* ? */
282 State->Processor = CurrentStateChange.Processor;
283 State->ProcessorLevel = CurrentStateChange.ProcessorLevel;
284 if (MessageData)
285 MessageData->Length = 0;
286 *MessageLength = 0;
287 return KdPacketReceived;
288 }
289 #endif
290
291 static
292 void
293 ReadMemorySendHandler(
294 _In_ ULONG PacketType,
295 _In_ PSTRING MessageHeader,
296 _In_ PSTRING MessageData)
297 {
298 DBGKD_MANIPULATE_STATE64* State = (DBGKD_MANIPULATE_STATE64*)MessageHeader->Buffer;
299
300 if (PacketType != PACKET_TYPE_KD_STATE_MANIPULATE)
301 {
302 // KdAssert
303 KDDBGPRINT("Wrong packet type (%lu) received after DbgKdReadVirtualMemoryApi request.\n", PacketType);
304 while (1);
305 }
306
307 if (State->ApiNumber != DbgKdReadVirtualMemoryApi)
308 {
309 KDDBGPRINT("Wrong API number (%lu) after DbgKdReadVirtualMemoryApi request.\n", State->ApiNumber);
310 }
311
312 /* Check status */
313 if (!NT_SUCCESS(State->ReturnStatus))
314 send_gdb_ntstatus(State->ReturnStatus);
315 else
316 send_gdb_memory(MessageData->Buffer, MessageData->Length);
317 KdpSendPacketHandler = NULL;
318 KdpManipulateStateHandler = NULL;
319 }
320
321 static
322 KDSTATUS
323 handle_gdb_read_mem(
324 _Out_ DBGKD_MANIPULATE_STATE64* State,
325 _Out_ PSTRING MessageData,
326 _Out_ PULONG MessageLength)
327 {
328 State->ApiNumber = DbgKdReadVirtualMemoryApi;
329 State->ReturnStatus = STATUS_SUCCESS; /* ? */
330 State->Processor = CurrentStateChange.Processor;
331 State->ProcessorLevel = CurrentStateChange.ProcessorLevel;
332 if (MessageData)
333 MessageData->Length = 0;
334 *MessageLength = 0;
335
336 State->u.ReadMemory.TargetBaseAddress = hex_to_address(&gdb_input[1]);
337 State->u.ReadMemory.TransferCount = hex_to_address(strstr(&gdb_input[1], ",") + 1);
338
339 /* KD will reply with KdSendPacket. Catch it */
340 KdpSendPacketHandler = ReadMemorySendHandler;
341
342 return KdPacketReceived;
343 }
344
345 static
346 KDSTATUS
347 handle_gdb_v(
348 _Out_ DBGKD_MANIPULATE_STATE64* State,
349 _Out_ PSTRING MessageData,
350 _Out_ PULONG MessageLength,
351 _Inout_ PKD_CONTEXT KdContext)
352 {
353 if (strncmp(gdb_input, "vCont", 5) == 0)
354 {
355 if (gdb_input[5] == '?')
356 {
357 KDSTATUS Status;
358 /* Report what we support */
359 send_gdb_packet("vCont;c;C;s;S");
360 Status = gdb_receive_packet(KdContext);
361 if (Status != KdPacketReceived)
362 return Status;
363 return gdb_interpret_input(State, MessageData, MessageLength, KdContext);
364 }
365
366 if (strcmp(gdb_input, "vCont;c") == 0)
367 {
368 DBGKM_EXCEPTION64* Exception = NULL;
369
370 /* Tell GDB everything is fine, we will handle it */
371 send_gdb_packet("OK");
372
373 if (CurrentStateChange.NewState == DbgKdExceptionStateChange)
374 Exception = &CurrentStateChange.u.Exception;
375
376 /* See if we should update the program counter (unlike windbg, gdb doesn't do it for us) */
377 if (Exception && (Exception->ExceptionRecord.ExceptionCode == STATUS_BREAKPOINT)
378 && (Exception->ExceptionRecord.ExceptionInformation[0] == 0))
379 {
380 ULONG_PTR ProgramCounter;
381
382 /* So we must get past the breakpoint instruction */
383 ProgramCounter = KdpGetContextPc(&CurrentContext);
384 KdpSetContextPc(&CurrentContext, ProgramCounter + KD_BREAKPOINT_SIZE);
385
386 SetContextManipulateHandler(State, MessageData, MessageLength, KdContext);
387 KdpManipulateStateHandler = ContinueManipulateStateHandler;
388 return KdPacketReceived;
389 }
390
391 return ContinueManipulateStateHandler(State, MessageData, MessageLength, KdContext);
392 }
393 }
394
395 KDDBGPRINT("Unhandled 'v' packet: %s\n", gdb_input);
396 return KdPacketReceived;
397 }
398
399 /* GLOBAL FUNCTIONS ***********************************************************/
400 KDSTATUS
401 gdb_interpret_input(
402 _Out_ DBGKD_MANIPULATE_STATE64* State,
403 _Out_ PSTRING MessageData,
404 _Out_ PULONG MessageLength,
405 _Inout_ PKD_CONTEXT KdContext)
406 {
407 switch (gdb_input[0])
408 {
409 case '?':
410 /* Send the Status */
411 gdb_send_exception();
412 break;
413 case 'g':
414 return gdb_send_registers(State, MessageData, MessageLength, KdContext);
415 case 'H':
416 handle_gdb_set_thread();
417 break;
418 case 'm':
419 return handle_gdb_read_mem(State, MessageData, MessageLength);
420 case 'p':
421 return gdb_send_register(State, MessageData, MessageLength, KdContext);
422 case 'q':
423 return handle_gdb_query(State, MessageData, MessageLength, KdContext);
424 case 'T':
425 handle_gdb_thread_alive();
426 break;
427 case 'v':
428 return handle_gdb_v(State, MessageData, MessageLength, KdContext);
429 default:
430 /* We don't know how to handle this request. Maybe this is something for KD */
431 State->ReturnStatus = STATUS_NOT_SUPPORTED;
432 KDDBGPRINT("Unsupported GDB command: %s.\n", gdb_input);
433 return KdPacketReceived;
434 }
435 return gdb_receive_and_interpret_packet(State, MessageData, MessageLength, KdContext);
436 }