diff options
Diffstat (limited to 'sys/arch')
-rw-r--r-- | sys/arch/mips64/include/cpu.h | 3 | ||||
-rw-r--r-- | sys/arch/mips64/mips64/pmap.c | 12 | ||||
-rw-r--r-- | sys/arch/mips64/mips64/r4000_errata.c | 47 |
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); } } - |