[AVR32] optimize pagefault path
[linux-2.6] / arch / avr32 / mm / fault.c
1 /*
2  * Copyright (C) 2004-2006 Atmel Corporation
3  *
4  * Based on linux/arch/sh/mm/fault.c:
5  *   Copyright (C) 1999  Niibe Yutaka
6  *
7  * This program is free software; you can redistribute it and/or modify
8  * it under the terms of the GNU General Public License version 2 as
9  * published by the Free Software Foundation.
10  */
11
12 #include <linux/mm.h>
13 #include <linux/module.h>
14 #include <linux/pagemap.h>
15 #include <linux/kdebug.h>
16 #include <linux/kprobes.h>
17
18 #include <asm/mmu_context.h>
19 #include <asm/sysreg.h>
20 #include <asm/tlb.h>
21 #include <asm/uaccess.h>
22
23 #ifdef CONFIG_KPROBES
24 static inline int notify_page_fault(struct pt_regs *regs, int trap)
25 {
26         int ret = 0;
27
28         if (!user_mode(regs)) {
29                 if (kprobe_running() && kprobe_fault_handler(regs, trap))
30                         ret = 1;
31         }
32
33         return ret;
34 }
35 #else
36 static inline int notify_page_fault(struct pt_regs *regs, int trap)
37 {
38         return 0;
39 }
40 #endif
41
42 int exception_trace = 1;
43
44 /*
45  * This routine handles page faults. It determines the address and the
46  * problem, and then passes it off to one of the appropriate routines.
47  *
48  * ecr is the Exception Cause Register. Possible values are:
49  *   6:  Protection fault (instruction access)
50  *   15: Protection fault (read access)
51  *   16: Protection fault (write access)
52  *   20: Page not found (instruction access)
53  *   24: Page not found (read access)
54  *   28: Page not found (write access)
55  */
56 asmlinkage void do_page_fault(unsigned long ecr, struct pt_regs *regs)
57 {
58         struct task_struct *tsk;
59         struct mm_struct *mm;
60         struct vm_area_struct *vma;
61         const struct exception_table_entry *fixup;
62         unsigned long address;
63         unsigned long page;
64         int writeaccess;
65         long signr;
66         int code;
67
68         if (notify_page_fault(regs, ecr))
69                 return;
70
71         address = sysreg_read(TLBEAR);
72
73         tsk = current;
74         mm = tsk->mm;
75
76         signr = SIGSEGV;
77         code = SEGV_MAPERR;
78
79         /*
80          * If we're in an interrupt or have no user context, we must
81          * not take the fault...
82          */
83         if (in_atomic() || !mm || regs->sr & SYSREG_BIT(GM))
84                 goto no_context;
85
86         local_irq_enable();
87
88         down_read(&mm->mmap_sem);
89
90         vma = find_vma(mm, address);
91         if (!vma)
92                 goto bad_area;
93         if (vma->vm_start <= address)
94                 goto good_area;
95         if (!(vma->vm_flags & VM_GROWSDOWN))
96                 goto bad_area;
97         if (expand_stack(vma, address))
98                 goto bad_area;
99
100         /*
101          * Ok, we have a good vm_area for this memory access, so we
102          * can handle it...
103          */
104 good_area:
105         code = SEGV_ACCERR;
106         writeaccess = 0;
107
108         switch (ecr) {
109         case ECR_PROTECTION_X:
110         case ECR_TLB_MISS_X:
111                 if (!(vma->vm_flags & VM_EXEC))
112                         goto bad_area;
113                 break;
114         case ECR_PROTECTION_R:
115         case ECR_TLB_MISS_R:
116                 if (!(vma->vm_flags & (VM_READ | VM_WRITE | VM_EXEC)))
117                         goto bad_area;
118                 break;
119         case ECR_PROTECTION_W:
120         case ECR_TLB_MISS_W:
121                 if (!(vma->vm_flags & VM_WRITE))
122                         goto bad_area;
123                 writeaccess = 1;
124                 break;
125         default:
126                 panic("Unhandled case %lu in do_page_fault!", ecr);
127         }
128
129         /*
130          * If for any reason at all we couldn't handle the fault, make
131          * sure we exit gracefully rather than endlessly redo the
132          * fault.
133          */
134 survive:
135         switch (handle_mm_fault(mm, vma, address, writeaccess)) {
136         case VM_FAULT_MINOR:
137                 tsk->min_flt++;
138                 break;
139         case VM_FAULT_MAJOR:
140                 tsk->maj_flt++;
141                 break;
142         case VM_FAULT_SIGBUS:
143                 goto do_sigbus;
144         case VM_FAULT_OOM:
145                 goto out_of_memory;
146         default:
147                 BUG();
148         }
149
150         up_read(&mm->mmap_sem);
151         return;
152
153         /*
154          * Something tried to access memory that isn't in our memory
155          * map. Fix it, but check if it's kernel or user first...
156          */
157 bad_area:
158         up_read(&mm->mmap_sem);
159
160         if (user_mode(regs)) {
161                 if (exception_trace)
162                         printk("%s%s[%d]: segfault at %08lx pc %08lx "
163                                "sp %08lx ecr %lu\n",
164                                is_init(tsk) ? KERN_EMERG : KERN_INFO,
165                                tsk->comm, tsk->pid, address, regs->pc,
166                                regs->sp, ecr);
167                 _exception(SIGSEGV, regs, code, address);
168                 return;
169         }
170
171 no_context:
172         /* Are we prepared to handle this kernel fault? */
173         fixup = search_exception_tables(regs->pc);
174         if (fixup) {
175                 regs->pc = fixup->fixup;
176                 return;
177         }
178
179         /*
180          * Oops. The kernel tried to access some bad page. We'll have
181          * to terminate things with extreme prejudice.
182          */
183         if (address < PAGE_SIZE)
184                 printk(KERN_ALERT
185                        "Unable to handle kernel NULL pointer dereference");
186         else
187                 printk(KERN_ALERT
188                        "Unable to handle kernel paging request");
189         printk(" at virtual address %08lx\n", address);
190
191         page = sysreg_read(PTBR);
192         printk(KERN_ALERT "ptbr = %08lx", page);
193         if (page) {
194                 page = ((unsigned long *)page)[address >> 22];
195                 printk(" pgd = %08lx", page);
196                 if (page & _PAGE_PRESENT) {
197                         page &= PAGE_MASK;
198                         address &= 0x003ff000;
199                         page = ((unsigned long *)__va(page))[address >> PAGE_SHIFT];
200                         printk(" pte = %08lx", page);
201                 }
202         }
203         printk("\n");
204         die("Kernel access of bad area", regs, signr);
205         return;
206
207         /*
208          * We ran out of memory, or some other thing happened to us
209          * that made us unable to handle the page fault gracefully.
210          */
211 out_of_memory:
212         up_read(&mm->mmap_sem);
213         if (is_init(current)) {
214                 yield();
215                 down_read(&mm->mmap_sem);
216                 goto survive;
217         }
218         printk("VM: Killing process %s\n", tsk->comm);
219         if (user_mode(regs))
220                 do_exit(SIGKILL);
221         goto no_context;
222
223 do_sigbus:
224         up_read(&mm->mmap_sem);
225
226         /* Kernel mode? Handle exceptions or die */
227         signr = SIGBUS;
228         code = BUS_ADRERR;
229         if (!user_mode(regs))
230                 goto no_context;
231
232         if (exception_trace)
233                 printk("%s%s[%d]: bus error at %08lx pc %08lx "
234                        "sp %08lx ecr %lu\n",
235                        is_init(tsk) ? KERN_EMERG : KERN_INFO,
236                        tsk->comm, tsk->pid, address, regs->pc,
237                        regs->sp, ecr);
238
239         _exception(SIGBUS, regs, BUS_ADRERR, address);
240 }
241
242 asmlinkage void do_bus_error(unsigned long addr, int write_access,
243                              struct pt_regs *regs)
244 {
245         printk(KERN_ALERT
246                "Bus error at physical address 0x%08lx (%s access)\n",
247                addr, write_access ? "write" : "read");
248         printk(KERN_INFO "DTLB dump:\n");
249         dump_dtlb();
250         die("Bus Error", regs, SIGKILL);
251 }
252
253 /*
254  * This functionality is currently not possible to implement because
255  * we're using segmentation to ensure a fixed mapping of the kernel
256  * virtual address space.
257  *
258  * It would be possible to implement this, but it would require us to
259  * disable segmentation at startup and load the kernel mappings into
260  * the TLB like any other pages. There will be lots of trickery to
261  * avoid recursive invocation of the TLB miss handler, though...
262  */
263 #ifdef CONFIG_DEBUG_PAGEALLOC
264 void kernel_map_pages(struct page *page, int numpages, int enable)
265 {
266
267 }
268 EXPORT_SYMBOL(kernel_map_pages);
269 #endif