blob: 2c2f710ed1dc905a8c9e0509d36cd76ed073e0ce [file] [log] [blame]
Chris Zankel3f65ce42005-06-23 22:01:24 -07001// 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>
Alexey Dobriyan5a891ed2009-03-10 12:55:49 -070017#include <linux/hardirq.h>
Chris Zankel3f65ce42005-06-23 22:01:24 -070018#include <asm/mmu_context.h>
19#include <asm/cacheflush.h>
20#include <asm/hardirq.h>
21#include <asm/uaccess.h>
Chris Zankel3f65ce42005-06-23 22:01:24 -070022#include <asm/pgalloc.h>
23
Chris Zankel173d6682006-12-10 02:18:48 -080024unsigned long asid_cache = ASID_USER_FIRST;
Chris Zankel3f65ce42005-06-23 22:01:24 -070025void bad_page_fault(struct pt_regs*, unsigned long, int);
26
Chris Zankel66569202007-08-22 10:14:51 -070027#undef DEBUG_PAGE_FAULT
28
Chris Zankel3f65ce42005-06-23 22:01:24 -070029/*
30 * This routine handles page faults. It determines the address,
31 * and the problem, and then passes it off to one of the appropriate
32 * routines.
33 *
34 * Note: does not handle Miss and MultiHit.
35 */
36
37void do_page_fault(struct pt_regs *regs)
38{
39 struct vm_area_struct * vma;
40 struct mm_struct *mm = current->mm;
41 unsigned int exccause = regs->exccause;
42 unsigned int address = regs->excvaddr;
43 siginfo_t info;
44
45 int is_write, is_exec;
Nick Piggin83c54072007-07-19 01:47:05 -070046 int fault;
Kautuk Consulf1077012012-07-30 14:39:21 -070047 unsigned int flags = FAULT_FLAG_ALLOW_RETRY | FAULT_FLAG_KILLABLE;
Chris Zankel3f65ce42005-06-23 22:01:24 -070048
49 info.si_code = SEGV_MAPERR;
50
51 /* We fault-in kernel-space virtual memory on-demand. The
52 * 'reference' page table is init_mm.pgd.
53 */
54 if (address >= TASK_SIZE && !user_mode(regs))
55 goto vmalloc_fault;
56
57 /* If we're in an interrupt or have no user
58 * context, we must not take the fault..
59 */
60 if (in_atomic() || !mm) {
61 bad_page_fault(regs, address, SIGSEGV);
62 return;
63 }
64
Chris Zankel173d6682006-12-10 02:18:48 -080065 is_write = (exccause == EXCCAUSE_STORE_CACHE_ATTRIBUTE) ? 1 : 0;
66 is_exec = (exccause == EXCCAUSE_ITLB_PRIVILEGE ||
67 exccause == EXCCAUSE_ITLB_MISS ||
68 exccause == EXCCAUSE_FETCH_CACHE_ATTRIBUTE) ? 1 : 0;
Chris Zankel3f65ce42005-06-23 22:01:24 -070069
Chris Zankel66569202007-08-22 10:14:51 -070070#ifdef DEBUG_PAGE_FAULT
Chris Zankel3f65ce42005-06-23 22:01:24 -070071 printk("[%s:%d:%08x:%d:%08x:%s%s]\n", current->comm, current->pid,
72 address, exccause, regs->pc, is_write? "w":"", is_exec? "x":"");
73#endif
74
Kautuk Consulf1077012012-07-30 14:39:21 -070075retry:
Chris Zankel3f65ce42005-06-23 22:01:24 -070076 down_read(&mm->mmap_sem);
77 vma = find_vma(mm, address);
78
79 if (!vma)
80 goto bad_area;
81 if (vma->vm_start <= address)
82 goto good_area;
83 if (!(vma->vm_flags & VM_GROWSDOWN))
84 goto bad_area;
85 if (expand_stack(vma, address))
86 goto bad_area;
87
88 /* Ok, we have a good vm_area for this memory access, so
89 * we can handle it..
90 */
91
92good_area:
93 info.si_code = SEGV_ACCERR;
94
95 if (is_write) {
96 if (!(vma->vm_flags & VM_WRITE))
97 goto bad_area;
Kautuk Consulf1077012012-07-30 14:39:21 -070098 flags |= FAULT_FLAG_WRITE;
Chris Zankel3f65ce42005-06-23 22:01:24 -070099 } else if (is_exec) {
100 if (!(vma->vm_flags & VM_EXEC))
101 goto bad_area;
102 } else /* Allow read even from write-only pages. */
103 if (!(vma->vm_flags & (VM_READ | VM_WRITE)))
104 goto bad_area;
105
106 /* If for any reason at all we couldn't handle the fault,
107 * make sure we exit gracefully rather than endlessly redo
108 * the fault.
109 */
Kautuk Consulf1077012012-07-30 14:39:21 -0700110 fault = handle_mm_fault(mm, vma, address, flags);
111
112 if ((fault & VM_FAULT_RETRY) && fatal_signal_pending(current))
113 return;
114
Nick Piggin83c54072007-07-19 01:47:05 -0700115 if (unlikely(fault & VM_FAULT_ERROR)) {
116 if (fault & VM_FAULT_OOM)
117 goto out_of_memory;
118 else if (fault & VM_FAULT_SIGBUS)
119 goto do_sigbus;
Chris Zankel3f65ce42005-06-23 22:01:24 -0700120 BUG();
121 }
Kautuk Consulf1077012012-07-30 14:39:21 -0700122 if (flags & FAULT_FLAG_ALLOW_RETRY) {
123 if (fault & VM_FAULT_MAJOR)
124 current->maj_flt++;
125 else
126 current->min_flt++;
127 if (fault & VM_FAULT_RETRY) {
128 flags &= ~FAULT_FLAG_ALLOW_RETRY;
Shaohua Li45cac652012-10-08 16:32:19 -0700129 flags |= FAULT_FLAG_TRIED;
Kautuk Consulf1077012012-07-30 14:39:21 -0700130
131 /* No need to up_read(&mm->mmap_sem) as we would
132 * have already released it in __lock_page_or_retry
133 * in mm/filemap.c.
134 */
135
136 goto retry;
137 }
138 }
Chris Zankel3f65ce42005-06-23 22:01:24 -0700139
140 up_read(&mm->mmap_sem);
141 return;
142
143 /* Something tried to access memory that isn't in our memory map..
144 * Fix it, but check if it's kernel or user first..
145 */
146bad_area:
147 up_read(&mm->mmap_sem);
148 if (user_mode(regs)) {
149 current->thread.bad_vaddr = address;
150 current->thread.error_code = is_write;
151 info.si_signo = SIGSEGV;
152 info.si_errno = 0;
153 /* info.si_code has been set above */
154 info.si_addr = (void *) address;
155 force_sig_info(SIGSEGV, &info, current);
156 return;
157 }
158 bad_page_fault(regs, address, SIGSEGV);
159 return;
160
161
162 /* We ran out of memory, or some other thing happened to us that made
163 * us unable to handle the page fault gracefully.
164 */
165out_of_memory:
166 up_read(&mm->mmap_sem);
Nick Pigginf76f5d72010-06-04 14:14:51 -0700167 if (!user_mode(regs))
168 bad_page_fault(regs, address, SIGKILL);
169 else
170 pagefault_out_of_memory();
Chris Zankel3f65ce42005-06-23 22:01:24 -0700171 return;
172
173do_sigbus:
174 up_read(&mm->mmap_sem);
175
176 /* Send a sigbus, regardless of whether we were in kernel
177 * or user mode.
178 */
179 current->thread.bad_vaddr = address;
180 info.si_code = SIGBUS;
181 info.si_errno = 0;
182 info.si_code = BUS_ADRERR;
183 info.si_addr = (void *) address;
184 force_sig_info(SIGBUS, &info, current);
185
186 /* Kernel mode? Handle exceptions or die */
187 if (!user_mode(regs))
188 bad_page_fault(regs, address, SIGBUS);
189
190vmalloc_fault:
191 {
192 /* Synchronize this task's top level page-table
193 * with the 'reference' page table.
194 */
195 struct mm_struct *act_mm = current->active_mm;
196 int index = pgd_index(address);
197 pgd_t *pgd, *pgd_k;
198 pmd_t *pmd, *pmd_k;
199 pte_t *pte_k;
200
201 if (act_mm == NULL)
202 goto bad_page_fault;
203
204 pgd = act_mm->pgd + index;
205 pgd_k = init_mm.pgd + index;
206
207 if (!pgd_present(*pgd_k))
208 goto bad_page_fault;
209
210 pgd_val(*pgd) = pgd_val(*pgd_k);
211
212 pmd = pmd_offset(pgd, address);
213 pmd_k = pmd_offset(pgd_k, address);
214 if (!pmd_present(*pmd) || !pmd_present(*pmd_k))
215 goto bad_page_fault;
216
217 pmd_val(*pmd) = pmd_val(*pmd_k);
218 pte_k = pte_offset_kernel(pmd_k, address);
219
220 if (!pte_present(*pte_k))
221 goto bad_page_fault;
222 return;
223 }
224bad_page_fault:
225 bad_page_fault(regs, address, SIGKILL);
226 return;
227}
228
229
230void
231bad_page_fault(struct pt_regs *regs, unsigned long address, int sig)
232{
233 extern void die(const char*, struct pt_regs*, long);
234 const struct exception_table_entry *entry;
235
236 /* Are we prepared to handle this kernel fault? */
237 if ((entry = search_exception_tables(regs->pc)) != NULL) {
Chris Zankel66569202007-08-22 10:14:51 -0700238#ifdef DEBUG_PAGE_FAULT
Chris Zankel3f65ce42005-06-23 22:01:24 -0700239 printk(KERN_DEBUG "%s: Exception at pc=%#010lx (%lx)\n",
240 current->comm, regs->pc, entry->fixup);
241#endif
242 current->thread.bad_uaddr = address;
243 regs->pc = entry->fixup;
244 return;
245 }
246
247 /* Oops. The kernel tried to access some bad page. We'll have to
248 * terminate things with extreme prejudice.
249 */
250 printk(KERN_ALERT "Unable to handle kernel paging request at virtual "
251 "address %08lx\n pc = %08lx, ra = %08lx\n",
252 address, regs->pc, regs->areg[0]);
253 die("Oops", regs, sig);
254 do_exit(sig);
255}
256