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#include <linux/uaccess.h> 38 39/* 40 * This routine handles page faults. It determines the address, 41 * and the problem, and then passes it off to one of the appropriate 42 * routines. 43 */ 44asmlinkage void do_page_fault(struct pt_regs *regs, unsigned long write, 45 unsigned long address) 46{ 47 struct vm_area_struct *vma = NULL; 48 struct task_struct *tsk = current; 49 struct mm_struct *mm = tsk->mm; 50 const int field = sizeof(unsigned long) * 2; 51 unsigned long flags = 0; 52 siginfo_t info; 53 int fault; 54 55 info.si_code = SEGV_MAPERR; 56 57 /* 58 * We fault-in kernel-space virtual memory on-demand. The 59 * 'reference' page table is init_mm.pgd. 60 * 61 * NOTE! We MUST NOT take any locks for this case. We may 62 * be in an interrupt or a critical region, and should 63 * only copy the information from the master page table, 64 * nothing more. 65 */ 66 if (unlikely(address >= VMALLOC_START && address <= VMALLOC_END)) 67 goto vmalloc_fault; 68#ifdef MODULE_START 69 if (unlikely(address >= MODULE_START && address < MODULE_END)) 70 goto vmalloc_fault; 71#endif 72 73 /* 74 * If we're in an interrupt or have no user 75 * context, we must not take the fault.. 76 */ 77 if (pagefault_disabled() || !mm) 78 goto bad_area_nosemaphore; 79 80 if (user_mode(regs)) 81 flags |= FAULT_FLAG_USER; 82 83 down_read(&mm->mmap_sem); 84 vma = find_vma(mm, address); 85 if (!vma) 86 goto bad_area; 87 if (vma->vm_start <= address) 88 goto good_area; 89 if (!(vma->vm_flags & VM_GROWSDOWN)) 90 goto bad_area; 91 if (expand_stack(vma, address)) 92 goto bad_area; 93 /* 94 * Ok, we have a good vm_area for this memory access, so 95 * we can handle it.. 96 */ 97good_area: 98 info.si_code = SEGV_ACCERR; 99 100 if (write) { 101 if (!(vma->vm_flags & VM_WRITE)) 102 goto bad_area; 103 flags |= FAULT_FLAG_WRITE; 104 } else { 105 if (!(vma->vm_flags & (VM_READ | VM_WRITE | VM_EXEC))) 106 goto bad_area; 107 } 108 109 /* 110 * If for any reason at all we couldn't handle the fault, 111 * make sure we exit gracefully rather than endlessly redo 112 * the fault. 113 */ 114 fault = handle_mm_fault(mm, vma, address, flags); 115 if (unlikely(fault & VM_FAULT_ERROR)) { 116 if (fault & VM_FAULT_OOM) 117 goto out_of_memory; 118 else if (fault & VM_FAULT_SIGSEGV) 119 goto bad_area; 120 else if (fault & VM_FAULT_SIGBUS) 121 goto do_sigbus; 122 BUG(); 123 } 124 if (fault & VM_FAULT_MAJOR) 125 tsk->maj_flt++; 126 else 127 tsk->min_flt++; 128 129 up_read(&mm->mmap_sem); 130 return; 131 132 /* 133 * Something tried to access memory that isn't in our memory map.. 134 * Fix it, but check if it's kernel or user first.. 135 */ 136bad_area: 137 up_read(&mm->mmap_sem); 138 139bad_area_nosemaphore: 140 /* User mode accesses just cause a SIGSEGV */ 141 if (user_mode(regs)) { 142 tsk->thread.cp0_badvaddr = address; 143 tsk->thread.error_code = write; 144 info.si_signo = SIGSEGV; 145 info.si_errno = 0; 146 /* info.si_code has been set above */ 147 info.si_addr = (void __user *) address; 148 force_sig_info(SIGSEGV, &info, tsk); 149 return; 150 } 151 152no_context: 153 /* Are we prepared to handle this kernel fault? */ 154 if (fixup_exception(regs)) { 155 current->thread.cp0_baduaddr = address; 156 return; 157 } 158 159 /* 160 * Oops. The kernel tried to access some bad page. We'll have to 161 * terminate things with extreme prejudice. 162 */ 163 bust_spinlocks(1); 164 165 printk(KERN_ALERT "CPU %d Unable to handle kernel paging request at " 166 "virtual address %0*lx, epc == %0*lx, ra == %0*lx\n", 167 0, field, address, field, regs->cp0_epc, 168 field, regs->regs[3]); 169 die("Oops", regs); 170 171 /* 172 * We ran out of memory, or some other thing happened to us that made 173 * us unable to handle the page fault gracefully. 174 */ 175out_of_memory: 176 up_read(&mm->mmap_sem); 177 if (!user_mode(regs)) 178 goto no_context; 179 pagefault_out_of_memory(); 180 return; 181 182do_sigbus: 183 up_read(&mm->mmap_sem); 184 /* Kernel mode? Handle exceptions or die */ 185 if (!user_mode(regs)) 186 goto no_context; 187 else 188 /* 189 * Send a sigbus, regardless of whether we were in kernel 190 * or user mode. 191 */ 192 tsk->thread.cp0_badvaddr = address; 193 info.si_signo = SIGBUS; 194 info.si_errno = 0; 195 info.si_code = BUS_ADRERR; 196 info.si_addr = (void __user *) address; 197 force_sig_info(SIGBUS, &info, tsk); 198 return; 199vmalloc_fault: 200 { 201 /* 202 * Synchronize this task's top level page-table 203 * with the 'reference' page table. 204 * 205 * Do _not_ use "tsk" here. We might be inside 206 * an interrupt in the middle of a task switch.. 207 */ 208 int offset = __pgd_offset(address); 209 pgd_t *pgd, *pgd_k; 210 pud_t *pud, *pud_k; 211 pmd_t *pmd, *pmd_k; 212 pte_t *pte_k; 213 214 pgd = (pgd_t *) pgd_current + offset; 215 pgd_k = init_mm.pgd + offset; 216 217 if (!pgd_present(*pgd_k)) 218 goto no_context; 219 set_pgd(pgd, *pgd_k); 220 221 pud = pud_offset(pgd, address); 222 pud_k = pud_offset(pgd_k, address); 223 if (!pud_present(*pud_k)) 224 goto no_context; 225 226 pmd = pmd_offset(pud, address); 227 pmd_k = pmd_offset(pud_k, address); 228 if (!pmd_present(*pmd_k)) 229 goto no_context; 230 set_pmd(pmd, *pmd_k); 231 232 pte_k = pte_offset_kernel(pmd_k, address); 233 if (!pte_present(*pte_k)) 234 goto no_context; 235 return; 236 } 237} 238