Commit 858693c6 authored by Fabrice Bellard's avatar Fabrice Bellard
Browse files

moved gdbstub to qemu - new asynchronous gdbstub


git-svn-id: svn://svn.savannah.nongnu.org/qemu/trunk@686 c046a42c-6fe2-441c-8c8c-71466251a162
parent 9b14bb04
Loading
Loading
Loading
Loading
+2 −2
Original line number Diff line number Diff line
@@ -166,7 +166,7 @@ SRCS:= $(OBJS:.o=.c)
OBJS+= libqemu.a

# cpu emulator library
LIBOBJS=exec.o translate-all.o cpu-exec.o gdbstub.o \
LIBOBJS=exec.o translate-all.o cpu-exec.o\
        translate.o op.o

ifeq ($(TARGET_ARCH), i386)
@@ -219,7 +219,7 @@ ifeq ($(ARCH),alpha)
endif

# must use static linking to avoid leaving stuff in virtual address space
VL_OBJS=vl.o osdep.o block.o monitor.o \
VL_OBJS=vl.o osdep.o block.o monitor.o gdbstub.o \
        ide.o ne2000.o pckbd.o vga.o sb16.o dma.o oss.o \
        fdc.o mc146818rtc.o serial.o i8259.o i8254.o pc.o
ifeq ($(TARGET_ARCH), ppc)
+0 −7
Original line number Diff line number Diff line
@@ -611,8 +611,6 @@ void page_unprotect_range(uint8_t *data, unsigned long data_size);

#endif /* SINGLE_CPU_DEFINES */

#define DEFAULT_GDBSTUB_PORT 1234

void cpu_abort(CPUState *env, const char *fmt, ...);
extern CPUState *cpu_single_env;
extern int code_copy_enabled;
@@ -722,9 +720,4 @@ static inline void cpu_physical_memory_set_dirty(target_ulong addr)

void cpu_physical_memory_reset_dirty(target_ulong start, target_ulong end);

/* gdb stub API */
extern int gdbstub_fd;
CPUState *cpu_gdbstub_get_env(void *opaque);
int cpu_gdbstub(void *opaque, int (*main_loop)(void *opaque), int port);

#endif /* CPU_ALL_H */
+289 −230
Original line number Diff line number Diff line
@@ -26,70 +26,36 @@
#include <netinet/in.h>
#include <netinet/tcp.h>
#include <signal.h>
#include <fcntl.h>

#include "cpu.h"
#include "exec-all.h"
#include "vl.h"

//#define DEBUG_GDB

int gdbstub_fd = -1;
enum RSState {
    RS_IDLE,
    RS_GETLINE,
    RS_CHKSUM1,
    RS_CHKSUM2,
};

/* return 0 if OK */
static int gdbstub_open(int port)
{
    struct sockaddr_in sockaddr;
    socklen_t len;
    int fd, val, ret;
static int gdbserver_fd;

    fd = socket(PF_INET, SOCK_STREAM, 0);
    if (fd < 0) {
        perror("socket");
        return -1;
    }
typedef struct GDBState {
    enum RSState state;
    int fd;
    char line_buf[4096];
    int line_buf_index;
    int line_csum;
} GDBState;

    /* allow fast reuse */
    val = 1;
    setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &val, sizeof(val));

    sockaddr.sin_family = AF_INET;
    sockaddr.sin_port = htons(port);
    sockaddr.sin_addr.s_addr = 0;
    ret = bind(fd, (struct sockaddr *)&sockaddr, sizeof(sockaddr));
    if (ret < 0) {
        perror("bind");
        return -1;
    }
    ret = listen(fd, 0);
    if (ret < 0) {
        perror("listen");
        return -1;
    }
    
    /* now wait for one connection */
    for(;;) {
        len = sizeof(sockaddr);
        gdbstub_fd = accept(fd, (struct sockaddr *)&sockaddr, &len);
        if (gdbstub_fd < 0 && errno != EINTR) {
            perror("accept");
            return -1;
        } else if (gdbstub_fd >= 0) {
            break;
        }
    }
    
    /* set short latency */
    val = 1;
    setsockopt(gdbstub_fd, SOL_TCP, TCP_NODELAY, &val, sizeof(val));
    return 0;
}

static int get_char(void)
static int get_char(GDBState *s)
{
    uint8_t ch;
    int ret;

    for(;;) {
        ret = read(gdbstub_fd, &ch, 1);
        ret = read(s->fd, &ch, 1);
        if (ret < 0) {
            if (errno != EINTR && errno != EAGAIN)
                return -1;
@@ -102,12 +68,12 @@ static int get_char(void)
    return ch;
}

static void put_buffer(const uint8_t *buf, int len)
static void put_buffer(GDBState *s, const uint8_t *buf, int len)
{
    int ret;

    while (len > 0) {
        ret = write(gdbstub_fd, buf, len);
        ret = write(s->fd, buf, len);
        if (ret < 0) {
            if (errno != EINTR && errno != EAGAIN)
                return;
@@ -161,59 +127,8 @@ static void hextomem(uint8_t *mem, const char *buf, int len)
    }
}

/* return -1 if error or EOF */
static int get_packet(char *buf, int buf_size)
{
    int ch, len, csum, csum1;
    char reply[1];
    
    for(;;) {
        for(;;) {
            ch = get_char();
            if (ch < 0)
                return -1;
            if (ch == '$')
                break;
        }
        len = 0;
        csum = 0;
        for(;;) {
            ch = get_char();
            if (ch < 0)
                return -1;
            if (ch == '#')
                break;
            if (len > buf_size - 1)
                return -1;
            buf[len++] = ch;
            csum += ch;
        }
        buf[len] = '\0';
        ch = get_char();
        if (ch < 0)
            return -1;
        csum1 = fromhex(ch) << 4;
        ch = get_char();
        if (ch < 0)
            return -1;
        csum1 |= fromhex(ch);
        if ((csum & 0xff) != csum1) {
            reply[0] = '-';
            put_buffer(reply, 1);
        } else {
            reply[0] = '+';
            put_buffer(reply, 1);
            break;
        }
    }
#ifdef DEBUG_GDB
    printf("command='%s'\n", buf);
#endif
    return len;
}

/* return -1 if error, 0 if OK */
static int put_packet(char *buf)
static int put_packet(GDBState *s, char *buf)
{
    char buf1[3];
    int len, csum, ch, i;
@@ -224,9 +139,9 @@ static int put_packet(char *buf)

    for(;;) {
        buf1[0] = '$';
        put_buffer(buf1, 1);
        put_buffer(s, buf1, 1);
        len = strlen(buf);
        put_buffer(buf, len);
        put_buffer(s, buf, len);
        csum = 0;
        for(i = 0; i < len; i++) {
            csum += buf[i];
@@ -235,9 +150,9 @@ static int put_packet(char *buf)
        buf1[1] = tohex((csum >> 4) & 0xf);
        buf1[2] = tohex((csum) & 0xf);

        put_buffer(buf1, 3);
        put_buffer(s, buf1, 3);

        ch = get_char();
        ch = get_char(s);
        if (ch < 0)
            return -1;
        if (ch == '+')
@@ -387,51 +302,38 @@ static void cpu_gdb_write_registers(CPUState *env, uint8_t *mem_buf, int size)
#endif

/* port = 0 means default port */
int cpu_gdbstub(void *opaque, int (*main_loop)(void *opaque), int port)
static int gdb_handle_packet(GDBState *s, const char *line_buf)
{
    CPUState *env;
    CPUState *env = cpu_single_env;
    const char *p;
    int ret, ch, reg_size, type;
    int ch, reg_size, type;
    char buf[4096];
    uint8_t mem_buf[2000];
    uint32_t *registers;
    uint32_t addr, len;
    
    printf("Waiting gdb connection on port %d\n", port);
    if (gdbstub_open(port) < 0)
        return -1;
    printf("Connected\n");
    for(;;) {
        ret = get_packet(buf, sizeof(buf));
        if (ret < 0)
            break;
        p = buf;
#ifdef DEBUG_GDB
    printf("command='%s'\n", line_buf);
#endif
    p = line_buf;
    ch = *p++;
    switch(ch) {
    case '?':
        snprintf(buf, sizeof(buf), "S%02x", SIGTRAP);
            put_packet(buf);
        put_packet(s, buf);
        break;
    case 'c':
        if (*p != '\0') {
            addr = strtoul(p, (char **)&p, 16);
                env = cpu_gdbstub_get_env(opaque);
#if defined(TARGET_I386)
            env->eip = addr;
#elif defined (TARGET_PPC)
            env->nip = addr;
#endif
        }
            ret = main_loop(opaque);
            if (ret == EXCP_DEBUG)
                ret = SIGTRAP;
            else
                ret = 0;
            snprintf(buf, sizeof(buf), "S%02x", ret);
            put_packet(buf);
        vm_start();
        break;
    case 's':
            env = cpu_gdbstub_get_env(opaque);
        if (*p != '\0') {
            addr = strtoul(p, (char **)&p, 16);
#if defined(TARGET_I386)
@@ -441,31 +343,21 @@ int cpu_gdbstub(void *opaque, int (*main_loop)(void *opaque), int port)
#endif
        }
        cpu_single_step(env, 1);
            ret = main_loop(opaque);
            cpu_single_step(env, 0);
            if (ret == EXCP_DEBUG)
                ret = SIGTRAP;
            else
                ret = 0;
            snprintf(buf, sizeof(buf), "S%02x", ret);
            put_packet(buf);
        vm_start();
        break;
    case 'g':
            env = cpu_gdbstub_get_env(opaque);
        reg_size = cpu_gdb_read_registers(env, mem_buf);
        memtohex(buf, mem_buf, reg_size);
            put_packet(buf);
        put_packet(s, buf);
        break;
    case 'G':
            env = cpu_gdbstub_get_env(opaque);
        registers = (void *)mem_buf;
        len = strlen(p) / 2;
        hextomem((uint8_t *)registers, p, len);
        cpu_gdb_write_registers(env, mem_buf, len);
            put_packet("OK");
        put_packet(s, "OK");
        break;
    case 'm':
            env = cpu_gdbstub_get_env(opaque);
        addr = strtoul(p, (char **)&p, 16);
        if (*p == ',')
            p++;
@@ -473,10 +365,9 @@ int cpu_gdbstub(void *opaque, int (*main_loop)(void *opaque), int port)
        if (cpu_memory_rw_debug(env, addr, mem_buf, len, 0) != 0)
            memset(mem_buf, 0, len);
        memtohex(buf, mem_buf, len);
            put_packet(buf);
        put_packet(s, buf);
        break;
    case 'M':
            env = cpu_gdbstub_get_env(opaque);
        addr = strtoul(p, (char **)&p, 16);
        if (*p == ',')
            p++;
@@ -485,9 +376,9 @@ int cpu_gdbstub(void *opaque, int (*main_loop)(void *opaque), int port)
            p++;
        hextomem(mem_buf, p, len);
        if (cpu_memory_rw_debug(env, addr, mem_buf, len, 1) != 0)
                put_packet("ENN");
            put_packet(s, "ENN");
        else
                put_packet("OK");
            put_packet(s, "OK");
        break;
    case 'Z':
        type = strtoul(p, (char **)&p, 16);
@@ -498,13 +389,12 @@ int cpu_gdbstub(void *opaque, int (*main_loop)(void *opaque), int port)
            p++;
        len = strtoul(p, (char **)&p, 16);
        if (type == 0 || type == 1) {
                env = cpu_gdbstub_get_env(opaque);
            if (cpu_breakpoint_insert(env, addr) < 0)
                goto breakpoint_error;
                put_packet("OK");
            put_packet(s, "OK");
        } else {
        breakpoint_error:
                put_packet("ENN");
            put_packet(s, "ENN");
        }
        break;
    case 'z':
@@ -516,9 +406,8 @@ int cpu_gdbstub(void *opaque, int (*main_loop)(void *opaque), int port)
            p++;
        len = strtoul(p, (char **)&p, 16);
        if (type == 0 || type == 1) {
                env = cpu_gdbstub_get_env(opaque);
            cpu_breakpoint_remove(env, addr);
                put_packet("OK");
            put_packet(s, "OK");
        } else {
            goto breakpoint_error;
        }
@@ -527,9 +416,179 @@ int cpu_gdbstub(void *opaque, int (*main_loop)(void *opaque), int port)
        //        unknown_command:
        /* put empty packet */
        buf[0] = '\0';
            put_packet(buf);
        put_packet(s, buf);
        break;
    }
    return RS_IDLE;
}

static void gdb_vm_stopped(void *opaque, int reason)
{
    GDBState *s = opaque;
    char buf[256];
    int ret;

    /* disable single step if it was enable */
    cpu_single_step(cpu_single_env, 0);

    if (reason == EXCP_DEBUG)
        ret = SIGTRAP;
    else
        ret = 0;
    snprintf(buf, sizeof(buf), "S%02x", ret);
    put_packet(s, buf);
}

static void gdb_read_byte(GDBState *s, int ch)
{
    int i, csum;
    char reply[1];

    if (vm_running) {
        /* when the CPU is running, we cannot do anything except stop
           it when receiving a char */
        vm_stop(EXCP_INTERRUPT);
    } else {
        switch(s->state) {
        case RS_IDLE:
            if (ch == '$') {
                s->line_buf_index = 0;
                s->state = RS_GETLINE;
            }
            break;
        case RS_GETLINE:
            if (ch == '#') {
            s->state = RS_CHKSUM1;
            } else if (s->line_buf_index >= sizeof(s->line_buf) - 1) {
                s->state = RS_IDLE;
            } else {
            s->line_buf[s->line_buf_index++] = ch;
            }
            break;
        case RS_CHKSUM1:
            s->line_buf[s->line_buf_index] = '\0';
            s->line_csum = fromhex(ch) << 4;
            s->state = RS_CHKSUM2;
            break;
        case RS_CHKSUM2:
            s->line_csum |= fromhex(ch);
            csum = 0;
            for(i = 0; i < s->line_buf_index; i++) {
                csum += s->line_buf[i];
            }
            if (s->line_csum != (csum & 0xff)) {
                reply[0] = '-';
                put_buffer(s, reply, 1);
                s->state = RS_IDLE;
            } else {
                reply[0] = '+';
                put_buffer(s, reply, 1);
                s->state = gdb_handle_packet(s, s->line_buf);
            }
            break;
        }
    }
}

static int gdb_can_read(void *opaque)
{
    return 256;
}

static void gdb_read(void *opaque, const uint8_t *buf, int size)
{
    GDBState *s = opaque;
    int i;
    if (size == 0) {
        /* end of connection */
        qemu_del_vm_stop_handler(gdb_vm_stopped, s);
        qemu_del_fd_read_handler(s->fd);
        qemu_free(s);
        vm_start();
    } else {
        for(i = 0; i < size; i++)
            gdb_read_byte(s, buf[i]);
    }
}

static void gdb_accept(void *opaque, const uint8_t *buf, int size)
{
    GDBState *s;
    struct sockaddr_in sockaddr;
    socklen_t len;
    int val, fd;

    for(;;) {
        len = sizeof(sockaddr);
        fd = accept(gdbserver_fd, (struct sockaddr *)&sockaddr, &len);
        if (fd < 0 && errno != EINTR) {
            perror("accept");
            return;
        } else if (fd >= 0) {
            break;
        }
    }

    /* set short latency */
    val = 1;
    setsockopt(fd, SOL_TCP, TCP_NODELAY, &val, sizeof(val));
    
    s = qemu_mallocz(sizeof(GDBState));
    if (!s) {
        close(fd);
        return;
    }
    s->fd = fd;

    fcntl(fd, F_SETFL, O_NONBLOCK);

    /* stop the VM */
    vm_stop(EXCP_INTERRUPT);

    /* start handling I/O */
    qemu_add_fd_read_handler(s->fd, gdb_can_read, gdb_read, s);
    /* when the VM is stopped, the following callback is called */
    qemu_add_vm_stop_handler(gdb_vm_stopped, s);
}

static int gdbserver_open(int port)
{
    struct sockaddr_in sockaddr;
    int fd, val, ret;

    fd = socket(PF_INET, SOCK_STREAM, 0);
    if (fd < 0) {
        perror("socket");
        return -1;
    }

    /* allow fast reuse */
    val = 1;
    setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &val, sizeof(val));

    sockaddr.sin_family = AF_INET;
    sockaddr.sin_port = htons(port);
    sockaddr.sin_addr.s_addr = 0;
    ret = bind(fd, (struct sockaddr *)&sockaddr, sizeof(sockaddr));
    if (ret < 0) {
        perror("bind");
        return -1;
    }
    ret = listen(fd, 0);
    if (ret < 0) {
        perror("listen");
        return -1;
    }
    fcntl(fd, F_SETFL, O_NONBLOCK);
    return fd;
}

int gdbserver_start(int port)
{
    gdbserver_fd = gdbserver_open(port);
    if (gdbserver_fd < 0)
        return -1;
    /* accept connections */
    qemu_add_fd_read_handler(gdbserver_fd, NULL, gdb_accept, NULL);
    return 0;
}