Commit 75fb1f97 authored by Russell King's avatar Russell King

[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.
parent ea1e2d62
...@@ -439,15 +439,16 @@ static int __init pci_v3_setup_resources(struct resource **resource) ...@@ -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_LBFADDR (IO_ADDRESS(INTEGRATOR_SC_BASE) + 0x20)
#define SC_LBFCODE (IO_ADDRESS(INTEGRATOR_SC_BASE) + 0x24) #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 pc = instruction_pointer(regs);
unsigned long instr = *(unsigned long *)pc; unsigned long instr = *(unsigned long *)pc;
#if 0 #if 0
char buf[128]; char buf[128];
sprintf(buf, "V3 fault: address=0x%08lx, pc=0x%08lx [%08lx] LBFADDR=%08x LBFCODE=%02x ISTAT=%02x\n", sprintf(buf, "V3 fault: addr 0x%08lx, FSR 0x%03x, PC 0x%08lx [%08lx] LBFADDR=%08x LBFCODE=%02x ISTAT=%02x\n",
addr, pc, instr, __raw_readl(SC_LBFADDR), __raw_readl(SC_LBFCODE) & 255, addr, fsr, pc, instr, __raw_readl(SC_LBFADDR), __raw_readl(SC_LBFCODE) & 255,
v3_readb(V3_LB_ISTAT)); v3_readb(V3_LB_ISTAT));
printk(KERN_DEBUG "%s", buf); printk(KERN_DEBUG "%s", buf);
printascii(buf); printascii(buf);
...@@ -515,8 +516,6 @@ static void v3_irq(int irq, void *devid, struct pt_regs *regs) ...@@ -515,8 +516,6 @@ static void v3_irq(int irq, void *devid, struct pt_regs *regs)
#endif #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 __init pci_v3_setup(int nr, struct pci_sys_data *sys)
{ {
int ret = 0; int ret = 0;
...@@ -547,7 +546,10 @@ void __init pci_v3_preinit(void) ...@@ -547,7 +546,10 @@ void __init pci_v3_preinit(void)
/* /*
* Hook in our fault handler for PCI errors * 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); spin_lock_irqsave(&v3_lock, flags);
......
...@@ -106,19 +106,32 @@ static int iop310_pri_pci_status(void) ...@@ -106,19 +106,32 @@ static int iop310_pri_pci_status(void)
return ret; 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 static int
iop310_pri_rd_cfg_byte(struct pci_dev *dev, int where, u8 *p) iop310_pri_rd_cfg_byte(struct pci_dev *dev, int where, u8 *p)
{ {
int ret;
u8 val; u8 val;
*IOP310_POCCAR = iop310_cfg_address(dev, where); val = iop310_pri_read(dev, where) >> ((where & 3) * 8);
val = (*IOP310_POCCDR) >> ((where & 3) * 8);
__asm__ __volatile__("nop; nop; nop; nop");
ret = iop310_pri_pci_status(); if (iop310_pri_pci_status())
if (ret)
val = 0xff; val = 0xff;
*p = val; *p = val;
...@@ -129,16 +142,11 @@ iop310_pri_rd_cfg_byte(struct pci_dev *dev, int where, u8 *p) ...@@ -129,16 +142,11 @@ iop310_pri_rd_cfg_byte(struct pci_dev *dev, int where, u8 *p)
static int static int
iop310_pri_rd_cfg_word(struct pci_dev *dev, int where, u16 *p) iop310_pri_rd_cfg_word(struct pci_dev *dev, int where, u16 *p)
{ {
int ret;
u16 val; u16 val;
*IOP310_POCCAR = iop310_cfg_address(dev, where); val = iop310_pri_read(dev, where) >> ((where & 3) * 8);
val = (*IOP310_POCCDR) >> ((where & 2) * 8);
__asm__ __volatile__("nop; nop; nop; nop");
ret = iop310_pri_pci_status(); if (iop310_pri_pci_status())
if (ret)
val = 0xffff; val = 0xffff;
*p = val; *p = val;
...@@ -149,16 +157,11 @@ iop310_pri_rd_cfg_word(struct pci_dev *dev, int where, u16 *p) ...@@ -149,16 +157,11 @@ iop310_pri_rd_cfg_word(struct pci_dev *dev, int where, u16 *p)
static int static int
iop310_pri_rd_cfg_dword(struct pci_dev *dev, int where, u32 *p) iop310_pri_rd_cfg_dword(struct pci_dev *dev, int where, u32 *p)
{ {
int ret;
u32 val; u32 val;
*IOP310_POCCAR = iop310_cfg_address(dev, where); val = iop310_pri_read(dev, where);
val = *IOP310_POCCDR; if (iop310_pri_pci_status())
__asm__ __volatile__("nop; nop; nop; nop");
ret = iop310_pri_pci_status();
if (ret)
val = 0xffffffff; val = 0xffffffff;
*p = val; *p = val;
...@@ -169,16 +172,11 @@ iop310_pri_rd_cfg_dword(struct pci_dev *dev, int where, u32 *p) ...@@ -169,16 +172,11 @@ iop310_pri_rd_cfg_dword(struct pci_dev *dev, int where, u32 *p)
static int static int
iop310_pri_wr_cfg_byte(struct pci_dev *dev, int where, u8 v) iop310_pri_wr_cfg_byte(struct pci_dev *dev, int where, u8 v)
{ {
int ret;
u32 val; u32 val;
*IOP310_POCCAR = iop310_cfg_address(dev, where); val = iop310_pri_read(dev, where);
val = *IOP310_POCCDR; if (iop310_pri_pci_status() == 0) {
__asm__ __volatile__("nop; nop; nop; nop");
ret = iop310_pri_pci_status();
if (ret == 0) {
where = (where & 3) * 8; where = (where & 3) * 8;
val &= ~(0xff << where); val &= ~(0xff << where);
val |= v << where; val |= v << where;
...@@ -191,16 +189,11 @@ iop310_pri_wr_cfg_byte(struct pci_dev *dev, int where, u8 v) ...@@ -191,16 +189,11 @@ iop310_pri_wr_cfg_byte(struct pci_dev *dev, int where, u8 v)
static int static int
iop310_pri_wr_cfg_word(struct pci_dev *dev, int where, u16 v) iop310_pri_wr_cfg_word(struct pci_dev *dev, int where, u16 v)
{ {
int ret;
u32 val; 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 (iop310_pri_pci_status() == 0) {
if (ret == 0) {
where = (where & 2) * 8; where = (where & 2) * 8;
val &= ~(0xffff << where); val &= ~(0xffff << where);
val |= v << where; val |= v << where;
...@@ -211,11 +204,21 @@ iop310_pri_wr_cfg_word(struct pci_dev *dev, int where, u16 v) ...@@ -211,11 +204,21 @@ iop310_pri_wr_cfg_word(struct pci_dev *dev, int where, u16 v)
} }
static int 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); unsigned long addr;
*IOP310_POCCDR = v;
__asm__ __volatile__("nop; nop; nop; nop"); 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; return PCIBIOS_SUCCESSFUL;
} }
...@@ -248,28 +251,38 @@ static int iop310_sec_pci_status(void) ...@@ -248,28 +251,38 @@ static int iop310_sec_pci_status(void)
ret = 1; ret = 1;
} }
if (ret) if (ret)
DBG("ERROR (%08lx %08lx)", usr, uisr); DBG("ERROR (%08x %08x)", usr, uisr);
return ret; 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 static int
iop310_sec_rd_cfg_byte(struct pci_dev *dev, int where, u8 *p) iop310_sec_rd_cfg_byte(struct pci_dev *dev, int where, u8 *p)
{ {
int ret;
u8 val; u8 val;
DBG("rdb: %d:%02x.%x %02x ", dev->bus->number, PCI_SLOT(dev->devfn), val = iop310_sec_read(dev, where) >> ((where & 3) * 8);
PCI_FUNC(dev->devfn), where);
*IOP310_SOCCAR = iop310_cfg_address(dev, where);
val = (*IOP310_SOCCDR) >> ((where & 3) * 8); if (iop310_sec_pci_status())
__asm__ __volatile__("nop; nop; nop; nop");
DBG(">= %08lx ", val);
ret = iop310_sec_pci_status();
if (ret)
val = 0xff; val = 0xff;
DBG("\n");
*p = val; *p = val;
return PCIBIOS_SUCCESSFUL; return PCIBIOS_SUCCESSFUL;
...@@ -278,21 +291,13 @@ iop310_sec_rd_cfg_byte(struct pci_dev *dev, int where, u8 *p) ...@@ -278,21 +291,13 @@ iop310_sec_rd_cfg_byte(struct pci_dev *dev, int where, u8 *p)
static int static int
iop310_sec_rd_cfg_word(struct pci_dev *dev, int where, u16 *p) iop310_sec_rd_cfg_word(struct pci_dev *dev, int where, u16 *p)
{ {
int ret;
u16 val; u16 val;
DBG("rdw: %d:%02x.%x %02x ", dev->bus->number, PCI_SLOT(dev->devfn), val = iop310_sec_read(dev, where) >> ((where & 3) * 8);
PCI_FUNC(dev->devfn), where);
*IOP310_SOCCAR = iop310_cfg_address(dev, where);
val = (*IOP310_SOCCDR) >> ((where & 3) * 8);
__asm__ __volatile__("nop; nop; nop; nop");
DBG(">= %08lx ", val); if (iop310_sec_pci_status())
ret = iop310_sec_pci_status();
if (ret)
val = 0xffff; val = 0xffff;
DBG("\n");
*p = val; *p = val;
return PCIBIOS_SUCCESSFUL; return PCIBIOS_SUCCESSFUL;
...@@ -301,21 +306,13 @@ iop310_sec_rd_cfg_word(struct pci_dev *dev, int where, u16 *p) ...@@ -301,21 +306,13 @@ iop310_sec_rd_cfg_word(struct pci_dev *dev, int where, u16 *p)
static int static int
iop310_sec_rd_cfg_dword(struct pci_dev *dev, int where, u32 *p) iop310_sec_rd_cfg_dword(struct pci_dev *dev, int where, u32 *p)
{ {
int ret;
u32 val; u32 val;
DBG("rdl: %d:%02x.%x %02x ", dev->bus->number, PCI_SLOT(dev->devfn), val = iop310_sec_read(dev, where);
PCI_FUNC(dev->devfn), where);
*IOP310_SOCCAR = iop310_cfg_address(dev, where);
val = *IOP310_SOCCDR;
__asm__ __volatile__("nop; nop; nop; nop");
DBG(">= %08lx ", val); if (iop310_sec_pci_status())
ret = iop310_sec_pci_status();
if (ret)
val = 0xffffffff; val = 0xffffffff;
DBG("\n");
*p = val; *p = val;
return PCIBIOS_SUCCESSFUL; return PCIBIOS_SUCCESSFUL;
...@@ -324,63 +321,54 @@ iop310_sec_rd_cfg_dword(struct pci_dev *dev, int where, u32 *p) ...@@ -324,63 +321,54 @@ iop310_sec_rd_cfg_dword(struct pci_dev *dev, int where, u32 *p)
static int static int
iop310_sec_wr_cfg_byte(struct pci_dev *dev, int where, u8 v) iop310_sec_wr_cfg_byte(struct pci_dev *dev, int where, u8 v)
{ {
int ret;
u32 val; u32 val;
DBG("wrb: %d:%02x.%x %02x ", dev->bus->number, PCI_SLOT(dev->devfn), val = iop310_sec_read(dev, where);
PCI_FUNC(dev->devfn), where);
*IOP310_SOCCAR = iop310_cfg_address(dev, where);
val = *IOP310_SOCCDR;
__asm__ __volatile__("nop; nop; nop; nop");
DBG("<= %08lx", v); if (iop310_sec_pci_status() == 0) {
ret = iop310_sec_pci_status();
if (ret == 0) {
where = (where & 3) * 8; where = (where & 3) * 8;
val &= ~(0xff << where); val &= ~(0xff << where);
val |= v << where; val |= v << where;
*IOP310_SOCCDR = val; *IOP310_SOCCDR = val;
} }
DBG("\n");
return PCIBIOS_SUCCESSFUL; return PCIBIOS_SUCCESSFUL;
} }
static int static int
iop310_sec_wr_cfg_word(struct pci_dev *dev, int where, u16 v) iop310_sec_wr_cfg_word(struct pci_dev *dev, int where, u16 v)
{ {
int ret;
u32 val; u32 val;
DBG("wrw: %d:%02x.%x %02x ", dev->bus->number, PCI_SLOT(dev->devfn), val = iop310_sec_read(dev, where);
PCI_FUNC(dev->devfn), where);
*IOP310_SOCCAR = iop310_cfg_address(dev, where);
val = *IOP310_SOCCDR; if (iop310_sec_pci_status() == 0) {
__asm__ __volatile__("nop; nop; nop; nop");
DBG("<= %08lx", v);
ret = iop310_sec_pci_status();
if (ret == 0) {
where = (where & 2) * 8; where = (where & 2) * 8;
val &= ~(0xffff << where); val &= ~(0xffff << where);
val |= v << where; val |= v << where;
*IOP310_SOCCDR = val; *IOP310_SOCCDR = val;
} }
DBG("\n");
return PCIBIOS_SUCCESSFUL; return PCIBIOS_SUCCESSFUL;
} }
static int 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), unsigned long addr;
PCI_FUNC(dev->devfn), where);
*IOP310_SOCCAR = iop310_cfg_address(dev, where); addr = iop310_cfg_address(dev, where);
*IOP310_SOCCDR = v;
__asm__ __volatile__("nop; nop; nop; nop"); __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; return PCIBIOS_SUCCESSFUL;
} }
...@@ -394,13 +382,34 @@ static struct pci_ops iop310_secondary_ops = { ...@@ -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 * When a PCI device does not exist during config cycles, the 80200 gets
* bus error instead of returning 0xffffffff. This handler simply returns. * 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", DBG("PCI abort: address = 0x%08lx fsr = 0x%03x PC = 0x%08lx LR = 0x%08lx\n",
addr, regs->ARM_pc, regs->ARM_lr); 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; return 0;
} }
...@@ -515,6 +524,5 @@ void iop310_init(void) ...@@ -515,6 +524,5 @@ void iop310_init(void)
*/ */
*IOP310_PCR &= 0xfff8; *IOP310_PCR &= 0xfff8;
external_fault = iop310_pci_abort_handler; hook_fault_code(6, iop310_pci_abort, SIGBUS, "imprecise external abort");
} }
...@@ -30,9 +30,7 @@ ...@@ -30,9 +30,7 @@
#include <asm/pgtable.h> #include <asm/pgtable.h>
#include <asm/unaligned.h> #include <asm/unaligned.h>
extern void #include "fault.h"
do_bad_area(struct task_struct *tsk, struct mm_struct *mm, unsigned long addr,
int error_code, struct pt_regs *regs);
/* /*
* 32-bit misaligned trap handler (c) 1998 San Mehat (CCC) -July 1998 * 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, ...@@ -130,31 +128,6 @@ static int proc_alignment_write(struct file *file, const char *buffer,
return count; 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 */ #endif /* CONFIG_PROC_FS */
union offset_union { union offset_union {
...@@ -486,7 +459,8 @@ do_alignment_ldmstm(unsigned long addr, unsigned long instr, struct pt_regs *reg ...@@ -486,7 +459,8 @@ do_alignment_ldmstm(unsigned long addr, unsigned long instr, struct pt_regs *reg
return TYPE_ERROR; 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; union offset_union offset;
unsigned long instr, instrptr; unsigned long instr, instrptr;
...@@ -577,7 +551,7 @@ int do_alignment(unsigned long addr, int error_code, struct pt_regs *regs) ...@@ -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. * 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; return 0;
bad: bad:
...@@ -594,8 +568,8 @@ int do_alignment(unsigned long addr, int error_code, struct pt_regs *regs) ...@@ -594,8 +568,8 @@ int do_alignment(unsigned long addr, int error_code, struct pt_regs *regs)
if (ai_usermode & 1) if (ai_usermode & 1)
printk("Alignment trap: %s (%d) PC=0x%08lx Instr=0x%08lx " printk("Alignment trap: %s (%d) PC=0x%08lx Instr=0x%08lx "
"Address=0x%08lx Code 0x%02x\n", current->comm, "Address=0x%08lx FSR 0x%03x\n", current->comm,
current->pid, instrptr, instr, addr, error_code); current->pid, instrptr, instr, addr, fsr);
if (ai_usermode & 2) if (ai_usermode & 2)
goto fixup; goto fixup;
...@@ -607,3 +581,34 @@ int do_alignment(unsigned long addr, int error_code, struct pt_regs *regs) ...@@ -607,3 +581,34 @@ int do_alignment(unsigned long addr, int error_code, struct pt_regs *regs)
return 0; 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);
...@@ -2,102 +2,62 @@ ...@@ -2,102 +2,62 @@
* linux/arch/arm/mm/fault-armv.c * linux/arch/arm/mm/fault-armv.c
* *
* Copyright (C) 1995 Linus Torvalds * 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 * 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 * it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation. * published by the Free Software Foundation.
*/ */
#include <linux/config.h>
#include <linux/compiler.h>
#include <linux/signal.h>
#include <linux/sched.h> #include <linux/sched.h>
#include <linux/kernel.h> #include <linux/kernel.h>
#include <linux/errno.h>
#include <linux/string.h>
#include <linux/types.h> #include <linux/types.h>
#include <linux/ptrace.h> #include <linux/ptrace.h>
#include <linux/mman.h>
#include <linux/mm.h> #include <linux/mm.h>
#include <linux/interrupt.h>
#include <linux/proc_fs.h>
#include <linux/bitops.h> #include <linux/bitops.h>
#include <linux/init.h> #include <linux/init.h>
#include <asm/system.h>
#include <asm/uaccess.h>
#include <asm/pgalloc.h> #include <asm/pgalloc.h>
#include <asm/pgtable.h> #include <asm/pgtable.h>
extern void die_if_kernel(const char *str, struct pt_regs *regs, int err); #include "fault.h"
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
/* /*
* Some section permission faults need to be handled gracefully. * Some section permission faults need to be handled gracefully.
* They can happen due to a __{get,put}_user during an oops. * They can happen due to a __{get,put}_user during an oops.
*/ */
static int 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; 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; 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". * This abort handler always returns "fault".
*/ */
static int 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; return 1;
} }
static const struct fsr_info { static struct fsr_info {
int (*fn)(unsigned long addr, int error_code, struct pt_regs *regs); int (*fn)(unsigned long addr, unsigned int fsr, struct pt_regs *regs);
int sig; int sig;
const char *name; const char *name;
} fsr_info[] = { } fsr_info[] = {
{ do_bad, SIGSEGV, "vector exception" }, { do_bad, SIGSEGV, "vector exception" },
{ do_alignment, SIGILL, "alignment exception" }, { do_bad, SIGILL, "alignment exception" },
{ do_bad, SIGKILL, "terminal exception" }, { do_bad, SIGKILL, "terminal exception" },
{ do_alignment, SIGILL, "alignment exception" }, { do_bad, SIGILL, "alignment exception" },
{ do_external_fault, SIGBUS, "external abort on linefetch" }, { do_bad, SIGBUS, "external abort on linefetch" },
{ do_translation_fault, SIGSEGV, "section translation fault" }, { 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_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_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, SIGSEGV, "page domain fault" },
{ do_bad, SIGBUS, "external abort on translation" }, { do_bad, SIGBUS, "external abort on translation" },
{ do_sect_fault, SIGSEGV, "section permission fault" }, { do_sect_fault, SIGSEGV, "section permission fault" },
...@@ -105,6 +65,17 @@ static const struct fsr_info { ...@@ -105,6 +65,17 @@ static const struct fsr_info {
{ do_page_fault, SIGSEGV, "page permission fault" } { 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. * Dispatch a data abort to the relevant handler.
*/ */
......
...@@ -23,9 +23,9 @@ ...@@ -23,9 +23,9 @@
#include <linux/init.h> #include <linux/init.h>
#include <asm/system.h> #include <asm/system.h>
#include <asm/uaccess.h>
#include <asm/pgtable.h> #include <asm/pgtable.h>
#include <asm/unaligned.h>
#include "fault.h"
#ifdef CONFIG_CPU_26 #ifdef CONFIG_CPU_26
#define FAULT_CODE_WRITE 0x02 #define FAULT_CODE_WRITE 0x02
...@@ -43,8 +43,6 @@ ...@@ -43,8 +43,6 @@
#define READ_FAULT(code) (!DO_COW(code)) #define READ_FAULT(code) (!DO_COW(code))
#endif #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 * This is useful to dump out the page tables associated with
* 'addr' in mm 'mm'. * 'addr' in mm 'mm'.
...@@ -101,7 +99,7 @@ void show_pte(struct mm_struct *mm, unsigned long addr) ...@@ -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. * Oops. The kernel tried to access some page that wasn't present.
*/ */
static void 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) struct pt_regs *regs)
{ {
unsigned long fixup; unsigned long fixup;
...@@ -127,7 +125,7 @@ __do_kernel_fault(struct mm_struct *mm, unsigned long addr, int error_code, ...@@ -127,7 +125,7 @@ __do_kernel_fault(struct mm_struct *mm, unsigned long addr, int error_code,
"paging request", addr); "paging request", addr);
show_pte(mm, addr); show_pte(mm, addr);
die("Oops", regs, error_code); die("Oops", regs, fsr);
do_exit(SIGKILL); do_exit(SIGKILL);
} }
...@@ -136,20 +134,20 @@ __do_kernel_fault(struct mm_struct *mm, unsigned long addr, int error_code, ...@@ -136,20 +134,20 @@ __do_kernel_fault(struct mm_struct *mm, unsigned long addr, int error_code,
* User mode accesses just cause a SIGSEGV * User mode accesses just cause a SIGSEGV
*/ */
static void static void
__do_user_fault(struct task_struct *tsk, unsigned long addr, int error_code, __do_user_fault(struct task_struct *tsk, unsigned long addr,
int code, struct pt_regs *regs) unsigned int fsr, int code, struct pt_regs *regs)
{ {
struct siginfo si; struct siginfo si;
#ifdef CONFIG_DEBUG_USER #ifdef CONFIG_DEBUG_USER
printk(KERN_DEBUG "%s: unhandled page fault at 0x%08lx, code 0x%03x\n", 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_pte(tsk->mm, addr);
show_regs(regs); show_regs(regs);
#endif #endif
tsk->thread.address = addr; tsk->thread.address = addr;
tsk->thread.error_code = error_code; tsk->thread.error_code = fsr;
tsk->thread.trap_no = 14; tsk->thread.trap_no = 14;
si.si_signo = SIGSEGV; si.si_signo = SIGSEGV;
si.si_errno = 0; si.si_errno = 0;
...@@ -160,20 +158,20 @@ __do_user_fault(struct task_struct *tsk, unsigned long addr, int error_code, ...@@ -160,20 +158,20 @@ __do_user_fault(struct task_struct *tsk, unsigned long addr, int error_code,
void void
do_bad_area(struct task_struct *tsk, struct mm_struct *mm, unsigned long addr, 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 * If we are in kernel mode at this point, we
* have no context to handle this fault with. * have no context to handle this fault with.
*/ */
if (user_mode(regs)) if (user_mode(regs))
__do_user_fault(tsk, addr, error_code, SEGV_MAPERR, regs); __do_user_fault(tsk, addr, fsr, SEGV_MAPERR, regs);
else else
__do_kernel_fault(mm, addr, error_code, regs); __do_kernel_fault(mm, addr, fsr, regs);
} }
static int 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 task_struct *tsk)
{ {
struct vm_area_struct *vma; struct vm_area_struct *vma;
...@@ -191,7 +189,7 @@ __do_page_fault(struct mm_struct *mm, unsigned long addr, int error_code, ...@@ -191,7 +189,7 @@ __do_page_fault(struct mm_struct *mm, unsigned long addr, int error_code,
* memory access, so we can handle it. * memory access, so we can handle it.
*/ */
good_area: good_area:
if (READ_FAULT(error_code)) /* read? */ if (READ_FAULT(fsr)) /* read? */
mask = VM_READ|VM_EXEC; mask = VM_READ|VM_EXEC;
else else
mask = VM_WRITE; mask = VM_WRITE;
...@@ -206,7 +204,7 @@ __do_page_fault(struct mm_struct *mm, unsigned long addr, int error_code, ...@@ -206,7 +204,7 @@ __do_page_fault(struct mm_struct *mm, unsigned long addr, int error_code,
* than endlessly redo the fault. * than endlessly redo the fault.
*/ */
survive: 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 * Handle the "normal" cases first - successful and sigbus
...@@ -239,7 +237,7 @@ __do_page_fault(struct mm_struct *mm, unsigned long addr, int error_code, ...@@ -239,7 +237,7 @@ __do_page_fault(struct mm_struct *mm, unsigned long addr, int error_code,
return fault; 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 task_struct *tsk;
struct mm_struct *mm; struct mm_struct *mm;
...@@ -256,7 +254,7 @@ int do_page_fault(unsigned long addr, int error_code, struct pt_regs *regs) ...@@ -256,7 +254,7 @@ int do_page_fault(unsigned long addr, int error_code, struct pt_regs *regs)
goto no_context; goto no_context;
down_read(&mm->mmap_sem); 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); up_read(&mm->mmap_sem);
/* /*
...@@ -287,7 +285,7 @@ int do_page_fault(unsigned long addr, int error_code, struct pt_regs *regs) ...@@ -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); printk("VM: killing process %s\n", tsk->comm);
do_exit(SIGKILL); do_exit(SIGKILL);
} else } else
__do_user_fault(tsk, addr, error_code, fault == -1 ? __do_user_fault(tsk, addr, fsr, fault == -1 ?
SEGV_ACCERR : SEGV_MAPERR, regs); SEGV_ACCERR : SEGV_MAPERR, regs);
return 0; return 0;
...@@ -302,7 +300,7 @@ int do_page_fault(unsigned long addr, int error_code, struct pt_regs *regs) ...@@ -302,7 +300,7 @@ int do_page_fault(unsigned long addr, int error_code, struct pt_regs *regs)
* or user mode. * or user mode.
*/ */
tsk->thread.address = addr; tsk->thread.address = addr;
tsk->thread.error_code = error_code; tsk->thread.error_code = fsr;
tsk->thread.trap_no = 14; tsk->thread.trap_no = 14;
force_sig(SIGBUS, tsk); force_sig(SIGBUS, tsk);
#ifdef CONFIG_DEBUG_USER #ifdef CONFIG_DEBUG_USER
...@@ -315,7 +313,7 @@ int do_page_fault(unsigned long addr, int error_code, struct pt_regs *regs) ...@@ -315,7 +313,7 @@ int do_page_fault(unsigned long addr, int error_code, struct pt_regs *regs)
return 0; return 0;
no_context: no_context:
__do_kernel_fault(mm, addr, error_code, regs); __do_kernel_fault(mm, addr, fsr, regs);
return 0; return 0;
} }
...@@ -336,7 +334,8 @@ int do_page_fault(unsigned long addr, int error_code, struct pt_regs *regs) ...@@ -336,7 +334,8 @@ int do_page_fault(unsigned long addr, int error_code, struct pt_regs *regs)
* interrupt or a critical region, and should only copy the information * interrupt or a critical region, and should only copy the information
* from the master page table, nothing more. * 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; struct task_struct *tsk;
unsigned int offset; unsigned int offset;
...@@ -344,7 +343,7 @@ int do_translation_fault(unsigned long addr, int error_code, struct pt_regs *reg ...@@ -344,7 +343,7 @@ int do_translation_fault(unsigned long addr, int error_code, struct pt_regs *reg
pmd_t *pmd, *pmd_k; pmd_t *pmd, *pmd_k;
if (addr < TASK_SIZE) if (addr < TASK_SIZE)
return do_page_fault(addr, error_code, regs); return do_page_fault(addr, fsr, regs);
offset = __pgd_offset(addr); offset = __pgd_offset(addr);
...@@ -372,6 +371,6 @@ int do_translation_fault(unsigned long addr, int error_code, struct pt_regs *reg ...@@ -372,6 +371,6 @@ int do_translation_fault(unsigned long addr, int error_code, struct pt_regs *reg
bad_area: bad_area:
tsk = current; 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; return 0;
} }
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);
...@@ -24,6 +24,17 @@ extern int have_isa_bridge; ...@@ -24,6 +24,17 @@ extern int have_isa_bridge;
#define have_isa_bridge (0) #define have_isa_bridge (0)
#endif #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> #include <asm/proc-fns.h>
#define xchg(ptr,x) \ #define xchg(ptr,x) \
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment