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

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)