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

lfn serial ticket/834-toolchain-update topic/msim-upgrade topic/simplify-dev-export
Last change on this file since 2d01bbd was 2d01bbd, checked in by Ondrej Palkovsky <ondrap@…>, 19 years ago

Fixed shrinking of as area.

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