X-Git-Url: http://git.onelab.eu/?a=blobdiff_plain;f=arch%2Fppc%2Fmm%2Ffault.c;h=8ad72e006b6e373fe2db6b7dbf68cb13d4d36f81;hb=refs%2Fheads%2Fvserver;hp=e31abd874386e37290f68226bde56b8858a4a71e;hpb=5273a3df6485dc2ad6aa7ddd441b9a21970f003b;p=linux-2.6.git diff --git a/arch/ppc/mm/fault.c b/arch/ppc/mm/fault.c index e31abd874..8ad72e006 100644 --- a/arch/ppc/mm/fault.c +++ b/arch/ppc/mm/fault.c @@ -1,6 +1,4 @@ /* - * arch/ppc/mm/fault.c - * * PowerPC version * Copyright (C) 1995-1996 Gary Thomas (gdt@linuxppc.org) * @@ -15,7 +13,6 @@ * 2 of the License, or (at your option) any later version. */ -#include #include #include #include @@ -51,11 +48,6 @@ unsigned long pte_misses; /* updated by do_page_fault() */ 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. @@ -64,7 +56,7 @@ static int store_updates_sp(struct pt_regs *regs) { 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) @@ -97,14 +89,14 @@ static int store_updates_sp(struct pt_regs *regs) * 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; @@ -119,26 +111,25 @@ void do_page_fault(struct pt_regs *regs, unsigned long address, 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) @@ -206,10 +197,11 @@ good_area: 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 @@ -220,32 +212,34 @@ good_area: 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; } @@ -277,7 +271,7 @@ good_area: * -- Cort */ pte_misses++; - return; + return 0; bad_area: up_read(&mm->mmap_sem); @@ -285,16 +279,11 @@ bad_area: /* 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 @@ -302,37 +291,37 @@ bad_area: */ 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? */ @@ -359,7 +348,6 @@ pte_t *va_to_pte(unsigned long address) pgd_t *dir; pmd_t *pmd; pte_t *pte; - struct mm_struct *mm; if (address < TASK_SIZE) return NULL;