# HG changeset patch
# User kaf24@xxxxxxxxxxxxxxxxxxxx
# Node ID 1b839e1b1de1b1fcf608fa3803b861f9f0672e4b
# Parent ef88c2db00ad5076594dc5921e14db89bdf6cb7e
Rename cdb to gdbstub and split it into arch dependent/neutral part.
Signed-off-by: Isaku Yamahata <yamahata@xxxxxxxxxxxxx>
Signed-off-by: Hollis Blanchard <hollisb@xxxxxxxxxx>
diff -r ef88c2db00ad -r 1b839e1b1de1 xen/arch/x86/Makefile
--- a/xen/arch/x86/Makefile Sat Jan 14 09:36:40 2006
+++ b/xen/arch/x86/Makefile Sat Jan 14 15:58:54 2006
@@ -32,7 +32,7 @@
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)
diff -r ef88c2db00ad -r 1b839e1b1de1 xen/common/Makefile
--- a/xen/common/Makefile Sat Jan 14 09:36:40 2006
+++ b/xen/common/Makefile Sat Jan 14 15:58:54 2006
@@ -3,6 +3,9 @@
ifneq ($(perfc),y)
OBJS := $(subst perfc.o,,$(OBJS))
+endif
+ifneq ($(crash_debug),y)
+OBJS := $(patsubst gdbstub.o,,$(OBJS))
endif
default: common.o
diff -r ef88c2db00ad -r 1b839e1b1de1 xen/include/asm-x86/debugger.h
--- a/xen/include/asm-x86/debugger.h Sat Jan 14 09:36:40 2006
+++ b/xen/include/asm-x86/debugger.h Sat Jan 14 15:58:54 2006
@@ -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 @@
}
/* 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 @@
}
#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__ */
diff -r ef88c2db00ad -r 1b839e1b1de1 xen/arch/x86/gdbstub.c
--- /dev/null Sat Jan 14 09:36:40 2006
+++ b/xen/arch/x86/gdbstub.c Sat Jan 14 15:58:54 2006
@@ -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:
+ */
diff -r ef88c2db00ad -r 1b839e1b1de1 xen/common/gdbstub.c
--- /dev/null Sat Jan 14 09:36:40 2006
+++ b/xen/common/gdbstub.c Sat Jan 14 15:58:54 2006
@@ -0,0 +1,593 @@
+/*
+ * Copyright (C) 2005 Jimi Xenidis <jimix@xxxxxxxxxxxxxx>, 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:
+ */
diff -r ef88c2db00ad -r 1b839e1b1de1 xen/include/xen/gdbstub.h
--- /dev/null Sat Jan 14 09:36:40 2006
+++ b/xen/include/xen/gdbstub.h Sat Jan 14 15:58:54 2006
@@ -0,0 +1,96 @@
+/*
+ * Copyright (C) 2005 Hollis Blanchard <hollisb@xxxxxxxxxx>, 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:
+ */
diff -r ef88c2db00ad -r 1b839e1b1de1 xen/arch/x86/cdb.c
--- a/xen/arch/x86/cdb.c Sat Jan 14 09:36:40 2006
+++ /dev/null Sat Jan 14 15:58:54 2006
@@ -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);
_______________________________________________
Xen-changelog mailing list
Xen-changelog@xxxxxxxxxxxxxxxxxxx
http://lists.xensource.com/xen-changelog
|