diff options
author | Linus Torvalds <torvalds@ppc970.osdl.org> | 2005-04-16 18:20:36 -0400 |
---|---|---|
committer | Linus Torvalds <torvalds@ppc970.osdl.org> | 2005-04-16 18:20:36 -0400 |
commit | 1da177e4c3f41524e886b7f1b8a0c1fc7321cac2 (patch) | |
tree | 0bba044c4ce775e45a88a51686b5d9f90697ea9d /arch/cris/mm/fault.c |
Linux-2.6.12-rc2v2.6.12-rc2
Initial git repository build. I'm not bothering with the full history,
even though we have it. We can create a separate "historical" git
archive of that later if we want to, and in the meantime it's about
3.2GB when imported into git - space that would just make the early
git days unnecessarily complicated, when we don't have a lot of good
infrastructure for it.
Let it rip!
Diffstat (limited to 'arch/cris/mm/fault.c')
-rw-r--r-- | arch/cris/mm/fault.c | 387 |
1 files changed, 387 insertions, 0 deletions
diff --git a/arch/cris/mm/fault.c b/arch/cris/mm/fault.c new file mode 100644 index 000000000000..03254b9eded1 --- /dev/null +++ b/arch/cris/mm/fault.c | |||
@@ -0,0 +1,387 @@ | |||
1 | /* | ||
2 | * linux/arch/cris/mm/fault.c | ||
3 | * | ||
4 | * Copyright (C) 2000, 2001 Axis Communications AB | ||
5 | * | ||
6 | * Authors: Bjorn Wesen | ||
7 | * | ||
8 | * $Log: fault.c,v $ | ||
9 | * Revision 1.11 2004/05/14 07:58:05 starvik | ||
10 | * Merge of changes from 2.4 | ||
11 | * | ||
12 | * Revision 1.10 2003/10/27 14:51:24 starvik | ||
13 | * Removed debugcode | ||
14 | * | ||
15 | * Revision 1.9 2003/10/27 14:50:42 starvik | ||
16 | * Changed do_page_fault signature | ||
17 | * | ||
18 | * Revision 1.8 2003/07/04 13:02:48 tobiasa | ||
19 | * Moved code snippet from arch/cris/mm/fault.c that searches for fixup code | ||
20 | * to seperate function in arch-specific files. | ||
21 | * | ||
22 | * Revision 1.7 2003/01/22 06:48:38 starvik | ||
23 | * Fixed warnings issued by GCC 3.2.1 | ||
24 | * | ||
25 | * Revision 1.6 2003/01/09 14:42:52 starvik | ||
26 | * Merge of Linux 2.5.55 | ||
27 | * | ||
28 | * Revision 1.5 2002/12/11 14:44:48 starvik | ||
29 | * Extracted v10 (ETRAX 100LX) specific stuff to arch/cris/arch-v10/mm | ||
30 | * | ||
31 | * Revision 1.4 2002/11/13 15:10:28 starvik | ||
32 | * pte_offset has been renamed to pte_offset_kernel | ||
33 | * | ||
34 | * Revision 1.3 2002/11/05 06:45:13 starvik | ||
35 | * Merge of Linux 2.5.45 | ||
36 | * | ||
37 | * Revision 1.2 2001/12/18 13:35:22 bjornw | ||
38 | * Applied the 2.4.13->2.4.16 CRIS patch to 2.5.1 (is a copy of 2.4.15). | ||
39 | * | ||
40 | * Revision 1.20 2001/11/22 13:34:06 bjornw | ||
41 | * * Bug workaround (LX TR89): force a rerun of the whole of an interrupted | ||
42 | * unaligned write, because the second half of the write will be corrupted | ||
43 | * otherwise. Affected unaligned writes spanning not-yet mapped pages. | ||
44 | * * Optimization: use the wr_rd bit in R_MMU_CAUSE to know whether a miss | ||
45 | * was due to a read or a write (before we didn't know this until the next | ||
46 | * restart of the interrupted instruction, thus wasting one fault-irq) | ||
47 | * | ||
48 | * Revision 1.19 2001/11/12 19:02:10 pkj | ||
49 | * Fixed compiler warnings. | ||
50 | * | ||
51 | * Revision 1.18 2001/07/18 22:14:32 bjornw | ||
52 | * Enable interrupts in the bulk of do_page_fault | ||
53 | * | ||
54 | * Revision 1.17 2001/07/18 13:07:23 bjornw | ||
55 | * * Detect non-existant PTE's in vmalloc pmd synchronization | ||
56 | * * Remove comment about fast-paths for VMALLOC_START etc, because all that | ||
57 | * was totally bogus anyway it turned out :) | ||
58 | * * Fix detection of vmalloc-area synchronization | ||
59 | * * Add some comments | ||
60 | * | ||
61 | * Revision 1.16 2001/06/13 00:06:08 bjornw | ||
62 | * current_pgd should be volatile | ||
63 | * | ||
64 | * Revision 1.15 2001/06/13 00:02:23 bjornw | ||
65 | * Use a separate variable to store the current pgd to avoid races in schedule | ||
66 | * | ||
67 | * Revision 1.14 2001/05/16 17:41:07 hp | ||
68 | * Last comment tweak further tweaked. | ||
69 | * | ||
70 | * Revision 1.13 2001/05/15 00:58:44 hp | ||
71 | * Expand a bit on the comment why we compare address >= TASK_SIZE rather | ||
72 | * than >= VMALLOC_START. | ||
73 | * | ||
74 | * Revision 1.12 2001/04/04 10:51:14 bjornw | ||
75 | * mmap_sem is grabbed for reading | ||
76 | * | ||
77 | * Revision 1.11 2001/03/23 07:36:07 starvik | ||
78 | * Corrected according to review remarks | ||
79 | * | ||
80 | * Revision 1.10 2001/03/21 16:10:11 bjornw | ||
81 | * CRIS_FRAME_FIXUP not needed anymore, use FRAME_NORMAL | ||
82 | * | ||
83 | * Revision 1.9 2001/03/05 13:22:20 bjornw | ||
84 | * Spell-fix and fix in vmalloc_fault handling | ||
85 | * | ||
86 | * Revision 1.8 2000/11/22 14:45:31 bjornw | ||
87 | * * 2.4.0-test10 removed the set_pgdir instantaneous kernel global mapping | ||
88 | * into all processes. Instead we fill in the missing PTE entries on demand. | ||
89 | * | ||
90 | * Revision 1.7 2000/11/21 16:39:09 bjornw | ||
91 | * fixup switches frametype | ||
92 | * | ||
93 | * Revision 1.6 2000/11/17 16:54:08 bjornw | ||
94 | * More detailed siginfo reporting | ||
95 | * | ||
96 | * | ||
97 | */ | ||
98 | |||
99 | #include <linux/mm.h> | ||
100 | #include <linux/interrupt.h> | ||
101 | #include <linux/module.h> | ||
102 | #include <asm/uaccess.h> | ||
103 | |||
104 | extern int find_fixup_code(struct pt_regs *); | ||
105 | extern void die_if_kernel(const char *, struct pt_regs *, long); | ||
106 | |||
107 | /* debug of low-level TLB reload */ | ||
108 | #undef DEBUG | ||
109 | |||
110 | #ifdef DEBUG | ||
111 | #define D(x) x | ||
112 | #else | ||
113 | #define D(x) | ||
114 | #endif | ||
115 | |||
116 | /* debug of higher-level faults */ | ||
117 | #define DPG(x) | ||
118 | |||
119 | /* current active page directory */ | ||
120 | |||
121 | volatile pgd_t *current_pgd; | ||
122 | |||
123 | /* | ||
124 | * This routine handles page faults. It determines the address, | ||
125 | * and the problem, and then passes it off to one of the appropriate | ||
126 | * routines. | ||
127 | * | ||
128 | * Notice that the address we're given is aligned to the page the fault | ||
129 | * occurred in, since we only get the PFN in R_MMU_CAUSE not the complete | ||
130 | * address. | ||
131 | * | ||
132 | * error_code: | ||
133 | * bit 0 == 0 means no page found, 1 means protection fault | ||
134 | * bit 1 == 0 means read, 1 means write | ||
135 | * | ||
136 | * If this routine detects a bad access, it returns 1, otherwise it | ||
137 | * returns 0. | ||
138 | */ | ||
139 | |||
140 | asmlinkage void | ||
141 | do_page_fault(unsigned long address, struct pt_regs *regs, | ||
142 | int protection, int writeaccess) | ||
143 | { | ||
144 | struct task_struct *tsk; | ||
145 | struct mm_struct *mm; | ||
146 | struct vm_area_struct * vma; | ||
147 | siginfo_t info; | ||
148 | |||
149 | D(printk("Page fault for %X at %X, prot %d write %d\n", | ||
150 | address, regs->erp, protection, writeaccess)); | ||
151 | |||
152 | tsk = current; | ||
153 | |||
154 | /* | ||
155 | * We fault-in kernel-space virtual memory on-demand. The | ||
156 | * 'reference' page table is init_mm.pgd. | ||
157 | * | ||
158 | * NOTE! We MUST NOT take any locks for this case. We may | ||
159 | * be in an interrupt or a critical region, and should | ||
160 | * only copy the information from the master page table, | ||
161 | * nothing more. | ||
162 | * | ||
163 | * NOTE2: This is done so that, when updating the vmalloc | ||
164 | * mappings we don't have to walk all processes pgdirs and | ||
165 | * add the high mappings all at once. Instead we do it as they | ||
166 | * are used. However vmalloc'ed page entries have the PAGE_GLOBAL | ||
167 | * bit set so sometimes the TLB can use a lingering entry. | ||
168 | * | ||
169 | * This verifies that the fault happens in kernel space | ||
170 | * and that the fault was not a protection error (error_code & 1). | ||
171 | */ | ||
172 | |||
173 | if (address >= VMALLOC_START && | ||
174 | !protection && | ||
175 | !user_mode(regs)) | ||
176 | goto vmalloc_fault; | ||
177 | |||
178 | /* we can and should enable interrupts at this point */ | ||
179 | sti(); | ||
180 | |||
181 | mm = tsk->mm; | ||
182 | info.si_code = SEGV_MAPERR; | ||
183 | |||
184 | /* | ||
185 | * If we're in an interrupt or have no user | ||
186 | * context, we must not take the fault.. | ||
187 | */ | ||
188 | |||
189 | if (in_interrupt() || !mm) | ||
190 | goto no_context; | ||
191 | |||
192 | down_read(&mm->mmap_sem); | ||
193 | vma = find_vma(mm, address); | ||
194 | if (!vma) | ||
195 | goto bad_area; | ||
196 | if (vma->vm_start <= address) | ||
197 | goto good_area; | ||
198 | if (!(vma->vm_flags & VM_GROWSDOWN)) | ||
199 | goto bad_area; | ||
200 | if (user_mode(regs)) { | ||
201 | /* | ||
202 | * accessing the stack below usp is always a bug. | ||
203 | * we get page-aligned addresses so we can only check | ||
204 | * if we're within a page from usp, but that might be | ||
205 | * enough to catch brutal errors at least. | ||
206 | */ | ||
207 | if (address + PAGE_SIZE < rdusp()) | ||
208 | goto bad_area; | ||
209 | } | ||
210 | if (expand_stack(vma, address)) | ||
211 | goto bad_area; | ||
212 | |||
213 | /* | ||
214 | * Ok, we have a good vm_area for this memory access, so | ||
215 | * we can handle it.. | ||
216 | */ | ||
217 | |||
218 | good_area: | ||
219 | info.si_code = SEGV_ACCERR; | ||
220 | |||
221 | /* first do some preliminary protection checks */ | ||
222 | |||
223 | if (writeaccess) { | ||
224 | if (!(vma->vm_flags & VM_WRITE)) | ||
225 | goto bad_area; | ||
226 | } else { | ||
227 | if (!(vma->vm_flags & (VM_READ | VM_EXEC))) | ||
228 | goto bad_area; | ||
229 | } | ||
230 | |||
231 | /* | ||
232 | * If for any reason at all we couldn't handle the fault, | ||
233 | * make sure we exit gracefully rather than endlessly redo | ||
234 | * the fault. | ||
235 | */ | ||
236 | |||
237 | switch (handle_mm_fault(mm, vma, address, writeaccess)) { | ||
238 | case 1: | ||
239 | tsk->min_flt++; | ||
240 | break; | ||
241 | case 2: | ||
242 | tsk->maj_flt++; | ||
243 | break; | ||
244 | case 0: | ||
245 | goto do_sigbus; | ||
246 | default: | ||
247 | goto out_of_memory; | ||
248 | } | ||
249 | |||
250 | up_read(&mm->mmap_sem); | ||
251 | return; | ||
252 | |||
253 | /* | ||
254 | * Something tried to access memory that isn't in our memory map.. | ||
255 | * Fix it, but check if it's kernel or user first.. | ||
256 | */ | ||
257 | |||
258 | bad_area: | ||
259 | up_read(&mm->mmap_sem); | ||
260 | |||
261 | bad_area_nosemaphore: | ||
262 | DPG(show_registers(regs)); | ||
263 | |||
264 | /* User mode accesses just cause a SIGSEGV */ | ||
265 | |||
266 | if (user_mode(regs)) { | ||
267 | info.si_signo = SIGSEGV; | ||
268 | info.si_errno = 0; | ||
269 | /* info.si_code has been set above */ | ||
270 | info.si_addr = (void *)address; | ||
271 | force_sig_info(SIGSEGV, &info, tsk); | ||
272 | return; | ||
273 | } | ||
274 | |||
275 | no_context: | ||
276 | |||
277 | /* Are we prepared to handle this kernel fault? | ||
278 | * | ||
279 | * (The kernel has valid exception-points in the source | ||
280 | * when it acesses user-memory. When it fails in one | ||
281 | * of those points, we find it in a table and do a jump | ||
282 | * to some fixup code that loads an appropriate error | ||
283 | * code) | ||
284 | */ | ||
285 | |||
286 | if (find_fixup_code(regs)) | ||
287 | return; | ||
288 | |||
289 | /* | ||
290 | * Oops. The kernel tried to access some bad page. We'll have to | ||
291 | * terminate things with extreme prejudice. | ||
292 | */ | ||
293 | |||
294 | if ((unsigned long) (address) < PAGE_SIZE) | ||
295 | printk(KERN_ALERT "Unable to handle kernel NULL pointer dereference"); | ||
296 | else | ||
297 | printk(KERN_ALERT "Unable to handle kernel access"); | ||
298 | printk(" at virtual address %08lx\n",address); | ||
299 | |||
300 | die_if_kernel("Oops", regs, (writeaccess << 1) | protection); | ||
301 | |||
302 | do_exit(SIGKILL); | ||
303 | |||
304 | /* | ||
305 | * We ran out of memory, or some other thing happened to us that made | ||
306 | * us unable to handle the page fault gracefully. | ||
307 | */ | ||
308 | |||
309 | out_of_memory: | ||
310 | up_read(&mm->mmap_sem); | ||
311 | printk("VM: killing process %s\n", tsk->comm); | ||
312 | if (user_mode(regs)) | ||
313 | do_exit(SIGKILL); | ||
314 | goto no_context; | ||
315 | |||
316 | do_sigbus: | ||
317 | up_read(&mm->mmap_sem); | ||
318 | |||
319 | /* | ||
320 | * Send a sigbus, regardless of whether we were in kernel | ||
321 | * or user mode. | ||
322 | */ | ||
323 | info.si_signo = SIGBUS; | ||
324 | info.si_errno = 0; | ||
325 | info.si_code = BUS_ADRERR; | ||
326 | info.si_addr = (void *)address; | ||
327 | force_sig_info(SIGBUS, &info, tsk); | ||
328 | |||
329 | /* Kernel mode? Handle exceptions or die */ | ||
330 | if (!user_mode(regs)) | ||
331 | goto no_context; | ||
332 | return; | ||
333 | |||
334 | vmalloc_fault: | ||
335 | { | ||
336 | /* | ||
337 | * Synchronize this task's top level page-table | ||
338 | * with the 'reference' page table. | ||
339 | * | ||
340 | * Use current_pgd instead of tsk->active_mm->pgd | ||
341 | * since the latter might be unavailable if this | ||
342 | * code is executed in a misfortunately run irq | ||
343 | * (like inside schedule() between switch_mm and | ||
344 | * switch_to...). | ||
345 | */ | ||
346 | |||
347 | int offset = pgd_index(address); | ||
348 | pgd_t *pgd, *pgd_k; | ||
349 | pmd_t *pmd, *pmd_k; | ||
350 | pte_t *pte_k; | ||
351 | |||
352 | pgd = (pgd_t *)current_pgd + offset; | ||
353 | pgd_k = init_mm.pgd + offset; | ||
354 | |||
355 | /* Since we're two-level, we don't need to do both | ||
356 | * set_pgd and set_pmd (they do the same thing). If | ||
357 | * we go three-level at some point, do the right thing | ||
358 | * with pgd_present and set_pgd here. | ||
359 | * | ||
360 | * Also, since the vmalloc area is global, we don't | ||
361 | * need to copy individual PTE's, it is enough to | ||
362 | * copy the pgd pointer into the pte page of the | ||
363 | * root task. If that is there, we'll find our pte if | ||
364 | * it exists. | ||
365 | */ | ||
366 | |||
367 | pmd = pmd_offset(pgd, address); | ||
368 | pmd_k = pmd_offset(pgd_k, address); | ||
369 | |||
370 | if (!pmd_present(*pmd_k)) | ||
371 | goto bad_area_nosemaphore; | ||
372 | |||
373 | set_pmd(pmd, *pmd_k); | ||
374 | |||
375 | /* Make sure the actual PTE exists as well to | ||
376 | * catch kernel vmalloc-area accesses to non-mapped | ||
377 | * addresses. If we don't do this, this will just | ||
378 | * silently loop forever. | ||
379 | */ | ||
380 | |||
381 | pte_k = pte_offset_kernel(pmd_k, address); | ||
382 | if (!pte_present(*pte_k)) | ||
383 | goto no_context; | ||
384 | |||
385 | return; | ||
386 | } | ||
387 | } | ||