diff options
author | Chris Metcalf <cmetcalf@tilera.com> | 2013-08-07 12:11:56 -0400 |
---|---|---|
committer | Chris Metcalf <cmetcalf@tilera.com> | 2013-08-13 16:26:13 -0400 |
commit | 70d2b5958a04139fbffecf27791cf913dce8038e (patch) | |
tree | 7df7a5b793bf53dfd319faac4f711487770511ef /arch/tile/kernel | |
parent | dadf78bf0359ee87a9d0b19ce42dbce7d0a72a61 (diff) |
tile: improve illegal translation interrupt handling
First, don't re-enable interrupts blindly in the Linux trap handler.
We already handle page faults this way; synchronous interrupts like
ILL_TRANS will fire even when interrupts are disabled, and we don't
want to re-enable interrupts in that case.
For ILL_TRANS, we now pass the ILL_VA_PC reason into the trap handler
so we can report it properly; this is the address that caused the
illegal translation trap. We print the address as part of the
pr_alert() message now if it's coming from the kernel.
Signed-off-by: Chris Metcalf <cmetcalf@tilera.com>
Diffstat (limited to 'arch/tile/kernel')
-rw-r--r-- | arch/tile/kernel/intvec_64.S | 2 | ||||
-rw-r--r-- | arch/tile/kernel/traps.c | 25 |
2 files changed, 16 insertions, 11 deletions
diff --git a/arch/tile/kernel/intvec_64.S b/arch/tile/kernel/intvec_64.S index 38a60f27707c..562886db780b 100644 --- a/arch/tile/kernel/intvec_64.S +++ b/arch/tile/kernel/intvec_64.S | |||
@@ -492,7 +492,7 @@ intvec_\vecname: | |||
492 | mfspr r3, SPR_SYSTEM_SAVE_K_2 /* info about page fault */ | 492 | mfspr r3, SPR_SYSTEM_SAVE_K_2 /* info about page fault */ |
493 | .else | 493 | .else |
494 | .ifc \vecnum, INT_ILL_TRANS | 494 | .ifc \vecnum, INT_ILL_TRANS |
495 | mfspr r2, ILL_TRANS_REASON | 495 | mfspr r2, ILL_VA_PC |
496 | .else | 496 | .else |
497 | .ifc \vecnum, INT_DOUBLE_FAULT | 497 | .ifc \vecnum, INT_DOUBLE_FAULT |
498 | mfspr r2, SPR_SYSTEM_SAVE_K_2 /* double fault info from HV */ | 498 | mfspr r2, SPR_SYSTEM_SAVE_K_2 /* double fault info from HV */ |
diff --git a/arch/tile/kernel/traps.c b/arch/tile/kernel/traps.c index 5b19a23c8908..a1bbc5de4d00 100644 --- a/arch/tile/kernel/traps.c +++ b/arch/tile/kernel/traps.c | |||
@@ -222,8 +222,9 @@ void __kprobes do_trap(struct pt_regs *regs, int fault_num, | |||
222 | unsigned long address = 0; | 222 | unsigned long address = 0; |
223 | bundle_bits instr; | 223 | bundle_bits instr; |
224 | 224 | ||
225 | /* Re-enable interrupts. */ | 225 | /* Re-enable interrupts, if they were previously enabled. */ |
226 | local_irq_enable(); | 226 | if (!(regs->flags & PT_FLAGS_DISABLE_IRQ)) |
227 | local_irq_enable(); | ||
227 | 228 | ||
228 | /* | 229 | /* |
229 | * If it hits in kernel mode and we can't fix it up, just exit the | 230 | * If it hits in kernel mode and we can't fix it up, just exit the |
@@ -231,7 +232,8 @@ void __kprobes do_trap(struct pt_regs *regs, int fault_num, | |||
231 | */ | 232 | */ |
232 | if (!user_mode(regs)) { | 233 | if (!user_mode(regs)) { |
233 | const char *name; | 234 | const char *name; |
234 | if (fixup_exception(regs)) /* only UNALIGN_DATA in practice */ | 235 | char buf[100]; |
236 | if (fixup_exception(regs)) /* ILL_TRANS or UNALIGN_DATA */ | ||
235 | return; | 237 | return; |
236 | if (fault_num >= 0 && | 238 | if (fault_num >= 0 && |
237 | fault_num < sizeof(int_name)/sizeof(int_name[0]) && | 239 | fault_num < sizeof(int_name)/sizeof(int_name[0]) && |
@@ -239,10 +241,16 @@ void __kprobes do_trap(struct pt_regs *regs, int fault_num, | |||
239 | name = int_name[fault_num]; | 241 | name = int_name[fault_num]; |
240 | else | 242 | else |
241 | name = "Unknown interrupt"; | 243 | name = "Unknown interrupt"; |
242 | pr_alert("Kernel took bad trap %d (%s) at PC %#lx\n", | ||
243 | fault_num, name, regs->pc); | ||
244 | if (fault_num == INT_GPV) | 244 | if (fault_num == INT_GPV) |
245 | pr_alert("GPV_REASON is %#lx\n", reason); | 245 | snprintf(buf, sizeof(buf), "; GPV_REASON %#lx", reason); |
246 | #ifdef __tilegx__ | ||
247 | else if (fault_num == INT_ILL_TRANS) | ||
248 | snprintf(buf, sizeof(buf), "; address %#lx", reason); | ||
249 | #endif | ||
250 | else | ||
251 | buf[0] = '\0'; | ||
252 | pr_alert("Kernel took bad trap %d (%s) at PC %#lx%s\n", | ||
253 | fault_num, name, regs->pc, buf); | ||
246 | show_regs(regs); | 254 | show_regs(regs); |
247 | do_exit(SIGKILL); /* FIXME: implement i386 die() */ | 255 | do_exit(SIGKILL); /* FIXME: implement i386 die() */ |
248 | return; | 256 | return; |
@@ -324,11 +332,8 @@ void __kprobes do_trap(struct pt_regs *regs, int fault_num, | |||
324 | fill_ra_stack(); | 332 | fill_ra_stack(); |
325 | 333 | ||
326 | signo = SIGSEGV; | 334 | signo = SIGSEGV; |
335 | address = reason; | ||
327 | code = SEGV_MAPERR; | 336 | code = SEGV_MAPERR; |
328 | if (reason & SPR_ILL_TRANS_REASON__I_STREAM_VA_RMASK) | ||
329 | address = regs->pc; | ||
330 | else | ||
331 | address = 0; /* FIXME: GX: single-step for address */ | ||
332 | break; | 337 | break; |
333 | } | 338 | } |
334 | #endif | 339 | #endif |