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@998 | 30 | #include "netutil.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@998 | 107 | size_t gdb_read_hex_data( struct gdb_server *server, unsigned char *buf, unsigned 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@998 | 123 | size_t gdb_read_binary_data( struct gdb_server *server, unsigned char *buf, unsigned 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@998 | 248 | 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@998 | 267 | 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@998 | 350 | 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@998 | 498 | net_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@998 | 517 | int fd = net_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@998 | 526 | net_register_tcp_listener( fd, gdb_server_connect_callback, server, gdb_server_free );
|
nkeynes@998 | 527 | INFO( "%s GDB server running on port %d", cpu->name, port );
|
nkeynes@998 | 528 | }
|