summaryrefslogtreecommitdiff
path: root/sys/arch/mips64
diff options
context:
space:
mode:
authorMiod Vallat <miod@cvs.openbsd.org>2014-04-04 20:52:06 +0000
committerMiod Vallat <miod@cvs.openbsd.org>2014-04-04 20:52:06 +0000
commit6c2d14612151ea5d0cab49976c6e35cdc38768c4 (patch)
tree8e4d6c4731d6395e8ea5ccb3cbbca6a202f5e634 /sys/arch/mips64
parent33b1013f864554568f49136fe0e3dacbd172161e (diff)
Second step of the R4000 EOP errata WAR: when pmap invalidates a page which
is currently being covered by the wired TLB entries, flush them, so that, if the process' pc is still running in a vulnerable page, the WAR will reapply immediately and fault the next page.
Diffstat (limited to 'sys/arch/mips64')
-rw-r--r--sys/arch/mips64/include/cpu.h3
-rw-r--r--sys/arch/mips64/mips64/pmap.c12
-rw-r--r--sys/arch/mips64/mips64/r4000_errata.c47
3 files changed, 51 insertions, 11 deletions
diff --git a/sys/arch/mips64/include/cpu.h b/sys/arch/mips64/include/cpu.h
index d705d91367b..a7665714035 100644
--- a/sys/arch/mips64/include/cpu.h
+++ b/sys/arch/mips64/include/cpu.h
@@ -1,4 +1,4 @@
-/* $OpenBSD: cpu.h,v 1.100 2014/03/31 20:21:19 miod Exp $ */
+/* $OpenBSD: cpu.h,v 1.101 2014/04/04 20:52:05 miod Exp $ */
/*-
* Copyright (c) 1992, 1993
@@ -444,6 +444,7 @@ int classify_insn(uint32_t);
extern int r4000_errata;
u_int eop_page_check(paddr_t);
+void eop_tlb_flush_addr(struct pmap *, vaddr_t, u_long);
int eop_tlb_miss_handler(struct trap_frame *, struct cpu_info *,
struct proc *);
void eop_cleanup(struct trap_frame *, struct proc *);
diff --git a/sys/arch/mips64/mips64/pmap.c b/sys/arch/mips64/mips64/pmap.c
index 059861d06f2..23e65e37c7b 100644
--- a/sys/arch/mips64/mips64/pmap.c
+++ b/sys/arch/mips64/mips64/pmap.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: pmap.c,v 1.73 2014/03/31 20:21:19 miod Exp $ */
+/* $OpenBSD: pmap.c,v 1.74 2014/04/04 20:52:05 miod Exp $ */
/*
* Copyright (c) 2001-2004 Opsycon AB (www.opsycon.se / www.opsycon.com)
@@ -319,8 +319,14 @@ pmap_invalidate_user_page(pmap_t pmap, vaddr_t va)
u_long asid = pmap->pm_asid[cpuid].pma_asid << PG_ASID_SHIFT;
if (pmap->pm_asid[cpuid].pma_asidgen ==
- pmap_asid_info[cpuid].pma_asidgen)
- tlb_flush_addr(va | asid);
+ pmap_asid_info[cpuid].pma_asidgen) {
+#ifdef CPU_R4000
+ if (r4000_errata != 0)
+ eop_tlb_flush_addr(pmap, va, asid);
+ else
+#endif
+ tlb_flush_addr(va | asid);
+ }
}
void
diff --git a/sys/arch/mips64/mips64/r4000_errata.c b/sys/arch/mips64/mips64/r4000_errata.c
index 9811e1d841c..f0e6af8e69d 100644
--- a/sys/arch/mips64/mips64/r4000_errata.c
+++ b/sys/arch/mips64/mips64/r4000_errata.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: r4000_errata.c,v 1.3 2014/04/03 08:07:16 mpi Exp $ */
+/* $OpenBSD: r4000_errata.c,v 1.4 2014/04/04 20:52:05 miod Exp $ */
/*
* Copyright (c) 2014 Miodrag Vallat.
@@ -87,6 +87,16 @@
int r4000_errata;
+static inline void eop_undo(struct pcb *);
+
+static inline void
+eop_undo(struct pcb *pcb)
+{
+ tlb_set_wired(UPAGES / 2);
+ tlb_flush((UPAGES / 2) + pcb->pcb_nwired);
+ pcb->pcb_nwired = 0;
+}
+
/*
* Check for an R4000 end-of-page errata condition in an executable code page.
* Returns a bitmask to set in the given page pg_flags.
@@ -104,6 +114,33 @@ eop_page_check(paddr_t pa)
}
/*
+ * Invalidate a TLB entry. If it is part of a wired pair, drop all wired
+ * entries.
+ *
+ * Note that, in case of heavy swapping, this can cause the page following
+ * a vulnerable page to be swapped out and immediately faulted back in,
+ * iff the userland pc is in the vulnerable page. Help me, Obi Wan LRU.
+ * You are my only hope.
+ */
+void
+eop_tlb_flush_addr(struct pmap *pmap, vaddr_t va, u_long asid)
+{
+ struct proc *p = curproc;
+ struct pcb *pcb;
+
+ if (p->p_vmspace->vm_map.pmap == pmap) {
+ pcb = &p->p_addr->u_pcb;
+ if (pcb->pcb_nwired != 0 &&
+ (va - pcb->pcb_wiredva) < ptoa(pcb->pcb_nwired * 2)) {
+ eop_undo(pcb);
+ return;
+ }
+ }
+
+ tlb_flush_addr(va | asid);
+}
+
+/*
* Handle a TLB miss exception for a page marked as able to trigger the
* end-of-page errata.
* Returns nonzero if the exception has been completely serviced, and no
@@ -235,11 +272,7 @@ eop_cleanup(struct trap_frame *trapframe, struct proc *p)
pcb = &p->p_addr->u_pcb;
if (pcb->pcb_nwired != 0) {
- if (trunc_page(trapframe->pc) != pcb->pcb_wiredpc) {
- tlb_set_wired(UPAGES / 2);
- tlb_flush((UPAGES / 2) + pcb->pcb_nwired);
- pcb->pcb_nwired = 0;
- }
+ if (trunc_page(trapframe->pc) != pcb->pcb_wiredpc)
+ eop_undo(pcb);
}
}
-