#include <asm/psrcompat.h>
#include <asm/processor.h>
#include <asm/timer.h>
+#include <asm/kdebug.h>
#ifdef CONFIG_KMOD
#include <linux/kmod.h>
#endif
+struct notifier_block *sparc64die_chain;
+static spinlock_t die_notifier_lock = SPIN_LOCK_UNLOCKED;
+
+int register_die_notifier(struct notifier_block *nb)
+{
+ int err = 0;
+ unsigned long flags;
+ spin_lock_irqsave(&die_notifier_lock, flags);
+ err = notifier_chain_register(&sparc64die_chain, nb);
+ spin_unlock_irqrestore(&die_notifier_lock, flags);
+ return err;
+}
+
/* When an irrecoverable trap occurs at tl > 0, the trap entry
* code logs the trap state registers at every level in the trap
* stack. It is found at (pt_regs + sizeof(pt_regs)) and the layout
}
}
-void bad_trap (struct pt_regs *regs, long lvl)
+void do_call_debug(struct pt_regs *regs)
+{
+ notify_die(DIE_CALL, "debug call", regs, 0, 255, SIGINT);
+}
+
+void bad_trap(struct pt_regs *regs, long lvl)
{
char buffer[32];
siginfo_t info;
+ if (notify_die(DIE_TRAP, "bad trap", regs,
+ 0, lvl, SIGTRAP) == NOTIFY_STOP)
+ return;
+
if (lvl < 0x100) {
sprintf(buffer, "Bad hw trap %lx at tl0\n", lvl);
die_if_kernel(buffer, regs);
lvl -= 0x100;
if (regs->tstate & TSTATE_PRIV) {
sprintf(buffer, "Kernel bad sw trap %lx", lvl);
- die_if_kernel (buffer, regs);
+ die_if_kernel(buffer, regs);
}
if (test_thread_flag(TIF_32BIT)) {
regs->tpc &= 0xffffffff;
info.si_signo = SIGILL;
info.si_errno = 0;
info.si_code = ILL_ILLTRP;
- info.si_addr = (void *)regs->tpc;
+ info.si_addr = (void __user *)regs->tpc;
info.si_trapno = lvl;
force_sig_info(SIGILL, &info, current);
}
-void bad_trap_tl1 (struct pt_regs *regs, long lvl)
+void bad_trap_tl1(struct pt_regs *regs, long lvl)
{
char buffer[32];
+ if (notify_die(DIE_TRAP_TL1, "bad trap tl1", regs,
+ 0, lvl, SIGTRAP) == NOTIFY_STOP)
+ return;
+
dump_tl1_traplog((struct tl1_traplog *)(regs + 1));
sprintf (buffer, "Bad trap %lx at tl>0", lvl);
{
siginfo_t info;
+ if (notify_die(DIE_TRAP, "instruction access exception", regs,
+ 0, 0x8, SIGTRAP) == NOTIFY_STOP)
+ return;
+
if (regs->tstate & TSTATE_PRIV) {
printk("instruction_access_exception: SFSR[%016lx] SFAR[%016lx], going.\n",
sfsr, sfar);
info.si_signo = SIGSEGV;
info.si_errno = 0;
info.si_code = SEGV_MAPERR;
- info.si_addr = (void *)regs->tpc;
+ info.si_addr = (void __user *)regs->tpc;
info.si_trapno = 0;
force_sig_info(SIGSEGV, &info, current);
}
void instruction_access_exception_tl1(struct pt_regs *regs,
unsigned long sfsr, unsigned long sfar)
{
+ if (notify_die(DIE_TRAP_TL1, "instruction access exception tl1", regs,
+ 0, 0x8, SIGTRAP) == NOTIFY_STOP)
+ return;
+
dump_tl1_traplog((struct tl1_traplog *)(regs + 1));
instruction_access_exception(regs, sfsr, sfar);
}
-void data_access_exception (struct pt_regs *regs,
- unsigned long sfsr, unsigned long sfar)
+void data_access_exception(struct pt_regs *regs,
+ unsigned long sfsr, unsigned long sfar)
{
siginfo_t info;
+ if (notify_die(DIE_TRAP, "data access exception", regs,
+ 0, 0x30, SIGTRAP) == NOTIFY_STOP)
+ return;
+
if (regs->tstate & TSTATE_PRIV) {
/* Test if this comes from uaccess places. */
unsigned long fixup;
info.si_signo = SIGSEGV;
info.si_errno = 0;
info.si_code = SEGV_MAPERR;
- info.si_addr = (void *)sfar;
+ info.si_addr = (void __user *)sfar;
info.si_trapno = 0;
force_sig_info(SIGSEGV, &info, current);
}
spitfire_clean_and_reenable_l1_caches();
+ if (notify_die(DIE_TRAP, "instruction access exception", regs,
+ 0, 0x8, SIGTRAP) == NOTIFY_STOP)
+ return;
+
info.si_signo = SIGBUS;
info.si_errno = 0;
info.si_code = BUS_OBJERR;
void do_dae(struct pt_regs *regs)
{
+ siginfo_t info;
+
#ifdef CONFIG_PCI
if (pci_poke_in_progress && pci_poke_cpu == smp_processor_id()) {
spitfire_clean_and_reenable_l1_caches();
return;
}
#endif
- do_iae(regs);
+ spitfire_clean_and_reenable_l1_caches();
+
+ if (notify_die(DIE_TRAP, "data access exception", regs,
+ 0, 0x30, SIGTRAP) == NOTIFY_STOP)
+ return;
+
+ info.si_signo = SIGBUS;
+ info.si_errno = 0;
+ info.si_code = BUS_OBJERR;
+ info.si_addr = (void *)0;
+ info.si_trapno = 0;
+ force_sig_info(SIGBUS, &info, current);
}
static char ecc_syndrome_table[] = {
}
info.si_signo = SIGFPE;
info.si_errno = 0;
- info.si_addr = (void *)regs->tpc;
+ info.si_addr = (void __user *)regs->tpc;
info.si_trapno = 0;
info.si_code = __SI_FAULT;
if ((fsr & 0x1c000) == (1 << 14)) {
void do_fpieee(struct pt_regs *regs)
{
+ if (notify_die(DIE_TRAP, "fpu exception ieee", regs,
+ 0, 0x24, SIGFPE) == NOTIFY_STOP)
+ return;
+
do_fpe_common(regs);
}
struct fpustate *f = FPUSTATE;
int ret = 0;
+ if (notify_die(DIE_TRAP, "fpu exception other", regs,
+ 0, 0x25, SIGFPE) == NOTIFY_STOP)
+ return;
+
switch ((current_thread_info()->xfsr[0] & 0x1c000)) {
case (2 << 14): /* unfinished_FPop */
case (3 << 14): /* unimplemented_FPop */
{
siginfo_t info;
+ if (notify_die(DIE_TRAP, "tagged arithmetic overflow", regs,
+ 0, 0x26, SIGEMT) == NOTIFY_STOP)
+ return;
+
if (regs->tstate & TSTATE_PRIV)
die_if_kernel("Penguin overflow trap from kernel mode", regs);
if (test_thread_flag(TIF_32BIT)) {
info.si_signo = SIGEMT;
info.si_errno = 0;
info.si_code = EMT_TAGOVF;
- info.si_addr = (void *)regs->tpc;
+ info.si_addr = (void __user *)regs->tpc;
info.si_trapno = 0;
force_sig_info(SIGEMT, &info, current);
}
{
siginfo_t info;
+ if (notify_die(DIE_TRAP, "integer division by zero", regs,
+ 0, 0x28, SIGFPE) == NOTIFY_STOP)
+ return;
+
if (regs->tstate & TSTATE_PRIV)
die_if_kernel("TL0: Kernel divide by zero.", regs);
if (test_thread_flag(TIF_32BIT)) {
info.si_signo = SIGFPE;
info.si_errno = 0;
info.si_code = FPE_INTDIV;
- info.si_addr = (void *)regs->tpc;
+ info.si_addr = (void __user *)regs->tpc;
info.si_trapno = 0;
force_sig_info(SIGFPE, &info, current);
}
printk("\n");
}
-void user_instruction_dump (unsigned int *pc)
+static void user_instruction_dump (unsigned int __user *pc)
{
int i;
unsigned int buf[9];
EXPORT_SYMBOL(dump_stack);
+static inline int is_kernel_stack(struct task_struct *task,
+ struct reg_window *rw)
+{
+ unsigned long rw_addr = (unsigned long) rw;
+ unsigned long thread_base, thread_end;
+
+ if (rw_addr < PAGE_OFFSET) {
+ if (task != &init_task)
+ return 0;
+ }
+
+ thread_base = (unsigned long) task->thread_info;
+ thread_end = thread_base + sizeof(union thread_union);
+ if (rw_addr >= thread_base &&
+ rw_addr < thread_end &&
+ !(rw_addr & 0x7UL))
+ return 1;
+
+ return 0;
+}
+
+static inline struct reg_window *kernel_stack_up(struct reg_window *rw)
+{
+ unsigned long fp = rw->ins[6];
+
+ if (!fp)
+ return NULL;
+
+ return (struct reg_window *) (fp + STACK_BIAS);
+}
+
void die_if_kernel(char *str, struct pt_regs *regs)
{
static int die_counter;
" \\__U_/\n");
printk("%s(%d): %s [#%d]\n", current->comm, current->pid, str, ++die_counter);
+ notify_die(DIE_OOPS, str, regs, 0, 255, SIGSEGV);
__asm__ __volatile__("flushw");
__show_regs(regs);
if (regs->tstate & TSTATE_PRIV) {
/* Stop the back trace when we hit userland or we
* find some badly aligned kernel stack.
*/
- while (rw &&
- count++ < 30 &&
- (((unsigned long) rw) >= PAGE_OFFSET) &&
- (char *) rw < ((char *) current)
- + sizeof (union thread_union) &&
- !(((unsigned long) rw) & 0x7)) {
+ while (rw &&
+ count++ < 30&&
+ is_kernel_stack(current, rw)) {
printk("Caller[%016lx]", rw->ins[7]);
print_symbol(": %s", rw->ins[7]);
printk("\n");
- rw = (struct reg_window *)
- (rw->ins[6] + STACK_BIAS);
+
+ rw = kernel_stack_up(rw);
}
instruction_dump ((unsigned int *) regs->tpc);
} else {
regs->tpc &= 0xffffffff;
regs->tnpc &= 0xffffffff;
}
- user_instruction_dump ((unsigned int *) regs->tpc);
+ user_instruction_dump ((unsigned int __user *) regs->tpc);
}
#ifdef CONFIG_SMP
smp_report_regs();
u32 insn;
siginfo_t info;
+ if (notify_die(DIE_TRAP, "illegal instruction", regs,
+ 0, 0x10, SIGILL) == NOTIFY_STOP)
+ return;
+
if (tstate & TSTATE_PRIV)
die_if_kernel("Kernel illegal instruction", regs);
if (test_thread_flag(TIF_32BIT))
pc = (u32)pc;
- if (get_user(insn, (u32 *)pc) != -EFAULT) {
+ if (get_user(insn, (u32 __user *) pc) != -EFAULT) {
if ((insn & 0xc1ffc000) == 0x81700000) /* POPC */ {
if (handle_popc(insn, regs))
return;
info.si_signo = SIGILL;
info.si_errno = 0;
info.si_code = ILL_ILLOPC;
- info.si_addr = (void *)pc;
+ info.si_addr = (void __user *)pc;
info.si_trapno = 0;
force_sig_info(SIGILL, &info, current);
}
{
siginfo_t info;
+ if (notify_die(DIE_TRAP, "memory address unaligned", regs,
+ 0, 0x34, SIGSEGV) == NOTIFY_STOP)
+ return;
+
if (regs->tstate & TSTATE_PRIV) {
extern void kernel_unaligned_trap(struct pt_regs *regs,
unsigned int insn,
- unsigned long sfar, unsigned long sfsr);
+ unsigned long sfar,
+ unsigned long sfsr);
- return kernel_unaligned_trap(regs, *((unsigned int *)regs->tpc), sfar, sfsr);
+ kernel_unaligned_trap(regs, *((unsigned int *)regs->tpc),
+ sfar, sfsr);
+ return;
}
info.si_signo = SIGBUS;
info.si_errno = 0;
info.si_code = BUS_ADRALN;
- info.si_addr = (void *)sfar;
+ info.si_addr = (void __user *)sfar;
info.si_trapno = 0;
force_sig_info(SIGBUS, &info, current);
}
{
siginfo_t info;
+ if (notify_die(DIE_TRAP, "privileged operation", regs,
+ 0, 0x11, SIGILL) == NOTIFY_STOP)
+ return;
+
if (test_thread_flag(TIF_32BIT)) {
regs->tpc &= 0xffffffff;
regs->tnpc &= 0xffffffff;
info.si_signo = SIGILL;
info.si_errno = 0;
info.si_code = ILL_PRVOPC;
- info.si_addr = (void *)regs->tpc;
+ info.si_addr = (void __user *)regs->tpc;
info.si_trapno = 0;
force_sig_info(SIGILL, &info, current);
}