Search
lxdream.org :: lxdream/src/gdbserver.c
lxdream 0.9.1
released Jun 29
Download Now
filename src/gdbserver.c
changeset 1272:0b947d924029
prev1271:3edc4bdd7c0b
next1298:d0eb2307b847
author nkeynes
date Sat Mar 17 08:36:17 2012 +1000 (12 years ago)
permissions -rw-r--r--
last change Fix uninitialised sinlen in gdb_server_connect_callback, patch from Guinux,
thanks!
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
}
.