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