gdbio.c 14.4 KB
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406
/*---------------------------------------------------------------------*
 *  Copyright (C) 2004 BroadOn Communications Corp.
 *                 
 *  $RCSfile: gdbio.c,v $ (derived from rmonsio.c)
 *  $Revision: 1.4 $
 *  $Date: 2004/02/18 03:01:13 $
 *---------------------------------------------------------------------*/

/* 
 * This file contains functions for communicating with GDB via the RDB,
 * using the GDB Remote Serial Protocol (RSP).
 *
 * The BBplayer communicates with GDB through the following chain:
 *
 *         TCP/IP           USB                Shared variables  
 *  GDB <----------> MUX <----------> RDB Slave <---------> GDB Stub
 *   1                2                   3                    4
 *
 *
 *  1. GDB sents the GDB RSP Request to Mux. The maximum size of a
 *     GDB packet is >= 400 bytes and it set so that it can accommodate
 *     the largest packet (a 'G' packet) in the protocol.  For the MIPS-32
 *     architecture that would be 72 (# regs) * 8 (bytes to hex encode 32-bit)
 *     + 32 (for padding) = 608 bytes.
 *
 *  2. MUX reads from the GDB TCP socket and sends the information to the
 *     BBPlayer via USB.  MUX wraps the GDB data in RDB packets with
 *     type RDB_TYPE_HtoG_DEBUG and sends it through USB.  Each packet
 *     has a command byte where the upper 8 bit is used for the RDB type.
 *     The length is specified as follows for a RDB_TYPE_HtoG_DEBUG packet:
 *        It is assumed that the RDB_TYPE_HtoG_DEBUG packet will only
 *          be sent if the length of the debug data is > 0.
 *        If the length is <= 3 bytes, then the length is stored in
 *          the lower 2 bits.  The number of bytes used for sending
 *          n bytes of data will be 1+n.  This is good for sending GDB
 *          single character acknowledgments ('+').
 *        If the length is > 3 bytes and < 254 bytes, the lower 2 bits are 
 *          set to 0.  The length is specified in the next byte.  The 
 *          number of bytes used for sending n bytes of data will be 2+n.
 *        If the data is longer than 254 bytes, it is chopped up into 
 *          254 size blocks and set.  This is because the USB interface
 *          can only handle up to 256 bytes at a time.
 *
 *     After all data from this read is sent, MUX will sent a RDB packet of
 *     type RDB_TYPE_HtoG_DEBUG_DONE (consuming 1 byte) to RDB, indicating
 *     that there is no more data from the debuggin stream.  
 *
 *     At this point, MUX will also block the debug stream until it gets a 
 *     RDB_TYPE_GtoH_DEBUG_READY signal from the RDB. If GDB still attempts
 *     to communicate with MUX, all incoming data on the debug stream is 
 *     dropped.  GDB will have to retransmit the data.
 *
 *  3. The RDB will read the data from the USB/RDB buffer (with a maximumum
 *     size of RIP_BULK_BUFSZ = 8K) and copy data that has a RDB type
 *     of RDB_TYPE_HtoG_DEBUG into the shared debug buffer pointed to by 
 *     __osRdb_DbgRead_Buf.  The size of the buffer is specified by 
 *     __osRdb_DbgRead_BufSize.  It is set to GDB_DBG_BUF_SIZE = 2048 bytes.  
 *     The number of bytes read is kept in __osRdb_DbgRead_Cnt.  The RDB will 
 *     continue to copy data into the buffer until it becomes full or it gets
 *     a RDB packet with RDB type RDB_TYPE_HtoG_DEBUG_DONE.  Once it receives
 *     the signal that the debug data has finish, it will send the event
 *     OS_EVENT_RDB_DBG_DONE, waking up the GDB IO thread. If the buffer becomes
 *     full before it receives this signal, the RDB will drop any additional 
 *     data for the debugger.
 *
 *  4. The GDB IO thread waits in a loop for input from the RDB.  It will 
 *     initially set the __osRdb_DbgRead_Buf to point to its buffer 
 *     gdbRdbReadBuf (with maximum buffer size GDB_DBG_BUF_SIZE = 2048 bytes)
 *     and set__osRdb_DbgRead_Cnt to 0.  Once it gets the OS_EVENT_RDB_DBG_DONE
 *     message, it will proceed to process the next GDB packet, character by 
 *     character, removing the GDB packet headers/trailers and copying it into 
 *     the __gdbInBuffer (with maximum buffer size GDB_DBG_BUF_SIZE = 2048 
 *     bytes).  When it has consumed all the characters in its buffer, it will 
 *     resets the shared variables __osRdb_DbgRead_Buf and __osRdb_DbgRead_Cnt 
 *     and send a message back to MUX (via RDB/USB) with the RDB packet type 
 *     of RDB_TYPE_GtoH_DEBUG_READY, indicating that it is ready for addtional 
 *     GDB input.  At that point, MUX will unblock the debug stream, and will 
 *     be willing to copy data from the GDB debugger to the BBPlayer again.
 */

#include "ultragdb.h"
#include "rdb.h"
#include "os_bb.h"
#include <sys/signal.h>

#ifndef _FINALROM

static OSMesgQueue IOmq;
static OSMesg IOmsgs;
OSMesgQueue BKmq;
OSMesg BKmsgs;

u8  gdbRdbReadBuf[GDB_DBG_BUF_SIZE];
u32 gdbRdbReadBufSize = 0;

extern u8  *__osRdb_DbgRead_Buf;
extern u32 __osRdb_DbgRead_Ct;
extern u32 __osRdb_DbgRead_BufSize;

extern char hexchars[];

void __gdbSendFault( OSThread * thread )
{
    volatile float f;
    u8   *tPtr;
    u32  sent = 0;

    /*
     * Touch a floating point register to become the owner of the floating
     * point unit (and force the previous owner to save its registers).
     */
    
    f = 0.0F;
    
    tPtr = (u8*)thread;
    while(sent < sizeof(OSThread))
    {
        sent += __osRdbSend(&tPtr[sent], sizeof(OSThread)-sent, RDB_TYPE_GtoH_FAULT);
    }
}

void __gdbIOflush( void )
{
    int              sent = 0;
    unsigned char    tstr[4];

    while(sent < 1)
        sent += __osRdbSend(&tstr[0], 1, RDB_TYPE_GtoH_DEBUG_DONE);
}

int __gdbSend(char *s, int n)
{
    int sent = 0;

    while(sent < n)
        sent +=  __osRdbSend(&s[sent], n-sent, RDB_TYPE_GtoH_DEBUG);
    return sent;
}

char __gdbPutDebugChar(char c)
{
    __gdbSend(&c, 1);
    return c; 
}


/* This functions gets the next character for the debug stream from the RDB. */
int __gdbGetDebugChar(void)
{
    u32  saveMask;
    char c = 0;
    static int cnt = 0;
    static int sent;
    unsigned char    tstr[4]; /* junk, but needed for osRdbSend */    

    while (cnt >= gdbRdbReadBufSize) {
        /* Make sure we are not interrupted when resetting the shared
         * variables for the debug buffer */
        /* Disable interrupts */
        saveMask = __osDisableInt();
        __osRdb_DbgRead_Buf = gdbRdbReadBuf;  /* reset the buffer pointer */
        __osRdb_DbgRead_Ct  = 0;
        /* Restore interrupts */
        __osRestoreInt(saveMask);
	
        sent = 0;
        while(sent < 1)
            sent += __osRdbSend(&tstr[0],1,RDB_TYPE_GtoH_DEBUG_READY);

        osRecvMesg(&IOmq, NULL, OS_MESG_BLOCK);

        /* Make sure we are not interrupted when reading the shared variable
         * __osRdb_DbgRead_Ct */
        
        /* For the MIPS R4300, the store is an atomic operation so we don't 
         * need to disable interrupts */
        /* Disable interrupts */
        /* saveMask = __osDisableInt(); */
        gdbRdbReadBufSize = __osRdb_DbgRead_Ct;
        /* Restore interrupts */
        /*__osRestoreInt(saveMask); */

        if (gdbDebug & GDB_DEBUG_SHOW_INFO)
        {
            GDB_PRINTF("%s: Debug Message Received: %d bytes\n",
                       gdb_module_name, gdbRdbReadBufSize);
        }
        cnt = 0;
    }

    if (gdbRdbReadBufSize)
        c = gdbRdbReadBuf[cnt++];
    
    return c;
}

/************************************************************************
 Function: __gdbGetPacket
 Args: 	   char* buffer  - The buffer to store the packet into.
           u32   bufsize - The size of the buffer.
           int*  psig    - Pointer to signal value.
 Purpose:  This function retrieves a GDB packet by scanning for the 
           sequence $<data>#<checksum>.  This function will not return 
           until it has retrieved a GDB packet with the correct checksum 
           (it will request GDB retransmit the packet if the checksum is
            incorrect).  The <data> portion of the GDB packet is stored
           in buffer along with a null-termination character at the end.

           If a CTRL-C is received out-side of a packet, then *psig
           is set to SIGINT, and the function returns immediately.
           Otherwise, *psig is set to 0.
           
 Returns:  u32 - The number of characters in the packet.

 TODO:     Improve handling for buffer overflow.
************************************************************************/
u32 __gdbGetPacket(char *buffer, u32 bufsize, int* psig)
{
    static int badchars = 0;

    unsigned char checksum;
    unsigned char xmitcsum;
    int  i;
    u32  count;
    char ch;

    if (psig != NULL) *psig = 0;

    if (buffer == NULL) return 0;
    if (bufsize == 0) return 0;
  
    do {
        /* wait around for the start character, ignore all other characters */
        while ((ch = __gdbGetDebugChar() & 0x7f) != '$')
        {
            if (ch == 0x03) /* Control-C */
            {
                /* Signal to stop program */
                if (psig != NULL) *psig = SIGINT;
                return 0;
            }

            if (ch != '+' && ch != '-') 
            {
                if (gdbDebug & GDB_DEBUG_SHOW_CHAR)
                {
                    GDB_PRINTF("%s: Got char (outside of packet): %c (0x%x)\n", 
                               gdb_module_name, ch, ch);
                }

                /* Hmm, we are seeing some funny characters */
                /* let's send a request for retransmission */
                if ((badchars % 10) == 0)
                {
                    __gdbPutDebugChar('-');
                }
                badchars++;
            }
        }
        checksum = 0;
        xmitcsum = -1;
    
        count = 0;
    
        /* now, read until a # or end of buffer is found */
        /* save one char in buffer for null terminator */
        while (count < bufsize-1) {
            ch = __gdbGetDebugChar() & 0x7f;
            if (ch == '#') break;
            checksum = checksum + ch;
            buffer[count] = ch;
            count++;
        }
        buffer[count] = 0; /* NULL-terminate */

        if (ch == '#') {
            /* Got end of packet - everything is good */
            /* Let's check the checksum */
            xmitcsum = hex(__gdbGetDebugChar() & 0x7f) << 4;
            xmitcsum += hex(__gdbGetDebugChar() & 0x7f);
            if ((gdbDebug & GDB_DEBUG_SHOW_ERROR) && (checksum != xmitcsum)) {
                GDB_PRINTF("%s: Packet failed checksum - requesting retransmission\n",
                           gdb_module_name);
                /* error */
            }
      
            if (checksum != xmitcsum)
                __gdbPutDebugChar('-'); /* failed checksum */ 
            else {
                __gdbPutDebugChar('+');  /* successful transfer */
                /* if a sequence char is present, reply the sequence ID */
                if (buffer[2] == ':') {
                    __gdbPutDebugChar( buffer[0] );
                    __gdbPutDebugChar( buffer[1] );
                    /* remove sequence chars from buffer */
                    count = strlen(buffer);
                    for (i=3; i <= count; i++) buffer[i-3] = buffer[i];
                } 
            } 
        } 
        else {
            /* TODO */
            /* We must have terminated the loop because we've reached 
               the maximum size of the buffer and haven't gotten
               the end of the packet yet.  What should we do????? */

            /* We don't want to allocate more memory for the messages, but
               we really cannot handle this message - is there a way to respond to
               GDB saying the message is too big and to break it down? */
            if (gdbDebug & GDB_DEBUG_SHOW_ERROR)
            {
                GDB_PRINTF("%s: GDB packet too big for buffer size %d\n",
                           gdb_module_name, bufsize);
            }

            /* Right now, we are just going to ignore this sequence of characters 
               and continue looping around until we get a packet we can handle */

        }
    } while (checksum != xmitcsum);
    return count;
}

/************************************************************************
 Function: __gdbPutPacket
 Args: 	   char* buffer  - The packet info to send to GDB.
 Assumes:  buffer is null-terminated
 Purpose:  This function wraps up the contents of the buffer in the format
           $<packet info>$<checksum> by adding a leading '$', the trailing
           '#', and calculating the checksum and sending the resulting 
           packet to GDB.
           This function returns immediately after sending the packet
           and does not check to see if GDB has recieved it.

 TODO:     Wait and get acknowledge from GDB ('+') and resend packet
           if needed?????
************************************************************************/
void __gdbPutPacket(char *buffer)
{
    unsigned char checksum;
    int  count;
    char ch;

    if (buffer == NULL) return;

    /* Buffer is assumed to be null-terminated */
    if (gdbDebug & GDB_DEBUG_SHOW_PACKET)
        GDB_PRINTF("%s: sending packet: %s\n", gdb_module_name, buffer);
  
    /*  $<packet info>#<checksum>. */
    do {
        __gdbPutDebugChar('$');
        checksum = 0;
        count    = 0;
  
        while ((ch=buffer[count])) {
/*          For efficiency, we will send the entire buffer at once */
/*            if (! __gdbPutDebugChar(ch)) return; */
            checksum += ch;
            count += 1;
        }
        
        if (count > 0)
            __gdbSend(buffer, count);
        __gdbPutDebugChar('#');
        __gdbPutDebugChar(hexchars[checksum >> 4]);
        __gdbPutDebugChar(hexchars[checksum % 16]);

    } while (1 == 0);  /* (__gdbGetDebugChar() != '+'); */  
}

/************************************************************************
 Function: __gdbIOhandler
 Purpose:  This function is the entry point of the thread responsible 
           for communicating with GDB.
************************************************************************/
void __gdbIOhandler(void)
{
    u32 saveMask;

    osCreateMesgQueue(&IOmq, &IOmsgs, 1);
    osSetEventMesg(OS_EVENT_RDB_DBG_DONE, &IOmq, 0);

    osCreateMesgQueue(&BKmq, &BKmsgs, 1);
    osSetEventMesg(OS_EVENT_DBG_WAIT_BRK, &BKmq, 0);

    saveMask = __osDisableInt();    
    __osRdb_DbgRead_BufSize = GDB_DBG_BUF_SIZE;
    __osRdb_DbgRead_Buf = gdbRdbReadBuf;
    __osRdb_DbgRead_Ct  = 0;
    __osRestoreInt(saveMask);
	
    while(1)
    {
        /* Use GDB Serial Protocol */
        gdbProcPacket();
    }
}

#endif /* #ifndef _FINALROM */