nkeynes@998 | 1 | /**
|
nkeynes@1020 | 2 | * $Id$
|
nkeynes@998 | 3 | *
|
nkeynes@998 | 4 | * GDB RDP server stub - SH4 + ARM
|
nkeynes@998 | 5 | *
|
nkeynes@998 | 6 | * Copyright (c) 2009 Nathan Keynes.
|
nkeynes@998 | 7 | *
|
nkeynes@998 | 8 | * This program is free software; you can redistribute it and/or modify
|
nkeynes@998 | 9 | * it under the terms of the GNU General Public License as published by
|
nkeynes@998 | 10 | * the Free Software Foundation; either version 2 of the License, or
|
nkeynes@998 | 11 | * (at your option) any later version.
|
nkeynes@998 | 12 | *
|
nkeynes@998 | 13 | * This program is distributed in the hope that it will be useful,
|
nkeynes@998 | 14 | * but WITHOUT ANY WARRANTY; without even the implied warranty of
|
nkeynes@998 | 15 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
nkeynes@998 | 16 | * GNU General Public License for more details.
|
nkeynes@998 | 17 | */
|
nkeynes@998 | 18 |
|
nkeynes@998 | 19 |
|
nkeynes@998 | 20 | #include <errno.h>
|
nkeynes@998 | 21 | #include <stdio.h>
|
nkeynes@998 | 22 | #include <stdlib.h>
|
nkeynes@998 | 23 | #include <stdarg.h>
|
nkeynes@998 | 24 | #include <string.h>
|
nkeynes@998 | 25 | #include <unistd.h>
|
nkeynes@998 | 26 | #include <glib.h>
|
nkeynes@998 | 27 | #include <arpa/inet.h>
|
nkeynes@998 | 28 | #include "lxdream.h"
|
nkeynes@1271 | 29 | #include "dream.h"
|
nkeynes@998 | 30 | #include "dreamcast.h"
|
nkeynes@1077 | 31 | #include "ioutil.h"
|
nkeynes@998 | 32 | #include "cpu.h"
|
nkeynes@1271 | 33 | #include "gui.h"
|
nkeynes@998 | 34 |
|
nkeynes@998 | 35 | #define DEFAULT_BUFFER_SIZE 1024
|
nkeynes@998 | 36 | #define BUFFER_SIZE_MARGIN 32
|
nkeynes@998 | 37 | #define MAX_BUFFER_SIZE 65536
|
nkeynes@998 | 38 |
|
nkeynes@998 | 39 | /* These are just local interpretations - they're not interpreted by GDB
|
nkeynes@998 | 40 | * in any way shape or form.
|
nkeynes@998 | 41 | */
|
nkeynes@998 | 42 | #define GDB_ERROR_FORMAT 1 /* Badly formatted command */
|
nkeynes@998 | 43 | #define GDB_ERROR_INVAL 2 /* Invalid data */
|
nkeynes@998 | 44 | #define GDB_ERROR_FAIL 3 /* Command failed */
|
nkeynes@998 | 45 | struct gdb_server {
|
nkeynes@998 | 46 | cpu_desc_t cpu;
|
nkeynes@998 | 47 | gboolean mmu;
|
nkeynes@998 | 48 | int fd;
|
nkeynes@998 | 49 | const gchar *peer_name;
|
nkeynes@998 | 50 | char *buf;
|
nkeynes@998 | 51 | int buf_size;
|
nkeynes@998 | 52 | int buf_posn;
|
nkeynes@998 | 53 | };
|
nkeynes@998 | 54 |
|
nkeynes@1271 | 55 | static GList *gdb_server_conn_list = NULL;
|
nkeynes@1271 | 56 |
|
nkeynes@998 | 57 | void gdb_server_free( gpointer data )
|
nkeynes@998 | 58 | {
|
nkeynes@998 | 59 | struct gdb_server *server = (struct gdb_server *)data;
|
nkeynes@1271 | 60 | gdb_server_conn_list = g_list_remove(gdb_server_conn_list, server);
|
nkeynes@998 | 61 | free((char *)server->peer_name);
|
nkeynes@998 | 62 | free(server->buf);
|
nkeynes@998 | 63 | free(data);
|
nkeynes@998 | 64 | }
|
nkeynes@998 | 65 |
|
nkeynes@998 | 66 | int gdb_checksum( char *data, int length )
|
nkeynes@998 | 67 | {
|
nkeynes@998 | 68 | int i;
|
nkeynes@998 | 69 | int result = 0;
|
nkeynes@998 | 70 | for( i=0; i<length; i++ )
|
nkeynes@998 | 71 | result += data[i];
|
nkeynes@998 | 72 | result &= 0xFF;
|
nkeynes@998 | 73 | return result;
|
nkeynes@998 | 74 | }
|
nkeynes@998 | 75 |
|
nkeynes@998 | 76 | void gdb_send_frame( struct gdb_server *server, char *data, int length )
|
nkeynes@998 | 77 | {
|
nkeynes@998 | 78 | char out[length+5];
|
nkeynes@998 | 79 | snprintf( out, length+5, "$%.*s#%02x", length, data, gdb_checksum(data,length) );
|
nkeynes@998 | 80 | write( server->fd, out, length+4 );
|
nkeynes@998 | 81 | }
|
nkeynes@998 | 82 |
|
nkeynes@998 | 83 | /**
|
nkeynes@998 | 84 | * Send bulk data (ie memory dump) as hex, with optional string prefix.
|
nkeynes@998 | 85 | * Saves double copying when going through gdb_send_frame.
|
nkeynes@998 | 86 | */
|
nkeynes@998 | 87 | void gdb_send_hex_data( struct gdb_server *server, char *prefix, unsigned char *data, int datalen )
|
nkeynes@998 | 88 | {
|
nkeynes@998 | 89 | int prefixlen = 0;
|
nkeynes@998 | 90 | if( prefix != NULL )
|
nkeynes@998 | 91 | prefixlen = strlen(prefix);
|
nkeynes@998 | 92 | int totallen = datalen*2 + prefixlen + 4;
|
nkeynes@998 | 93 | char out[totallen+1];
|
nkeynes@998 | 94 | char *p = &out[1];
|
nkeynes@998 | 95 | int i;
|
nkeynes@998 | 96 |
|
nkeynes@998 | 97 | out[0] = '$';
|
nkeynes@998 | 98 | if( prefix != NULL ) {
|
nkeynes@998 | 99 | p += sprintf( p, "%s", prefix );
|
nkeynes@998 | 100 | }
|
nkeynes@998 | 101 | for( i=0; i<datalen; i++ ) {
|
nkeynes@998 | 102 | p += sprintf( p, "%02x", data[i] );
|
nkeynes@998 | 103 | }
|
nkeynes@998 | 104 | *p++ = '#';
|
nkeynes@998 | 105 | sprintf( p, "%02x", gdb_checksum(out+1, datalen*2 + prefixlen) );
|
nkeynes@998 | 106 | write( server->fd, out, totallen );
|
nkeynes@998 | 107 | }
|
nkeynes@998 | 108 |
|
nkeynes@998 | 109 | /**
|
nkeynes@998 | 110 | * Parse bulk hex data - buffer should be at least datalen/2 bytes long
|
nkeynes@998 | 111 | */
|
nkeynes@1025 | 112 | size_t gdb_read_hex_data( struct gdb_server *server, unsigned char *buf, char *data, int datalen )
|
nkeynes@998 | 113 | {
|
nkeynes@998 | 114 | char *p = data;
|
nkeynes@998 | 115 | for( int i=0; i<datalen/2; i++ ) {
|
nkeynes@998 | 116 | int v;
|
nkeynes@998 | 117 | sscanf( p, "%02x", &v );
|
nkeynes@998 | 118 | buf[i] = v;
|
nkeynes@998 | 119 | p += 2;
|
nkeynes@998 | 120 | }
|
nkeynes@998 | 121 | return datalen/2;
|
nkeynes@998 | 122 | }
|
nkeynes@998 | 123 |
|
nkeynes@998 | 124 | /**
|
nkeynes@998 | 125 | * Parse bulk binary-encoded data - $, #, 0x7D are encoded as 0x7d, char ^ 0x20.
|
nkeynes@998 | 126 | * Buffer should be at least datalen bytes longs.
|
nkeynes@998 | 127 | */
|
nkeynes@1025 | 128 | size_t gdb_read_binary_data( struct gdb_server *server, unsigned char *buf, char *data, int datalen )
|
nkeynes@998 | 129 | {
|
nkeynes@998 | 130 | unsigned char *q = buf;
|
nkeynes@998 | 131 | for( int i=0, j=0; i<datalen; i++ ) {
|
nkeynes@998 | 132 | if( data[i] == 0x7D ) {
|
nkeynes@998 | 133 | if( i == datalen-1 ) {
|
nkeynes@998 | 134 | return -1;
|
nkeynes@998 | 135 | } else {
|
nkeynes@998 | 136 | *q++ = data[++i] ^ 0x20;
|
nkeynes@998 | 137 | }
|
nkeynes@998 | 138 | } else {
|
nkeynes@998 | 139 | *q++ = data[i];
|
nkeynes@998 | 140 | }
|
nkeynes@998 | 141 | }
|
nkeynes@998 | 142 | return q - buf;
|
nkeynes@998 | 143 | }
|
nkeynes@998 | 144 |
|
nkeynes@998 | 145 | void gdb_printf_frame( struct gdb_server *server, char *msg, ... )
|
nkeynes@998 | 146 | {
|
nkeynes@998 | 147 | va_list va;
|
nkeynes@998 | 148 |
|
nkeynes@998 | 149 | va_start(va,msg);
|
nkeynes@998 | 150 | int len = vsnprintf( NULL, 0, msg, va );
|
nkeynes@998 | 151 | char buf[len+1];
|
nkeynes@998 | 152 | vsnprintf( buf, len+1, msg, va);
|
nkeynes@998 | 153 | va_end(va);
|
nkeynes@998 | 154 | gdb_send_frame( server, buf, len );
|
nkeynes@998 | 155 | }
|
nkeynes@998 | 156 |
|
nkeynes@998 | 157 | int gdb_print_registers( struct gdb_server *server, char *buf, int buflen, int firstreg, int regcount )
|
nkeynes@998 | 158 | {
|
nkeynes@998 | 159 | int i;
|
nkeynes@998 | 160 | char *p = buf;
|
nkeynes@998 | 161 | char *endp = buf + (buflen-8);
|
nkeynes@998 | 162 | for( i=firstreg; i < firstreg + regcount && p < endp; i++ ) {
|
nkeynes@998 | 163 | uint8_t *val = server->cpu->get_register(i);
|
nkeynes@998 | 164 | if( val == NULL ) {
|
nkeynes@998 | 165 | sprintf( p, "00000000" );
|
nkeynes@998 | 166 | } else {
|
nkeynes@998 | 167 | sprintf( p, "%02x%02x%02x%02x", val[0], val[1], val[2], val[3] );
|
nkeynes@998 | 168 | }
|
nkeynes@998 | 169 | p += 8;
|
nkeynes@998 | 170 | }
|
nkeynes@998 | 171 |
|
nkeynes@998 | 172 | return i - firstreg;
|
nkeynes@998 | 173 | }
|
nkeynes@998 | 174 |
|
nkeynes@998 | 175 | void gdb_set_registers( struct gdb_server *server, char *buf, int firstreg, int regcount )
|
nkeynes@998 | 176 | {
|
nkeynes@998 | 177 | int i;
|
nkeynes@998 | 178 | char *p = buf;
|
nkeynes@998 | 179 | for( i=firstreg; i < firstreg + regcount; i++ ) {
|
nkeynes@998 | 180 | uint8_t *val = server->cpu->get_register(i);
|
nkeynes@1020 | 181 | unsigned int a,b,c,d;
|
nkeynes@998 | 182 | if( val != NULL ) {
|
nkeynes@1020 | 183 | sscanf( p, "%02x%02x%02x%02x", &a, &b, &c, &d );
|
nkeynes@1020 | 184 | val[0] = (uint8_t)a;
|
nkeynes@1020 | 185 | val[1] = (uint8_t)b;
|
nkeynes@1020 | 186 | val[2] = (uint8_t)c;
|
nkeynes@1020 | 187 | val[3] = (uint8_t)d;
|
nkeynes@998 | 188 | }
|
nkeynes@998 | 189 | p += 8;
|
nkeynes@998 | 190 | }
|
nkeynes@998 | 191 | }
|
nkeynes@998 | 192 |
|
nkeynes@998 | 193 | /**
|
nkeynes@998 | 194 | * Send a 2-digit error code. There's no actual definition for any of the codes
|
nkeynes@998 | 195 | * so they're more for our own amusement really.
|
nkeynes@998 | 196 | */
|
nkeynes@998 | 197 | void gdb_send_error( struct gdb_server *server, int error )
|
nkeynes@998 | 198 | {
|
nkeynes@998 | 199 | char out[4];
|
nkeynes@998 | 200 | snprintf( out, 4, "E%02X", (error&0xFF) );
|
nkeynes@998 | 201 | gdb_send_frame( server, out, 3 );
|
nkeynes@998 | 202 | }
|
nkeynes@998 | 203 |
|
nkeynes@998 | 204 | void gdb_server_handle_frame( struct gdb_server *server, int command, char *data, int length )
|
nkeynes@998 | 205 | {
|
nkeynes@998 | 206 | unsigned int tmp, tmp2, tmp3;
|
nkeynes@998 | 207 | char buf[512];
|
nkeynes@998 | 208 |
|
nkeynes@998 | 209 | switch( command ) {
|
nkeynes@998 | 210 | case '!': /* Enable extended mode */
|
nkeynes@998 | 211 | gdb_send_frame( server, "OK", 2 );
|
nkeynes@998 | 212 | break;
|
nkeynes@998 | 213 | case '?': /* Get stop reason - always return 5 (TRAP) */
|
nkeynes@998 | 214 | gdb_send_frame( server, "S05", 3 );
|
nkeynes@998 | 215 | break;
|
nkeynes@998 | 216 | case 'c': /* Continue */
|
nkeynes@1271 | 217 | gui_do_later(dreamcast_run);
|
nkeynes@998 | 218 | break;
|
nkeynes@998 | 219 | case 'g': /* Read all general registers */
|
nkeynes@998 | 220 | gdb_print_registers( server, buf, sizeof(buf), 0, server->cpu->num_gpr_regs );
|
nkeynes@998 | 221 | gdb_send_frame( server, buf, strlen(buf) );
|
nkeynes@998 | 222 | break;
|
nkeynes@998 | 223 | case 'G': /* Write all general registers */
|
nkeynes@998 | 224 | if( length != server->cpu->num_gpr_regs*8 ) {
|
nkeynes@998 | 225 | gdb_send_error( server, GDB_ERROR_FORMAT );
|
nkeynes@998 | 226 | } else {
|
nkeynes@998 | 227 | gdb_set_registers( server, data, 0, server->cpu->num_gpr_regs );
|
nkeynes@998 | 228 | gdb_send_frame( server, "OK", 2 );
|
nkeynes@998 | 229 | }
|
nkeynes@998 | 230 | break;
|
nkeynes@998 | 231 | case 'H': /* Set thread - only thread 1 is supported here */
|
nkeynes@998 | 232 | if( length < 2 ) {
|
nkeynes@998 | 233 | gdb_send_error( server, GDB_ERROR_FORMAT );
|
nkeynes@998 | 234 | } else {
|
nkeynes@998 | 235 | int thread;
|
nkeynes@998 | 236 | sscanf( data+1, "%d", &thread );
|
nkeynes@998 | 237 | if( thread >= -1 && thread <= 1 ) {
|
nkeynes@998 | 238 | gdb_send_frame( server, "OK", 2 );
|
nkeynes@998 | 239 | } else {
|
nkeynes@998 | 240 | gdb_send_error( server, GDB_ERROR_INVAL );
|
nkeynes@998 | 241 | }
|
nkeynes@998 | 242 | }
|
nkeynes@998 | 243 | break;
|
nkeynes@998 | 244 | case 'k': /* kill - do nothing */
|
nkeynes@998 | 245 | gdb_send_frame( server, "", 0 );
|
nkeynes@998 | 246 | break;
|
nkeynes@998 | 247 | case 'm': /* Read memory */
|
nkeynes@998 | 248 | if( sscanf( data, "%x,%x", &tmp, &tmp2 ) != 2 ) {
|
nkeynes@998 | 249 | gdb_send_error( server, GDB_ERROR_FORMAT );
|
nkeynes@998 | 250 | } else {
|
nkeynes@998 | 251 | size_t datalen;
|
nkeynes@1025 | 252 | unsigned char mem[tmp2];
|
nkeynes@998 | 253 | if( server->mmu ) {
|
nkeynes@998 | 254 | datalen = server->cpu->read_mem_vma(mem, tmp, tmp2);
|
nkeynes@998 | 255 | } else {
|
nkeynes@998 | 256 | datalen = server->cpu->read_mem_phys(mem, tmp, tmp2);
|
nkeynes@998 | 257 | }
|
nkeynes@998 | 258 | if( datalen == 0 ) {
|
nkeynes@998 | 259 | gdb_send_error( server, GDB_ERROR_INVAL );
|
nkeynes@998 | 260 | } else {
|
nkeynes@998 | 261 | gdb_send_hex_data( server, NULL, mem, datalen );
|
nkeynes@998 | 262 | }
|
nkeynes@998 | 263 | }
|
nkeynes@998 | 264 | break;
|
nkeynes@998 | 265 | case 'M': /* Write memory */
|
nkeynes@998 | 266 | if( sscanf( data, "%x,%x:%n", &tmp, &tmp2, &tmp3 ) != 2 ||
|
nkeynes@998 | 267 | length-tmp3 != tmp2*2 ) {
|
nkeynes@998 | 268 | gdb_send_error( server, GDB_ERROR_FORMAT );
|
nkeynes@998 | 269 | } else {
|
nkeynes@998 | 270 | size_t len;
|
nkeynes@1025 | 271 | unsigned char mem[tmp2];
|
nkeynes@998 | 272 | len = gdb_read_hex_data( server, mem, data+tmp3, length-tmp3 );
|
nkeynes@998 | 273 | if( len != tmp2 ) {
|
nkeynes@998 | 274 | gdb_send_error( server, GDB_ERROR_FORMAT );
|
nkeynes@998 | 275 | } else {
|
nkeynes@998 | 276 | if( server->mmu ) {
|
nkeynes@998 | 277 | len = server->cpu->write_mem_vma(tmp, mem, tmp2);
|
nkeynes@998 | 278 | } else {
|
nkeynes@998 | 279 | len = server->cpu->write_mem_phys(tmp, mem, tmp2);
|
nkeynes@998 | 280 | }
|
nkeynes@998 | 281 | if( len != tmp2 ) {
|
nkeynes@998 | 282 | gdb_send_error( server, GDB_ERROR_INVAL );
|
nkeynes@998 | 283 | } else {
|
nkeynes@998 | 284 | gdb_send_frame( server, "OK", 2 );
|
nkeynes@998 | 285 | }
|
nkeynes@998 | 286 | }
|
nkeynes@998 | 287 | }
|
nkeynes@998 | 288 | break;
|
nkeynes@998 | 289 | case 'p': /* Read single register */
|
nkeynes@998 | 290 | if( sscanf( data, "%x", &tmp ) != 1 ) {
|
nkeynes@998 | 291 | gdb_send_error( server, GDB_ERROR_FORMAT );
|
nkeynes@998 | 292 | } else if( tmp >= server->cpu->num_gdb_regs ) {
|
nkeynes@998 | 293 | gdb_send_error( server, GDB_ERROR_INVAL );
|
nkeynes@998 | 294 | } else {
|
nkeynes@998 | 295 | gdb_print_registers( server, buf, sizeof(buf), tmp, 1 );
|
nkeynes@998 | 296 | gdb_send_frame( server, buf, 8 );
|
nkeynes@998 | 297 | }
|
nkeynes@998 | 298 | break;
|
nkeynes@998 | 299 | case 'P': /* Write single register. */
|
nkeynes@998 | 300 | if( sscanf( data, "%x=%n", &tmp, &tmp2 ) != 1 ||
|
nkeynes@998 | 301 | length-tmp2 != 8) {
|
nkeynes@998 | 302 | gdb_send_error( server, GDB_ERROR_FORMAT );
|
nkeynes@998 | 303 | } else if( tmp >= server->cpu->num_gdb_regs ) {
|
nkeynes@998 | 304 | gdb_send_error( server, GDB_ERROR_INVAL );
|
nkeynes@998 | 305 | } else {
|
nkeynes@998 | 306 | gdb_set_registers( server, data+tmp2, tmp, 1 );
|
nkeynes@998 | 307 | gdb_send_frame( server, "OK", 2 );
|
nkeynes@998 | 308 | }
|
nkeynes@998 | 309 | break;
|
nkeynes@998 | 310 | case 'q': /* Query data */
|
nkeynes@998 | 311 | if( strcmp( data, "C" ) == 0 ) {
|
nkeynes@998 | 312 | gdb_send_frame( server, "QC1", 3 );
|
nkeynes@998 | 313 | } else if( strcmp( data, "fThreadInfo" ) == 0 ) {
|
nkeynes@998 | 314 | gdb_send_frame( server, "m1", 2 );
|
nkeynes@998 | 315 | } else if( strcmp( data, "sThreadInfo" ) == 0 ) {
|
nkeynes@998 | 316 | gdb_send_frame( server, "l", 1 );
|
nkeynes@998 | 317 | } else if( strncmp( data, "Supported", 9 ) == 0 ) {
|
nkeynes@998 | 318 | gdb_send_frame( server, "PacketSize=4000", 15 );
|
nkeynes@998 | 319 | } else if( strcmp( data, "Symbol::" ) == 0 ) {
|
nkeynes@998 | 320 | gdb_send_frame( server, "OK", 2 );
|
nkeynes@998 | 321 | } else {
|
nkeynes@998 | 322 | gdb_send_frame( server, "", 0 );
|
nkeynes@998 | 323 | }
|
nkeynes@998 | 324 | break;
|
nkeynes@998 | 325 | case 's': /* Single-step */
|
nkeynes@998 | 326 | if( length != 0 ) {
|
nkeynes@998 | 327 | if( sscanf( data, "%x", &tmp ) != 1 ) {
|
nkeynes@998 | 328 | gdb_send_error( server, GDB_ERROR_FORMAT );
|
nkeynes@998 | 329 | } else {
|
nkeynes@998 | 330 | *server->cpu->pc = tmp;
|
nkeynes@998 | 331 | }
|
nkeynes@998 | 332 | }
|
nkeynes@998 | 333 | server->cpu->step_func();
|
nkeynes@998 | 334 | gdb_send_frame( server, "S05", 3 );
|
nkeynes@998 | 335 | break;
|
nkeynes@998 | 336 | case 'T': /* Thread alive */
|
nkeynes@998 | 337 | if( sscanf( data, "%x", &tmp ) != 1 ) {
|
nkeynes@998 | 338 | gdb_send_error( server, GDB_ERROR_FORMAT );
|
nkeynes@998 | 339 | } else if( tmp != 1 ) {
|
nkeynes@998 | 340 | gdb_send_error( server, GDB_ERROR_INVAL );
|
nkeynes@998 | 341 | } else {
|
nkeynes@998 | 342 | gdb_send_frame( server, "OK", 2 );
|
nkeynes@998 | 343 | }
|
nkeynes@998 | 344 | break;
|
nkeynes@998 | 345 | case 'v': /* Verbose */
|
nkeynes@998 | 346 | /* Only current one is vCont, which we don't bother supporting
|
nkeynes@998 | 347 | * at the moment, but don't warn about it either */
|
nkeynes@998 | 348 | gdb_send_frame( server, "", 0 );
|
nkeynes@998 | 349 | break;
|
nkeynes@998 | 350 | case 'X': /* Write memory binary */
|
nkeynes@998 | 351 | if( sscanf( data, "%x,%x:%n", &tmp, &tmp2, &tmp3 ) != 2 ) {
|
nkeynes@998 | 352 | gdb_send_error( server, GDB_ERROR_FORMAT );
|
nkeynes@998 | 353 | } else {
|
nkeynes@1025 | 354 | unsigned char mem[length - tmp3];
|
nkeynes@998 | 355 | size_t len = gdb_read_binary_data( server, mem, data + tmp3, length-tmp3 );
|
nkeynes@998 | 356 | if( len != tmp2 ) {
|
nkeynes@998 | 357 | gdb_send_error( server, GDB_ERROR_FORMAT );
|
nkeynes@998 | 358 | } else {
|
nkeynes@998 | 359 | if( server->mmu ) {
|
nkeynes@998 | 360 | len = server->cpu->write_mem_vma(tmp, mem, tmp2);
|
nkeynes@998 | 361 | } else {
|
nkeynes@998 | 362 | len = server->cpu->write_mem_phys(tmp, mem, tmp2);
|
nkeynes@998 | 363 | }
|
nkeynes@998 | 364 | if( len != tmp2 ) {
|
nkeynes@998 | 365 | gdb_send_error( server, GDB_ERROR_INVAL );
|
nkeynes@998 | 366 | } else {
|
nkeynes@998 | 367 | gdb_send_frame( server, "OK", 2 );
|
nkeynes@998 | 368 | }
|
nkeynes@998 | 369 | }
|
nkeynes@998 | 370 | }
|
nkeynes@998 | 371 | break;
|
nkeynes@998 | 372 | case 'z': /* Remove Break/watchpoint */
|
nkeynes@998 | 373 | if( sscanf( data, "%d,%x,%x", &tmp, &tmp2, &tmp3 ) != 3 ) {
|
nkeynes@998 | 374 | gdb_send_error( server, GDB_ERROR_FORMAT );
|
nkeynes@998 | 375 | } else {
|
nkeynes@998 | 376 | if( tmp == 0 || tmp == 1 ) { /* soft break or hard break */
|
nkeynes@998 | 377 | server->cpu->clear_breakpoint( tmp2, BREAK_KEEP );
|
nkeynes@998 | 378 | gdb_send_frame( server, "OK", 2 );
|
nkeynes@998 | 379 | } else {
|
nkeynes@998 | 380 | gdb_send_frame( server, "", 0 );
|
nkeynes@998 | 381 | }
|
nkeynes@998 | 382 | }
|
nkeynes@998 | 383 | break;
|
nkeynes@998 | 384 | case 'Z': /* Insert Break/watchpoint */
|
nkeynes@998 | 385 | if( sscanf( data, "%d,%x,%x", &tmp, &tmp2, &tmp3 ) != 3 ) {
|
nkeynes@998 | 386 | gdb_send_error( server, GDB_ERROR_FORMAT );
|
nkeynes@998 | 387 | } else {
|
nkeynes@998 | 388 | if( tmp == 0 || tmp == 1 ) { /* soft break or hard break */
|
nkeynes@998 | 389 | server->cpu->set_breakpoint( tmp2, BREAK_KEEP );
|
nkeynes@998 | 390 | gdb_send_frame( server, "OK", 2 );
|
nkeynes@998 | 391 | } else {
|
nkeynes@998 | 392 | gdb_send_frame( server, "", 0 );
|
nkeynes@998 | 393 | }
|
nkeynes@998 | 394 | }
|
nkeynes@998 | 395 | break;
|
nkeynes@998 | 396 | default:
|
nkeynes@998 | 397 | /* Command unsupported */
|
nkeynes@998 | 398 | WARN( "Received unknown GDB command '%c%s'", command, data );
|
nkeynes@998 | 399 | gdb_send_frame( server, "", 0 );
|
nkeynes@998 | 400 | break;
|
nkeynes@998 | 401 | }
|
nkeynes@998 | 402 |
|
nkeynes@998 | 403 | }
|
nkeynes@998 | 404 |
|
nkeynes@998 | 405 | /**
|
nkeynes@998 | 406 | * Decode out frames from the raw data stream. A frame takes the form of
|
nkeynes@998 | 407 | * $<data>#<checksum>
|
nkeynes@998 | 408 | * where data may not contain a '#' character, and checksum is a simple
|
nkeynes@998 | 409 | * 8-bit modulo sum of all bytes in data, encoded as a 2-char hex number.
|
nkeynes@998 | 410 | *
|
nkeynes@998 | 411 | * The only other legal wire forms are
|
nkeynes@998 | 412 | * +
|
nkeynes@998 | 413 | * indicating successful reception of the last message (ignored here), and
|
nkeynes@998 | 414 | * -
|
nkeynes@998 | 415 | * indicating failed reception of the last message - need to resend.
|
nkeynes@998 | 416 | */
|
nkeynes@998 | 417 | void gdb_server_process_buffer( struct gdb_server *server )
|
nkeynes@998 | 418 | {
|
nkeynes@998 | 419 | int i, frame_start = -1, frame_len = -1;
|
nkeynes@998 | 420 | for( i=0; i<server->buf_posn; i++ ) {
|
nkeynes@998 | 421 | if( frame_start == -1 ) {
|
nkeynes@998 | 422 | if( server->buf[i] == '$' ) {
|
nkeynes@998 | 423 | frame_start = i;
|
nkeynes@998 | 424 | } else if( server->buf[i] == '+' ) {
|
nkeynes@998 | 425 | /* Success */
|
nkeynes@998 | 426 | continue;
|
nkeynes@998 | 427 | } else if( server->buf[i] == '-' ) {
|
nkeynes@998 | 428 | /* Request retransmit */
|
nkeynes@1271 | 429 | } else if( server->buf[i] == '\003' ) { /* Control-C */
|
nkeynes@1271 | 430 | if( dreamcast_is_running() ) {
|
nkeynes@1271 | 431 | dreamcast_stop();
|
nkeynes@1271 | 432 | }
|
nkeynes@998 | 433 | } /* Anything else is noise */
|
nkeynes@998 | 434 | } else if( server->buf[i] == '#' ) {
|
nkeynes@998 | 435 | frame_len = i - frame_start - 1;
|
nkeynes@998 | 436 | if( i+2 < server->buf_posn ) {
|
nkeynes@998 | 437 | int calc_checksum = gdb_checksum( &server->buf[frame_start+1], frame_len );
|
nkeynes@998 | 438 | int frame_checksum = 0;
|
nkeynes@998 | 439 | sscanf( &server->buf[i+1], "%02x", &frame_checksum );
|
nkeynes@998 | 440 |
|
nkeynes@998 | 441 | if( calc_checksum != frame_checksum ) {
|
nkeynes@998 | 442 | WARN( "GDB frame checksum failure (expected %02X but was %02X)",
|
nkeynes@998 | 443 | calc_checksum, frame_checksum );
|
nkeynes@998 | 444 | write( server->fd, "-", 1 );
|
nkeynes@998 | 445 | } else if( frame_len == 0 ) {
|
nkeynes@998 | 446 | /* Empty frame - should never occur as a request */
|
nkeynes@998 | 447 | WARN( "Empty GDB frame received" );
|
nkeynes@998 | 448 | write( server->fd, "-", 1 );
|
nkeynes@998 | 449 | } else {
|
nkeynes@998 | 450 | /* We have a good frame */
|
nkeynes@998 | 451 | write( server->fd, "+", 1 );
|
nkeynes@998 | 452 | server->buf[i] = '\0';
|
nkeynes@998 | 453 | gdb_server_handle_frame( server, server->buf[frame_start+1], &server->buf[frame_start+2], frame_len-1 );
|
nkeynes@998 | 454 | }
|
nkeynes@998 | 455 | i+=2;
|
nkeynes@998 | 456 | frame_start = -1;
|
nkeynes@998 | 457 | }
|
nkeynes@998 | 458 | }
|
nkeynes@998 | 459 | }
|
nkeynes@998 | 460 | if( frame_start == -1 ) {
|
nkeynes@998 | 461 | server->buf_posn = 0; /* Consumed whole buffer */
|
nkeynes@998 | 462 | } else if( frame_start > 0 ) {
|
nkeynes@998 | 463 | memmove(&server->buf[0], &server->buf[frame_start], server->buf_posn - frame_start);
|
nkeynes@998 | 464 | server->buf_posn -= frame_start;
|
nkeynes@998 | 465 | }
|
nkeynes@998 | 466 | }
|
nkeynes@998 | 467 |
|
nkeynes@998 | 468 | gboolean gdb_server_data_callback( int fd, void *data )
|
nkeynes@998 | 469 | {
|
nkeynes@998 | 470 | struct gdb_server *server = (struct gdb_server *)data;
|
nkeynes@998 | 471 |
|
nkeynes@998 | 472 | size_t len = read( fd, &server->buf[server->buf_posn], server->buf_size - server->buf_posn );
|
nkeynes@998 | 473 | if( len > 0 ) {
|
nkeynes@998 | 474 | server->buf_posn += len;
|
nkeynes@998 | 475 | gdb_server_process_buffer( server );
|
nkeynes@998 | 476 |
|
nkeynes@998 | 477 | /* If we have an oversized packet, extend the buffer */
|
nkeynes@998 | 478 | if( server->buf_posn > server->buf_size - BUFFER_SIZE_MARGIN &&
|
nkeynes@998 | 479 | server->buf_size < MAX_BUFFER_SIZE ) {
|
nkeynes@998 | 480 | server->buf_size <<= 1;
|
nkeynes@998 | 481 | server->buf = realloc( server->buf, server->buf_size );
|
nkeynes@998 | 482 | assert( server->buf != NULL );
|
nkeynes@998 | 483 | }
|
nkeynes@998 | 484 | return TRUE;
|
nkeynes@998 | 485 | } else {
|
nkeynes@998 | 486 | INFO( "GDB disconnected" );
|
nkeynes@998 | 487 | return FALSE;
|
nkeynes@998 | 488 | }
|
nkeynes@998 | 489 | }
|
nkeynes@998 | 490 |
|
nkeynes@998 | 491 | gboolean gdb_server_connect_callback( int fd, gpointer data )
|
nkeynes@998 | 492 | {
|
nkeynes@998 | 493 | struct sockaddr_in sin;
|
nkeynes@1272 | 494 | socklen_t sinlen = sizeof(sin);
|
nkeynes@998 | 495 | struct gdb_server *server = (struct gdb_server *)data;
|
nkeynes@998 | 496 | int conn_fd = accept( fd, (struct sockaddr *)&sin, &sinlen);
|
nkeynes@998 | 497 | if( conn_fd != -1 ) {
|
nkeynes@998 | 498 | struct gdb_server *chan_serv = calloc( sizeof(struct gdb_server), 1 );
|
nkeynes@998 | 499 | chan_serv->cpu = server->cpu;
|
nkeynes@998 | 500 | chan_serv->mmu = server->mmu;
|
nkeynes@998 | 501 | chan_serv->fd = conn_fd;
|
nkeynes@998 | 502 | chan_serv->peer_name = g_strdup_printf("%s:%d", inet_ntoa(sin.sin_addr), ntohs(sin.sin_port));
|
nkeynes@998 | 503 | chan_serv->buf = malloc(1024);
|
nkeynes@998 | 504 | chan_serv->buf_size = 1024;
|
nkeynes@998 | 505 | chan_serv->buf_posn = 0;
|
nkeynes@1077 | 506 | io_register_tcp_listener( conn_fd, gdb_server_data_callback, chan_serv, gdb_server_free );
|
nkeynes@1271 | 507 | gdb_server_conn_list = g_list_append(gdb_server_conn_list, chan_serv);
|
nkeynes@998 | 508 | INFO( "GDB connected from %s", chan_serv->peer_name );
|
nkeynes@998 | 509 | }
|
nkeynes@998 | 510 | return TRUE;
|
nkeynes@998 | 511 | }
|
nkeynes@998 | 512 |
|
nkeynes@1271 | 513 | void gdb_server_notify_stopped( struct gdb_server *server )
|
nkeynes@1271 | 514 | {
|
nkeynes@1271 | 515 | gdb_send_frame( server, "S05", 3 );
|
nkeynes@1271 | 516 | }
|
nkeynes@1271 | 517 |
|
nkeynes@1271 | 518 | /**
|
nkeynes@1271 | 519 | * stop handler to generate notifications to all connected clients
|
nkeynes@1271 | 520 | */
|
nkeynes@1271 | 521 | void gdb_server_on_stop()
|
nkeynes@1271 | 522 | {
|
nkeynes@1271 | 523 | GList *ptr;
|
nkeynes@1271 | 524 | for( ptr = gdb_server_conn_list; ptr != NULL; ptr = ptr->next ) {
|
nkeynes@1271 | 525 | gdb_server_notify_stopped( (struct gdb_server *)ptr->data );
|
nkeynes@1271 | 526 | }
|
nkeynes@1271 | 527 | }
|
nkeynes@1271 | 528 |
|
nkeynes@1271 | 529 | static gboolean module_registered = FALSE;
|
nkeynes@1271 | 530 | static struct dreamcast_module gdb_server_module = {
|
nkeynes@1271 | 531 | "gdbserver", NULL, NULL, NULL, NULL, gdb_server_on_stop, NULL, NULL };
|
nkeynes@1271 | 532 |
|
nkeynes@1271 | 533 |
|
nkeynes@998 | 534 | /**
|
nkeynes@998 | 535 | * Bind a network port for a GDB remote server for the specified cpu. The
|
nkeynes@998 | 536 | * port is registered for the system network callback.
|
nkeynes@998 | 537 | *
|
nkeynes@998 | 538 | * @param interface network interface to bind to, or null for the default (all) interface
|
nkeynes@998 | 539 | * @param port
|
nkeynes@998 | 540 | * @param cpu CPU to make available over the network port..
|
nkeynes@998 | 541 | * @param mmu if TRUE, virtual memory is made available to GDB, otherwise GDB
|
nkeynes@998 | 542 | * accesses physical memory.
|
nkeynes@998 | 543 | * @return TRUE if the server was bound successfully.
|
nkeynes@998 | 544 | */
|
nkeynes@998 | 545 | gboolean gdb_init_server( const char *interface, int port, cpu_desc_t cpu, gboolean mmu )
|
nkeynes@998 | 546 | {
|
nkeynes@1271 | 547 | if( !module_registered ) {
|
nkeynes@1271 | 548 | dreamcast_register_module( &gdb_server_module );
|
nkeynes@1271 | 549 | module_registered = TRUE;
|
nkeynes@1271 | 550 | }
|
nkeynes@1271 | 551 |
|
nkeynes@1077 | 552 | int fd = io_create_server_socket( interface, port );
|
nkeynes@998 | 553 | if( fd == -1 ) {
|
nkeynes@998 | 554 | return FALSE;
|
nkeynes@998 | 555 | }
|
nkeynes@998 | 556 |
|
nkeynes@998 | 557 | struct gdb_server *server = calloc( sizeof(struct gdb_server), 1 );
|
nkeynes@998 | 558 | server->cpu = cpu;
|
nkeynes@998 | 559 | server->mmu = mmu;
|
nkeynes@998 | 560 | server->fd = fd;
|
nkeynes@1081 | 561 | gboolean result = io_register_tcp_listener( fd, gdb_server_connect_callback, server, gdb_server_free ) != NULL;
|
nkeynes@1071 | 562 | INFO( "%s GDB server running on port %d", cpu->name, port );
|
nkeynes@1071 | 563 | return result;
|
nkeynes@998 | 564 | }
|