diff options
| author | Russell King <rmk@flint.arm.linux.org.uk> | 2002-06-05 14:57:54 +0100 |
|---|---|---|
| committer | Russell King <rmk@flint.arm.linux.org.uk> | 2002-06-05 14:57:54 +0100 |
| commit | 75fb1f976e7b45a585ecdb214a6027e271a44dbf (patch) | |
| tree | 3ce1cc765fcda2f76660917c60842b0b74ae4d99 | |
| parent | ea1e2d6259e83d6fae83473f7b90a683ec770322 (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.c | 14 | ||||
| -rw-r--r-- | arch/arm/mach-iop310/iop310-pci.c | 226 | ||||
| -rw-r--r-- | arch/arm/mm/alignment.c | 69 | ||||
| -rw-r--r-- | arch/arm/mm/fault-armv.c | 77 | ||||
| -rw-r--r-- | arch/arm/mm/fault-common.c | 47 | ||||
| -rw-r--r-- | arch/arm/mm/fault.h | 8 | ||||
| -rw-r--r-- | include/asm-arm/system.h | 11 |
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) \ |
