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
- Lines: 172
- Date:
Sun Sep 17 09:48:07 2000
- Orig file:
v2.4.0-test8/linux/arch/ppc/kernel/traps.c
- Orig date:
Mon May 15 14:53:30 2000
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)