📄 traps.c
字号:
/* $Id: traps.c,v 1.82 2001/11/18 00:12:56 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/sched.h> /* for jiffies */#include <linux/kernel.h>#include <linux/signal.h>#include <linux/smp.h>#include <linux/smp_lock.h>#include <linux/mm.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/psrcompat.h>#include <asm/processor.h>#ifdef CONFIG_KMOD#include <linux/kmod.h>#endif/* 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 bad_trap (struct pt_regs *regs, long lvl){ char buffer[32]; siginfo_t info; 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 ((current->thread.flags & SPARC_FLAG_32BIT) != 0) { regs->tpc &= 0xffffffff; regs->tnpc &= 0xffffffff; } info.si_signo = SIGILL; info.si_errno = 0; info.si_code = ILL_ILLTRP; info.si_addr = (void *)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]; 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 instruction_access_exception(struct pt_regs *regs, unsigned long sfsr, unsigned long sfar){ siginfo_t info; if (regs->tstate & TSTATE_PRIV) { printk("instruction_access_exception: SFSR[%016lx] SFAR[%016lx], going.\n", sfsr, sfar); die_if_kernel("Iax", regs); } if ((current->thread.flags & SPARC_FLAG_32BIT) != 0) { regs->tpc &= 0xffffffff; regs->tnpc &= 0xffffffff; } info.si_signo = SIGSEGV; info.si_errno = 0; info.si_code = SEGV_MAPERR; info.si_addr = (void *)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){ 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){ siginfo_t info; if (regs->tstate & TSTATE_PRIV) { /* Test if this comes from uaccess places. */ unsigned long fixup, g2; g2 = regs->u_regs[UREG_G2]; if ((fixup = search_exception_table (regs->tpc, &g2))) { /* Ouch, somebody is trying ugly VM hole tricks on us... */#ifdef DEBUG_EXCEPTIONS printk("Exception: PC<%016lx> faddr<UNKNOWN>\n", regs->tpc); printk("EX_TABLE: insn<%016lx> fixup<%016lx> " "g2<%016lx>\n", regs->tpc, fixup, g2);#endif regs->tpc = fixup; regs->tnpc = regs->tpc + 4; regs->u_regs[UREG_G2] = g2; return; } /* Shit... */ printk("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 *)sfar; info.si_trapno = 0; force_sig_info(SIGSEGV, &info, current);}#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");}void do_iae(struct pt_regs *regs){ siginfo_t info; spitfire_clean_and_reenable_l1_caches(); 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 do_dae(struct pt_regs *regs){#ifdef CONFIG_PCI if (pci_poke_in_progress && pci_poke_cpu == smp_processor_id()) { spitfire_clean_and_reenable_l1_caches(); pci_poke_faulted = 1; /* Why the fuck did they have to change this? */ if (tlb_type == cheetah || tlb_type == cheetah_plus) regs->tpc += 4; regs->tnpc = regs->tpc + 4; return; }#endif do_iae(regs);}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};/* cee_trap in entry.S encodes AFSR/UDBH/UDBL error status * in the following format. The AFAR is left as is, with * reserved bits cleared, and is a raw 40-bit physical * address. */#define CE_STATUS_UDBH_UE (1UL << (43 + 9))#define CE_STATUS_UDBH_CE (1UL << (43 + 8))#define CE_STATUS_UDBH_ESYNDR (0xffUL << 43)#define CE_STATUS_UDBH_SHIFT 43#define CE_STATUS_UDBL_UE (1UL << (33 + 9))#define CE_STATUS_UDBL_CE (1UL << (33 + 8))#define CE_STATUS_UDBL_ESYNDR (0xffUL << 33)#define CE_STATUS_UDBL_SHIFT 33#define CE_STATUS_AFSR_MASK (0x1ffffffffUL)#define CE_STATUS_AFSR_ME (1UL << 32)#define CE_STATUS_AFSR_PRIV (1UL << 31)#define CE_STATUS_AFSR_ISAP (1UL << 30)#define CE_STATUS_AFSR_ETP (1UL << 29)#define CE_STATUS_AFSR_IVUE (1UL << 28)#define CE_STATUS_AFSR_TO (1UL << 27)#define CE_STATUS_AFSR_BERR (1UL << 26)#define CE_STATUS_AFSR_LDP (1UL << 25)#define CE_STATUS_AFSR_CP (1UL << 24)#define CE_STATUS_AFSR_WP (1UL << 23)#define CE_STATUS_AFSR_EDP (1UL << 22)#define CE_STATUS_AFSR_UE (1UL << 21)#define CE_STATUS_AFSR_CE (1UL << 20)#define CE_STATUS_AFSR_ETS (0xfUL << 16)#define CE_STATUS_AFSR_ETS_SHIFT 16#define CE_STATUS_AFSR_PSYND (0xffffUL << 0)#define CE_STATUS_AFSR_PSYND_SHIFT 0/* Layout of Ecache TAG Parity Syndrome of AFSR */#define AFSR_ETSYNDROME_7_0 0x1UL /* E$-tag bus bits <7:0> */#define AFSR_ETSYNDROME_15_8 0x2UL /* E$-tag bus bits <15:8> */#define AFSR_ETSYNDROME_21_16 0x4UL /* E$-tag bus bits <21:16> */#define AFSR_ETSYNDROME_24_22 0x8UL /* E$-tag bus bits <24:22> */static char *syndrome_unknown = "<Unknown>";asmlinkage void cee_log(unsigned long ce_status, unsigned long afar, struct pt_regs *regs){ char memmod_str[64]; char *p; unsigned short scode, udb_reg; printk(KERN_WARNING "CPU[%d]: Correctable ECC Error " "AFSR[%lx] AFAR[%016lx] UDBL[%lx] UDBH[%lx]\n", smp_processor_id(), (ce_status & CE_STATUS_AFSR_MASK), afar, ((ce_status >> CE_STATUS_UDBL_SHIFT) & 0x3ffUL), ((ce_status >> CE_STATUS_UDBH_SHIFT) & 0x3ffUL)); udb_reg = ((ce_status >> CE_STATUS_UDBL_SHIFT) & 0x3ffUL); if (udb_reg & (1 << 8)) { scode = ecc_syndrome_table[udb_reg & 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); } udb_reg = ((ce_status >> CE_STATUS_UDBH_SHIFT) & 0x3ffUL); if (udb_reg & (1 << 8)) { scode = ecc_syndrome_table[udb_reg & 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); }}/* Cheetah error trap handling. */static unsigned long ecache_flush_physbase;static unsigned long ecache_flush_linesize;static unsigned long ecache_flush_size;/* WARNING: The error trap handlers in assembly know the precise * layout of the following structure. * * C-level handlers below use this information to log the error * and then determine how to recover (if possible). */struct cheetah_err_info {/*0x00*/u64 afsr;/*0x08*/u64 afar; /* D-cache state *//*0x10*/u64 dcache_data[4]; /* The actual data *//*0x30*/u64 dcache_index; /* D-cache index *//*0x38*/u64 dcache_tag; /* D-cache tag/valid *//*0x40*/u64 dcache_utag; /* D-cache microtag *//*0x48*/u64 dcache_stag; /* D-cache snooptag */ /* I-cache state *//*0x50*/u64 icache_data[8]; /* The actual insns + predecode *//*0x90*/u64 icache_index; /* I-cache index *//*0x98*/u64 icache_tag; /* I-cache phys tag *//*0xa0*/u64 icache_utag; /* I-cache microtag *//*0xa8*/u64 icache_stag; /* I-cache snooptag *//*0xb0*/u64 icache_upper; /* I-cache upper-tag *//*0xb8*/u64 icache_lower; /* I-cache lower-tag */ /* E-cache state *//*0xc0*/u64 ecache_data[4]; /* 32 bytes from staging registers *//*0xe0*/u64 ecache_index; /* E-cache index *//*0xe8*/u64 ecache_tag; /* E-cache tag/state *//*0xf0*/u64 __pad[32 - 30];};#define CHAFSR_INVALID ((u64)-1L)/* This is allocated at boot time based upon the largest hardware * cpu ID in the system. We allocate two entries per cpu, one for * TL==0 logging and one for TL >= 1 logging. */struct cheetah_err_info *cheetah_error_log;static __inline__ struct cheetah_err_info *cheetah_get_error_log(unsigned long afsr){ struct cheetah_err_info *p; int cpu = smp_processor_id(); if (!cheetah_error_log) return NULL; p = cheetah_error_log + (cpu * 2); if ((afsr & CHAFSR_TL1) != 0UL) p++; return p;}extern unsigned int tl0_icpe[], tl1_icpe[];extern unsigned int tl0_dcpe[], tl1_dcpe[];extern unsigned int tl0_fecc[], tl1_fecc[];extern unsigned int tl0_cee[], tl1_cee[];extern unsigned int tl0_iae[], tl1_iae[];extern unsigned int tl0_dae[], tl1_dae[];extern unsigned int cheetah_plus_icpe_trap_vector[], cheetah_plus_icpe_trap_vector_tl1[];extern unsigned int cheetah_plus_dcpe_trap_vector[], cheetah_plus_dcpe_trap_vector_tl1[];extern unsigned int cheetah_fecc_trap_vector[], cheetah_fecc_trap_vector_tl1[];extern unsigned int cheetah_cee_trap_vector[], cheetah_cee_trap_vector_tl1[];extern unsigned int cheetah_deferred_trap_vector[], cheetah_deferred_trap_vector_tl1[];void cheetah_ecache_flush_init(void){ unsigned long largest_size, smallest_linesize, order; char type[16]; int node, highest_cpu, i; /* Scan all cpu device tree nodes, note two values: * 1) largest E-cache size * 2) smallest E-cache line size */ largest_size = 0UL; smallest_linesize = ~0UL; node = prom_getchild(prom_root_node); while ((node = prom_getsibling(node)) != 0) { prom_getstring(node, "device_type", type, sizeof(type)); if (!strcmp(type, "cpu")) { unsigned long val;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -