Rename cdb to gdbstub and split it into arch dependent/neutral part.

Signed-off-by: Isaku Yamahata <yamahata@valinux.co.jp>
Signed-off-by: Hollis Blanchard <hollisb@us.ibm.com>
This commit is contained in:
kaf24@firebug.cl.cam.ac.uk committed 2006-01-14 16:58:54 +01:00
1 parent 843f4eb435
commit 360a1871b5
7 files changed
+845 -419

No files matched your search

+1 -1
View File
@@ -32,7 +32,7 @@ OBJS := $(subst $(TARGET_SUBARCH)/asm-offsets.o,,$(OBJS))
OBJS := $(subst $(TARGET_SUBARCH)/xen.lds.o,,$(OBJS))
ifneq ($(crash_debug),y)
OBJS := $(patsubst cdb%.o,,$(OBJS))
OBJS := $(patsubst gdbstub%.o,,$(OBJS))
endif
default: $(TARGET)
-414
View File
@@ -1,414 +0,0 @@
/* Simple hacked-up version of pdb for use in post-mortem debugging of
Xen and domain 0. This should be a little cleaner, hopefully. Note
that we can't share a serial line with PDB. */
/* We try to avoid assuming much about what the rest of the system is
doing. In particular, dynamic memory allocation is out of the
question. */
/* Resuming after we've stopped used to work, but more through luck
than any actual intention. It doesn't at the moment. */
#include <xen/lib.h>
#include <asm/uaccess.h>
#include <xen/spinlock.h>
#include <xen/serial.h>
#include <xen/irq.h>
#include <asm/debugger.h>
#include <xen/init.h>
#include <xen/smp.h>
#include <xen/console.h>
#include <asm/apic.h>
/* Printk isn't particularly safe just after we've trapped to the
debugger. so avoid it. */
#define dbg_printk(...)
static char opt_cdb[30] = "none";
string_param("cdb", opt_cdb);
struct xendbg_context {
int serhnd;
u8 reply_csum;
int currently_attached:1;
};
/* Like copy_from_user, but safe to call with interrupts disabled.
Trust me, and don't look behind the curtain. */
static unsigned
dbg_copy_from_user(void *dest, const void *src, unsigned len)
{
int __d0, __d1, __d2;
ASSERT(!local_irq_is_enabled());
__asm__ __volatile__(
"1: rep; movsb\n"
"2:\n"
".section .fixup,\"ax\"\n"
"3: addl $4, %%esp\n"
" jmp 2b\n"
".previous\n"
".section __pre_ex_table,\"a\"\n"
" .align 4\n"
" .long 1b,3b\n"
".previous\n"
".section __ex_table,\"a\"\n"
" .align 4\n"
" .long 1b,2b\n"
".previous\n"
: "=c"(__d2), "=D" (__d0), "=S" (__d1)
: "0"(len), "1"(dest), "2"(src)
: "memory");
ASSERT(!local_irq_is_enabled());
return __d2;
}
static void
xendbg_put_char(u8 data, struct xendbg_context *ctx)
{
ctx->reply_csum += data;
serial_putc(ctx->serhnd, data);
}
static int
hex_char_val(unsigned char c)
{
if (c >= '0' && c <= '9')
return c - '0';
else if (c >= 'a' && c <= 'f')
return c - 'a' + 10;
else if (c >= 'A' && c <= 'F')
return c - 'A' + 10;
else
BUG();
return -1;
}
/* Receive a command. Returns -1 on csum error, 0 otherwise. */
/* Does not acknowledge. */
static int
attempt_receive_packet(char *recv_buf, struct xendbg_context *ctx)
{
int count;
u8 csum;
u8 received_csum;
u8 ch;
/* Skip over everything up to the first '$' */
while ((ch = serial_getc(ctx->serhnd)) != '$')
;
csum = 0;
for (count = 0; count < 4096; count++) {
ch = serial_getc(ctx->serhnd);
if (ch == '#')
break;
recv_buf[count] = ch;
csum += ch;
}
if (count == 4096) {
dbg_printk("WARNING: GDB sent a stupidly big packet.\n");
return -1;
}
recv_buf[count] = 0;
received_csum = hex_char_val(serial_getc(ctx->serhnd)) * 16 +
hex_char_val(serial_getc(ctx->serhnd));
if (received_csum == csum) {
return 0;
} else {
return -1;
}
}
/* Send a string of bytes to the debugger. */
static void
xendbg_send(const char *buf, int count, struct xendbg_context *ctx)
{
int x;
for (x = 0; x < count; x++)
xendbg_put_char(buf[x], ctx);
}
/* Receive a command, discarding up to ten packets with csum
* errors. Acknowledges all received packets. */
static int
receive_command(char *recv_buf, struct xendbg_context *ctx)
{
int r;
int count;
count = 0;
do {
r = attempt_receive_packet(recv_buf, ctx);
if (r < 0)
xendbg_put_char('-', ctx);
else
xendbg_put_char('+', ctx);
count++;
} while (r < 0 && count < 10);
return r;
}
static void
xendbg_start_reply(struct xendbg_context *ctx)
{
xendbg_put_char('$', ctx);
ctx->reply_csum = 0;
}
/* Return 0 if the reply was successfully received, !0 otherwise. */
static int
xendbg_finish_reply(struct xendbg_context *ctx)
{
char ch;
char buf[3];
sprintf(buf, "%.02x\n", ctx->reply_csum);
xendbg_put_char('#', ctx);
xendbg_send(buf, 2, ctx);
ch = serial_getc(ctx->serhnd);
if (ch == '+')
return 0;
else
return 1;
}
/* Swap the order of the bytes in a work. */
static inline unsigned
bswab32(unsigned val)
{
return (((val >> 0) & 0xff) << 24) |
(((val >> 8) & 0xff) << 16) |
(((val >> 16) & 0xff) << 8) |
(((val >> 24) & 0xff) << 0);
}
static int
handle_memory_read_command(unsigned long addr, unsigned long length,
struct xendbg_context *ctx)
{
int x;
unsigned char val;
int r;
char buf[2];
dbg_printk("Memory read starting at %lx, length %lx.\n", addr,
length);
xendbg_start_reply(ctx);
for (x = 0; x < length; x++) {
r = dbg_copy_from_user(&val, (void *)(addr + x), 1);
if (r != 0) {
dbg_printk("Error reading from %lx.\n", addr + x);
break;
}
sprintf(buf, "%.02x", val);
xendbg_send(buf, 2, ctx);
}
if (x == 0)
xendbg_send("E05", 3, ctx);
dbg_printk("Read done.\n");
return xendbg_finish_reply(ctx);
}
static int
xendbg_send_reply(const char *buf, struct xendbg_context *ctx)
{
xendbg_start_reply(ctx);
xendbg_send(buf, strlen(buf), ctx);
return xendbg_finish_reply(ctx);
}
static int
handle_register_read_command(struct cpu_user_regs *regs, struct xendbg_context *ctx)
{
char buf[121];
sprintf(buf,
"%.08x%.08x%.08x%.08x%.08x%.08x%.08x%.08x%.08x%.08x%.08x%.08x%.08x%.08x%.08x%.08x",
bswab32(regs->eax),
bswab32(regs->ecx),
bswab32(regs->edx),
bswab32(regs->ebx),
bswab32(regs->esp),
bswab32(regs->ebp),
bswab32(regs->esi),
bswab32(regs->edi),
bswab32(regs->eip),
bswab32(regs->eflags),
bswab32(regs->cs),
bswab32(regs->ss),
bswab32(regs->ds),
bswab32(regs->es),
bswab32(regs->fs),
bswab32(regs->gs));
return xendbg_send_reply(buf, ctx);
}
static int
process_command(char *received_packet, struct cpu_user_regs *regs,
struct xendbg_context *ctx)
{
char *ptr;
unsigned long addr, length;
int retry;
int counter;
int resume = 0;
/* Repeat until gdb acks the reply */
counter = 0;
do {
switch (received_packet[0]) {
case 'g': /* Read registers */
retry = handle_register_read_command(regs, ctx);
ASSERT(!local_irq_is_enabled());
break;
case 'm': /* Read memory */
addr = simple_strtoul(received_packet + 1, &ptr, 16);
if (ptr == received_packet + 1 ||
ptr[0] != ',') {
xendbg_send_reply("E03", ctx);
return 0;
}
length = simple_strtoul(ptr + 1, &ptr, 16);
if (ptr[0] != 0) {
xendbg_send_reply("E04", ctx);
return 0;
}
retry =
handle_memory_read_command(addr,
length,
ctx);
ASSERT(!local_irq_is_enabled());
break;
case 'G': /* Write registers */
case 'M': /* Write memory */
retry = xendbg_send_reply("E02", ctx);
break;
case 'D':
resume = 1;
ctx->currently_attached = 0;
retry = xendbg_send_reply("", ctx);
break;
case 'c': /* Resume at current address */
ctx->currently_attached = 1;
resume = 1;
retry = 0;
break;
case 'Z': /* We need to claim to support these or gdb
won't let you continue the process. */
case 'z':
retry = xendbg_send_reply("OK", ctx);
break;
case 's': /* Single step */
case '?':
retry = xendbg_send_reply("S01", ctx);
break;
default:
retry = xendbg_send_reply("", ctx);
break;
}
counter++;
} while (retry == 1 && counter < 10);
if (retry) {
dbg_printk("WARNING: gdb disappeared when we were trying to send it a reply.\n");
return 1;
}
return resume;
}
static struct xendbg_context
xdb_ctx = {
serhnd : -1
};
int
__trap_to_cdb(struct cpu_user_regs *regs)
{
int resume = 0;
int r;
static atomic_t xendbg_running = ATOMIC_INIT(1);
static char recv_buf[4096];
unsigned flags;
if (xdb_ctx.serhnd < 0) {
dbg_printk("Debugger not ready yet.\n");
return 0;
}
/* We rely on our caller to ensure we're only on one processor
* at a time... We should probably panic here, but given that
* we're a debugger we should probably be a little tolerant of
* things going wrong. */
/* We don't want to use a spin lock here, because we're doing
two distinct things:
1 -- we don't want to run on more than one processor at a time,
and
2 -- we want to do something sensible if we re-enter ourselves.
Spin locks are good for 1, but useless for 2. */
if (!atomic_dec_and_test(&xendbg_running)) {
printk("WARNING WARNING WARNING: Avoiding recursive xendbg.\n");
atomic_inc(&xendbg_running);
return 0;
}
smp_send_stop();
/* Try to make things a little more stable by disabling
interrupts while we're here. */
local_irq_save(flags);
watchdog_disable();
console_start_sync();
/* Shouldn't really do this, but otherwise we stop for no
obvious reason, which is Bad */
printk("Waiting for GDB to attach to XenDBG\n");
/* If gdb is already attached, tell it we've stopped again. */
if (xdb_ctx.currently_attached) {
do {
r = xendbg_send_reply("S01", &xdb_ctx);
} while (r != 0);
}
while (resume == 0) {
ASSERT(!local_irq_is_enabled());
r = receive_command(recv_buf, &xdb_ctx);
ASSERT(!local_irq_is_enabled());
if (r < 0) {
dbg_printk("GDB disappeared, trying to resume Xen...\n");
resume = 1;
} else {
ASSERT(!local_irq_is_enabled());
resume = process_command(recv_buf, regs, &xdb_ctx);
ASSERT(!local_irq_is_enabled());
}
}
console_end_sync();
watchdog_enable();
atomic_inc(&xendbg_running);
local_irq_restore(flags);
return 0;
}
static int
initialize_xendbg(void)
{
if (!strcmp(opt_cdb, "none"))
return 0;
xdb_ctx.serhnd = serial_parse_handle(opt_cdb);
if (xdb_ctx.serhnd == -1)
panic("Can't parse %s as CDB serial info.\n", opt_cdb);
/* Acknowledge any spurious GDB packets. */
xendbg_put_char('+', &xdb_ctx);
printk("Xendbg initialised.\n");
return 0;
}
__initcall(initialize_xendbg);
+146
View File
@@ -0,0 +1,146 @@
/*
* x86-specific gdb stub routines
* based on x86 cdb(xen/arch/x86/cdb.c), but Extensively modified.
*
* Copyright (C) 2006 Isaku Yamahata <yamahata at valinux co jp>
* VA Linux Systems Japan. K.K.
*
* 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 <asm/debugger.h>
u16
gdb_arch_signal_num(struct cpu_user_regs *regs, unsigned long cookie)
{
/* XXX */
return 1;
}
void
gdb_arch_read_reg_array(struct cpu_user_regs *regs, struct gdb_context *ctx)
{
#define GDB_REG(r) gdb_write_to_packet_hex(r, sizeof(r), ctx);
GDB_REG(regs->eax);
GDB_REG(regs->ecx);
GDB_REG(regs->edx);
GDB_REG(regs->ebx);
GDB_REG(regs->esp);
GDB_REG(regs->ebp);
GDB_REG(regs->esi);
GDB_REG(regs->edi);
GDB_REG(regs->eip);
GDB_REG(regs->eflags);
#undef GDB_REG
#define GDB_SEG_REG(s) gdb_write_to_packet_hex(s, sizeof(u32), ctx);
/* sizeof(segment) = 16bit */
/* but gdb requires its return value as 32bit value */
GDB_SEG_REG(regs->cs);
GDB_SEG_REG(regs->ss);
GDB_SEG_REG(regs->ds);
GDB_SEG_REG(regs->es);
GDB_SEG_REG(regs->fs);
GDB_SEG_REG(regs->gs);
#undef GDB_SEG_REG
gdb_send_packet(ctx);
}
void
gdb_arch_write_reg_array(struct cpu_user_regs *regs, const char* buf,
struct gdb_context *ctx)
{
/* XXX TODO */
gdb_send_reply("E02", ctx);
}
void
gdb_arch_read_reg(unsigned long regnum, struct cpu_user_regs *regs,
struct gdb_context *ctx)
{
gdb_send_reply("", ctx);
}
/* Like copy_from_user, but safe to call with interrupts disabled.
Trust me, and don't look behind the curtain. */
unsigned
gdb_arch_copy_from_user(void *dest, const void *src, unsigned len)
{
int __d0, __d1, __d2;
ASSERT(!local_irq_is_enabled());
__asm__ __volatile__(
"1: rep; movsb\n"
"2:\n"
".section .fixup,\"ax\"\n"
"3: addl $4, %%esp\n"
" jmp 2b\n"
".previous\n"
".section __pre_ex_table,\"a\"\n"
" .align 4\n"
" .long 1b,3b\n"
".previous\n"
".section __ex_table,\"a\"\n"
" .align 4\n"
" .long 1b,2b\n"
".previous\n"
: "=c"(__d2), "=D" (__d0), "=S" (__d1)
: "0"(len), "1"(dest), "2"(src)
: "memory");
ASSERT(!local_irq_is_enabled());
return __d2;
}
unsigned int
gdb_arch_copy_to_user(void *dest, const void *src, unsigned len)
{
/* XXX */
return len;
}
void
gdb_arch_resume(struct cpu_user_regs *regs,
unsigned long addr, unsigned long type,
struct gdb_context *ctx)
{
/* XXX */
if (type == GDB_STEP) {
gdb_send_reply("S01", ctx);
}
}
void
gdb_arch_print_state(struct cpu_user_regs *regs)
{
/* XXX */
}
void
gdb_arch_enter(struct cpu_user_regs *regs)
{
/* nothing */
}
void
gdb_arch_exit(struct cpu_user_regs *regs)
{
/* nothing */
}
/*
* Local variables:
* mode: C
* c-set-style: "BSD"
* c-basic-offset: 4
* tab-width: 4
* End:
*/
+3
View File
@@ -4,6 +4,9 @@ include $(BASEDIR)/Rules.mk
ifneq ($(perfc),y)
OBJS := $(subst perfc.o,,$(OBJS))
endif
ifneq ($(crash_debug),y)
OBJS := $(patsubst gdbstub.o,,$(OBJS))
endif
default: common.o
common.o: $(OBJS)
+593
View File
@@ -0,0 +1,593 @@
/*
* Copyright (C) 2005 Jimi Xenidis <jimix@watson.ibm.com>, IBM Corporation
* Copyright (C) 2006 Isaku Yamahata <yamahata at valinux co jp>
* VA Linux Systems Japan. K.K.
*
* gdbstub arch neutral part
* Based on x86 cdb (xen/arch/x86/cdb.c) and ppc gdbstub(xen/common/gdbstub.c)
* But extensively modified.
*
* 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
*/
/*
* gdbstub: implements the architecture independant parts of the
* gdb remote protocol.
*/
/* We try to avoid assuming much about what the rest of the system is
doing. In particular, dynamic memory allocation is out of the
question. */
/* Resuming after we've stopped used to work, but more through luck
than any actual intention. It doesn't at the moment. */
#include <xen/lib.h>
#include <asm/uaccess.h>
#include <xen/spinlock.h>
#include <xen/serial.h>
#include <xen/irq.h>
#include <asm/debugger.h>
#include <xen/init.h>
#include <xen/smp.h>
#include <xen/console.h>
/* Printk isn't particularly safe just after we've trapped to the
debugger. so avoid it. */
#define dbg_printk(...)
/*#define dbg_printk(...) printk(__VA_ARGS__)*/
#define GDB_RETRY_MAX 10
static char opt_gdb[30] = "none";
string_param("gdb", opt_gdb);
/* value <-> char (de)serialzers */
char
hex2char(unsigned long x)
{
const char array[] = "0123456789abcdef";
return array[x & 15];
}
int
char2hex(unsigned char c)
{
if ( (c >= '0') && (c <= '9') )
return c - '0';
else if ( (c >= 'a') && (c <= 'f') )
return c - 'a' + 10;
else if ( (c >= 'A') && (c <= 'F') )
return c - 'A' + 10;
else
BUG();
return -1;
}
char
str2hex(const char *str)
{
return (char2hex(str[0]) << 4) | char2hex(str[1]);
}
unsigned long
str2ulong(const char *str, unsigned long bytes)
{
unsigned long x = 0;
unsigned long i = 0;
while ( *str && (i < (bytes * 2)) )
{
x <<= 4;
x += char2hex(*str);
++str;
++i;
}
return x;
}
/* gdb io wrappers */
static signed long
gdb_io_write(const char *buf, unsigned long len, struct gdb_context *ctx)
{
int i;
for ( i = 0; i < len; i++ )
serial_putc(ctx->serhnd, buf[i]);
return i;
}
static int
gdb_io_write_char(u8 data, struct gdb_context *ctx)
{
return gdb_io_write((char*)&data, 1, ctx);
}
static unsigned char
gdb_io_read(struct gdb_context *ctx)
{
return serial_getc(ctx->serhnd);
}
/* Receive a command. Returns -1 on csum error, 0 otherwise. */
/* Does not acknowledge. */
static int
attempt_receive_packet(struct gdb_context *ctx)
{
u8 csum;
u8 received_csum;
u8 ch;
/* Skip over everything up to the first '$' */
while ( (ch = gdb_io_read(ctx)) != '$' )
continue;
csum = 0;
for ( ctx->in_bytes = 0;
ctx->in_bytes < sizeof(ctx->in_buf);
ctx->in_bytes++ )
{
ch = gdb_io_read(ctx);
if ( ch == '#' )
break;
ctx->in_buf[ctx->in_bytes] = ch;
csum += ch;
}
if ( ctx->in_bytes == sizeof(ctx->in_buf) )
{
dbg_printk("WARNING: GDB sent a stupidly big packet.\n");
return -1;
}
ctx->in_buf[ctx->in_bytes] = '\0';
received_csum = char2hex(gdb_io_read(ctx)) * 16 +
char2hex(gdb_io_read(ctx));
return (received_csum == csum) ? 0 : -1;
}
/* Receive a command, discarding up to ten packets with csum
* errors. Acknowledges all received packets. */
static int
receive_command(struct gdb_context *ctx)
{
int r, count = 0;
count = 0;
do {
r = attempt_receive_packet(ctx);
gdb_io_write_char((r < 0) ? '-' : '+', ctx);
count++;
} while ( (r < 0) && (count < GDB_RETRY_MAX) );
return r;
}
/* routines to send reply packets */
static void
gdb_start_packet(struct gdb_context *ctx)
{
ctx->out_buf[0] = '$';
ctx->out_offset = 1;
ctx->out_csum = 0;
}
static void
gdb_write_to_packet_char(u8 data, struct gdb_context *ctx)
{
ctx->out_csum += data;
ctx->out_buf[ctx->out_offset] = data;
ctx->out_offset++;
}
void
gdb_write_to_packet(const char *buf, int count, struct gdb_context *ctx)
{
int x;
for ( x = 0; x < count; x++ )
gdb_write_to_packet_char(buf[x], ctx);
}
void
gdb_write_to_packet_str(const char *buf, struct gdb_context *ctx)
{
gdb_write_to_packet(buf, strlen(buf), ctx);
}
void
gdb_write_to_packet_hex(unsigned long x, int int_size, struct gdb_context *ctx)
{
char buf[sizeof(unsigned long) * 2 + 1];
int i = sizeof(unsigned long) * 2;
int width = int_size * 2;
buf[sizeof(unsigned long) * 2] = 0;
switch ( int_size )
{
case sizeof(u8):
case sizeof(u16):
case sizeof(u32):
case sizeof(u64):
break;
default:
dbg_printk("WARNING: %s x: 0x%lx int_size: %d\n",
__func__, x, int_size);
break;
}
do {
buf[--i] = hex2char(x & 15);
x >>= 4;
} while ( x );
while ( (i + width) > (sizeof(unsigned long) * 2) )
buf[--i] = '0';
gdb_write_to_packet(&buf[i], width, ctx);
}
static int
gdb_check_ack(struct gdb_context *ctx)
{
u8 c = gdb_io_read(ctx);
switch ( c )
{
case '+':
return 1;
case '-':
return 0;
default:
printk("Bad ack: %c\n", c);
return 0;
}
}
/* Return 0 if the reply was successfully received, !0 otherwise. */
void
gdb_send_packet(struct gdb_context *ctx)
{
char buf[3];
int count;
sprintf(buf, "%.02x\n", ctx->out_csum);
gdb_write_to_packet_char('#', ctx);
gdb_write_to_packet(buf, 2, ctx);
count = 0;
do {
gdb_io_write(ctx->out_buf, ctx->out_offset, ctx);
} while ( !gdb_check_ack(ctx) && (count++ < GDB_RETRY_MAX) );
if ( count == GDB_RETRY_MAX )
dbg_printk("WARNING: %s reached max retry %d\n",
__func__, GDB_RETRY_MAX);
}
void
gdb_send_reply(const char *buf, struct gdb_context *ctx)
{
gdb_start_packet(ctx);
gdb_write_to_packet_str(buf, ctx);
gdb_send_packet(ctx);
}
/* arch neutral command handlers */
static void
gdb_cmd_signum(struct gdb_context *ctx)
{
gdb_write_to_packet_char('S', ctx);
gdb_write_to_packet_hex(ctx->signum, sizeof(ctx->signum), ctx);
gdb_send_packet(ctx);
}
static void
gdb_cmd_read_mem(unsigned long addr, unsigned long length,
struct gdb_context *ctx)
{
int x, r;
unsigned char val;
dbg_printk("Memory read starting at %lx, length %lx.\n", addr,
length);
for ( x = 0; x < length; x++ )
{
r = gdb_arch_copy_from_user(&val, (void *)(addr + x), 1);
if ( r != 0 )
{
dbg_printk("Error reading from %lx.\n", addr + x);
break;
}
gdb_write_to_packet_hex(val, sizeof(val), ctx);
}
if ( x == 0 )
gdb_write_to_packet_str("E05", ctx);
dbg_printk("Read done.\n");
gdb_send_packet(ctx);
}
static void
gdb_cmd_write_mem(unsigned long addr, unsigned long length,
const char *buf, struct gdb_context *ctx)
{
int x, r;
unsigned char val;
dbg_printk("Memory write starting at %lx, length %lx.\n", addr, length);
for ( x = 0; x < length; x++, addr++, buf += 2 )
{
val = str2ulong(buf, sizeof(val));
r = gdb_arch_copy_to_user((void*)addr, (void*)&val, 1);
if ( r != 0 )
{
dbg_printk("Error writing to %lx.\n", addr);
break;
}
}
gdb_write_to_packet_str((x != length) ? "OK" : "E11", ctx);
dbg_printk("Write done.\n");
gdb_send_packet(ctx);
}
/* command dispatcher */
static int
process_command(struct cpu_user_regs *regs, struct gdb_context *ctx)
{
char *ptr;
unsigned long addr, length;
int resume = 0;
/* XXX check ctx->in_bytes >= 2 or similar. */
gdb_start_packet(ctx);
switch ( ctx->in_buf[0] )
{
case '?': /* query signal number */
gdb_cmd_signum(ctx);
break;
case 'H': /* thread operations */
gdb_send_reply("OK", ctx);
break;
case 'g': /* Read registers */
gdb_arch_read_reg_array(regs, ctx);
ASSERT(!local_irq_is_enabled());
break;
case 'G': /* Write registers */
gdb_arch_write_reg_array(regs, ctx->in_buf + 1, ctx);
break;
case 'm': /* Read memory */
addr = simple_strtoul(ctx->in_buf + 1, &ptr, 16);
if ( (ptr == (ctx->in_buf + 1)) || (ptr[0] != ',') )
{
gdb_send_reply("E03", ctx);
return 0;
}
length = simple_strtoul(ptr + 1, &ptr, 16);
if ( ptr[0] != 0 )
{
gdb_send_reply("E04", ctx);
return 0;
}
gdb_cmd_read_mem(addr, length, ctx);
ASSERT(!local_irq_is_enabled());
break;
case 'M': /* Write memory */
addr = simple_strtoul(ctx->in_buf + 1, &ptr, 16);
if ( (ptr == (ctx->in_buf + 1)) || (ptr[0] != ':') )
{
gdb_send_reply("E03", ctx);
return 0;
}
length = simple_strtoul(ptr + 1, &ptr, 16);
gdb_cmd_write_mem(addr, length, ptr, ctx);
break;
case 'p': /* read register */
addr = simple_strtoul(ctx->in_buf + 1, &ptr, 16);
if ( ptr == (ctx->in_buf + 1) )
{
gdb_send_reply("E03", ctx);
return 0;
}
if ( ptr[0] != 0 )
{
gdb_send_reply("E04", ctx);
return 0;
}
gdb_arch_read_reg(addr, regs, ctx);
break;
case 'Z': /* We need to claim to support these or gdb
won't let you continue the process. */
case 'z':
gdb_send_reply("OK", ctx);
break;
case 'D':
ctx->currently_attached = 0;
gdb_send_reply("OK", ctx);
/* fall through */
case 'k':
ctx->connected = 0;
/* fall through */
case 's': /* Single step */
case 'c': /* Resume at current address */
{
unsigned long addr = ~((unsigned long)0);
unsigned long type = GDB_CONTINUE;
if ( ctx->in_buf[0] == 's' )
type = GDB_STEP;
if ( ((ctx->in_buf[0] == 's') || (ctx->in_buf[0] == 'c')) &&
ctx->in_buf[1] )
addr = str2ulong(&ctx->in_buf[1], sizeof(unsigned long));
if ( ctx->in_buf[0] != 'D' )
ctx->currently_attached = 1;
resume = 1;
gdb_arch_resume(regs, addr, type, ctx);
break;
}
default:
gdb_send_reply("", ctx);
break;
}
return resume;
}
static struct gdb_context
__gdb_ctx = {
.serhnd = -1,
.currently_attached = 0,
.running = ATOMIC_INIT(1),
.connected = 0,
.signum = 1,
.in_bytes = 0,
.out_offset = 0,
.out_csum = 0,
};
static struct gdb_context *gdb_ctx = &__gdb_ctx;
/* trap handler: main entry point */
int
__trap_to_gdb(struct cpu_user_regs *regs, unsigned long cookie)
{
int resume = 0;
int r;
unsigned flags;
if ( gdb_ctx->serhnd < 0 )
{
dbg_printk("Debugger not ready yet.\n");
return 0;
}
/* We rely on our caller to ensure we're only on one processor
* at a time... We should probably panic here, but given that
* we're a debugger we should probably be a little tolerant of
* things going wrong. */
/* We don't want to use a spin lock here, because we're doing
two distinct things:
1 -- we don't want to run on more than one processor at a time,
and
2 -- we want to do something sensible if we re-enter ourselves.
Spin locks are good for 1, but useless for 2. */
if ( !atomic_dec_and_test(&gdb_ctx->running) )
{
printk("WARNING WARNING WARNING: Avoiding recursive gdb.\n");
atomic_inc(&gdb_ctx->running);
return 0;
}
if ( !gdb_ctx->connected )
{
printk("GDB connection activated\n");
gdb_arch_print_state(regs);
gdb_ctx->connected = 1;
}
smp_send_stop();
/* Try to make things a little more stable by disabling
interrupts while we're here. */
local_irq_save(flags);
watchdog_disable();
console_start_sync();
/* Shouldn't really do this, but otherwise we stop for no
obvious reason, which is Bad */
printk("Waiting for GDB to attach to Gdb\n");
gdb_arch_enter(regs);
gdb_ctx->signum = gdb_arch_signal_num(regs, cookie);
/* If gdb is already attached, tell it we've stopped again. */
if ( gdb_ctx->currently_attached )
{
gdb_start_packet(gdb_ctx);
gdb_cmd_signum(gdb_ctx);
}
while ( resume == 0 )
{
ASSERT(!local_irq_is_enabled());
r = receive_command(gdb_ctx);
ASSERT(!local_irq_is_enabled());
if ( r < 0 )
{
dbg_printk("GDB disappeared, trying to resume Xen...\n");
resume = 1;
}
else
{
ASSERT(!local_irq_is_enabled());
resume = process_command(regs, gdb_ctx);
ASSERT(!local_irq_is_enabled());
}
}
gdb_arch_exit(regs);
console_end_sync();
watchdog_enable();
atomic_inc(&gdb_ctx->running);
local_irq_restore(flags);
return 0;
}
/*
* initialization
* XXX TODO
* This should be an explicit call from architecture code.
* initcall is far too late for some early debugging, and only the
* architecture code knows when this call can be made.
*/
static int
initialize_gdb(void)
{
if ( !strcmp(opt_gdb, "none") )
return 0;
gdb_ctx->serhnd = serial_parse_handle(opt_gdb);
if ( gdb_ctx->serhnd == -1 )
panic("Can't parse %s as GDB serial info.\n", opt_gdb);
printk("Gdb initialised.\n");
return 0;
}
__initcall(initialize_gdb);
/*
* Local variables:
* mode: C
* c-set-style: "BSD"
* c-basic-offset: 4
* tab-width: 4
* End:
*/
+6 -4
View File
@@ -42,19 +42,19 @@
#if defined(CRASH_DEBUG)
extern int __trap_to_cdb(struct cpu_user_regs *r);
#include <xen/gdbstub.h>
#define __debugger_trap_entry(_v, _r) (0)
static inline int __debugger_trap_fatal(
unsigned int vector, struct cpu_user_regs *regs)
{
(void)__trap_to_cdb(regs);
(void)__trap_to_gdb(regs, vector);
return (vector == TRAP_int3); /* int3 is harmless */
}
/* Int3 is a trivial way to gather cpu_user_regs context. */
#define __debugger_trap_immediate() __asm__ __volatile__ ( "int3" );
#define debugger_trap_immediate() __asm__ __volatile__ ( "int3" );
#elif 0
@@ -73,7 +73,7 @@ static inline int __debugger_trap_fatal(
}
/* Int3 is a trivial way to gather cpu_user_regs context. */
#define __debugger_trap_immediate() __asm__ __volatile__ ( "int3" )
#define debugger_trap_immediate() __asm__ __volatile__ ( "int3" )
#else
@@ -100,6 +100,8 @@ static inline int debugger_trap_entry(
}
#define debugger_trap_fatal(v, r) (__debugger_trap_fatal(v, r))
#ifndef debugger_trap_immediate
#define debugger_trap_immediate() (__debugger_trap_immediate())
#endif
#endif /* __X86_DEBUGGER_H__ */
+96
View File
@@ -0,0 +1,96 @@
/*
* Copyright (C) 2005 Hollis Blanchard <hollisb@us.ibm.com>, IBM Corporation
* Copyright (C) 2006 Isaku Yamahata <yamahata at valinux co jp>
* VA Linux Systems Japan. K.K.
*
* 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
*/
#ifndef __XEN_GDBSTUB_H__
#define __XEN_GDBSTUB_H__
/* value <-> char (de)serialzers for arch specific gdb backends */
char hex2char(unsigned long x);
int char2hex(unsigned char c);
char str2hex(const char *str);
unsigned long str2ulong(const char *str, unsigned long bytes);
struct gdb_context {
int serhnd;
int currently_attached:1;
atomic_t running;
unsigned long connected;
u8 signum;
char in_buf[PAGE_SIZE];
unsigned long in_bytes;
char out_buf[PAGE_SIZE];
unsigned long out_offset;
u8 out_csum;
};
/* interface to arch specific routines */
void gdb_write_to_packet(
const char *buf, int count, struct gdb_context *ctx);
void gdb_write_to_packet_hex(
unsigned long x, int int_size, struct gdb_context *ctx);
void gdb_send_packet(struct gdb_context *ctx);
void gdb_send_reply(const char *buf, struct gdb_context *ctx);
/* gdb stub trap handler: entry point */
int __trap_to_gdb(struct cpu_user_regs *regs, unsigned long cookie);
/* arch specific routines */
u16 gdb_arch_signal_num(
struct cpu_user_regs *regs, unsigned long cookie);
void gdb_arch_read_reg_array(
struct cpu_user_regs *regs, struct gdb_context *ctx);
void gdb_arch_write_reg_array(
struct cpu_user_regs *regs, const char* buf, struct gdb_context *ctx);
void gdb_arch_read_reg(
unsigned long regnum, struct cpu_user_regs *regs, struct gdb_context *ctx);
unsigned int gdb_arch_copy_from_user(
void *dest, const void *src, unsigned len);
unsigned int gdb_arch_copy_to_user(
void *dest, const void *src, unsigned len);
void gdb_arch_resume(
struct cpu_user_regs *regs, unsigned long addr,
unsigned long type, struct gdb_context *ctx);
void gdb_arch_print_state(struct cpu_user_regs *regs);
void gdb_arch_enter(struct cpu_user_regs *regs);
void gdb_arch_exit(struct cpu_user_regs *regs);
#define GDB_CONTINUE 0
#define GDB_STEP 1
#define SIGILL 4
#define SIGTRAP 5
#define SIGBUS 7
#define SIGFPE 8
#define SIGSEGV 11
#define SIGALRM 14
#define SIGTERM 15
#endif /* __XEN_GDBSTUB_H__ */
/*
* Local variables:
* mode: C
* c-set-style: "BSD"
* c-basic-offset: 4
* tab-width: 4
* End:
*/
Reference in new issue
Block a user
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.