fault.c revision 609838cfed972d49a65aac7923a9ff5cbe482e30
1/* 2 * arch/score/mm/fault.c 3 * 4 * Score Processor version. 5 * 6 * Copyright (C) 2009 Sunplus Core Technology Co., Ltd. 7 * Lennox Wu <lennox.wu@sunplusct.com> 8 * Chen Liqin <liqin.chen@sunplusct.com> 9 * 10 * This program is free software; you can redistribute it and/or modify 11 * it under the terms of the GNU General Public License as published by 12 * the Free Software Foundation; either version 2 of the License, or 13 * (at your option) any later version. 14 * 15 * This program is distributed in the hope that it will be useful, 16 * but WITHOUT ANY WARRANTY; without even the implied warranty of 17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 18 * GNU General Public License for more details. 19 * 20 * You should have received a copy of the GNU General Public License 21 * along with this program; if not, see the file COPYING, or write 22 * to the Free Software Foundation, Inc., 23 * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA 24 */ 25 26#include <linux/errno.h> 27#include <linux/interrupt.h> 28#include <linux/kernel.h> 29#include <linux/mm.h> 30#include <linux/mman.h> 31#include <linux/module.h> 32#include <linux/signal.h> 33#include <linux/sched.h> 34#include <linux/string.h> 35#include <linux/types.h> 36#include <linux/ptrace.h> 37 38/* 39 * This routine handles page faults. It determines the address, 40 * and the problem, and then passes it off to one of the appropriate 41 * routines. 42 */ 43asmlinkage void do_page_fault(struct pt_regs *regs, unsigned long write, 44 unsigned long address) 45{ 46 struct vm_area_struct *vma = NULL; 47 struct task_struct *tsk = current; 48 struct mm_struct *mm = tsk->mm; 49 const int field = sizeof(unsigned long) * 2; 50 siginfo_t info; 51 int fault; 52 53 info.si_code = SEGV_MAPERR; 54 55 /* 56 * We fault-in kernel-space virtual memory on-demand. The 57 * 'reference' page table is init_mm.pgd. 58 * 59 * NOTE! We MUST NOT take any locks for this case. We may 60 * be in an interrupt or a critical region, and should 61 * only copy the information from the master page table, 62 * nothing more. 63 */ 64 if (unlikely(address >= VMALLOC_START && address <= VMALLOC_END)) 65 goto vmalloc_fault; 66#ifdef MODULE_START 67 if (unlikely(address >= MODULE_START && address < MODULE_END)) 68 goto vmalloc_fault; 69#endif 70 71 /* 72 * If we're in an interrupt or have no user 73 * context, we must not take the fault.. 74 */ 75 if (in_atomic() || !mm) 76 goto bad_area_nosemaphore; 77 78 down_read(&mm->mmap_sem); 79 vma = find_vma(mm, address); 80 if (!vma) 81 goto bad_area; 82 if (vma->vm_start <= address) 83 goto good_area; 84 if (!(vma->vm_flags & VM_GROWSDOWN)) 85 goto bad_area; 86 if (expand_stack(vma, address)) 87 goto bad_area; 88 /* 89 * Ok, we have a good vm_area for this memory access, so 90 * we can handle it.. 91 */ 92good_area: 93 info.si_code = SEGV_ACCERR; 94 95 if (write) { 96 if (!(vma->vm_flags & VM_WRITE)) 97 goto bad_area; 98 } else { 99 if (!(vma->vm_flags & (VM_READ | VM_WRITE | VM_EXEC))) 100 goto bad_area; 101 } 102 103survive: 104 /* 105 * If for any reason at all we couldn't handle the fault, 106 * make sure we exit gracefully rather than endlessly redo 107 * the fault. 108 */ 109 fault = handle_mm_fault(mm, vma, address, write); 110 if (unlikely(fault & VM_FAULT_ERROR)) { 111 if (fault & VM_FAULT_OOM) 112 goto out_of_memory; 113 else if (fault & VM_FAULT_SIGBUS) 114 goto do_sigbus; 115 BUG(); 116 } 117 if (fault & VM_FAULT_MAJOR) 118 tsk->maj_flt++; 119 else 120 tsk->min_flt++; 121 122 up_read(&mm->mmap_sem); 123 return; 124 125 /* 126 * Something tried to access memory that isn't in our memory map.. 127 * Fix it, but check if it's kernel or user first.. 128 */ 129bad_area: 130 up_read(&mm->mmap_sem); 131 132bad_area_nosemaphore: 133 /* User mode accesses just cause a SIGSEGV */ 134 if (user_mode(regs)) { 135 tsk->thread.cp0_badvaddr = address; 136 tsk->thread.error_code = write; 137 info.si_signo = SIGSEGV; 138 info.si_errno = 0; 139 /* info.si_code has been set above */ 140 info.si_addr = (void __user *) address; 141 force_sig_info(SIGSEGV, &info, tsk); 142 return; 143 } 144 145no_context: 146 /* Are we prepared to handle this kernel fault? */ 147 if (fixup_exception(regs)) { 148 current->thread.cp0_baduaddr = address; 149 return; 150 } 151 152 /* 153 * Oops. The kernel tried to access some bad page. We'll have to 154 * terminate things with extreme prejudice. 155 */ 156 bust_spinlocks(1); 157 158 printk(KERN_ALERT "CPU %d Unable to handle kernel paging request at " 159 "virtual address %0*lx, epc == %0*lx, ra == %0*lx\n", 160 0, field, address, field, regs->cp0_epc, 161 field, regs->regs[3]); 162 die("Oops", regs); 163 164 /* 165 * We ran out of memory, or some other thing happened to us that made 166 * us unable to handle the page fault gracefully. 167 */ 168out_of_memory: 169 up_read(&mm->mmap_sem); 170 if (is_global_init(tsk)) { 171 yield(); 172 down_read(&mm->mmap_sem); 173 goto survive; 174 } 175 if (!user_mode(regs)) 176 goto no_context; 177 pagefault_out_of_memory(); 178 return; 179 180do_sigbus: 181 up_read(&mm->mmap_sem); 182 /* Kernel mode? Handle exceptions or die */ 183 if (!user_mode(regs)) 184 goto no_context; 185 else 186 /* 187 * Send a sigbus, regardless of whether we were in kernel 188 * or user mode. 189 */ 190 tsk->thread.cp0_badvaddr = address; 191 info.si_signo = SIGBUS; 192 info.si_errno = 0; 193 info.si_code = BUS_ADRERR; 194 info.si_addr = (void __user *) address; 195 force_sig_info(SIGBUS, &info, tsk); 196 return; 197vmalloc_fault: 198 { 199 /* 200 * Synchronize this task's top level page-table 201 * with the 'reference' page table. 202 * 203 * Do _not_ use "tsk" here. We might be inside 204 * an interrupt in the middle of a task switch.. 205 */ 206 int offset = __pgd_offset(address); 207 pgd_t *pgd, *pgd_k; 208 pud_t *pud, *pud_k; 209 pmd_t *pmd, *pmd_k; 210 pte_t *pte_k; 211 212 pgd = (pgd_t *) pgd_current + offset; 213 pgd_k = init_mm.pgd + offset; 214 215 if (!pgd_present(*pgd_k)) 216 goto no_context; 217 set_pgd(pgd, *pgd_k); 218 219 pud = pud_offset(pgd, address); 220 pud_k = pud_offset(pgd_k, address); 221 if (!pud_present(*pud_k)) 222 goto no_context; 223 224 pmd = pmd_offset(pud, address); 225 pmd_k = pmd_offset(pud_k, address); 226 if (!pmd_present(*pmd_k)) 227 goto no_context; 228 set_pmd(pmd, *pmd_k); 229 230 pte_k = pte_offset_kernel(pmd_k, address); 231 if (!pte_present(*pte_k)) 232 goto no_context; 233 return; 234 } 235} 236