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

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

Modify the hierarchy of page fault handlers to pass access mode that caused the fault.
Architectures are required to pass either PF_ACCESS_READ, PF_ACCESS_WRITE or PF_ACCESS_EXEC
to as_page_fault(), depending on the cause of the fault.

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