patch-2.4.20 linux-2.4.20/arch/sparc64/kernel/traps.c
Next file: linux-2.4.20/arch/sparc64/kernel/ttable.S
Previous file: linux-2.4.20/arch/sparc64/kernel/trampoline.S
Back to the patch index
Back to the overall index
- Lines: 434
- Date:
Thu Nov 28 15:53:12 2002
- Orig file:
linux-2.4.19/arch/sparc64/kernel/traps.c
- Orig date:
Fri Dec 21 09:41:53 2001
diff -urN linux-2.4.19/arch/sparc64/kernel/traps.c linux-2.4.20/arch/sparc64/kernel/traps.c
@@ -36,6 +36,37 @@
#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];
@@ -65,8 +96,10 @@
void bad_trap_tl1 (struct pt_regs *regs, long lvl)
{
- char buffer[24];
+ 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);
}
@@ -79,8 +112,8 @@
}
#endif
-void instruction_access_exception (struct pt_regs *regs,
- unsigned long sfsr, unsigned long sfar)
+void instruction_access_exception(struct pt_regs *regs,
+ unsigned long sfsr, unsigned long sfar)
{
siginfo_t info;
@@ -101,6 +134,13 @@
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)
{
@@ -145,43 +185,36 @@
#endif
/* When access exceptions happen, we must do this. */
-static void clean_and_reenable_l1_caches(void)
+static void spitfire_clean_and_reenable_l1_caches(void)
{
unsigned long va;
- if (tlb_type == spitfire) {
- /* Clean 'em. */
- for (va = 0; va < (PAGE_SIZE << 1); va += 32) {
- spitfire_put_icache_tag(va, 0x0);
- spitfire_put_dcache_tag(va, 0x0);
- }
+ if (tlb_type != spitfire)
+ BUG();
- /* 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");
- } else if (tlb_type == cheetah) {
- /* Flush D-cache */
- for (va = 0; va < (1 << 16); va += (1 << 5)) {
- __asm__ __volatile__("stxa %%g0, [%0] %1\n\t"
- "membar #Sync"
- : /* no outputs */
- : "r" (va), "i" (ASI_DCACHE_TAG));
- }
+ /* 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;
- clean_and_reenable_l1_caches();
+ spitfire_clean_and_reenable_l1_caches();
info.si_signo = SIGBUS;
info.si_errno = 0;
@@ -195,12 +228,12 @@
{
#ifdef CONFIG_PCI
if (pci_poke_in_progress && pci_poke_cpu == smp_processor_id()) {
- clean_and_reenable_l1_caches();
+ spitfire_clean_and_reenable_l1_caches();
pci_poke_faulted = 1;
/* Why the fuck did they have to change this? */
- if (tlb_type == cheetah)
+ if (tlb_type == cheetah || tlb_type == cheetah_plus)
regs->tpc += 4;
regs->tnpc = regs->tpc + 4;
@@ -389,10 +422,14 @@
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[];
@@ -494,6 +531,12 @@
memcpy(tl1_iae, cheetah_deferred_trap_vector_tl1, (8 * 4));
memcpy(tl0_dae, cheetah_deferred_trap_vector, (8 * 4));
memcpy(tl1_dae, cheetah_deferred_trap_vector_tl1, (8 * 4));
+ if (tlb_type == cheetah_plus) {
+ memcpy(tl0_dcpe, cheetah_plus_dcpe_trap_vector, (8 * 4));
+ memcpy(tl1_dcpe, cheetah_plus_dcpe_trap_vector_tl1, (8 * 4));
+ memcpy(tl0_icpe, cheetah_plus_icpe_trap_vector, (8 * 4));
+ memcpy(tl1_icpe, cheetah_plus_icpe_trap_vector_tl1, (8 * 4));
+ }
flushi(PAGE_OFFSET);
}
@@ -532,9 +575,22 @@
*
* So we must only flush the I-cache when it is disabled.
*/
+static void __cheetah_flush_icache(void)
+{
+ unsigned long i;
+
+ /* Clear the valid bits in all the tags. */
+ for (i = 0; i < (1 << 15); i += (1 << 5)) {
+ __asm__ __volatile__("stxa %%g0, [%0] %1\n\t"
+ "membar #Sync"
+ : /* no outputs */
+ : "r" (i | (2 << 3)), "i" (ASI_IC_TAG));
+ }
+}
+
static void cheetah_flush_icache(void)
{
- unsigned long dcu_save, i;
+ unsigned long dcu_save;
/* Save current DCU, disable I-cache. */
__asm__ __volatile__("ldxa [%%g0] %1, %0\n\t"
@@ -545,13 +601,7 @@
: "i" (ASI_DCU_CONTROL_REG), "i" (DCU_IC)
: "g1");
- /* Clear the valid bits in all the tags. */
- for (i = 0; i < (1 << 16); i += (1 << 5)) {
- __asm__ __volatile__("stxa %%g0, [%0] %1\n\t"
- "membar #Sync"
- : /* no outputs */
- : "r" (i | (2 << 3)), "i" (ASI_IC_TAG));
- }
+ __cheetah_flush_icache();
/* Restore DCU register */
__asm__ __volatile__("stxa %0, [%%g0] %1\n\t"
@@ -572,6 +622,34 @@
}
}
+/* In order to make the even parity correct we must do two things.
+ * First, we clear DC_data_parity and set DC_utag to an appropriate value.
+ * Next, we clear out all 32-bytes of data for that line. Data of
+ * all-zero + tag parity value of zero == correct parity.
+ */
+static void cheetah_plus_zap_dcache_parity(void)
+{
+ unsigned long i;
+
+ for (i = 0; i < (1 << 16); i += (1 << 5)) {
+ unsigned long tag = (i >> 14);
+ unsigned long j;
+
+ __asm__ __volatile__("membar #Sync\n\t"
+ "stxa %0, [%1] %2\n\t"
+ "membar #Sync"
+ : /* no outputs */
+ : "r" (tag), "r" (i),
+ "i" (ASI_DCACHE_UTAG));
+ for (j = i; j < i + (1 << 5); j += (1 << 3))
+ __asm__ __volatile__("membar #Sync\n\t"
+ "stxa %%g0, [%0] %1\n\t"
+ "membar #Sync"
+ : /* no outputs */
+ : "r" (j), "i" (ASI_DCACHE_DATA));
+ }
+}
+
/* Conversion tables used to frob Cheetah AFSR syndrome values into
* something palatable to the memory controller driver get_unumber
* routine.
@@ -1294,6 +1372,46 @@
panic("Irrecoverable deferred error trap.\n");
}
+/* Handle a D/I cache parity error trap. TYPE is encoded as:
+ *
+ * Bit0: 0=dcache,1=icache
+ * Bit1: 0=recoverable,1=unrecoverable
+ *
+ * The hardware has disabled both the I-cache and D-cache in
+ * the %dcr register.
+ */
+void cheetah_plus_parity_error(int type, struct pt_regs *regs)
+{
+ if (type & 0x1)
+ __cheetah_flush_icache();
+ else
+ cheetah_plus_zap_dcache_parity();
+ cheetah_flush_dcache();
+
+ /* Re-enable I-cache/D-cache */
+ __asm__ __volatile__("ldxa [%%g0] %0, %%g1\n\t"
+ "or %%g1, %1, %%g1\n\t"
+ "stxa %%g1, [%%g0] %0\n\t"
+ "membar #Sync"
+ : /* no outputs */
+ : "i" (ASI_DCU_CONTROL_REG),
+ "i" (DCU_DC | DCU_IC)
+ : "g1");
+
+ if (type & 0x2) {
+ printk(KERN_EMERG "CPU[%d]: Cheetah+ %c-cache parity error at TPC[%016lx]\n",
+ smp_processor_id(),
+ (type & 0x1) ? 'I' : 'D',
+ regs->tpc);
+ panic("Irrecoverable Cheetah+ parity error.");
+ }
+
+ printk(KERN_WARNING "CPU[%d]: Cheetah+ %c-cache parity error at TPC[%016lx]\n",
+ smp_processor_id(),
+ (type & 0x1) ? 'I' : 'D',
+ regs->tpc);
+}
+
void do_fpe_common(struct pt_regs *regs)
{
if(regs->tstate & TSTATE_PRIV) {
@@ -1373,6 +1491,8 @@
{
siginfo_t info;
+ if (regs->tstate & TSTATE_PRIV)
+ die_if_kernel("TL0: Kernel divide by zero.", regs);
if ((current->thread.flags & SPARC_FLAG_32BIT) != 0) {
regs->tpc &= 0xffffffff;
regs->tnpc &= 0xffffffff;
@@ -1415,17 +1535,14 @@
printk("\n");
}
-void show_trace_task(struct task_struct *tsk)
+void show_trace_raw(struct task_struct *tsk, unsigned long ksp)
{
unsigned long pc, fp;
unsigned long task_base = (unsigned long)tsk;
struct reg_window *rw;
int count = 0;
- if (!tsk)
- return;
-
- fp = tsk->thread.ksp + STACK_BIAS;
+ fp = ksp + STACK_BIAS;
do {
/* Bogus frame pointer? */
if (fp < (task_base + sizeof(struct task_struct)) ||
@@ -1439,6 +1556,12 @@
printk("\n");
}
+void show_trace_task(struct task_struct *tsk)
+{
+ if (tsk)
+ show_trace_raw(tsk, tsk->thread.ksp);
+}
+
void die_if_kernel(char *str, struct pt_regs *regs)
{
extern void __show_regs(struct pt_regs * regs);
@@ -1571,56 +1694,67 @@
void do_cee_tl1(struct pt_regs *regs)
{
+ dump_tl1_traplog((struct tl1_traplog *)(regs + 1));
die_if_kernel("TL1: Cache Error Exception", regs);
}
void do_dae_tl1(struct pt_regs *regs)
{
+ dump_tl1_traplog((struct tl1_traplog *)(regs + 1));
die_if_kernel("TL1: Data Access Exception", regs);
}
void do_iae_tl1(struct pt_regs *regs)
{
+ dump_tl1_traplog((struct tl1_traplog *)(regs + 1));
die_if_kernel("TL1: Instruction Access Exception", regs);
}
void do_div0_tl1(struct pt_regs *regs)
{
+ dump_tl1_traplog((struct tl1_traplog *)(regs + 1));
die_if_kernel("TL1: DIV0 Exception", regs);
}
void do_fpdis_tl1(struct pt_regs *regs)
{
+ dump_tl1_traplog((struct tl1_traplog *)(regs + 1));
die_if_kernel("TL1: FPU Disabled", regs);
}
void do_fpieee_tl1(struct pt_regs *regs)
{
+ dump_tl1_traplog((struct tl1_traplog *)(regs + 1));
die_if_kernel("TL1: FPU IEEE Exception", regs);
}
void do_fpother_tl1(struct pt_regs *regs)
{
+ dump_tl1_traplog((struct tl1_traplog *)(regs + 1));
die_if_kernel("TL1: FPU Other Exception", regs);
}
void do_ill_tl1(struct pt_regs *regs)
{
+ dump_tl1_traplog((struct tl1_traplog *)(regs + 1));
die_if_kernel("TL1: Illegal Instruction Exception", regs);
}
void do_irq_tl1(struct pt_regs *regs)
{
+ dump_tl1_traplog((struct tl1_traplog *)(regs + 1));
die_if_kernel("TL1: IRQ Exception", regs);
}
void do_lddfmna_tl1(struct pt_regs *regs)
{
+ dump_tl1_traplog((struct tl1_traplog *)(regs + 1));
die_if_kernel("TL1: LDDF Exception", regs);
}
void do_stdfmna_tl1(struct pt_regs *regs)
{
+ dump_tl1_traplog((struct tl1_traplog *)(regs + 1));
die_if_kernel("TL1: STDF Exception", regs);
}
@@ -1631,6 +1765,7 @@
void do_paw_tl1(struct pt_regs *regs)
{
+ dump_tl1_traplog((struct tl1_traplog *)(regs + 1));
die_if_kernel("TL1: Phys Watchpoint Exception", regs);
}
@@ -1641,11 +1776,13 @@
void do_vaw_tl1(struct pt_regs *regs)
{
+ dump_tl1_traplog((struct tl1_traplog *)(regs + 1));
die_if_kernel("TL1: Virt Watchpoint Exception", regs);
}
void do_tof_tl1(struct pt_regs *regs)
{
+ dump_tl1_traplog((struct tl1_traplog *)(regs + 1));
die_if_kernel("TL1: Tag Overflow Exception", regs);
}
FUNET's LINUX-ADM group, linux-adm@nic.funet.fi
TCL-scripts by Sam Shen (who was at: slshen@lbl.gov)