main gdb_remote.c
  1/*
  2 Copyright 2013 Michael Pavone
  3 This file is part of BlastEm.
  4 BlastEm is free software distributed under the terms of the GNU General Public License version 3 or greater. See COPYING for full license text.
  5*/
  6#define GDB_IN_FD STDIN_FILENO
  7#define GDB_OUT_FD STDOUT_FILENO
  8#define GDB_READ read
  9#define GDB_WRITE write
 10
 11#include "gdb_remote.h"
 12#include "68kinst.h"
 13#include "debug.h"
 14#include "util.h"
 15#include <unistd.h>
 16#include <fcntl.h>
 17#include <stddef.h>
 18#include <stdlib.h>
 19#include <stdio.h>
 20#include <string.h>
 21
 22
 23
 24#define INITIAL_BUFFER_SIZE (16*1024)
 25
 26#ifdef DO_DEBUG_PRINT
 27#define dfprintf fprintf
 28#else
 29#define dfprintf
 30#endif
 31
 32char * buf = NULL;
 33char * curbuf = NULL;
 34char * end = NULL;
 35size_t bufsize;
 36int cont = 0;
 37int expect_break_response=0;
 38uint32_t resume_pc;
 39
 40
 41static uint16_t branch_t;
 42static uint16_t branch_f;
 43
 44static bp_def * breakpoints = NULL;
 45static uint32_t bp_index = 0;
 46
 47
 48void hex_32(uint32_t num, char * out)
 49{
 50	for (int32_t shift = 28; shift >= 0; shift -= 4)
 51	{
 52		uint8_t nibble = num >> shift & 0xF;
 53		*(out++) = nibble > 9 ? nibble - 0xA + 'A' : nibble + '0';
 54	}
 55}
 56
 57void hex_16(uint16_t num, char * out)
 58{
 59	for (int16_t shift = 14; shift >= 0; shift -= 4)
 60	{
 61		uint8_t nibble = num >> shift & 0xF;
 62		*(out++) = nibble > 9 ? nibble - 0xA + 'A' : nibble + '0';
 63	}
 64}
 65
 66void hex_8(uint8_t num, char * out)
 67{
 68	uint8_t nibble = num >> 4;
 69	*(out++) = nibble > 9 ? nibble - 0xA + 'A' : nibble + '0';
 70	nibble = num & 0xF;
 71	*out = nibble > 9 ? nibble - 0xA + 'A' : nibble + '0';
 72}
 73
 74void gdb_calc_checksum(char * command, char *out)
 75{
 76	uint8_t checksum = 0;
 77	while (*command)
 78	{
 79		checksum += *(command++);
 80	}
 81	hex_8(checksum, out);
 82}
 83
 84void write_or_die(int fd, const void *buf, size_t count)
 85{
 86	if (GDB_WRITE(fd, buf, count) < count) {
 87		fatal_error("Error writing to stdout\n");
 88	}
 89}
 90
 91void gdb_send_command(char * command)
 92{
 93	char end[3];
 94	write_or_die(GDB_OUT_FD, "$", 1);
 95	write_or_die(GDB_OUT_FD, command, strlen(command));
 96	end[0] = '#';
 97	gdb_calc_checksum(command, end+1);
 98	write_or_die(GDB_OUT_FD, end, 3);
 99	dfprintf(stderr, "Sent $%s#%c%c\n", command, end[1], end[2]);
100}
101
102uint32_t calc_status(m68k_context * context)
103{
104	uint32_t status = context->status << 3;
105	for (int i = 0; i < 5; i++)
106	{
107		status <<= 1;
108		status |= context->flags[i];
109	}
110	return status;
111}
112
113void update_status(m68k_context * context, uint16_t value)
114{
115	context->status = value >> 8;
116	for (int i = 4; i >= 0; i--)
117	{
118		context->flags[i] = value & 1;
119		value >>= 1;
120	}
121}
122
123uint8_t m68k_read_byte(m68k_context * context, uint32_t address)
124{
125	
126	genesis_context *gen = context->system;
127	//TODO: Use generated read/write functions to support access to hardware that is not ROM or RAM
128	uint16_t * word = get_native_pointer(address & 0xFFFFFFFE, (void **)context->mem_pointers, &context->options->gen);
129	if (word) {	
130	if (address & 1) {
131		return *word;
132	}
133	return *word >> 8;
134}
135	if (address >= 0xA00000 && address < 0xA04000) {
136		return gen->zram[address & 0x1FFF];
137	}
138	return 0;
139}
140
141void m68k_write_byte(m68k_context * context, uint32_t address, uint8_t value)
142{
143	genesis_context *gen = context->system;
144	//TODO: Use generated read/write functions so that memory map is properly respected
145	uint16_t * word = get_native_pointer(address & 0xFFFFFFFE, (void **)context->mem_pointers, &context->options->gen);
146	if (word) {
147		if (address & 1) {
148			*word = (*word & 0xFF00) | value;
149		} else {
150			*word = (*word & 0xFF) | value << 8;
151		}
152		//TODO: Deal with this more generally once m68k_handle_code_write can handle it
153		if (address >= 0xE00000) {
154			m68k_handle_code_write(address, context);
155		}
156		return;
157	}
158	if (address >= 0xA00000 && address < 0xA04000) {
159		gen->zram[address & 0x1FFF] = value;
160		genesis_context * gen = context->system;
161		z80_handle_code_write(address & 0x1FFF, gen->z80);
162		return;
163	} else {
164		return;
165	}
166}
167
168void gdb_run_command(m68k_context * context, uint32_t pc, char * command)
169{
170	char send_buf[512];
171	dfprintf(stderr, "Received command %s\n", command);
172	switch(*command)
173	{
174
175	case 'c':
176		if (*(command+1) != 0) {
177			//TODO: implement resuming at an arbitrary address
178			goto not_impl;
179		}
180		cont = 1;
181		expect_break_response = 1;
182		break;
183	case 's': {
184		if (*(command+1) != 0) {
185			//TODO: implement resuming at an arbitrary address
186			goto not_impl;
187		}
188		m68kinst inst;
189		genesis_context *gen = context->system;
190		uint16_t * pc_ptr = get_native_pointer(pc, (void **)context->mem_pointers, &context->options->gen);
191		if (!pc_ptr) {
192			fatal_error("Entered gdb remote debugger stub at address %X\n", pc);
193		}
194		uint16_t * after_pc = m68k_decode(pc_ptr, &inst, pc & 0xFFFFFF);
195		uint32_t after = pc + (after_pc-pc_ptr)*2;
196
197		if (inst.op == M68K_RTS) {
198			after = (read_dma_value(context->aregs[7]/2) << 16) | read_dma_value(context->aregs[7]/2 + 1);
199		} else if (inst.op == M68K_RTE || inst.op == M68K_RTR) {
200			after = (read_dma_value((context->aregs[7]+2)/2) << 16) | read_dma_value((context->aregs[7]+2)/2 + 1);
201		} else if(m68k_is_branch(&inst)) {
202			if (inst.op == M68K_BCC && inst.extra.cond != COND_TRUE) {
203				branch_f = after;
204				branch_t = m68k_branch_target(&inst, context->dregs, context->aregs) & 0xFFFFFF;
205				insert_breakpoint(context, branch_t, gdb_debug_enter);
206			} else if(inst.op == M68K_DBCC && inst.extra.cond != COND_FALSE) {
207				branch_t = after;
208				branch_f = m68k_branch_target(&inst, context->dregs, context->aregs) & 0xFFFFFF;
209				insert_breakpoint(context, branch_f, gdb_debug_enter);
210			} else {
211				after = m68k_branch_target(&inst, context->dregs, context->aregs) & 0xFFFFFF;
212			}
213		}
214		insert_breakpoint(context, after, gdb_debug_enter);
215
216		cont = 1;
217		expect_break_response = 1;
218		break;
219	}
220	case 'H':
221		if (command[1] == 'g' || command[1] == 'c') {;
222			//no thread suport, just acknowledge
223			gdb_send_command("OK");
224		} else {
225			goto not_impl;
226		}
227		break;
228	case 'Z': {
229		uint8_t type = command[1];
230		if (type < '2') {
231			uint32_t address = strtoul(command+3, NULL, 16);
232			insert_breakpoint(context, address, gdb_debug_enter);
233			bp_def *new_bp = malloc(sizeof(bp_def));
234			new_bp->next = breakpoints;
235			new_bp->address = address;
236			new_bp->index = bp_index++;
237			breakpoints = new_bp;
238			gdb_send_command("OK");
239		} else {
240			//watchpoints are not currently supported
241			gdb_send_command("");
242		}
243		break;
244	}
245	case 'z': {
246		uint8_t type = command[1];
247		if (type < '2') {
248			uint32_t address = strtoul(command+3, NULL, 16);
249			remove_breakpoint(context, address);
250			bp_def **found = find_breakpoint(&breakpoints, address);
251			if (*found)
252			{
253				bp_def * to_remove = *found;
254				*found = to_remove->next;
255				free(to_remove);
256			}
257			gdb_send_command("OK");
258		} else {
259			//watchpoints are not currently supported
260			gdb_send_command("");
261		}
262		break;
263	}
264	case 'g': {
265		char * cur = send_buf;
266		for (int i = 0; i < 8; i++)
267		{
268			hex_32(context->dregs[i], cur);
269			cur += 8;
270		}
271		for (int i = 0; i < 8; i++)
272		{
273			hex_32(context->aregs[i], cur);
274			cur += 8;
275		}
276		hex_32(calc_status(context), cur);
277		cur += 8;
278		hex_32(pc, cur);
279		cur += 8;
280		*cur = 0;
281		gdb_send_command(send_buf);
282		break;
283	}
284	case 'm': {
285		char * rest;
286		uint32_t address = strtoul(command+1, &rest, 16);
287		uint32_t size = strtoul(rest+1, NULL, 16);
288		if (size > (sizeof(send_buf)-1)/2) {
289			size = (sizeof(send_buf)-1)/2;
290		}
291		char *cur = send_buf;
292		while (size)
293		{
294			hex_8(m68k_read_byte(context, address), cur);
295			cur += 2;
296			address++;
297			size--;
298		}
299		*cur = 0;
300		gdb_send_command(send_buf);
301		break;
302	}
303	case 'M': {
304		char * rest;
305		uint32_t address = strtoul(command+1, &rest, 16);
306		uint32_t size = strtoul(rest+1, &rest, 16);
307
308		char *cur = rest+1;
309		while (size)
310		{
311			char tmp[3];
312			tmp[0] = *(cur++);
313			tmp[1] = *(cur++);
314			tmp[2] = 0;
315			m68k_write_byte(context, address, strtoul(tmp, NULL, 16));
316			address++;
317			size--;
318		}
319		gdb_send_command("OK");
320		break;
321	}
322	case 'X':
323		//binary transfers aren't supported currently as I don't feel like dealing with the escaping
324		gdb_send_command("");
325		break;
326	case 'p': {
327		unsigned long reg = strtoul(command+1, NULL, 16);
328
329		if (reg < 8) {
330			hex_32(context->dregs[reg], send_buf);
331		} else if (reg < 16) {
332			hex_32(context->aregs[reg-8], send_buf);
333		} else if (reg == 16) {
334			hex_32(calc_status(context), send_buf);
335		} else if (reg == 17) {
336			hex_32(pc, send_buf);
337		} else {
338			send_buf[0] = 0;
339		}
340		send_buf[8] = 0;
341		gdb_send_command(send_buf);
342		break;
343	}
344	case 'P': {
345		char *after = NULL;
346		unsigned long reg = strtoul(command+1, &after, 16);
347		uint32_t value = strtoul(after+1, NULL, 16);
348
349		if (reg < 8) {
350			context->dregs[reg] = value;
351		} else if (reg < 16) {
352			context->aregs[reg-8] = value;
353		} else if (reg == 16) {
354			update_status(context, value);
355		} else {
356			//supporting updates to PC is going to be a pain
357			gdb_send_command("E01");
358			break;
359		}
360		gdb_send_command("OK");
361		break;
362	}
363	case 'q':
364		if (!memcmp("Supported", command+1, strlen("Supported"))) {
365			sprintf(send_buf, "PacketSize=%X", (int)bufsize);
366			gdb_send_command(send_buf);
367		} else if (!memcmp("Attached", command+1, strlen("Attached"))) {
368			//not really meaningful for us, but saying we spawned a new process
369			//is probably closest to the truth
370			gdb_send_command("0");
371		} else if (!memcmp("Offsets", command+1, strlen("Offsets"))) {
372			//no relocations, so offsets are all 0
373			gdb_send_command("Text=0;Data=0;Bss=0");
374		} else if (!memcmp("Symbol", command+1, strlen("Symbol"))) {
375			gdb_send_command("");
376		} else if (!memcmp("TStatus", command+1, strlen("TStatus"))) {
377			//TODO: actual tracepoint support
378			gdb_send_command("T0;tnotrun:0");
379		} else if (!memcmp("TfV", command+1, strlen("TfV")) || !memcmp("TfP", command+1, strlen("TfP"))) {
380			//TODO: actual tracepoint support
381			gdb_send_command("");
382		} else if (command[1] == 'C') {
383			//we only support a single thread currently, so send 1
384			gdb_send_command("QC1");
385		} else if (!strcmp("fThreadInfo", command + 1)) {
386			//we only support a single thread currently, so send 1
387			gdb_send_command("m1");
388		} else if (!strcmp("sThreadInfo", command + 1)) {
389			gdb_send_command("l");
390		} else {
391			goto not_impl;
392		}
393		break;
394	case 'v':
395		if (!memcmp("Cont?", command+1, strlen("Cont?"))) {
396			gdb_send_command("vCont;c;C;s;S");
397		} else if (!strcmp("MustReplyEmpty", command + 1)) {
398			gdb_send_command("");
399		} else if (!memcmp("Cont;", command+1, strlen("Cont;"))) {
400			switch (*(command + 1 + strlen("Cont;")))
401			{
402			case 'c':
403			case 'C':
404				//might be interesting to have continue with signal fire a
405				//trap exception or something, but for no we'll treat it as
406				//a normal continue
407				cont = 1;
408				expect_break_response = 1;
409				break;
410			case 's':
411			case 'S': {
412				m68kinst inst;
413				genesis_context *gen = context->system;
414				uint16_t * pc_ptr = get_native_pointer(pc, (void **)context->mem_pointers, &context->options->gen);
415				if (!pc_ptr) {
416					fatal_error("Entered gdb remote debugger stub at address %X\n", pc);
417				}
418				uint16_t * after_pc = m68k_decode(pc_ptr, &inst, pc & 0xFFFFFF);
419				uint32_t after = pc + (after_pc-pc_ptr)*2;
420
421				if (inst.op == M68K_RTS) {
422					after = (read_dma_value(context->aregs[7]/2) << 16) | read_dma_value(context->aregs[7]/2 + 1);
423				} else if (inst.op == M68K_RTE || inst.op == M68K_RTR) {
424					after = (read_dma_value((context->aregs[7]+2)/2) << 16) | read_dma_value((context->aregs[7]+2)/2 + 1);
425				} else if(m68k_is_branch(&inst)) {
426					if (inst.op == M68K_BCC && inst.extra.cond != COND_TRUE) {
427						branch_f = after;
428						branch_t = m68k_branch_target(&inst, context->dregs, context->aregs) & 0xFFFFFF;
429						insert_breakpoint(context, branch_t, gdb_debug_enter);
430					} else if(inst.op == M68K_DBCC && inst.extra.cond != COND_FALSE) {
431						branch_t = after;
432						branch_f = m68k_branch_target(&inst, context->dregs, context->aregs) & 0xFFFFFF;
433						insert_breakpoint(context, branch_f, gdb_debug_enter);
434					} else {
435						after = m68k_branch_target(&inst, context->dregs, context->aregs) & 0xFFFFFF;
436					}
437				}
438				insert_breakpoint(context, after, gdb_debug_enter);
439
440				cont = 1;
441				expect_break_response = 1;
442				break;
443			}
444			default:
445				goto not_impl;
446			}
447		} else {
448			goto not_impl;
449		}
450		break;
451	case '?':
452		gdb_send_command("S05");
453		break;
454	default:
455		goto not_impl;
456
457	}
458	return;
459not_impl:
460	fatal_error("Command %s is not implemented, exiting...\n", command);
461}
462
463void  gdb_debug_enter(m68k_context * context, uint32_t pc)
464{
465	dfprintf(stderr, "Entered debugger at address %X\n", pc);
466	if (expect_break_response) {
467		gdb_send_command("S05");
468		expect_break_response = 0;
469	}
470	if ((pc & 0xFFFFFF) == branch_t) {
471		bp_def ** f_bp = find_breakpoint(&breakpoints, branch_f);
472		if (!*f_bp) {
473			remove_breakpoint(context, branch_f);
474		}
475		branch_t = branch_f = 0;
476	} else if((pc & 0xFFFFFF) == branch_f) {
477		bp_def ** t_bp = find_breakpoint(&breakpoints, branch_t);
478		if (!*t_bp) {
479			remove_breakpoint(context, branch_t);
480		}
481		branch_t = branch_f = 0;
482	}
483	//Check if this is a user set breakpoint, or just a temporary one
484	bp_def ** this_bp = find_breakpoint(&breakpoints, pc & 0xFFFFFF);
485	if (!*this_bp) {
486		remove_breakpoint(context, pc & 0xFFFFFF);
487	}
488	resume_pc = pc;
489	cont = 0;
490	uint8_t partial = 0;
491	while(!cont)
492	{
493		if (!curbuf) {
494			int numread = GDB_READ(GDB_IN_FD, buf, bufsize);
495			if (numread < 0) {
496				fatal_error("Failed to read on GDB input file descriptor\n");
497			}
498			dfprintf(stderr, "read %d bytes\n", numread);
499			curbuf = buf;
500			end = buf + numread;
501		} else if (partial) {
502			if (curbuf != buf) {
503				memmove(curbuf, buf, end-curbuf);
504				end -= curbuf - buf;
505			}
506			int numread = GDB_READ(GDB_IN_FD, end, bufsize - (end-buf));
507			end += numread;
508			curbuf = buf;
509		}
510		for (; curbuf < end; curbuf++)
511		{
512			if (*curbuf == '$')
513			{
514				curbuf++;
515				char * start = curbuf;
516				while (curbuf < end && *curbuf != '#') {
517					curbuf++;
518				}
519				if (*curbuf == '#') {
520					//check to make sure we've received the checksum bytes
521					if (end-curbuf >= 2) {
522						//TODO: verify checksum
523						//Null terminate payload
524						*curbuf = 0;
525						//send acknowledgement
526						if (GDB_WRITE(GDB_OUT_FD, "+", 1) < 1) {
527							fatal_error("Error writing to stdout\n");
528						}
529						gdb_run_command(context, pc, start);
530						curbuf += 2;
531					}
532				} else {
533					curbuf--;
534					partial = 1;
535					break;
536				}
537			} else {
538				dfprintf(stderr, "Ignoring character %c\n", *curbuf);
539			}
540		}
541		if (curbuf == end) {
542			curbuf = NULL;
543		}
544	}
545}
546
547void gdb_remote_init(void)
548{
549	buf = malloc(INITIAL_BUFFER_SIZE);
550	curbuf = NULL;
551	bufsize = INITIAL_BUFFER_SIZE;
552	disable_stdout_messages();
553}