The new kgdb architecture specific handler registers and unregisters dynamically for exceptions depending on when you configure a kgdb I/O driver. Asside from initializing the exceptions earlier in the boot process, kgdb should have no impact on a device when it is compiled in so long as an I/O module is not configured for use. There have been quite a number of contributors during the existence of this patch (see arch/mips/kernel/kgdb.c). Signed-off-by: Jason Wessel <jason.wessel@xxxxxxxxxxxxx> --- arch/mips/Kconfig | 1 + arch/mips/kernel/Makefile | 1 + arch/mips/kernel/irq.c | 15 ++ arch/mips/kernel/kgdb.c | 295 ++++++++++++++++++++++++++++++++++ arch/mips/kernel/kgdb_handler.S | 339 +++++++++++++++++++++++++++++++++++++++ arch/mips/kernel/traps.c | 6 + include/asm-mips/asmmacro-32.h | 43 +++++ include/asm-mips/asmmacro-64.h | 99 ++++++++++++ include/asm-mips/kgdb.h | 54 ++++++ 9 files changed, 853 insertions(+), 0 deletions(-) create mode 100644 arch/mips/kernel/kgdb.c create mode 100644 arch/mips/kernel/kgdb_handler.S create mode 100644 include/asm-mips/kgdb.h diff --git a/arch/mips/Kconfig b/arch/mips/Kconfig index 1c07cb9..8c1b36e 100644 --- a/arch/mips/Kconfig +++ b/arch/mips/Kconfig @@ -3,6 +3,7 @@ config MIPS default y select HAVE_IDE select HAVE_OPROFILE + select HAVE_ARCH_KGDB # Horrible source of confusion. Die, die, die ... select EMBEDDED select RTC_LIB diff --git a/arch/mips/kernel/Makefile b/arch/mips/kernel/Makefile index 73ff048..a312d4c 100644 --- a/arch/mips/kernel/Makefile +++ b/arch/mips/kernel/Makefile @@ -71,6 +71,7 @@ obj-$(CONFIG_MIPS32_COMPAT) += linux32.o ptrace32.o signal32.o obj-$(CONFIG_MIPS32_N32) += binfmt_elfn32.o scall64-n32.o signal_n32.o obj-$(CONFIG_MIPS32_O32) += binfmt_elfo32.o scall64-o32.o +obj-$(CONFIG_KGDB) += kgdb_handler.o kgdb.o obj-$(CONFIG_PROC_FS) += proc.o obj-$(CONFIG_64BIT) += cpu-bugs64.o diff --git a/arch/mips/kernel/irq.c b/arch/mips/kernel/irq.c index 8acba08..7f1337b 100644 --- a/arch/mips/kernel/irq.c +++ b/arch/mips/kernel/irq.c @@ -21,11 +21,16 @@ #include <linux/sched.h> #include <linux/seq_file.h> #include <linux/kallsyms.h> +#include <linux/kgdb.h> #include <asm/atomic.h> #include <asm/system.h> #include <asm/uaccess.h> +#ifdef CONFIG_KGDB +int kgdb_early_setup; +#endif + static unsigned long irq_map[NR_IRQS / BITS_PER_LONG]; int allocate_irqno(void) @@ -133,5 +138,15 @@ void __init init_IRQ(void) for (i = 0; i < NR_IRQS; i++) set_irq_noprobe(i); +#ifdef CONFIG_KGDB + if (kgdb_early_setup) + return; +#endif + arch_init_irq(); + +#ifdef CONFIG_KGDB + if (!kgdb_early_setup) + kgdb_early_setup = 1; +#endif } diff --git a/arch/mips/kernel/kgdb.c b/arch/mips/kernel/kgdb.c new file mode 100644 index 0000000..d553d21 --- /dev/null +++ b/arch/mips/kernel/kgdb.c @@ -0,0 +1,295 @@ +/* + * arch/mips/kernel/kgdb.c + * + * Originally written by Glenn Engel, Lake Stevens Instrument Division + * + * Contributed by HP Systems + * + * Modified for SPARC by Stu Grossman, Cygnus Support. + * + * Modified for Linux/MIPS (and MIPS in general) by Andreas Busse + * Send complaints, suggestions etc. to <andy@xxxxxxxxxxxxxxx> + * + * Copyright (C) 1995 Andreas Busse + * + * Copyright (C) 2003 MontaVista Software Inc. + * Author: Jun Sun, jsun@xxxxxxxxxx or jsun@xxxxxxxxxx + * + * Copyright (C) 2004-2005 MontaVista Software Inc. + * Author: Manish Lachwani, mlachwani@xxxxxxxxxx or manish@xxxxxxxxxxxxxxxx + * + * Copyright (C) 2007-2008 Wind River Systems, Inc. + * Author: Jason Wessel, jason.wessel@xxxxxxxxxxxxx + * + * This file is licensed under the terms of the GNU General Public License + * version 2. This program is licensed "as is" without any warranty of any + * kind, whether express or implied. + */ + +#include <linux/string.h> +#include <linux/kernel.h> +#include <linux/sched.h> +#include <linux/smp.h> +#include <linux/spinlock.h> +#include <linux/delay.h> +#include <linux/ptrace.h> /* for linux pt_regs struct */ +#include <asm/system.h> +#include <linux/kgdb.h> +#include <linux/init.h> +#include <linux/kdebug.h> +#include <asm/inst.h> +#include <asm/gdb-stub.h> +#include <asm/cacheflush.h> + +static struct hard_trap_info { + unsigned char tt; /* Trap type code for MIPS R3xxx and R4xxx */ + unsigned char signo; /* Signal that we map this trap into */ +} hard_trap_info[] = { + { 6, SIGBUS }, /* instruction bus error */ + { 7, SIGBUS }, /* data bus error */ + { 9, SIGTRAP }, /* break */ +/* { 11, SIGILL }, */ /* CPU unusable */ + { 12, SIGFPE }, /* overflow */ + { 13, SIGTRAP }, /* trap */ + { 14, SIGSEGV }, /* virtual instruction cache coherency */ + { 15, SIGFPE }, /* floating point exception */ + { 23, SIGSEGV }, /* watch */ + { 31, SIGSEGV }, /* virtual data cache coherency */ + { 0, 0} /* Must be last */ +}; + +/* Save the normal trap handlers for user-mode traps. */ +void *saved_vectors[32]; + +static void kgdb_call_nmi_hook(void *ignored) +{ + kgdb_nmicallback(raw_smp_processor_id(), (void *)0); +} + +void kgdb_roundup_cpus(unsigned long flags) +{ + local_irq_enable(); + smp_call_function(kgdb_call_nmi_hook, NULL, NULL); + local_irq_disable(); +} + +static int compute_signal(int tt) +{ + struct hard_trap_info *ht; + + for (ht = hard_trap_info; ht->tt && ht->signo; ht++) + if (ht->tt == tt) + return ht->signo; + + return SIGHUP; /* default for things we don't know about */ +} + +/* + * Set up exception handlers for tracing and breakpoints + */ +void handle_exception(struct pt_regs *regs) +{ + int trap = (regs->cp0_cause & 0x7c) >> 2; + + if (fixup_exception(regs)) + return; + + if (atomic_read(&kgdb_active) != -1) + kgdb_nmicallback(smp_processor_id(), regs); + + if (atomic_read(&kgdb_setting_breakpoint)) + if ((trap == 9) && (regs->cp0_epc == (unsigned long)breakinst)) + regs->cp0_epc += 4; + + kgdb_handle_exception(0, compute_signal(trap), 0, regs); + + /* In SMP mode, __flush_cache_all does IPI */ + local_irq_enable(); + __flush_cache_all(); +} + +void set_debug_traps(void) +{ + struct hard_trap_info *ht; + unsigned long flags; + + local_irq_save(flags); + + for (ht = hard_trap_info; ht->tt && ht->signo; ht++) + saved_vectors[ht->tt] = set_except_vector(ht->tt, trap_low); + + local_irq_restore(flags); +} + +void pt_regs_to_gdb_regs(unsigned long *gdb_regs, struct pt_regs *regs) +{ + int reg; +#if (KGDB_GDB_REG_SIZE == 32) + u32 *ptr = (u32 *)gdb_regs; +#else + u64 *ptr = (u64 *)gdb_regs; +#endif + + for (reg = 0; reg < 32; reg++) + *(ptr++) = regs->regs[reg]; + + *(ptr++) = regs->cp0_status; + *(ptr++) = regs->lo; + *(ptr++) = regs->hi; + *(ptr++) = regs->cp0_badvaddr; + *(ptr++) = regs->cp0_cause; + *(ptr++) = regs->cp0_epc; + + return; +} + +void gdb_regs_to_pt_regs(unsigned long *gdb_regs, struct pt_regs *regs) +{ + + int reg; +#if (KGDB_GDB_REG_SIZE == 32) + const u32 *ptr = (u32 *)gdb_regs; +#else + const u64 *ptr = (u64 *)gdb_regs; +#endif + + for (reg = 0; reg < 32; reg++) + regs->regs[reg] = *(ptr++); + + regs->cp0_status = *(ptr++); + regs->lo = *(ptr++); + regs->hi = *(ptr++); + regs->cp0_badvaddr = *(ptr++); + regs->cp0_cause = *(ptr++); + regs->cp0_epc = *(ptr++); + + return; +} + +/* + * Similar to regs_to_gdb_regs() except that process is sleeping and so + * we may not be able to get all the info. + */ +void sleeping_thread_to_gdb_regs(unsigned long *gdb_regs, struct task_struct *p) +{ + int reg; + struct thread_info *ti = task_thread_info(p); + unsigned long ksp = (unsigned long)ti + THREAD_SIZE - 32; + struct pt_regs *regs = (struct pt_regs *)ksp - 1; +#if (KGDB_GDB_REG_SIZE == 32) + u32 *ptr = (u32 *)gdb_regs; +#else + u64 *ptr = (u64 *)gdb_regs; +#endif + + for (reg = 0; reg < 16; reg++) + *(ptr++) = regs->regs[reg]; + + /* S0 - S7 */ + for (reg = 16; reg < 24; reg++) + *(ptr++) = regs->regs[reg]; + + for (reg = 24; reg < 28; reg++) + *(ptr++) = 0; + + /* GP, SP, FP, RA */ + for (reg = 28; reg < 32; reg++) + *(ptr++) = regs->regs[reg]; + + *(ptr++) = regs->cp0_status; + *(ptr++) = regs->lo; + *(ptr++) = regs->hi; + *(ptr++) = regs->cp0_badvaddr; + *(ptr++) = regs->cp0_cause; + *(ptr++) = regs->cp0_epc; + + return; +} + +/* + * Calls linux_debug_hook before the kernel dies. If KGDB is enabled, + * then try to fall into the debugger + */ +static int kgdb_mips_notify(struct notifier_block *self, unsigned long cmd, + void *ptr) +{ + struct die_args *args = (struct die_args *)ptr; + struct pt_regs *regs = args->regs; + int trap = (regs->cp0_cause & 0x7c) >> 2; + + /* See if KGDB is interested. */ + if (user_mode(regs)) + /* Userpace events, ignore. */ + return NOTIFY_DONE; + + kgdb_handle_exception(trap, compute_signal(trap), 0, regs); + return NOTIFY_OK; +} + +static struct notifier_block kgdb_notifier = { + .notifier_call = kgdb_mips_notify, +}; + +/* + * Handle the 's' and 'c' commands + */ +int kgdb_arch_handle_exception(int vector, int signo, int err_code, + char *remcom_in_buffer, char *remcom_out_buffer, + struct pt_regs *regs) +{ + char *ptr; + unsigned long address; + int cpu = smp_processor_id(); + + switch (remcom_in_buffer[0]) { + case 's': + case 'c': + /* handle the optional parameter */ + ptr = &remcom_in_buffer[1]; + if (kgdb_hex2long(&ptr, &address)) + regs->cp0_epc = address; + + atomic_set(&kgdb_cpu_doing_single_step, -1); + if (remcom_in_buffer[0] == 's') + if (kgdb_contthread) + atomic_set(&kgdb_cpu_doing_single_step, cpu); + + return 0; + } + + return -1; +} + +struct kgdb_arch arch_kgdb_ops = { +#ifdef CONFIG_CPU_LITTLE_ENDIAN + .gdb_bpt_instr = {0xd}, +#else /* ! CONFIG_CPU_LITTLE_ENDIAN */ + .gdb_bpt_instr = {0x00, 0x00, 0x00, 0x0d}, +#endif +}; + +/* + * We use kgdb_early_setup so that functions we need to call now don't + * cause trouble when called again later. + */ +int kgdb_arch_init(void) +{ + /* Set our traps. */ + /* This needs to be done more finely grained again, paired in + * a before/after in kgdb_handle_exception(...) -- Tom */ + set_debug_traps(); + register_die_notifier(&kgdb_notifier); + + return 0; +} + +/** + * kgdb_arch_exit - Perform any architecture specific uninitalization. + * + * This function will handle the uninitalization of any architecture + * specific callbacks, for dynamic registration and unregistration. + */ +void kgdb_arch_exit(void) +{ + unregister_die_notifier(&kgdb_notifier); +} diff --git a/arch/mips/kernel/kgdb_handler.S b/arch/mips/kernel/kgdb_handler.S new file mode 100644 index 0000000..6d7028d --- /dev/null +++ b/arch/mips/kernel/kgdb_handler.S @@ -0,0 +1,339 @@ +/* + * arch/mips/kernel/kgdb_handler.S + * + * Copyright (C) 2007-2008 Wind River Systems, Inc. + * + * Copyright (C) 2004-2005 MontaVista Software Inc. + * Author: Manish Lachwani, mlachwani@xxxxxxxxxx or manish@xxxxxxxxxxxxxxxx + * + * This file is licensed under the terms of the GNU General Public + * version 2. This program is licensed "as is" without any warranty of any + * kind, whether express or implied. + */ + +/* + * Trap Handler for the new KGDB framework. The main KGDB handler is + * handle_exception that will be called from here + * + */ + +#include <linux/sys.h> + +#include <asm/asm.h> +#include <asm/errno.h> +#include <asm/mipsregs.h> +#include <asm/regdef.h> +#include <asm/stackframe.h> +#include <asm/gdb-stub.h> + +#ifdef CONFIG_32BIT +#define DMFC0 mfc0 +#define DMTC0 mtc0 +#define LDC1 lwc1 +#define SDC1 swc1 +#endif +#ifdef CONFIG_64BIT +#define DMFC0 dmfc0 +#define DMTC0 dmtc0 +#define LDC1 ldc1 +#define SDC1 sdc1 +#endif + +#include <asm/asmmacro.h> + +/* + * [jsun] We reserves about 2x GDB_FR_SIZE in stack. The lower (addressed) + * part is used to store registers and passed to exception handler. + * The upper part is reserved for "call func" feature where gdb client + * saves some of the regs, setups call frame and passes args. + * + * A trace shows about 200 bytes are used to store about half of all regs. + * The rest should be big enough for frame setup and passing args. + */ + +/* + * The low level trap handler + */ + .align 5 + NESTED(trap_low, GDB_FR_SIZE, sp) + .set noat + .set noreorder + + mfc0 k0, CP0_STATUS + sll k0, 3 /* extract cu0 bit */ + bltz k0, 1f + move k1, sp + + /* + * Called from user mode, go somewhere else. + */ +#if defined(CONFIG_32BIT) + lui k1, %hi(saved_vectors) + mfc0 k0, CP0_CAUSE + andi k0, k0, 0x7c + add k1, k1, k0 + lw k0, %lo(saved_vectors)(k1) +#elif defined(CONFIG_64BIT) && !defined(CONFIG_BUILD_ELF32) + DMFC0 k0, CP0_CAUSE + lui k1, %highest(saved_vectors) + andi k0, k0, 0x7c /* mask exception type */ + dsll k0, 1 /* turn into byte offset */ + daddiu k1, %higher(saved_vectors) + dsll k1, k1, 16 + daddiu k1, %hi(saved_vectors) + dsll k1, k1, 16 + daddu k1, k1, k0 + LONG_L k0, %lo(saved_vectors)(k1) +#else +#error "MIPS configuration is unsupported for kgdb!!" +#endif + jr k0 + nop +1: + move k0, sp + PTR_SUBU sp, k1, GDB_FR_SIZE*2 # see comment above + LONG_S k0, GDB_FR_REG29(sp) + LONG_S $2, GDB_FR_REG2(sp) + +/* + * First save the CP0 and special registers + */ + + mfc0 v0, CP0_STATUS + LONG_S v0, GDB_FR_STATUS(sp) + mfc0 v0, CP0_CAUSE + LONG_S v0, GDB_FR_CAUSE(sp) + DMFC0 v0, CP0_EPC + LONG_S v0, GDB_FR_EPC(sp) + DMFC0 v0, CP0_BADVADDR + LONG_S v0, GDB_FR_BADVADDR(sp) + mfhi v0 + LONG_S v0, GDB_FR_HI(sp) + mflo v0 + LONG_S v0, GDB_FR_LO(sp) + +/* + * Now the integer registers + */ + + LONG_S zero, GDB_FR_REG0(sp) /* I know... */ + LONG_S $1, GDB_FR_REG1(sp) + /* v0 already saved */ + LONG_S $3, GDB_FR_REG3(sp) + LONG_S $4, GDB_FR_REG4(sp) + LONG_S $5, GDB_FR_REG5(sp) + LONG_S $6, GDB_FR_REG6(sp) + LONG_S $7, GDB_FR_REG7(sp) + LONG_S $8, GDB_FR_REG8(sp) + LONG_S $9, GDB_FR_REG9(sp) + LONG_S $10, GDB_FR_REG10(sp) + LONG_S $11, GDB_FR_REG11(sp) + LONG_S $12, GDB_FR_REG12(sp) + LONG_S $13, GDB_FR_REG13(sp) + LONG_S $14, GDB_FR_REG14(sp) + LONG_S $15, GDB_FR_REG15(sp) + LONG_S $16, GDB_FR_REG16(sp) + LONG_S $17, GDB_FR_REG17(sp) + LONG_S $18, GDB_FR_REG18(sp) + LONG_S $19, GDB_FR_REG19(sp) + LONG_S $20, GDB_FR_REG20(sp) + LONG_S $21, GDB_FR_REG21(sp) + LONG_S $22, GDB_FR_REG22(sp) + LONG_S $23, GDB_FR_REG23(sp) + LONG_S $24, GDB_FR_REG24(sp) + LONG_S $25, GDB_FR_REG25(sp) + LONG_S $26, GDB_FR_REG26(sp) + LONG_S $27, GDB_FR_REG27(sp) + LONG_S $28, GDB_FR_REG28(sp) + /* sp already saved */ + LONG_S $30, GDB_FR_REG30(sp) + LONG_S $31, GDB_FR_REG31(sp) + + CLI /* disable interrupts */ + +/* + * Followed by the floating point registers + */ + mfc0 v0, CP0_STATUS /* FPU enabled? */ + srl v0, v0, 16 + andi v0, v0, (ST0_CU1 >> 16) + + beqz v0,3f /* disabled, skip */ + nop + + li t0, 0 +#ifdef CONFIG_64BIT + mfc0 t0, CP0_STATUS +#endif + fpu_save_double_kgdb sp t0 t1 # clobbers t1 + + +/* + * Current stack frame ptr + */ + +3: + LONG_S sp, GDB_FR_FRP(sp) + +/* + * CP0 registers (R4000/R4400 unused registers skipped) + */ + + mfc0 v0, CP0_INDEX + LONG_S v0, GDB_FR_CP0_INDEX(sp) + mfc0 v0, CP0_RANDOM + LONG_S v0, GDB_FR_CP0_RANDOM(sp) + DMFC0 v0, CP0_ENTRYLO0 + LONG_S v0, GDB_FR_CP0_ENTRYLO0(sp) + DMFC0 v0, CP0_ENTRYLO1 + LONG_S v0, GDB_FR_CP0_ENTRYLO1(sp) + DMFC0 v0, CP0_CONTEXT + LONG_S v0, GDB_FR_CP0_CONTEXT(sp) + mfc0 v0, CP0_PAGEMASK + LONG_S v0, GDB_FR_CP0_PAGEMASK(sp) + mfc0 v0, CP0_WIRED + LONG_S v0, GDB_FR_CP0_WIRED(sp) + DMFC0 v0, CP0_ENTRYHI + LONG_S v0, GDB_FR_CP0_ENTRYHI(sp) + mfc0 v0, CP0_PRID + LONG_S v0, GDB_FR_CP0_PRID(sp) + + .set at + +/* + * Continue with the higher level handler + */ + + move a0,sp + + jal handle_exception + nop + +/* + * Restore all writable registers, in reverse order + */ + + .set noat + + LONG_L v0, GDB_FR_CP0_ENTRYHI(sp) + LONG_L v1, GDB_FR_CP0_WIRED(sp) + DMTC0 v0, CP0_ENTRYHI + mtc0 v1, CP0_WIRED + LONG_L v0, GDB_FR_CP0_PAGEMASK(sp) + LONG_L v1, GDB_FR_CP0_ENTRYLO1(sp) + mtc0 v0, CP0_PAGEMASK + DMTC0 v1, CP0_ENTRYLO1 + LONG_L v0, GDB_FR_CP0_ENTRYLO0(sp) + LONG_L v1, GDB_FR_CP0_INDEX(sp) + DMTC0 v0, CP0_ENTRYLO0 + LONG_L v0, GDB_FR_CP0_CONTEXT(sp) + mtc0 v1, CP0_INDEX + DMTC0 v0, CP0_CONTEXT + + +/* + * Next, the floating point registers + */ + mfc0 v0, CP0_STATUS /* check if FPU is enabled */ + srl v0, v0, 16 + andi v0, v0, (ST0_CU1 >> 16) + + beqz v0, 3f /* disabled, skip */ + nop + + li t0, 0 +#ifdef CONFIG_64BIT + mfc0 t0, CP0_STATUS +#endif + fpu_restore_double_kgdb sp t0 t1 # clobbers t1 + + +/* + * Now the CP0 and integer registers + */ + +3: + mfc0 t0, CP0_STATUS + ori t0, 0x1f + xori t0, 0x1f + mtc0 t0, CP0_STATUS + + LONG_L v0, GDB_FR_STATUS(sp) + LONG_L v1, GDB_FR_EPC(sp) + mtc0 v0, CP0_STATUS + DMTC0 v1, CP0_EPC + LONG_L v0, GDB_FR_HI(sp) + LONG_L v1, GDB_FR_LO(sp) + mthi v0 + mtlo v1 + LONG_L $31, GDB_FR_REG31(sp) + LONG_L $30, GDB_FR_REG30(sp) + LONG_L $28, GDB_FR_REG28(sp) + LONG_L $27, GDB_FR_REG27(sp) + LONG_L $26, GDB_FR_REG26(sp) + LONG_L $25, GDB_FR_REG25(sp) + LONG_L $24, GDB_FR_REG24(sp) + LONG_L $23, GDB_FR_REG23(sp) + LONG_L $22, GDB_FR_REG22(sp) + LONG_L $21, GDB_FR_REG21(sp) + LONG_L $20, GDB_FR_REG20(sp) + LONG_L $19, GDB_FR_REG19(sp) + LONG_L $18, GDB_FR_REG18(sp) + LONG_L $17, GDB_FR_REG17(sp) + LONG_L $16, GDB_FR_REG16(sp) + LONG_L $15, GDB_FR_REG15(sp) + LONG_L $14, GDB_FR_REG14(sp) + LONG_L $13, GDB_FR_REG13(sp) + LONG_L $12, GDB_FR_REG12(sp) + LONG_L $11, GDB_FR_REG11(sp) + LONG_L $10, GDB_FR_REG10(sp) + LONG_L $9, GDB_FR_REG9(sp) + LONG_L $8, GDB_FR_REG8(sp) + LONG_L $7, GDB_FR_REG7(sp) + LONG_L $6, GDB_FR_REG6(sp) + LONG_L $5, GDB_FR_REG5(sp) + LONG_L $4, GDB_FR_REG4(sp) + LONG_L $3, GDB_FR_REG3(sp) + LONG_L $2, GDB_FR_REG2(sp) + LONG_L $1, GDB_FR_REG1(sp) +#if defined(CONFIG_CPU_R3000) || defined(CONFIG_CPU_TX39XX) + LONG_L k0, GDB_FR_EPC(sp) + LONG_L $29, GDB_FR_REG29(sp) /* Deallocate stack */ + jr k0 + rfe +#else + LONG_L sp, GDB_FR_REG29(sp) /* Deallocate stack */ + + .set mips3 + eret + .set mips0 +#endif + .set at + .set reorder + END(trap_low) + +LEAF(kgdb_read_byte) +4: lb t0, (a0) + sb t0, (a1) + li v0, 0 + jr ra + .section __ex_table,"a" + PTR 4b, kgdbfault + .previous + END(kgdb_read_byte) + +LEAF(kgdb_write_byte) +5: sb a0, (a1) + li v0, 0 + jr ra + .section __ex_table,"a" + PTR 5b, kgdbfault + .previous + END(kgdb_write_byte) + + .type kgdbfault@function + .ent kgdbfault + +kgdbfault: li v0, -EFAULT + jr ra + .end kgdbfault diff --git a/arch/mips/kernel/traps.c b/arch/mips/kernel/traps.c index b8ea4e9..129ee35 100644 --- a/arch/mips/kernel/traps.c +++ b/arch/mips/kernel/traps.c @@ -23,6 +23,7 @@ #include <linux/bootmem.h> #include <linux/interrupt.h> #include <linux/ptrace.h> +#include <linux/kgdb.h> #include <asm/bootinfo.h> #include <asm/branch.h> @@ -1537,6 +1538,11 @@ void __init trap_init(void) extern char except_vec4; unsigned long i; +#if defined(CONFIG_KGDB) + if (kgdb_early_setup) + return; /* Already done */ +#endif + if (cpu_has_veic || cpu_has_vint) ebase = (unsigned long) alloc_bootmem_low_pages(0x200 + VECTORSPACING*64); else diff --git a/include/asm-mips/asmmacro-32.h b/include/asm-mips/asmmacro-32.h index 5de3963..ed9be99 100644 --- a/include/asm-mips/asmmacro-32.h +++ b/include/asm-mips/asmmacro-32.h @@ -11,6 +11,28 @@ #include <asm/regdef.h> #include <asm/fpregdef.h> #include <asm/mipsregs.h> +#include <asm/gdb-stub.h> + + .macro fpu_save_double_kgdb stack status tmp1 = t0 + cfc1 \tmp1, fcr31 + sdc1 $f0, GDB_FR_FPR0(\stack) + sdc1 $f2, GDB_FR_FPR2(\stack) + sdc1 $f4, GDB_FR_FPR4(\stack) + sdc1 $f6, GDB_FR_FPR6(\stack) + sdc1 $f8, GDB_FR_FPR8(\stack) + sdc1 $f10, GDB_FR_FPR10(\stack) + sdc1 $f12, GDB_FR_FPR12(\stack) + sdc1 $f14, GDB_FR_FPR14(\stack) + sdc1 $f16, GDB_FR_FPR16(\stack) + sdc1 $f18, GDB_FR_FPR18(\stack) + sdc1 $f20, GDB_FR_FPR20(\stack) + sdc1 $f22, GDB_FR_FPR22(\stack) + sdc1 $f24, GDB_FR_FPR24(\stack) + sdc1 $f26, GDB_FR_FPR26(\stack) + sdc1 $f28, GDB_FR_FPR28(\stack) + sdc1 $f30, GDB_FR_FPR30(\stack) + sw \tmp1, GDB_FR_FSR(\stack) + .endm .macro fpu_save_double thread status tmp1=t0 cfc1 \tmp1, fcr31 @@ -91,6 +113,27 @@ ctc1 \tmp, fcr31 .endm + .macro fpu_restore_double_kgdb stack status tmp = t0 + lw \tmp, GDB_FR_FSR(\stack) + ldc1 $f0, GDB_FR_FPR0(\stack) + ldc1 $f2, GDB_FR_FPR2(\stack) + ldc1 $f4, GDB_FR_FPR4(\stack) + ldc1 $f6, GDB_FR_FPR6(\stack) + ldc1 $f8, GDB_FR_FPR8(\stack) + ldc1 $f10, GDB_FR_FPR10(\stack) + ldc1 $f12, GDB_FR_FPR12(\stack) + ldc1 $f14, GDB_FR_FPR14(\stack) + ldc1 $f16, GDB_FR_FPR16(\stack) + ldc1 $f18, GDB_FR_FPR18(\stack) + ldc1 $f20, GDB_FR_FPR20(\stack) + ldc1 $f22, GDB_FR_FPR22(\stack) + ldc1 $f24, GDB_FR_FPR24(\stack) + ldc1 $f26, GDB_FR_FPR26(\stack) + ldc1 $f28, GDB_FR_FPR28(\stack) + ldc1 $f30, GDB_FR_FPR30(\stack) + ctc1 \tmp, fcr31 + .endm + .macro fpu_restore_single thread tmp=t0 lw \tmp, THREAD_FCR31(\thread) lwc1 $f0, THREAD_FPR0(\thread) diff --git a/include/asm-mips/asmmacro-64.h b/include/asm-mips/asmmacro-64.h index 225feef..9789218 100644 --- a/include/asm-mips/asmmacro-64.h +++ b/include/asm-mips/asmmacro-64.h @@ -12,6 +12,7 @@ #include <asm/regdef.h> #include <asm/fpregdef.h> #include <asm/mipsregs.h> +#include <asm/gdb-stub.h> .macro fpu_save_16even thread tmp=t0 cfc1 \tmp, fcr31 @@ -53,6 +54,46 @@ sdc1 $f31, THREAD_FPR31(\thread) .endm + .macro fpu_save_16odd_kgdb stack + sdc1 $f1, GDB_FR_FPR1(\stack) + sdc1 $f3, GDB_FR_FPR3(\stack) + sdc1 $f5, GDB_FR_FPR5(\stack) + sdc1 $f7, GDB_FR_FPR7(\stack) + sdc1 $f9, GDB_FR_FPR9(\stack) + sdc1 $f11, GDB_FR_FPR11(\stack) + sdc1 $f13, GDB_FR_FPR13(\stack) + sdc1 $f15, GDB_FR_FPR15(\stack) + sdc1 $f17, GDB_FR_FPR17(\stack) + sdc1 $f19, GDB_FR_FPR19(\stack) + sdc1 $f21, GDB_FR_FPR21(\stack) + sdc1 $f23, GDB_FR_FPR23(\stack) + sdc1 $f25, GDB_FR_FPR25(\stack) + sdc1 $f27, GDB_FR_FPR27(\stack) + sdc1 $f29, GDB_FR_FPR29(\stack) + sdc1 $f31, GDB_FR_FPR31(\stack) + .endm + + .macro fpu_save_16even_kgdb stack tmp = t0 + cfc1 \tmp, fcr31 + sdc1 $f0, GDB_FR_FPR0(\stack) + sdc1 $f2, GDB_FR_FPR2(\stack) + sdc1 $f4, GDB_FR_FPR4(\stack) + sdc1 $f6, GDB_FR_FPR6(\stack) + sdc1 $f8, GDB_FR_FPR8(\stack) + sdc1 $f10, GDB_FR_FPR10(\stack) + sdc1 $f12, GDB_FR_FPR12(\stack) + sdc1 $f14, GDB_FR_FPR14(\stack) + sdc1 $f16, GDB_FR_FPR16(\stack) + sdc1 $f18, GDB_FR_FPR18(\stack) + sdc1 $f20, GDB_FR_FPR20(\stack) + sdc1 $f22, GDB_FR_FPR22(\stack) + sdc1 $f24, GDB_FR_FPR24(\stack) + sdc1 $f26, GDB_FR_FPR26(\stack) + sdc1 $f28, GDB_FR_FPR28(\stack) + sdc1 $f30, GDB_FR_FPR30(\stack) + sw \tmp, GDB_FR_FSR(\stack) + .endm + .macro fpu_save_double thread status tmp sll \tmp, \status, 5 bgez \tmp, 2f @@ -61,6 +102,15 @@ fpu_save_16even \thread \tmp .endm + .macro fpu_save_double_kgdb stack status tmp + sll \tmp, \status, 5 + bgez \tmp, 2f + nop + fpu_save_16odd_kgdb \stack +2: + fpu_save_16even_kgdb \stack \tmp + .endm + .macro fpu_restore_16even thread tmp=t0 lw \tmp, THREAD_FCR31(\thread) ldc1 $f0, THREAD_FPR0(\thread) @@ -101,6 +151,46 @@ ldc1 $f31, THREAD_FPR31(\thread) .endm + .macro fpu_restore_16even_kgdb stack tmp = t0 + lw \tmp, GDB_FR_FSR(\stack) + ldc1 $f0, GDB_FR_FPR0(\stack) + ldc1 $f2, GDB_FR_FPR2(\stack) + ldc1 $f4, GDB_FR_FPR4(\stack) + ldc1 $f6, GDB_FR_FPR6(\stack) + ldc1 $f8, GDB_FR_FPR8(\stack) + ldc1 $f10, GDB_FR_FPR10(\stack) + ldc1 $f12, GDB_FR_FPR12(\stack) + ldc1 $f14, GDB_FR_FPR14(\stack) + ldc1 $f16, GDB_FR_FPR16(\stack) + ldc1 $f18, GDB_FR_FPR18(\stack) + ldc1 $f20, GDB_FR_FPR20(\stack) + ldc1 $f22, GDB_FR_FPR22(\stack) + ldc1 $f24, GDB_FR_FPR24(\stack) + ldc1 $f26, GDB_FR_FPR26(\stack) + ldc1 $f28, GDB_FR_FPR28(\stack) + ldc1 $f30, GDB_FR_FPR30(\stack) + ctc1 \tmp, fcr31 + .endm + + .macro fpu_restore_16odd_kgdb stack + ldc1 $f1, GDB_FR_FPR1(\stack) + ldc1 $f3, GDB_FR_FPR3(\stack) + ldc1 $f5, GDB_FR_FPR5(\stack) + ldc1 $f7, GDB_FR_FPR7(\stack) + ldc1 $f9, GDB_FR_FPR9(\stack) + ldc1 $f11, GDB_FR_FPR11(\stack) + ldc1 $f13, GDB_FR_FPR13(\stack) + ldc1 $f15, GDB_FR_FPR15(\stack) + ldc1 $f17, GDB_FR_FPR17(\stack) + ldc1 $f19, GDB_FR_FPR19(\stack) + ldc1 $f21, GDB_FR_FPR21(\stack) + ldc1 $f23, GDB_FR_FPR23(\stack) + ldc1 $f25, GDB_FR_FPR25(\stack) + ldc1 $f27, GDB_FR_FPR27(\stack) + ldc1 $f29, GDB_FR_FPR29(\stack) + ldc1 $f31, GDB_FR_FPR31(\stack) + .endm + .macro fpu_restore_double thread status tmp sll \tmp, \status, 5 bgez \tmp, 1f # 16 register mode? @@ -109,6 +199,15 @@ 1: fpu_restore_16even \thread \tmp .endm + .macro fpu_restore_double_kgdb stack status tmp + sll \tmp, \status, 5 + bgez \tmp, 1f # 16 register mode? + nop + + fpu_restore_16odd_kgdb \stack +1: fpu_restore_16even_kgdb \stack \tmp + .endm + .macro cpu_save_nonscratch thread LONG_S s0, THREAD_REG16(\thread) LONG_S s1, THREAD_REG17(\thread) diff --git a/include/asm-mips/kgdb.h b/include/asm-mips/kgdb.h new file mode 100644 index 0000000..0e0ed89 --- /dev/null +++ b/include/asm-mips/kgdb.h @@ -0,0 +1,54 @@ +#ifdef __KERNEL__ +#ifndef _ASM_KGDB_H_ +#define _ASM_KGDB_H_ + +#include <asm/sgidefs.h> + +#ifndef __ASSEMBLY__ +#if (_MIPS_ISA == _MIPS_ISA_MIPS1) || (_MIPS_ISA == _MIPS_ISA_MIPS2) || \ + (_MIPS_ISA == _MIPS_ISA_MIPS32) + +#define KGDB_GDB_REG_SIZE 32 + +#elif (_MIPS_ISA == _MIPS_ISA_MIPS3) || (_MIPS_ISA == _MIPS_ISA_MIPS4) || \ + (_MIPS_ISA == _MIPS_ISA_MIPS64) + +#ifdef CONFIG_32BIT +#define KGDB_GDB_REG_SIZE 32 +#else /* CONFIG_CPU_32BIT */ +#define KGDB_GDB_REG_SIZE 64 +#endif +#else +#error "Need to set KGDB_GDB_REG_SIZE for MIPS ISA" +#endif /* _MIPS_ISA */ + +#define BUFMAX 2048 +#if (KGDB_GDB_REG_SIZE == 32) +#define NUMREGBYTES (90*sizeof(u32)) +#define NUMCRITREGBYTES (12*sizeof(u32)) +#else +#define NUMREGBYTES (90*sizeof(u64)) +#define NUMCRITREGBYTES (12*sizeof(u64)) +#endif +#define BREAK_INSTR_SIZE 4 +static inline void arch_kgdb_breakpoint(void) +{ + __asm__ __volatile__( + ".globl breakinst\n\t" + ".set\tnoreorder\n\t" + "nop\n" + "breakinst:\tbreak\n\t" + "nop\n\t" + ".set\treorder"); +} +#define CACHE_FLUSH_IS_SAFE 0 + +extern int kgdb_early_setup; +extern void *saved_vectors[32]; +extern void handle_exception(struct pt_regs *regs); +extern void trap_low(void); +extern void breakinst(void); + +#endif /* !__ASSEMBLY__ */ +#endif /* _ASM_KGDB_H_ */ +#endif /* __KERNEL__ */ -- 1.5.5.1