Implemented GDB client driver.

This commit is contained in:
Daniel Beer 2011-06-06 15:47:52 +12:00
parent f3a8df82f9
commit 57d5ea34d5
9 changed files with 513 additions and 17 deletions

View File

@ -65,7 +65,7 @@ mspdebug: main.o fet.o rf2500.o dis.o uif.o olimex.o ihex.o elf32.o stab.o \
fet_db.o usbutil.o titext.o srec.o device.o coff.o opdb.o output.o \
cmddb.o stdcmd.o prog.o flash_bsl.o list.o simio.o simio_tracer.o \
simio_timer.o simio_wdt.o simio_hwmult.o simio_gpio.o aliasdb.o \
gdb_proto.o
gdb_proto.o gdbc.o
$(CC) $(LDFLAGS) $(PORTS_LDFLAGS) -o $@ $^ -lusb $(READLINE_LIBS)
.c.o:

2
gdb.c
View File

@ -288,7 +288,7 @@ static int run(struct gdb_data *data, char *buf)
if (status == DEVICE_STATUS_INTR)
goto out;
while (gdb_peek(data)) {
while (gdb_peek(data, 0)) {
int c = gdb_getc(data);
if (c < 0)

View File

@ -51,20 +51,20 @@ void gdb_printf(struct gdb_data *data, const char *fmt, ...)
data->outlen += len;
}
static int gdb_read(struct gdb_data *data, int blocking)
static int gdb_read(struct gdb_data *data, int timeout_ms)
{
fd_set r;
int len;
struct timeval to = {
.tv_sec = 0,
.tv_usec = 0
.tv_sec = timeout_ms / 1000,
.tv_usec = timeout_ms % 1000
};
FD_ZERO(&r);
FD_SET(data->sock, &r);
if (select(data->sock + 1, &r, NULL, NULL,
blocking ? NULL : &to) < 0) {
timeout_ms < 0 ? NULL : &to) < 0) {
pr_error("gdb: select");
return -1;
}
@ -90,9 +90,9 @@ static int gdb_read(struct gdb_data *data, int blocking)
return len;
}
int gdb_peek(struct gdb_data *data)
int gdb_peek(struct gdb_data *data, int timeout_ms)
{
if (data->head == data->tail && gdb_read(data, 0) < 0)
if (data->head == data->tail && gdb_read(data, timeout_ms) < 0)
return -1;
return data->head != data->tail;
@ -103,7 +103,7 @@ int gdb_getc(struct gdb_data *data)
int c;
/* If the buffer is empty, receive some more data */
if (data->head == data->tail && gdb_read(data, 1) < 0)
if (data->head == data->tail && gdb_read(data, -1) < 0)
return -1;
c = data->xbuf[data->head];
@ -128,20 +128,21 @@ int gdb_flush_ack(struct gdb_data *data)
{
int c;
do {
data->outbuf[data->outlen] = 0;
#ifdef DEBUG_GDB
printc("-> %s\n", data->outbuf);
printc("-> %s\n", data->outbuf);
#endif
data->outbuf[data->outlen] = 0;
do {
if (send(data->sock, data->outbuf, data->outlen, 0) < 0) {
data->error = errno;
pr_error("gdb: flush_ack");
return -1;
}
c = gdb_getc(data);
if (c < 0)
return -1;
do {
c = gdb_getc(data);
} while (c != '+' && c != '-');
} while (c != '+');
data->outlen = 0;

View File

@ -39,7 +39,7 @@ void gdb_printf(struct gdb_data *data, const char *fmt, ...);
int gdb_send(struct gdb_data *data, const char *msg);
void gdb_packet_start(struct gdb_data *data);
void gdb_packet_end(struct gdb_data *data);
int gdb_peek(struct gdb_data *data);
int gdb_peek(struct gdb_data *data, int timeout_ms);
int gdb_getc(struct gdb_data *data);
int gdb_flush_ack(struct gdb_data *data);
int gdb_read_packet(struct gdb_data *data, char *buf);

446
gdbc.c Normal file
View File

@ -0,0 +1,446 @@
/* MSPDebug - debugging tool for MSP430 MCUs
* Copyright (C) 2009-2011 Daniel Beer
*
* 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <netdb.h>
#include <unistd.h>
#include "output.h"
#include "gdbc.h"
#include "gdb_proto.h"
#include "opdb.h"
struct gdb_client {
struct device base;
struct gdb_data gdb;
int is_running;
struct device_breakpoint last_bps[DEVICE_MAX_BREAKPOINTS];
};
static int get_xfer_size(void)
{
int x = opdb_get_numeric("gdbc_xfer_size");
if (x < 2)
return 2;
if (x > GDB_MAX_XFER)
return GDB_MAX_XFER;
return x;
}
static int check_ok(struct gdb_data *gdb)
{
char buf[GDB_BUF_SIZE];
int len;
len = gdb_read_packet(gdb, buf);
if (len < 0)
return -1;
if (len < 1 || buf[0] == 'E') {
printc_err("gdbc: bad response: %s\n", buf);
return -1;
}
return 0;
}
static void gdbc_destroy(device_t dev_base)
{
struct gdb_client *c = (struct gdb_client *)dev_base;
shutdown(c->gdb.sock, 2);
close(c->gdb.sock);
free(c);
}
static int gdbc_readmem(device_t dev_base, address_t addr,
uint8_t *mem, address_t len)
{
struct gdb_client *dev = (struct gdb_client *)dev_base;
int xfer_size = get_xfer_size();
char buf[GDB_BUF_SIZE];
while (len) {
int plen = len > xfer_size ? xfer_size : len;
int r;
int i;
gdb_packet_start(&dev->gdb);
gdb_printf(&dev->gdb, "m%04x,%x", addr, plen);
gdb_packet_end(&dev->gdb);
if (gdb_flush_ack(&dev->gdb) < 0)
return -1;
r = gdb_read_packet(&dev->gdb, buf);
if (r < 0)
return -1;
if (r < plen * 2) {
printc_err("gdbc: short read at 0x%04x: expected %d "
"bytes, got %d\n", addr, plen, r / 2);
return -1;
}
for (i = 0; i * 2 < r; i++)
mem[i] = (hexval(buf[i * 2]) << 4) |
hexval(buf[i * 2 + 1]);
mem += plen;
len -= plen;
addr += plen;
}
return 0;
}
static int gdbc_writemem(device_t dev_base, address_t addr,
const uint8_t *mem, address_t len)
{
struct gdb_client *dev = (struct gdb_client *)dev_base;
int xfer_size = get_xfer_size();
while (len) {
int plen = len > xfer_size ? xfer_size : len;
int i;
gdb_packet_start(&dev->gdb);
gdb_printf(&dev->gdb, "M%04x,%x:", addr, plen);
for (i = 0; i < plen; i++)
gdb_printf(&dev->gdb, "%02x", mem[i]);
gdb_packet_end(&dev->gdb);
if (gdb_flush_ack(&dev->gdb) < 0)
return -1;
if (check_ok(&dev->gdb) < 0)
return -1;
mem += plen;
len -= plen;
addr += plen;
}
return 0;
}
static int gdbc_getregs(device_t dev_base, address_t *regs)
{
struct gdb_client *dev = (struct gdb_client *)dev_base;
char buf[GDB_BUF_SIZE];
int len;
int i;
if (gdb_send(&dev->gdb, "g") < 0)
return -1;
len = gdb_read_packet(&dev->gdb, buf);
if (len < 0)
return -1;
if (len < DEVICE_NUM_REGS * 4) {
printc_err("gdbc: short read: expected %d chars, got %d\n",
DEVICE_NUM_REGS * 4, len);
return -1;
}
for (i = 0; i < DEVICE_NUM_REGS; i++) {
char *text = buf + i * 4;
regs[i] = (hexval(text[0]) << 4) | (hexval(text[1])) |
(hexval(text[2]) << 12) | (hexval(text[3]) << 8);
}
return 0;
}
static int gdbc_setregs(device_t dev_base, const address_t *regs)
{
struct gdb_client *dev = (struct gdb_client *)dev_base;
int i;
gdb_packet_start(&dev->gdb);
gdb_printf(&dev->gdb, "G");
for (i = 0; i < DEVICE_NUM_REGS; i++)
gdb_printf(&dev->gdb, "%02x%02x",
regs[i] & 0xff, (regs[i] >> 8) & 0xff);
gdb_packet_end(&dev->gdb);
if (gdb_flush_ack(&dev->gdb) < 0)
return -1;
return check_ok(&dev->gdb);
}
static int do_reset(struct gdb_client *dev)
{
char buf[GDB_BUF_SIZE];
int len;
if (gdb_send(&dev->gdb, "R00") < 0)
return -1;
len = gdb_read_packet(&dev->gdb, buf);
if (!len) {
if (gdb_send(&dev->gdb, "r") < 0)
return -1;
len = gdb_read_packet(&dev->gdb, buf);
}
if (len < 0)
return -1;
if (len < 2 || buf[0] != 'O' || buf[1] != 'K') {
printc_err("gdbc: reset: bad response: %s\n", buf);
return -1;
}
return 0;
}
static int bp_send(struct gdb_data *gdb, int c, address_t addr)
{
gdb_packet_start(gdb);
gdb_printf(gdb, "%c1,%04x,2", c, addr);
gdb_packet_end(gdb);
if (gdb_flush_ack(gdb) < 0)
return -1;
return check_ok(gdb);
}
static int refresh_bps(struct gdb_client *dev)
{
int i;
for (i = 0; i < dev->base.max_breakpoints; i++) {
struct device_breakpoint *bp = &dev->base.breakpoints[i];
struct device_breakpoint *old = &dev->last_bps[i];
if (!(bp->flags & DEVICE_BP_DIRTY))
continue;
if ((old->flags & DEVICE_BP_ENABLED) &&
(bp_send(&dev->gdb, 'z', old->addr) < 0))
return -1;
if ((bp->flags & DEVICE_BP_ENABLED) &&
(bp_send(&dev->gdb, 'Z', bp->addr) < 0))
return -1;
bp->flags &= ~DEVICE_BP_DIRTY;
}
memcpy(dev->last_bps, dev->base.breakpoints, sizeof(dev->last_bps));
return 0;
}
static int gdbc_ctl(device_t dev_base, device_ctl_t op)
{
struct gdb_client *dev = (struct gdb_client *)dev_base;
switch (op) {
case DEVICE_CTL_STEP:
if (gdb_send(&dev->gdb, "s") < 0)
return -1;
return check_ok(&dev->gdb);
case DEVICE_CTL_RUN:
if (refresh_bps(dev) < 0)
return -1;
if (gdb_send(&dev->gdb, "c") < 0)
return -1;
dev->is_running = 1;
return 0;
case DEVICE_CTL_HALT:
if (dev->is_running) {
if (write_all(dev->gdb.sock,
(const uint8_t *)"\003", 1) < 0) {
pr_error("gdbc: write");
return -1;
}
dev->is_running = 0;
return check_ok(&dev->gdb);
}
return 0;
case DEVICE_CTL_RESET:
return do_reset(dev);
}
return 0;
}
static int gdbc_erase(device_t dev_base, device_erase_type_t type,
address_t addr)
{
struct gdb_client *dev = (struct gdb_client *)dev_base;
const char *cmd = "erase";
char buf[GDB_BUF_SIZE];
int len;
gdb_packet_start(&dev->gdb);
gdb_printf(&dev->gdb, "qRcmd,");
while (*cmd)
gdb_printf(&dev->gdb, "%02x", *(cmd++));
gdb_packet_end(&dev->gdb);
if (gdb_flush_ack(&dev->gdb) < 0)
return -1;
len = gdb_read_packet(&dev->gdb, buf);
if (len < 0)
return -1;
return 0;
}
static device_status_t gdbc_poll(device_t dev_base)
{
struct gdb_client *dev = (struct gdb_client *)dev_base;
char buf[GDB_BUF_SIZE];
int len;
if (!dev->is_running)
return DEVICE_STATUS_HALTED;
len = gdb_peek(&dev->gdb, 50);
if (len < 0) {
if (errno == EINTR)
return DEVICE_STATUS_INTR;
dev->is_running = 0;
return DEVICE_STATUS_ERROR;
}
if (!len)
return DEVICE_STATUS_RUNNING;
len = gdb_read_packet(&dev->gdb, buf);
if (len < 0) {
dev->is_running = 0;
return DEVICE_STATUS_ERROR;
}
dev->is_running = 0;
return DEVICE_STATUS_HALTED;
}
static int connect_to(const char *spec)
{
const char *port_text;
int hn_len;
int port = 2000;
char hostname[128];
struct hostent *ent;
struct sockaddr_in addr;
int sock;
if (!spec) {
printc_err("gdbc: no remote target specified\n");
return -1;
}
port_text = strchr(spec, ':');
if (port_text) {
port = atoi(port_text + 1);
hn_len = port_text - spec;
} else {
hn_len = strlen(spec);
}
if (hn_len + 1 > sizeof(hostname))
hn_len = sizeof(hostname) - 1;
memcpy(hostname, spec, hn_len);
hostname[hn_len] = 0;
printc_dbg("Looking up %s...\n", hostname);
ent = gethostbyname(hostname);
if (!ent) {
printc_err("No such host: %s: %s\n", hostname,
hstrerror(h_errno));
return -1;
}
sock = socket(PF_INET, SOCK_STREAM, 0);
if (!sock) {
printc_err("socket: %s\n", strerror(errno));
return -1;
}
addr.sin_family = AF_INET;
addr.sin_port = htons(port);
addr.sin_addr = *(struct in_addr *)ent->h_addr;
printc_dbg("Connecting to %s:%d...\n",
inet_ntoa(addr.sin_addr), port);
if (connect(sock, (struct sockaddr *)&addr, sizeof(addr)) < 0) {
printc_err("connect: %s\n", strerror(errno));
close(sock);
return -1;
}
return sock;
}
static device_t gdbc_open(const struct device_args *args)
{
int sock = connect_to(args->path);
struct gdb_client *dev;
if (sock < 0)
return NULL;
dev = malloc(sizeof(struct gdb_client));
if (!dev) {
printc_err("gdbc: can't allocate memory: %s\n",
strerror(errno));
return NULL;
}
memset(dev, 0, sizeof(*dev));
dev->base.type = &device_gdbc;
dev->base.max_breakpoints = DEVICE_MAX_BREAKPOINTS;
gdb_init(&dev->gdb, sock);
return (device_t)dev;
}
const struct device_class device_gdbc = {
.name = "gdbc",
.help = "GDB client mode",
.open = gdbc_open,
.destroy = gdbc_destroy,
.readmem = gdbc_readmem,
.writemem = gdbc_writemem,
.erase = gdbc_erase,
.getregs = gdbc_getregs,
.setregs = gdbc_setregs,
.ctl = gdbc_ctl,
.poll = gdbc_poll
};

27
gdbc.h Normal file
View File

@ -0,0 +1,27 @@
/* MSPDebug - debugging tool for MSP430 MCUs
* Copyright (C) 2009-2011 Daniel Beer
*
* 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
#ifndef GDBC_H_
#define GDBC_H_
#include "device.h"
/* GDB client implementation */
extern const struct device_class device_gdbc;
#endif

4
main.c
View File

@ -46,6 +46,7 @@
#include "vector.h"
#include "fet_db.h"
#include "flash_bsl.h"
#include "gdbc.h"
#include "uif.h"
#include "olimex.h"
@ -64,7 +65,8 @@ static const struct device_class *const driver_table[] = {
&device_sim,
&device_uif,
&device_bsl,
&device_flash_bsl
&device_flash_bsl,
&device_gdbc
};
static const char *version_text =

View File

@ -117,6 +117,11 @@ Connect RTS to the device's TEST pin and DTR to the device's RST pin.
Use an appropriate serial level-shifter to make the connection, if necessary.
If connecting to a device with non-multiplexed JTAG pins, connect RTS to
the device's TCK pin via an inverter.
.IP "\fBgdbc\fR"
GDB client mode. Connect to a server which implements the GDB remote
protocol and provide an interface to it. To use this driver, specify
the remote address in \fIhostname:port\fR format using the \fB-d\fR
option.
.SH COMMANDS
MSPDebug can accept commands either through an interactive prompt, or
non-interactively when specified on the command line. The supported
@ -608,6 +613,10 @@ speed, but may cause problems with some chips.
Automatically restart the GDB server after disconnection. If this
option is set, then the GDB server keeps running until an error occurs,
or the user interrupts with Ctrl+C.
.IP "\fBgdbc_xfer_size\fR (numeric)"
Maximum size of memory transfers for the GDB client. Increasing this
value will result in faster transfers, but may cause problems with some
servers.
.IP "\fBiradix\fR (numeric)"
Default input radix for address expressions. For address values with
no radix specifier, this value gives the input radix, which is

11
opdb.c
View File

@ -66,6 +66,17 @@ const static struct opdb_key keys[] = {
.defval = {
.numeric = 64
}
},
{
.name = "gdbc_xfer_size",
.type = OPDB_TYPE_NUMERIC,
.help =
"Maximum size of memory transfers for the GDB client. Increasing this\n"
"value will result in faster transfers, but may cause problems with some\n"
"servers.\n",
.defval = {
.numeric = 64
}
}
};