diff -Nur --exclude='*.o' --exclude='*.s' --exclude='*.swp' --exclude=compile.h --exclude=serial.c xen-unstable.hg/xen/arch/ia64/xen/gdbstub.c xen-unstable.hg.new/xen/arch/ia64/xen/gdbstub.c --- xen-unstable.hg/xen/arch/ia64/xen/gdbstub.c 2007-12-20 12:44:41.000000000 -0800 +++ xen-unstable.hg.new/xen/arch/ia64/xen/gdbstub.c 2007-12-20 12:44:13.000000000 -0800 @@ -62,6 +62,13 @@ } void +gdb_arch_write_reg(unsigned long regnum, unsigned long val, + struct cpu_user_regs *regs, struct gdb_context *ctx) +{ + gdb_send_reply("", ctx); +} + +void gdb_arch_write_reg_array(struct cpu_user_regs *regs, const char* buf, struct gdb_context *ctx) { diff -Nur --exclude='*.o' --exclude='*.s' --exclude='*.swp' --exclude=compile.h --exclude=serial.c xen-unstable.hg/xen/arch/powerpc/gdbstub.c xen-unstable.hg.new/xen/arch/powerpc/gdbstub.c --- xen-unstable.hg/xen/arch/powerpc/gdbstub.c 2007-12-20 12:44:41.000000000 -0800 +++ xen-unstable.hg.new/xen/arch/powerpc/gdbstub.c 2007-12-20 12:42:50.000000000 -0800 @@ -132,6 +132,14 @@ gdb_send_packet(ctx); } +void +gdb_arch_write_reg(unsigned long regnum, unsigned long val, + struct cpu_user_regs *regs, struct gdb_context *ctx) +{ + unimplemented(); + gdb_send_reply("", ctx); +} + void gdb_arch_write_reg_array(struct cpu_user_regs *regs, const char *buf, struct gdb_context *ctx) diff -Nur --exclude='*.o' --exclude='*.s' --exclude='*.swp' --exclude=compile.h --exclude=serial.c xen-unstable.hg/xen/arch/x86/gdbstub.c xen-unstable.hg.new/xen/arch/x86/gdbstub.c --- xen-unstable.hg/xen/arch/x86/gdbstub.c 2007-12-20 12:44:41.000000000 -0800 +++ xen-unstable.hg.new/xen/arch/x86/gdbstub.c 2007-12-19 19:02:21.000000000 -0800 @@ -24,51 +24,7 @@ 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); + return 5; /* TRAP signal. see include/gdb/signals.h */ } /* @@ -87,17 +43,6 @@ return __copy_to_user(dest, src, 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) { diff -Nur --exclude='*.o' --exclude='*.s' --exclude='*.swp' --exclude=compile.h --exclude=serial.c xen-unstable.hg/xen/arch/x86/traps.c xen-unstable.hg.new/xen/arch/x86/traps.c --- xen-unstable.hg/xen/arch/x86/traps.c 2007-12-20 12:44:41.000000000 -0800 +++ xen-unstable.hg.new/xen/arch/x86/traps.c 2007-12-19 09:21:29.000000000 -0800 @@ -800,9 +800,8 @@ if ( !guest_mode(regs) ) { - DEBUGGER_trap_fatal(TRAP_int3, regs); - show_execution_state(regs); - panic("FATAL TRAP: vector = 3 (Int3)\n"); + debugger_trap_fatal(TRAP_int3, regs); + return; } do_guest_trap(TRAP_int3, regs, 0); @@ -2753,7 +2752,7 @@ set_intr_gate(TRAP_divide_error,÷_error); set_intr_gate(TRAP_debug,&debug); set_intr_gate(TRAP_nmi,&nmi); - set_system_gate(TRAP_int3,&int3); /* usable from all privileges */ + set_intr_gate(TRAP_int3,&int3); /* usable from all privileges */ set_system_gate(TRAP_overflow,&overflow); /* usable from all privileges */ set_intr_gate(TRAP_bounds,&bounds); set_intr_gate(TRAP_invalid_op,&invalid_op); diff -Nur --exclude='*.o' --exclude='*.s' --exclude='*.swp' --exclude=compile.h --exclude=serial.c xen-unstable.hg/xen/arch/x86/x86_32/gdbstub.c xen-unstable.hg.new/xen/arch/x86/x86_32/gdbstub.c --- xen-unstable.hg/xen/arch/x86/x86_32/gdbstub.c 1969-12-31 16:00:00.000000000 -0800 +++ xen-unstable.hg.new/xen/arch/x86/x86_32/gdbstub.c 2007-12-19 09:21:29.000000000 -0800 @@ -0,0 +1,75 @@ +/* + * 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 + + +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); +} + +/* + * Local variables: + * mode: C + * c-set-style: "BSD" + * c-basic-offset: 4 + * tab-width: 4 + * End: + */ diff -Nur --exclude='*.o' --exclude='*.s' --exclude='*.swp' --exclude=compile.h --exclude=serial.c xen-unstable.hg/xen/arch/x86/x86_64/gdbstub.c xen-unstable.hg.new/xen/arch/x86/x86_64/gdbstub.c --- xen-unstable.hg/xen/arch/x86/x86_64/gdbstub.c 1969-12-31 16:00:00.000000000 -0800 +++ xen-unstable.hg.new/xen/arch/x86/x86_64/gdbstub.c 2007-12-20 12:41:26.000000000 -0800 @@ -0,0 +1,164 @@ +/* + * x86_64 -specific gdb stub routines + * + * Copyright (C) 2007 Dan Doucette ddoucette@xxxxxxxxxxxx + * + * 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 + +#define GDB_REG64(r) gdb_write_to_packet_hex(r, sizeof(u64), ctx) +#define GDB_REG32(r) gdb_write_to_packet_hex(r, sizeof(u32), ctx) + +void +gdb_arch_read_reg_array(struct cpu_user_regs *regs, struct gdb_context *ctx) +{ + GDB_REG64(regs->rax); + GDB_REG64(regs->rbx); + GDB_REG64(regs->rcx); + GDB_REG64(regs->rdx); + GDB_REG64(regs->rsi); + GDB_REG64(regs->rdi); + GDB_REG64(regs->rbp); + GDB_REG64(regs->rsp); + + GDB_REG64(regs->r8); + GDB_REG64(regs->r9); + GDB_REG64(regs->r10); + GDB_REG64(regs->r11); + GDB_REG64(regs->r12); + GDB_REG64(regs->r13); + GDB_REG64(regs->r14); + GDB_REG64(regs->r15); + + GDB_REG64(regs->rip); + GDB_REG32(regs->eflags); + + GDB_REG32(regs->cs); + GDB_REG32(regs->ss); + GDB_REG32(regs->ds); + GDB_REG32(regs->es); + GDB_REG32(regs->fs); + GDB_REG32(regs->gs); + + gdb_send_packet(ctx); +} + +void +gdb_arch_write_reg_array(struct cpu_user_regs *regs, const char* buf, + struct gdb_context *ctx) +{ + gdb_send_reply("", ctx); +} + +void +gdb_arch_read_reg(unsigned long regnum, struct cpu_user_regs *regs, + struct gdb_context *ctx) +{ + switch (regnum) + { + case 0: GDB_REG64(regs->rax); break; + case 1: GDB_REG64(regs->rbx); break; + case 2: GDB_REG64(regs->rcx); break; + case 3: GDB_REG64(regs->rdx); break; + case 4: GDB_REG64(regs->rsi); break; + case 5: GDB_REG64(regs->rdi); break; + case 6: GDB_REG64(regs->rbp); break; + case 7: GDB_REG64(regs->rsp); break; + + case 8: GDB_REG64(regs->r8); break; + case 9: GDB_REG64(regs->r9); break; + case 10: GDB_REG64(regs->r10); break; + case 11: GDB_REG64(regs->r11); break; + case 12: GDB_REG64(regs->r12); break; + case 13: GDB_REG64(regs->r13); break; + case 14: GDB_REG64(regs->r14); break; + case 15: GDB_REG64(regs->r15); break; + + case 16: GDB_REG64(regs->rip); break; + case 17: GDB_REG32(regs->rflags); break; + case 18: GDB_REG32(regs->cs); break; + case 19: GDB_REG32(regs->ss); break; + case 20: GDB_REG32(regs->ds); break; + case 21: GDB_REG32(regs->es); break; + case 22: GDB_REG32(regs->fs); break; + case 23: GDB_REG32(regs->gs); break; + default: + GDB_REG64(0xbaadf00ddeadbeef); + break; + } + gdb_send_packet(ctx); +} + +void +gdb_arch_write_reg(unsigned long regnum, unsigned long val, + struct cpu_user_regs *regs, struct gdb_context *ctx) +{ + switch (regnum) + { + case 0: regs->rax = val; break; + case 1: regs->rbx = val; break; + case 2: regs->rcx = val; break; + case 3: regs->rdx = val; break; + case 4: regs->rsi = val; break; + case 5: regs->rdi = val; break; + case 6: regs->rbp = val; break; + case 7: regs->rsp = val; break; + + case 8: regs->r8 = val; break; + case 9: regs->r9 = val; break; + case 10: regs->r10 = val; break; + case 11: regs->r11 = val; break; + case 12: regs->r12 = val; break; + case 13: regs->r13 = val; break; + case 14: regs->r14 = val; break; + case 15: regs->r15 = val; break; + + case 16: regs->rip = val; break; + case 17: regs->rflags = (u32)val; break; + case 18: regs->cs = (u16)val; break; + case 19: regs->ss = (u16)val; break; + case 20: regs->ds = (u16)val; break; + case 21: regs->es = (u16)val; break; + case 22: regs->fs = (u16)val; break; + case 23: regs->gs = (u16)val; break; + default: + break; + } + gdb_send_reply("OK", ctx); +} + +void +gdb_arch_resume(struct cpu_user_regs *regs, + unsigned long addr, unsigned long type, + struct gdb_context *ctx) +{ + if ( addr != -1UL ) + { + regs->rip = addr; + } + + regs->rflags &= ~X86_EFLAGS_TF; + /* + * Set eflags.RF to ensure we do not re-enter. + */ + regs->rflags |= X86_EFLAGS_RF; + + /* + * Set the trap flag if we are single stepping. + */ + if ( type == GDB_STEP ) + regs->rflags |= X86_EFLAGS_TF; +} diff -Nur --exclude='*.o' --exclude='*.s' --exclude='*.swp' --exclude=compile.h --exclude=serial.c xen-unstable.hg/xen/arch/x86/x86_64/Makefile xen-unstable.hg.new/xen/arch/x86/x86_64/Makefile --- xen-unstable.hg/xen/arch/x86/x86_64/Makefile 2007-12-20 12:44:41.000000000 -0800 +++ xen-unstable.hg.new/xen/arch/x86/x86_64/Makefile 2007-12-19 09:21:29.000000000 -0800 @@ -5,6 +5,7 @@ obj-y += mm.o obj-y += traps.o +obj-$(crash_debug) += gdbstub.o obj-$(CONFIG_COMPAT) += compat.o obj-$(CONFIG_COMPAT) += compat_kexec.o obj-$(CONFIG_COMPAT) += domain.o diff -Nur --exclude='*.o' --exclude='*.s' --exclude='*.swp' --exclude=compile.h --exclude=serial.c xen-unstable.hg/xen/common/gdbstub.c xen-unstable.hg.new/xen/common/gdbstub.c --- xen-unstable.hg/xen/common/gdbstub.c 2007-12-20 12:44:41.000000000 -0800 +++ xen-unstable.hg.new/xen/common/gdbstub.c 2007-12-20 12:46:39.000000000 -0800 @@ -43,6 +43,7 @@ #include #include #include +#include #include /* Printk isn't particularly safe just after we've trapped to the @@ -52,6 +53,19 @@ #define GDB_RETRY_MAX 10 +struct gdb_cpu_info +{ + atomic_t paused; + atomic_t ack; +}; + +static struct gdb_cpu_info gdb_cpu[NR_CPUS]; +static atomic_t gdb_smp_paused_count; + +static void gdb_smp_pause(void); +static void gdb_smp_resume(void); + + static char opt_gdb[30] = "none"; string_param("gdb", opt_gdb); @@ -396,8 +410,9 @@ process_command(struct cpu_user_regs *regs, struct gdb_context *ctx) { const char *ptr; - unsigned long addr, length; + unsigned long addr, length, val; int resume = 0; + unsigned long type = GDB_CONTINUE; /* XXX check ctx->in_bytes >= 2 or similar. */ @@ -460,30 +475,40 @@ } gdb_arch_read_reg(addr, regs, ctx); break; + case 'P': /* write 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] != '=' ) + { + gdb_send_reply("E04", ctx); + return 0; + } + ptr++; + val = str2ulong(ptr, sizeof(unsigned long)); + gdb_arch_write_reg(addr, val, regs, ctx); + break; case 'D': + case 'k': gdbstub_detach(ctx); gdb_send_reply("OK", ctx); - /* fall through */ - case 'k': ctx->connected = 0; - /* fall through */ + resume = 1; + break; case 's': /* Single step */ + type = GDB_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 = ~((unsigned long)0); + + if ( ctx->in_buf[1] ) addr = str2ulong(&ctx->in_buf[1], sizeof(unsigned long)); - if ( ctx->in_buf[0] != 'D' ) - gdbstub_attach(ctx); + gdbstub_attach(ctx); resume = 1; gdb_arch_resume(regs, addr, type, ctx); break; - } - default: gdb_send_reply("", ctx); break; @@ -555,10 +580,8 @@ gdb_ctx->connected = 1; } - smp_send_stop(); + gdb_smp_pause(); - /* Try to make things a little more stable by disabling - interrupts while we're here. */ local_irq_save(flags); watchdog_disable(); @@ -587,6 +610,8 @@ } } while ( process_command(regs, gdb_ctx) == 0 ); + gdb_smp_resume(); + gdb_arch_exit(regs); console_end_sync(); watchdog_enable(); @@ -606,6 +631,95 @@ serial_start_sync(gdb_ctx->serhnd); } +static void gdb_pause_this_cpu(void *unused) +{ + unsigned long flags; + + local_irq_save(flags); + + atomic_set(&gdb_cpu[smp_processor_id()].ack, 1); + atomic_inc(&gdb_smp_paused_count); + + while (atomic_read(&gdb_cpu[smp_processor_id()].paused)) + mdelay(1); + + atomic_dec(&gdb_smp_paused_count); + atomic_set(&gdb_cpu[smp_processor_id()].ack, 0); + + /* Restore interrupts */ + local_irq_restore(flags); +} + +static void gdb_smp_pause(void) +{ + int timeout = 100; + int cpu; + + for_each_online_cpu(cpu) + { + atomic_set(&gdb_cpu[cpu].ack, 0); + atomic_set(&gdb_cpu[cpu].paused, 1); + } + + atomic_set(&gdb_smp_paused_count, 0); + + smp_call_function(gdb_pause_this_cpu, NULL, /* dont wait! */0, 0); + + /* Wait 100ms for all other CPUs to enter pause loop */ + while ( (atomic_read(&gdb_smp_paused_count) < (num_online_cpus() - 1)) + && (timeout-- > 0) ) + mdelay(1); + + if ( atomic_read(&gdb_smp_paused_count) < (num_online_cpus() - 1) ) + { + printk("GDB: Not all CPUs have paused, missing CPUs "); + for_each_online_cpu(cpu) + { + if ( cpu == smp_processor_id() ) + continue; + + if ( !atomic_read(&gdb_cpu[cpu].ack) ) + { + printk("%d ", cpu); + } + } + printk("\n"); + } +} + +static void gdb_smp_resume(void) +{ + int cpu; + int timeout = 100; + + for_each_online_cpu(cpu) + { + atomic_set(&gdb_cpu[cpu].paused, 0); + } + + /* Make sure all CPUs resume */ + while ( (atomic_read(&gdb_smp_paused_count) > 0) + && (timeout-- > 0) ) + mdelay(1); + + if ( atomic_read(&gdb_smp_paused_count) > 0 ) + { + printk("GDB: Not all CPUs have resumed execution, missing CPUs "); + for_each_online_cpu(cpu) + { + if ( cpu == smp_processor_id() ) + continue; + + if ( !atomic_read(&gdb_cpu[cpu].ack) ) + { + printk("%d ", cpu); + } + } + printk("\n"); + } +} + + /* * Local variables: * mode: C diff -Nur --exclude='*.o' --exclude='*.s' --exclude='*.swp' --exclude=compile.h --exclude=serial.c xen-unstable.hg/xen/include/xen/gdbstub.h xen-unstable.hg.new/xen/include/xen/gdbstub.h --- xen-unstable.hg/xen/include/xen/gdbstub.h 2007-12-20 12:44:42.000000000 -0800 +++ xen-unstable.hg.new/xen/include/xen/gdbstub.h 2007-12-19 09:21:29.000000000 -0800 @@ -69,6 +69,9 @@ 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); +void gdb_arch_write_reg( + unsigned long regnum, unsigned long val, 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(