comparison gdb_remote.c @ 515:1495179d6737

Initial GDB remote debugging support. Lacks some features, but breakpoints and basic inspection of registers and memory work.
author Mike Pavone <pavone@retrodev.com>
date Sat, 08 Feb 2014 23:37:09 -0800
parents b7b7a1cab44a
children 62860044337d
comparison
equal deleted inserted replaced
514:f66c78cbdcaa 515:1495179d6737
1 /* 1 /*
2 Copyright 2013 Michael Pavone 2 Copyright 2013 Michael Pavone
3 This file is part of BlastEm. 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. 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 */ 5 */
6 #include "blastem.h" 6 #include "gdb_remote.h"
7 #include <unistd.h> 7 #include <unistd.h>
8 #include <fcntl.h> 8 #include <fcntl.h>
9 #include <stddef.h> 9 #include <stddef.h>
10 #include <stdlib.h> 10 #include <stdlib.h>
11 #include <stdio.h> 11 #include <stdio.h>
12 12 #include <string.h>
13 #define INITIAL_BUFFER_SIZE 4096 13
14 #define INITIAL_BUFFER_SIZE (16*1024)
14 15
15 char * buf = NULL; 16 char * buf = NULL;
16 char * curbuf = NULL; 17 char * curbuf = NULL;
17 char * end = NULL; 18 char * end = NULL;
18 size_t bufsize; 19 size_t bufsize;
19 int cont = 0; 20 int cont = 0;
20 int expect_break_response=0; 21 int expect_break_response=0;
21 uint32_t resume_pc; 22 uint32_t resume_pc;
22 23
23 void gdb_debug_enter(genesis_context * gen, uint32_t pc) 24
24 { 25 void hex_32(uint32_t num, char * out)
25 fcntl(STDIN_FILENO, FD_SETFL, 0); 26 {
27 for (int32_t shift = 28; shift >= 0; shift -= 4)
28 {
29 uint8_t nibble = num >> shift & 0xF;
30 *(out++) = nibble > 9 ? nibble - 0xA + 'A' : nibble + '0';
31 }
32 }
33
34 void hex_16(uint16_t num, char * out)
35 {
36 for (int16_t shift = 14; shift >= 0; shift -= 4)
37 {
38 uint8_t nibble = num >> shift & 0xF;
39 *(out++) = nibble > 9 ? nibble - 0xA + 'A' : nibble + '0';
40 }
41 }
42
43 void hex_8(uint8_t num, char * out)
44 {
45 uint8_t nibble = num >> 4;
46 *(out++) = nibble > 9 ? nibble - 0xA + 'A' : nibble + '0';
47 nibble = num & 0xF;
48 *out = nibble > 9 ? nibble - 0xA + 'A' : nibble + '0';
49 }
50
51 void gdb_calc_checksum(char * command, char *out)
52 {
53 uint8_t checksum = 0;
54 while (*command)
55 {
56 checksum += *(command++);
57 }
58 hex_8(checksum, out);
59 }
60
61 void write_or_die(int fd, const void *buf, size_t count)
62 {
63 if (write(fd, buf, count) < count) {
64 fputs("Error writing to stdout\n", stderr);
65 exit(1);
66 }
67 }
68
69 void gdb_send_command(char * command)
70 {
71 char end[3];
72 write_or_die(STDOUT_FILENO, "$", 1);
73 write_or_die(STDOUT_FILENO, command, strlen(command));
74 end[0] = '#';
75 gdb_calc_checksum(command, end+1);
76 write_or_die(STDOUT_FILENO, end, 3);
77 fprintf(stderr, "Sent $%s#%c%c\n", command, end[1], end[2]);
78 }
79
80 uint32_t calc_status(m68k_context * context)
81 {
82 uint32_t status = context->status << 3;
83 for (int i = 0; i < 5; i++)
84 {
85 status <<= 1;
86 status |= context->flags[i];
87 }
88 return status;
89 }
90
91 uint8_t read_byte(m68k_context * context, uint32_t address)
92 {
93 uint16_t * word;
94 //TODO: Use generated read/write functions so that memory map is properly respected
95 if (address < 0x400000) {
96 word = context->mem_pointers[0] + address/2;
97 } else if (address >= 0xE00000) {
98 word = context->mem_pointers[1] + (address & 0xFFFF)/2;
99 } else {
100 return 0;
101 }
102 if (address & 1) {
103 return *word;
104 }
105 return *word >> 8;
106 }
107
108 void gdb_run_command(m68k_context * context, uint32_t pc, char * command)
109 {
110 char send_buf[512];
111 fprintf(stderr, "Received command %s\n", command);
112 switch(*command)
113 {
114
115 case 'c':
116 if (*(command+1) != 0) {
117 //TODO: implement resuming at an arbitrary address
118 goto not_impl;
119 }
120 cont = 1;
121 expect_break_response = 1;
122 break;
123 case 'H':
124 if (command[1] == 'g' || command[1] == 'c') {;
125 //no thread suport, just acknowledge
126 gdb_send_command("OK");
127 } else {
128 goto not_impl;
129 }
130 break;
131 case 'Z': {
132 uint8_t type = command[1];
133 if (type < '2') {
134 uint32_t address = strtoul(command+3, NULL, 16);
135 insert_breakpoint(context, address, (uint8_t *)gdb_debug_enter);
136 gdb_send_command("OK");
137 } else {
138 //watchpoints are not currently supported
139 gdb_send_command("");
140 }
141 break;
142 }
143 case 'z': {
144 uint8_t type = command[1];
145 if (type < '2') {
146 uint32_t address = strtoul(command+3, NULL, 16);
147 remove_breakpoint(context, address);
148 gdb_send_command("OK");
149 } else {
150 //watchpoints are not currently supported
151 gdb_send_command("");
152 }
153 break;
154 }
155 case 'g': {
156 char * cur = send_buf;
157 for (int i = 0; i < 8; i++)
158 {
159 hex_32(context->dregs[i], cur);
160 cur += 8;
161 }
162 for (int i = 0; i < 8; i++)
163 {
164 hex_32(context->aregs[i], cur);
165 cur += 8;
166 }
167 hex_32(calc_status(context), cur);
168 cur += 8;
169 hex_32(pc, cur);
170 cur += 8;
171 *cur = 0;
172 gdb_send_command(send_buf);
173 break;
174 }
175 case 'm': {
176 char * rest;
177 uint32_t address = strtoul(command+1, &rest, 16);
178 uint32_t size = strtoul(rest+1, NULL, 16);
179 if (size > sizeof(send_buf-1)/2) {
180 size = sizeof(send_buf-1)/2;
181 }
182 char *cur = send_buf;
183 while (size)
184 {
185 hex_8(read_byte(context, address), cur);
186 cur += 2;
187 address++;
188 size--;
189 }
190 *cur = 0;
191 gdb_send_command(send_buf);
192 break;
193 }
194 case 'p': {
195 unsigned long reg = strtoul(command+1, NULL, 16);
196
197 if (reg < 8) {
198 hex_32(context->dregs[reg], send_buf);
199 } else if (reg < 16) {
200 hex_32(context->aregs[reg-8], send_buf);
201 } else if (reg == 16) {
202 hex_32(calc_status(context), send_buf);
203 } else if (reg == 17) {
204 hex_32(pc, send_buf);
205 } else {
206 send_buf[0] = 0;
207 }
208 send_buf[8] = 0;
209 gdb_send_command(send_buf);
210 break;
211 }
212 case 'q':
213 if (!memcmp("Supported", command+1, strlen("Supported"))) {
214 sprintf(send_buf, "PacketSize=%X", (int)bufsize);
215 gdb_send_command(send_buf);
216 } else if (!memcmp("Attached", command+1, strlen("Supported"))) {
217 //not really meaningful for us, but saying we spawned a new process
218 //is probably closest to the truth
219 gdb_send_command("0");
220 } else if (!memcmp("Offsets", command+1, strlen("Offsets"))) {
221 //no relocations, so offsets are all 0
222 gdb_send_command("Text=0;Data=0;Bss=0");
223 } else if (!memcmp("Symbol", command+1, strlen("Symbol"))) {
224 gdb_send_command("");
225 } else if (!memcmp("TStatus", command+1, strlen("TStatus"))) {
226 //TODO: actual tracepoint support
227 gdb_send_command("T0;tnotrun:0");
228 } else if (!memcmp("TfV", command+1, strlen("TfV")) || !memcmp("TfP", command+1, strlen("TfP"))) {
229 //TODO: actual tracepoint support
230 gdb_send_command("");
231 } else if (command[1] == 'C') {
232 //we only support a single thread currently, so send 1
233 gdb_send_command("1");
234 } else {
235 goto not_impl;
236 }
237 break;
238 case 'v':
239 if (!memcmp("Cont?", command+1, strlen("Cont?"))) {
240 gdb_send_command("vCont;c;C;s;S");
241 } else if (!memcmp("Cont;", command+1, strlen("Cont;"))) {
242 switch (*(command + 1 + strlen("Cont;")))
243 {
244 case 'c':
245 case 'C':
246 //might be interesting to have continue with signal fire a
247 //trap exception or something, but for no we'll treat it as
248 //a normal continue
249 cont = 1;
250 expect_break_response = 1;
251 break;
252 default:
253 goto not_impl;
254 }
255 } else {
256 goto not_impl;
257 }
258 break;
259 case '?':
260 gdb_send_command("S05");
261 break;
262 default:
263 goto not_impl;
264
265 }
266 return;
267 not_impl:
268 fprintf(stderr, "Command %s is not implemented, exiting...\n", command);
269 exit(1);
270 }
271
272 m68k_context * gdb_debug_enter(m68k_context * context, uint32_t pc)
273 {
274 fprintf(stderr, "Entered debugger at address %X\n", pc);
275 if (expect_break_response) {
276 gdb_send_command("S05");
277 expect_break_response = 0;
278 }
26 resume_pc = pc; 279 resume_pc = pc;
27 cont = 0; 280 cont = 0;
28 uint8_t partial = 0; 281 uint8_t partial = 0;
29 while(!cont) 282 while(!cont)
30 { 283 {
33 curbuf = buf; 286 curbuf = buf;
34 end = buf + numread; 287 end = buf + numread;
35 } else if (partial) { 288 } else if (partial) {
36 if (curbuf != buf) { 289 if (curbuf != buf) {
37 memmove(curbuf, buf, end-curbuf); 290 memmove(curbuf, buf, end-curbuf);
38 end -= cufbuf - buf; 291 end -= curbuf - buf;
39 } 292 }
40 int numread = read(STDIN_FILENO, end, bufsize - (end-buf)); 293 int numread = read(STDIN_FILENO, end, bufsize - (end-buf));
41 end += numread; 294 end += numread;
42 curbuf = buf; 295 curbuf = buf;
43 } 296 }
53 if (*curbuf == '#') { 306 if (*curbuf == '#') {
54 //check to make sure we've received the checksum bytes 307 //check to make sure we've received the checksum bytes
55 if (end-curbuf >= 2) { 308 if (end-curbuf >= 2) {
56 //TODO: verify checksum 309 //TODO: verify checksum
57 //Null terminate payload 310 //Null terminate payload
58 *curbuf = 0 311 *curbuf = 0;
59 //send acknowledgement 312 //send acknowledgement
60 write(FILENO_STDOUT, "+", 1); 313 if (write(STDOUT_FILENO, "+", 1) < 1) {
61 gdb_run_command(genesis_context * gen, start); 314 fputs("Error writing to stdout\n", stderr);
315 exit(1);
316 }
317 gdb_run_command(context, pc, start);
62 curbuf += 2; 318 curbuf += 2;
63 } 319 }
64 } else { 320 } else {
65 curbuf--; 321 curbuf--;
66 partial = 1; 322 partial = 1;
67 break; 323 break;
68 } 324 }
325 } else {
326 fprintf(stderr, "Ignoring character %c\n", *curbuf);
69 } 327 }
70 } 328 }
71 } 329 if (curbuf == end) {
72 fcntl(STDIN_FILENO, FD_SETFL, O_NONBLOCK); 330 curbuf = NULL;
73 } 331 }
74 332 }
75 void gdb_run_command(genesis_context * gen, char * command) 333 return context;
76 { 334 }
77 switch(*command) 335
78 { 336 void gdb_remote_init(void)
79 case 'c': 337 {
80 if (*(command+1) != 0) {
81 resume_pc =
82 }
83 cont = 1;
84 expect_break_response = 1;
85 break;
86 case 's':
87
88 }
89 }
90
91 void gdb_run_commands(genesis_context * gen)
92 {
93 int enter_debugger = 0;
94 char * cur = buf;
95 while(cur < curbuf);
96 {
97 if(*cur == '$') {
98 cur++
99 char * start = cur;
100 while (cur < curbuf && *cur != '#') {
101 cur++;
102 }
103 if (*cur == '#') {
104 //check to make sure we've received the checksum bytes
105 if (curbuf-cur >= 2) {
106 //TODO: verify checksum
107 //Null terminate payload
108 //send acknowledgement
109 write(FILENO_STDOUT, "+", 1);
110 gdb_run_command(genesis_context * gen, start);
111 cur += 2;
112 } else {
113 cur = start - 1;
114 break;
115 }
116 } else {
117 cur = start - 1;
118 break;
119 }
120 } else {
121 if (*cur == 0x03) {
122 enter_debugger = 1;
123 }
124 cur++;
125 }
126 }
127
128 //FIXME
129 if (consumed == curbuf-buf) {
130 curbuf = buf;
131 } else if (consumed > 0) {
132 memmove(buf, buf + consumed, curbuf - buf - consumed);
133 curbuf -= consumed;
134 }
135 }
136
137 int gdb_command_poll(genesis_context * gen)
138 {
139 for(;;)
140 {
141 if (curbuf == buf + bufsize) {
142 //buffer is full, expand it
143 bufsize *= 2;
144 buf = realloc(buf, bufsize);
145 if (!buf) {
146 fprintf(stderr, "Failed to grow GDB command buffer to %d bytes\n", (int)bufsize);
147 exit(1);
148 }
149 curbuf = buf + bufsize/2;
150 }
151 int numread = read(STDIN_FILENO, buf, bufsize);
152 if (numread < 0) {
153 if (errno == EAGAIN || errno == EWOULDBLOCK) {
154 return 0;
155 } else {
156 fprintf(stderr, "Error %d while reading GDB commands from stdin", errno);
157 exit(1);
158 }
159 } else if (numread == 0) {
160 exit(0);
161 }
162 for (curbuf = buf, end = buf+numread; curbuf < end; curbuf++)
163 {
164 if (*curbuf = 0x03)
165 {
166 curbuf++;
167 return 1;
168 }
169 }
170 }
171 return 0;
172 }
173
174 void gdb_remote_init()
175 {
176 fcntl(STDIN_FILENO, FD_SETFL, O_NONBLOCK);
177 buf = malloc(INITIAL_BUFFER_SIZE); 338 buf = malloc(INITIAL_BUFFER_SIZE);
178 curbuf = buf; 339 curbuf = NULL;
179 bufzie = INITIAL_BUFFER_SIZE; 340 bufsize = INITIAL_BUFFER_SIZE;
180 } 341 }