mirror of https://github.com/xemu-project/xemu.git
moved gdbstub to qemu - new asynchronous gdbstub
git-svn-id: svn://svn.savannah.nongnu.org/qemu/trunk@686 c046a42c-6fe2-441c-8c8c-71466251a162
This commit is contained in:
parent
9b14bb04ca
commit
858693c638
|
@ -166,7 +166,7 @@ SRCS:= $(OBJS:.o=.c)
|
||||||
OBJS+= libqemu.a
|
OBJS+= libqemu.a
|
||||||
|
|
||||||
# cpu emulator library
|
# 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
|
translate.o op.o
|
||||||
|
|
||||||
ifeq ($(TARGET_ARCH), i386)
|
ifeq ($(TARGET_ARCH), i386)
|
||||||
|
@ -219,7 +219,7 @@ ifeq ($(ARCH),alpha)
|
||||||
endif
|
endif
|
||||||
|
|
||||||
# must use static linking to avoid leaving stuff in virtual address space
|
# 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 \
|
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
|
fdc.o mc146818rtc.o serial.o i8259.o i8254.o pc.o
|
||||||
ifeq ($(TARGET_ARCH), ppc)
|
ifeq ($(TARGET_ARCH), ppc)
|
||||||
|
|
|
@ -611,8 +611,6 @@ void page_unprotect_range(uint8_t *data, unsigned long data_size);
|
||||||
|
|
||||||
#endif /* SINGLE_CPU_DEFINES */
|
#endif /* SINGLE_CPU_DEFINES */
|
||||||
|
|
||||||
#define DEFAULT_GDBSTUB_PORT 1234
|
|
||||||
|
|
||||||
void cpu_abort(CPUState *env, const char *fmt, ...);
|
void cpu_abort(CPUState *env, const char *fmt, ...);
|
||||||
extern CPUState *cpu_single_env;
|
extern CPUState *cpu_single_env;
|
||||||
extern int code_copy_enabled;
|
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);
|
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 */
|
#endif /* CPU_ALL_H */
|
||||||
|
|
531
gdbstub.c
531
gdbstub.c
|
@ -26,70 +26,36 @@
|
||||||
#include <netinet/in.h>
|
#include <netinet/in.h>
|
||||||
#include <netinet/tcp.h>
|
#include <netinet/tcp.h>
|
||||||
#include <signal.h>
|
#include <signal.h>
|
||||||
|
#include <fcntl.h>
|
||||||
|
|
||||||
#include "cpu.h"
|
#include "vl.h"
|
||||||
#include "exec-all.h"
|
|
||||||
|
|
||||||
//#define DEBUG_GDB
|
//#define DEBUG_GDB
|
||||||
|
|
||||||
int gdbstub_fd = -1;
|
enum RSState {
|
||||||
|
RS_IDLE,
|
||||||
|
RS_GETLINE,
|
||||||
|
RS_CHKSUM1,
|
||||||
|
RS_CHKSUM2,
|
||||||
|
};
|
||||||
|
|
||||||
/* return 0 if OK */
|
static int gdbserver_fd;
|
||||||
static int gdbstub_open(int port)
|
|
||||||
{
|
|
||||||
struct sockaddr_in sockaddr;
|
|
||||||
socklen_t len;
|
|
||||||
int fd, val, ret;
|
|
||||||
|
|
||||||
fd = socket(PF_INET, SOCK_STREAM, 0);
|
typedef struct GDBState {
|
||||||
if (fd < 0) {
|
enum RSState state;
|
||||||
perror("socket");
|
int fd;
|
||||||
return -1;
|
char line_buf[4096];
|
||||||
}
|
int line_buf_index;
|
||||||
|
int line_csum;
|
||||||
|
} GDBState;
|
||||||
|
|
||||||
/* allow fast reuse */
|
static int get_char(GDBState *s)
|
||||||
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)
|
|
||||||
{
|
{
|
||||||
uint8_t ch;
|
uint8_t ch;
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
for(;;) {
|
for(;;) {
|
||||||
ret = read(gdbstub_fd, &ch, 1);
|
ret = read(s->fd, &ch, 1);
|
||||||
if (ret < 0) {
|
if (ret < 0) {
|
||||||
if (errno != EINTR && errno != EAGAIN)
|
if (errno != EINTR && errno != EAGAIN)
|
||||||
return -1;
|
return -1;
|
||||||
|
@ -102,12 +68,12 @@ static int get_char(void)
|
||||||
return ch;
|
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;
|
int ret;
|
||||||
|
|
||||||
while (len > 0) {
|
while (len > 0) {
|
||||||
ret = write(gdbstub_fd, buf, len);
|
ret = write(s->fd, buf, len);
|
||||||
if (ret < 0) {
|
if (ret < 0) {
|
||||||
if (errno != EINTR && errno != EAGAIN)
|
if (errno != EINTR && errno != EAGAIN)
|
||||||
return;
|
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 */
|
/* return -1 if error, 0 if OK */
|
||||||
static int put_packet(char *buf)
|
static int put_packet(GDBState *s, char *buf)
|
||||||
{
|
{
|
||||||
char buf1[3];
|
char buf1[3];
|
||||||
int len, csum, ch, i;
|
int len, csum, ch, i;
|
||||||
|
@ -224,9 +139,9 @@ static int put_packet(char *buf)
|
||||||
|
|
||||||
for(;;) {
|
for(;;) {
|
||||||
buf1[0] = '$';
|
buf1[0] = '$';
|
||||||
put_buffer(buf1, 1);
|
put_buffer(s, buf1, 1);
|
||||||
len = strlen(buf);
|
len = strlen(buf);
|
||||||
put_buffer(buf, len);
|
put_buffer(s, buf, len);
|
||||||
csum = 0;
|
csum = 0;
|
||||||
for(i = 0; i < len; i++) {
|
for(i = 0; i < len; i++) {
|
||||||
csum += buf[i];
|
csum += buf[i];
|
||||||
|
@ -235,9 +150,9 @@ static int put_packet(char *buf)
|
||||||
buf1[1] = tohex((csum >> 4) & 0xf);
|
buf1[1] = tohex((csum >> 4) & 0xf);
|
||||||
buf1[2] = tohex((csum) & 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)
|
if (ch < 0)
|
||||||
return -1;
|
return -1;
|
||||||
if (ch == '+')
|
if (ch == '+')
|
||||||
|
@ -387,149 +302,293 @@ static void cpu_gdb_write_registers(CPUState *env, uint8_t *mem_buf, int size)
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
/* port = 0 means default port */
|
/* 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;
|
const char *p;
|
||||||
int ret, ch, reg_size, type;
|
int ch, reg_size, type;
|
||||||
char buf[4096];
|
char buf[4096];
|
||||||
uint8_t mem_buf[2000];
|
uint8_t mem_buf[2000];
|
||||||
uint32_t *registers;
|
uint32_t *registers;
|
||||||
uint32_t addr, len;
|
uint32_t addr, len;
|
||||||
|
|
||||||
printf("Waiting gdb connection on port %d\n", port);
|
#ifdef DEBUG_GDB
|
||||||
if (gdbstub_open(port) < 0)
|
printf("command='%s'\n", line_buf);
|
||||||
return -1;
|
|
||||||
printf("Connected\n");
|
|
||||||
for(;;) {
|
|
||||||
ret = get_packet(buf, sizeof(buf));
|
|
||||||
if (ret < 0)
|
|
||||||
break;
|
|
||||||
p = buf;
|
|
||||||
ch = *p++;
|
|
||||||
switch(ch) {
|
|
||||||
case '?':
|
|
||||||
snprintf(buf, sizeof(buf), "S%02x", SIGTRAP);
|
|
||||||
put_packet(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
|
#endif
|
||||||
}
|
p = line_buf;
|
||||||
ret = main_loop(opaque);
|
ch = *p++;
|
||||||
if (ret == EXCP_DEBUG)
|
switch(ch) {
|
||||||
ret = SIGTRAP;
|
case '?':
|
||||||
else
|
snprintf(buf, sizeof(buf), "S%02x", SIGTRAP);
|
||||||
ret = 0;
|
put_packet(s, buf);
|
||||||
snprintf(buf, sizeof(buf), "S%02x", ret);
|
break;
|
||||||
put_packet(buf);
|
case 'c':
|
||||||
break;
|
if (*p != '\0') {
|
||||||
case 's':
|
addr = strtoul(p, (char **)&p, 16);
|
||||||
env = cpu_gdbstub_get_env(opaque);
|
|
||||||
if (*p != '\0') {
|
|
||||||
addr = strtoul(p, (char **)&p, 16);
|
|
||||||
#if defined(TARGET_I386)
|
#if defined(TARGET_I386)
|
||||||
env->eip = addr;
|
env->eip = addr;
|
||||||
#elif defined (TARGET_PPC)
|
#elif defined (TARGET_PPC)
|
||||||
env->nip = addr;
|
env->nip = addr;
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
cpu_single_step(env, 1);
|
vm_start();
|
||||||
ret = main_loop(opaque);
|
break;
|
||||||
cpu_single_step(env, 0);
|
case 's':
|
||||||
if (ret == EXCP_DEBUG)
|
if (*p != '\0') {
|
||||||
ret = SIGTRAP;
|
|
||||||
else
|
|
||||||
ret = 0;
|
|
||||||
snprintf(buf, sizeof(buf), "S%02x", ret);
|
|
||||||
put_packet(buf);
|
|
||||||
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);
|
|
||||||
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");
|
|
||||||
break;
|
|
||||||
case 'm':
|
|
||||||
env = cpu_gdbstub_get_env(opaque);
|
|
||||||
addr = strtoul(p, (char **)&p, 16);
|
addr = strtoul(p, (char **)&p, 16);
|
||||||
if (*p == ',')
|
#if defined(TARGET_I386)
|
||||||
p++;
|
env->eip = addr;
|
||||||
len = strtoul(p, NULL, 16);
|
#elif defined (TARGET_PPC)
|
||||||
if (cpu_memory_rw_debug(env, addr, mem_buf, len, 0) != 0)
|
env->nip = addr;
|
||||||
memset(mem_buf, 0, len);
|
#endif
|
||||||
memtohex(buf, mem_buf, len);
|
}
|
||||||
put_packet(buf);
|
cpu_single_step(env, 1);
|
||||||
break;
|
vm_start();
|
||||||
case 'M':
|
break;
|
||||||
env = cpu_gdbstub_get_env(opaque);
|
case 'g':
|
||||||
addr = strtoul(p, (char **)&p, 16);
|
reg_size = cpu_gdb_read_registers(env, mem_buf);
|
||||||
if (*p == ',')
|
memtohex(buf, mem_buf, reg_size);
|
||||||
p++;
|
put_packet(s, buf);
|
||||||
len = strtoul(p, (char **)&p, 16);
|
break;
|
||||||
if (*p == ',')
|
case 'G':
|
||||||
p++;
|
registers = (void *)mem_buf;
|
||||||
hextomem(mem_buf, p, len);
|
len = strlen(p) / 2;
|
||||||
if (cpu_memory_rw_debug(env, addr, mem_buf, len, 1) != 0)
|
hextomem((uint8_t *)registers, p, len);
|
||||||
put_packet("ENN");
|
cpu_gdb_write_registers(env, mem_buf, len);
|
||||||
else
|
put_packet(s, "OK");
|
||||||
put_packet("OK");
|
break;
|
||||||
break;
|
case 'm':
|
||||||
case 'Z':
|
addr = strtoul(p, (char **)&p, 16);
|
||||||
type = strtoul(p, (char **)&p, 16);
|
if (*p == ',')
|
||||||
if (*p == ',')
|
p++;
|
||||||
p++;
|
len = strtoul(p, NULL, 16);
|
||||||
addr = strtoul(p, (char **)&p, 16);
|
if (cpu_memory_rw_debug(env, addr, mem_buf, len, 0) != 0)
|
||||||
if (*p == ',')
|
memset(mem_buf, 0, len);
|
||||||
p++;
|
memtohex(buf, mem_buf, len);
|
||||||
len = strtoul(p, (char **)&p, 16);
|
put_packet(s, buf);
|
||||||
if (type == 0 || type == 1) {
|
break;
|
||||||
env = cpu_gdbstub_get_env(opaque);
|
case 'M':
|
||||||
if (cpu_breakpoint_insert(env, addr) < 0)
|
addr = strtoul(p, (char **)&p, 16);
|
||||||
goto breakpoint_error;
|
if (*p == ',')
|
||||||
put_packet("OK");
|
p++;
|
||||||
} else {
|
len = strtoul(p, (char **)&p, 16);
|
||||||
breakpoint_error:
|
if (*p == ',')
|
||||||
put_packet("ENN");
|
p++;
|
||||||
}
|
hextomem(mem_buf, p, len);
|
||||||
break;
|
if (cpu_memory_rw_debug(env, addr, mem_buf, len, 1) != 0)
|
||||||
case 'z':
|
put_packet(s, "ENN");
|
||||||
type = strtoul(p, (char **)&p, 16);
|
else
|
||||||
if (*p == ',')
|
put_packet(s, "OK");
|
||||||
p++;
|
break;
|
||||||
addr = strtoul(p, (char **)&p, 16);
|
case 'Z':
|
||||||
if (*p == ',')
|
type = strtoul(p, (char **)&p, 16);
|
||||||
p++;
|
if (*p == ',')
|
||||||
len = strtoul(p, (char **)&p, 16);
|
p++;
|
||||||
if (type == 0 || type == 1) {
|
addr = strtoul(p, (char **)&p, 16);
|
||||||
env = cpu_gdbstub_get_env(opaque);
|
if (*p == ',')
|
||||||
cpu_breakpoint_remove(env, addr);
|
p++;
|
||||||
put_packet("OK");
|
len = strtoul(p, (char **)&p, 16);
|
||||||
} else {
|
if (type == 0 || type == 1) {
|
||||||
|
if (cpu_breakpoint_insert(env, addr) < 0)
|
||||||
goto breakpoint_error;
|
goto breakpoint_error;
|
||||||
|
put_packet(s, "OK");
|
||||||
|
} else {
|
||||||
|
breakpoint_error:
|
||||||
|
put_packet(s, "ENN");
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case 'z':
|
||||||
|
type = strtoul(p, (char **)&p, 16);
|
||||||
|
if (*p == ',')
|
||||||
|
p++;
|
||||||
|
addr = strtoul(p, (char **)&p, 16);
|
||||||
|
if (*p == ',')
|
||||||
|
p++;
|
||||||
|
len = strtoul(p, (char **)&p, 16);
|
||||||
|
if (type == 0 || type == 1) {
|
||||||
|
cpu_breakpoint_remove(env, addr);
|
||||||
|
put_packet(s, "OK");
|
||||||
|
} else {
|
||||||
|
goto breakpoint_error;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
// unknown_command:
|
||||||
|
/* put empty packet */
|
||||||
|
buf[0] = '\0';
|
||||||
|
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;
|
break;
|
||||||
default:
|
case RS_GETLINE:
|
||||||
// unknown_command:
|
if (ch == '#') {
|
||||||
/* put empty packet */
|
s->state = RS_CHKSUM1;
|
||||||
buf[0] = '\0';
|
} else if (s->line_buf_index >= sizeof(s->line_buf) - 1) {
|
||||||
put_packet(buf);
|
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;
|
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;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue