filename | src/gdbserver.c |
changeset | 1272:0b947d924029 |
prev | 1271:3edc4bdd7c0b |
next | 1298:d0eb2307b847 |
author | nkeynes |
date | Tue Mar 20 08:29:38 2012 +1000 (12 years ago) |
permissions | -rw-r--r-- |
last change | More android WIP - Implement onPause/onResume (although resume is not actually working yet) - Implement BGRA => RGBA texture conversion (BGRA doesn't seem to work on the TFP) Boot swirl is now displayed, albeit depth buffering seems to be broken. |
file | annotate | diff | log | raw |
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 | } |
.