Files
2017-05-02 18:03:08 -07:00

1391 lines
39 KiB
C++

/*
****************************************************************************
* Uzem - The Uzebox Emulator
*
* This file was derived from the Simulavr project by Filipe Rinaldi (2009)
*
* simulavr - A simulator for the Atmel AVR family of microcontrollers.
* Copyright (C) 2001, 2002, 2003 Theodore A. Roth, Klaus Rudolph
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
****************************************************************************
*/
#include <iostream>
#include <algorithm>
using namespace std;
#include <stdio.h>
#include <string.h>
#include <errno.h>
#include <fcntl.h>
#include <signal.h>
#include "gdbserver.h"
#include "avr8.h"
#define avr_new(type, count, flag) ((type *) do_avr_new(((unsigned) sizeof (type) * (count)), flag))
static void *do_avr_new(size_t size, bool blank_it)
{
if (size)
{
void *ptr;
ptr = malloc( size );
if (ptr)
{
if (blank_it == true)
memset(ptr, 0, size);
return ptr;
}
else
{
fprintf(stderr, "malloc failed");
exit(0);
}
}
return NULL;
}
static char *avr_strdup(const char *s)
{
if (s)
{
char *ptr;
ptr = strdup(s);
if (ptr)
return ptr;
fprintf(stderr, "strdup failed" );
exit(0);
}
return NULL;
}
static void avr_free(void *ptr)
{
if (ptr)
free(ptr);
}
GdbServer::GdbServer(avr8 *c, int _port, int debug, int _waitForGdbConnection): core(c), port(_port), global_debug_on(debug), waitForGdbConnection(_waitForGdbConnection) {
last_reply=NULL; //init static var for last_reply()
//is_running=0; //init static var for continue()
block_on=1; //init static var for pre_parse_packet()
conn=-1; //no connection opened
runMode= GDB_RET_NOTHING_RECEIVED;
int i;
#if defined(__WIN32__)
WSADATA wsaData;
if (WSAStartup(MAKEWORD(2, 2), &wsaData)) {
printf("WSAStartup() failed");
exit(1);
}
#endif
if ( (sock = socket( PF_INET, SOCK_STREAM, 0 )) < 0 )
printf( "Can't create socket: %s", strerror(errno) );
/* Let the kernel reuse the socket address. This lets us run
twice in a row, without waiting for the (ip, port) tuple
to time out. */
i = 1;
setsockopt( sock, SOL_SOCKET, SO_REUSEADDR, (char*)&i, sizeof(i) );
#if defined(__WIN32__)
u_long iMode = 1;
ioctlsocket(sock, FIONBIO, &iMode);
#else
fcntl( sock, F_SETFL, fcntl(sock, F_GETFL, 0) | O_NONBLOCK); //dont know
#endif
address->sin_family = AF_INET;
address->sin_port = htons(port);
memset( &address->sin_addr, 0, sizeof(address->sin_addr) );
if ( bind( sock, (struct sockaddr *)address, sizeof(address) ) )
printf( "Can not bind socket: %s", strerror(errno) );
if ( listen(sock, 1) < 0)
{
cerr << "Can not listen on socket: " << strerror(errno) << endl;
}
fprintf( stderr, "Waiting on port %d for gdb client to connect...\n", port );
logFile=fopen("gdb.log","w");
fprintf(logFile,"Opening GDB Session 2\n");
}
GdbServer::~GdbServer() {
CLOSE_SOCK(conn);
CLOSE_SOCK(sock);
fprintf(logFile,"Closing GDB Session\n");
fclose(logFile);
#if defined(__WIN32__)
WSACleanup();
#endif
}
word GdbServer::avr_core_flash_read(int addr) {
return core->progmem[addr];
}
void GdbServer::avr_core_flash_write( unsigned int addr, word val) {
if (addr>= progSize) {
cerr << "try to write in flash after last valid address!" << endl;
exit(0);
}
core->progmem[addr]=val;
}
void GdbServer::avr_core_flash_write_hi8( unsigned int addr, byte val) {
if ((addr*2)>= progSize) {
cerr << "try to write in flash after last valid address!" << endl;
exit(0);
}
u16 tmp = (core->progmem[addr] & 0x00FF) | (val << 8);
core->progmem[addr] = tmp;
}
void GdbServer::avr_core_flash_write_lo8( unsigned int addr, byte val) {
if (addr>=progSize) {
cerr << "try to write in flash after last valid address!" << endl;
exit(0);
}
u16 tmp = (core->progmem[addr] & 0xFF00) | (val);
core->progmem[addr] = tmp;
}
void GdbServer::avr_core_remove_breakpoint(dword pc) {
Breakpoints::iterator ii;
if ((ii= find(BP.begin(), BP.end(), pc)) != BP.end())
BP.erase(ii);
}
void GdbServer::avr_core_insert_breakpoint(dword pc) {
BP.push_back(pc);
}
int GdbServer::signal_has_occurred(int signo) {(void)signo; return 0;}
void GdbServer::signal_watch_start(int signo){(void)signo;}
void GdbServer::signal_watch_stop(int signo){(void)signo;}
static char HEX_DIGIT[] = "0123456789abcdef";
/* Wrap read(2) so we can read a byte without having
to do a shit load of error checking every time. */
int GdbServer::gdb_read_byte( )
{
char c;
int res;
int cnt = MAX_READ_RETRY;
while (cnt--)
{
res = recv( conn, &c, 1, 0 );
#if defined(__WIN32__)
if (res == SOCKET_ERROR)
{
if (WSAGetLastError() == WSAEWOULDBLOCK)
/* fd was set to non-blocking and no data was available */
return -1;
printf( "read failed: %s", strerror(errno) );
}
#else
if (res<0)
{
if (errno == EAGAIN)
/* fd was set to non-blocking and no data was available */
return -1;
printf( "read failed: %s", strerror(errno) );
}
#endif
if (res == 0) {
printf( "gdb closed connection. Exiting...\n" );
exit(0);
}
return c;
}
printf( "Maximum read retries reached\n" );
exit(0);
return 0; /* make compiler happy */
}
/* Convert a hexidecimal digit to a 4 bit nibble. */
int GdbServer::hex2nib( char hex )
{
if ( (hex >= 'A') && (hex <= 'F') )
return (10 + (hex - 'A'));
else if ( (hex >= 'a') && (hex <= 'f') )
return (10 + (hex - 'a'));
else if ( (hex >= '0') && (hex <= '9') )
return (hex - '0');
/* Shouldn't get here unless the developer screwed up ;) */
printf( "Invalid hexidecimal digit: 0x%02x", hex );
return 0; /* make compiler happy */
}
/* Wrapper for write(2) which hides all the repetitive error
checking crap. */
void GdbServer::gdb_write( const void *buf, size_t count )
{
int res;
res = send( conn, (const char*)buf, count, 0 );
/* FIXME: should we try and catch interrupted system calls here? */
if (res < 0)
printf( "write failed: %s", strerror(errno) );
/* FIXME: if this happens a lot, we could try to resend the
unsent bytes. */
if ((unsigned int)res != count)
printf( "write only wrote %d of %ld bytes", res, count );
}
/* Use a single function for storing/getting the last reply message.
If reply is NULL, return pointer to the last reply saved.
Otherwise, make a copy of the buffer pointed to by reply. */
const char* GdbServer::gdb_last_reply( const char *reply )
{
if (reply == NULL)
{
if (last_reply == NULL)
return "";
else
return last_reply;
}
avr_free(last_reply);
last_reply = avr_strdup( reply );
if (last_reply == 0)
{
fprintf(stderr, "Failed to copy string %s\n",reply);
exit(0);
}
return last_reply;
}
/* Acknowledge a packet from GDB */
void GdbServer::gdb_send_ack( )
{
if (global_debug_on)
fprintf( stderr, " Ack -> gdb\n");
gdb_write( "+", 1 );
}
/* Send a reply to GDB. */
void GdbServer::gdb_send_reply( const char *reply )
{
int cksum = 0;
int bytes;
/* Save the reply to last reply so we can resend if need be. */
gdb_last_reply( reply );
if (global_debug_on)
fprintf( stderr, "Sent: $%s#", reply );
if (*reply == '\0')
{
gdb_write( "$#00", 4 );
if (global_debug_on)
fprintf( stderr, "%02x\n", cksum & 0xff );
}
else
{
memset( buf, '\0', sizeof(buf) );
buf[0] = '$';
bytes = 1;
while (*reply)
{
cksum += (unsigned char)*reply;
buf[bytes] = *reply;
bytes++;
reply++;
/* must account for "#cc" to be added */
if (bytes == (MAX_BUF-3))
{
/* FIXME: TRoth 2002/02/18 - splitting reply would be better */
printf( "buffer overflow" );
}
}
if (global_debug_on)
fprintf( stderr, "%02x\n", cksum & 0xff );
buf[bytes++] = '#';
buf[bytes++] = HEX_DIGIT[(cksum >> 4) & 0xf];
buf[bytes++] = HEX_DIGIT[cksum & 0xf];
gdb_write( buf, bytes );
}
}
/* GDB needs the 32 8-bit, gpw registers (r00 - r31), the
8-bit SREG, the 16-bit SP (stack pointer) and the 32-bit PC
(program counter). Thus need to send a reply with
r00, r01, ..., r31, SREG, SPL, SPH, PCL, PCH
Low bytes before High since AVR is little endian. */
void GdbServer::gdb_read_registers( )
{
int i;
dword val; /* ensure it's 32 bit value */
/* (32 gpwr, SREG, SP, PC) * 2 hex bytes + terminator */
size_t buf_sz = (32 + 1 + 2 + 4)*2 + 1;
char *buf;
buf = avr_new( char, buf_sz, true );
/* 32 gen purpose working registers */
for ( i=0; i<32; i++ )
{
val = core->r[i];
buf[i*2] = HEX_DIGIT[(val >> 4) & 0xf];
buf[i*2+1] = HEX_DIGIT[val & 0xf];
}
/* GDB thinks SREG is register number 32 */
val = core->SREG;
buf[i*2] = HEX_DIGIT[(val >> 4) & 0xf];
buf[i*2+1] = HEX_DIGIT[val & 0xf];
i++;
/* GDB thinks SP is register number 33 */
//val = avr_core_mem_read(core, SPL_ADDR);
val=core->SPL;
buf[i*2] = HEX_DIGIT[(val >> 4) & 0xf];
buf[i*2+1] = HEX_DIGIT[val & 0xf];
i++;
//val = avr_core_mem_read(core, SPH_ADDR);
val=core->SPH;
buf[i*2] = HEX_DIGIT[(val >> 4) & 0xf];
buf[i*2+1] = HEX_DIGIT[val & 0xf];
i++;
/* GDB thinks PC is register number 34.
GDB stores PC in a 32 bit value (only uses 23 bits though).
GDB thinks PC is bytes into flash, not words like in simulavr. */
val = core->pc * 2;
buf[i*2] = HEX_DIGIT[(val >> 4) & 0xf];
buf[i*2+1] = HEX_DIGIT[val & 0xf];
val >>= 8;
buf[i*2+2] = HEX_DIGIT[(val >> 4) & 0xf];
buf[i*2+3] = HEX_DIGIT[val & 0xf];
val >>= 8;
buf[i*2+4] = HEX_DIGIT[(val >> 4) & 0xf];
buf[i*2+5] = HEX_DIGIT[val & 0xf];
val >>= 8;
buf[i*2+6] = HEX_DIGIT[(val >> 4) & 0xf];
buf[i*2+7] = HEX_DIGIT[val & 0xf];
gdb_send_reply( buf );
avr_free(buf);
}
/* GDB is sending values to be written to the registers. Registers are the
same and in the same order as described in gdb_read_registers() above. */
void GdbServer::gdb_write_registers( char *pkt )
{
int i;
byte bval;
dword val; /* ensure it's a 32 bit value */
/* 32 gen purpose working registers */
for ( i=0; i<32; i++ )
{
bval = hex2nib(*pkt++) << 4;
bval += hex2nib(*pkt++);
core->r[i]=bval;
}
/* GDB thinks SREG is register number 32 */
bval = hex2nib(*pkt++) << 4;
bval += hex2nib(*pkt++);
core->SREG=bval;
/* GDB thinks SP is register number 33 */
bval = hex2nib(*pkt++) << 4;
bval += hex2nib(*pkt++);
core->SPL = bval;
bval = hex2nib(*pkt++) << 4;
bval += hex2nib(*pkt++);
core->SPH = bval;
/* GDB thinks PC is register number 34.
GDB stores PC in a 32 bit value (only uses 23 bits though).
GDB thinks PC is bytes into flash, not words like in simulavr.
Must cast to dword so as not to get mysterious truncation. */
val = ((dword)hex2nib(*pkt++)) << 4;
val += ((dword)hex2nib(*pkt++));
val += ((dword)hex2nib(*pkt++)) << 12;
val += ((dword)hex2nib(*pkt++)) << 8;
val += ((dword)hex2nib(*pkt++)) << 20;
val += ((dword)hex2nib(*pkt++)) << 16;
val += ((dword)hex2nib(*pkt++)) << 28;
val += ((dword)hex2nib(*pkt++)) << 24;
core->pc=val/2;
gdb_send_reply( "OK" );
}
/* Extract a hexidecimal number from the pkt. Keep scanning pkt until stop char
is reached or size of int is exceeded or a NULL is reached. pkt is modified
to point to stop char when done.
Use this function to extract a num with an arbitrary num of hex
digits. This should _not_ be used to extract n digits from a m len string
of digits (n <= m). */
int GdbServer::gdb_extract_hex_num( char **pkt, char stop )
{
int i = 0;
int num = 0;
char *p = *pkt;
int max_shifts = sizeof(int)*2-1; /* max number of nibbles to shift through */
while ( (*p != stop) && (*p != '\0') )
{
if (i > max_shifts)
printf( "number too large" );
num = (num << 4) | hex2nib(*p);
i++;
p++;
}
*pkt = p;
return num;
}
/* Read a single register. Packet form: 'pn' where n is a hex number with no
zero padding. */
void GdbServer::gdb_read_register( char *pkt )
{
int reg;
char reply[MAX_BUF];
memset(reply, '\0', sizeof(reply));
reg = gdb_extract_hex_num(&pkt, '\0');
if ( (reg >= 0) && (reg < 32) )
{ /* general regs */
byte val = core->r[reg];
snprintf( reply, sizeof(reply)-1, "%02x", val );
}
else if (reg == 32) /* sreg */
{
byte val = core->SREG;
snprintf( reply, sizeof(reply)-1, "%02x", val );
}
else if (reg == 33) /* SP */
{
byte spl, sph;
//spl = avr_core_mem_read( core, SPL_ADDR );
//sph = avr_core_mem_read( core, SPH_ADDR );
spl=core->SPL;
sph=core->SPH;
snprintf( reply, sizeof(reply)-1, "%02x%02x", spl, sph );
}
else if (reg == 34) /* PC */
{
dword val = core->pc * 2;
snprintf( reply, sizeof(reply)-1,
"%02x%02x" "%02x%02x",
val & 0xff, (val >> 8) & 0xff,
(val >> 16) & 0xff, (val >> 24) & 0xff );
}
else
{
printf( "Bad register value: %d\n", reg );
gdb_send_reply( "E00" );
return;
}
gdb_send_reply( reply );
}
/* Write a single register. Packet form: 'Pn=r' where n is a hex number with
no zero padding and r is two hex digits for each byte in register (target
byte order). */
void GdbServer::gdb_write_register( char *pkt )
{
int reg;
int val, hval;
dword dval;
reg = gdb_extract_hex_num(&pkt, '=');
pkt++; /* skip over '=' character */
/* extract the low byte of value from pkt */
val = hex2nib(*pkt++) << 4;
val += hex2nib(*pkt++);
if ( (reg >= 0) && (reg < 33) )
{
/* r0 to r31 and SREG */
if (reg == 32) /* gdb thinks SREG is register 32 */
{
core->SREG=val&0xff;
}
else
{
core->r[reg]=val&0xff;
}
}
else if (reg == 33)
{
/* SP is 2 bytes long so extract upper byte */
hval = hex2nib(*pkt++) << 4;
hval += hex2nib(*pkt++);
//avr_core_mem_write( core, SPL_ADDR, val & 0xff );
//avr_core_mem_write( core, SPH_ADDR, hval & 0xff );
core->SPL = (val&0xff);
core->SPH = (hval&0xff);
}
else if (reg == 34)
{
/* GDB thinks PC is register number 34.
GDB stores PC in a 32 bit value (only uses 23 bits though).
GDB thinks PC is bytes into flash, not words like in simulavr.
Must cast to dword so as not to get mysterious truncation. */
dval = (dword)val; /* we already read the first two nibbles */
dval += ((dword)hex2nib(*pkt++)) << 12;
dval += ((dword)hex2nib(*pkt++)) << 8;
dval += ((dword)hex2nib(*pkt++)) << 20;
dval += ((dword)hex2nib(*pkt++)) << 16;
dval += ((dword)hex2nib(*pkt++)) << 28;
dval += ((dword)hex2nib(*pkt++)) << 24;
core->pc=dval / 2;
}
else
{
printf( "Bad register value: %d\n", reg );
gdb_send_reply( "E00" );
return;
}
gdb_send_reply( "OK" );
}
/* Parse the pkt string for the addr and length.
a_end is first char after addr.
l_end is first char after len.
Returns number of characters to advance pkt. */
int GdbServer::gdb_get_addr_len( char *pkt, char a_end, char l_end, unsigned int *addr, int *len )
{
char *orig_pkt = pkt;
*addr = 0;
*len = 0;
/* Get the addr from the packet */
while (*pkt != a_end)
*addr = (*addr << 4) + hex2nib(*pkt++);
pkt++; /* skip over a_end */
/* Get the length from the packet */
while (*pkt != l_end)
*len = (*len << 4) + hex2nib(*pkt++);
pkt++; /* skip over l_end */
/* fprintf( stderr, "+++++++++++++ addr = 0x%08x\n", *addr ); */
/* fprintf( stderr, "+++++++++++++ len = %d\n", *len ); */
return (pkt - orig_pkt);
}
void GdbServer::gdb_read_memory( char *pkt )
{
unsigned int addr = 0;
int len = 0;
byte *buf;
byte bval;
word wval;
int i;
int is_odd_addr;
pkt += gdb_get_addr_len( pkt, ',', '\0', &addr, &len );
buf = avr_new( byte, (len*2)+1, true );
//if(addr>=0x804000) addr&=0xffff;
fprintf(logFile,"%x:",addr);
if ( (addr & MEM_SPACE_MASK) == EEPROM_OFFSET )
{
/* addressing eeprom */
addr = addr & ~MEM_SPACE_MASK; /* remove the offset bits */
//printf( "reading of eeprom not yet implemented: 0x%x.\n", addr );
//snprintf( (char*)buf, len*2, "E%02x", EIO );
for ( i=0; i<len; i++ )
{
bval = core->eeprom[addr+i];
buf[i*2] = HEX_DIGIT[bval >> 4];
buf[i*2+1] = HEX_DIGIT[bval & 0xf];
}
}
else if ( (addr & MEM_SPACE_MASK) == SRAM_OFFSET )
{
/* addressing sram */
addr = addr & ~MEM_SPACE_MASK; /* remove the offset bits */
if (addr >= (SRAMBASE+sramSize))
{
fprintf(logFile,"invalid ram");
if (global_debug_on)
printf("Sram address:%x invalid\n",addr);
snprintf( (char*)buf, len*2, "E%02x", EIO );
}
else
{
// Trying to access one of the registers (below IOBASE)
if (addr < IOBASE)
{
bval = core->r[addr];
buf[0] = HEX_DIGIT[bval >> 4];
buf[1] = HEX_DIGIT[bval & 0xf];
}
// Trying to access one of the IOs (below SRAMBASE)
else if (addr < SRAMBASE )
{
bval = core->io[addr - IOBASE];
buf[0] = HEX_DIGIT[bval >> 4];
buf[1] = HEX_DIGIT[bval & 0xf];
}
else
// Trying to access the generic SRAM memory
{
// Uzem has an array only for the generic SRAM, so, we need calculate the correct address
addr -= SRAMBASE;
for ( i=0; i<len; i++ )
{
if (global_debug_on)
printf("Reading sram address:%x\n",addr+i);
bval = core->sram[addr+i];
buf[i*2] = HEX_DIGIT[bval >> 4];
buf[i*2+1] = HEX_DIGIT[bval & 0xf];
}
}
}
}
else if ( (addr & MEM_SPACE_MASK) == FLASH_OFFSET )
{
/* addressing flash */
addr = addr & ~MEM_SPACE_MASK; /* remove the offset bits */
is_odd_addr = addr % 2;
i = 0;
if (is_odd_addr)
{
bval = avr_core_flash_read( addr/2 ) >> 8;
buf[i++] = HEX_DIGIT[bval >> 4];
buf[i++] = HEX_DIGIT[bval & 0xf];
addr++;
len--;
}
while (len > 1)
{
wval = avr_core_flash_read( addr/2 );
bval = wval & 0xff;
buf[i++] = HEX_DIGIT[bval >> 4];
buf[i++] = HEX_DIGIT[bval & 0xf];
bval = wval >> 8;
buf[i++] = HEX_DIGIT[bval >> 4];
buf[i++] = HEX_DIGIT[bval & 0xf];
len -= 2;
addr += 2;
}
if (len == 1)
{
bval = avr_core_flash_read( addr/2 ) & 0xff;
buf[i++] = HEX_DIGIT[bval >> 4];
buf[i++] = HEX_DIGIT[bval & 0xf];
}
}
else
{
/* gdb asked for memory space which doesn't exist */
printf( "Invalid memory address: 0x%x.\n", addr );
fprintf(logFile,"Invalid memory address: 0x%x.\n", addr);
snprintf( (char*)buf, len*2, "E%02x", EIO );
}
gdb_send_reply( (char*)buf );
avr_free(buf);
}
void GdbServer::gdb_write_memory( char *pkt )
{
unsigned int addr = 0;
int len = 0;
byte bval;
word wval;
int is_odd_addr;
unsigned int i;
char reply[10];
/* Set the default reply. */
strncpy( reply, "OK", sizeof(reply) );
pkt += gdb_get_addr_len( pkt, ',', ':', &addr, &len );
if ( (addr & MEM_SPACE_MASK) == EEPROM_OFFSET )
{
/* addressing eeprom */
addr = addr & ~MEM_SPACE_MASK; /* remove the offset bits */
// printf( "writing of eeprom not yet implemented: 0x%x.\n", addr );
// snprintf( reply, sizeof(reply), "E%02x", EIO );
while (len>0) {
bval = hex2nib(*pkt++) << 4;
bval += hex2nib(*pkt++);
len--;
core->eeprom[addr] = bval;
addr++;
}
}
else if ( (addr & MEM_SPACE_MASK) == SRAM_OFFSET )
{
/* addressing sram */
addr = addr & ~MEM_SPACE_MASK; /* remove the offset bits */
if (addr >= (SRAMBASE+sramSize))
{
if (global_debug_on)
printf("Sram address:%x invalid\n",addr);
snprintf( (char*)buf, len*2, "E%02x", EIO );
}
// Trying to access one of the registers (below IOBASE)
else if (addr < IOBASE)
{
bval = hex2nib(*pkt++) << 4;
bval += hex2nib(*pkt++);
core->r[addr]=bval;
}
// Trying to access one of the IOs (below SRAMBASE)
else if (addr < SRAMBASE )
{
addr -= IOBASE;
bval = hex2nib(*pkt++) << 4;
bval += hex2nib(*pkt++);
core->io[addr]=bval;
}
else
// Trying to access the generic SRAM memory
{
// Uzem has an array only for the generic SRAM, so, we need calculate the correct address
addr -= SRAMBASE;
for ( i=addr; i < addr+len; i++ )
{
bval = hex2nib(*pkt++) << 4;
bval += hex2nib(*pkt++);
core->sram[i]=bval;
}
}
}
else if ( (addr & MEM_SPACE_MASK) == FLASH_OFFSET )
{
/* addressing flash */
addr = addr & ~MEM_SPACE_MASK; /* remove the offset bits */
is_odd_addr = addr % 2;
if (is_odd_addr)
{
bval = hex2nib(*pkt++) << 4;
bval += hex2nib(*pkt++);
avr_core_flash_write_hi8(addr/2, bval);
len--;
addr++;
}
while (len > 1)
{
wval = hex2nib(*pkt++) << 4; /* low byte first */
wval += hex2nib(*pkt++);
wval += hex2nib(*pkt++) << 12; /* high byte last */
wval += hex2nib(*pkt++) << 8;
avr_core_flash_write( addr/2, wval);
len -= 2;
addr += 2;
}
if ( len == 1 )
{
/* one more byte to write */
bval = hex2nib(*pkt++) << 4;
bval += hex2nib(*pkt++);
avr_core_flash_write_lo8( addr/2, bval );
}
}
else
{
/* gdb asked for memory space which doesn't exist */
printf( "Invalid memory address: 0x%x.\n", addr );
snprintf( reply, sizeof(reply), "E%02x", EIO );
}
gdb_send_reply( reply );
}
/* Format of breakpoint commands (both insert and remove):
"z<t>,<addr>,<length>" - remove break/watch point
"Z<t>,<add>r,<length>" - insert break/watch point
In both cases t can be the following:
t = '0' - software breakpoint
t = '1' - hardware breakpoint
t = '2' - write watch point
t = '3' - read watch point
t = '4' - access watch point
addr is address.
length is in bytes
For a software breakpoint, length specifies the size of the instruction to
be patched. For hardware breakpoints and watchpoints, length specifies the
memory region to be monitored. To avoid potential problems, the operations
should be implemented in an idempotent way. -- GDB 5.0 manual. */
void GdbServer::gdb_break_point( char *pkt )
{
unsigned int addr = 0;
int len = 0;
char z = *(pkt-1); /* get char parser already looked at */
char t = *pkt++;
pkt++; /* skip over first ',' */
//cout << "###############################################" << endl;
gdb_get_addr_len( pkt, ',', '\0', &addr, &len );
switch (t) {
case '0': /* software breakpoint */
/* addr/2 since addr refers to PC */
if ( addr >= progSize )
{
printf( "Attempt to set break at invalid addr\n" );
gdb_send_reply( "E01" );
return;
}
if (z == 'z')
{
//cout << "Try to UNSET a software breakpoint" << endl;
//cout << "at address :" << addr << " with len " << len << endl;
avr_core_remove_breakpoint( addr/2 );
}
else
{
//cout << "Try to SET a software breakpoint" << endl;
//cout << "at address :" << addr << " with len " << len << endl;
avr_core_insert_breakpoint( addr/2 );
}
break;
case '1': /* hardware breakpoint */
//cout << "Try to set a hardware breakpoint" << endl;
//cout << "at address :" << addr << " with len " << len << endl;
gdb_send_reply( "" );
return;
break;
case '2': /* write watchpoint */
//cout << "Try to set a watchpoint" << endl;
//cout << "at address :" << addr << " with len " << len << endl;
gdb_send_reply( "" );
return;
break;
case '3': /* read watchpoint */
//cout << "Try to set a read watchpoint" << endl;
//cout << "at address :" << addr << " with len " << len << endl;
gdb_send_reply( "" );
return;
break;
case '4': /* access watchpoint */
//cout << "try to set a access watchpoint" << endl;
//cout << "at address :" << addr << " with len " << len << endl;
gdb_send_reply( "" );
return; /* unsupported yet */
}
gdb_send_reply( "OK" );
}
/* Continue command format: "c<addr>" or "s<addr>"
If addr is given, resume at that address, otherwise, resume at current
address. */
/* Continue with signal command format: "C<sig>;<addr>" or "S<sig>;<addr>"
"<sig>" should always be 2 hex digits, possibly zero padded.
";<addr>" part is optional.
If addr is given, resume at that address, otherwise, resume at current
address. */
int GdbServer::gdb_get_signal( char *pkt )
{
int signo;
//char step = *(pkt-1);
/* strip out the signal part of the packet */
signo = (hex2nib( *pkt++ ) << 4);
signo += (hex2nib( *pkt++ ) & 0xf);
if (global_debug_on)
fprintf( stderr, "GDB sent signal: %d\n", signo );
/* Process signals send via remote protocol from gdb. Signals really don't
make sense to the program running in the simulator, so we are using
them sort of as an 'out of band' data. */
switch (signo)
{
case SIGHUP:
/* Gdb user issuing the 'signal SIGHUP' command tells sim to reset
itself. We reply with a SIGTRAP the same as we do when gdb
makes first connection with simulator. */
//TODO: Create a reset function for avr8
//core->reset( );
printf("Reset not yet supported, aborting...");
//gdb_send_reply( "S05" );
core->shutdown(0);
}
return signo;
}
/* Parse the packet. Assumes that packet is null terminated.
Return GDB_RET_KILL_REQUEST if packet is 'kill' command,
GDB_RET_OK otherwise. */
int GdbServer::gdb_parse_packet( char *pkt )
{
fprintf(logFile,"\n%s:",pkt);
switch (*pkt++) {
case '?': /* last signal */
gdb_send_reply( "S05" ); /* signal # 5 is SIGTRAP */
break;
case 'g': /* read registers */
gdb_read_registers( );
break;
case 'G': /* write registers */
gdb_write_registers( pkt );
break;
case 'p': /* read a single register */
gdb_read_register( pkt );
break;
case 'P': /* write single register */
gdb_write_register( pkt );
break;
case 'm': /* read memory */
gdb_read_memory( pkt );
break;
case 'M': /* write memory */
gdb_write_memory( pkt );
break;
case 'D': /* detach the debugger */
case 'k': /* kill request */
/* Reset the simulator since there may be another connection
before the simulator stops running. */
gdb_send_reply( "OK" );
return GDB_RET_KILL_REQUEST;
case 'c': /* continue */
return GDB_RET_CONTINUE;
break;
case 'C': /* continue with signal */
if(SIGHUP==gdb_get_signal(pkt)) { //very special solution only for regression testing woth
//old scripts from old simulavr! Continue means not continue
//if signal is SIGHUP :-(, so we do nothing then!
return GDB_RET_OK;
}
return GDB_RET_CONTINUE;
break;
case 's': /* step */
return GDB_RET_SINGLE_STEP;
break;
case 'S': /* step with signal */
gdb_get_signal(pkt);
return GDB_RET_SINGLE_STEP;
break;
case 'z': /* remove break/watch point */
case 'Z': /* insert break/watch point */
gdb_break_point( pkt );
break;
case 'q': /* query requests */
gdb_send_reply( "" );
break;
default:
gdb_send_reply( "" );
}
return GDB_RET_OK;
}
void GdbServer::gdb_set_blocking_mode( int mode )
{
#if defined(__WIN32__)
u_long imode = mode;
if (ioctlsocket(sock, FIONBIO, &imode))
printf( "fcntl failed: %s\n", strerror(errno) );
#else
if (mode)
{
/* turn non-blocking mode off */
if (fcntl( conn, F_SETFL, fcntl(conn, F_GETFL, 0) & ~O_NONBLOCK) < 0)
printf( "fcntl failed: %s\n", strerror(errno) );
}
else
{
/* turn non-blocking mode on */
if (fcntl( conn, F_SETFL, fcntl(conn, F_GETFL, 0) | O_NONBLOCK) < 0)
printf( "fcntl failed: %s\n", strerror(errno) );
}
#endif
}
/* Perform pre-packet parsing. This will handle messages from gdb which are
outside the realm of packets or prepare a packet for parsing.
Use the static block_on flag to reduce the over head of turning blocking on
and off every time this function is called. */
int GdbServer::gdb_pre_parse_packet( int blocking )
{
int i, res;
int c;
char pkt_buf[MAX_BUF+1];
int cksum, pkt_cksum;
gdb_set_blocking_mode( blocking);
c = gdb_read_byte( );
switch (c) {
case '$': /* read a packet */
/* insure that packet is null terminated. */
memset( pkt_buf, 0, sizeof(pkt_buf) );
/* make sure we block on fd */
gdb_set_blocking_mode( GDB_BLOCKING_ON );
pkt_cksum = i = 0;
c = gdb_read_byte();
while ( (c != '#') && (i < MAX_BUF) )
{
pkt_buf[i++] = c;
pkt_cksum += (unsigned char)c;
c = gdb_read_byte();
}
cksum = hex2nib( gdb_read_byte() ) << 4;
cksum |= hex2nib( gdb_read_byte() );
/* FIXME: Should send "-" (Nak) instead of aborting when we get
checksum errors. Leave this as an error until it is actually
seen (I've yet to see a bad checksum - TRoth). It's not a simple
matter of sending (Nak) since you don't want to get into an
infinite loop of (bad cksum, nak, resend, repeat).*/
if ( (pkt_cksum & 0xff) != cksum )
fprintf( stderr, "Bad checksum: sent 0x%x <--> computed 0x%x",
cksum, pkt_cksum );
if (global_debug_on)
fprintf( stderr, "Recv: \"$%s#%02x\"\n", pkt_buf, cksum );
/* always acknowledge a well formed packet immediately */
gdb_send_ack( );
res = gdb_parse_packet( pkt_buf );
if (res < 0 )
return res;
break;
case '-':
if (global_debug_on)
fprintf( stderr, " gdb -> Nak\n" );
gdb_send_reply( gdb_last_reply(NULL) );
break;
case '+':
if (global_debug_on)
fprintf( stderr, " gdb -> Ack\n" );
break;
case 0x03:
/* Gdb sends this when the user hits C-c. This is gdb's way of
telling the simulator to interrupt what it is doing and return
control back to gdb. */
return GDB_RET_CTRL_C;
case -1:
/* fd is non-blocking and no data to read */
return GDB_RET_NOTHING_RECEIVED;
break;
default:
printf( "Unknown request from gdb: %c (0x%02x)\n", c, c );
}
return GDB_RET_OK;
}
/* try to open a new connection to gdb */
bool GdbServer::TryConnectGdb() {
int i;
/* accept() needs this set, or it fails (sometimes) */
addrLength[0] = sizeof(struct sockaddr_in);
/* We only want to accept a single connection, thus don't need a loop. */
/* Wait until we have a connection */
conn = accept( sock, (struct sockaddr *)address, addrLength );
#if defined(__WIN32__)
if (conn != INVALID_SOCKET) {
#else
if (conn>0) {
#endif
/* Tell TCP not to delay small packets. This greatly speeds up
interactive response. WARNING: If TCP_NODELAY is set on, then gdb
may timeout in mid-packet if the (gdb)packet is not sent within a
single (tcp)packet, thus all outgoing (gdb)packets _must_ be sent
with a single call to write. (see Stevens "Unix Network
Programming", Vol 1, 2nd Ed, page 202 for more info) */
i = 1;
setsockopt (conn, IPPROTO_TCP, TCP_NODELAY, (char*)&i, sizeof (i));
core->state = CPU_SINGLE_STEP;
printf("Connection opened!\n");
return true;
}
// Check SDL events, may be the user wants close the application
core->idle();
return false;
}
void GdbServer::exec(void) {
static int wait = 0;
char reply[MAX_BUF+1];
bool leave = false;
if ((conn<0) && (TryConnectGdb() == false))
return;
// If we check for gdb packets after eachinstruction, it takes much time.
// So, if the user sends a 'continue', try to execute a bunch of instructions before check gdb again.
if (wait)
{
wait--;
return;
}
if (core->gdbBreakpointFound == true)
{
if (global_debug_on)
fprintf( stderr, "Run on breakpoint\n");
core->gdbBreakpointFound = false;
runMode=GDB_RET_OK; //we will stop next call from GdbServer::Step
SendPosition(SIGTRAP);
}
if (core->gdbInvalidOpcode == true)
{
snprintf( reply, MAX_BUF, "S%02x", SIGILL );
gdb_send_reply( reply );
runMode=GDB_RET_OK;
SendPosition(SIGILL);
}
if (runMode==GDB_RET_SINGLE_STEP) {
runMode=GDB_RET_OK;
SendPosition(SIGTRAP);
}
do {
int gdbRet=gdb_pre_parse_packet(GDB_BLOCKING_OFF);
switch (gdbRet) {
case GDB_RET_NOTHING_RECEIVED: //nothing changes here
break;
case GDB_RET_OK: //dont change any modes
runMode=GDB_RET_OK;
break;
case GDB_RET_CONTINUE:
runMode=GDB_RET_CONTINUE; //lets continue until we receive something from gdb (normal CTRL-C)
break; //or we run into a break point or illegal instruction
case GDB_RET_SINGLE_STEP:
runMode=GDB_RET_SINGLE_STEP;
break;
case GDB_RET_CTRL_C:
gdb_debug("Ctr+C issued. PC:0x%04x\n",core->pc*2);
runMode=GDB_RET_CTRL_C;
SendPosition(SIGINT); //Give gdb an idea where the core is now
break;
case GDB_RET_KILL_REQUEST:
printf("Killed from the client...bye!\n");
core->shutdown(0);
break;
}
if ((runMode == GDB_RET_SINGLE_STEP ) || ( runMode == GDB_RET_CONTINUE))
leave=true;
else{
// Idle, how about check the SDL events?
core->idle();
}
} while (leave==false);
// Reload wait counter
if (runMode == GDB_RET_CONTINUE)
wait = 100000000;
}
void GdbServer::SendPosition(int signo) {
/* Send gdb PC, FP, SP */
int bytes = 0;
char reply[MAX_BUF+1];
int pc = core->pc * 2;
gdb_debug("Sending position [signo:%i]\n",signo);
bytes = snprintf( reply, MAX_BUF, "T%02x", signo );
/* SREG, SP & PC */
snprintf( reply+bytes, MAX_BUF-bytes,
"20:%02x;" "21:%02x%02x;" "22:%02x%02x%02x%02x;",
((int)(core->SREG)),
core->SPL, core->SPH,
pc & 0xff, (pc >> 8) & 0xff, (pc >> 16) & 0xff, (pc >> 24) & 0xff );
gdb_send_reply(reply);
}