📄 traps.c
字号:
/* $Id: traps.c,v 1.85 2002/02/09 19:49:31 davem Exp $ * arch/sparc64/kernel/traps.c * * Copyright (C) 1995,1997 David S. Miller (davem@caip.rutgers.edu) * Copyright (C) 1997,1999,2000 Jakub Jelinek (jakub@redhat.com) *//* * I like traps on v9, :)))) */#include <linux/config.h>#include <linux/module.h>#include <linux/sched.h> /* for jiffies */#include <linux/kernel.h>#include <linux/kallsyms.h>#include <linux/signal.h>#include <linux/smp.h>#include <linux/smp_lock.h>#include <linux/mm.h>#include <linux/init.h>#include <asm/delay.h>#include <asm/system.h>#include <asm/ptrace.h>#include <asm/oplib.h>#include <asm/page.h>#include <asm/pgtable.h>#include <asm/unistd.h>#include <asm/uaccess.h>#include <asm/fpumacro.h>#include <asm/lsu.h>#include <asm/dcu.h>#include <asm/estate.h>#include <asm/chafsr.h>#include <asm/sfafsr.h>#include <asm/psrcompat.h>#include <asm/processor.h>#include <asm/timer.h>#include <asm/kdebug.h>#ifdef CONFIG_KMOD#include <linux/kmod.h>#endifstruct notifier_block *sparc64die_chain;static DEFINE_SPINLOCK(die_notifier_lock);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 * is as follows: */struct tl1_traplog { struct { unsigned long tstate; unsigned long tpc; unsigned long tnpc; unsigned long tt; } trapstack[4]; unsigned long tl;};static void dump_tl1_traplog(struct tl1_traplog *p){ int i; printk("TRAPLOG: Error at trap level 0x%lx, dumping track stack.\n", p->tl); for (i = 0; i < 4; i++) { printk(KERN_CRIT "TRAPLOG: Trap level %d TSTATE[%016lx] TPC[%016lx] " "TNPC[%016lx] TT[%lx]\n", i + 1, p->trapstack[i].tstate, p->trapstack[i].tpc, p->trapstack[i].tnpc, p->trapstack[i].tt); }}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); } if (test_thread_flag(TIF_32BIT)) { regs->tpc &= 0xffffffff; regs->tnpc &= 0xffffffff; } info.si_signo = SIGILL; info.si_errno = 0; info.si_code = ILL_ILLTRP; 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){ 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); die_if_kernel (buffer, regs);}#ifdef CONFIG_DEBUG_BUGVERBOSEvoid do_BUG(const char *file, int line){ bust_spinlocks(1); printk("kernel BUG at %s:%d!\n", file, line);}#endifvoid spitfire_insn_access_exception(struct pt_regs *regs, unsigned long sfsr, unsigned long sfar){ siginfo_t info; if (notify_die(DIE_TRAP, "instruction access exception", regs, 0, 0x8, SIGTRAP) == NOTIFY_STOP) return; if (regs->tstate & TSTATE_PRIV) { printk("spitfire_insn_access_exception: SFSR[%016lx] " "SFAR[%016lx], going.\n", sfsr, sfar); die_if_kernel("Iax", regs); } if (test_thread_flag(TIF_32BIT)) { regs->tpc &= 0xffffffff; regs->tnpc &= 0xffffffff; } info.si_signo = SIGSEGV; info.si_errno = 0; info.si_code = SEGV_MAPERR; info.si_addr = (void __user *)regs->tpc; info.si_trapno = 0; force_sig_info(SIGSEGV, &info, current);}void spitfire_insn_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)); spitfire_insn_access_exception(regs, sfsr, sfar);}void spitfire_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. */ const struct exception_table_entry *entry; entry = search_exception_tables(regs->tpc); if (entry) { /* Ouch, somebody is trying VM hole tricks on us... */#ifdef DEBUG_EXCEPTIONS printk("Exception: PC<%016lx> faddr<UNKNOWN>\n", regs->tpc); printk("EX_TABLE: insn<%016lx> fixup<%016lx>\n", regs->tpc, entry->fixup);#endif regs->tpc = entry->fixup; regs->tnpc = regs->tpc + 4; return; } /* Shit... */ printk("spitfire_data_access_exception: SFSR[%016lx] " "SFAR[%016lx], going.\n", sfsr, sfar); die_if_kernel("Dax", regs); } info.si_signo = SIGSEGV; info.si_errno = 0; info.si_code = SEGV_MAPERR; info.si_addr = (void __user *)sfar; info.si_trapno = 0; force_sig_info(SIGSEGV, &info, current);}void spitfire_data_access_exception_tl1(struct pt_regs *regs, unsigned long sfsr, unsigned long sfar){ if (notify_die(DIE_TRAP_TL1, "data access exception tl1", regs, 0, 0x30, SIGTRAP) == NOTIFY_STOP) return; dump_tl1_traplog((struct tl1_traplog *)(regs + 1)); spitfire_data_access_exception(regs, sfsr, sfar);}#ifdef CONFIG_PCI/* This is really pathetic... */extern volatile int pci_poke_in_progress;extern volatile int pci_poke_cpu;extern volatile int pci_poke_faulted;#endif/* When access exceptions happen, we must do this. */static void spitfire_clean_and_reenable_l1_caches(void){ unsigned long va; if (tlb_type != spitfire) BUG(); /* Clean 'em. */ for (va = 0; va < (PAGE_SIZE << 1); va += 32) { spitfire_put_icache_tag(va, 0x0); spitfire_put_dcache_tag(va, 0x0); } /* Re-enable in LSU. */ __asm__ __volatile__("flush %%g6\n\t" "membar #Sync\n\t" "stxa %0, [%%g0] %1\n\t" "membar #Sync" : /* no outputs */ : "r" (LSU_CONTROL_IC | LSU_CONTROL_DC | LSU_CONTROL_IM | LSU_CONTROL_DM), "i" (ASI_LSU_CONTROL) : "memory");}static void spitfire_enable_estate_errors(void){ __asm__ __volatile__("stxa %0, [%%g0] %1\n\t" "membar #Sync" : /* no outputs */ : "r" (ESTATE_ERR_ALL), "i" (ASI_ESTATE_ERROR_EN));}static char ecc_syndrome_table[] = { 0x4c, 0x40, 0x41, 0x48, 0x42, 0x48, 0x48, 0x49, 0x43, 0x48, 0x48, 0x49, 0x48, 0x49, 0x49, 0x4a, 0x44, 0x48, 0x48, 0x20, 0x48, 0x39, 0x4b, 0x48, 0x48, 0x25, 0x31, 0x48, 0x28, 0x48, 0x48, 0x2c, 0x45, 0x48, 0x48, 0x21, 0x48, 0x3d, 0x04, 0x48, 0x48, 0x4b, 0x35, 0x48, 0x2d, 0x48, 0x48, 0x29, 0x48, 0x00, 0x01, 0x48, 0x0a, 0x48, 0x48, 0x4b, 0x0f, 0x48, 0x48, 0x4b, 0x48, 0x49, 0x49, 0x48, 0x46, 0x48, 0x48, 0x2a, 0x48, 0x3b, 0x27, 0x48, 0x48, 0x4b, 0x33, 0x48, 0x22, 0x48, 0x48, 0x2e, 0x48, 0x19, 0x1d, 0x48, 0x1b, 0x4a, 0x48, 0x4b, 0x1f, 0x48, 0x4a, 0x4b, 0x48, 0x4b, 0x4b, 0x48, 0x48, 0x4b, 0x24, 0x48, 0x07, 0x48, 0x48, 0x36, 0x4b, 0x48, 0x48, 0x3e, 0x48, 0x30, 0x38, 0x48, 0x49, 0x48, 0x48, 0x4b, 0x48, 0x4b, 0x16, 0x48, 0x48, 0x12, 0x4b, 0x48, 0x49, 0x48, 0x48, 0x4b, 0x47, 0x48, 0x48, 0x2f, 0x48, 0x3f, 0x4b, 0x48, 0x48, 0x06, 0x37, 0x48, 0x23, 0x48, 0x48, 0x2b, 0x48, 0x05, 0x4b, 0x48, 0x4b, 0x48, 0x48, 0x32, 0x26, 0x48, 0x48, 0x3a, 0x48, 0x34, 0x3c, 0x48, 0x48, 0x11, 0x15, 0x48, 0x13, 0x4a, 0x48, 0x4b, 0x17, 0x48, 0x4a, 0x4b, 0x48, 0x4b, 0x4b, 0x48, 0x49, 0x48, 0x48, 0x4b, 0x48, 0x4b, 0x1e, 0x48, 0x48, 0x1a, 0x4b, 0x48, 0x49, 0x48, 0x48, 0x4b, 0x48, 0x08, 0x0d, 0x48, 0x02, 0x48, 0x48, 0x49, 0x03, 0x48, 0x48, 0x49, 0x48, 0x4b, 0x4b, 0x48, 0x49, 0x48, 0x48, 0x49, 0x48, 0x4b, 0x10, 0x48, 0x48, 0x14, 0x4b, 0x48, 0x4b, 0x48, 0x48, 0x4b, 0x49, 0x48, 0x48, 0x49, 0x48, 0x4b, 0x18, 0x48, 0x48, 0x1c, 0x4b, 0x48, 0x4b, 0x48, 0x48, 0x4b, 0x4a, 0x0c, 0x09, 0x48, 0x0e, 0x48, 0x48, 0x4b, 0x0b, 0x48, 0x48, 0x4b, 0x48, 0x4b, 0x4b, 0x4a};static char *syndrome_unknown = "<Unknown>";static void spitfire_log_udb_syndrome(unsigned long afar, unsigned long udbh, unsigned long udbl, unsigned long bit){ unsigned short scode; char memmod_str[64], *p; if (udbl & bit) { scode = ecc_syndrome_table[udbl & 0xff]; if (prom_getunumber(scode, afar, memmod_str, sizeof(memmod_str)) == -1) p = syndrome_unknown; else p = memmod_str; printk(KERN_WARNING "CPU[%d]: UDBL Syndrome[%x] " "Memory Module \"%s\"\n", smp_processor_id(), scode, p); } if (udbh & bit) { scode = ecc_syndrome_table[udbh & 0xff]; if (prom_getunumber(scode, afar, memmod_str, sizeof(memmod_str)) == -1) p = syndrome_unknown; else p = memmod_str; printk(KERN_WARNING "CPU[%d]: UDBH Syndrome[%x] " "Memory Module \"%s\"\n", smp_processor_id(), scode, p); }}static void spitfire_cee_log(unsigned long afsr, unsigned long afar, unsigned long udbh, unsigned long udbl, int tl1, struct pt_regs *regs){ printk(KERN_WARNING "CPU[%d]: Correctable ECC Error " "AFSR[%lx] AFAR[%016lx] UDBL[%lx] UDBH[%lx] TL>1[%d]\n", smp_processor_id(), afsr, afar, udbl, udbh, tl1); spitfire_log_udb_syndrome(afar, udbh, udbl, UDBE_CE); /* We always log it, even if someone is listening for this * trap. */ notify_die(DIE_TRAP, "Correctable ECC Error", regs, 0, TRAP_TYPE_CEE, SIGTRAP); /* The Correctable ECC Error trap does not disable I/D caches. So * we only have to restore the ESTATE Error Enable register. */ spitfire_enable_estate_errors();}static void spitfire_ue_log(unsigned long afsr, unsigned long afar, unsigned long udbh, unsigned long udbl, unsigned long tt, int tl1, struct pt_regs *regs){ siginfo_t info; printk(KERN_WARNING "CPU[%d]: Uncorrectable Error AFSR[%lx] " "AFAR[%lx] UDBL[%lx] UDBH[%ld] TT[%lx] TL>1[%d]\n", smp_processor_id(), afsr, afar, udbl, udbh, tt, tl1); /* XXX add more human friendly logging of the error status * XXX as is implemented for cheetah */ spitfire_log_udb_syndrome(afar, udbh, udbl, UDBE_UE); /* We always log it, even if someone is listening for this * trap. */ notify_die(DIE_TRAP, "Uncorrectable Error", regs, 0, tt, SIGTRAP); if (regs->tstate & TSTATE_PRIV) { if (tl1) dump_tl1_traplog((struct tl1_traplog *)(regs + 1)); die_if_kernel("UE", regs); } /* XXX need more intelligent processing here, such as is implemented * XXX for cheetah errors, in fact if the E-cache still holds the * XXX line with bad parity this will loop */ spitfire_clean_and_reenable_l1_caches(); spitfire_enable_estate_errors(); if (test_thread_flag(TIF_32BIT)) { regs->tpc &= 0xffffffff; regs->tnpc &= 0xffffffff; } 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);}void spitfire_access_error(struct pt_regs *regs, unsigned long status_encoded, unsigned long afar){ unsigned long afsr, tt, udbh, udbl; int tl1; afsr = (status_encoded & SFSTAT_AFSR_MASK) >> SFSTAT_AFSR_SHIFT; tt = (status_encoded & SFSTAT_TRAP_TYPE) >> SFSTAT_TRAP_TYPE_SHIFT; tl1 = (status_encoded & SFSTAT_TL_GT_ONE) ? 1 : 0; udbl = (status_encoded & SFSTAT_UDBL_MASK) >> SFSTAT_UDBL_SHIFT; udbh = (status_encoded & SFSTAT_UDBH_MASK) >> SFSTAT_UDBH_SHIFT;#ifdef CONFIG_PCI if (tt == TRAP_TYPE_DAE && pci_poke_in_progress && pci_poke_cpu == smp_processor_id()) { spitfire_clean_and_reenable_l1_caches(); spitfire_enable_estate_errors(); pci_poke_faulted = 1; regs->tnpc = regs->tpc + 4; return; }#endif if (afsr & SFAFSR_UE) spitfire_ue_log(afsr, afar, udbh, udbl, tt, tl1, regs); if (tt == TRAP_TYPE_CEE) { /* Handle the case where we took a CEE trap, but ACK'd * only the UE state in the UDB error registers.
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -