summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRussell King <rmk@flint.arm.linux.org.uk>2002-06-05 14:57:54 +0100
committerRussell King <rmk@flint.arm.linux.org.uk>2002-06-05 14:57:54 +0100
commit75fb1f976e7b45a585ecdb214a6027e271a44dbf (patch)
tree3ce1cc765fcda2f76660917c60842b0b74ae4d99
parentea1e2d6259e83d6fae83473f7b90a683ec770322 (diff)
[ARM] Generic hook for page faults
Provide a method where various other parts of the kernel (eg, alignment fault handler, PCI subsystems, etc) can hook into the page fault processing to handle alignment and PCI faults respectively.
-rw-r--r--arch/arm/mach-integrator/pci_v3.c14
-rw-r--r--arch/arm/mach-iop310/iop310-pci.c226
-rw-r--r--arch/arm/mm/alignment.c69
-rw-r--r--arch/arm/mm/fault-armv.c77
-rw-r--r--arch/arm/mm/fault-common.c47
-rw-r--r--arch/arm/mm/fault.h8
-rw-r--r--include/asm-arm/system.h11
7 files changed, 228 insertions, 224 deletions
diff --git a/arch/arm/mach-integrator/pci_v3.c b/arch/arm/mach-integrator/pci_v3.c
index 16c4544a322c..c7bdfa94e0ce 100644
--- a/arch/arm/mach-integrator/pci_v3.c
+++ b/arch/arm/mach-integrator/pci_v3.c
@@ -439,15 +439,16 @@ static int __init pci_v3_setup_resources(struct resource **resource)
#define SC_LBFADDR (IO_ADDRESS(INTEGRATOR_SC_BASE) + 0x20)
#define SC_LBFCODE (IO_ADDRESS(INTEGRATOR_SC_BASE) + 0x24)
-static int v3_fault(unsigned long addr, struct pt_regs *regs)
+static int
+v3_pci_fault(unsigned long addr, unsigned int fsr, struct pt_regs *regs)
{
unsigned long pc = instruction_pointer(regs);
unsigned long instr = *(unsigned long *)pc;
#if 0
char buf[128];
- sprintf(buf, "V3 fault: address=0x%08lx, pc=0x%08lx [%08lx] LBFADDR=%08x LBFCODE=%02x ISTAT=%02x\n",
- addr, pc, instr, __raw_readl(SC_LBFADDR), __raw_readl(SC_LBFCODE) & 255,
+ sprintf(buf, "V3 fault: addr 0x%08lx, FSR 0x%03x, PC 0x%08lx [%08lx] LBFADDR=%08x LBFCODE=%02x ISTAT=%02x\n",
+ addr, fsr, pc, instr, __raw_readl(SC_LBFADDR), __raw_readl(SC_LBFCODE) & 255,
v3_readb(V3_LB_ISTAT));
printk(KERN_DEBUG "%s", buf);
printascii(buf);
@@ -515,8 +516,6 @@ static void v3_irq(int irq, void *devid, struct pt_regs *regs)
#endif
}
-extern int (*external_fault)(unsigned long addr, struct pt_regs *regs);
-
int __init pci_v3_setup(int nr, struct pci_sys_data *sys)
{
int ret = 0;
@@ -547,7 +546,10 @@ void __init pci_v3_preinit(void)
/*
* Hook in our fault handler for PCI errors
*/
- external_fault = v3_fault;
+ hook_fault_code(4, v3_pci_fault, SIGBUS, "external abort on linefetch");
+ hook_fault_code(6, v3_pci_fault, SIGBUS, "external abort on linefetch");
+ hook_fault_code(8, v3_pci_fault, SIGBUS, "external abort on non-linefetch");
+ hook_fault_code(10, v3_pci_fault, SIGBUS, "external abort on non-linefetch");
spin_lock_irqsave(&v3_lock, flags);
diff --git a/arch/arm/mach-iop310/iop310-pci.c b/arch/arm/mach-iop310/iop310-pci.c
index 3e1a5f6b4480..a92ff17fe834 100644
--- a/arch/arm/mach-iop310/iop310-pci.c
+++ b/arch/arm/mach-iop310/iop310-pci.c
@@ -106,19 +106,32 @@ static int iop310_pri_pci_status(void)
return ret;
}
+static inline u32 iop310_pri_read(struct pci_dev *dev, int where)
+{
+ unsigned long addr = iop310_cfg_address(dev, where);
+ u32 val;
+
+ __asm__ __volatile__(
+ "str %1, [%2]\n\t"
+ "ldr %0, [%3]\n\t"
+ "nop\n\t"
+ "nop\n\t"
+ "nop\n\t"
+ "nop\n\t"
+ : "=r" (val)
+ : "r" (addr), "r" (IOP310_POCCAR), "r" (IOP310_POCCDR));
+
+ return val;
+}
+
static int
iop310_pri_rd_cfg_byte(struct pci_dev *dev, int where, u8 *p)
{
- int ret;
u8 val;
- *IOP310_POCCAR = iop310_cfg_address(dev, where);
-
- val = (*IOP310_POCCDR) >> ((where & 3) * 8);
- __asm__ __volatile__("nop; nop; nop; nop");
+ val = iop310_pri_read(dev, where) >> ((where & 3) * 8);
- ret = iop310_pri_pci_status();
- if (ret)
+ if (iop310_pri_pci_status())
val = 0xff;
*p = val;
@@ -129,16 +142,11 @@ iop310_pri_rd_cfg_byte(struct pci_dev *dev, int where, u8 *p)
static int
iop310_pri_rd_cfg_word(struct pci_dev *dev, int where, u16 *p)
{
- int ret;
u16 val;
- *IOP310_POCCAR = iop310_cfg_address(dev, where);
-
- val = (*IOP310_POCCDR) >> ((where & 2) * 8);
- __asm__ __volatile__("nop; nop; nop; nop");
+ val = iop310_pri_read(dev, where) >> ((where & 3) * 8);
- ret = iop310_pri_pci_status();
- if (ret)
+ if (iop310_pri_pci_status())
val = 0xffff;
*p = val;
@@ -149,16 +157,11 @@ iop310_pri_rd_cfg_word(struct pci_dev *dev, int where, u16 *p)
static int
iop310_pri_rd_cfg_dword(struct pci_dev *dev, int where, u32 *p)
{
- int ret;
u32 val;
- *IOP310_POCCAR = iop310_cfg_address(dev, where);
+ val = iop310_pri_read(dev, where);
- val = *IOP310_POCCDR;
- __asm__ __volatile__("nop; nop; nop; nop");
-
- ret = iop310_pri_pci_status();
- if (ret)
+ if (iop310_pri_pci_status())
val = 0xffffffff;
*p = val;
@@ -169,16 +172,11 @@ iop310_pri_rd_cfg_dword(struct pci_dev *dev, int where, u32 *p)
static int
iop310_pri_wr_cfg_byte(struct pci_dev *dev, int where, u8 v)
{
- int ret;
u32 val;
- *IOP310_POCCAR = iop310_cfg_address(dev, where);
+ val = iop310_pri_read(dev, where);
- val = *IOP310_POCCDR;
- __asm__ __volatile__("nop; nop; nop; nop");
-
- ret = iop310_pri_pci_status();
- if (ret == 0) {
+ if (iop310_pri_pci_status() == 0) {
where = (where & 3) * 8;
val &= ~(0xff << where);
val |= v << where;
@@ -191,16 +189,11 @@ iop310_pri_wr_cfg_byte(struct pci_dev *dev, int where, u8 v)
static int
iop310_pri_wr_cfg_word(struct pci_dev *dev, int where, u16 v)
{
- int ret;
u32 val;
- *IOP310_POCCAR = iop310_cfg_address(dev, where);
-
- val = *IOP310_POCCDR;
- __asm__ __volatile__("nop; nop; nop; nop");
+ val = iop310_pri_read(dev, where);
- ret = iop310_pri_pci_status();
- if (ret == 0) {
+ if (iop310_pri_pci_status() == 0) {
where = (where & 2) * 8;
val &= ~(0xffff << where);
val |= v << where;
@@ -211,11 +204,21 @@ iop310_pri_wr_cfg_word(struct pci_dev *dev, int where, u16 v)
}
static int
-iop310_pri_wr_cfg_dword(struct pci_dev *dev, int where, u32 v)
+iop310_pri_wr_cfg_dword(struct pci_dev *dev, int where, u32 val)
{
- *IOP310_POCCAR = iop310_cfg_address(dev, where);
- *IOP310_POCCDR = v;
- __asm__ __volatile__("nop; nop; nop; nop");
+ unsigned long addr;
+
+ addr = iop310_cfg_address(dev, where);
+
+ __asm__ __volatile__(
+ "str %1, [%2]\n\t"
+ "str %0, [%3]\n\t"
+ "nop\n\t"
+ "nop\n\t"
+ "nop\n\t"
+ "nop\n\t"
+ :
+ : "r" (val), "r" (addr), "r" (IOP310_POCCAR), "r" (IOP310_POCCDR));
return PCIBIOS_SUCCESSFUL;
}
@@ -248,28 +251,38 @@ static int iop310_sec_pci_status(void)
ret = 1;
}
if (ret)
- DBG("ERROR (%08lx %08lx)", usr, uisr);
+ DBG("ERROR (%08x %08x)", usr, uisr);
return ret;
}
+static inline u32 iop310_sec_read(struct pci_dev *dev, int where)
+{
+ unsigned long addr = iop310_cfg_address(dev, where);
+ u32 val;
+
+ __asm__ __volatile__(
+ "str %1, [%2]\n\t"
+ "ldr %0, [%3]\n\t"
+ "nop\n\t"
+ "nop\n\t"
+ "nop\n\t"
+ "nop\n\t"
+ : "=r" (val)
+ : "r" (addr), "r" (IOP310_SOCCAR), "r" (IOP310_SOCCDR));
+
+ return val;
+}
+
static int
iop310_sec_rd_cfg_byte(struct pci_dev *dev, int where, u8 *p)
{
- int ret;
u8 val;
- DBG("rdb: %d:%02x.%x %02x ", dev->bus->number, PCI_SLOT(dev->devfn),
- PCI_FUNC(dev->devfn), where);
- *IOP310_SOCCAR = iop310_cfg_address(dev, where);
+ val = iop310_sec_read(dev, where) >> ((where & 3) * 8);
- val = (*IOP310_SOCCDR) >> ((where & 3) * 8);
- __asm__ __volatile__("nop; nop; nop; nop");
-
- DBG(">= %08lx ", val);
- ret = iop310_sec_pci_status();
- if (ret)
+ if (iop310_sec_pci_status())
val = 0xff;
- DBG("\n");
+
*p = val;
return PCIBIOS_SUCCESSFUL;
@@ -278,21 +291,13 @@ iop310_sec_rd_cfg_byte(struct pci_dev *dev, int where, u8 *p)
static int
iop310_sec_rd_cfg_word(struct pci_dev *dev, int where, u16 *p)
{
- int ret;
u16 val;
- DBG("rdw: %d:%02x.%x %02x ", dev->bus->number, PCI_SLOT(dev->devfn),
- PCI_FUNC(dev->devfn), where);
- *IOP310_SOCCAR = iop310_cfg_address(dev, where);
-
- val = (*IOP310_SOCCDR) >> ((where & 3) * 8);
- __asm__ __volatile__("nop; nop; nop; nop");
+ val = iop310_sec_read(dev, where) >> ((where & 3) * 8);
- DBG(">= %08lx ", val);
- ret = iop310_sec_pci_status();
- if (ret)
+ if (iop310_sec_pci_status())
val = 0xffff;
- DBG("\n");
+
*p = val;
return PCIBIOS_SUCCESSFUL;
@@ -301,21 +306,13 @@ iop310_sec_rd_cfg_word(struct pci_dev *dev, int where, u16 *p)
static int
iop310_sec_rd_cfg_dword(struct pci_dev *dev, int where, u32 *p)
{
- int ret;
u32 val;
- DBG("rdl: %d:%02x.%x %02x ", dev->bus->number, PCI_SLOT(dev->devfn),
- PCI_FUNC(dev->devfn), where);
- *IOP310_SOCCAR = iop310_cfg_address(dev, where);
-
- val = *IOP310_SOCCDR;
- __asm__ __volatile__("nop; nop; nop; nop");
+ val = iop310_sec_read(dev, where);
- DBG(">= %08lx ", val);
- ret = iop310_sec_pci_status();
- if (ret)
+ if (iop310_sec_pci_status())
val = 0xffffffff;
- DBG("\n");
+
*p = val;
return PCIBIOS_SUCCESSFUL;
@@ -324,63 +321,54 @@ iop310_sec_rd_cfg_dword(struct pci_dev *dev, int where, u32 *p)
static int
iop310_sec_wr_cfg_byte(struct pci_dev *dev, int where, u8 v)
{
- int ret;
u32 val;
- DBG("wrb: %d:%02x.%x %02x ", dev->bus->number, PCI_SLOT(dev->devfn),
- PCI_FUNC(dev->devfn), where);
- *IOP310_SOCCAR = iop310_cfg_address(dev, where);
-
- val = *IOP310_SOCCDR;
- __asm__ __volatile__("nop; nop; nop; nop");
+ val = iop310_sec_read(dev, where);
- DBG("<= %08lx", v);
- ret = iop310_sec_pci_status();
- if (ret == 0) {
+ if (iop310_sec_pci_status() == 0) {
where = (where & 3) * 8;
val &= ~(0xff << where);
val |= v << where;
*IOP310_SOCCDR = val;
}
- DBG("\n");
+
return PCIBIOS_SUCCESSFUL;
}
static int
iop310_sec_wr_cfg_word(struct pci_dev *dev, int where, u16 v)
{
- int ret;
u32 val;
- DBG("wrw: %d:%02x.%x %02x ", dev->bus->number, PCI_SLOT(dev->devfn),
- PCI_FUNC(dev->devfn), where);
- *IOP310_SOCCAR = iop310_cfg_address(dev, where);
+ val = iop310_sec_read(dev, where);
- val = *IOP310_SOCCDR;
- __asm__ __volatile__("nop; nop; nop; nop");
-
- DBG("<= %08lx", v);
- ret = iop310_sec_pci_status();
- if (ret == 0) {
+ if (iop310_sec_pci_status() == 0) {
where = (where & 2) * 8;
val &= ~(0xffff << where);
val |= v << where;
*IOP310_SOCCDR = val;
}
- DBG("\n");
+
return PCIBIOS_SUCCESSFUL;
}
static int
-iop310_sec_wr_cfg_dword(struct pci_dev *dev, int where, u32 v)
+iop310_sec_wr_cfg_dword(struct pci_dev *dev, int where, u32 val)
{
- DBG("wrl: %d:%02x.%x %02x ", dev->bus->number, PCI_SLOT(dev->devfn),
- PCI_FUNC(dev->devfn), where);
- *IOP310_SOCCAR = iop310_cfg_address(dev, where);
- *IOP310_SOCCDR = v;
- __asm__ __volatile__("nop; nop; nop; nop");
+ unsigned long addr;
+
+ addr = iop310_cfg_address(dev, where);
+
+ __asm__ __volatile__(
+ "str %1, [%2]\n\t"
+ "str %0, [%3]\n\t"
+ "nop\n\t"
+ "nop\n\t"
+ "nop\n\t"
+ "nop\n\t"
+ :
+ : "r" (val), "r" (addr), "r" (IOP310_SOCCAR), "r" (IOP310_SOCCDR));
- DBG("<= %08lx\n", v);
return PCIBIOS_SUCCESSFUL;
}
@@ -394,13 +382,34 @@ static struct pci_ops iop310_secondary_ops = {
};
/*
- * When a PCI device does not exist during config cycles, the 80200 gets a
- * bus error instead of returning 0xffffffff. This handler simply returns.
+ * When a PCI device does not exist during config cycles, the 80200 gets
+ * an external abort instead of returning 0xffffffff. If it was an
+ * imprecise abort, we need to correct the return address to point after
+ * the instruction. Also note that the Xscale manual says:
+ *
+ * "if a stall-until-complete LD or ST instruction triggers an
+ * imprecise fault, then that fault will be seen by the program
+ * within 3 instructions."
+ *
+ * This does not appear to be the case. With 8 NOPs after the load, we
+ * see the imprecise abort occuring on the STM of iop310_sec_pci_status()
+ * which is about 10 instructions away.
+ *
+ * Always trust reality!
*/
-int iop310_pci_abort_handler(unsigned long addr, struct pt_regs *regs)
+static int
+iop310_pci_abort(unsigned long addr, unsigned int fsr, struct pt_regs *regs)
{
- DBG("PCI abort: address = %08x PC = %08x LR = %08lx\n",
- addr, regs->ARM_pc, regs->ARM_lr);
+ DBG("PCI abort: address = 0x%08lx fsr = 0x%03x PC = 0x%08lx LR = 0x%08lx\n",
+ addr, fsr, regs->ARM_pc, regs->ARM_lr);
+
+ /*
+ * If it was an imprecise abort, then we need to correct the
+ * return address to be _after_ the instruction.
+ */
+ if (fsr & (1 << 10))
+ regs->ARM_pc += 4;
+
return 0;
}
@@ -515,6 +524,5 @@ void iop310_init(void)
*/
*IOP310_PCR &= 0xfff8;
- external_fault = iop310_pci_abort_handler;
+ hook_fault_code(6, iop310_pci_abort, SIGBUS, "imprecise external abort");
}
-
diff --git a/arch/arm/mm/alignment.c b/arch/arm/mm/alignment.c
index 0e76c39dc3f5..0b678b1d7fdd 100644
--- a/arch/arm/mm/alignment.c
+++ b/arch/arm/mm/alignment.c
@@ -30,9 +30,7 @@
#include <asm/pgtable.h>
#include <asm/unaligned.h>
-extern void
-do_bad_area(struct task_struct *tsk, struct mm_struct *mm, unsigned long addr,
- int error_code, struct pt_regs *regs);
+#include "fault.h"
/*
* 32-bit misaligned trap handler (c) 1998 San Mehat (CCC) -July 1998
@@ -130,31 +128,6 @@ static int proc_alignment_write(struct file *file, const char *buffer,
return count;
}
-/*
- * This needs to be done after sysctl_init, otherwise sys/ will be
- * overwritten. Actually, this shouldn't be in sys/ at all since
- * it isn't a sysctl, and it doesn't contain sysctl information.
- * We now locate it in /proc/cpu/alignment instead.
- */
-static int __init alignment_init(void)
-{
- struct proc_dir_entry *res;
-
- res = proc_mkdir("cpu", NULL);
- if (!res)
- return -ENOMEM;
-
- res = create_proc_entry("alignment", S_IWUSR | S_IRUGO, res);
- if (!res)
- return -ENOMEM;
-
- res->read_proc = proc_alignment_read;
- res->write_proc = proc_alignment_write;
-
- return 0;
-}
-
-__initcall(alignment_init);
#endif /* CONFIG_PROC_FS */
union offset_union {
@@ -486,7 +459,8 @@ bad:
return TYPE_ERROR;
}
-int do_alignment(unsigned long addr, int error_code, struct pt_regs *regs)
+static int
+do_alignment(unsigned long addr, unsigned int fsr, struct pt_regs *regs)
{
union offset_union offset;
unsigned long instr, instrptr;
@@ -577,7 +551,7 @@ int do_alignment(unsigned long addr, int error_code, struct pt_regs *regs)
/*
* We got a fault - fix it up, or die.
*/
- do_bad_area(current, current->mm, addr, error_code, regs);
+ do_bad_area(current, current->mm, addr, fsr, regs);
return 0;
bad:
@@ -594,8 +568,8 @@ int do_alignment(unsigned long addr, int error_code, struct pt_regs *regs)
if (ai_usermode & 1)
printk("Alignment trap: %s (%d) PC=0x%08lx Instr=0x%08lx "
- "Address=0x%08lx Code 0x%02x\n", current->comm,
- current->pid, instrptr, instr, addr, error_code);
+ "Address=0x%08lx FSR 0x%03x\n", current->comm,
+ current->pid, instrptr, instr, addr, fsr);
if (ai_usermode & 2)
goto fixup;
@@ -607,3 +581,34 @@ int do_alignment(unsigned long addr, int error_code, struct pt_regs *regs)
return 0;
}
+
+/*
+ * This needs to be done after sysctl_init, otherwise sys/ will be
+ * overwritten. Actually, this shouldn't be in sys/ at all since
+ * it isn't a sysctl, and it doesn't contain sysctl information.
+ * We now locate it in /proc/cpu/alignment instead.
+ */
+static int __init alignment_init(void)
+{
+#ifdef CONFIG_PROC_FS
+ struct proc_dir_entry *res;
+
+ res = proc_mkdir("cpu", NULL);
+ if (!res)
+ return -ENOMEM;
+
+ res = create_proc_entry("alignment", S_IWUSR | S_IRUGO, res);
+ if (!res)
+ return -ENOMEM;
+
+ res->read_proc = proc_alignment_read;
+ res->write_proc = proc_alignment_write;
+#endif
+
+ hook_fault_code(1, do_alignment, SIGILL, "alignment exception");
+ hook_fault_code(3, do_alignment, SIGILL, "alignment exception");
+
+ return 0;
+}
+
+__initcall(alignment_init);
diff --git a/arch/arm/mm/fault-armv.c b/arch/arm/mm/fault-armv.c
index dea4ccc1e604..aa6333a7a659 100644
--- a/arch/arm/mm/fault-armv.c
+++ b/arch/arm/mm/fault-armv.c
@@ -2,102 +2,62 @@
* linux/arch/arm/mm/fault-armv.c
*
* Copyright (C) 1995 Linus Torvalds
- * Modifications for ARM processor (c) 1995-2001 Russell King
+ * Modifications for ARM processor (c) 1995-2002 Russell King
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/
-#include <linux/config.h>
-#include <linux/compiler.h>
-#include <linux/signal.h>
#include <linux/sched.h>
#include <linux/kernel.h>
-#include <linux/errno.h>
-#include <linux/string.h>
#include <linux/types.h>
#include <linux/ptrace.h>
-#include <linux/mman.h>
#include <linux/mm.h>
-#include <linux/interrupt.h>
-#include <linux/proc_fs.h>
#include <linux/bitops.h>
#include <linux/init.h>
-#include <asm/system.h>
-#include <asm/uaccess.h>
#include <asm/pgalloc.h>
#include <asm/pgtable.h>
-extern void die_if_kernel(const char *str, struct pt_regs *regs, int err);
-extern void show_pte(struct mm_struct *mm, unsigned long addr);
-extern int do_page_fault(unsigned long addr, int error_code,
- struct pt_regs *regs);
-extern int do_translation_fault(unsigned long addr, int error_code,
- struct pt_regs *regs);
-extern void do_bad_area(struct task_struct *tsk, struct mm_struct *mm,
- unsigned long addr, int error_code,
- struct pt_regs *regs);
-
-#ifdef CONFIG_ALIGNMENT_TRAP
-extern int do_alignment(unsigned long addr, int error_code, struct pt_regs *regs);
-#else
-#define do_alignment do_bad
-#endif
-
+#include "fault.h"
/*
* Some section permission faults need to be handled gracefully.
* They can happen due to a __{get,put}_user during an oops.
*/
static int
-do_sect_fault(unsigned long addr, int error_code, struct pt_regs *regs)
+do_sect_fault(unsigned long addr, unsigned int fsr, struct pt_regs *regs)
{
struct task_struct *tsk = current;
- do_bad_area(tsk, tsk->active_mm, addr, error_code, regs);
+ do_bad_area(tsk, tsk->active_mm, addr, fsr, regs);
return 0;
}
/*
- * Hook for things that need to trap external faults. Note that
- * we don't guarantee that this will be the final version of the
- * interface.
- */
-int (*external_fault)(unsigned long addr, struct pt_regs *regs);
-
-static int
-do_external_fault(unsigned long addr, int error_code, struct pt_regs *regs)
-{
- if (external_fault)
- return external_fault(addr, regs);
- return 1;
-}
-
-/*
* This abort handler always returns "fault".
*/
static int
-do_bad(unsigned long addr, int error_code, struct pt_regs *regs)
+do_bad(unsigned long addr, unsigned int fsr, struct pt_regs *regs)
{
return 1;
}
-static const struct fsr_info {
- int (*fn)(unsigned long addr, int error_code, struct pt_regs *regs);
+static struct fsr_info {
+ int (*fn)(unsigned long addr, unsigned int fsr, struct pt_regs *regs);
int sig;
const char *name;
} fsr_info[] = {
{ do_bad, SIGSEGV, "vector exception" },
- { do_alignment, SIGILL, "alignment exception" },
+ { do_bad, SIGILL, "alignment exception" },
{ do_bad, SIGKILL, "terminal exception" },
- { do_alignment, SIGILL, "alignment exception" },
- { do_external_fault, SIGBUS, "external abort on linefetch" },
+ { do_bad, SIGILL, "alignment exception" },
+ { do_bad, SIGBUS, "external abort on linefetch" },
{ do_translation_fault, SIGSEGV, "section translation fault" },
- { do_external_fault, SIGBUS, "external abort on linefetch" },
+ { do_bad, SIGBUS, "external abort on linefetch" },
{ do_page_fault, SIGSEGV, "page translation fault" },
- { do_external_fault, SIGBUS, "external abort on non-linefetch" },
+ { do_bad, SIGBUS, "external abort on non-linefetch" },
{ do_bad, SIGSEGV, "section domain fault" },
- { do_external_fault, SIGBUS, "external abort on non-linefetch" },
+ { do_bad, SIGBUS, "external abort on non-linefetch" },
{ do_bad, SIGSEGV, "page domain fault" },
{ do_bad, SIGBUS, "external abort on translation" },
{ do_sect_fault, SIGSEGV, "section permission fault" },
@@ -105,6 +65,17 @@ static const struct fsr_info {
{ do_page_fault, SIGSEGV, "page permission fault" }
};
+void __init
+hook_fault_code(int nr, int (*fn)(unsigned long, unsigned int, struct pt_regs *),
+ int sig, const char *name)
+{
+ if (nr >= 0 && nr < 16) {
+ fsr_info[nr].fn = fn;
+ fsr_info[nr].sig = sig;
+ fsr_info[nr].name = name;
+ }
+}
+
/*
* Dispatch a data abort to the relevant handler.
*/
diff --git a/arch/arm/mm/fault-common.c b/arch/arm/mm/fault-common.c
index 37770f82b356..37c2d96302c4 100644
--- a/arch/arm/mm/fault-common.c
+++ b/arch/arm/mm/fault-common.c
@@ -23,9 +23,9 @@
#include <linux/init.h>
#include <asm/system.h>
-#include <asm/uaccess.h>
#include <asm/pgtable.h>
-#include <asm/unaligned.h>
+
+#include "fault.h"
#ifdef CONFIG_CPU_26
#define FAULT_CODE_WRITE 0x02
@@ -43,8 +43,6 @@
#define READ_FAULT(code) (!DO_COW(code))
#endif
-NORET_TYPE void die(const char *msg, struct pt_regs *regs, int err) ATTRIB_NORET;
-
/*
* This is useful to dump out the page tables associated with
* 'addr' in mm 'mm'.
@@ -101,7 +99,7 @@ void show_pte(struct mm_struct *mm, unsigned long addr)
* Oops. The kernel tried to access some page that wasn't present.
*/
static void
-__do_kernel_fault(struct mm_struct *mm, unsigned long addr, int error_code,
+__do_kernel_fault(struct mm_struct *mm, unsigned long addr, unsigned int fsr,
struct pt_regs *regs)
{
unsigned long fixup;
@@ -127,7 +125,7 @@ __do_kernel_fault(struct mm_struct *mm, unsigned long addr, int error_code,
"paging request", addr);
show_pte(mm, addr);
- die("Oops", regs, error_code);
+ die("Oops", regs, fsr);
do_exit(SIGKILL);
}
@@ -136,20 +134,20 @@ __do_kernel_fault(struct mm_struct *mm, unsigned long addr, int error_code,
* User mode accesses just cause a SIGSEGV
*/
static void
-__do_user_fault(struct task_struct *tsk, unsigned long addr, int error_code,
- int code, struct pt_regs *regs)
+__do_user_fault(struct task_struct *tsk, unsigned long addr,
+ unsigned int fsr, int code, struct pt_regs *regs)
{
struct siginfo si;
#ifdef CONFIG_DEBUG_USER
printk(KERN_DEBUG "%s: unhandled page fault at 0x%08lx, code 0x%03x\n",
- tsk->comm, addr, error_code);
+ tsk->comm, addr, fsr);
show_pte(tsk->mm, addr);
show_regs(regs);
#endif
tsk->thread.address = addr;
- tsk->thread.error_code = error_code;
+ tsk->thread.error_code = fsr;
tsk->thread.trap_no = 14;
si.si_signo = SIGSEGV;
si.si_errno = 0;
@@ -160,20 +158,20 @@ __do_user_fault(struct task_struct *tsk, unsigned long addr, int error_code,
void
do_bad_area(struct task_struct *tsk, struct mm_struct *mm, unsigned long addr,
- int error_code, struct pt_regs *regs)
+ unsigned int fsr, struct pt_regs *regs)
{
/*
* If we are in kernel mode at this point, we
* have no context to handle this fault with.
*/
if (user_mode(regs))
- __do_user_fault(tsk, addr, error_code, SEGV_MAPERR, regs);
+ __do_user_fault(tsk, addr, fsr, SEGV_MAPERR, regs);
else
- __do_kernel_fault(mm, addr, error_code, regs);
+ __do_kernel_fault(mm, addr, fsr, regs);
}
static int
-__do_page_fault(struct mm_struct *mm, unsigned long addr, int error_code,
+__do_page_fault(struct mm_struct *mm, unsigned long addr, unsigned int fsr,
struct task_struct *tsk)
{
struct vm_area_struct *vma;
@@ -191,7 +189,7 @@ __do_page_fault(struct mm_struct *mm, unsigned long addr, int error_code,
* memory access, so we can handle it.
*/
good_area:
- if (READ_FAULT(error_code)) /* read? */
+ if (READ_FAULT(fsr)) /* read? */
mask = VM_READ|VM_EXEC;
else
mask = VM_WRITE;
@@ -206,7 +204,7 @@ good_area:
* than endlessly redo the fault.
*/
survive:
- fault = handle_mm_fault(mm, vma, addr & PAGE_MASK, DO_COW(error_code));
+ fault = handle_mm_fault(mm, vma, addr & PAGE_MASK, DO_COW(fsr));
/*
* Handle the "normal" cases first - successful and sigbus
@@ -239,7 +237,7 @@ out:
return fault;
}
-int do_page_fault(unsigned long addr, int error_code, struct pt_regs *regs)
+int do_page_fault(unsigned long addr, unsigned int fsr, struct pt_regs *regs)
{
struct task_struct *tsk;
struct mm_struct *mm;
@@ -256,7 +254,7 @@ int do_page_fault(unsigned long addr, int error_code, struct pt_regs *regs)
goto no_context;
down_read(&mm->mmap_sem);
- fault = __do_page_fault(mm, addr, error_code, tsk);
+ fault = __do_page_fault(mm, addr, fsr, tsk);
up_read(&mm->mmap_sem);
/*
@@ -287,7 +285,7 @@ int do_page_fault(unsigned long addr, int error_code, struct pt_regs *regs)
printk("VM: killing process %s\n", tsk->comm);
do_exit(SIGKILL);
} else
- __do_user_fault(tsk, addr, error_code, fault == -1 ?
+ __do_user_fault(tsk, addr, fsr, fault == -1 ?
SEGV_ACCERR : SEGV_MAPERR, regs);
return 0;
@@ -302,7 +300,7 @@ do_sigbus:
* or user mode.
*/
tsk->thread.address = addr;
- tsk->thread.error_code = error_code;
+ tsk->thread.error_code = fsr;
tsk->thread.trap_no = 14;
force_sig(SIGBUS, tsk);
#ifdef CONFIG_DEBUG_USER
@@ -315,7 +313,7 @@ do_sigbus:
return 0;
no_context:
- __do_kernel_fault(mm, addr, error_code, regs);
+ __do_kernel_fault(mm, addr, fsr, regs);
return 0;
}
@@ -336,7 +334,8 @@ no_context:
* interrupt or a critical region, and should only copy the information
* from the master page table, nothing more.
*/
-int do_translation_fault(unsigned long addr, int error_code, struct pt_regs *regs)
+int do_translation_fault(unsigned long addr, unsigned int fsr,
+ struct pt_regs *regs)
{
struct task_struct *tsk;
unsigned int offset;
@@ -344,7 +343,7 @@ int do_translation_fault(unsigned long addr, int error_code, struct pt_regs *reg
pmd_t *pmd, *pmd_k;
if (addr < TASK_SIZE)
- return do_page_fault(addr, error_code, regs);
+ return do_page_fault(addr, fsr, regs);
offset = __pgd_offset(addr);
@@ -372,6 +371,6 @@ int do_translation_fault(unsigned long addr, int error_code, struct pt_regs *reg
bad_area:
tsk = current;
- do_bad_area(tsk, tsk->active_mm, addr, error_code, regs);
+ do_bad_area(tsk, tsk->active_mm, addr, fsr, regs);
return 0;
}
diff --git a/arch/arm/mm/fault.h b/arch/arm/mm/fault.h
new file mode 100644
index 000000000000..b4d709819aae
--- /dev/null
+++ b/arch/arm/mm/fault.h
@@ -0,0 +1,8 @@
+void do_bad_area(struct task_struct *tsk, struct mm_struct *mm,
+ unsigned long addr, unsigned int fsr, struct pt_regs *regs);
+
+void show_pte(struct mm_struct *mm, unsigned long addr);
+
+int do_page_fault(unsigned long addr, unsigned int fsr, struct pt_regs *regs);
+
+int do_translation_fault(unsigned long addr, unsigned int fsr, struct pt_regs *regs);
diff --git a/include/asm-arm/system.h b/include/asm-arm/system.h
index 67f15f1be682..d9a68af9d5f9 100644
--- a/include/asm-arm/system.h
+++ b/include/asm-arm/system.h
@@ -24,6 +24,17 @@ extern int have_isa_bridge;
#define have_isa_bridge (0)
#endif
+struct pt_regs;
+
+void die(const char *msg, struct pt_regs *regs, int err)
+ __attribute__((noreturn));
+
+void die_if_kernel(const char *str, struct pt_regs *regs, int err);
+
+void hook_fault_code(int nr, int (*fn)(unsigned long, unsigned int,
+ struct pt_regs *),
+ int sig, const char *name);
+
#include <asm/proc-fns.h>
#define xchg(ptr,x) \