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