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