aboutsummaryrefslogtreecommitdiffstats
path: root/arch/arm/mm/fault.c
diff options
context:
space:
mode:
authorHaavard Skinnemoen <haavard.skinnemoen@atmel.com>2009-10-06 11:36:55 -0400
committerHaavard Skinnemoen <haavard.skinnemoen@atmel.com>2009-10-06 11:36:55 -0400
commitd94e5fcbf1420366dcb4102bafe04dbcfc0d0d4b (patch)
treea9b7de7df6da5c3132cc68169b9c47ba288ccd42 /arch/arm/mm/fault.c
parentd55651168a20078a94597a297d5cdfd807bf07b6 (diff)
parent374576a8b6f865022c0fd1ca62396889b23d66dd (diff)
Merge commit 'v2.6.32-rc3'
Diffstat (limited to 'arch/arm/mm/fault.c')
-rw-r--r--arch/arm/mm/fault.c185
1 files changed, 136 insertions, 49 deletions
diff --git a/arch/arm/mm/fault.c b/arch/arm/mm/fault.c
index 6fdcbb709827..ae0e25f5a70e 100644
--- a/arch/arm/mm/fault.c
+++ b/arch/arm/mm/fault.c
@@ -16,6 +16,8 @@
16#include <linux/kprobes.h> 16#include <linux/kprobes.h>
17#include <linux/uaccess.h> 17#include <linux/uaccess.h>
18#include <linux/page-flags.h> 18#include <linux/page-flags.h>
19#include <linux/sched.h>
20#include <linux/highmem.h>
19 21
20#include <asm/system.h> 22#include <asm/system.h>
21#include <asm/pgtable.h> 23#include <asm/pgtable.h>
@@ -23,6 +25,20 @@
23 25
24#include "fault.h" 26#include "fault.h"
25 27
28/*
29 * Fault status register encodings. We steal bit 31 for our own purposes.
30 */
31#define FSR_LNX_PF (1 << 31)
32#define FSR_WRITE (1 << 11)
33#define FSR_FS4 (1 << 10)
34#define FSR_FS3_0 (15)
35
36static inline int fsr_fs(unsigned int fsr)
37{
38 return (fsr & FSR_FS3_0) | (fsr & FSR_FS4) >> 6;
39}
40
41#ifdef CONFIG_MMU
26 42
27#ifdef CONFIG_KPROBES 43#ifdef CONFIG_KPROBES
28static inline int notify_page_fault(struct pt_regs *regs, unsigned int fsr) 44static inline int notify_page_fault(struct pt_regs *regs, unsigned int fsr)
@@ -97,6 +113,10 @@ void show_pte(struct mm_struct *mm, unsigned long addr)
97 113
98 printk("\n"); 114 printk("\n");
99} 115}
116#else /* CONFIG_MMU */
117void show_pte(struct mm_struct *mm, unsigned long addr)
118{ }
119#endif /* CONFIG_MMU */
100 120
101/* 121/*
102 * Oops. The kernel tried to access some page that wasn't present. 122 * Oops. The kernel tried to access some page that wasn't present.
@@ -171,21 +191,39 @@ void do_bad_area(unsigned long addr, unsigned int fsr, struct pt_regs *regs)
171 __do_kernel_fault(mm, addr, fsr, regs); 191 __do_kernel_fault(mm, addr, fsr, regs);
172} 192}
173 193
194#ifdef CONFIG_MMU
174#define VM_FAULT_BADMAP 0x010000 195#define VM_FAULT_BADMAP 0x010000
175#define VM_FAULT_BADACCESS 0x020000 196#define VM_FAULT_BADACCESS 0x020000
176 197
177static int 198/*
199 * Check that the permissions on the VMA allow for the fault which occurred.
200 * If we encountered a write fault, we must have write permission, otherwise
201 * we allow any permission.
202 */
203static inline bool access_error(unsigned int fsr, struct vm_area_struct *vma)
204{
205 unsigned int mask = VM_READ | VM_WRITE | VM_EXEC;
206
207 if (fsr & FSR_WRITE)
208 mask = VM_WRITE;
209 if (fsr & FSR_LNX_PF)
210 mask = VM_EXEC;
211
212 return vma->vm_flags & mask ? false : true;
213}
214
215static int __kprobes
178__do_page_fault(struct mm_struct *mm, unsigned long addr, unsigned int fsr, 216__do_page_fault(struct mm_struct *mm, unsigned long addr, unsigned int fsr,
179 struct task_struct *tsk) 217 struct task_struct *tsk)
180{ 218{
181 struct vm_area_struct *vma; 219 struct vm_area_struct *vma;
182 int fault, mask; 220 int fault;
183 221
184 vma = find_vma(mm, addr); 222 vma = find_vma(mm, addr);
185 fault = VM_FAULT_BADMAP; 223 fault = VM_FAULT_BADMAP;
186 if (!vma) 224 if (unlikely(!vma))
187 goto out; 225 goto out;
188 if (vma->vm_start > addr) 226 if (unlikely(vma->vm_start > addr))
189 goto check_stack; 227 goto check_stack;
190 228
191 /* 229 /*
@@ -193,47 +231,24 @@ __do_page_fault(struct mm_struct *mm, unsigned long addr, unsigned int fsr,
193 * memory access, so we can handle it. 231 * memory access, so we can handle it.
194 */ 232 */
195good_area: 233good_area:
196 if (fsr & (1 << 11)) /* write? */ 234 if (access_error(fsr, vma)) {
197 mask = VM_WRITE; 235 fault = VM_FAULT_BADACCESS;
198 else
199 mask = VM_READ|VM_EXEC|VM_WRITE;
200
201 fault = VM_FAULT_BADACCESS;
202 if (!(vma->vm_flags & mask))
203 goto out; 236 goto out;
237 }
204 238
205 /* 239 /*
206 * If for any reason at all we couldn't handle 240 * If for any reason at all we couldn't handle the fault, make
207 * the fault, make sure we exit gracefully rather 241 * sure we exit gracefully rather than endlessly redo the fault.
208 * than endlessly redo the fault.
209 */ 242 */
210survive: 243 fault = handle_mm_fault(mm, vma, addr & PAGE_MASK, (fsr & FSR_WRITE) ? FAULT_FLAG_WRITE : 0);
211 fault = handle_mm_fault(mm, vma, addr & PAGE_MASK, (fsr & (1 << 11)) ? FAULT_FLAG_WRITE : 0); 244 if (unlikely(fault & VM_FAULT_ERROR))
212 if (unlikely(fault & VM_FAULT_ERROR)) { 245 return fault;
213 if (fault & VM_FAULT_OOM)
214 goto out_of_memory;
215 else if (fault & VM_FAULT_SIGBUS)
216 return fault;
217 BUG();
218 }
219 if (fault & VM_FAULT_MAJOR) 246 if (fault & VM_FAULT_MAJOR)
220 tsk->maj_flt++; 247 tsk->maj_flt++;
221 else 248 else
222 tsk->min_flt++; 249 tsk->min_flt++;
223 return fault; 250 return fault;
224 251
225out_of_memory:
226 if (!is_global_init(tsk))
227 goto out;
228
229 /*
230 * If we are out of memory for pid1, sleep for a while and retry
231 */
232 up_read(&mm->mmap_sem);
233 yield();
234 down_read(&mm->mmap_sem);
235 goto survive;
236
237check_stack: 252check_stack:
238 if (vma->vm_flags & VM_GROWSDOWN && !expand_stack(vma, addr)) 253 if (vma->vm_flags & VM_GROWSDOWN && !expand_stack(vma, addr))
239 goto good_area; 254 goto good_area;
@@ -270,6 +285,13 @@ do_page_fault(unsigned long addr, unsigned int fsr, struct pt_regs *regs)
270 if (!user_mode(regs) && !search_exception_tables(regs->ARM_pc)) 285 if (!user_mode(regs) && !search_exception_tables(regs->ARM_pc))
271 goto no_context; 286 goto no_context;
272 down_read(&mm->mmap_sem); 287 down_read(&mm->mmap_sem);
288 } else {
289 /*
290 * The above down_read_trylock() might have succeeded in
291 * which case, we'll have missed the might_sleep() from
292 * down_read()
293 */
294 might_sleep();
273 } 295 }
274 296
275 fault = __do_page_fault(mm, addr, fsr, tsk); 297 fault = __do_page_fault(mm, addr, fsr, tsk);
@@ -281,6 +303,16 @@ do_page_fault(unsigned long addr, unsigned int fsr, struct pt_regs *regs)
281 if (likely(!(fault & (VM_FAULT_ERROR | VM_FAULT_BADMAP | VM_FAULT_BADACCESS)))) 303 if (likely(!(fault & (VM_FAULT_ERROR | VM_FAULT_BADMAP | VM_FAULT_BADACCESS))))
282 return 0; 304 return 0;
283 305
306 if (fault & VM_FAULT_OOM) {
307 /*
308 * We ran out of memory, call the OOM killer, and return to
309 * userspace (which will retry the fault, or kill us if we
310 * got oom-killed)
311 */
312 pagefault_out_of_memory();
313 return 0;
314 }
315
284 /* 316 /*
285 * If we are in kernel mode at this point, we 317 * If we are in kernel mode at this point, we
286 * have no context to handle this fault with. 318 * have no context to handle this fault with.
@@ -288,16 +320,6 @@ do_page_fault(unsigned long addr, unsigned int fsr, struct pt_regs *regs)
288 if (!user_mode(regs)) 320 if (!user_mode(regs))
289 goto no_context; 321 goto no_context;
290 322
291 if (fault & VM_FAULT_OOM) {
292 /*
293 * We ran out of memory, or some other thing
294 * happened to us that made us unable to handle
295 * the page fault gracefully.
296 */
297 printk("VM: killing process %s\n", tsk->comm);
298 do_group_exit(SIGKILL);
299 return 0;
300 }
301 if (fault & VM_FAULT_SIGBUS) { 323 if (fault & VM_FAULT_SIGBUS) {
302 /* 324 /*
303 * We had some memory, but were unable to 325 * We had some memory, but were unable to
@@ -322,6 +344,13 @@ no_context:
322 __do_kernel_fault(mm, addr, fsr, regs); 344 __do_kernel_fault(mm, addr, fsr, regs);
323 return 0; 345 return 0;
324} 346}
347#else /* CONFIG_MMU */
348static int
349do_page_fault(unsigned long addr, unsigned int fsr, struct pt_regs *regs)
350{
351 return 0;
352}
353#endif /* CONFIG_MMU */
325 354
326/* 355/*
327 * First Level Translation Fault Handler 356 * First Level Translation Fault Handler
@@ -340,6 +369,7 @@ no_context:
340 * interrupt or a critical region, and should only copy the information 369 * interrupt or a critical region, and should only copy the information
341 * from the master page table, nothing more. 370 * from the master page table, nothing more.
342 */ 371 */
372#ifdef CONFIG_MMU
343static int __kprobes 373static int __kprobes
344do_translation_fault(unsigned long addr, unsigned int fsr, 374do_translation_fault(unsigned long addr, unsigned int fsr,
345 struct pt_regs *regs) 375 struct pt_regs *regs)
@@ -378,6 +408,14 @@ bad_area:
378 do_bad_area(addr, fsr, regs); 408 do_bad_area(addr, fsr, regs);
379 return 0; 409 return 0;
380} 410}
411#else /* CONFIG_MMU */
412static int
413do_translation_fault(unsigned long addr, unsigned int fsr,
414 struct pt_regs *regs)
415{
416 return 0;
417}
418#endif /* CONFIG_MMU */
381 419
382/* 420/*
383 * Some section permission faults need to be handled gracefully. 421 * Some section permission faults need to be handled gracefully.
@@ -465,10 +503,10 @@ hook_fault_code(int nr, int (*fn)(unsigned long, unsigned int, struct pt_regs *)
465asmlinkage void __exception 503asmlinkage void __exception
466do_DataAbort(unsigned long addr, unsigned int fsr, struct pt_regs *regs) 504do_DataAbort(unsigned long addr, unsigned int fsr, struct pt_regs *regs)
467{ 505{
468 const struct fsr_info *inf = fsr_info + (fsr & 15) + ((fsr & (1 << 10)) >> 6); 506 const struct fsr_info *inf = fsr_info + fsr_fs(fsr);
469 struct siginfo info; 507 struct siginfo info;
470 508
471 if (!inf->fn(addr, fsr, regs)) 509 if (!inf->fn(addr, fsr & ~FSR_LNX_PF, regs))
472 return; 510 return;
473 511
474 printk(KERN_ALERT "Unhandled fault: %s (0x%03x) at 0x%08lx\n", 512 printk(KERN_ALERT "Unhandled fault: %s (0x%03x) at 0x%08lx\n",
@@ -481,9 +519,58 @@ do_DataAbort(unsigned long addr, unsigned int fsr, struct pt_regs *regs)
481 arm_notify_die("", regs, &info, fsr, 0); 519 arm_notify_die("", regs, &info, fsr, 0);
482} 520}
483 521
522
523static struct fsr_info ifsr_info[] = {
524 { do_bad, SIGBUS, 0, "unknown 0" },
525 { do_bad, SIGBUS, 0, "unknown 1" },
526 { do_bad, SIGBUS, 0, "debug event" },
527 { do_bad, SIGSEGV, SEGV_ACCERR, "section access flag fault" },
528 { do_bad, SIGBUS, 0, "unknown 4" },
529 { do_translation_fault, SIGSEGV, SEGV_MAPERR, "section translation fault" },
530 { do_bad, SIGSEGV, SEGV_ACCERR, "page access flag fault" },
531 { do_page_fault, SIGSEGV, SEGV_MAPERR, "page translation fault" },
532 { do_bad, SIGBUS, 0, "external abort on non-linefetch" },
533 { do_bad, SIGSEGV, SEGV_ACCERR, "section domain fault" },
534 { do_bad, SIGBUS, 0, "unknown 10" },
535 { do_bad, SIGSEGV, SEGV_ACCERR, "page domain fault" },
536 { do_bad, SIGBUS, 0, "external abort on translation" },
537 { do_sect_fault, SIGSEGV, SEGV_ACCERR, "section permission fault" },
538 { do_bad, SIGBUS, 0, "external abort on translation" },
539 { do_page_fault, SIGSEGV, SEGV_ACCERR, "page permission fault" },
540 { do_bad, SIGBUS, 0, "unknown 16" },
541 { do_bad, SIGBUS, 0, "unknown 17" },
542 { do_bad, SIGBUS, 0, "unknown 18" },
543 { do_bad, SIGBUS, 0, "unknown 19" },
544 { do_bad, SIGBUS, 0, "unknown 20" },
545 { do_bad, SIGBUS, 0, "unknown 21" },
546 { do_bad, SIGBUS, 0, "unknown 22" },
547 { do_bad, SIGBUS, 0, "unknown 23" },
548 { do_bad, SIGBUS, 0, "unknown 24" },
549 { do_bad, SIGBUS, 0, "unknown 25" },
550 { do_bad, SIGBUS, 0, "unknown 26" },
551 { do_bad, SIGBUS, 0, "unknown 27" },
552 { do_bad, SIGBUS, 0, "unknown 28" },
553 { do_bad, SIGBUS, 0, "unknown 29" },
554 { do_bad, SIGBUS, 0, "unknown 30" },
555 { do_bad, SIGBUS, 0, "unknown 31" },
556};
557
484asmlinkage void __exception 558asmlinkage void __exception
485do_PrefetchAbort(unsigned long addr, struct pt_regs *regs) 559do_PrefetchAbort(unsigned long addr, unsigned int ifsr, struct pt_regs *regs)
486{ 560{
487 do_translation_fault(addr, 0, regs); 561 const struct fsr_info *inf = ifsr_info + fsr_fs(ifsr);
562 struct siginfo info;
563
564 if (!inf->fn(addr, ifsr | FSR_LNX_PF, regs))
565 return;
566
567 printk(KERN_ALERT "Unhandled prefetch abort: %s (0x%03x) at 0x%08lx\n",
568 inf->name, ifsr, addr);
569
570 info.si_signo = inf->sig;
571 info.si_errno = 0;
572 info.si_code = inf->code;
573 info.si_addr = (void __user *)addr;
574 arm_notify_die("", regs, &info, ifsr, 0);
488} 575}
489 576