/*
- * arch/ppc/mm/fault.c
- *
* PowerPC version
* Copyright (C) 1995-1996 Gary Thomas (gdt@linuxppc.org)
*
* 2 of the License, or (at your option) any later version.
*/
-#include <linux/config.h>
#include <linux/signal.h>
#include <linux/sched.h>
#include <linux/kernel.h>
unsigned long pte_errors; /* updated by do_page_fault() */
unsigned int probingmem;
-extern void die_if_kernel(char *, struct pt_regs *, long);
-void bad_page_fault(struct pt_regs *, unsigned long, int sig);
-void do_page_fault(struct pt_regs *, unsigned long, unsigned long);
-extern int get_pteptr(struct mm_struct *mm, unsigned long addr, pte_t **ptep);
-
/*
* Check whether the instruction at regs->nip is a store using
* an update addressing form which will update r1.
{
unsigned int inst;
- if (get_user(inst, (unsigned int *)regs->nip))
+ if (get_user(inst, (unsigned int __user *)regs->nip))
return 0;
/* check for 1 in the rA field */
if (((inst >> 16) & 0x1f) != 1)
* the error_code parameter is ESR for a data fault, 0 for an instruction
* fault.
*/
-void do_page_fault(struct pt_regs *regs, unsigned long address,
- unsigned long error_code)
+int do_page_fault(struct pt_regs *regs, unsigned long address,
+ unsigned long error_code)
{
struct vm_area_struct * vma;
struct mm_struct *mm = current->mm;
siginfo_t info;
int code = SEGV_MAPERR;
-#if defined(CONFIG_4xx)
+#if defined(CONFIG_4xx) || defined (CONFIG_BOOKE)
int is_write = error_code & ESR_DST;
#else
int is_write = 0;
error_code &= 0x48200000;
else
is_write = error_code & 0x02000000;
-#endif /* CONFIG_4xx */
+#endif /* CONFIG_4xx || CONFIG_BOOKE */
#if defined(CONFIG_XMON) || defined(CONFIG_KGDB)
if (debugger_fault_handler && TRAP(regs) == 0x300) {
debugger_fault_handler(regs);
- return;
+ return 0;
}
-#if !defined(CONFIG_4xx)
+#if !(defined(CONFIG_4xx) || defined(CONFIG_BOOKE))
if (error_code & 0x00400000) {
/* DABR match */
if (debugger_dabr_match(regs))
- return;
+ return 0;
}
-#endif /* !CONFIG_4xx */
+#endif /* !(CONFIG_4xx || CONFIG_BOOKE)*/
#endif /* CONFIG_XMON || CONFIG_KGDB */
- if (in_atomic() || mm == NULL) {
- bad_page_fault(regs, address, SIGSEGV);
- return;
- }
+ if (in_atomic() || mm == NULL)
+ return SIGSEGV;
+
down_read(&mm->mmap_sem);
vma = find_vma(mm, address);
if (!vma)
if (is_write) {
if (!(vma->vm_flags & VM_WRITE))
goto bad_area;
-#if defined(CONFIG_4xx)
- /* an exec - 4xx allows for per-page execute permission */
+#if defined(CONFIG_4xx) || defined(CONFIG_BOOKE)
+ /* an exec - 4xx/Book-E allows for per-page execute permission */
} else if (TRAP(regs) == 0x400) {
pte_t *ptep;
+ pmd_t *pmdp;
#if 0
/* It would be nice to actually enforce the VM execute
goto bad_area;
#endif
- /* Since 4xx supports per-page execute permission,
+ /* Since 4xx/Book-E supports per-page execute permission,
* we lazily flush dcache to icache. */
ptep = NULL;
- if (get_pteptr(mm, address, &ptep) && pte_present(*ptep)) {
- struct page *page = pte_page(*ptep);
-
- if (! test_bit(PG_arch_1, &page->flags)) {
- unsigned long phys = page_to_pfn(page) << PAGE_SHIFT;
- __flush_dcache_icache_phys(phys);
- set_bit(PG_arch_1, &page->flags);
+ if (get_pteptr(mm, address, &ptep, &pmdp)) {
+ spinlock_t *ptl = pte_lockptr(mm, pmdp);
+ spin_lock(ptl);
+ if (pte_present(*ptep)) {
+ struct page *page = pte_page(*ptep);
+
+ if (!test_bit(PG_arch_1, &page->flags)) {
+ flush_dcache_icache_page(page);
+ set_bit(PG_arch_1, &page->flags);
+ }
+ pte_update(ptep, 0, _PAGE_HWEXEC);
+ _tlbie(address);
+ pte_unmap_unlock(ptep, ptl);
+ up_read(&mm->mmap_sem);
+ return 0;
}
- pte_update(ptep, 0, _PAGE_HWEXEC);
- _tlbie(address);
- pte_unmap(ptep);
- up_read(&mm->mmap_sem);
- return;
+ pte_unmap_unlock(ptep, ptl);
}
- if (ptep != NULL)
- pte_unmap(ptep);
#endif
/* a read */
} else {
/* protection fault */
if (error_code & 0x08000000)
goto bad_area;
- if (!(vma->vm_flags & (VM_READ | VM_EXEC)))
+ if (!(vma->vm_flags & (VM_READ | VM_EXEC | VM_WRITE)))
goto bad_area;
}
* -- Cort
*/
pte_misses++;
- return;
+ return 0;
bad_area:
up_read(&mm->mmap_sem);
/* User mode accesses cause a SIGSEGV */
if (user_mode(regs)) {
- info.si_signo = SIGSEGV;
- info.si_errno = 0;
- info.si_code = code;
- info.si_addr = (void *) address;
- force_sig_info(SIGSEGV, &info, current);
- return;
+ _exception(SIGSEGV, regs, code, address);
+ return 0;
}
- bad_page_fault(regs, address, SIGSEGV);
- return;
+ return SIGSEGV;
/*
* We ran out of memory, or some other thing happened to us that made
*/
out_of_memory:
up_read(&mm->mmap_sem);
- if (current->pid == 1) {
+ if (is_init(current)) {
yield();
down_read(&mm->mmap_sem);
goto survive;
}
- printk("VM: killing process %s\n", current->comm);
+ printk("VM: killing process %s(%d:#%u)\n",
+ current->comm, current->pid, current->xid);
if (user_mode(regs))
do_exit(SIGKILL);
- bad_page_fault(regs, address, SIGKILL);
- return;
+ return SIGKILL;
do_sigbus:
up_read(&mm->mmap_sem);
info.si_signo = SIGBUS;
info.si_errno = 0;
info.si_code = BUS_ADRERR;
- info.si_addr = (void *)address;
+ info.si_addr = (void __user *)address;
force_sig_info (SIGBUS, &info, current);
if (!user_mode(regs))
- bad_page_fault(regs, address, SIGBUS);
+ return SIGBUS;
+ return 0;
}
/*
* bad_page_fault is called when we have a bad access from the kernel.
- * It is called from do_page_fault above and from some of the procedures
- * in traps.c.
+ * It is called from the DSI and ISI handlers in head.S and from some
+ * of the procedures in traps.c.
*/
void
bad_page_fault(struct pt_regs *regs, unsigned long address, int sig)
{
- extern void die(const char *,struct pt_regs *,long);
const struct exception_table_entry *entry;
/* Are we prepared to handle this fault? */
pgd_t *dir;
pmd_t *pmd;
pte_t *pte;
- struct mm_struct *mm;
if (address < TASK_SIZE)
return NULL;