diff options
Diffstat (limited to 'sys/mips/mips/tlb.c')
-rw-r--r-- | sys/mips/mips/tlb.c | 75 |
1 files changed, 74 insertions, 1 deletions
diff --git a/sys/mips/mips/tlb.c b/sys/mips/mips/tlb.c index dff5b63..2c1fcb5 100644 --- a/sys/mips/mips/tlb.c +++ b/sys/mips/mips/tlb.c @@ -35,7 +35,7 @@ #include <sys/smp.h> #include <vm/vm.h> -#include <vm/vm_page.h> +#include <vm/pmap.h> #include <machine/pte.h> #include <machine/tlb.h> @@ -187,6 +187,79 @@ tlb_invalidate_all_user(struct pmap *pmap) intr_restore(s); } +/* + * Invalidates any TLB entries that map a virtual page from the specified + * address range. If "end" is zero, then every virtual page is considered to + * be within the address range's upper bound. + */ +void +tlb_invalidate_range(pmap_t pmap, vm_offset_t start, vm_offset_t end) +{ + register_t asid, end_hi, hi, hi_pagemask, s, save_asid, start_hi; + int i; + + KASSERT(start < end || (end == 0 && start > 0), + ("tlb_invalidate_range: invalid range")); + + /* + * Truncate the virtual address "start" to an even page frame number, + * and round the virtual address "end" to an even page frame number. + */ + start &= ~((1 << TLBMASK_SHIFT) - 1); + end = (end + (1 << TLBMASK_SHIFT) - 1) & ~((1 << TLBMASK_SHIFT) - 1); + + s = intr_disable(); + save_asid = mips_rd_entryhi() & TLBHI_ASID_MASK; + + asid = pmap_asid(pmap); + start_hi = TLBHI_ENTRY(start, asid); + end_hi = TLBHI_ENTRY(end, asid); + + /* + * Select the fastest method for invalidating the TLB entries. + */ + if (end - start < num_tlbentries << TLBMASK_SHIFT || (end == 0 && + start >= -(num_tlbentries << TLBMASK_SHIFT))) { + /* + * The virtual address range is small compared to the size of + * the TLB. Probe the TLB for each even numbered page frame + * within the virtual address range. + */ + for (hi = start_hi; hi != end_hi; hi += 1 << TLBMASK_SHIFT) { + mips_wr_pagemask(0); + mips_wr_entryhi(hi); + tlb_probe(); + i = mips_rd_index(); + if (i >= 0) + tlb_invalidate_one(i); + } + } else { + /* + * The virtual address range is large compared to the size of + * the TLB. Test every non-wired TLB entry. + */ + for (i = mips_rd_wired(); i < num_tlbentries; i++) { + mips_wr_index(i); + tlb_read(); + hi = mips_rd_entryhi(); + if ((hi & TLBHI_ASID_MASK) == asid && (hi < end_hi || + end == 0)) { + /* + * If "hi" is a large page that spans + * "start_hi", then it must be invalidated. + */ + hi_pagemask = mips_rd_pagemask(); + if (hi >= (start_hi & ~(hi_pagemask << + TLBMASK_SHIFT))) + tlb_invalidate_one(i); + } + } + } + + mips_wr_entryhi(save_asid); + intr_restore(s); +} + /* XXX Only if DDB? */ void tlb_save(void) |