2
* Copyright (c) 2003-2004 Jakub Jermar
5
* Redistribution and use in source and binary forms, with or without
6
* modification, are permitted provided that the following conditions
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.
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.
29
/** @addtogroup mips64mm
35
#include <arch/mm/tlb.h>
43
#include <synch/mutex.h>
47
#include <interrupt.h>
52
* Invalidate all entries and mark wired entries.
55
void tlb_arch_init(void)
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);
62
/* Clear and initialize TLB. */
64
for (unsigned int i = 0; i < TLB_ENTRY_COUNT; i++) {
70
* The kernel is going to make use of some wired
71
* entries (e.g. mapping kernel stacks in kseg3).
73
cp0_wired_write(TLB_WIRED);
76
/** Try to find PTE for faulting address.
78
* @param badvaddr Faulting virtual address.
79
* @param access Access mode that caused the fault.
80
* @param istate Pointer to interrupted state.
81
* @param pfrc Pointer to variable where as_page_fault()
82
* return code will be stored.
84
* @return PTE on success, NULL otherwise.
87
static pte_t *find_mapping_and_check(uintptr_t badvaddr, int access,
88
istate_t *istate, int *pfrc)
91
hi.value = cp0_entry_hi_read();
94
* Handler cannot succeed if the ASIDs don't match.
96
if (hi.asid != AS->asid) {
97
printf("EntryHi.asid=%d, AS->asid=%d\n", hi.asid, AS->asid);
102
* Check if the mapping exists in page tables.
104
pte_t *pte = page_mapping_find(AS, badvaddr, true);
105
if ((pte) && (pte->p) && ((pte->w) || (access != PF_ACCESS_WRITE))) {
107
* Mapping found in page tables.
108
* Immediately succeed.
115
* Mapping not found in page tables.
116
* Resort to higher-level page fault handler.
118
switch (rc = as_page_fault(badvaddr, access, istate)) {
121
* The higher-level page fault handler succeeded,
122
* The mapping ought to be in place.
124
pte = page_mapping_find(AS, badvaddr, true);
127
ASSERT((pte->w) || (access != PF_ACCESS_WRITE));
136
panic("Unexpected return code (%d).", rc);
141
void tlb_prepare_entry_lo(entry_lo_t *lo, bool g, bool v, bool d,
142
bool c, uintptr_t addr)
148
lo->c = c ? PAGE_CACHEABLE_EXC_WRITE : PAGE_UNCACHED;
149
lo->pfn = ADDR2PFN(addr);
152
void tlb_prepare_entry_hi(entry_hi_t *hi, asid_t asid, uintptr_t addr)
154
hi->value = ALIGN_DOWN(addr, PAGE_SIZE * 2);
158
static void tlb_refill_fail(istate_t *istate)
160
uintptr_t va = cp0_badvaddr_read();
162
fault_if_from_uspace(istate, "TLB Refill Exception on %p.",
164
panic_memtrap(istate, PF_ACCESS_UNKNOWN, va, "TLB Refill Exception.");
167
static void tlb_invalid_fail(istate_t *istate)
169
uintptr_t va = cp0_badvaddr_read();
171
fault_if_from_uspace(istate, "TLB Invalid Exception on %p.",
173
panic_memtrap(istate, PF_ACCESS_UNKNOWN, va, "TLB Invalid Exception.");
176
static void tlb_modified_fail(istate_t *istate)
178
uintptr_t va = cp0_badvaddr_read();
180
fault_if_from_uspace(istate, "TLB Modified Exception on %p.",
182
panic_memtrap(istate, PF_ACCESS_WRITE, va, "TLB Modified Exception.");
185
/** Process TLB Refill Exception.
187
* @param istate Interrupted register context.
190
void tlb_refill(istate_t *istate)
192
uintptr_t badvaddr = cp0_badvaddr_read();
194
mutex_lock(&AS->lock);
195
asid_t asid = AS->asid;
196
mutex_unlock(&AS->lock);
199
pte_t *pte = find_mapping_and_check(badvaddr, PF_ACCESS_READ,
208
* The page fault came during copy_from_uspace()
209
* or copy_to_uspace().
213
panic("Unexpected pfrc (%d).", pfrc);
218
* Record access to PTE.
225
tlb_prepare_entry_hi(&hi, asid, badvaddr);
226
tlb_prepare_entry_lo(&lo, pte->g, pte->p, pte->d, pte->c,
230
* New entry is to be inserted into TLB
232
cp0_entry_hi_write(hi.value);
234
if ((badvaddr / PAGE_SIZE) % 2 == 0) {
235
cp0_entry_lo0_write(lo.value);
236
cp0_entry_lo1_write(0);
238
cp0_entry_lo0_write(0);
239
cp0_entry_lo1_write(lo.value);
242
cp0_pagemask_write(TLB_PAGE_MASK_16K);
248
tlb_refill_fail(istate);
251
/** Process TLB Invalid Exception.
253
* @param istate Interrupted register context.
256
void tlb_invalid(istate_t *istate)
258
uintptr_t badvaddr = cp0_badvaddr_read();
261
* Locate the faulting entry in TLB.
264
hi.value = cp0_entry_hi_read();
266
tlb_prepare_entry_hi(&hi, hi.asid, badvaddr);
267
cp0_entry_hi_write(hi.value);
271
index.value = cp0_index_read();
274
* Fail if the entry is not in TLB.
277
printf("TLB entry not found.\n");
282
pte_t *pte = find_mapping_and_check(badvaddr, PF_ACCESS_READ,
291
* The page fault came during copy_from_uspace()
292
* or copy_to_uspace().
296
panic("Unexpected pfrc (%d).", pfrc);
301
* Read the faulting TLB entry.
306
* Record access to PTE.
311
tlb_prepare_entry_lo(&lo, pte->g, pte->p, pte->d, pte->c,
315
* The entry is to be updated in TLB.
317
if ((badvaddr / PAGE_SIZE) % 2 == 0)
318
cp0_entry_lo0_write(lo.value);
320
cp0_entry_lo1_write(lo.value);
322
cp0_pagemask_write(TLB_PAGE_MASK_16K);
328
tlb_invalid_fail(istate);
331
/** Process TLB Modified Exception.
333
* @param istate Interrupted register context.
336
void tlb_modified(istate_t *istate)
338
uintptr_t badvaddr = cp0_badvaddr_read();
341
* Locate the faulting entry in TLB.
344
hi.value = cp0_entry_hi_read();
346
tlb_prepare_entry_hi(&hi, hi.asid, badvaddr);
347
cp0_entry_hi_write(hi.value);
351
index.value = cp0_index_read();
354
* Fail if the entry is not in TLB.
357
printf("TLB entry not found.\n");
362
pte_t *pte = find_mapping_and_check(badvaddr, PF_ACCESS_WRITE,
371
* The page fault came during copy_from_uspace()
372
* or copy_to_uspace().
376
panic("Unexpected pfrc (%d).", pfrc);
381
* Read the faulting TLB entry.
386
* Record access and write to PTE.
392
tlb_prepare_entry_lo(&lo, pte->g, pte->p, pte->w, pte->c,
396
* The entry is to be updated in TLB.
398
if ((badvaddr / PAGE_SIZE) % 2 == 0)
399
cp0_entry_lo0_write(lo.value);
401
cp0_entry_lo1_write(lo.value);
403
cp0_pagemask_write(TLB_PAGE_MASK_16K);
409
tlb_modified_fail(istate);
412
/** Print contents of TLB. */
416
hi_save.value = cp0_entry_hi_read();
418
printf("[nr] [asid] [vpn2] [mask] [gvdc] [pfn ]\n");
420
for (unsigned int i = 0; i < TLB_ENTRY_COUNT; i++) {
425
mask.value = cp0_pagemask_read();
428
hi.value = cp0_entry_hi_read();
431
lo0.value = cp0_entry_lo0_read();
434
lo1.value = cp0_entry_lo1_read();
436
printf("%-4u %-6u %#6x %#6x %1u%1u%1u%1u %#6x\n",
437
i, hi.asid, hi.vpn2, mask.mask,
438
lo0.g, lo0.v, lo0.d, lo0.c, lo0.pfn);
439
printf(" %1u%1u%1u%1u %#6x\n",
440
lo1.g, lo1.v, lo1.d, lo1.c, lo1.pfn);
443
cp0_entry_hi_write(hi_save.value);
446
/** Invalidate all not wired TLB entries. */
447
void tlb_invalidate_all(void)
450
hi_save.value = cp0_entry_hi_read();
451
ipl_t ipl = interrupts_disable();
453
for (unsigned int i = TLB_WIRED; i < TLB_ENTRY_COUNT; i++) {
458
lo0.value = cp0_entry_lo0_read();
461
lo1.value = cp0_entry_lo1_read();
466
cp0_entry_lo0_write(lo0.value);
467
cp0_entry_lo1_write(lo1.value);
472
interrupts_restore(ipl);
473
cp0_entry_hi_write(hi_save.value);
476
/** Invalidate all TLB entries belonging to specified address space.
478
* @param asid Address space identifier.
481
void tlb_invalidate_asid(asid_t asid)
483
ASSERT(asid != ASID_INVALID);
486
hi_save.value = cp0_entry_hi_read();
487
ipl_t ipl = interrupts_disable();
489
for (unsigned int i = 0; i < TLB_ENTRY_COUNT; i++) {
494
hi.value = cp0_entry_hi_read();
496
if (hi.asid == asid) {
498
lo0.value = cp0_entry_lo0_read();
501
lo1.value = cp0_entry_lo1_read();
506
cp0_entry_lo0_write(lo0.value);
507
cp0_entry_lo1_write(lo1.value);
513
interrupts_restore(ipl);
514
cp0_entry_hi_write(hi_save.value);
517
/** Invalidate TLB entries for specified page range belonging to specified
520
* @param asid Address space identifier.
521
* @param page First page whose TLB entry is to be invalidated.
522
* @param cnt Number of entries to invalidate.
525
void tlb_invalidate_pages(asid_t asid, uintptr_t page, size_t cnt)
527
if (asid == ASID_INVALID)
531
hi_save.value = cp0_entry_hi_read();
532
ipl_t ipl = interrupts_disable();
534
for (unsigned int i = 0; i < cnt + 1; i += 2) {
537
tlb_prepare_entry_hi(&hi, asid, page + i * PAGE_SIZE);
538
cp0_entry_hi_write(hi.value);
543
index.value = cp0_index_read();
547
* Entry was found, index register contains valid
553
lo0.value = cp0_entry_lo0_read();
556
lo1.value = cp0_entry_lo1_read();
561
cp0_entry_lo0_write(lo0.value);
562
cp0_entry_lo1_write(lo1.value);
568
interrupts_restore(ipl);
569
cp0_entry_hi_write(hi_save.value);