1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25#include <linux/signal.h>
26#include <linux/sched.h>
27#include <linux/kernel.h>
28#include <linux/errno.h>
29#include <linux/string.h>
30#include <linux/types.h>
31#include <linux/ptrace.h>
32#include <linux/mman.h>
33#include <linux/mm.h>
34#include <linux/smp.h>
35#include <linux/interrupt.h>
36#include <asm/system.h>
37#include <asm/tlb.h>
38#include <asm/io.h>
39#include <asm/uaccess.h>
40#include <asm/pgalloc.h>
41#include <asm/mmu_context.h>
42#include <cpu/registers.h>
43
44
45inline void __do_tlb_refill(unsigned long address,
46 unsigned long long is_text_not_data, pte_t *pte)
47{
48 unsigned long long ptel;
49 unsigned long long pteh=0;
50 struct tlb_info *tlbp;
51 unsigned long long next;
52
53
54 ptel = pte_val(*pte);
55
56
57
58
59 pteh = address & MMU_VPN_MASK;
60
61
62#if (NEFF == 32)
63
64 pteh = (unsigned long long)(signed long long)(signed long)pteh;
65#else
66
67 pteh = (pteh & NEFF_SIGN) ? (pteh | NEFF_MASK) : pteh;
68#endif
69
70
71 pteh |= get_asid() << PTEH_ASID_SHIFT;
72 pteh |= PTEH_VALID;
73
74
75 ptel &= _PAGE_FLAGS_HARDWARE_MASK;
76
77 tlbp = is_text_not_data ? &(cpu_data->itlb) : &(cpu_data->dtlb);
78 next = tlbp->next;
79 __flush_tlb_slot(next);
80 asm volatile ("putcfg %0,1,%2\n\n\t"
81 "putcfg %0,0,%1\n"
82 : : "r" (next), "r" (pteh), "r" (ptel) );
83
84 next += TLB_STEP;
85 if (next > tlbp->last) next = tlbp->first;
86 tlbp->next = next;
87
88}
89
90static int handle_vmalloc_fault(struct mm_struct *mm,
91 unsigned long protection_flags,
92 unsigned long long textaccess,
93 unsigned long address)
94{
95 pgd_t *dir;
96 pud_t *pud;
97 pmd_t *pmd;
98 static pte_t *pte;
99 pte_t entry;
100
101 dir = pgd_offset_k(address);
102
103 pud = pud_offset(dir, address);
104 if (pud_none_or_clear_bad(pud))
105 return 0;
106
107 pmd = pmd_offset(pud, address);
108 if (pmd_none_or_clear_bad(pmd))
109 return 0;
110
111 pte = pte_offset_kernel(pmd, address);
112 entry = *pte;
113
114 if (pte_none(entry) || !pte_present(entry))
115 return 0;
116 if ((pte_val(entry) & protection_flags) != protection_flags)
117 return 0;
118
119 __do_tlb_refill(address, textaccess, pte);
120
121 return 1;
122}
123
124static int handle_tlbmiss(struct mm_struct *mm,
125 unsigned long long protection_flags,
126 unsigned long long textaccess,
127 unsigned long address)
128{
129 pgd_t *dir;
130 pud_t *pud;
131 pmd_t *pmd;
132 pte_t *pte;
133 pte_t entry;
134
135
136
137
138
139
140
141
142
143
144
145 if (address >= (unsigned long) TASK_SIZE)
146
147 return 0;
148
149 dir = pgd_offset(mm, address);
150 if (pgd_none(*dir) || !pgd_present(*dir))
151 return 0;
152 if (!pgd_present(*dir))
153 return 0;
154
155 pud = pud_offset(dir, address);
156 if (pud_none(*pud) || !pud_present(*pud))
157 return 0;
158
159 pmd = pmd_offset(pud, address);
160 if (pmd_none(*pmd) || !pmd_present(*pmd))
161 return 0;
162
163 pte = pte_offset_kernel(pmd, address);
164 entry = *pte;
165
166 if (pte_none(entry) || !pte_present(entry))
167 return 0;
168
169
170
171
172
173
174
175 if ((pte_val(entry) & protection_flags) != protection_flags)
176 return 0;
177
178 __do_tlb_refill(address, textaccess, pte);
179
180 return 1;
181}
182
183
184
185
186
187
188struct expevt_lookup {
189 unsigned short protection_flags[8];
190 unsigned char is_text_access[8];
191 unsigned char is_write_access[8];
192};
193
194#define PRU (1<<9)
195#define PRW (1<<8)
196#define PRX (1<<7)
197#define PRR (1<<6)
198
199#define DIRTY (_PAGE_DIRTY | _PAGE_ACCESSED)
200#define YOUNG (_PAGE_ACCESSED)
201
202
203
204static struct expevt_lookup expevt_lookup_table = {
205 .protection_flags = {PRX, PRX, 0, 0, PRR, PRR, PRW, PRW},
206 .is_text_access = {1, 1, 0, 0, 0, 0, 0, 0}
207};
208
209
210
211
212
213
214
215
216
217asmlinkage int do_fast_page_fault(unsigned long long ssr_md,
218 unsigned long long expevt,
219 unsigned long address)
220{
221 struct task_struct *tsk;
222 struct mm_struct *mm;
223 unsigned long long textaccess;
224 unsigned long long protection_flags;
225 unsigned long long index;
226 unsigned long long expevt4;
227
228
229
230
231
232
233
234
235
236
237 expevt4 = (expevt >> 4);
238
239
240 index = expevt4 ^ (expevt4 >> 5);
241 index &= 7;
242 protection_flags = expevt_lookup_table.protection_flags[index];
243 textaccess = expevt_lookup_table.is_text_access[index];
244
245
246
247
248
249
250
251
252
253
254
255
256 tsk = current;
257 mm = tsk->mm;
258
259 if ((address >= VMALLOC_START && address < VMALLOC_END) ||
260 (address >= IOBASE_VADDR && address < IOBASE_END)) {
261 if (ssr_md)
262
263
264
265
266 if (handle_vmalloc_fault(mm, protection_flags,
267 textaccess, address))
268 return 1;
269 } else if (!in_interrupt() && mm) {
270 if (handle_tlbmiss(mm, protection_flags, textaccess, address))
271 return 1;
272 }
273
274 return 0;
275}
276