1
2
3
4
5
6
7
8
9
10
11#include <linux/sched.h>
12#include <linux/kernel.h>
13#include <linux/types.h>
14#include <linux/ptrace.h>
15#include <linux/mm.h>
16#include <linux/bitops.h>
17#include <linux/init.h>
18
19#include <asm/pgalloc.h>
20#include <asm/pgtable.h>
21
22#include "fault.h"
23
24
25
26
27
28static int
29do_sect_fault(unsigned long addr, unsigned int fsr, struct pt_regs *regs)
30{
31 struct task_struct *tsk = current;
32 do_bad_area(tsk, tsk->active_mm, addr, fsr, regs);
33 return 0;
34}
35
36
37
38
39static int
40do_bad(unsigned long addr, unsigned int fsr, struct pt_regs *regs)
41{
42 return 1;
43}
44
45static struct fsr_info {
46 int (*fn)(unsigned long addr, unsigned int fsr, struct pt_regs *regs);
47 int sig;
48 const char *name;
49} fsr_info[] = {
50
51
52
53
54 { do_bad, SIGSEGV, "vector exception" },
55 { do_bad, SIGILL, "alignment exception" },
56 { do_bad, SIGKILL, "terminal exception" },
57 { do_bad, SIGILL, "alignment exception" },
58 { do_bad, SIGBUS, "external abort on linefetch" },
59 { do_translation_fault, SIGSEGV, "section translation fault" },
60 { do_bad, SIGBUS, "external abort on linefetch" },
61 { do_page_fault, SIGSEGV, "page translation fault" },
62 { do_bad, SIGBUS, "external abort on non-linefetch" },
63 { do_bad, SIGSEGV, "section domain fault" },
64 { do_bad, SIGBUS, "external abort on non-linefetch" },
65 { do_bad, SIGSEGV, "page domain fault" },
66 { do_bad, SIGBUS, "external abort on translation" },
67 { do_sect_fault, SIGSEGV, "section permission fault" },
68 { do_bad, SIGBUS, "external abort on translation" },
69 { do_page_fault, SIGSEGV, "page permission fault" },
70
71
72
73
74
75 { do_bad, SIGBUS, "unknown 16" },
76 { do_bad, SIGBUS, "unknown 17" },
77 { do_bad, SIGBUS, "unknown 18" },
78 { do_bad, SIGBUS, "unknown 19" },
79 { do_bad, SIGBUS, "lock abort" },
80 { do_bad, SIGBUS, "unknown 21" },
81 { do_bad, SIGBUS, "imprecise external abort" },
82 { do_bad, SIGBUS, "unknown 23" },
83 { do_bad, SIGBUS, "dcache parity error" },
84 { do_bad, SIGBUS, "unknown 25" },
85 { do_bad, SIGBUS, "unknown 26" },
86 { do_bad, SIGBUS, "unknown 27" },
87 { do_bad, SIGBUS, "unknown 28" },
88 { do_bad, SIGBUS, "unknown 29" },
89 { do_bad, SIGBUS, "unknown 30" },
90 { do_bad, SIGBUS, "unknown 31" }
91};
92
93void __init
94hook_fault_code(int nr, int (*fn)(unsigned long, unsigned int, struct pt_regs *),
95 int sig, const char *name)
96{
97 if (nr >= 0 && nr < ARRAY_SIZE(fsr_info)) {
98 fsr_info[nr].fn = fn;
99 fsr_info[nr].sig = sig;
100 fsr_info[nr].name = name;
101 }
102}
103
104
105
106
107asmlinkage void
108do_DataAbort(unsigned long addr, unsigned int fsr, struct pt_regs *regs)
109{
110 const struct fsr_info *inf = fsr_info + (fsr & 15) + ((fsr & (1 << 10)) >> 6);
111
112 if (!inf->fn(addr, fsr, regs))
113 return;
114
115 printk(KERN_ALERT "Unhandled fault: %s (0x%03x) at 0x%08lx\n",
116 inf->name, fsr, addr);
117 force_sig(inf->sig, current);
118 show_pte(current->mm, addr);
119 die_if_kernel("Oops", regs, 0);
120}
121
122asmlinkage void
123do_PrefetchAbort(unsigned long addr, struct pt_regs *regs)
124{
125 do_translation_fault(addr, 0, regs);
126}
127
128
129
130
131
132static void adjust_pte(struct vm_area_struct *vma, unsigned long address)
133{
134 pgd_t *pgd;
135 pmd_t *pmd;
136 pte_t *pte, entry;
137
138 pgd = pgd_offset(vma->vm_mm, address);
139 if (pgd_none(*pgd))
140 return;
141 if (pgd_bad(*pgd))
142 goto bad_pgd;
143
144 pmd = pmd_offset(pgd, address);
145 if (pmd_none(*pmd))
146 return;
147 if (pmd_bad(*pmd))
148 goto bad_pmd;
149
150 pte = pte_offset_map(pmd, address);
151 entry = *pte;
152
153
154
155
156
157 if (pte_present(entry) && pte_val(entry) & L_PTE_CACHEABLE) {
158 flush_cache_page(vma, address);
159 pte_val(entry) &= ~L_PTE_CACHEABLE;
160 set_pte(pte, entry);
161 flush_tlb_page(vma, address);
162 }
163 pte_unmap(pte);
164 return;
165
166bad_pgd:
167 pgd_ERROR(*pgd);
168 pgd_clear(pgd);
169 return;
170
171bad_pmd:
172 pmd_ERROR(*pmd);
173 pmd_clear(pmd);
174 return;
175}
176
177static void
178make_coherent(struct vm_area_struct *vma, unsigned long addr, struct page *page)
179{
180 struct list_head *l;
181 struct mm_struct *mm = vma->vm_mm;
182 unsigned long pgoff = (addr - vma->vm_start) >> PAGE_SHIFT;
183 int aliases = 0;
184
185
186
187
188
189
190 list_for_each(l, &page->mapping->i_mmap_shared) {
191 struct vm_area_struct *mpnt;
192 unsigned long off;
193
194 mpnt = list_entry(l, struct vm_area_struct, shared);
195
196
197
198
199
200
201 if (mpnt->vm_mm != mm || mpnt == vma)
202 continue;
203
204
205
206
207 if (pgoff < mpnt->vm_pgoff)
208 continue;
209
210 off = pgoff - mpnt->vm_pgoff;
211 if (off >= (mpnt->vm_end - mpnt->vm_start) >> PAGE_SHIFT)
212 continue;
213
214
215
216
217 adjust_pte(mpnt, mpnt->vm_start + (off << PAGE_SHIFT));
218 aliases ++;
219 }
220 if (aliases)
221 adjust_pte(vma, addr);
222}
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237void update_mmu_cache(struct vm_area_struct *vma, unsigned long addr, pte_t pte)
238{
239 unsigned long pfn = pte_pfn(pte);
240 struct page *page;
241
242 if (!pfn_valid(pfn))
243 return;
244 page = pfn_to_page(pfn);
245 if (page->mapping) {
246 if (test_and_clear_bit(PG_dcache_dirty, &page->flags))
247 __flush_dcache_page(page);
248
249 make_coherent(vma, addr, page);
250 }
251}
252