source: mainline/kernel/arch/mips64/src/mm/tlb.c@ 1dbc43f

lfn serial ticket/834-toolchain-update topic/msim-upgrade topic/simplify-dev-export
Last change on this file since 1dbc43f was 1dbc43f, checked in by Jakub Jermar <jakub@…>, 13 years ago

Unify user page fault handling in as_page_fault().

  • Remove lots of architecture-dependent boilerplate code.
  • Property mode set to 100644
File size: 9.6 KB
Line 
1/*
2 * Copyright (c) 2003-2004 Jakub Jermar
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 *
9 * - Redistributions of source code must retain the above copyright
10 * notice, this list of conditions and the following disclaimer.
11 * - Redistributions in binary form must reproduce the above copyright
12 * notice, this list of conditions and the following disclaimer in the
13 * documentation and/or other materials provided with the distribution.
14 * - The name of the author may not be used to endorse or promote products
15 * derived from this software without specific prior written permission.
16 *
17 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
18 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
19 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
20 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
21 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
22 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
23 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
24 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
26 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27 */
28
29/** @addtogroup mips64mm
30 * @{
31 */
32/** @file
33 */
34
35#include <arch/mm/tlb.h>
36#include <mm/asid.h>
37#include <mm/tlb.h>
38#include <mm/page.h>
39#include <mm/as.h>
40#include <arch/cp0.h>
41#include <panic.h>
42#include <arch.h>
43#include <synch/mutex.h>
44#include <print.h>
45#include <debug.h>
46#include <align.h>
47#include <interrupt.h>
48#include <symtab.h>
49
50/** Initialize TLB.
51 *
52 * Invalidate all entries and mark wired entries.
53 *
54 */
55void tlb_arch_init(void)
56{
57 cp0_pagemask_write(TLB_PAGE_MASK_16K);
58 cp0_entry_hi_write(0);
59 cp0_entry_lo0_write(0);
60 cp0_entry_lo1_write(0);
61
62 /* Clear and initialize TLB. */
63
64 for (unsigned int i = 0; i < TLB_ENTRY_COUNT; i++) {
65 cp0_index_write(i);
66 tlbwi();
67 }
68
69 /*
70 * The kernel is going to make use of some wired
71 * entries (e.g. mapping kernel stacks in kseg3).
72 */
73 cp0_wired_write(TLB_WIRED);
74}
75
76/** Try to find PTE for faulting address.
77 *
78 * @param badvaddr Faulting virtual address.
79 * @param access Access mode that caused the fault.
80 * @param istate Pointer to interrupted state.
81 *
82 * @return PTE on success, NULL otherwise.
83 *
84 */
85static pte_t *find_mapping_and_check(uintptr_t badvaddr, int access,
86 istate_t *istate)
87{
88 entry_hi_t hi;
89 hi.value = cp0_entry_hi_read();
90
91 ASSERT(hi.asid == AS->asid);
92
93 /*
94 * Check if the mapping exists in page tables.
95 */
96 pte_t *pte = page_mapping_find(AS, badvaddr, true);
97 if ((pte) && (pte->p) && ((pte->w) || (access != PF_ACCESS_WRITE))) {
98 /*
99 * Mapping found in page tables.
100 * Immediately succeed.
101 */
102 return pte;
103 }
104
105 /*
106 * Mapping not found in page tables.
107 * Resort to higher-level page fault handler.
108 */
109 if (as_page_fault(badvaddr, access, istate) == AS_PF_OK) {
110 /*
111 * The higher-level page fault handler succeeded,
112 * The mapping ought to be in place.
113 */
114 pte = page_mapping_find(AS, badvaddr, true);
115 ASSERT(pte);
116 ASSERT(pte->p);
117 ASSERT((pte->w) || (access != PF_ACCESS_WRITE));
118 return pte;
119 }
120
121 return NULL;
122}
123
124void tlb_prepare_entry_lo(entry_lo_t *lo, bool g, bool v, bool d,
125 bool c, uintptr_t addr)
126{
127 lo->value = 0;
128 lo->g = g;
129 lo->v = v;
130 lo->d = d;
131 lo->c = c ? PAGE_CACHEABLE_EXC_WRITE : PAGE_UNCACHED;
132 lo->pfn = ADDR2PFN(addr);
133}
134
135void tlb_prepare_entry_hi(entry_hi_t *hi, asid_t asid, uintptr_t addr)
136{
137 hi->value = ALIGN_DOWN(addr, PAGE_SIZE * 2);
138 hi->asid = asid;
139}
140
141/** Process TLB Refill Exception.
142 *
143 * @param istate Interrupted register context.
144 *
145 */
146void tlb_refill(istate_t *istate)
147{
148 uintptr_t badvaddr = cp0_badvaddr_read();
149
150 mutex_lock(&AS->lock);
151 asid_t asid = AS->asid;
152 mutex_unlock(&AS->lock);
153
154 pte_t *pte = find_mapping_and_check(badvaddr, PF_ACCESS_READ, istate);
155 if (pte) {
156 /*
157 * Record access to PTE.
158 */
159 pte->a = 1;
160
161 entry_lo_t lo;
162 entry_hi_t hi;
163
164 tlb_prepare_entry_hi(&hi, asid, badvaddr);
165 tlb_prepare_entry_lo(&lo, pte->g, pte->p, pte->d, pte->c,
166 pte->frame);
167
168 /*
169 * New entry is to be inserted into TLB
170 */
171 cp0_entry_hi_write(hi.value);
172
173 if ((badvaddr / PAGE_SIZE) % 2 == 0) {
174 cp0_entry_lo0_write(lo.value);
175 cp0_entry_lo1_write(0);
176 } else {
177 cp0_entry_lo0_write(0);
178 cp0_entry_lo1_write(lo.value);
179 }
180
181 cp0_pagemask_write(TLB_PAGE_MASK_16K);
182 tlbwr();
183 }
184}
185
186/** Process TLB Invalid Exception.
187 *
188 * @param istate Interrupted register context.
189 *
190 */
191void tlb_invalid(istate_t *istate)
192{
193 uintptr_t badvaddr = cp0_badvaddr_read();
194
195 /*
196 * Locate the faulting entry in TLB.
197 */
198 entry_hi_t hi;
199 hi.value = cp0_entry_hi_read();
200
201 tlb_prepare_entry_hi(&hi, hi.asid, badvaddr);
202 cp0_entry_hi_write(hi.value);
203 tlbp();
204
205 tlb_index_t index;
206 index.value = cp0_index_read();
207
208 ASSERT(!index.p);
209
210 pte_t *pte = find_mapping_and_check(badvaddr, PF_ACCESS_READ, istate);
211 if (pte) {
212 /*
213 * Read the faulting TLB entry.
214 */
215 tlbr();
216
217 /*
218 * Record access to PTE.
219 */
220 pte->a = 1;
221
222 entry_lo_t lo;
223 tlb_prepare_entry_lo(&lo, pte->g, pte->p, pte->d, pte->c,
224 pte->frame);
225
226 /*
227 * The entry is to be updated in TLB.
228 */
229 if ((badvaddr / PAGE_SIZE) % 2 == 0)
230 cp0_entry_lo0_write(lo.value);
231 else
232 cp0_entry_lo1_write(lo.value);
233
234 cp0_pagemask_write(TLB_PAGE_MASK_16K);
235 tlbwi();
236 }
237
238}
239
240/** Process TLB Modified Exception.
241 *
242 * @param istate Interrupted register context.
243 *
244 */
245void tlb_modified(istate_t *istate)
246{
247 uintptr_t badvaddr = cp0_badvaddr_read();
248
249 /*
250 * Locate the faulting entry in TLB.
251 */
252 entry_hi_t hi;
253 hi.value = cp0_entry_hi_read();
254
255 tlb_prepare_entry_hi(&hi, hi.asid, badvaddr);
256 cp0_entry_hi_write(hi.value);
257 tlbp();
258
259 tlb_index_t index;
260 index.value = cp0_index_read();
261
262 ASSERT(!index.p);
263
264 pte_t *pte = find_mapping_and_check(badvaddr, PF_ACCESS_WRITE, istate);
265 if (pte) {
266 /*
267 * Read the faulting TLB entry.
268 */
269 tlbr();
270
271 /*
272 * Record access and write to PTE.
273 */
274 pte->a = 1;
275 pte->d = 1;
276
277 entry_lo_t lo;
278 tlb_prepare_entry_lo(&lo, pte->g, pte->p, pte->w, pte->c,
279 pte->frame);
280
281 /*
282 * The entry is to be updated in TLB.
283 */
284 if ((badvaddr / PAGE_SIZE) % 2 == 0)
285 cp0_entry_lo0_write(lo.value);
286 else
287 cp0_entry_lo1_write(lo.value);
288
289 cp0_pagemask_write(TLB_PAGE_MASK_16K);
290 tlbwi();
291 }
292}
293
294/** Print contents of TLB. */
295void tlb_print(void)
296{
297 entry_hi_t hi_save;
298 hi_save.value = cp0_entry_hi_read();
299
300 printf("[nr] [asid] [vpn2] [mask] [gvdc] [pfn ]\n");
301
302 for (unsigned int i = 0; i < TLB_ENTRY_COUNT; i++) {
303 cp0_index_write(i);
304 tlbr();
305
306 page_mask_t mask;
307 mask.value = cp0_pagemask_read();
308
309 entry_hi_t hi;
310 hi.value = cp0_entry_hi_read();
311
312 entry_lo_t lo0;
313 lo0.value = cp0_entry_lo0_read();
314
315 entry_lo_t lo1;
316 lo1.value = cp0_entry_lo1_read();
317
318 printf("%-4u %-6u %#6x %#6x %1u%1u%1u%1u %#6x\n",
319 i, hi.asid, hi.vpn2, mask.mask,
320 lo0.g, lo0.v, lo0.d, lo0.c, lo0.pfn);
321 printf(" %1u%1u%1u%1u %#6x\n",
322 lo1.g, lo1.v, lo1.d, lo1.c, lo1.pfn);
323 }
324
325 cp0_entry_hi_write(hi_save.value);
326}
327
328/** Invalidate all not wired TLB entries. */
329void tlb_invalidate_all(void)
330{
331 entry_hi_t hi_save;
332 hi_save.value = cp0_entry_hi_read();
333 ipl_t ipl = interrupts_disable();
334
335 for (unsigned int i = TLB_WIRED; i < TLB_ENTRY_COUNT; i++) {
336 cp0_index_write(i);
337 tlbr();
338
339 entry_lo_t lo0;
340 lo0.value = cp0_entry_lo0_read();
341
342 entry_lo_t lo1;
343 lo1.value = cp0_entry_lo1_read();
344
345 lo0.v = 0;
346 lo1.v = 0;
347
348 cp0_entry_lo0_write(lo0.value);
349 cp0_entry_lo1_write(lo1.value);
350
351 tlbwi();
352 }
353
354 interrupts_restore(ipl);
355 cp0_entry_hi_write(hi_save.value);
356}
357
358/** Invalidate all TLB entries belonging to specified address space.
359 *
360 * @param asid Address space identifier.
361 *
362 */
363void tlb_invalidate_asid(asid_t asid)
364{
365 ASSERT(asid != ASID_INVALID);
366
367 entry_hi_t hi_save;
368 hi_save.value = cp0_entry_hi_read();
369 ipl_t ipl = interrupts_disable();
370
371 for (unsigned int i = 0; i < TLB_ENTRY_COUNT; i++) {
372 cp0_index_write(i);
373 tlbr();
374
375 entry_hi_t hi;
376 hi.value = cp0_entry_hi_read();
377
378 if (hi.asid == asid) {
379 entry_lo_t lo0;
380 lo0.value = cp0_entry_lo0_read();
381
382 entry_lo_t lo1;
383 lo1.value = cp0_entry_lo1_read();
384
385 lo0.v = 0;
386 lo1.v = 0;
387
388 cp0_entry_lo0_write(lo0.value);
389 cp0_entry_lo1_write(lo1.value);
390
391 tlbwi();
392 }
393 }
394
395 interrupts_restore(ipl);
396 cp0_entry_hi_write(hi_save.value);
397}
398
399/** Invalidate TLB entries for specified page range belonging to specified
400 * address space.
401 *
402 * @param asid Address space identifier.
403 * @param page First page whose TLB entry is to be invalidated.
404 * @param cnt Number of entries to invalidate.
405 *
406 */
407void tlb_invalidate_pages(asid_t asid, uintptr_t page, size_t cnt)
408{
409 if (asid == ASID_INVALID)
410 return;
411
412 entry_hi_t hi_save;
413 hi_save.value = cp0_entry_hi_read();
414 ipl_t ipl = interrupts_disable();
415
416 for (unsigned int i = 0; i < cnt + 1; i += 2) {
417 entry_hi_t hi;
418 hi.value = 0;
419 tlb_prepare_entry_hi(&hi, asid, page + i * PAGE_SIZE);
420 cp0_entry_hi_write(hi.value);
421
422 tlbp();
423
424 tlb_index_t index;
425 index.value = cp0_index_read();
426
427 if (!index.p) {
428 /*
429 * Entry was found, index register contains valid
430 * index.
431 */
432 tlbr();
433
434 entry_lo_t lo0;
435 lo0.value = cp0_entry_lo0_read();
436
437 entry_lo_t lo1;
438 lo1.value = cp0_entry_lo1_read();
439
440 lo0.v = 0;
441 lo1.v = 0;
442
443 cp0_entry_lo0_write(lo0.value);
444 cp0_entry_lo1_write(lo1.value);
445
446 tlbwi();
447 }
448 }
449
450 interrupts_restore(ipl);
451 cp0_entry_hi_write(hi_save.value);
452}
453
454/** @}
455 */
Note: See TracBrowser for help on using the repository browser.