From 600d7edc422da95ad4ac11f0f3127cd01f8206d1 Mon Sep 17 00:00:00 2001 From: cnlohr <lohr85@gmail.com> Date: Wed, 3 May 2023 06:17:10 -0400 Subject: [PATCH] Separate out the gdbserver into microgdbstub.h --- minichlink/microgdbstub.h | 619 ++++++++++++++++++++++++++++++++++++++ minichlink/minichgdb.c | 567 +--------------------------------- 2 files changed, 635 insertions(+), 551 deletions(-) create mode 100644 minichlink/microgdbstub.h diff --git a/minichlink/microgdbstub.h b/minichlink/microgdbstub.h new file mode 100644 index 0000000..30e9dc3 --- /dev/null +++ b/minichlink/microgdbstub.h @@ -0,0 +1,619 @@ +/* + * Micro GDBStub Driver, for implementing a gdbserver. + * Copyright (C) Charles Lohr 2023 + * You may freely license this file under the MIT-x11, or the 2- or 3- or New BSD Licenses. + * You may also use this as though it is public domain. + * + * Simply: + * 1: define the RV_ Functions + * 2: Call the MicroGDB* functions needed. + * 3: Define MICROGDBSTUB_IMPLEMENTATION at least in one place this is included in your program. + * 4: If you want to let this manage the server as a network device, simply #define MICROGDBSTUB_SOCKETS + * + */ + +#ifndef _MICROGDBSTUB_H +#define _MICROGDBSTUB_H + +// You must write these for your processor. +void RVNetPoll(void * dev ); +int RVSendGDBHaltReason( void * dev ); +void RVNetConnect( void * dev ); +int RVSendGDBHaltReason( void * dev ); +void RVNetPoll(void * dev ); +int RVReadCPURegister( void * dev, int regno, uint32_t * regret ); +void RVDebugExec( void * dev, int halt_reset_or_resume ); +int RVReadMem( void * dev, uint32_t memaddy, uint8_t * payload, int len ); +int RVHandleBreakpoint( void * dev, int set, uint32_t address ); +int RVWriteRAM(void * dev, uint32_t memaddy, uint32_t length, uint8_t * payload ); +void RVHandleDisconnect( void * dev ); +void RVHandleGDBBreakRequest( void * dev ); + +#ifdef MICROGDBSTUB_SOCKETS +int MicroGDBPollServer( void * dev ); +int MicroGDBStubStartup( void * dev ); +void MicroGDBExitServer( void * dev ); +#endif + +// If you are not a network socket, you can pass in this data. +void MicroGDBStubSendReply( const void * data, int len, int docs ); +void MicroGDBStubHandleClientData( void * dev, const uint8_t * rxdata, int len ); + + +#ifdef MICROGDBSTUB_IMPLEMENTATION + +/////////////////////////////////////////////////////////////////////////////// +// Protocol Stuff + +#include <string.h> +#include <stdio.h> +#include <stdlib.h> + +uint16_t htons(uint16_t hostshort); +uint32_t htonl(uint32_t hostlong); + +char gdbbuffer[65536]; +uint8_t gdbchecksum = 0; +int gdbbufferplace = 0; +int gdbbufferstate = 0; + + +static inline char ToHEXNibble( int i ) +{ + i &= 0xf; + return ( i < 10 )?('0' + i):('a' - 10 + i); +} + +static int fromhex( char c ) +{ + if( c >= '0' && c <= '9' ) c = c - '0'; + else if( c >= 'A' && c <= 'F' ) c = c - 'A' + 10; + else if( c >= 'a' && c <= 'f' ) c = c - 'a' + 10; + else return -1; + return c; +} + +// if (numhex < 0) +static int ReadHex( char ** instr, int numhex, uint32_t * outwrite ) +{ + if( !instr ) return -1; + char * str = *instr; + // If negative - error. + // If positive - number of bytes read. + + *outwrite = 0; + + int scanhex = numhex; + if( scanhex < 0 ) + scanhex = strlen( str ); + + int i; + for( i = 0; i < scanhex; i++ ) + { + int v = fromhex( *(str++) ); + if( v < 0 ) + { + if( numhex < 0 ) + { + str--; + *instr = str; + return i; + } + else + { + *instr = str; + return - i - 1; + } + } + (*outwrite) = ((*outwrite) << 4) | v; + } + *instr = str; + return i; +} + +static int StringMatch( const char * haystack, const char * mat ) +{ + int i; + for( i = 0; mat[i] && haystack[i]; i++ ) + if( mat[i] != haystack[i] || haystack[i] == 0 ) break; + return mat[i] == 0; +} + +void SendReplyFull( const char * replyMessage ) +{ + MicroGDBStubSendReply( replyMessage, -1, '$' ); +} + +/////////////////////////////////////////////////////////////////////////////// +// General Protocol + +void HandleGDBPacket( void * dev, char * data, int len ) +{ + int i; + + //printf( ":::%s:::\n", data ); + // Got a packet? + if( data[0] != '$' ) return; + + data++; + + char cmd = *(data++); + switch( cmd ) + { + case 'q': + if( StringMatch( data, "Attached" ) ) + SendReplyFull( "1" ); //Attached to an existing process. + else if( StringMatch( data, "Supported" ) ) + SendReplyFull( "PacketSize=f000;qXfer:memory-map:read+" ); + else if( StringMatch( data, "C") ) // Get Current Thread ID. (Can't be -1 or 0. Those are special) + SendReplyFull( "QC1" ); + else if( StringMatch( data, "fThreadInfo" ) ) // Query all active thread IDs (Can't be 0 or 1) + SendReplyFull( "m1" ); + else if( StringMatch( data, "sThreadInfo" ) ) // Query all active thread IDs, continued + SendReplyFull( "l" ); + else if( StringMatch( data, "Xfer:memory-map" ) ) + SendReplyFull( MICROGDBSTUB_MEMORY_MAP ); + else + SendReplyFull( "" ); + break; + case 'c': + case 'C': + RVDebugExec( dev, (cmd == 'C')?4:2 ); + SendReplyFull( "OK" ); + break; + case 'D': + // Handle disconnect. + RVHandleDisconnect( dev ); + break; + case 'Z': + case 'z': + { + uint32_t type = 0; + uint32_t addr = 0; + uint32_t time = 0; + if( ReadHex( &data, -1, &type ) < 0 ) goto err; + if( *(data++) != ',' ) goto err; + if( ReadHex( &data, -1, &addr ) < 0 ) goto err; + if( *(data++) != ',' ) goto err; + if( ReadHex( &data, -1, &time ) < 0 ) goto err; + if( RVHandleBreakpoint( dev, cmd == 'Z', addr ) == 0 ) + { + SendReplyFull( "OK" ); + } + else + goto err; + break; + } + case 'm': + { + // Read memory (Binary) + uint32_t address_to_read = 0; + uint32_t length_to_read = 0; + if( ReadHex( &data, -1, &address_to_read ) < 0 ) goto err; + if( *(data++) != ',' ) goto err; + if( ReadHex( &data, -1, &length_to_read ) < 0 ) goto err; + + uint8_t * pl = alloca( length_to_read * 2 ); + if( RVReadMem( dev, address_to_read, pl, length_to_read ) < 0 ) + goto err; + char * repl = alloca( length_to_read * 2 ); + int i; + for( i = 0; i < length_to_read; i++ ) + { + sprintf( repl + i * 2, "%02x", pl[i] ); + } + SendReplyFull( repl ); + break; + } + case 'M': + { + uint32_t address_to_write = 0; + uint32_t length_to_write = 0; + if( ReadHex( &data, -1, &address_to_write ) < 0 ) goto err; + if( *(data++) != ',' ) goto err; + if( ReadHex( &data, -1, &length_to_write ) < 0 ) goto err; + if( *(data++) != ':' ) goto err; + + uint8_t * meml = alloca( length_to_write ); + int i; + for( i = 0; i < length_to_write; i++ ) + { + uint32_t rv; + if( ReadHex( &data, 2, &rv ) < 0 ) goto err; + meml[i] = rv; + } + if( RVWriteRAM( dev, address_to_write, length_to_write, meml ) < 0 ) goto err; + SendReplyFull( "OK" ); + break; + } + case 'X': + { + // Write memory, binary. + uint32_t address_to_write = 0; + uint32_t length_to_write = 0; + if( ReadHex( &data, -1, &address_to_write ) < 0 ) goto err; + if( *(data++) != ',' ) goto err; + if( ReadHex( &data, -1, &length_to_write ) < 0 ) goto err; + if( *(data++) != ':' ) goto err; + if( RVWriteRAM( dev, address_to_write, length_to_write, (uint8_t*)data ) < 0 ) goto err; + SendReplyFull( "OK" ); + break; + } + case 'v': + if( StringMatch( data, "Cont?" ) ) + { + // Request a list of actions supported by the ‘vCont’ packet. + // We don't support vCont + SendReplyFull( "vCont;s;c;t;" ); //no ;s + //SendReplyFull( "" ); + } + else + { + SendReplyFull( "" ); //"vMustReplyEmpty" + } + break; + case 'g': + { + // Register Read (All regs) + char cts[17*8+1]; + for( i = 0; i < 17; i++ ) + { + uint32_t regret; + if( RVReadCPURegister( dev, i, ®ret ) ) goto err; + sprintf( cts + i * 8, "%08x", htonl( regret ) ); + } + SendReplyFull( cts ); + break; + } + case 'p': + { + uint32_t regno; + // Register Read (Specific Reg) + if( ReadHex( &data, 2, ®no ) < 0 ) + SendReplyFull( "E 10" ); + else + { + char cts[9]; + uint32_t regret; + if( RVReadCPURegister( dev, regno, ®ret ) ) goto err; + sprintf( cts, "%08x", htonl( regret ) ); + SendReplyFull( cts ); + } + break; + } + case '?': // Query reason for target halt. + RVSendGDBHaltReason( dev ); + break; + case 'H': + // This is for things like selecting threads. + // I am going to leave this stubbed out. + SendReplyFull( "" ); + break; + default: + printf( "UNKNOWN PACKET: %d (%s)\n", len, data-1 ); + for( i = 0; i < len; i++ ) + { + printf( "%02x ", data[i] ); + } + printf( "\n" ); + goto err; + break; + } + return; +err: + SendReplyFull( "E 00" ); +} + +void MicroGDBStubHandleClientData( void * dev, const uint8_t * rxdata, int len ) +{ + int pl = 0; + for( pl = 0; pl < len; pl++ ) + { + int c = rxdata[pl]; + if( c == '$' && gdbbufferstate == 0 ) + { + gdbbufferplace = 0; + gdbbufferstate = 1; + } + if( c == 3 && gdbbufferstate == 0 ) + { + RVHandleGDBBreakRequest( dev ); + continue; + } + + switch( gdbbufferstate ) + { + default: + break; + case 1: + if( gdbbufferplace < sizeof( gdbbuffer ) - 2 ) + { + if( c == '}' ) { gdbbufferstate = 9; break; } + gdbbuffer[gdbbufferplace++] = c; + } + if( c == '#' ) gdbbufferstate = 2; + break; + case 9: // escape + gdbbuffer[gdbbufferplace++] = c ^ 0x20; + break; + case 2: + case 3: + { + int i; + c = fromhex( c ); + if( gdbbufferstate == 2 ) + { + gdbchecksum = c << 4; + gdbbufferstate++; + break; + } + if( gdbbufferstate == 3 ) gdbchecksum |= c; + + gdbbuffer[gdbbufferplace] = 0; + + uint8_t checkcsum = 0; + for( i = 1; i < gdbbufferplace - 1; i++ ) + { + checkcsum += ((uint8_t*)gdbbuffer)[i]; + } + if( checkcsum == gdbchecksum ) + { + MicroGDBStubSendReply( "+", -1, 0 ); + HandleGDBPacket( dev, (char*)gdbbuffer, gdbbufferplace ); + } + else + { + MicroGDBStubSendReply( "-", -1, 0 ); + } + + gdbbufferplace = 0; + gdbbufferstate = 0; + break; + } + } + } +} + + + + +#ifdef MICROGDBSTUB_SOCKETS + + +#ifdef WIN32 +#include <winsock2.h> +#define socklen_t uint32_t +#define SHUT_RDWR SD_BOTH +#define MSG_NOSIGNAL 0 +#else +#define closesocket close +#include <sys/types.h> +#include <sys/socket.h> +#include <linux/in.h> +#endif + +#include <fcntl.h> +#include <unistd.h> +#include <sys/time.h> +#include <poll.h> + +#include <stdio.h> +#include <string.h> +#include <time.h> +#include <stdlib.h> + + +int listenMode; // 0 for uninit. 1 for server, 2 for client. +int serverSocket; + +/////////////////////////////////////////////////////////////////////////////// +// Network layer. + + +void MicroGDBStubHandleDisconnect( void * dev ) +{ + RVHandleDisconnect( dev ); +} + +static int GDBListen( void * dev ) +{ + struct sockaddr_in sin; + serverSocket = socket(AF_INET, SOCK_STREAM, 0); + + //Make sure the socket worked. + if( serverSocket == -1 ) + { + fprintf( stderr, "Error: Cannot create socket.\n" ); + return -1; + } + + //Disable SO_LINGER (Well, enable it but turn it way down) +#ifdef WIN32 + struct linger lx; + lx.l_onoff = 1; + lx.l_linger = 0; + setsockopt( serverSocket, SOL_SOCKET, SO_LINGER, (const char *)&lx, sizeof( lx ) ); + + //Enable SO_REUSEADDR + int reusevar = 1; + setsockopt(serverSocket, SOL_SOCKET, SO_REUSEADDR, (const char*)&reusevar, sizeof(reusevar)); +#else + struct linger lx; + lx.l_onoff = 1; + lx.l_linger = 0; + setsockopt( serverSocket, SOL_SOCKET, SO_LINGER, &lx, sizeof( lx ) ); + + //Enable SO_REUSEADDR + int reusevar = 1; + setsockopt(serverSocket, SOL_SOCKET, SO_REUSEADDR, &reusevar, sizeof(reusevar)); +#endif + //Setup socket for listening address. + memset( &sin, 0, sizeof( sin ) ); + sin.sin_family = AF_INET; + sin.sin_addr.s_addr = INADDR_ANY; + sin.sin_port = htons( MICROGDBSTUB_PORT ); + + //Actually bind to the socket + if( bind( serverSocket, (struct sockaddr *) &sin, sizeof( sin ) ) == -1 ) + { + fprintf( stderr, "Could not bind to socket: %d\n", MICROGDBSTUB_PORT ); + closesocket( serverSocket ); + serverSocket = 0; + return -1; + } + + //Finally listen. + if( listen( serverSocket, 5 ) == -1 ) + { + fprintf(stderr, "Could not lieten to socket."); + closesocket( serverSocket ); + serverSocket = 0; + return -1; + } + return 0; +} + +int MicroGDBPollServer( void * dev ) +{ + if( !serverSocket ) return -4; + + struct pollfd allpolls[2]; + + int pollct = 1; + allpolls[0].fd = serverSocket; + allpolls[0].events = POLLIN; + + //Do something to watch all currently-waiting sockets. + poll( allpolls, pollct, 0 ); + + //If there's faults, bail. + if( allpolls[0].revents & (POLLERR|POLLHUP) ) + { + closesocket( serverSocket ); + if( listenMode == 1 ) + { + // Some sort of weird fatal close? Is this even possible? + fprintf( stderr, "Error: serverSocke was forcibly closed\n" ); + exit( -4 ); + } + else if( listenMode == 2 ) + { + MicroGDBStubHandleDisconnect( dev ); + if( serverSocket ) close( serverSocket ); + serverSocket = 0; + listenMode = 1; + GDBListen( dev ); + } + } + if( allpolls[0].revents & POLLIN ) + { + if( listenMode == 1 ) + { + struct sockaddr_in tin; + socklen_t addrlen = sizeof(tin); + memset( &tin, 0, addrlen ); + int tsocket = accept( serverSocket, (struct sockaddr *)&tin, &addrlen ); + closesocket( serverSocket ); + serverSocket = tsocket; + listenMode = 2; + gdbbufferstate = 0; + RVNetConnect( dev ); + // Established. + } + else if( listenMode == 2 ) + { + // Got data from a peer. + uint8_t buffer[16384]; + ssize_t rx = recv( serverSocket, buffer, sizeof( buffer ), MSG_NOSIGNAL ); + if( rx == 0 ) + { + MicroGDBStubHandleDisconnect( dev ); + close( serverSocket ); + serverSocket = 0; + listenMode = 1; + GDBListen( dev ); + } + else + MicroGDBStubHandleClientData( dev, buffer, (int)rx ); + } + } + + if( listenMode == 2 ) + { + RVNetPoll( dev ); + } + + return 0; +} + +void MicroGDBExitServer( void * dev ) +{ + shutdown( serverSocket, SHUT_RDWR ); + if( listenMode == 2 ) + { + MicroGDBStubHandleDisconnect( dev ); + } +} + + +void MicroGDBStubSendReply( const void * data, int len, int docs ) +{ + if( len < 0 ) len = strlen( data ); + if( docs ) + { + uint8_t * localbuffer = alloca( len ) + 5; + localbuffer[0] = '$'; + uint8_t checksum = 0; + int i; + for( i = 0; i < len; i++ ) + { + uint8_t v = ((const uint8_t*)data)[i]; + checksum += v; + localbuffer[i+1] = v; + } + localbuffer[len+1] = '#'; + localbuffer[len+2] = ToHEXNibble( checksum >> 4 ); + localbuffer[len+3] = ToHEXNibble( checksum ); + localbuffer[len+4] = 0; + data = (void*)localbuffer; + len += 4; + } + + if( listenMode == 2 ) + { + //printf( ">>>>%s<<<<(%d)\n", data ); + send( serverSocket, data, len, MSG_NOSIGNAL ); + } +} + + +int MicroGDBStubStartup( void * dev ) +{ +#ifdef WIN32 +{ + WORD wVersionRequested; + WSADATA wsaData; + int err; + wVersionRequested = MAKEWORD(2, 2); + + err = WSAStartup(wVersionRequested, &wsaData); + if (err != 0) { + /* Tell the user that we could not find a usable */ + /* Winsock DLL. */ + fprintf( stderr, "WSAStartup failed with error: %d\n", err); + return 1; + } +} +#endif + + listenMode = 1; + + return GDBListen( dev ); +} + + +#endif +#endif + +#endif + diff --git a/minichlink/minichgdb.c b/minichlink/minichgdb.c index 9526355..0735901 100644 --- a/minichlink/minichgdb.c +++ b/minichlink/minichgdb.c @@ -3,45 +3,14 @@ // Connect in with: // gdb-multiarch -ex 'target remote :2000' ./blink.elf - #include "minichlink.h" -#ifdef WIN32 -#include <winsock2.h> -#define socklen_t uint32_t -#define SHUT_RDWR SD_BOTH -#define MSG_NOSIGNAL 0 -#else -#define closesocket close -#include <sys/types.h> -#include <sys/socket.h> -#include <linux/in.h> -#endif - -#include <fcntl.h> -#include <unistd.h> -#include <sys/time.h> -#include <poll.h> -uint16_t htons(uint16_t hostshort); -uint32_t htonl(uint32_t hostlong); - -#include <stdio.h> -#include <string.h> -#include <time.h> -#include <stdlib.h> - -#define GDBSERVER_PORT 2000 -void SendReplyFull( const char * replyMessage ); - -/////////////////////////////////////////////////////////////////////////////// -// Actual Chip Operations - -// TODO: Breakpoints -// TODO: Unaligned access? +#define MICROGDBSTUB_IMPLEMENTATION +#define MICROGDBSTUB_SOCKETS +#define MICROGDBSTUB_PORT 2000 -// Mostly from PicoRVD. -const char* memory_map = "l<?xml version=\"1.0\"?>" +const char* MICROGDBSTUB_MEMORY_MAP = "l<?xml version=\"1.0\"?>" "<!DOCTYPE memory-map PUBLIC \"+//IDN gnu.org//DTD GDB Memory Map V1.0//EN\" \"http://sourceware.org/gdb/gdb-memory-map.dtd\">" "<memory-map>" " <memory type=\"flash\" start=\"0x00000000\" length=\"0x4000\">" @@ -52,6 +21,14 @@ const char* memory_map = "l<?xml version=\"1.0\"?>" " </memory>" "</memory-map>"; +#include "microgdbstub.h" + +void SendReplyFull( const char * replyMessage ); + +/////////////////////////////////////////////////////////////////////////////// +// Actual Chip Operations + +// Several pieces from picorvd. int shadow_running_state = 1; int last_halt_reason = 5; uint32_t backup_regs[17]; @@ -348,532 +325,20 @@ void RVHandleGDBBreakRequest( void * dev ) } -/////////////////////////////////////////////////////////////////////////////// -// Protocol Stuff - - -int listenMode; // 0 for uninit. 1 for server, 2 for client. -int serverSocket; - -char gdbbuffer[65536]; -uint8_t gdbchecksum = 0; -int gdbbufferplace = 0; -int gdbbufferstate = 0; - -static inline char ToHEXNibble( int i ) -{ - i &= 0xf; - return ( i < 10 )?('0' + i):('a' - 10 + i); -} - -int fromhex( char c ) -{ - if( c >= '0' && c <= '9' ) c = c - '0'; - else if( c >= 'A' && c <= 'F' ) c = c - 'A' + 10; - else if( c >= 'a' && c <= 'f' ) c = c - 'a' + 10; - else return -1; - return c; -} - -// if (numhex < 0) -int ReadHex( char ** instr, int numhex, uint32_t * outwrite ) -{ - if( !instr ) return -1; - char * str = *instr; - // If negative - error. - // If positive - number of bytes read. - - *outwrite = 0; - - int scanhex = numhex; - if( scanhex < 0 ) - scanhex = strlen( str ); - - int i; - for( i = 0; i < scanhex; i++ ) - { - int v = fromhex( *(str++) ); - if( v < 0 ) - { - if( numhex < 0 ) - { - str--; - *instr = str; - return i; - } - else - { - *instr = str; - return - i - 1; - } - } - (*outwrite) = ((*outwrite) << 4) | v; - } - *instr = str; - return i; -} - -int StringMatch( const char * haystack, const char * mat ) -{ - int i; - for( i = 0; mat[i] && haystack[i]; i++ ) - if( mat[i] != haystack[i] || haystack[i] == 0 ) break; - return mat[i] == 0; -} - -/////////////////////////////////////////////////////////////////////////////// -// General Protocol - - -void SendGDBReply( const void * data, int len, int docs ) -{ - if( len < 0 ) len = strlen( data ); - if( docs ) - { - uint8_t * localbuffer = alloca( len ) + 5; - localbuffer[0] = '$'; - uint8_t checksum = 0; - int i; - for( i = 0; i < len; i++ ) - { - uint8_t v = ((const uint8_t*)data)[i]; - checksum += v; - localbuffer[i+1] = v; - } - localbuffer[len+1] = '#'; - localbuffer[len+2] = ToHEXNibble( checksum >> 4 ); - localbuffer[len+3] = ToHEXNibble( checksum ); - localbuffer[len+4] = 0; - data = (void*)localbuffer; - len += 4; - } - - if( listenMode == 2 ) - { - //printf( ">>>>%s<<<<(%d)\n", data ); - send( serverSocket, data, len, MSG_NOSIGNAL ); - } -} - -void SendReplyFull( const char * replyMessage ) -{ - SendGDBReply( replyMessage, -1, '$' ); -} - -void HandleGDBPacket( void * dev, char * data, int len ) -{ - int i; - - //printf( ":::%s:::\n", data ); - // Got a packet? - if( data[0] != '$' ) return; - - data++; - - char cmd = *(data++); - switch( cmd ) - { - case 'q': - if( StringMatch( data, "Attached" ) ) - SendReplyFull( "1" ); //Attached to an existing process. - else if( StringMatch( data, "Supported" ) ) - SendReplyFull( "PacketSize=f000;qXfer:memory-map:read+" ); - else if( StringMatch( data, "C") ) // Get Current Thread ID. (Can't be -1 or 0. Those are special) - SendReplyFull( "QC1" ); - else if( StringMatch( data, "fThreadInfo" ) ) // Query all active thread IDs (Can't be 0 or 1) - SendReplyFull( "m1" ); - else if( StringMatch( data, "sThreadInfo" ) ) // Query all active thread IDs, continued - SendReplyFull( "l" ); - else if( StringMatch( data, "Xfer:memory-map" ) ) - SendReplyFull( memory_map ); - else - SendReplyFull( "" ); - break; - case 'c': - case 'C': - RVDebugExec( dev, (cmd == 'C')?4:2 ); - SendReplyFull( "OK" ); - break; - case 'D': - // Handle disconnect. - RVHandleDisconnect( dev ); - break; - case 'Z': - case 'z': - { - uint32_t type = 0; - uint32_t addr = 0; - uint32_t time = 0; - if( ReadHex( &data, -1, &type ) < 0 ) goto err; - if( *(data++) != ',' ) goto err; - if( ReadHex( &data, -1, &addr ) < 0 ) goto err; - if( *(data++) != ',' ) goto err; - if( ReadHex( &data, -1, &time ) < 0 ) goto err; - if( RVHandleBreakpoint( dev, cmd == 'Z', addr ) == 0 ) - { - SendReplyFull( "OK" ); - } - else - goto err; - break; - } - case 'm': - { - // Read memory (Binary) - uint32_t address_to_read = 0; - uint32_t length_to_read = 0; - if( ReadHex( &data, -1, &address_to_read ) < 0 ) goto err; - if( *(data++) != ',' ) goto err; - if( ReadHex( &data, -1, &length_to_read ) < 0 ) goto err; - - uint8_t * pl = alloca( length_to_read * 2 ); - if( RVReadMem( dev, address_to_read, pl, length_to_read ) < 0 ) - goto err; - char * repl = alloca( length_to_read * 2 ); - int i; - for( i = 0; i < length_to_read; i++ ) - { - sprintf( repl + i * 2, "%02x", pl[i] ); - } - SendReplyFull( repl ); - break; - } - case 'M': - { - uint32_t address_to_write = 0; - uint32_t length_to_write = 0; - if( ReadHex( &data, -1, &address_to_write ) < 0 ) goto err; - if( *(data++) != ',' ) goto err; - if( ReadHex( &data, -1, &length_to_write ) < 0 ) goto err; - if( *(data++) != ':' ) goto err; - - uint8_t * meml = alloca( length_to_write ); - int i; - for( i = 0; i < length_to_write; i++ ) - { - uint32_t rv; - if( ReadHex( &data, 2, &rv ) < 0 ) goto err; - meml[i] = rv; - } - if( RVWriteRAM( dev, address_to_write, length_to_write, meml ) < 0 ) goto err; - SendReplyFull( "OK" ); - break; - } - case 'X': - { - // Write memory, binary. - uint32_t address_to_write = 0; - uint32_t length_to_write = 0; - if( ReadHex( &data, -1, &address_to_write ) < 0 ) goto err; - if( *(data++) != ',' ) goto err; - if( ReadHex( &data, -1, &length_to_write ) < 0 ) goto err; - if( *(data++) != ':' ) goto err; - if( RVWriteRAM( dev, address_to_write, length_to_write, (uint8_t*)data ) < 0 ) goto err; - SendReplyFull( "OK" ); - break; - } - case 'v': - if( StringMatch( data, "Cont?" ) ) - { - // Request a list of actions supported by the ‘vCont’ packet. - // We don't support vCont - SendReplyFull( "vCont;s;c;t;" ); //no ;s - //SendReplyFull( "" ); - } - else - { - SendReplyFull( "" ); //"vMustReplyEmpty" - } - break; - case 'g': - { - // Register Read (All regs) - char cts[17*8+1]; - for( i = 0; i < 17; i++ ) - { - uint32_t regret; - if( RVReadCPURegister( dev, i, ®ret ) ) goto err; - sprintf( cts + i * 8, "%08x", htonl( regret ) ); - } - SendReplyFull( cts ); - break; - } - case 'p': - { - uint32_t regno; - // Register Read (Specific Reg) - if( ReadHex( &data, 2, ®no ) < 0 ) - SendReplyFull( "E 10" ); - else - { - char cts[9]; - uint32_t regret; - if( RVReadCPURegister( dev, regno, ®ret ) ) goto err; - sprintf( cts, "%08x", htonl( regret ) ); - SendReplyFull( cts ); - } - break; - } - case '?': // Query reason for target halt. - RVSendGDBHaltReason( dev ); - break; - case 'H': - // This is for things like selecting threads. - // I am going to leave this stubbed out. - SendReplyFull( "" ); - break; - default: - printf( "UNKNOWN PACKET: %d (%s)\n", len, data-1 ); - for( i = 0; i < len; i++ ) - { - printf( "%02x ", data[i] ); - } - printf( "\n" ); - goto err; - break; - } - return; -err: - SendReplyFull( "E 00" ); -} - -void HandleClientData( void * dev, const uint8_t * rxdata, int len ) -{ - int pl = 0; - for( pl = 0; pl < len; pl++ ) - { - int c = rxdata[pl]; - if( c == '$' && gdbbufferstate == 0 ) - { - gdbbufferplace = 0; - gdbbufferstate = 1; - } - if( c == 3 && gdbbufferstate == 0 ) - { - RVHandleGDBBreakRequest( dev ); - continue; - } - - switch( gdbbufferstate ) - { - default: - break; - case 1: - if( gdbbufferplace < sizeof( gdbbuffer ) - 2 ) - { - if( c == '}' ) { gdbbufferstate = 9; break; } - gdbbuffer[gdbbufferplace++] = c; - } - if( c == '#' ) gdbbufferstate = 2; - break; - case 9: // escape - gdbbuffer[gdbbufferplace++] = c ^ 0x20; - break; - case 2: - case 3: - { - int i; - c = fromhex( c ); - if( gdbbufferstate == 2 ) - { - gdbchecksum = c << 4; - gdbbufferstate++; - break; - } - if( gdbbufferstate == 3 ) gdbchecksum |= c; - - gdbbuffer[gdbbufferplace] = 0; - - uint8_t checkcsum = 0; - for( i = 1; i < gdbbufferplace - 1; i++ ) - { - checkcsum += ((uint8_t*)gdbbuffer)[i]; - } - if( checkcsum == gdbchecksum ) - { - SendGDBReply( "+", -1, 0 ); - HandleGDBPacket( dev, (char*)gdbbuffer, gdbbufferplace ); - } - else - { - SendGDBReply( "-", -1, 0 ); - } - - gdbbufferplace = 0; - gdbbufferstate = 0; - break; - } - } - } -} - - -void HandleDisconnect( void * dev ) -{ - RVHandleDisconnect( dev ); -} - -/////////////////////////////////////////////////////////////////////////////// -// Network layer. - -static int GDBListen( void * dev ) -{ - struct sockaddr_in sin; - serverSocket = socket(AF_INET, SOCK_STREAM, 0); - - //Make sure the socket worked. - if( serverSocket == -1 ) - { - fprintf( stderr, "Error: Cannot create socket.\n" ); - return -1; - } - - //Disable SO_LINGER (Well, enable it but turn it way down) -#ifdef WIN32 - struct linger lx; - lx.l_onoff = 1; - lx.l_linger = 0; - setsockopt( serverSocket, SOL_SOCKET, SO_LINGER, (const char *)&lx, sizeof( lx ) ); - - //Enable SO_REUSEADDR - int reusevar = 1; - setsockopt(serverSocket, SOL_SOCKET, SO_REUSEADDR, (const char*)&reusevar, sizeof(reusevar)); -#else - struct linger lx; - lx.l_onoff = 1; - lx.l_linger = 0; - setsockopt( serverSocket, SOL_SOCKET, SO_LINGER, &lx, sizeof( lx ) ); - - //Enable SO_REUSEADDR - int reusevar = 1; - setsockopt(serverSocket, SOL_SOCKET, SO_REUSEADDR, &reusevar, sizeof(reusevar)); -#endif - //Setup socket for listening address. - memset( &sin, 0, sizeof( sin ) ); - sin.sin_family = AF_INET; - sin.sin_addr.s_addr = INADDR_ANY; - sin.sin_port = htons( GDBSERVER_PORT ); - - //Actually bind to the socket - if( bind( serverSocket, (struct sockaddr *) &sin, sizeof( sin ) ) == -1 ) - { - fprintf( stderr, "Could not bind to socket: %d\n", GDBSERVER_PORT ); - closesocket( serverSocket ); - serverSocket = 0; - return -1; - } - - //Finally listen. - if( listen( serverSocket, 5 ) == -1 ) - { - fprintf(stderr, "Could not lieten to socket."); - closesocket( serverSocket ); - serverSocket = 0; - return -1; - } - return 0; -} - int PollGDBServer( void * dev ) { - if( !serverSocket ) return -4; - - struct pollfd allpolls[2]; - - int pollct = 1; - allpolls[0].fd = serverSocket; - allpolls[0].events = POLLIN; - - //Do something to watch all currently-waiting sockets. - poll( allpolls, pollct, 0 ); - - //If there's faults, bail. - if( allpolls[0].revents & (POLLERR|POLLHUP) ) - { - closesocket( serverSocket ); - if( listenMode == 1 ) - { - // Some sort of weird fatal close? Is this even possible? - fprintf( stderr, "Error: serverSocke was forcibly closed\n" ); - exit( -4 ); - } - else if( listenMode == 2 ) - { - HandleDisconnect( dev ); - if( serverSocket ) close( serverSocket ); - serverSocket = 0; - listenMode = 1; - GDBListen( dev ); - } - } - if( allpolls[0].revents & POLLIN ) - { - if( listenMode == 1 ) - { - struct sockaddr_in tin; - socklen_t addrlen = sizeof(tin); - memset( &tin, 0, addrlen ); - int tsocket = accept( serverSocket, (struct sockaddr *)&tin, &addrlen ); - closesocket( serverSocket ); - serverSocket = tsocket; - listenMode = 2; - gdbbufferstate = 0; - RVNetConnect( dev ); - // Established. - } - else if( listenMode == 2 ) - { - // Got data from a peer. - uint8_t buffer[16384]; - ssize_t rx = recv( serverSocket, buffer, sizeof( buffer ), MSG_NOSIGNAL ); - if( rx == 0 ) - { - HandleDisconnect( dev ); - close( serverSocket ); - serverSocket = 0; - listenMode = 1; - GDBListen( dev ); - } - else - HandleClientData( dev, buffer, (int)rx ); - } - } - - if( listenMode == 2 ) - { - RVNetPoll( dev ); - } - - return 0; + return MicroGDBPollServer( dev ); } void ExitGDBServer( void * dev ) { - shutdown( serverSocket, SHUT_RDWR ); + MicroGDBExitServer( dev ); } + int SetupGDBServer( void * dev ) { -#ifdef WIN32 -{ - WORD wVersionRequested; - WSADATA wsaData; - int err; - wVersionRequested = MAKEWORD(2, 2); - - err = WSAStartup(wVersionRequested, &wsaData); - if (err != 0) { - /* Tell the user that we could not find a usable */ - /* Winsock DLL. */ - fprintf( stderr, "WSAStartup failed with error: %d\n", err); - return 1; - } -} -#endif - - listenMode = 1; - - return GDBListen( dev ); + return MicroGDBStubStartup( dev ); } -- GitLab