# HG changeset patch # User yamahata@xxxxxxxxxxxxx # Node ID a5aa872297f5a5195b2cef1bb4ac6f3cd9bd3195 # Parent ef88c2db00ad5076594dc5921e14db89bdf6cb7e clean up cdb. renamed cdb to gdbstub and split it into arch dependent/neutral part. Signed-off-by: Isaku Yamahata Signed-off-by: Hollis Blanchard diff -r ef88c2db00ad -r a5aa872297f5 xen/arch/x86/Makefile --- a/xen/arch/x86/Makefile Sat Jan 14 10:36:40 2006 +0100 +++ b/xen/arch/x86/Makefile Sat Jan 14 20:16:23 2006 +0900 @@ -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 a5aa872297f5 xen/common/Makefile --- a/xen/common/Makefile Sat Jan 14 10:36:40 2006 +0100 +++ b/xen/common/Makefile Sat Jan 14 20:16:23 2006 +0900 @@ -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 a5aa872297f5 xen/include/asm-x86/debugger.h --- a/xen/include/asm-x86/debugger.h Sat Jan 14 10:36:40 2006 +0100 +++ b/xen/include/asm-x86/debugger.h Sat Jan 14 20:16:23 2006 +0900 @@ -42,19 +42,19 @@ #if defined(CRASH_DEBUG) -extern int __trap_to_cdb(struct cpu_user_regs *r); +#include #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 a5aa872297f5 xen/arch/x86/gdbstub.c --- /dev/null Thu Jan 1 00:00:00 1970 +0000 +++ b/xen/arch/x86/gdbstub.c Sat Jan 14 20:16:23 2006 +0900 @@ -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 + * 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 + +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 a5aa872297f5 xen/common/gdbstub.c --- /dev/null Thu Jan 1 00:00:00 1970 +0000 +++ b/xen/common/gdbstub.c Sat Jan 14 20:16:23 2006 +0900 @@ -0,0 +1,585 @@ +/* + * Copyright (C) 2005 Jimi Xenidis , IBM Corporation + * Copyright (C) 2006 Isaku Yamahata + * 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. + */ +/* 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 +#include +#include +#include +#include +#include +#include +#include +#include + +/* 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)) != '$') + ; + 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)); + if (received_csum == csum) { + return 0; + } else { + return -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; + int count; + + count = 0; + do { + r = attempt_receive_packet(ctx); + if (r < 0) + gdb_io_write_char('-', ctx); + else + gdb_io_write_char('+', 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; + buf[sizeof (unsigned long) * 2] = 0; + int width = int_size * 2; + + 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 { + --i; + 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; + + 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; + unsigned char val; + int r; + + 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; + unsigned char val; + int r; + + 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; + } + } + if (x != length) { + gdb_write_to_packet_str("OK", ctx); + } else { + gdb_write_to_packet_str("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); + +/* + * XXX + */ +static unsigned long +_lib_gdb_xlate_ea(unsigned long eaddr) +{ + return eaddr; +} +extern unsigned long gdb_xlate_ea(unsigned long eaddr) + __attribute__ ((weak, alias("_lib_gdb_xlate_ea"))); + +/* + * Local variables: + * mode: C + * c-set-style: "BSD" + * c-basic-offset: 4 + * tab-width: 4 + * End: + */ diff -r ef88c2db00ad -r a5aa872297f5 xen/include/xen/gdb.h --- /dev/null Thu Jan 1 00:00:00 1970 +0000 +++ b/xen/include/xen/gdb.h Sat Jan 14 20:16:23 2006 +0900 @@ -0,0 +1,85 @@ +/* + * Copyright (C) 2005 Hollis Blanchard , IBM Corporation + * Copyright (C) 2006 Isaku Yamahata + * 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_GDB_H__ +#define __XEN_GDB_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_GDB_H__ */ + +/* + * Local variables: + * mode: C + * c-set-style: "BSD" + * c-basic-offset: 4 + * tab-width: 4 + * End: + */ diff -r ef88c2db00ad -r a5aa872297f5 xen/arch/x86/cdb.c --- a/xen/arch/x86/cdb.c Sat Jan 14 10:36:40 2006 +0100 +++ /dev/null Thu Jan 1 00:00:00 1970 +0000 @@ -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 -#include -#include -#include -#include -#include -#include -#include -#include -#include - -/* 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);