1/*
2 * Copyright 2005-2007, Axel Dörfler, axeld@pinc-software.de.
3 * Distributed under the terms of the MIT License.
4 *
5 * Copyright 2002, Manuel J. Petit. All rights reserved.
6 * Distributed under the terms of the NewOS License.
7 */
8
9/** Contains the code to interface with a remote GDB */
10
11#include "gdb.h"
12
13#include <string.h>
14#include <stdarg.h>
15#include <stdio.h>
16
17#include <ByteOrder.h>
18
19#include <arch/debug.h>
20#include <arch/debug_console.h>
21#include <debug.h>
22#include <elf.h>
23#include <elf_priv.h>
24#include <smp.h>
25#include <vm/vm.h>
26
27
28enum { INIT = 0, CMDREAD, CKSUM1, CKSUM2, WAITACK, QUIT, GDBSTATES };
29
30
31static char sCommand[512];
32static int sCommandIndex;
33static int sCheckSum;
34
35static char sReply[512];
36static char sSafeMemory[512];
37
38
39// utility functions
40
41
42static int
43parse_nibble(int input)
44{
45	int nibble = 0xff;
46
47	if (input >= '0' && input <= '9')
48		nibble = input - '0';
49
50	if (input >= 'A' && input <= 'F')
51		nibble = 0x0a + input - 'A';
52
53	if (input >= 'a' && input <= 'f')
54		nibble = 0x0a + input - 'a';
55
56	return nibble;
57}
58
59
60//	#pragma mark - GDB protocol
61
62
63static void
64gdb_ack(void)
65{
66	arch_debug_serial_putchar('+');
67}
68
69
70static void
71gdb_nak(void)
72{
73	arch_debug_serial_putchar('-');
74}
75
76
77static void
78gdb_resend_reply(void)
79{
80	arch_debug_serial_puts(sReply);
81}
82
83
84static void
85gdb_reply(char const* format, ...)
86{
87	int i;
88	int len;
89	int sum;
90	va_list args;
91
92	va_start(args, format);
93	sReply[0] = '$';
94	vsprintf(sReply + 1, format, args);
95	va_end(args);
96
97	len = strlen(sReply);
98	sum = 0;
99	for (i = 1; i < len; i++) {
100		sum += sReply[i];
101	}
102	sum %= 256;
103
104	sprintf(sReply + len, "#%02x", sum);
105
106	gdb_resend_reply();
107}
108
109
110static void
111gdb_regreply()
112{
113	sReply[0] = '$';
114
115	// get registers (architecture specific)
116	ssize_t bytesWritten = arch_debug_gdb_get_registers(sReply + 1,
117		sizeof(sReply) - 1);
118	if (bytesWritten < 0) {
119		gdb_reply("E01");
120		return;
121	}
122
123	// add 1 for the leading '$'
124	bytesWritten++;
125
126	// compute check sum
127	int sum = 0;
128	for (int32 i = 1; i < bytesWritten; i++)
129		sum += sReply[i];
130	sum %= 256;
131
132	// print check sum
133	int result = snprintf(sReply + bytesWritten, sizeof(sReply) - bytesWritten,
134		"#%02x", sum);
135	if (result >= (ssize_t)sizeof(sReply) - bytesWritten) {
136		gdb_reply("E01");
137		return;
138	}
139
140	gdb_resend_reply();
141}
142
143
144static void
145gdb_memreply(char const* bytes, int numbytes)
146{
147	int i;
148	int len;
149	int sum;
150
151	sReply[0] = '$';
152	for (i = 0; i < numbytes; i++)
153		sprintf(sReply + 1 + 2 * i, "%02x", (uint8)bytes[i]);
154
155	len = strlen(sReply);
156	sum = 0;
157	for (i = 1; i < len; i++)
158		sum += sReply[i];
159	sum %= 256;
160
161	sprintf(sReply + len, "#%02x", sum);
162
163	gdb_resend_reply();
164}
165
166
167//	#pragma mark - checksum verification
168
169
170static int
171gdb_verify_checksum(void)
172{
173	int i;
174	int len;
175	int sum;
176
177	len = strlen(sCommand);
178	sum = 0;
179	for (i = 0; i < len; i++)
180		sum += sCommand[i];
181	sum %= 256;
182
183	return (sum == sCheckSum) ? 1 : 0;
184}
185
186
187//	#pragma mark - command parsing
188
189
190static int
191gdb_parse_command(void)
192{
193	if (!gdb_verify_checksum()) {
194		gdb_nak();
195		return INIT;
196	} else
197		gdb_ack();
198
199	switch (sCommand[0]) {
200		case '?':
201			// command '?' is used for retrieving the signal
202			// that stopped the program. Fully implemeting
203			// this command requires help from the debugger,
204			// by now we just fake a SIGKILL
205			gdb_reply("S09");	/* SIGKILL = 9 */
206			break;
207
208		case 'H':
209			// Command H (actually Hct) is used to select
210			// the current thread (-1 meaning all threads)
211			// We just fake we recognize the the command
212			// and send an 'OK' response.
213			gdb_reply("OK");
214			break;
215
216		case 'q':
217		{
218			// query commands
219
220			if (strcmp(sCommand + 1, "Supported") == 0) {
221				// get the supported features
222				gdb_reply("");
223			} else if (strcmp(sCommand + 1, "Offsets") == 0) {
224				// get the segment offsets
225				elf_image_info* kernelImage = elf_get_kernel_image();
226				gdb_reply("Text=%lx;Data=%lx;Bss=%lx",
227					kernelImage->text_region.delta,
228					kernelImage->data_region.delta,
229					kernelImage->data_region.delta);
230			} else
231				gdb_reply("");
232
233			break;
234		}
235
236		case 'c':
237			// continue at address
238			// TODO: Parse the address and resume there!
239			return QUIT;
240
241		case 'g':
242			gdb_regreply();
243			break;
244
245		case 'G':
246			// write registers
247			// TODO: Implement!
248			gdb_reply("E01");
249			break;
250
251
252		case 'm':
253		{
254			char* ptr;
255			addr_t address;
256			size_t len;
257
258			// The 'm' command has the form mAAA,LLL
259			// where AAA is the address and LLL is the
260			// number of bytes.
261			ptr = sCommand + 1;
262			address = 0;
263			len = 0;
264			while (ptr && *ptr && (*ptr != ',')) {
265				address <<= 4;
266				address += parse_nibble(*ptr);
267				ptr += 1;
268			}
269			if (*ptr == ',')
270				ptr += 1;
271
272			while (ptr && *ptr) {
273				len <<= 4;
274				len += parse_nibble(*ptr);
275				ptr += 1;
276			}
277
278			if (len > 128)
279				len = 128;
280
281			// We cannot directly access the requested memory
282			// for gdb may be trying to access an stray pointer
283			// We copy the memory to a safe buffer using
284			// the bulletproof debug_memcpy().
285			if (debug_memcpy(B_CURRENT_TEAM, sSafeMemory, (char*)address, len)
286					< 0) {
287				gdb_reply("E02");
288			} else
289				gdb_memreply(sSafeMemory, len);
290
291			break;
292		}
293
294		case 'D':
295			// detach
296			return QUIT;
297
298		case 'k':
299			// Command 'k' actual semantics is 'kill the damn thing'.
300			// However gdb sends that command when you disconnect
301			// from a debug session. I guess that 'kill' for the
302			// kernel would map to reboot... however that's a
303			// a very mean thing to do, instead we just quit
304			// the gdb state machine and fallback to the regular
305			// kernel debugger command prompt.
306			return QUIT;
307
308		case 's':
309			// "step" -- resume (?) at address
310			// TODO: Implement!
311			gdb_reply("E01");
312			break;
313
314		default:
315			gdb_reply("");
316			break;
317	}
318
319	return WAITACK;
320}
321
322
323//	#pragma mark - protocol state machine
324
325
326static int
327gdb_init_handler(int input)
328{
329	switch (input) {
330		case '$':
331			memset(sCommand, 0, sizeof(sCommand));
332			sCommandIndex = 0;
333			return CMDREAD;
334
335		default:
336#if 0
337			gdb_nak();
338#else
339			// looks to me like we should send
340			// a NAK here but it kinda works
341			// better if we just gobble all
342			// junk chars silently
343#endif
344			return INIT;
345	}
346}
347
348
349static int
350gdb_cmdread_handler(int input)
351{
352	switch (input) {
353		case '#':
354			return CKSUM1;
355
356		default:
357			sCommand[sCommandIndex] = input;
358			sCommandIndex += 1;
359			return CMDREAD;
360	}
361}
362
363
364static int
365gdb_cksum1_handler(int input)
366{
367	int nibble = parse_nibble(input);
368
369	if (nibble == 0xff) {
370#if 0
371		gdb_nak();
372		return INIT;
373#else
374		// looks to me like we should send
375		// a NAK here but it kinda works
376		// better if we just gobble all
377		// junk chars silently
378#endif
379	}
380
381	sCheckSum = nibble << 4;
382
383	return CKSUM2;
384}
385
386
387static int
388gdb_cksum2_handler(int input)
389{
390	int nibble = parse_nibble(input);
391
392	if (nibble == 0xff) {
393#if 0
394		gdb_nak();
395		return INIT;
396#else
397		// looks to me like we should send
398		// a NAK here but it kinda works
399		// better if we just gobble all
400		// junk chars silently
401#endif
402	}
403
404	sCheckSum += nibble;
405
406	return gdb_parse_command();
407}
408
409
410static int
411gdb_waitack_handler(int input)
412{
413	switch (input) {
414		case '+':
415			return INIT;
416		case '-':
417			gdb_resend_reply();
418			return WAITACK;
419
420		default:
421			// looks like gdb and us are out of sync,
422			// send a NAK and retry from INIT state.
423			gdb_nak();
424			return INIT;
425	}
426}
427
428
429static int
430gdb_quit_handler(int input)
431{
432	(void)(input);
433
434	// actually we should never be here
435	return QUIT;
436}
437
438
439static int (*dispatch_table[GDBSTATES])(int) = {
440	&gdb_init_handler,
441	&gdb_cmdread_handler,
442	&gdb_cksum1_handler,
443	&gdb_cksum2_handler,
444	&gdb_waitack_handler,
445	&gdb_quit_handler
446};
447
448
449static int
450gdb_state_dispatch(int curr, int input)
451{
452	if (curr < INIT || curr >= GDBSTATES)
453		return QUIT;
454
455	return dispatch_table[curr](input);
456}
457
458
459static int
460gdb_state_machine(void)
461{
462	int state = INIT;
463	int c;
464
465	while (state != QUIT) {
466		c = arch_debug_serial_getchar();
467		state = gdb_state_dispatch(state, c);
468	}
469
470	return 0;
471}
472
473
474//	#pragma mark -
475
476
477int
478cmd_gdb(int argc, char** argv)
479{
480	(void)(argc);
481	(void)(argv);
482
483	return gdb_state_machine();
484}
485