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 /arch/microblaze/kernel/kgdb.c | |
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>
Diffstat (limited to 'arch/microblaze/kernel/kgdb.c')
-rw-r--r-- | arch/microblaze/kernel/kgdb.c | 147 |
1 files changed, 147 insertions, 0 deletions
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 | }; | ||