summaryrefslogtreecommitdiffstats
path: root/arch/mips/kernel/unaligned.c
diff options
context:
space:
mode:
authorRalf Baechle <ralf@linux-mips.org>1998-05-04 09:12:48 +0000
committerRalf Baechle <ralf@linux-mips.org>1998-05-04 09:12:48 +0000
commit2e0f55e79c49509b7ff70ff1a10e1e9e90a3dfd4 (patch)
tree9ff1a23dd500f1c70eb99fcd688d47d38abb0253 /arch/mips/kernel/unaligned.c
parentbbc6b4b59f51131f040a752cbe20a1805db08b0b (diff)
o New memset. Fastest in town for size > 6 bytes.
o New clear_user. o Memcpy now efficiently copies the (src^dest)&3 != 0. o Memmove new correctly deals with overlaps o Rewrite csum_partial in assembler. o Rewrte csum_partial_from_user in assembler. o __copy_user is now integrated with memcpy. o get_user now returns a zero value on error. o copy_from_user now clears the destination buffer on error. o strncpy_user now has a more efficient caller routine. o strlen_user now has a more efficient caller routines and is faster. o The unaligned handler is now much cleaner. It's now also save from interrupt. Some more esotheric bugs fixed as well. o Don't export bcopy anymore, it's now a inline function. o Delete ancient junk from the first days of Linux/MIPS. o Delete dead code in indy_sc.c. o Including the IDE driver doesn't crash an Indy anymore. o Eleminate active_ds. We now use current_ds directly in the thread structure which is faster and threadsafe. Saves almost 2kb on the kernel. o Serial console should work again.
Diffstat (limited to 'arch/mips/kernel/unaligned.c')
-rw-r--r--arch/mips/kernel/unaligned.c137
1 files changed, 48 insertions, 89 deletions
diff --git a/arch/mips/kernel/unaligned.c b/arch/mips/kernel/unaligned.c
index ea81ba7db..52205475a 100644
--- a/arch/mips/kernel/unaligned.c
+++ b/arch/mips/kernel/unaligned.c
@@ -5,7 +5,9 @@
* License. See the file "COPYING" in the main directory of this archive
* for more details.
*
- * Copyright (C) 1996 by Ralf Baechle
+ * Copyright (C) 1996, 1998 by Ralf Baechle
+ *
+ * $Id: unaligned.c,v 1.4 1998/05/03 00:24:48 ralf Exp $
*
* This file contains exception handler for address error exception with the
* special capability to execute faulting instructions in software. The
@@ -34,13 +36,7 @@
* sysmips(MIPS_FIXADE, x);
* ...
*
- * The parameter x is 0 for disabeling software emulation. Set bit 0 for
- * enabeling software emulation and bit 1 for enabeling printing debug
- * messages into syslog to aid finding address errors in programs.
- *
- * The logging feature is an addition over RISC/os and IRIX where only the
- * values 0 and 1 are acceptable values for x. I'll probably remove this
- * hack later on.
+ * The argument x is 0 for disabling software emulation, enabled otherwise.
*
* Below a little program to play around with this feature.
*
@@ -69,12 +65,6 @@
* printf("\n");
* }
*
- * Until I've written the code to handle branch delay slots it may happen
- * that the kernel receives an ades/adel instruction from an insn in a
- * branch delay slot but is unable to handle this case. The kernel knows
- * this fact and therefore will kill the process. For most code you can
- * fix this temporarily by compiling with flags -fno-delayed-branch -Wa,-O0.
- *
* Coprozessor loads are not supported; I think this case is unimportant
* in the practice.
*
@@ -88,19 +78,15 @@
#include <linux/smp.h>
#include <linux/smp_lock.h>
+#include <asm/asm.h>
#include <asm/branch.h>
#include <asm/byteorder.h>
#include <asm/inst.h>
#include <asm/uaccess.h>
-#undef CONF_NO_UNALIGNED_KERNEL_ACCESS
-#undef CONF_LOG_UNALIGNED_ACCESSES
-
#define STR(x) __STR(x)
#define __STR(x) #x
-typedef unsigned long register_t;
-
/*
* User code may only access USEG; kernel code may access the
* entire address space.
@@ -110,10 +96,12 @@ typedef unsigned long register_t;
goto sigbus;
static inline void
-emulate_load_store_insn(struct pt_regs *regs, unsigned long addr, unsigned long pc)
+emulate_load_store_insn(struct pt_regs *regs,
+ unsigned long addr,
+ unsigned long pc)
{
union mips_instruction insn;
- register_t value;
+ unsigned long value, fixup;
regs->regs[0] = 0;
/*
@@ -358,99 +346,70 @@ emulate_load_store_insn(struct pt_regs *regs, unsigned long addr, unsigned long
*/
default:
/*
- * Pheeee... We encountered an yet unknown instruction ...
+ * Pheeee... We encountered an yet unknown instruction or
+ * cache coherence problem. Die sucker, die ...
*/
- force_sig(SIGILL, current);
+ goto sigill;
}
return;
fault:
+ /* Did we have an exception handler installed? */
+ fixup = search_exception_table(regs->cp0_epc);
+ if (fixup) {
+ long new_epc;
+ new_epc = fixup_exception(dpf_reg, fixup, regs->cp0_epc);
+ printk(KERN_DEBUG "%s: Forwarding exception at [<%lx>] (%lx)\n",
+ current->comm, regs->cp0_epc, new_epc);
+ regs->cp0_epc = new_epc;
+ return;
+ }
+
+ lock_kernel();
send_sig(SIGSEGV, current, 1);
+ unlock_kernel();
return;
sigbus:
+ lock_kernel();
send_sig(SIGBUS, current, 1);
+ unlock_kernel();
+ return;
+sigill:
+ lock_kernel();
+ send_sig(SIGILL, current, 1);
+ unlock_kernel();
return;
}
unsigned long unaligned_instructions;
-static inline void
-fix_ade(struct pt_regs *regs, unsigned long pc)
+asmlinkage void do_ade(struct pt_regs *regs)
{
+ unsigned long pc;
+
/*
* Did we catch a fault trying to load an instruction?
+ * This also catches attempts to activate MIPS16 code on
+ * CPUs which don't support it.
*/
- if (regs->cp0_badvaddr == pc) {
- /*
- * Phee... Either the code is severly messed up or the
- * process tried to activate some MIPS16 code.
- */
- force_sig(SIGBUS, current);
- }
+ if (regs->cp0_badvaddr == regs->cp0_epc)
+ goto sigbus;
+
+ pc = regs->cp0_epc + ((regs->cp0_cause & CAUSEF_BD) ? 4 : 0);
+ if (compute_return_epc(regs))
+ return;
+ if ((current->tss.mflags & MF_FIXADE) == 0)
+ goto sigbus;
- /*
- * Ok, this wasn't a failed instruction load. The CPU was capable of
- * reading the instruction and faulted after this. So we don't need
- * to verify_area the address of the instrucion. We still don't
- * know whether the address used was legal and therefore need to do
- * verify_area(). The CPU already did the checking for legal
- * instructions for us, so we don't need to do this.
- */
emulate_load_store_insn(regs, regs->cp0_badvaddr, pc);
unaligned_instructions++;
-}
-
-#define kernel_address(x) ((long)(x) < 0)
-asmlinkage void
-do_ade(struct pt_regs *regs)
-{
- register_t pc = regs->cp0_epc;
- register_t badvaddr __attribute__ ((unused)) = regs->cp0_badvaddr;
- char adels;
+ return;
+sigbus:
lock_kernel();
- adels = (((regs->cp0_cause & CAUSEF_EXCCODE) >>
- CAUSEB_EXCCODE) == 4) ? 'l' : 's';
-
-#ifdef CONF_NO_UNALIGNED_KERNEL_ACCESS
- /*
- * In an ideal world there are no unaligned accesses by the kernel.
- * So be a bit noisy ...
- */
- if (kernel_address(badvaddr) && !user_mode(regs)) {
- show_regs(regs);
- panic("Caught adel%c exception in kernel mode accessing %08lx.",
- adels, badvaddr);
- }
-#endif /* CONF_NO_UNALIGNED_KERNEL_ACCESS */
-
-#ifdef CONF_LOG_UNALIGNED_ACCESSES
- if (current->tss.mflags & MF_LOGADE) {
- register_t logpc = pc;
- if (regs->cp0_cause & CAUSEF_BD)
- logpc += 4;
- printk(KERN_DEBUG
- "Caught adel%c in '%s' at 0x%08lx accessing 0x%08lx.\n",
- adels, current->comm, logpc, regs->cp0_badvaddr);
- }
-#endif /* CONF_LOG_UNALIGNED_ACCESSES */
-
- if (compute_return_epc(regs))
- goto out;
- if(current->tss.mflags & MF_FIXADE) {
- pc += ((regs->cp0_cause & CAUSEF_BD) ? 4 : 0);
- fix_ade(regs, pc);
- goto out;
- }
-
-#ifdef CONF_DEBUG_EXCEPTIONS
- show_regs(regs);
-#endif
-
force_sig(SIGBUS, current);
-
-out:
unlock_kernel();
+
return;
}