1 // TODO VM_EXEC flag work-around, cache aliasing 2 /* 3 * arch/xtensa/mm/fault.c 4 * 5 * This file is subject to the terms and conditions of the GNU General Public 6 * License. See the file "COPYING" in the main directory of this archive 7 * for more details. 8 * 9 * Copyright (C) 2001 - 2005 Tensilica Inc. 10 * 11 * Chris Zankel <chris@zankel.net> 12 * Joe Taylor <joe@tensilica.com, joetylr@yahoo.com> 13 */ 14 15 #include <linux/mm.h> 16 #include <linux/module.h> 17 #include <asm/mmu_context.h> 18 #include <asm/cacheflush.h> 19 #include <asm/hardirq.h> 20 #include <asm/uaccess.h> 21 #include <asm/system.h> 22 #include <asm/pgalloc.h> 23 24 unsigned long asid_cache = ASID_USER_FIRST; 25 void bad_page_fault(struct pt_regs*, unsigned long, int); 26 27 /* 28 * This routine handles page faults. It determines the address, 29 * and the problem, and then passes it off to one of the appropriate 30 * routines. 31 * 32 * Note: does not handle Miss and MultiHit. 33 */ 34 35 void do_page_fault(struct pt_regs *regs) 36 { 37 struct vm_area_struct * vma; 38 struct mm_struct *mm = current->mm; 39 unsigned int exccause = regs->exccause; 40 unsigned int address = regs->excvaddr; 41 siginfo_t info; 42 43 int is_write, is_exec; 44 int fault; 45 46 info.si_code = SEGV_MAPERR; 47 48 /* We fault-in kernel-space virtual memory on-demand. The 49 * 'reference' page table is init_mm.pgd. 50 */ 51 if (address >= TASK_SIZE && !user_mode(regs)) 52 goto vmalloc_fault; 53 54 /* If we're in an interrupt or have no user 55 * context, we must not take the fault.. 56 */ 57 if (in_atomic() || !mm) { 58 bad_page_fault(regs, address, SIGSEGV); 59 return; 60 } 61 62 is_write = (exccause == EXCCAUSE_STORE_CACHE_ATTRIBUTE) ? 1 : 0; 63 is_exec = (exccause == EXCCAUSE_ITLB_PRIVILEGE || 64 exccause == EXCCAUSE_ITLB_MISS || 65 exccause == EXCCAUSE_FETCH_CACHE_ATTRIBUTE) ? 1 : 0; 66 67 #if 0 68 printk("[%s:%d:%08x:%d:%08x:%s%s]\n", current->comm, current->pid, 69 address, exccause, regs->pc, is_write? "w":"", is_exec? "x":""); 70 #endif 71 72 down_read(&mm->mmap_sem); 73 vma = find_vma(mm, address); 74 75 if (!vma) 76 goto bad_area; 77 if (vma->vm_start <= address) 78 goto good_area; 79 if (!(vma->vm_flags & VM_GROWSDOWN)) 80 goto bad_area; 81 if (expand_stack(vma, address)) 82 goto bad_area; 83 84 /* Ok, we have a good vm_area for this memory access, so 85 * we can handle it.. 86 */ 87 88 good_area: 89 info.si_code = SEGV_ACCERR; 90 91 if (is_write) { 92 if (!(vma->vm_flags & VM_WRITE)) 93 goto bad_area; 94 } else if (is_exec) { 95 if (!(vma->vm_flags & VM_EXEC)) 96 goto bad_area; 97 } else /* Allow read even from write-only pages. */ 98 if (!(vma->vm_flags & (VM_READ | VM_WRITE))) 99 goto bad_area; 100 101 /* If for any reason at all we couldn't handle the fault, 102 * make sure we exit gracefully rather than endlessly redo 103 * the fault. 104 */ 105 survive: 106 fault = handle_mm_fault(mm, vma, address, is_write); 107 if (unlikely(fault & VM_FAULT_ERROR)) { 108 if (fault & VM_FAULT_OOM) 109 goto out_of_memory; 110 else if (fault & VM_FAULT_SIGBUS) 111 goto do_sigbus; 112 BUG(); 113 } 114 if (fault & VM_FAULT_MAJOR) 115 current->maj_flt++; 116 else 117 current->min_flt++; 118 119 up_read(&mm->mmap_sem); 120 return; 121 122 /* Something tried to access memory that isn't in our memory map.. 123 * Fix it, but check if it's kernel or user first.. 124 */ 125 bad_area: 126 up_read(&mm->mmap_sem); 127 if (user_mode(regs)) { 128 current->thread.bad_vaddr = address; 129 current->thread.error_code = is_write; 130 info.si_signo = SIGSEGV; 131 info.si_errno = 0; 132 /* info.si_code has been set above */ 133 info.si_addr = (void *) address; 134 force_sig_info(SIGSEGV, &info, current); 135 return; 136 } 137 bad_page_fault(regs, address, SIGSEGV); 138 return; 139 140 141 /* We ran out of memory, or some other thing happened to us that made 142 * us unable to handle the page fault gracefully. 143 */ 144 out_of_memory: 145 up_read(&mm->mmap_sem); 146 if (is_init(current)) { 147 yield(); 148 down_read(&mm->mmap_sem); 149 goto survive; 150 } 151 printk("VM: killing process %s\n", current->comm); 152 if (user_mode(regs)) 153 do_exit(SIGKILL); 154 bad_page_fault(regs, address, SIGKILL); 155 return; 156 157 do_sigbus: 158 up_read(&mm->mmap_sem); 159 160 /* Send a sigbus, regardless of whether we were in kernel 161 * or user mode. 162 */ 163 current->thread.bad_vaddr = address; 164 info.si_code = SIGBUS; 165 info.si_errno = 0; 166 info.si_code = BUS_ADRERR; 167 info.si_addr = (void *) address; 168 force_sig_info(SIGBUS, &info, current); 169 170 /* Kernel mode? Handle exceptions or die */ 171 if (!user_mode(regs)) 172 bad_page_fault(regs, address, SIGBUS); 173 174 vmalloc_fault: 175 { 176 /* Synchronize this task's top level page-table 177 * with the 'reference' page table. 178 */ 179 struct mm_struct *act_mm = current->active_mm; 180 int index = pgd_index(address); 181 pgd_t *pgd, *pgd_k; 182 pmd_t *pmd, *pmd_k; 183 pte_t *pte_k; 184 185 if (act_mm == NULL) 186 goto bad_page_fault; 187 188 pgd = act_mm->pgd + index; 189 pgd_k = init_mm.pgd + index; 190 191 if (!pgd_present(*pgd_k)) 192 goto bad_page_fault; 193 194 pgd_val(*pgd) = pgd_val(*pgd_k); 195 196 pmd = pmd_offset(pgd, address); 197 pmd_k = pmd_offset(pgd_k, address); 198 if (!pmd_present(*pmd) || !pmd_present(*pmd_k)) 199 goto bad_page_fault; 200 201 pmd_val(*pmd) = pmd_val(*pmd_k); 202 pte_k = pte_offset_kernel(pmd_k, address); 203 204 if (!pte_present(*pte_k)) 205 goto bad_page_fault; 206 return; 207 } 208 bad_page_fault: 209 bad_page_fault(regs, address, SIGKILL); 210 return; 211 } 212 213 214 void 215 bad_page_fault(struct pt_regs *regs, unsigned long address, int sig) 216 { 217 extern void die(const char*, struct pt_regs*, long); 218 const struct exception_table_entry *entry; 219 220 /* Are we prepared to handle this kernel fault? */ 221 if ((entry = search_exception_tables(regs->pc)) != NULL) { 222 #if 1 223 printk(KERN_DEBUG "%s: Exception at pc=%#010lx (%lx)\n", 224 current->comm, regs->pc, entry->fixup); 225 #endif 226 current->thread.bad_uaddr = address; 227 regs->pc = entry->fixup; 228 return; 229 } 230 231 /* Oops. The kernel tried to access some bad page. We'll have to 232 * terminate things with extreme prejudice. 233 */ 234 printk(KERN_ALERT "Unable to handle kernel paging request at virtual " 235 "address %08lx\n pc = %08lx, ra = %08lx\n", 236 address, regs->pc, regs->areg[0]); 237 die("Oops", regs, sig); 238 do_exit(sig); 239 } 240 241