patch-2.4.0-test9 linux/arch/ppc/kernel/traps.c

Next file: linux/arch/ppc/kernel/walnut_setup.c
Previous file: linux/arch/ppc/kernel/time.c
Back to the patch index
Back to the overall index

diff -u --recursive --new-file v2.4.0-test8/linux/arch/ppc/kernel/traps.c linux/arch/ppc/kernel/traps.c
@@ -87,45 +87,76 @@
 void
 MachineCheckException(struct pt_regs *regs)
 {
-	if ( !user_mode(regs) )
-	{
-#if defined(CONFIG_8xx) && defined(CONFIG_PCI)
-		/* the qspan pci read routines can cause machine checks -- Cort */
-		bad_page_fault(regs, regs->dar);
+#ifdef CONFIG_ALL_PPC
+	unsigned long fixup;
+#endif /* CONFIG_ALL_PPC */
+
+	if (user_mode(regs)) {
+		_exception(SIGSEGV, regs);	
 		return;
+	}
+
+#if defined(CONFIG_8xx) && defined(CONFIG_PCI)
+	/* the qspan pci read routines can cause machine checks -- Cort */
+	bad_page_fault(regs, regs->dar);
+	return;
 #endif
 #if defined(CONFIG_XMON) || defined(CONFIG_KGDB)
-		if (debugger_fault_handler) {
-			debugger_fault_handler(regs);
-			return;
-		}
+	if (debugger_fault_handler) {
+		debugger_fault_handler(regs);
+		return;
+	}
 #endif
-		printk("Machine check in kernel mode.\n");
-		printk("Caused by (from SRR1=%lx): ", regs->msr);
-		switch (regs->msr & 0xF0000) {
-		case 0x80000:
-			printk("Machine check signal\n");
-			break;
-		case 0x40000:
-			printk("Transfer error ack signal\n");
-			break;
-		case 0x20000:
-			printk("Data parity error signal\n");
-			break;
-		case 0x10000:
-			printk("Address parity error signal\n");
-			break;
-		default:
-			printk("Unknown values in msr\n");
+
+#ifdef CONFIG_ALL_PPC
+	/*
+	 * I/O accesses can cause machine checks on powermacs.
+	 * Check if the NIP corresponds to the address of a sync
+	 * instruction for which there is an entry in the exception
+	 * table.
+	 */
+	if (regs->msr & (0x80000 | 0x40000)
+	    && (fixup = search_exception_table(regs->nip)) != 0) {
+		/*
+		 * Check that it's a sync instruction.
+		 * As the address is in the exception table
+		 * we should be able to read the instr there.
+		 */
+		if (*(unsigned int *)regs->nip == 0x7c0004ac) {
+			unsigned int lsi = ((unsigned int *)regs->nip)[-1];
+			int rb = (lsi >> 11) & 0x1f;
+			printk(KERN_DEBUG "%s bad port %lx at %lx\n",
+			       (lsi & 0x100)? "OUT to": "IN from",
+			       regs->gpr[rb] - _IO_BASE, regs->nip);
+			regs->nip = fixup;
+			return;
 		}
-		show_regs(regs);
+	}
+#endif /* CONFIG_ALL_PPC */
+	printk("Machine check in kernel mode.\n");
+	printk("Caused by (from SRR1=%lx): ", regs->msr);
+	switch (regs->msr & 0xF0000) {
+	case 0x80000:
+		printk("Machine check signal\n");
+		break;
+	case 0x40000:
+		printk("Transfer error ack signal\n");
+		break;
+	case 0x20000:
+		printk("Data parity error signal\n");
+		break;
+	case 0x10000:
+		printk("Address parity error signal\n");
+		break;
+	default:
+		printk("Unknown values in msr\n");
+	}
+	show_regs(regs);
 #if defined(CONFIG_XMON) || defined(CONFIG_KGDB)
-		debugger(regs);
+	debugger(regs);
 #endif
-		print_backtrace((unsigned long *)regs->gpr[1]);
-		panic("machine check");
-	}
-	_exception(SIGSEGV, regs);	
+	print_backtrace((unsigned long *)regs->gpr[1]);
+	panic("machine check");
 }
 
 void
@@ -166,6 +197,46 @@
 	_exception(SIGTRAP, regs);	
 }
 
+/* Illegal instruction emulation support.  Originally written to
+ * provide the PVR to user applications using the mfspr rd, PVR.
+ * Return non-zero if we can't emulate, or EFAULT if the associated
+ * memory access caused an access fault.  Return zero on success.
+ *
+ * There are a couple of ways to do this, either "decode" the instruction
+ * or directly match lots of bits.  In this case, matching lots of
+ * bits is faster and easier.
+ *
+ */
+#define INST_MFSPR_PVR		0x7c1f42a6
+#define INST_MFSPR_PVR_MASK	0xfc1fffff
+
+static int
+emulate_instruction(struct pt_regs *regs)
+{
+	uint    instword;
+	uint    rd;
+	uint    retval;
+
+	retval = EFAULT;
+
+	if (!user_mode(regs))
+		return retval;
+
+	if (get_user(instword, (uint *)(regs->nip)))
+		return retval;
+
+	/* Emulate the mfspr rD, PVR.
+	 */
+	if ((instword & INST_MFSPR_PVR_MASK) == INST_MFSPR_PVR) {
+		rd = (instword >> 21) & 0x1f;
+		regs->gpr[rd] = _get_PVR();
+		retval = 0;
+	}
+	if (retval == 0)
+		regs->nip += 4;
+	return(retval);
+}
+
 void
 ProgramCheckException(struct pt_regs *regs)
 {
@@ -193,7 +264,14 @@
 #endif
 		_exception(SIGTRAP, regs);
 	} else {
-		_exception(SIGILL, regs);
+		/* Try to emulate it if we should. */
+		int errcode;
+		if ((errcode = emulate_instruction(regs))) {
+			if (errcode == EFAULT)
+				_exception(SIGBUS, regs);
+			else
+				_exception(SIGILL, regs);
+		}
 	}
 #endif
 }

FUNET's LINUX-ADM group, linux-adm@nic.funet.fi
TCL-scripts by Sam Shen (who was at: slshen@lbl.gov)