source: buchla-emu/emu/gdb.c@ a13d63c

Last change on this file since a13d63c was 29618fe, checked in by Thomas Lopatic <thomas@…>, 7 years ago

No GDB connection -> no breakpoint response.

  • Property mode set to 100644
File size: 12.4 KB
Line 
1/*
2 * Copyright (C) 2017 The Contributors
3 *
4 * This program is free software: you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation, either version 3 of the License, or (at
7 * your option) any later version.
8 *
9 * This program is distributed in the hope that it will be useful, but
10 * WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * General Public License for more details.
13 *
14 * A copy of the GNU General Public License can be found in the file
15 * "gpl.txt" in the top directory of this repository.
16 */
17
18#include <all.h>
19
20#define ver(...) _ver(gdb_verbose, 0, __VA_ARGS__)
21#define ver2(...) _ver(gdb_verbose, 1, __VA_ARGS__)
22#define ver3(...) _ver(gdb_verbose, 2, __VA_ARGS__)
23
24int32_t gdb_verbose = 0;
25
26#define PORT 12053
27#define SZ_BUF 10000
28
29#define RES_ERR \
30 ((result_t){ .ok = false })
31
32#define RES_OK \
33 ((result_t){ .ok = true })
34
35#define RES_DAT(_out, _n_out) \
36 ((result_t){ .ok = true, .out = (uint8_t *)_out, .n_out = (int32_t)_n_out })
37
38#define LOCK_NONE 0
39#define LOCK_REQ 1
40#define LOCK_ACK 2
41#define LOCK_BP 3
42
43typedef enum {
44 STATE_HEAD,
45 STATE_DATA,
46 STATE_CHECK_1,
47 STATE_CHECK_2,
48 STATE_ACK
49} state_t;
50
51typedef struct {
52 bool ok;
53 const uint8_t *out;
54 int32_t n_out;
55} result_t;
56
57static TCPsocket lis;
58static SDLNet_SocketSet set;
59
60static state_t state;
61static SDL_atomic_t lock;
62
63void gdb_init(void)
64{
65 ver("gdb init");
66
67 IPaddress addr;
68
69 if (SDLNet_ResolveHost(&addr, NULL, PORT) < 0) {
70 fail("SDLNet_ResolveHost() failed: %s", SDLNet_GetError());
71 }
72
73 lis = SDLNet_TCP_Open(&addr);
74
75 if (lis == NULL) {
76 fail("SDLNet_TCP_Open() failed: %s", SDLNet_GetError());
77 }
78
79 set = SDLNet_AllocSocketSet(2);
80
81 if (set == NULL) {
82 fail("SDLNet_AllocSocketSet() failed: %s", SDLNet_GetError());
83 }
84
85 if (SDLNet_TCP_AddSocket(set, lis) < 0) {
86 fail("SDLNet_AddSocket() failed: %s", SDLNet_GetError());
87 }
88
89 SDL_AtomicSet(&lock, 0);
90}
91
92void gdb_quit(void)
93{
94 ver("gdb quit");
95
96 SDLNet_FreeSocketSet(set);
97 SDLNet_TCP_Close(lis);
98}
99
100void gdb_inst(bool bp)
101{
102 ver3("gdb inst");
103
104 if (!bp && SDL_AtomicGet(&lock) == LOCK_NONE) {
105 return;
106 }
107
108 if (SDL_UnlockMutex(cpu_mutex) < 0) {
109 fail("SDL_UnlockMutex() failed: %s", SDL_GetError());
110 }
111
112 if (bp) {
113 ver2("-> lock bp");
114 SDL_AtomicSet(&lock, LOCK_BP);
115
116 while (SDL_AtomicGet(&lock) != LOCK_REQ) {
117 SDL_Delay(100);
118 }
119 }
120
121 ver2("<- lock req");
122 ver2("-> lock ack");
123 SDL_AtomicSet(&lock, LOCK_ACK);
124
125 while (SDL_AtomicGet(&lock) == LOCK_ACK) {
126 SDL_Delay(100);
127 }
128
129 ver2("<- lock none / req");
130
131 if (SDL_LockMutex(cpu_mutex) < 0) {
132 fail("SDL_LockMutex() failed: %s", SDL_GetError());
133 }
134}
135
136static void wait_cpu(void)
137{
138 while (SDL_AtomicGet(&lock) != LOCK_ACK) {
139 SDL_Delay(100);
140 }
141
142 ver2("<- lock ack");
143}
144
145static void stop_cpu(void)
146{
147 ver2("-> lock req");
148 SDL_AtomicSet(&lock, LOCK_REQ);
149
150 wait_cpu();
151}
152
153static void cont_cpu(void)
154{
155 ver2("-> lock none");
156 SDL_AtomicSet(&lock, LOCK_NONE);
157}
158
159static int32_t hex_digit(char c)
160{
161 if (c >= '0' && c <= '9') {
162 return c - '0';
163 }
164
165 if (c >= 'a' && c <= 'f') {
166 return 10 + c - 'a';
167 }
168
169 if (c >= 'A' && c <= 'F') {
170 return 10 + c - 'A';
171 }
172
173 return -1;
174}
175
176static int32_t hex_num(const char **pp)
177{
178 int32_t res = 0;
179 int32_t dig;
180
181 while ((dig = hex_digit(**pp)) >= 0) {
182 res = (res << 4) | dig;
183 ++*pp;
184 }
185
186 return res;
187}
188
189static result_t com_reason(void)
190{
191 // 0x05 = SIGTRAP
192 return RES_DAT("S05", 3);
193}
194
195static void set_pc(const char **req)
196{
197 int32_t addr = hex_num(req);
198
199 if (SDL_LockMutex(cpu_mutex) < 0) {
200 fail("SDL_LockMutex() failed: %s", SDL_GetError());
201 }
202
203 m68k_set_reg(M68K_REG_PC, (uint32_t)addr);
204
205 if (SDL_UnlockMutex(cpu_mutex) < 0) {
206 fail("SDL_UnlockMutex() failed: %s", SDL_GetError());
207 }
208}
209
210static result_t com_cont(const char *req)
211{
212 if (req[0] != 0) {
213 set_pc(&req);
214 }
215
216 cont_cpu();
217
218 return RES_OK;
219}
220
221static result_t com_step(const char *req)
222{
223 if (req[0] != 0) {
224 set_pc(&req);
225 }
226
227 stop_cpu();
228
229 // 0x05 = SIGTRAP
230 return RES_DAT("S05", 3);
231}
232
233static result_t com_rd_reg(void)
234{
235 static char buf[(8 + 8 + 2) * 8 + 1];
236
237 if (SDL_LockMutex(cpu_mutex) < 0) {
238 fail("SDL_LockMutex() failed: %s", SDL_GetError());
239 }
240
241 uint32_t d0 = m68k_get_reg(NULL, M68K_REG_D0);
242 uint32_t d1 = m68k_get_reg(NULL, M68K_REG_D1);
243 uint32_t d2 = m68k_get_reg(NULL, M68K_REG_D2);
244 uint32_t d3 = m68k_get_reg(NULL, M68K_REG_D3);
245 uint32_t d4 = m68k_get_reg(NULL, M68K_REG_D4);
246 uint32_t d5 = m68k_get_reg(NULL, M68K_REG_D5);
247 uint32_t d6 = m68k_get_reg(NULL, M68K_REG_D6);
248 uint32_t d7 = m68k_get_reg(NULL, M68K_REG_D7);
249
250 uint32_t a0 = m68k_get_reg(NULL, M68K_REG_A0);
251 uint32_t a1 = m68k_get_reg(NULL, M68K_REG_A1);
252 uint32_t a2 = m68k_get_reg(NULL, M68K_REG_A2);
253 uint32_t a3 = m68k_get_reg(NULL, M68K_REG_A3);
254 uint32_t a4 = m68k_get_reg(NULL, M68K_REG_A4);
255 uint32_t a5 = m68k_get_reg(NULL, M68K_REG_A5);
256 uint32_t a6 = m68k_get_reg(NULL, M68K_REG_A6);
257 uint32_t a7 = m68k_get_reg(NULL, M68K_REG_A7);
258
259 uint32_t ps = m68k_get_reg(NULL, M68K_REG_SR);
260 uint32_t pc = m68k_get_reg(NULL, M68K_REG_PC);
261
262 if (SDL_UnlockMutex(cpu_mutex) < 0) {
263 fail("SDL_UnlockMutex() failed: %s", SDL_GetError());
264 }
265
266 sprintf(buf,
267 "%08x%08x%08x%08x%08x%08x%08x%08x"
268 "%08x%08x%08x%08x%08x%08x%08x%08x"
269 "%08x%08x",
270 d0, d1, d2, d3, d4, d5, d6, d7,
271 a0, a1, a2, a3, a4, a5, a6, a7,
272 ps, pc);
273
274 return RES_DAT(buf, sizeof buf - 1);
275}
276
277static result_t com_wr_reg(const char *req)
278{
279 uint32_t d0, d1, d2, d3, d4, d5, d6, d7;
280 uint32_t a0, a1, a2, a3, a4, a5, a6, a7;
281 uint32_t ps, pc;
282
283 if (sscanf(req,
284 "%08x%08x%08x%08x%08x%08x%08x%08x"
285 "%08x%08x%08x%08x%08x%08x%08x%08x"
286 "%08x%08x",
287 &d0, &d1, &d2, &d3, &d4, &d5, &d6, &d7,
288 &a0, &a1, &a2, &a3, &a4, &a5, &a6, &a7,
289 &ps, &pc) != 8 + 8 + 2) {
290 return RES_DAT("E01", 3);
291 }
292
293 if (SDL_LockMutex(cpu_mutex) < 0) {
294 fail("SDL_LockMutex() failed: %s", SDL_GetError());
295 }
296
297 m68k_set_reg(M68K_REG_D0, d0);
298 m68k_set_reg(M68K_REG_D1, d1);
299 m68k_set_reg(M68K_REG_D2, d2);
300 m68k_set_reg(M68K_REG_D3, d3);
301 m68k_set_reg(M68K_REG_D4, d4);
302 m68k_set_reg(M68K_REG_D5, d5);
303 m68k_set_reg(M68K_REG_D6, d6);
304 m68k_set_reg(M68K_REG_D7, d7);
305
306 m68k_set_reg(M68K_REG_A0, a0);
307 m68k_set_reg(M68K_REG_A1, a1);
308 m68k_set_reg(M68K_REG_A2, a2);
309 m68k_set_reg(M68K_REG_A3, a3);
310 m68k_set_reg(M68K_REG_A4, a4);
311 m68k_set_reg(M68K_REG_A5, a5);
312 m68k_set_reg(M68K_REG_A6, a6);
313 m68k_set_reg(M68K_REG_A7, a7);
314
315 m68k_set_reg(M68K_REG_SR, ps);
316 m68k_set_reg(M68K_REG_PC, pc);
317
318 if (SDL_UnlockMutex(cpu_mutex) < 0) {
319 fail("SDL_UnlockMutex() failed: %s", SDL_GetError());
320 }
321
322 return RES_DAT("OK", 2);
323}
324
325static result_t com_rd_mem(const char *req)
326{
327 int32_t addr = hex_num(&req);
328
329 if (*req++ != ',') {
330 return RES_DAT("E01", 3);
331 }
332
333 int32_t len = hex_num(&req);
334
335 if (*req != 0 || len == 0) {
336 return RES_DAT("E01", 3);
337 }
338
339 if (len > 10000) {
340 len = 10000;
341 }
342
343 static char buf[10000 * 2 + 1];
344
345 if (SDL_LockMutex(cpu_mutex) < 0) {
346 fail("SDL_LockMutex() failed: %s", SDL_GetError());
347 }
348
349 for (int32_t i = 0; i < len; ++i) {
350 uint8_t byte = cpu_peek(addr + i);
351 sprintf(buf + i * 2, "%02x", byte);
352 }
353
354 if (SDL_UnlockMutex(cpu_mutex) < 0) {
355 fail("SDL_UnlockMutex() failed: %s", SDL_GetError());
356 }
357
358 return RES_DAT(buf, len * 2);
359}
360
361static result_t com_wr_mem(const char *req)
362{
363 int32_t addr = hex_num(&req);
364
365 if (*req++ != ',') {
366 return RES_DAT("E01", 3);
367 }
368
369 int32_t len = hex_num(&req);
370
371 if (*req++ != ':') {
372 return RES_DAT("E01", 3);
373 }
374
375 if (SDL_LockMutex(cpu_mutex) < 0) {
376 fail("SDL_LockMutex() failed: %s", SDL_GetError());
377 }
378
379 int32_t i;
380
381 for (i = 0; i < len; ++i) {
382 int32_t hi = hex_digit(*req++);
383
384 if (hi < 0) {
385 break;
386 }
387
388 int32_t lo = hex_digit(*req++);
389
390 if (lo < 0) {
391 break;
392 }
393
394 cpu_poke(addr + i, (uint8_t)((hi << 4) | lo));
395 }
396
397 if (SDL_UnlockMutex(cpu_mutex) < 0) {
398 fail("SDL_UnlockMutex() failed: %s", SDL_GetError());
399 }
400
401 return RES_DAT("OK", 2);
402}
403
404static result_t handle(const char *req)
405{
406 result_t res;
407
408 switch (req[0]) {
409 case '?':
410 res = com_reason();
411 break;
412
413 case 'c':
414 res = com_cont(req + 1);
415 break;
416
417 case 'C':
418 res = com_cont(req + 4);
419 break;
420
421 case 's':
422 res = com_step(req + 1);
423 break;
424
425 case 'S':
426 res = com_step(req + 4);
427 break;
428
429 case 'g':
430 res = com_rd_reg();
431 break;
432
433 case 'G':
434 res = com_wr_reg(req + 1);
435 break;
436
437 case 'm':
438 res = com_rd_mem(req + 1);
439 break;
440
441 case 'M':
442 res = com_wr_mem(req + 1);
443 break;
444
445 default:
446 res = RES_DAT("", 0);
447 break;
448 }
449
450 if (!res.ok || res.n_out > SZ_BUF - 5) {
451 fail("unexpected result");
452 }
453
454 if (res.out == NULL) {
455 return RES_OK;
456 }
457
458 static const uint8_t *hex = (uint8_t *)"0123456789abcdef";
459 static uint8_t buf[SZ_BUF];
460
461 buf[0] = '$';
462 memcpy(buf + 1, res.out, (size_t)res.n_out);
463
464 int32_t sum = 0;
465
466 for (int32_t i = 0; i < res.n_out; ++i) {
467 sum = (sum + res.out[i]) % 256;
468 }
469
470 buf[res.n_out + 1] = '#';
471 buf[res.n_out + 2] = hex[sum / 16];
472 buf[res.n_out + 3] = hex[sum % 16];
473 buf[res.n_out + 4] = 0;
474
475 ver2("resp %s", (char *)buf);
476 return RES_DAT(buf, res.n_out + 4);
477}
478
479static result_t input(uint8_t byte)
480{
481 static int32_t n_buf;
482 static uint8_t buf[SZ_BUF];
483 static int32_t sum;
484 static int32_t check;
485
486 int32_t hex;
487 result_t res;
488
489 ver3("input %c st %d n %d", byte, (int32_t)state, n_buf);
490
491 if (state == STATE_HEAD && byte == 0x03) {
492 stop_cpu();
493 state = STATE_ACK;
494 return RES_DAT("$S05#b8", 7);
495 }
496
497 switch (state) {
498 case STATE_HEAD:
499 if (byte != '$') {
500 err("expected '$'");
501 return RES_ERR;
502 }
503
504 sum = 0;
505 n_buf = 0;
506 state = STATE_DATA;
507 return RES_OK;
508
509 case STATE_DATA:
510 if (n_buf == SZ_BUF - 1) {
511 err("packet too long");
512 return RES_ERR;
513 }
514
515 if (byte == '#') {
516 state = STATE_CHECK_1;
517 return RES_DAT("+", 1);
518 }
519
520 sum = (sum + byte) % 256;
521 buf[n_buf] = byte;
522 ++n_buf;
523 return RES_OK;
524
525 case STATE_CHECK_1:
526 hex = hex_digit((char)byte);
527
528 if (hex < 0) {
529 err("malformed checksum (0x%02x)", byte);
530 return RES_ERR;
531 }
532
533 check = hex << 4;
534 state = STATE_CHECK_2;
535 return RES_OK;
536
537 case STATE_CHECK_2:
538 hex = hex_digit((char)byte);
539
540 if (hex < 0) {
541 err("malformed checksum (0x%02x)", byte);
542 return RES_ERR;
543 }
544
545 check |= hex;
546
547 if (sum != check) {
548 err("invalid checksum");
549 return RES_ERR;
550 }
551
552 buf[n_buf] = 0;
553 ver2("pack %s", buf);
554
555 res = handle((char *)buf);
556
557 if (!res.ok) {
558 return RES_ERR;
559 }
560
561 if (res.out == NULL) {
562 state = STATE_HEAD;
563 return RES_OK;
564 }
565
566 state = STATE_ACK;
567 return RES_DAT(res.out, res.n_out);
568
569 case STATE_ACK:
570 if (byte != '+') {
571 err("invalid ACK (0x%02x)", byte);
572 return RES_ERR;
573 }
574
575 state = STATE_HEAD;
576 return RES_OK;
577
578 default:
579 fail("invalid state");
580 }
581
582 // not reached, but Eclipse doesn't know
583 return RES_ERR;
584}
585
586static void con_close(TCPsocket con)
587{
588 if (SDLNet_TCP_DelSocket(set, con) < 0) {
589 fail("SDLNet_TCP_DelSocket() failed: %s", SDLNet_GetError());
590 }
591
592 SDLNet_TCP_Close(con);
593 cont_cpu();
594}
595
596void gdb_loop(void)
597{
598 inf("entering GDB loop");
599 TCPsocket con = NULL;
600
601 while (SDL_AtomicGet(&run) != 0) {
602 int32_t n_act = SDLNet_CheckSockets(set, 100);
603
604 if (n_act < 0) {
605 fail("SDLNet_CheckSockets() failed: %s", SDLNet_GetError());
606 }
607
608 if (SDL_AtomicGet(&lock) == LOCK_BP) {
609 ver2("<- lock bp");
610 stop_cpu();
611 state = STATE_ACK;
612
613 if (con == NULL) {
614 cont_cpu();
615 continue;
616 }
617
618 if (SDLNet_TCP_Send(con, "$S05#b8", 7) != 7) {
619 err("connection error");
620 con_close(con);
621 con = NULL;
622 continue;
623 }
624
625 continue;
626 }
627
628 if (n_act == 0) {
629 continue;
630 }
631
632 if (SDLNet_SocketReady(lis) != 0) {
633 ver("incoming connection");
634
635 if (con != NULL) {
636 ver("closing old");
637 con_close(con);
638 }
639
640 ver("accepting new");
641 con = SDLNet_TCP_Accept(lis);
642
643 if (con == NULL) {
644 fail("SDLNet_TCP_Accept() failed: %s", SDLNet_GetError());
645 }
646
647 if (SDLNet_TCP_AddSocket(set, con) < 0) {
648 fail("SDLNet_AddSocket() failed: %s", SDLNet_GetError());
649 }
650
651 stop_cpu();
652 state = STATE_ACK;
653 continue;
654 }
655
656 if (con == NULL || SDLNet_SocketReady(con) == 0) {
657 continue;
658 }
659
660 ver3("reading");
661 uint8_t byte;
662
663 if (SDLNet_TCP_Recv(con, &byte, 1) < 1) {
664 ver("peer closed");
665 con_close(con);
666 con = NULL;
667 continue;
668 }
669
670 result_t res = input(byte);
671
672 if (!res.ok) {
673 err("invalid packet from GDB");
674 con_close(con);
675 con = NULL;
676 continue;
677 }
678
679 if (res.out == NULL) {
680 continue;
681 }
682
683 if (SDLNet_TCP_Send(con, res.out, res.n_out) != res.n_out) {
684 err("connection error");
685 con_close(con);
686 con = NULL;
687 continue;
688 }
689 }
690
691 if (con != NULL) {
692 con_close(con);
693 }
694
695 inf("leaving GDB loop");
696}
Note: See TracBrowser for help on using the repository browser.