sp_tlb_flush() - fix inline asm miscompiled by newer gcc versions.

As one national park director once said: "my problems start when the
dumber of my visitors meet the smarter of my bears".

Old inline asm used specific hardcoded registers "assuming that gcc
doesn't do anything funny with these".  Unfortunately now it does,
especially when this function is inlined.  We ended up restoring a
wrong context.  The result was mysterious infinite memory faults.

Rewrite in safer inline asm, so that gcc is not confused.

Many thanks to chs@ for his patience.
This commit is contained in:
uwe 2020-12-09 04:02:20 +00:00
parent 3818c9a287
commit bd65e18f86
1 changed files with 25 additions and 28 deletions

View File

@ -1,4 +1,4 @@
/* $NetBSD: pmap.c,v 1.367 2020/03/14 14:05:43 ad Exp $ */
/* $NetBSD: pmap.c,v 1.368 2020/12/09 04:02:20 uwe Exp $ */
/*
* Copyright (c) 1996
@ -56,7 +56,7 @@
*/
#include <sys/cdefs.h>
__KERNEL_RCSID(0, "$NetBSD: pmap.c,v 1.367 2020/03/14 14:05:43 ad Exp $");
__KERNEL_RCSID(0, "$NetBSD: pmap.c,v 1.368 2020/12/09 04:02:20 uwe Exp $");
#include "opt_ddb.h"
#include "opt_kgdb.h"
@ -631,42 +631,39 @@ void (*pmap_rmu_p)(struct pmap *, vaddr_t, vaddr_t, int, int);
/*
* SP versions of the tlb flush operations.
*
* Turn off traps to prevent register window overflows
* from writing user windows to the wrong stack.
* Turn off traps to prevent register window overflows from writing
* user windows to the wrong stack. Cf. tlb_flush_page_real() &c.
*/
static void
sp_tlb_flush(int va, int ctx, int lvl)
{
/*
* XXX convert %o3 (oldpsr), %o4 (SRMMU_CXR) and %o5 (old context)
* into generically named registers. right now we're assuming that
* gcc doesn't do anything funny with these registers.
*/
int opsr, octx;
/* Traps off */
__asm("rd %psr, %o3");
__asm("wr %%o3, %0, %%psr" :: "n" (PSR_ET));
/* Save context */
__asm("mov %0, %%o4" :: "n"(SRMMU_CXR));
__asm("lda [%%o4]%0, %%o5" :: "n"(ASI_SRMMU));
/* Set new context and flush type bits */
va &= ~0xfff;
__asm("sta %1, [%%o4]%0" :: "n"(ASI_SRMMU), "r"(ctx));
va |= lvl;
/* Do the TLB flush */
__asm("sta %%g0, [%1]%0" :: "n"(ASI_SRMMUFP), "r"(va));
/*
* Turn off traps.
*
* Like setpsr((opsr = getpsr()) & ~PSR_ET); but we can shave
* off one instruction b/c we never disable traps recursively,
* so we can use the xor done by wrpsr itself to clear the
* bit.
*
* XXX: Add to asm.h? We can use this in cache.c too.
*/
opsr = getpsr(); /* KDASSERT(opsr & PSR_ET); */
__asm volatile ("wr %0, %1, %%psr" :: "r"(opsr), "n"(PSR_ET));
__asm volatile ("nop; nop;nop");
/* Restore context */
__asm("sta %%o5, [%%o4]%0" :: "n"(ASI_SRMMU));
octx = getcontext4m(); /* save context */
/* and turn traps on again */
__asm("wr %o3, 0, %psr");
__asm("nop");
__asm("nop");
__asm("nop");
/* Do the TLB flush in "ctx" */
setcontext4m(ctx);
__asm volatile("sta %%g0, [%0]%1" :: "r"(va), "n"(ASI_SRMMUFP));
setcontext4m(octx); /* restore context */
setpsr(opsr); /* turn traps on again */
}
static inline void