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