summaryrefslogtreecommitdiffstats
path: root/sys/mips/mips/tlb.c
diff options
context:
space:
mode:
Diffstat (limited to 'sys/mips/mips/tlb.c')
-rw-r--r--sys/mips/mips/tlb.c75
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)
OpenPOWER on IntegriCloud