aboutsummaryrefslogtreecommitdiffstats
path: root/arch/tile/kernel/traps.c
diff options
context:
space:
mode:
authorChris Metcalf <cmetcalf@tilera.com>2013-08-07 12:11:56 -0400
committerChris Metcalf <cmetcalf@tilera.com>2013-08-13 16:26:13 -0400
commit70d2b5958a04139fbffecf27791cf913dce8038e (patch)
tree7df7a5b793bf53dfd319faac4f711487770511ef /arch/tile/kernel/traps.c
parentdadf78bf0359ee87a9d0b19ce42dbce7d0a72a61 (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/traps.c')
-rw-r--r--arch/tile/kernel/traps.c25
1 files changed, 15 insertions, 10 deletions
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