diff options
author | Paul Mundt <lethal@linux-sh.org> | 2006-12-13 03:40:05 -0500 |
---|---|---|
committer | Paul Mundt <lethal@linux-sh.org> | 2007-02-12 20:54:43 -0500 |
commit | f413d0d9fa7abcecc40e115cf4aead372d164a75 (patch) | |
tree | 72146d33f19d338076c55b7597dc15ce3f357f2c | |
parent | ec2f9d1331f658433411c58077871e1eef4ee1b4 (diff) |
sh: Use a jump call table for debug trap handlers.
This rips out most of the needlessly complicated sh_bios and kgdb
trap handling, and forces it all through a common fast dispatch path.
As more debug traps are inserted, it's important to keep them in sync
for all of the parts, not just SH-3/4.
As the SH-2 parts are unable to do traps in the >= 0x40 range, we
restrict the debug traps to the 0x30-0x3f range on all parts, and
also bump the kgdb breakpoint trap down in to this range (from 0xff
to 0x3c) so it's possible to use for nommu.
Optionally, this table can be padded out to catch spurious traps for
SH-3/4, but we don't do that yet..
Signed-off-by: Paul Mundt <lethal@linux-sh.org>
-rw-r--r-- | arch/sh/kernel/Makefile | 3 | ||||
-rw-r--r-- | arch/sh/kernel/cpu/sh2/entry.S | 2 | ||||
-rw-r--r-- | arch/sh/kernel/cpu/sh3/entry.S | 2 | ||||
-rw-r--r-- | arch/sh/kernel/debugtraps.S | 41 | ||||
-rw-r--r-- | arch/sh/kernel/entry-common.S | 119 | ||||
-rw-r--r-- | arch/sh/kernel/kgdb_stub.c | 7 | ||||
-rw-r--r-- | arch/sh/kernel/process.c | 24 | ||||
-rw-r--r-- | include/asm-sh/kgdb.h | 8 |
8 files changed, 117 insertions, 89 deletions
diff --git a/arch/sh/kernel/Makefile b/arch/sh/kernel/Makefile index 2f6d2bcb1c93..ff30d7f58043 100644 --- a/arch/sh/kernel/Makefile +++ b/arch/sh/kernel/Makefile | |||
@@ -6,7 +6,8 @@ extra-y := head.o init_task.o vmlinux.lds | |||
6 | 6 | ||
7 | obj-y := process.o signal.o traps.o irq.o \ | 7 | obj-y := process.o signal.o traps.o irq.o \ |
8 | ptrace.o setup.o time.o sys_sh.o semaphore.o \ | 8 | ptrace.o setup.o time.o sys_sh.o semaphore.o \ |
9 | io.o io_generic.o sh_ksyms.o syscalls.o | 9 | io.o io_generic.o sh_ksyms.o syscalls.o \ |
10 | debugtraps.o | ||
10 | 11 | ||
11 | obj-y += cpu/ timers/ | 12 | obj-y += cpu/ timers/ |
12 | obj-$(CONFIG_VSYSCALL) += vsyscall/ | 13 | obj-$(CONFIG_VSYSCALL) += vsyscall/ |
diff --git a/arch/sh/kernel/cpu/sh2/entry.S b/arch/sh/kernel/cpu/sh2/entry.S index d51fa5e9904a..8de48102ac85 100644 --- a/arch/sh/kernel/cpu/sh2/entry.S +++ b/arch/sh/kernel/cpu/sh2/entry.S | |||
@@ -206,7 +206,7 @@ trap_entry: | |||
206 | 206 | ||
207 | #if defined(CONFIG_SH_STANDARD_BIOS) | 207 | #if defined(CONFIG_SH_STANDARD_BIOS) |
208 | /* Unwind the stack and jmp to the debug entry */ | 208 | /* Unwind the stack and jmp to the debug entry */ |
209 | debug_kernel_fw: | 209 | ENTRY(sh_bios_handler) |
210 | mov r15,r0 | 210 | mov r15,r0 |
211 | add #(22-4)*4-4,r0 | 211 | add #(22-4)*4-4,r0 |
212 | ldc.l @r0+,gbr | 212 | ldc.l @r0+,gbr |
diff --git a/arch/sh/kernel/cpu/sh3/entry.S b/arch/sh/kernel/cpu/sh3/entry.S index 8c0dc2700c69..014ac37ca16a 100644 --- a/arch/sh/kernel/cpu/sh3/entry.S +++ b/arch/sh/kernel/cpu/sh3/entry.S | |||
@@ -173,7 +173,7 @@ call_dae: | |||
173 | 173 | ||
174 | #if defined(CONFIG_SH_STANDARD_BIOS) | 174 | #if defined(CONFIG_SH_STANDARD_BIOS) |
175 | /* Unwind the stack and jmp to the debug entry */ | 175 | /* Unwind the stack and jmp to the debug entry */ |
176 | debug_kernel_fw: | 176 | ENTRY(sh_bios_handler) |
177 | mov.l @r15+, r0 | 177 | mov.l @r15+, r0 |
178 | mov.l @r15+, r1 | 178 | mov.l @r15+, r1 |
179 | mov.l @r15+, r2 | 179 | mov.l @r15+, r2 |
diff --git a/arch/sh/kernel/debugtraps.S b/arch/sh/kernel/debugtraps.S new file mode 100644 index 000000000000..13b66746410a --- /dev/null +++ b/arch/sh/kernel/debugtraps.S | |||
@@ -0,0 +1,41 @@ | |||
1 | /* | ||
2 | * arch/sh/kernel/debugtraps.S | ||
3 | * | ||
4 | * Debug trap jump tables for SuperH | ||
5 | * | ||
6 | * Copyright (C) 2006 Paul Mundt | ||
7 | * | ||
8 | * This file is subject to the terms and conditions of the GNU General Public | ||
9 | * License. See the file "COPYING" in the main directory of this archive | ||
10 | * for more details. | ||
11 | */ | ||
12 | #include <linux/sys.h> | ||
13 | #include <linux/linkage.h> | ||
14 | |||
15 | #if !defined(CONFIG_SH_KGDB) | ||
16 | #define kgdb_handle_exception debug_trap_handler | ||
17 | #endif | ||
18 | |||
19 | #if !defined(CONFIG_SH_STANDARD_BIOS) | ||
20 | #define sh_bios_handler debug_trap_handler | ||
21 | #endif | ||
22 | |||
23 | .data | ||
24 | |||
25 | ENTRY(debug_trap_table) | ||
26 | .long debug_trap_handler /* 0x30 */ | ||
27 | .long debug_trap_handler /* 0x31 */ | ||
28 | .long debug_trap_handler /* 0x32 */ | ||
29 | .long debug_trap_handler /* 0x33 */ | ||
30 | .long debug_trap_handler /* 0x34 */ | ||
31 | .long debug_trap_handler /* 0x35 */ | ||
32 | .long debug_trap_handler /* 0x36 */ | ||
33 | .long debug_trap_handler /* 0x37 */ | ||
34 | .long debug_trap_handler /* 0x38 */ | ||
35 | .long debug_trap_handler /* 0x39 */ | ||
36 | .long debug_trap_handler /* 0x3a */ | ||
37 | .long debug_trap_handler /* 0x3b */ | ||
38 | .long kgdb_handle_exception /* 0x3c */ | ||
39 | .long debug_trap_handler /* 0x3d */ | ||
40 | .long bug_trap_handler /* 0x3e */ | ||
41 | .long sh_bios_handler /* 0x3f */ | ||
diff --git a/arch/sh/kernel/entry-common.S b/arch/sh/kernel/entry-common.S index fc279aeb73ab..ab4ebb856c2a 100644 --- a/arch/sh/kernel/entry-common.S +++ b/arch/sh/kernel/entry-common.S | |||
@@ -54,79 +54,24 @@ | |||
54 | # define resume_kernel __restore_all | 54 | # define resume_kernel __restore_all |
55 | #endif | 55 | #endif |
56 | 56 | ||
57 | #if defined(CONFIG_SH_STANDARD_BIOS) || defined(CONFIG_SH_KGDB) | ||
58 | ! Handle kernel debug if either kgdb (SW) or gdb-stub (FW) is present. | ||
59 | ! If both are configured, handle the debug traps (breakpoints) in SW, | ||
60 | ! but still allow BIOS traps to FW. | ||
61 | |||
62 | .align 2 | ||
63 | debug_kernel: | ||
64 | #if defined(CONFIG_SH_STANDARD_BIOS) && defined(CONFIG_SH_KGDB) | ||
65 | /* Force BIOS call to FW (debug_trap put TRA in r8) */ | ||
66 | mov r8,r0 | ||
67 | shlr2 r0 | ||
68 | cmp/eq #0x3f,r0 | ||
69 | bt debug_kernel_fw | ||
70 | #endif /* CONFIG_SH_STANDARD_BIOS && CONFIG_SH_KGDB */ | ||
71 | |||
72 | debug_enter: | ||
73 | #if defined(CONFIG_SH_KGDB) | ||
74 | /* Jump to kgdb, pass stacked regs as arg */ | ||
75 | debug_kernel_sw: | ||
76 | mov.l 3f, r0 | ||
77 | jmp @r0 | ||
78 | mov r15, r4 | ||
79 | .align 2 | ||
80 | 3: .long kgdb_handle_exception | ||
81 | #endif /* CONFIG_SH_KGDB */ | ||
82 | #ifdef CONFIG_SH_STANDARD_BIOS | ||
83 | bra debug_kernel_fw | ||
84 | nop | ||
85 | #endif | ||
86 | #endif /* CONFIG_SH_STANDARD_BIOS || CONFIG_SH_KGDB */ | ||
87 | |||
88 | .align 2 | ||
89 | debug_trap: | ||
90 | #if defined(CONFIG_SH_STANDARD_BIOS) || defined(CONFIG_SH_KGDB) | ||
91 | mov r8, r0 | ||
92 | shlr2 r0 | ||
93 | cmp/eq #0x3f, r0 ! sh_bios() trap | ||
94 | bf 1f | ||
95 | #ifdef CONFIG_SH_KGDB | ||
96 | cmp/eq #0xff, r0 ! XXX: KGDB trap, fix for SH-2. | ||
97 | bf 1f | ||
98 | #endif | ||
99 | mov #OFF_SR, r0 | ||
100 | mov.l @(r0,r15), r0 ! get status register | ||
101 | shll r0 | ||
102 | shll r0 ! kernel space? | ||
103 | bt/s debug_kernel | ||
104 | 1: | ||
105 | #endif | ||
106 | mov.l @r15, r0 ! Restore R0 value | ||
107 | mov.l 1f, r8 | ||
108 | jmp @r8 | ||
109 | nop | ||
110 | 57 | ||
111 | .align 2 | 58 | .align 2 |
112 | ENTRY(exception_error) | 59 | ENTRY(exception_error) |
113 | ! | 60 | ! |
114 | #ifdef CONFIG_TRACE_IRQFLAGS | 61 | #ifdef CONFIG_TRACE_IRQFLAGS |
115 | mov.l 3f, r0 | 62 | mov.l 2f, r0 |
116 | jsr @r0 | 63 | jsr @r0 |
117 | nop | 64 | nop |
118 | #endif | 65 | #endif |
119 | sti | 66 | sti |
120 | mov.l 2f, r0 | 67 | mov.l 1f, r0 |
121 | jmp @r0 | 68 | jmp @r0 |
122 | nop | 69 | nop |
123 | 70 | ||
124 | ! | ||
125 | .align 2 | 71 | .align 2 |
126 | 1: .long break_point_trap_software | 72 | 1: .long do_exception_error |
127 | 2: .long do_exception_error | ||
128 | #ifdef CONFIG_TRACE_IRQFLAGS | 73 | #ifdef CONFIG_TRACE_IRQFLAGS |
129 | 3: .long trace_hardirqs_on | 74 | 2: .long trace_hardirqs_on |
130 | #endif | 75 | #endif |
131 | 76 | ||
132 | .align 2 | 77 | .align 2 |
@@ -331,16 +276,31 @@ __restore_all: | |||
331 | 1: .long restore_all | 276 | 1: .long restore_all |
332 | 277 | ||
333 | .align 2 | 278 | .align 2 |
334 | not_syscall_tra: | ||
335 | bra debug_trap | ||
336 | nop | ||
337 | |||
338 | .align 2 | ||
339 | syscall_badsys: ! Bad syscall number | 279 | syscall_badsys: ! Bad syscall number |
340 | mov #-ENOSYS, r0 | 280 | mov #-ENOSYS, r0 |
341 | bra resume_userspace | 281 | bra resume_userspace |
342 | mov.l r0, @(OFF_R0,r15) ! Return value | 282 | mov.l r0, @(OFF_R0,r15) ! Return value |
343 | 283 | ||
284 | /* | ||
285 | * The main debug trap handler. | ||
286 | * | ||
287 | * r8=TRA (not the trap number!) | ||
288 | * | ||
289 | * Note: This assumes that the trapa value is left in its original | ||
290 | * form (without the shlr2 shift) so the calculation for the jump | ||
291 | * call table offset remains a simple in place mask. | ||
292 | */ | ||
293 | debug_trap: | ||
294 | mov r8, r0 | ||
295 | and #(0xf << 2), r0 | ||
296 | mov.l 1f, r8 | ||
297 | add r0, r8 | ||
298 | mov.l @r8, r8 | ||
299 | jmp @r8 | ||
300 | nop | ||
301 | |||
302 | .align 2 | ||
303 | 1: .long debug_trap_table | ||
344 | 304 | ||
345 | /* | 305 | /* |
346 | * Syscall interface: | 306 | * Syscall interface: |
@@ -348,17 +308,19 @@ syscall_badsys: ! Bad syscall number | |||
348 | * Syscall #: R3 | 308 | * Syscall #: R3 |
349 | * Arguments #0 to #3: R4--R7 | 309 | * Arguments #0 to #3: R4--R7 |
350 | * Arguments #4 to #6: R0, R1, R2 | 310 | * Arguments #4 to #6: R0, R1, R2 |
351 | * TRA: (number of arguments + 0x10) x 4 | 311 | * TRA: (number of arguments + ABI revision) x 4 |
352 | * | 312 | * |
353 | * This code also handles delegating other traps to the BIOS/gdb stub | 313 | * This code also handles delegating other traps to the BIOS/gdb stub |
354 | * according to: | 314 | * according to: |
355 | * | 315 | * |
356 | * Trap number | 316 | * Trap number |
357 | * (TRA>>2) Purpose | 317 | * (TRA>>2) Purpose |
358 | * -------- ------- | 318 | * -------- ------- |
359 | * 0x0-0xf old syscall ABI | 319 | * 0x00-0x0f original SH-3/4 syscall ABI (not in general use). |
360 | * 0x10-0x1f new syscall ABI | 320 | * 0x10-0x1f general SH-3/4 syscall ABI. |
361 | * 0x20-0xff delegated through debug_trap to BIOS/gdb stub. | 321 | * 0x20-0x2f syscall ABI for SH-2 parts. |
322 | * 0x30-0x3f debug traps used by the kernel. | ||
323 | * 0x40-0xff Not supported by all parts, so left unhandled. | ||
362 | * | 324 | * |
363 | * Note: When we're first called, the TRA value must be shifted | 325 | * Note: When we're first called, the TRA value must be shifted |
364 | * right 2 bits in order to get the value that was used as the "trapa" | 326 | * right 2 bits in order to get the value that was used as the "trapa" |
@@ -375,17 +337,22 @@ ret_from_fork: | |||
375 | nop | 337 | nop |
376 | .align 2 | 338 | .align 2 |
377 | 1: .long schedule_tail | 339 | 1: .long schedule_tail |
378 | ! | 340 | |
341 | /* | ||
342 | * The poorly named main trapa decode and dispatch routine, for | ||
343 | * system calls and debug traps through their respective jump tables. | ||
344 | */ | ||
379 | ENTRY(system_call) | 345 | ENTRY(system_call) |
380 | #if !defined(CONFIG_CPU_SH2) | 346 | #if !defined(CONFIG_CPU_SH2) |
381 | mov.l 1f, r9 | 347 | mov.l 1f, r9 |
382 | mov.l @r9, r8 ! Read from TRA (Trap Address) Register | 348 | mov.l @r9, r8 ! Read from TRA (Trap Address) Register |
383 | #endif | 349 | #endif |
384 | ! | 350 | /* |
385 | ! Is the trap argument >= 0x20? (TRA will be >= 0x80) | 351 | * Check the trap type |
386 | mov #0x7f, r9 | 352 | */ |
353 | mov #((0x20 << 2) - 1), r9 | ||
387 | cmp/hi r9, r8 | 354 | cmp/hi r9, r8 |
388 | bt/s not_syscall_tra | 355 | bt/s debug_trap ! it's a debug trap.. |
389 | mov #OFF_TRA, r9 | 356 | mov #OFF_TRA, r9 |
390 | add r15, r9 | 357 | add r15, r9 |
391 | mov.l r8, @r9 ! set TRA value to tra | 358 | mov.l r8, @r9 ! set TRA value to tra |
diff --git a/arch/sh/kernel/kgdb_stub.c b/arch/sh/kernel/kgdb_stub.c index 9c6315f0335d..d8927d85492e 100644 --- a/arch/sh/kernel/kgdb_stub.c +++ b/arch/sh/kernel/kgdb_stub.c | |||
@@ -1323,8 +1323,11 @@ static void kgdb_command_loop(const int excep_code, const int trapa_value) | |||
1323 | } | 1323 | } |
1324 | 1324 | ||
1325 | /* There has been an exception, most likely a breakpoint. */ | 1325 | /* There has been an exception, most likely a breakpoint. */ |
1326 | void kgdb_handle_exception(struct pt_regs *regs) | 1326 | asmlinkage void kgdb_handle_exception(unsigned long r4, unsigned long r5, |
1327 | unsigned long r6, unsigned long r7, | ||
1328 | struct pt_regs __regs) | ||
1327 | { | 1329 | { |
1330 | struct pt_regs *regs = RELOC_HIDE(&__regs, 0); | ||
1328 | int excep_code, vbr_val; | 1331 | int excep_code, vbr_val; |
1329 | int count; | 1332 | int count; |
1330 | int trapa_value = ctrl_inl(TRA); | 1333 | int trapa_value = ctrl_inl(TRA); |
@@ -1368,8 +1371,6 @@ void kgdb_handle_exception(struct pt_regs *regs) | |||
1368 | 1371 | ||
1369 | vbr_val = trap_registers.vbr; | 1372 | vbr_val = trap_registers.vbr; |
1370 | asm("ldc %0, vbr": :"r"(vbr_val)); | 1373 | asm("ldc %0, vbr": :"r"(vbr_val)); |
1371 | |||
1372 | return; | ||
1373 | } | 1374 | } |
1374 | 1375 | ||
1375 | /* Trigger a breakpoint by function */ | 1376 | /* Trigger a breakpoint by function */ |
diff --git a/arch/sh/kernel/process.c b/arch/sh/kernel/process.c index 486c06e18033..cc8f306fd682 100644 --- a/arch/sh/kernel/process.c +++ b/arch/sh/kernel/process.c | |||
@@ -493,9 +493,27 @@ asmlinkage void break_point_trap(void) | |||
493 | force_sig(SIGTRAP, current); | 493 | force_sig(SIGTRAP, current); |
494 | } | 494 | } |
495 | 495 | ||
496 | asmlinkage void break_point_trap_software(unsigned long r4, unsigned long r5, | 496 | /* |
497 | unsigned long r6, unsigned long r7, | 497 | * Generic trap handler. |
498 | struct pt_regs __regs) | 498 | */ |
499 | asmlinkage void debug_trap_handler(unsigned long r4, unsigned long r5, | ||
500 | unsigned long r6, unsigned long r7, | ||
501 | struct pt_regs __regs) | ||
502 | { | ||
503 | struct pt_regs *regs = RELOC_HIDE(&__regs, 0); | ||
504 | |||
505 | /* Rewind */ | ||
506 | regs->pc -= 2; | ||
507 | |||
508 | force_sig(SIGTRAP, current); | ||
509 | } | ||
510 | |||
511 | /* | ||
512 | * Special handler for BUG() traps. | ||
513 | */ | ||
514 | asmlinkage void bug_trap_handler(unsigned long r4, unsigned long r5, | ||
515 | unsigned long r6, unsigned long r7, | ||
516 | struct pt_regs __regs) | ||
499 | { | 517 | { |
500 | struct pt_regs *regs = RELOC_HIDE(&__regs, 0); | 518 | struct pt_regs *regs = RELOC_HIDE(&__regs, 0); |
501 | 519 | ||
diff --git a/include/asm-sh/kgdb.h b/include/asm-sh/kgdb.h index 7b26f53fe343..0095c665d272 100644 --- a/include/asm-sh/kgdb.h +++ b/include/asm-sh/kgdb.h | |||
@@ -85,10 +85,10 @@ extern int setjmp(jmp_buf __jmpb); | |||
85 | #define KGDB_PRINTK(...) printk("KGDB: " __VA_ARGS__) | 85 | #define KGDB_PRINTK(...) printk("KGDB: " __VA_ARGS__) |
86 | 86 | ||
87 | /* Forced breakpoint */ | 87 | /* Forced breakpoint */ |
88 | #define BREAKPOINT() do { \ | 88 | #define BREAKPOINT() \ |
89 | if (kgdb_enabled) { \ | 89 | do { \ |
90 | asm volatile("trapa #0xff"); \ | 90 | if (kgdb_enabled) \ |
91 | } \ | 91 | __asm__ __volatile__("trapa #0x3c"); \ |
92 | } while (0) | 92 | } while (0) |
93 | 93 | ||
94 | /* KGDB should be able to flush all kernel text space */ | 94 | /* KGDB should be able to flush all kernel text space */ |