diff options
| author | Michal Simek <monstr@monstr.eu> | 2010-08-03 05:45:08 -0400 |
|---|---|---|
| committer | Michal Simek <monstr@monstr.eu> | 2010-08-04 04:45:17 -0400 |
| commit | 2d5973cb5ac5d04662f86e19a06a4c52fa4c4ae3 (patch) | |
| tree | 21ddc58eed5ccad856fc44dfb0ee794da41d5b16 | |
| parent | 751f1605e03533a6279ccf456e938e9595c7d888 (diff) | |
microblaze: Add KGDB support
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>
| -rw-r--r-- | arch/microblaze/Kconfig | 1 | ||||
| -rw-r--r-- | arch/microblaze/include/asm/exceptions.h | 16 | ||||
| -rw-r--r-- | arch/microblaze/include/asm/kgdb.h | 28 | ||||
| -rw-r--r-- | arch/microblaze/kernel/Makefile | 1 | ||||
| -rw-r--r-- | arch/microblaze/kernel/entry.S | 106 | ||||
| -rw-r--r-- | arch/microblaze/kernel/exceptions.c | 1 | ||||
| -rw-r--r-- | arch/microblaze/kernel/kgdb.c | 147 | ||||
| -rw-r--r-- | arch/microblaze/mm/fault.c | 15 |
8 files changed, 234 insertions, 81 deletions
diff --git a/arch/microblaze/Kconfig b/arch/microblaze/Kconfig index a51742190c12..be3855250db6 100644 --- a/arch/microblaze/Kconfig +++ b/arch/microblaze/Kconfig | |||
| @@ -14,6 +14,7 @@ config MICROBLAZE | |||
| 14 | select USB_ARCH_HAS_EHCI | 14 | select USB_ARCH_HAS_EHCI |
| 15 | select ARCH_WANT_OPTIONAL_GPIOLIB | 15 | select ARCH_WANT_OPTIONAL_GPIOLIB |
| 16 | select HAVE_OPROFILE | 16 | select HAVE_OPROFILE |
| 17 | select HAVE_ARCH_KGDB | ||
| 17 | select HAVE_DMA_ATTRS | 18 | select HAVE_DMA_ATTRS |
| 18 | select HAVE_DMA_API_DEBUG | 19 | select HAVE_DMA_API_DEBUG |
| 19 | select TRACING_SUPPORT | 20 | select TRACING_SUPPORT |
diff --git a/arch/microblaze/include/asm/exceptions.h b/arch/microblaze/include/asm/exceptions.h index fa0e36657fdd..6479097b802b 100644 --- a/arch/microblaze/include/asm/exceptions.h +++ b/arch/microblaze/include/asm/exceptions.h | |||
| @@ -69,22 +69,6 @@ asmlinkage void full_exception(struct pt_regs *regs, unsigned int type, | |||
| 69 | void die(const char *str, struct pt_regs *fp, long err); | 69 | void die(const char *str, struct pt_regs *fp, long err); |
| 70 | void _exception(int signr, struct pt_regs *regs, int code, unsigned long addr); | 70 | void _exception(int signr, struct pt_regs *regs, int code, unsigned long addr); |
| 71 | 71 | ||
| 72 | #if defined(CONFIG_KGDB) | ||
| 73 | void (*debugger)(struct pt_regs *regs); | ||
| 74 | int (*debugger_bpt)(struct pt_regs *regs); | ||
| 75 | int (*debugger_sstep)(struct pt_regs *regs); | ||
| 76 | int (*debugger_iabr_match)(struct pt_regs *regs); | ||
| 77 | int (*debugger_dabr_match)(struct pt_regs *regs); | ||
| 78 | void (*debugger_fault_handler)(struct pt_regs *regs); | ||
| 79 | #else | ||
| 80 | #define debugger(regs) do { } while (0) | ||
| 81 | #define debugger_bpt(regs) 0 | ||
| 82 | #define debugger_sstep(regs) 0 | ||
| 83 | #define debugger_iabr_match(regs) 0 | ||
| 84 | #define debugger_dabr_match(regs) 0 | ||
| 85 | #define debugger_fault_handler ((void (*)(struct pt_regs *))0) | ||
| 86 | #endif | ||
| 87 | |||
| 88 | #endif /*__ASSEMBLY__ */ | 72 | #endif /*__ASSEMBLY__ */ |
| 89 | #endif /* __KERNEL__ */ | 73 | #endif /* __KERNEL__ */ |
| 90 | #endif /* _ASM_MICROBLAZE_EXCEPTIONS_H */ | 74 | #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 index 000000000000..78b17d40b235 --- /dev/null +++ b/arch/microblaze/include/asm/kgdb.h | |||
| @@ -0,0 +1,28 @@ | |||
| 1 | #ifdef __KERNEL__ | ||
| 2 | #ifndef __MICROBLAZE_KGDB_H__ | ||
| 3 | #define __MICROBLAZE_KGDB_H__ | ||
| 4 | |||
| 5 | #ifndef __ASSEMBLY__ | ||
| 6 | |||
| 7 | #define CACHE_FLUSH_IS_SAFE 1 | ||
| 8 | #define BUFMAX 2048 | ||
| 9 | |||
| 10 | /* | ||
| 11 | * 32 32-bit general purpose registers (r0-r31) | ||
| 12 | * 6 32-bit special registers (pc, msr, ear, esr, fsr, btr) | ||
| 13 | * 12 32-bit PVR | ||
| 14 | * 7 32-bit MMU Regs (redr, rpid, rzpr, rtlbx, rtlbsx, rtlblo, rtlbhi) | ||
| 15 | * ------ | ||
| 16 | * 57 registers | ||
| 17 | */ | ||
| 18 | #define NUMREGBYTES (57 * 4) | ||
| 19 | |||
| 20 | #define BREAK_INSTR_SIZE 4 | ||
| 21 | static inline void arch_kgdb_breakpoint(void) | ||
| 22 | { | ||
| 23 | __asm__ __volatile__("brki r16, 0x18;"); | ||
| 24 | } | ||
| 25 | |||
| 26 | #endif /* __ASSEMBLY__ */ | ||
| 27 | #endif /* __MICROBLAZE_KGDB_H__ */ | ||
| 28 | #endif /* __KERNEL__ */ | ||
diff --git a/arch/microblaze/kernel/Makefile b/arch/microblaze/kernel/Makefile index d66ddef53c07..5eecc9f1fbd9 100644 --- a/arch/microblaze/kernel/Makefile +++ b/arch/microblaze/kernel/Makefile | |||
| @@ -28,5 +28,6 @@ obj-$(CONFIG_MODULES) += microblaze_ksyms.o module.o | |||
| 28 | obj-$(CONFIG_MMU) += misc.o | 28 | obj-$(CONFIG_MMU) += misc.o |
| 29 | obj-$(CONFIG_STACKTRACE) += stacktrace.o | 29 | obj-$(CONFIG_STACKTRACE) += stacktrace.o |
| 30 | obj-$(CONFIG_FUNCTION_TRACER) += ftrace.o mcount.o | 30 | obj-$(CONFIG_FUNCTION_TRACER) += ftrace.o mcount.o |
| 31 | obj-$(CONFIG_KGDB) += kgdb.o | ||
| 31 | 32 | ||
| 32 | obj-y += entry$(MMU).o | 33 | obj-y += entry$(MMU).o |
diff --git a/arch/microblaze/kernel/entry.S b/arch/microblaze/kernel/entry.S index 5a5cb5842938..304882e56459 100644 --- a/arch/microblaze/kernel/entry.S +++ b/arch/microblaze/kernel/entry.S | |||
| @@ -745,11 +745,8 @@ IRQ_return: /* MS: Make global symbol for debugging */ | |||
| 745 | nop | 745 | nop |
| 746 | 746 | ||
| 747 | /* | 747 | /* |
| 748 | * `Debug' trap | 748 | * Debug trap for KGDB. Enter to _debug_exception by brki r16, 0x18 |
| 749 | * We enter dbtrap in "BIP" (breakpoint) mode. | 749 | * and call handling function with saved pt_regs |
| 750 | * So we exit the breakpoint mode with an 'rtbd' and proceed with the | ||
| 751 | * original dbtrap. | ||
| 752 | * however, wait to save state first | ||
| 753 | */ | 750 | */ |
| 754 | C_ENTRY(_debug_exception): | 751 | C_ENTRY(_debug_exception): |
| 755 | /* BIP bit is set on entry, no interrupts can occur */ | 752 | /* BIP bit is set on entry, no interrupts can occur */ |
| @@ -759,18 +756,44 @@ C_ENTRY(_debug_exception): | |||
| 759 | nop | 756 | nop |
| 760 | andi r1, r1, MSR_UMS | 757 | andi r1, r1, MSR_UMS |
| 761 | bnei r1, 1f | 758 | bnei r1, 1f |
| 762 | /* Kernel-mode state save. */ | 759 | /* MS: Kernel-mode state save - kgdb */ |
| 763 | lwi r1, r0, TOPHYS(PER_CPU(ENTRY_SP)); /* Reload kernel stack-ptr*/ | 760 | lwi r1, r0, TOPHYS(PER_CPU(ENTRY_SP)); /* Reload kernel stack-ptr*/ |
| 764 | tophys(r1,r1); | ||
| 765 | 761 | ||
| 766 | addik r1, r1, -STATE_SAVE_SIZE; /* Make room on the stack. */ | 762 | /* BIP bit is set on entry, no interrupts can occur */ |
| 763 | addik r1, r1, CONFIG_KERNEL_BASE_ADDR - CONFIG_KERNEL_START - STATE_SAVE_SIZE; | ||
| 767 | SAVE_REGS; | 764 | SAVE_REGS; |
| 765 | /* save all regs to pt_reg structure */ | ||
| 766 | swi r0, r1, PTO+PT_R0; /* R0 must be saved too */ | ||
| 767 | swi r14, r1, PTO+PT_R14 /* rewrite saved R14 value */ | ||
| 768 | swi r16, r1, PTO+PT_R16 | ||
| 769 | swi r16, r1, PTO+PT_PC; /* PC and r16 are the same */ | ||
| 770 | swi r17, r1, PTO+PT_R17 | ||
| 771 | /* save special purpose registers to pt_regs */ | ||
| 772 | mfs r11, rear; | ||
| 773 | swi r11, r1, PTO+PT_EAR; | ||
| 774 | mfs r11, resr; | ||
| 775 | swi r11, r1, PTO+PT_ESR; | ||
| 776 | mfs r11, rfsr; | ||
| 777 | swi r11, r1, PTO+PT_FSR; | ||
| 778 | |||
| 779 | /* stack pointer is in physical address at it is decrease | ||
| 780 | * by STATE_SAVE_SIZE but we need to get correct R1 value */ | ||
| 781 | addik r11, r1, CONFIG_KERNEL_START - CONFIG_KERNEL_BASE_ADDR + STATE_SAVE_SIZE; | ||
| 782 | swi r11, r1, PTO+PT_R1 | ||
| 783 | /* MS: r31 - current pointer isn't changed */ | ||
| 784 | tovirt(r1,r1) | ||
| 785 | #ifdef CONFIG_KGDB | ||
| 786 | addi r5, r1, PTO /* pass pt_reg address as the first arg */ | ||
| 787 | la r15, r0, dbtrap_call; /* return address */ | ||
| 788 | rtbd r0, microblaze_kgdb_break | ||
| 789 | nop; | ||
| 790 | #endif | ||
| 791 | /* MS: Place handler for brki from kernel space if KGDB is OFF. | ||
| 792 | * It is very unlikely that another brki instruction is called. */ | ||
| 793 | bri 0 | ||
| 768 | 794 | ||
| 769 | swi r1, r1, PTO + PT_MODE; | 795 | /* MS: User-mode state save - gdb */ |
| 770 | brid 2f; | 796 | 1: lwi r1, r0, TOPHYS(PER_CPU(CURRENT_SAVE)); /* get saved current */ |
| 771 | nop; /* Fill delay slot */ | ||
| 772 | 1: /* User-mode state save. */ | ||
| 773 | lwi r1, r0, TOPHYS(PER_CPU(CURRENT_SAVE)); /* get saved current */ | ||
| 774 | tophys(r1,r1); | 797 | tophys(r1,r1); |
| 775 | lwi r1, r1, TS_THREAD_INFO; /* get the thread info */ | 798 | lwi r1, r1, TS_THREAD_INFO; /* get the thread info */ |
| 776 | addik r1, r1, THREAD_SIZE; /* calculate kernel stack pointer */ | 799 | addik r1, r1, THREAD_SIZE; /* calculate kernel stack pointer */ |
| @@ -781,36 +804,32 @@ C_ENTRY(_debug_exception): | |||
| 781 | swi r17, r1, PTO+PT_R17; | 804 | swi r17, r1, PTO+PT_R17; |
| 782 | swi r16, r1, PTO+PT_R16; | 805 | swi r16, r1, PTO+PT_R16; |
| 783 | swi r16, r1, PTO+PT_PC; /* Save LP */ | 806 | swi r16, r1, PTO+PT_PC; /* Save LP */ |
| 784 | |||
| 785 | swi r0, r1, PTO + PT_MODE; /* Was in user-mode. */ | 807 | swi r0, r1, PTO + PT_MODE; /* Was in user-mode. */ |
| 786 | lwi r11, r0, TOPHYS(PER_CPU(ENTRY_SP)); | 808 | lwi r11, r0, TOPHYS(PER_CPU(ENTRY_SP)); |
| 787 | swi r11, r1, PTO+PT_R1; /* Store user SP. */ | 809 | swi r11, r1, PTO+PT_R1; /* Store user SP. */ |
| 788 | 2: lwi CURRENT_TASK, r0, TOPHYS(PER_CPU(CURRENT_SAVE)); | 810 | lwi CURRENT_TASK, r0, TOPHYS(PER_CPU(CURRENT_SAVE)); |
| 789 | tovirt(r1,r1) | 811 | tovirt(r1,r1) |
| 790 | |||
| 791 | set_vms; | 812 | set_vms; |
| 792 | addik r5, r1, PTO; | 813 | addik r5, r1, PTO; |
| 793 | addik r15, r0, dbtrap_call; | 814 | addik r15, r0, dbtrap_call; |
| 794 | dbtrap_call: /* return point for kernel/user entry */ | 815 | dbtrap_call: /* Return point for kernel/user entry + 8 because of rtsd r15, 8 */ |
| 795 | rtbd r0, sw_exception | 816 | rtbd r0, sw_exception |
| 796 | nop | 817 | nop |
| 797 | 818 | ||
| 798 | set_bip; /* Ints masked for state restore*/ | 819 | /* MS: The first instruction for the second part of the gdb/kgdb */ |
| 820 | set_bip; /* Ints masked for state restore */ | ||
| 799 | lwi r11, r1, PTO + PT_MODE; | 821 | lwi r11, r1, PTO + PT_MODE; |
| 800 | bnei r11, 2f; | 822 | bnei r11, 2f; |
| 801 | 823 | /* MS: Return to user space - gdb */ | |
| 802 | /* Get current task ptr into r11 */ | 824 | /* Get current task ptr into r11 */ |
| 803 | lwi r11, CURRENT_TASK, TS_THREAD_INFO; /* get thread info */ | 825 | lwi r11, CURRENT_TASK, TS_THREAD_INFO; /* get thread info */ |
| 804 | lwi r11, r11, TI_FLAGS; /* get flags in thread info */ | 826 | lwi r11, r11, TI_FLAGS; /* get flags in thread info */ |
| 805 | andi r11, r11, _TIF_NEED_RESCHED; | 827 | andi r11, r11, _TIF_NEED_RESCHED; |
| 806 | beqi r11, 5f; | 828 | beqi r11, 5f; |
| 807 | 829 | ||
| 808 | /* Call the scheduler before returning from a syscall/trap. */ | 830 | /* Call the scheduler before returning from a syscall/trap. */ |
| 809 | |||
| 810 | bralid r15, schedule; /* Call scheduler */ | 831 | bralid r15, schedule; /* Call scheduler */ |
| 811 | nop; /* delay slot */ | 832 | nop; /* delay slot */ |
| 812 | /* XXX Is PT_DTRACE handling needed here? */ | ||
| 813 | /* XXX m68knommu also checks TASK_STATE & TASK_COUNTER here. */ | ||
| 814 | 833 | ||
| 815 | /* Maybe handle a signal */ | 834 | /* Maybe handle a signal */ |
| 816 | 5: lwi r11, CURRENT_TASK, TS_THREAD_INFO; /* get thread info */ | 835 | 5: lwi r11, CURRENT_TASK, TS_THREAD_INFO; /* get thread info */ |
| @@ -818,48 +837,37 @@ dbtrap_call: /* return point for kernel/user entry */ | |||
| 818 | andi r11, r11, _TIF_SIGPENDING; | 837 | andi r11, r11, _TIF_SIGPENDING; |
| 819 | beqi r11, 1f; /* Signals to handle, handle them */ | 838 | beqi r11, 1f; /* Signals to handle, handle them */ |
| 820 | 839 | ||
| 821 | /* Handle a signal return; Pending signals should be in r18. */ | ||
| 822 | /* Not all registers are saved by the normal trap/interrupt entry | ||
| 823 | points (for instance, call-saved registers (because the normal | ||
| 824 | C-compiler calling sequence in the kernel makes sure they're | ||
| 825 | preserved), and call-clobbered registers in the case of | ||
| 826 | traps), but signal handlers may want to examine or change the | ||
| 827 | complete register state. Here we save anything not saved by | ||
| 828 | the normal entry sequence, so that it may be safely restored | ||
| 829 | (in a possibly modified form) after do_signal returns. */ | ||
| 830 | |||
| 831 | addik r5, r1, PTO; /* Arg 1: struct pt_regs *regs */ | 840 | addik r5, r1, PTO; /* Arg 1: struct pt_regs *regs */ |
| 832 | addi r7, r0, 0; /* Arg 3: int in_syscall */ | 841 | addi r7, r0, 0; /* Arg 3: int in_syscall */ |
| 833 | bralid r15, do_signal; /* Handle any signals */ | 842 | bralid r15, do_signal; /* Handle any signals */ |
| 834 | add r6, r0, r0; /* Arg 2: sigset_t *oldset */ | 843 | add r6, r0, r0; /* Arg 2: sigset_t *oldset */ |
| 835 | 844 | ||
| 836 | |||
| 837 | /* Finally, return to user state. */ | 845 | /* Finally, return to user state. */ |
| 838 | 1: | 846 | 1: swi CURRENT_TASK, r0, PER_CPU(CURRENT_SAVE); /* save current */ |
| 839 | swi CURRENT_TASK, r0, PER_CPU(CURRENT_SAVE); /* save current */ | ||
| 840 | VM_OFF; | 847 | VM_OFF; |
| 841 | tophys(r1,r1); | 848 | tophys(r1,r1); |
| 842 | 849 | /* MS: Restore all regs */ | |
| 843 | RESTORE_REGS | 850 | RESTORE_REGS |
| 844 | lwi r17, r1, PTO+PT_R17; | 851 | lwi r17, r1, PTO+PT_R17; |
| 845 | lwi r16, r1, PTO+PT_R16; | 852 | lwi r16, r1, PTO+PT_R16; |
| 846 | addik r1, r1, STATE_SAVE_SIZE /* Clean up stack space. */ | 853 | addik r1, r1, STATE_SAVE_SIZE /* Clean up stack space */ |
| 847 | 854 | lwi r1, r1, PT_R1 - PT_SIZE; /* Restore user stack pointer */ | |
| 848 | 855 | DBTRAP_return_user: /* MS: Make global symbol for debugging */ | |
| 849 | lwi r1, r1, PT_R1 - PT_SIZE; | 856 | rtbd r16, 0; /* MS: Instructions to return from a debug trap */ |
| 850 | /* Restore user stack pointer. */ | 857 | nop; |
| 851 | bri 6f; | ||
| 852 | 858 | ||
| 853 | /* Return to kernel state. */ | 859 | /* MS: Return to kernel state - kgdb */ |
| 854 | 2: VM_OFF; | 860 | 2: VM_OFF; |
| 855 | tophys(r1,r1); | 861 | tophys(r1,r1); |
| 862 | /* MS: Restore all regs */ | ||
| 856 | RESTORE_REGS | 863 | RESTORE_REGS |
| 857 | addik r1, r1, STATE_SAVE_SIZE /* Clean up stack space. */ | 864 | lwi r14, r1, PTO+PT_R14; |
| 858 | 865 | lwi r16, r1, PTO+PT_PC; | |
| 866 | lwi r17, r1, PTO+PT_R17; | ||
| 867 | addik r1, r1, STATE_SAVE_SIZE; /* MS: Clean up stack space */ | ||
| 859 | tovirt(r1,r1); | 868 | tovirt(r1,r1); |
| 860 | 6: | 869 | DBTRAP_return_kernel: /* MS: Make global symbol for debugging */ |
| 861 | DBTRAP_return: /* Make global symbol for debugging */ | 870 | rtbd r16, 0; /* MS: Instructions to return from a debug trap */ |
| 862 | rtbd r16, 0; /* Instructions to return from an IRQ */ | ||
| 863 | nop; | 871 | nop; |
| 864 | 872 | ||
| 865 | 873 | ||
diff --git a/arch/microblaze/kernel/exceptions.c b/arch/microblaze/kernel/exceptions.c index e0c6f8c2bd9d..b98ee8d0c1cd 100644 --- a/arch/microblaze/kernel/exceptions.c +++ b/arch/microblaze/kernel/exceptions.c | |||
| @@ -59,7 +59,6 @@ void _exception(int signr, struct pt_regs *regs, int code, unsigned long addr) | |||
| 59 | siginfo_t info; | 59 | siginfo_t info; |
| 60 | 60 | ||
| 61 | if (kernel_mode(regs)) { | 61 | if (kernel_mode(regs)) { |
| 62 | debugger(regs); | ||
| 63 | die("Exception in kernel mode", regs, signr); | 62 | die("Exception in kernel mode", regs, signr); |
| 64 | } | 63 | } |
| 65 | info.si_signo = signr; | 64 | info.si_signo = signr; |
diff --git a/arch/microblaze/kernel/kgdb.c b/arch/microblaze/kernel/kgdb.c new file mode 100644 index 000000000000..bfc006b7f2d8 --- /dev/null +++ b/arch/microblaze/kernel/kgdb.c | |||
| @@ -0,0 +1,147 @@ | |||
| 1 | /* | ||
| 2 | * Microblaze KGDB support | ||
| 3 | * | ||
| 4 | * This file is subject to the terms and conditions of the GNU General Public | ||
| 5 | * License. See the file "COPYING" in the main directory of this archive | ||
| 6 | * for more details. | ||
| 7 | */ | ||
| 8 | |||
| 9 | #include <linux/kgdb.h> | ||
| 10 | #include <linux/kdebug.h> | ||
| 11 | #include <linux/irq.h> | ||
| 12 | #include <linux/io.h> | ||
| 13 | #include <asm/cacheflush.h> | ||
| 14 | #include <asm/asm-offsets.h> | ||
| 15 | #include <asm/pvr.h> | ||
| 16 | |||
| 17 | #define GDB_REG 0 | ||
| 18 | #define GDB_PC 32 | ||
| 19 | #define GDB_MSR 33 | ||
| 20 | #define GDB_EAR 34 | ||
| 21 | #define GDB_ESR 35 | ||
| 22 | #define GDB_FSR 36 | ||
| 23 | #define GDB_BTR 37 | ||
| 24 | #define GDB_PVR 38 | ||
| 25 | #define GDB_REDR 50 | ||
| 26 | #define GDB_RPID 51 | ||
| 27 | #define GDB_RZPR 52 | ||
| 28 | #define GDB_RTLBX 53 | ||
| 29 | #define GDB_RTLBSX 54 /* mfs can't read it */ | ||
| 30 | #define GDB_RTLBLO 55 | ||
| 31 | #define GDB_RTLBHI 56 | ||
| 32 | |||
| 33 | /* keep pvr separately because it is unchangeble */ | ||
| 34 | struct pvr_s pvr; | ||
| 35 | |||
| 36 | void pt_regs_to_gdb_regs(unsigned long *gdb_regs, struct pt_regs *regs) | ||
| 37 | { | ||
| 38 | int i; | ||
| 39 | unsigned long *pt_regb = (unsigned long *)regs; | ||
| 40 | int temp; | ||
| 41 | /* registers r0 - r31, pc, msr, ear, esr, fsr + do not save pt_mode */ | ||
| 42 | for (i = 0; i < (sizeof(struct pt_regs) / 4) - 1; i++) | ||
| 43 | gdb_regs[i] = pt_regb[i]; | ||
| 44 | |||
| 45 | /* Branch target register can't be changed */ | ||
| 46 | __asm__ __volatile__ ("mfs %0, rbtr;" : "=r"(temp) : ); | ||
| 47 | gdb_regs[GDB_BTR] = temp; | ||
| 48 | |||
| 49 | /* pvr part - we have 11 pvr regs */ | ||
| 50 | for (i = 0; i < sizeof(struct pvr_s)/4; i++) | ||
| 51 | gdb_regs[GDB_PVR + i] = pvr.pvr[i]; | ||
| 52 | |||
| 53 | /* read special registers - can't be changed */ | ||
| 54 | __asm__ __volatile__ ("mfs %0, redr;" : "=r"(temp) : ); | ||
| 55 | gdb_regs[GDB_REDR] = temp; | ||
| 56 | __asm__ __volatile__ ("mfs %0, rpid;" : "=r"(temp) : ); | ||
| 57 | gdb_regs[GDB_RPID] = temp; | ||
| 58 | __asm__ __volatile__ ("mfs %0, rzpr;" : "=r"(temp) : ); | ||
| 59 | gdb_regs[GDB_RZPR] = temp; | ||
| 60 | __asm__ __volatile__ ("mfs %0, rtlbx;" : "=r"(temp) : ); | ||
| 61 | gdb_regs[GDB_RTLBX] = temp; | ||
| 62 | __asm__ __volatile__ ("mfs %0, rtlblo;" : "=r"(temp) : ); | ||
| 63 | gdb_regs[GDB_RTLBLO] = temp; | ||
| 64 | __asm__ __volatile__ ("mfs %0, rtlbhi;" : "=r"(temp) : ); | ||
| 65 | gdb_regs[GDB_RTLBHI] = temp; | ||
| 66 | } | ||
| 67 | |||
| 68 | void gdb_regs_to_pt_regs(unsigned long *gdb_regs, struct pt_regs *regs) | ||
| 69 | { | ||
| 70 | int i; | ||
| 71 | unsigned long *pt_regb = (unsigned long *)regs; | ||
| 72 | |||
| 73 | /* pt_regs and gdb_regs have the same 37 values. | ||
| 74 | * The rest of gdb_regs are unused and can't be changed. | ||
| 75 | * r0 register value can't be changed too. */ | ||
| 76 | for (i = 1; i < (sizeof(struct pt_regs) / 4) - 1; i++) | ||
| 77 | pt_regb[i] = gdb_regs[i]; | ||
| 78 | } | ||
| 79 | |||
| 80 | void microblaze_kgdb_break(struct pt_regs *regs) | ||
| 81 | { | ||
| 82 | if (kgdb_handle_exception(1, SIGTRAP, 0, regs) != 0) | ||
| 83 | return 0; | ||
| 84 | |||
| 85 | /* Jump over the first arch_kgdb_breakpoint which is barrier to | ||
| 86 | * get kgdb work. The same solution is used for powerpc */ | ||
| 87 | if (*(u32 *) (regs->pc) == *(u32 *) (&arch_kgdb_ops.gdb_bpt_instr)) | ||
| 88 | regs->pc += BREAK_INSTR_SIZE; | ||
| 89 | } | ||
| 90 | |||
| 91 | /* untested */ | ||
| 92 | void sleeping_thread_to_gdb_regs(unsigned long *gdb_regs, struct task_struct *p) | ||
| 93 | { | ||
| 94 | int i; | ||
| 95 | unsigned long *pt_regb = (unsigned long *)(p->thread.regs); | ||
| 96 | |||
| 97 | /* registers r0 - r31, pc, msr, ear, esr, fsr + do not save pt_mode */ | ||
| 98 | for (i = 0; i < (sizeof(struct pt_regs) / 4) - 1; i++) | ||
| 99 | gdb_regs[i] = pt_regb[i]; | ||
| 100 | |||
| 101 | /* pvr part - we have 11 pvr regs */ | ||
| 102 | for (i = 0; i < sizeof(struct pvr_s)/4; i++) | ||
| 103 | gdb_regs[GDB_PVR + i] = pvr.pvr[i]; | ||
| 104 | } | ||
| 105 | |||
| 106 | void kgdb_arch_set_pc(struct pt_regs *regs, unsigned long ip) | ||
| 107 | { | ||
| 108 | regs->pc = ip; | ||
| 109 | } | ||
| 110 | |||
| 111 | int kgdb_arch_handle_exception(int vector, int signo, int err_code, | ||
| 112 | char *remcom_in_buffer, char *remcom_out_buffer, | ||
| 113 | struct pt_regs *regs) | ||
| 114 | { | ||
| 115 | char *ptr; | ||
| 116 | unsigned long address; | ||
| 117 | int cpu = smp_processor_id(); | ||
| 118 | |||
| 119 | switch (remcom_in_buffer[0]) { | ||
| 120 | case 'c': | ||
| 121 | /* handle the optional parameter */ | ||
| 122 | ptr = &remcom_in_buffer[1]; | ||
| 123 | if (kgdb_hex2long(&ptr, &address)) | ||
| 124 | regs->pc = address; | ||
| 125 | |||
| 126 | return 0; | ||
| 127 | } | ||
| 128 | return -1; /* this means that we do not want to exit from the handler */ | ||
| 129 | } | ||
| 130 | |||
| 131 | int kgdb_arch_init(void) | ||
| 132 | { | ||
| 133 | get_pvr(&pvr); /* Fill PVR structure */ | ||
| 134 | return 0; | ||
| 135 | } | ||
| 136 | |||
| 137 | void kgdb_arch_exit(void) | ||
| 138 | { | ||
| 139 | /* Nothing to do */ | ||
| 140 | } | ||
| 141 | |||
| 142 | /* | ||
| 143 | * Global data | ||
| 144 | */ | ||
| 145 | struct kgdb_arch arch_kgdb_ops = { | ||
| 146 | .gdb_bpt_instr = {0xba, 0x0c, 0x00, 0x18}, /* brki r16, 0x18 */ | ||
| 147 | }; | ||
diff --git a/arch/microblaze/mm/fault.c b/arch/microblaze/mm/fault.c index b224c650a18d..57bd2a09610c 100644 --- a/arch/microblaze/mm/fault.c +++ b/arch/microblaze/mm/fault.c | |||
| @@ -37,10 +37,6 @@ | |||
| 37 | #include <linux/uaccess.h> | 37 | #include <linux/uaccess.h> |
| 38 | #include <asm/exceptions.h> | 38 | #include <asm/exceptions.h> |
| 39 | 39 | ||
| 40 | #if defined(CONFIG_KGDB) | ||
| 41 | int debugger_kernel_faults = 1; | ||
| 42 | #endif | ||
| 43 | |||
| 44 | static unsigned long pte_misses; /* updated by do_page_fault() */ | 40 | static unsigned long pte_misses; /* updated by do_page_fault() */ |
| 45 | static unsigned long pte_errors; /* updated by do_page_fault() */ | 41 | static unsigned long pte_errors; /* updated by do_page_fault() */ |
| 46 | 42 | ||
| @@ -81,10 +77,6 @@ void bad_page_fault(struct pt_regs *regs, unsigned long address, int sig) | |||
| 81 | } | 77 | } |
| 82 | 78 | ||
| 83 | /* kernel has accessed a bad area */ | 79 | /* kernel has accessed a bad area */ |
| 84 | #if defined(CONFIG_KGDB) | ||
| 85 | if (debugger_kernel_faults) | ||
| 86 | debugger(regs); | ||
| 87 | #endif | ||
| 88 | die("kernel access of bad area", regs, sig); | 80 | die("kernel access of bad area", regs, sig); |
| 89 | } | 81 | } |
| 90 | 82 | ||
| @@ -115,13 +107,6 @@ void do_page_fault(struct pt_regs *regs, unsigned long address, | |||
| 115 | if ((error_code & 0x13) == 0x13 || (error_code & 0x11) == 0x11) | 107 | if ((error_code & 0x13) == 0x13 || (error_code & 0x11) == 0x11) |
| 116 | is_write = 0; | 108 | is_write = 0; |
| 117 | 109 | ||
| 118 | #if defined(CONFIG_KGDB) | ||
| 119 | if (debugger_fault_handler && regs->trap == 0x300) { | ||
| 120 | debugger_fault_handler(regs); | ||
| 121 | return; | ||
| 122 | } | ||
| 123 | #endif /* CONFIG_KGDB */ | ||
| 124 | |||
| 125 | if (unlikely(in_atomic() || !mm)) { | 110 | if (unlikely(in_atomic() || !mm)) { |
| 126 | if (kernel_mode(regs)) | 111 | if (kernel_mode(regs)) |
| 127 | goto bad_area_nosemaphore; | 112 | goto bad_area_nosemaphore; |
