source: mainline/arch/mips32/src/mm/tlb.c@ 2299914

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

Page table locking.

  • Property mode set to 100644
File size: 11.2 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#include <arch/mm/tlb.h>
30#include <mm/asid.h>
31#include <mm/tlb.h>
32#include <mm/page.h>
33#include <mm/as.h>
34#include <arch/cp0.h>
35#include <panic.h>
36#include <arch.h>
37#include <symtab.h>
38#include <synch/spinlock.h>
39#include <print.h>
40#include <debug.h>
41#include <align.h>
42
43static void tlb_refill_fail(istate_t *istate);
44static void tlb_invalid_fail(istate_t *istate);
45static void tlb_modified_fail(istate_t *istate);
46
47static pte_t *find_mapping_and_check(__address badvaddr);
48
49static void prepare_entry_lo(entry_lo_t *lo, bool g, bool v, bool d, bool cacheable, __address pfn);
50static void prepare_entry_hi(entry_hi_t *hi, asid_t asid, __address addr);
51
52/** Initialize TLB
53 *
54 * Initialize TLB.
55 * Invalidate all entries and mark wired entries.
56 */
57void tlb_arch_init(void)
58{
59 int i;
60
61 cp0_pagemask_write(TLB_PAGE_MASK_16K);
62 cp0_entry_hi_write(0);
63 cp0_entry_lo0_write(0);
64 cp0_entry_lo1_write(0);
65
66 /* Clear and initialize TLB. */
67
68 for (i = 0; i < TLB_ENTRY_COUNT; i++) {
69 cp0_index_write(i);
70 tlbwi();
71 }
72
73
74 /*
75 * The kernel is going to make use of some wired
76 * entries (e.g. mapping kernel stacks in kseg3).
77 */
78 cp0_wired_write(TLB_WIRED);
79}
80
81/** Process TLB Refill Exception
82 *
83 * Process TLB Refill Exception.
84 *
85 * @param istate Interrupted register context.
86 */
87void tlb_refill(istate_t *istate)
88{
89 entry_lo_t lo;
90 entry_hi_t hi;
91 asid_t asid;
92 __address badvaddr;
93 pte_t *pte;
94
95 badvaddr = cp0_badvaddr_read();
96
97 spinlock_lock(&AS->lock);
98 asid = AS->asid;
99 spinlock_unlock(&AS->lock);
100
101 page_table_lock(AS, true);
102
103 pte = find_mapping_and_check(badvaddr);
104 if (!pte)
105 goto fail;
106
107 /*
108 * Record access to PTE.
109 */
110 pte->a = 1;
111
112 prepare_entry_hi(&hi, asid, badvaddr);
113 prepare_entry_lo(&lo, pte->g, pte->p, pte->d, pte->cacheable, pte->pfn);
114
115 /*
116 * New entry is to be inserted into TLB
117 */
118 cp0_entry_hi_write(hi.value);
119 if ((badvaddr/PAGE_SIZE) % 2 == 0) {
120 cp0_entry_lo0_write(lo.value);
121 cp0_entry_lo1_write(0);
122 }
123 else {
124 cp0_entry_lo0_write(0);
125 cp0_entry_lo1_write(lo.value);
126 }
127 cp0_pagemask_write(TLB_PAGE_MASK_16K);
128 tlbwr();
129
130 page_table_unlock(AS, true);
131 return;
132
133fail:
134 page_table_unlock(AS, true);
135 tlb_refill_fail(istate);
136}
137
138/** Process TLB Invalid Exception
139 *
140 * Process TLB Invalid Exception.
141 *
142 * @param istate Interrupted register context.
143 */
144void tlb_invalid(istate_t *istate)
145{
146 tlb_index_t index;
147 __address badvaddr;
148 entry_lo_t lo;
149 entry_hi_t hi;
150 pte_t *pte;
151
152 badvaddr = cp0_badvaddr_read();
153
154 /*
155 * Locate the faulting entry in TLB.
156 */
157 hi.value = cp0_entry_hi_read();
158 prepare_entry_hi(&hi, hi.asid, badvaddr);
159 cp0_entry_hi_write(hi.value);
160 tlbp();
161 index.value = cp0_index_read();
162
163 page_table_lock(AS, true);
164
165 /*
166 * Fail if the entry is not in TLB.
167 */
168 if (index.p) {
169 printf("TLB entry not found.\n");
170 goto fail;
171 }
172
173 pte = find_mapping_and_check(badvaddr);
174 if (!pte)
175 goto fail;
176
177 /*
178 * Read the faulting TLB entry.
179 */
180 tlbr();
181
182 /*
183 * Record access to PTE.
184 */
185 pte->a = 1;
186
187 prepare_entry_lo(&lo, pte->g, pte->p, pte->d, pte->cacheable, pte->pfn);
188
189 /*
190 * The entry is to be updated in TLB.
191 */
192 if ((badvaddr/PAGE_SIZE) % 2 == 0)
193 cp0_entry_lo0_write(lo.value);
194 else
195 cp0_entry_lo1_write(lo.value);
196 cp0_pagemask_write(TLB_PAGE_MASK_16K);
197 tlbwi();
198
199 page_table_unlock(AS, true);
200 return;
201
202fail:
203 page_table_unlock(AS, true);
204 tlb_invalid_fail(istate);
205}
206
207/** Process TLB Modified Exception
208 *
209 * Process TLB Modified Exception.
210 *
211 * @param istate Interrupted register context.
212 */
213void tlb_modified(istate_t *istate)
214{
215 tlb_index_t index;
216 __address badvaddr;
217 entry_lo_t lo;
218 entry_hi_t hi;
219 pte_t *pte;
220
221 badvaddr = cp0_badvaddr_read();
222
223 /*
224 * Locate the faulting entry in TLB.
225 */
226 hi.value = cp0_entry_hi_read();
227 prepare_entry_hi(&hi, hi.asid, badvaddr);
228 cp0_entry_hi_write(hi.value);
229 tlbp();
230 index.value = cp0_index_read();
231
232 page_table_lock(AS, true);
233
234 /*
235 * Fail if the entry is not in TLB.
236 */
237 if (index.p) {
238 printf("TLB entry not found.\n");
239 goto fail;
240 }
241
242 pte = find_mapping_and_check(badvaddr);
243 if (!pte)
244 goto fail;
245
246 /*
247 * Fail if the page is not writable.
248 */
249 if (!pte->w)
250 goto fail;
251
252 /*
253 * Read the faulting TLB entry.
254 */
255 tlbr();
256
257 /*
258 * Record access and write to PTE.
259 */
260 pte->a = 1;
261 pte->d = 1;
262
263 prepare_entry_lo(&lo, pte->g, pte->p, pte->w, pte->cacheable, pte->pfn);
264
265 /*
266 * The entry is to be updated in TLB.
267 */
268 if ((badvaddr/PAGE_SIZE) % 2 == 0)
269 cp0_entry_lo0_write(lo.value);
270 else
271 cp0_entry_lo1_write(lo.value);
272 cp0_pagemask_write(TLB_PAGE_MASK_16K);
273 tlbwi();
274
275 page_table_unlock(AS, true);
276 return;
277
278fail:
279 page_table_unlock(AS, true);
280 tlb_modified_fail(istate);
281}
282
283void tlb_refill_fail(istate_t *istate)
284{
285 char *symbol = "";
286 char *sym2 = "";
287
288 char *s = get_symtab_entry(istate->epc);
289 if (s)
290 symbol = s;
291 s = get_symtab_entry(istate->ra);
292 if (s)
293 sym2 = s;
294 panic("%X: TLB Refill Exception at %X(%s<-%s)\n", cp0_badvaddr_read(), istate->epc, symbol, sym2);
295}
296
297
298void tlb_invalid_fail(istate_t *istate)
299{
300 char *symbol = "";
301
302 char *s = get_symtab_entry(istate->epc);
303 if (s)
304 symbol = s;
305 panic("%X: TLB Invalid Exception at %X(%s)\n", cp0_badvaddr_read(), istate->epc, symbol);
306}
307
308void tlb_modified_fail(istate_t *istate)
309{
310 char *symbol = "";
311
312 char *s = get_symtab_entry(istate->epc);
313 if (s)
314 symbol = s;
315 panic("%X: TLB Modified Exception at %X(%s)\n", cp0_badvaddr_read(), istate->epc, symbol);
316}
317
318/** Try to find PTE for faulting address
319 *
320 * Try to find PTE for faulting address.
321 * The AS->lock must be held on entry to this function.
322 *
323 * @param badvaddr Faulting virtual address.
324 *
325 * @return PTE on success, NULL otherwise.
326 */
327pte_t *find_mapping_and_check(__address badvaddr)
328{
329 entry_hi_t hi;
330 pte_t *pte;
331
332 hi.value = cp0_entry_hi_read();
333
334 /*
335 * Handler cannot succeed if the ASIDs don't match.
336 */
337 if (hi.asid != AS->asid) {
338 printf("EntryHi.asid=%d, AS->asid=%d\n", hi.asid, AS->asid);
339 return NULL;
340 }
341
342 /*
343 * Check if the mapping exists in page tables.
344 */
345 pte = page_mapping_find(AS, badvaddr);
346 if (pte && pte->p) {
347 /*
348 * Mapping found in page tables.
349 * Immediately succeed.
350 */
351 return pte;
352 } else {
353 /*
354 * Mapping not found in page tables.
355 * Resort to higher-level page fault handler.
356 */
357 page_table_unlock(AS, true);
358 if (as_page_fault(badvaddr)) {
359 /*
360 * The higher-level page fault handler succeeded,
361 * The mapping ought to be in place.
362 */
363 page_table_lock(AS, true);
364 pte = page_mapping_find(AS, badvaddr);
365 ASSERT(pte && pte->p);
366 return pte;
367 } else {
368 page_table_lock(AS, true);
369 printf("Page fault.\n");
370 return NULL;
371 }
372
373 }
374}
375
376void prepare_entry_lo(entry_lo_t *lo, bool g, bool v, bool d, bool cacheable, __address pfn)
377{
378 lo->value = 0;
379 lo->g = g;
380 lo->v = v;
381 lo->d = d;
382 lo->c = cacheable ? PAGE_CACHEABLE_EXC_WRITE : PAGE_UNCACHED;
383 lo->pfn = pfn;
384}
385
386void prepare_entry_hi(entry_hi_t *hi, asid_t asid, __address addr)
387{
388 hi->value = ALIGN_DOWN(addr, PAGE_SIZE * 2);
389 hi->asid = asid;
390}
391
392/** Print contents of TLB. */
393void tlb_print(void)
394{
395 page_mask_t mask;
396 entry_lo_t lo0, lo1;
397 entry_hi_t hi, hi_save;
398 int i;
399
400 hi_save.value = cp0_entry_hi_read();
401
402 printf("TLB:\n");
403 for (i = 0; i < TLB_ENTRY_COUNT; i++) {
404 cp0_index_write(i);
405 tlbr();
406
407 mask.value = cp0_pagemask_read();
408 hi.value = cp0_entry_hi_read();
409 lo0.value = cp0_entry_lo0_read();
410 lo1.value = cp0_entry_lo1_read();
411
412 printf("%d: asid=%d, vpn2=%d, mask=%d\tg[0]=%d, v[0]=%d, d[0]=%d, c[0]=%B, pfn[0]=%d\n"
413 "\t\t\t\tg[1]=%d, v[1]=%d, d[1]=%d, c[1]=%B, pfn[1]=%d\n",
414 i, hi.asid, hi.vpn2, mask.mask, lo0.g, lo0.v, lo0.d, lo0.c, lo0.pfn,
415 lo1.g, lo1.v, lo1.d, lo1.c, lo1.pfn);
416 }
417
418 cp0_entry_hi_write(hi_save.value);
419}
420
421/** Invalidate all not wired TLB entries. */
422void tlb_invalidate_all(void)
423{
424 ipl_t ipl;
425 entry_lo_t lo0, lo1;
426 entry_hi_t hi_save;
427 int i;
428
429 hi_save.value = cp0_entry_hi_read();
430 ipl = interrupts_disable();
431
432 for (i = TLB_WIRED; i < TLB_ENTRY_COUNT; i++) {
433 cp0_index_write(i);
434 tlbr();
435
436 lo0.value = cp0_entry_lo0_read();
437 lo1.value = cp0_entry_lo1_read();
438
439 lo0.v = 0;
440 lo1.v = 0;
441
442 cp0_entry_lo0_write(lo0.value);
443 cp0_entry_lo1_write(lo1.value);
444
445 tlbwi();
446 }
447
448 interrupts_restore(ipl);
449 cp0_entry_hi_write(hi_save.value);
450}
451
452/** Invalidate all TLB entries belonging to specified address space.
453 *
454 * @param asid Address space identifier.
455 */
456void tlb_invalidate_asid(asid_t asid)
457{
458 ipl_t ipl;
459 entry_lo_t lo0, lo1;
460 entry_hi_t hi, hi_save;
461 int i;
462
463 ASSERT(asid != ASID_INVALID);
464
465 hi_save.value = cp0_entry_hi_read();
466 ipl = interrupts_disable();
467
468 for (i = 0; i < TLB_ENTRY_COUNT; i++) {
469 cp0_index_write(i);
470 tlbr();
471
472 hi.value = cp0_entry_hi_read();
473
474 if (hi.asid == asid) {
475 lo0.value = cp0_entry_lo0_read();
476 lo1.value = cp0_entry_lo1_read();
477
478 lo0.v = 0;
479 lo1.v = 0;
480
481 cp0_entry_lo0_write(lo0.value);
482 cp0_entry_lo1_write(lo1.value);
483
484 tlbwi();
485 }
486 }
487
488 interrupts_restore(ipl);
489 cp0_entry_hi_write(hi_save.value);
490}
491
492/** Invalidate TLB entries for specified page range belonging to specified address space.
493 *
494 * @param asid Address space identifier.
495 * @param page First page whose TLB entry is to be invalidated.
496 * @param cnt Number of entries to invalidate.
497 */
498void tlb_invalidate_pages(asid_t asid, __address page, count_t cnt)
499{
500 int i;
501 ipl_t ipl;
502 entry_lo_t lo0, lo1;
503 entry_hi_t hi, hi_save;
504 tlb_index_t index;
505
506 ASSERT(asid != ASID_INVALID);
507
508 hi_save.value = cp0_entry_hi_read();
509 ipl = interrupts_disable();
510
511 for (i = 0; i < cnt+1; i+=2) {
512 hi.value = 0;
513 prepare_entry_hi(&hi, asid, page + i * PAGE_SIZE);
514 cp0_entry_hi_write(hi.value);
515
516 tlbp();
517 index.value = cp0_index_read();
518
519 if (!index.p) {
520 /* Entry was found, index register contains valid index. */
521 tlbr();
522
523 lo0.value = cp0_entry_lo0_read();
524 lo1.value = cp0_entry_lo1_read();
525
526 lo0.v = 0;
527 lo1.v = 0;
528
529 cp0_entry_lo0_write(lo0.value);
530 cp0_entry_lo1_write(lo1.value);
531
532 tlbwi();
533 }
534 }
535
536 interrupts_restore(ipl);
537 cp0_entry_hi_write(hi_save.value);
538}
Note: See TracBrowser for help on using the repository browser.