microblaze: Add KGDB support
Michal Simek [Tue, 3 Aug 2010 09:45:08 +0000 (11:45 +0200)]
Kgdb uses brki r16, 0x18 instruction to call
low level _debug_exception function which save
current state to pt_regs and call microblaze_kgdb_break
function. _debug_exception should be called only from
the kernel space. User space calling is not supported
because user application debugging uses different handling.

pt_regs_to_gdb_regs loads additional special registers
which can't be changed

 * Enable KGDB in Kconfig
 * Remove ancient not-tested KGDB support
 * Remove ancient _debug_exception code from entry.S

Only MMU KGDB support is supported.

Signed-off-by: Michal Simek <monstr@monstr.eu>
CC: Jason Wessel <jason.wessel@windriver.com>
CC: John Williams <john.williams@petalogix.com>
CC: Edgar E. Iglesias <edgar.iglesias@petalogix.com>
CC: linux-kernel@vger.kernel.org
Acked-by: Jason Wessel <jason.wessel@windriver.com>

arch/microblaze/Kconfig
arch/microblaze/include/asm/exceptions.h
arch/microblaze/include/asm/kgdb.h [new file with mode: 0644]
arch/microblaze/kernel/Makefile
arch/microblaze/kernel/entry.S
arch/microblaze/kernel/exceptions.c
arch/microblaze/kernel/kgdb.c [new file with mode: 0644]
arch/microblaze/mm/fault.c

index a517421..be38552 100644 (file)
@@ -14,6 +14,7 @@ config MICROBLAZE
        select USB_ARCH_HAS_EHCI
        select ARCH_WANT_OPTIONAL_GPIOLIB
        select HAVE_OPROFILE
+       select HAVE_ARCH_KGDB
        select HAVE_DMA_ATTRS
        select HAVE_DMA_API_DEBUG
        select TRACING_SUPPORT
index fa0e366..6479097 100644 (file)
@@ -69,22 +69,6 @@ asmlinkage void full_exception(struct pt_regs *regs, unsigned int type,
 void die(const char *str, struct pt_regs *fp, long err);
 void _exception(int signr, struct pt_regs *regs, int code, unsigned long addr);
 
-#if defined(CONFIG_KGDB)
-void (*debugger)(struct pt_regs *regs);
-int (*debugger_bpt)(struct pt_regs *regs);
-int (*debugger_sstep)(struct pt_regs *regs);
-int (*debugger_iabr_match)(struct pt_regs *regs);
-int (*debugger_dabr_match)(struct pt_regs *regs);
-void (*debugger_fault_handler)(struct pt_regs *regs);
-#else
-#define debugger(regs)                 do { } while (0)
-#define debugger_bpt(regs)             0
-#define debugger_sstep(regs)           0
-#define debugger_iabr_match(regs)      0
-#define debugger_dabr_match(regs)      0
-#define debugger_fault_handler         ((void (*)(struct pt_regs *))0)
-#endif
-
 #endif /*__ASSEMBLY__ */
 #endif /* __KERNEL__ */
 #endif /* _ASM_MICROBLAZE_EXCEPTIONS_H */
diff --git a/arch/microblaze/include/asm/kgdb.h b/arch/microblaze/include/asm/kgdb.h
new file mode 100644 (file)
index 0000000..78b17d4
--- /dev/null
@@ -0,0 +1,28 @@
+#ifdef __KERNEL__
+#ifndef __MICROBLAZE_KGDB_H__
+#define __MICROBLAZE_KGDB_H__
+
+#ifndef __ASSEMBLY__
+
+#define CACHE_FLUSH_IS_SAFE    1
+#define BUFMAX                 2048
+
+/*
+ * 32 32-bit general purpose registers (r0-r31)
+ *  6 32-bit special registers (pc, msr, ear, esr, fsr, btr)
+ * 12 32-bit PVR
+ *   7 32-bit MMU Regs (redr, rpid, rzpr, rtlbx, rtlbsx, rtlblo, rtlbhi)
+ * ------
+ *  57 registers
+ */
+#define NUMREGBYTES    (57 * 4)
+
+#define BREAK_INSTR_SIZE       4
+static inline void arch_kgdb_breakpoint(void)
+{
+       __asm__ __volatile__("brki r16, 0x18;");
+}
+
+#endif /* __ASSEMBLY__ */
+#endif /* __MICROBLAZE_KGDB_H__ */
+#endif /* __KERNEL__ */
index d66ddef..5eecc9f 100644 (file)
@@ -28,5 +28,6 @@ obj-$(CONFIG_MODULES)         += microblaze_ksyms.o module.o
 obj-$(CONFIG_MMU)              += misc.o
 obj-$(CONFIG_STACKTRACE)       += stacktrace.o
 obj-$(CONFIG_FUNCTION_TRACER)  += ftrace.o mcount.o
+obj-$(CONFIG_KGDB)             += kgdb.o
 
 obj-y  += entry$(MMU).o
index 5a5cb58..304882e 100644 (file)
@@ -745,11 +745,8 @@ IRQ_return: /* MS: Make global symbol for debugging */
        nop
 
 /*
- * `Debug' trap
- *  We enter dbtrap in "BIP" (breakpoint) mode.
- *  So we exit the breakpoint mode with an 'rtbd' and proceed with the
- *  original dbtrap.
- *  however, wait to save state first
+ * Debug trap for KGDB. Enter to _debug_exception by brki r16, 0x18
+ * and call handling function with saved pt_regs
  */
 C_ENTRY(_debug_exception):
        /* BIP bit is set on entry, no interrupts can occur */
@@ -759,18 +756,44 @@ C_ENTRY(_debug_exception):
        nop
        andi    r1, r1, MSR_UMS
        bnei    r1, 1f
-       /* Kernel-mode state save.  */
+/* MS: Kernel-mode state save - kgdb */
        lwi     r1, r0, TOPHYS(PER_CPU(ENTRY_SP)); /* Reload kernel stack-ptr*/
-       tophys(r1,r1);
 
-       addik   r1, r1, -STATE_SAVE_SIZE; /* Make room on the stack.  */
+       /* BIP bit is set on entry, no interrupts can occur */
+       addik   r1, r1, CONFIG_KERNEL_BASE_ADDR - CONFIG_KERNEL_START - STATE_SAVE_SIZE;
        SAVE_REGS;
+       /* save all regs to pt_reg structure */
+       swi     r0, r1, PTO+PT_R0;      /* R0 must be saved too */
+       swi     r14, r1, PTO+PT_R14     /* rewrite saved R14 value */
+       swi     r16, r1, PTO+PT_R16
+       swi     r16, r1, PTO+PT_PC; /* PC and r16 are the same */
+       swi     r17, r1, PTO+PT_R17
+       /* save special purpose registers to pt_regs */
+       mfs     r11, rear;
+       swi     r11, r1, PTO+PT_EAR;
+       mfs     r11, resr;
+       swi     r11, r1, PTO+PT_ESR;
+       mfs     r11, rfsr;
+       swi     r11, r1, PTO+PT_FSR;
+
+       /* stack pointer is in physical address at it is decrease
+        * by STATE_SAVE_SIZE but we need to get correct R1 value */
+       addik   r11, r1, CONFIG_KERNEL_START - CONFIG_KERNEL_BASE_ADDR + STATE_SAVE_SIZE;
+       swi     r11, r1, PTO+PT_R1
+       /* MS: r31 - current pointer isn't changed */
+       tovirt(r1,r1)
+#ifdef CONFIG_KGDB
+       addi    r5, r1, PTO /* pass pt_reg address as the first arg */
+       la      r15, r0, dbtrap_call; /* return address */
+       rtbd    r0, microblaze_kgdb_break
+       nop;
+#endif
+       /* MS: Place handler for brki from kernel space if KGDB is OFF.
+        * It is very unlikely that another brki instruction is called. */
+       bri 0
 
-       swi     r1, r1, PTO + PT_MODE;
-       brid    2f;
-       nop;                            /* Fill delay slot */
-1:      /* User-mode state save.  */
-       lwi     r1, r0, TOPHYS(PER_CPU(CURRENT_SAVE)); /* get saved current */
+/* MS: User-mode state save - gdb */
+1:     lwi     r1, r0, TOPHYS(PER_CPU(CURRENT_SAVE)); /* get saved current */
        tophys(r1,r1);
        lwi     r1, r1, TS_THREAD_INFO; /* get the thread info */
        addik   r1, r1, THREAD_SIZE;    /* calculate kernel stack pointer */
@@ -781,36 +804,32 @@ C_ENTRY(_debug_exception):
        swi     r17, r1, PTO+PT_R17;
        swi     r16, r1, PTO+PT_R16;
        swi     r16, r1, PTO+PT_PC;     /* Save LP */
-
        swi     r0, r1, PTO + PT_MODE; /* Was in user-mode.  */
        lwi     r11, r0, TOPHYS(PER_CPU(ENTRY_SP));
        swi     r11, r1, PTO+PT_R1; /* Store user SP.  */
-2:     lwi     CURRENT_TASK, r0, TOPHYS(PER_CPU(CURRENT_SAVE));
+       lwi     CURRENT_TASK, r0, TOPHYS(PER_CPU(CURRENT_SAVE));
        tovirt(r1,r1)
-
        set_vms;
        addik   r5, r1, PTO;
        addik   r15, r0, dbtrap_call;
-dbtrap_call: /* return point for kernel/user entry */
+dbtrap_call: /* Return point for kernel/user entry + 8 because of rtsd r15, 8 */
        rtbd    r0, sw_exception
        nop
 
-       set_bip;                        /*  Ints masked for state restore*/
+       /* MS: The first instruction for the second part of the gdb/kgdb */
+       set_bip; /* Ints masked for state restore */
        lwi     r11, r1, PTO + PT_MODE;
        bnei    r11, 2f;
-
+/* MS: Return to user space - gdb */
        /* Get current task ptr into r11 */
        lwi     r11, CURRENT_TASK, TS_THREAD_INFO;      /* get thread info */
        lwi     r11, r11, TI_FLAGS;     /* get flags in thread info */
        andi    r11, r11, _TIF_NEED_RESCHED;
        beqi    r11, 5f;
 
-/* Call the scheduler before returning from a syscall/trap. */
-
+       /* Call the scheduler before returning from a syscall/trap. */
        bralid  r15, schedule;  /* Call scheduler */
        nop;                            /* delay slot */
-       /* XXX Is PT_DTRACE handling needed here? */
-       /* XXX m68knommu also checks TASK_STATE & TASK_COUNTER here.  */
 
        /* Maybe handle a signal */
 5:     lwi     r11, CURRENT_TASK, TS_THREAD_INFO;      /* get thread info */
@@ -818,48 +837,37 @@ dbtrap_call: /* return point for kernel/user entry */
        andi    r11, r11, _TIF_SIGPENDING;
        beqi    r11, 1f;                /* Signals to handle, handle them */
 
-/* Handle a signal return; Pending signals should be in r18.  */
-       /* Not all registers are saved by the normal trap/interrupt entry
-          points (for instance, call-saved registers (because the normal
-          C-compiler calling sequence in the kernel makes sure they're
-          preserved), and call-clobbered registers in the case of
-          traps), but signal handlers may want to examine or change the
-          complete register state.  Here we save anything not saved by
-          the normal entry sequence, so that it may be safely restored
-          (in a possibly modified form) after do_signal returns.  */
-
        addik   r5, r1, PTO;            /* Arg 1: struct pt_regs *regs */
        addi  r7, r0, 0;        /* Arg 3: int in_syscall */
        bralid  r15, do_signal; /* Handle any signals */
        add     r6, r0, r0;             /* Arg 2: sigset_t *oldset */
 
-
 /* Finally, return to user state.  */
-1:
-       swi     CURRENT_TASK, r0, PER_CPU(CURRENT_SAVE); /* save current */
+1:     swi     CURRENT_TASK, r0, PER_CPU(CURRENT_SAVE); /* save current */
        VM_OFF;
        tophys(r1,r1);
-
+       /* MS: Restore all regs */
        RESTORE_REGS
        lwi     r17, r1, PTO+PT_R17;
        lwi     r16, r1, PTO+PT_R16;
-       addik   r1, r1, STATE_SAVE_SIZE         /* Clean up stack space.  */
-
-
-       lwi     r1, r1, PT_R1 - PT_SIZE;
-                                       /* Restore user stack pointer. */
-       bri     6f;
+       addik   r1, r1, STATE_SAVE_SIZE  /* Clean up stack space */
+       lwi     r1, r1, PT_R1 - PT_SIZE; /* Restore user stack pointer */
+DBTRAP_return_user: /* MS: Make global symbol for debugging */
+       rtbd    r16, 0; /* MS: Instructions to return from a debug trap */
+       nop;
 
-/* Return to kernel state.  */
+/* MS: Return to kernel state - kgdb */
 2:     VM_OFF;
        tophys(r1,r1);
+       /* MS: Restore all regs */
        RESTORE_REGS
-       addik   r1, r1, STATE_SAVE_SIZE         /* Clean up stack space.  */
-
+       lwi     r14, r1, PTO+PT_R14;
+       lwi     r16, r1, PTO+PT_PC;
+       lwi     r17, r1, PTO+PT_R17;
+       addik   r1, r1, STATE_SAVE_SIZE; /* MS: Clean up stack space */
        tovirt(r1,r1);
-6:
-DBTRAP_return:         /* Make global symbol for debugging */
-       rtbd    r16, 0; /* Instructions to return from an IRQ */
+DBTRAP_return_kernel: /* MS: Make global symbol for debugging */
+       rtbd    r16, 0; /* MS: Instructions to return from a debug trap */
        nop;
 
 
index e0c6f8c..b98ee8d 100644 (file)
@@ -59,7 +59,6 @@ void _exception(int signr, struct pt_regs *regs, int code, unsigned long addr)
        siginfo_t info;
 
        if (kernel_mode(regs)) {
-               debugger(regs);
                die("Exception in kernel mode", regs, signr);
        }
        info.si_signo = signr;
diff --git a/arch/microblaze/kernel/kgdb.c b/arch/microblaze/kernel/kgdb.c
new file mode 100644 (file)
index 0000000..bfc006b
--- /dev/null
@@ -0,0 +1,147 @@
+/*
+ * Microblaze KGDB support
+ *
+ * This file is subject to the terms and conditions of the GNU General Public
+ * License.  See the file "COPYING" in the main directory of this archive
+ * for more details.
+ */
+
+#include <linux/kgdb.h>
+#include <linux/kdebug.h>
+#include <linux/irq.h>
+#include <linux/io.h>
+#include <asm/cacheflush.h>
+#include <asm/asm-offsets.h>
+#include <asm/pvr.h>
+
+#define GDB_REG                0
+#define GDB_PC         32
+#define GDB_MSR                33
+#define GDB_EAR                34
+#define GDB_ESR                35
+#define GDB_FSR                36
+#define GDB_BTR                37
+#define GDB_PVR                38
+#define GDB_REDR       50
+#define GDB_RPID       51
+#define GDB_RZPR       52
+#define GDB_RTLBX      53
+#define GDB_RTLBSX     54 /* mfs can't read it */
+#define GDB_RTLBLO     55
+#define GDB_RTLBHI     56
+
+/* keep pvr separately because it is unchangeble */
+struct pvr_s pvr;
+
+void pt_regs_to_gdb_regs(unsigned long *gdb_regs, struct pt_regs *regs)
+{
+       int i;
+       unsigned long *pt_regb = (unsigned long *)regs;
+       int temp;
+       /* registers r0 - r31, pc, msr, ear, esr, fsr + do not save pt_mode */
+       for (i = 0; i < (sizeof(struct pt_regs) / 4) - 1; i++)
+               gdb_regs[i] = pt_regb[i];
+
+       /* Branch target register can't be changed */
+       __asm__ __volatile__ ("mfs %0, rbtr;" : "=r"(temp) : );
+       gdb_regs[GDB_BTR] = temp;
+
+       /* pvr part  - we have 11 pvr regs */
+       for (i = 0; i < sizeof(struct pvr_s)/4; i++)
+               gdb_regs[GDB_PVR + i] = pvr.pvr[i];
+
+       /* read special registers - can't be changed */
+       __asm__ __volatile__ ("mfs %0, redr;" : "=r"(temp) : );
+       gdb_regs[GDB_REDR] = temp;
+       __asm__ __volatile__ ("mfs %0, rpid;" : "=r"(temp) : );
+       gdb_regs[GDB_RPID] = temp;
+       __asm__ __volatile__ ("mfs %0, rzpr;" : "=r"(temp) : );
+       gdb_regs[GDB_RZPR] = temp;
+       __asm__ __volatile__ ("mfs %0, rtlbx;" : "=r"(temp) : );
+       gdb_regs[GDB_RTLBX] = temp;
+       __asm__ __volatile__ ("mfs %0, rtlblo;" : "=r"(temp) : );
+       gdb_regs[GDB_RTLBLO] = temp;
+       __asm__ __volatile__ ("mfs %0, rtlbhi;" : "=r"(temp) : );
+       gdb_regs[GDB_RTLBHI] = temp;
+}
+
+void gdb_regs_to_pt_regs(unsigned long *gdb_regs, struct pt_regs *regs)
+{
+       int i;
+       unsigned long *pt_regb = (unsigned long *)regs;
+
+       /* pt_regs and gdb_regs have the same 37 values.
+        * The rest of gdb_regs are unused and can't be changed.
+        * r0 register value can't be changed too. */
+       for (i = 1; i < (sizeof(struct pt_regs) / 4) - 1; i++)
+               pt_regb[i] = gdb_regs[i];
+}
+
+void microblaze_kgdb_break(struct pt_regs *regs)
+{
+       if (kgdb_handle_exception(1, SIGTRAP, 0, regs) != 0)
+               return 0;
+
+       /* Jump over the first arch_kgdb_breakpoint which is barrier to
+        * get kgdb work. The same solution is used for powerpc */
+       if (*(u32 *) (regs->pc) == *(u32 *) (&arch_kgdb_ops.gdb_bpt_instr))
+               regs->pc += BREAK_INSTR_SIZE;
+}
+
+/* untested */
+void sleeping_thread_to_gdb_regs(unsigned long *gdb_regs, struct task_struct *p)
+{
+       int i;
+       unsigned long *pt_regb = (unsigned long *)(p->thread.regs);
+
+       /* registers r0 - r31, pc, msr, ear, esr, fsr + do not save pt_mode */
+       for (i = 0; i < (sizeof(struct pt_regs) / 4) - 1; i++)
+               gdb_regs[i] = pt_regb[i];
+
+       /* pvr part  - we have 11 pvr regs */
+       for (i = 0; i < sizeof(struct pvr_s)/4; i++)
+               gdb_regs[GDB_PVR + i] = pvr.pvr[i];
+}
+
+void kgdb_arch_set_pc(struct pt_regs *regs, unsigned long ip)
+{
+       regs->pc = ip;
+}
+
+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 'c':
+               /* handle the optional parameter */
+               ptr = &remcom_in_buffer[1];
+               if (kgdb_hex2long(&ptr, &address))
+                       regs->pc = address;
+
+               return 0;
+       }
+       return -1; /* this means that we do not want to exit from the handler */
+}
+
+int kgdb_arch_init(void)
+{
+       get_pvr(&pvr); /* Fill PVR structure */
+       return 0;
+}
+
+void kgdb_arch_exit(void)
+{
+       /* Nothing to do */
+}
+
+/*
+ * Global data
+ */
+struct kgdb_arch arch_kgdb_ops = {
+       .gdb_bpt_instr = {0xba, 0x0c, 0x00, 0x18}, /* brki r16, 0x18 */
+};
index b224c65..57bd2a0 100644 (file)
 #include <linux/uaccess.h>
 #include <asm/exceptions.h>
 
-#if defined(CONFIG_KGDB)
-int debugger_kernel_faults = 1;
-#endif
-
 static unsigned long pte_misses;       /* updated by do_page_fault() */
 static unsigned long pte_errors;       /* updated by do_page_fault() */
 
@@ -81,10 +77,6 @@ void bad_page_fault(struct pt_regs *regs, unsigned long address, int sig)
        }
 
        /* kernel has accessed a bad area */
-#if defined(CONFIG_KGDB)
-       if (debugger_kernel_faults)
-               debugger(regs);
-#endif
        die("kernel access of bad area", regs, sig);
 }
 
@@ -115,13 +107,6 @@ void do_page_fault(struct pt_regs *regs, unsigned long address,
        if ((error_code & 0x13) == 0x13 || (error_code & 0x11) == 0x11)
                is_write = 0;
 
-#if defined(CONFIG_KGDB)
-       if (debugger_fault_handler && regs->trap == 0x300) {
-               debugger_fault_handler(regs);
-               return;
-       }
-#endif /* CONFIG_KGDB */
-
        if (unlikely(in_atomic() || !mm)) {
                if (kernel_mode(regs))
                        goto bad_area_nosemaphore;