Message ID | 20210817201843.3829-1-peter.maydell@linaro.org (mailing list archive) |
---|---|
State | New, archived |
Headers | show |
Series | target/arm: Do hflags rebuild in cpsr_write() | expand |
On Tue, 17 Aug 2021 at 21:18, Peter Maydell <peter.maydell@linaro.org> wrote: > > Currently we rely on all the callsites of cpsr_write() to rebuild the > cached hflags if they change one of the CPSR bits which we use as a > TB flag and cache in hflags. This is a bit awkward when we want to > change the set of CPSR bits that we cache, because it means we need > to re-audit all the cpsr_write() callsites to see which flags they > are writing and whether they now need to rebuild the hflags. > > Switch instead to making cpsr_write() call arm_rebuild_hflags() > itself if one of the bits being changed is a cached bit. > > We don't do the rebuild for the CPSRWriteRaw write type, Doh. I said this, but then... > diff --git a/target/arm/helper.c b/target/arm/helper.c > index 201ecf8c67f..cdd6e0858fc 100644 > --- a/target/arm/helper.c > +++ b/target/arm/helper.c > @@ -9215,6 +9215,7 @@ void cpsr_write(CPUARMState *env, uint32_t val, uint32_t mask, > CPSRWriteType write_type) > { > uint32_t changed_daif; > + bool rebuild_hflags = mask & (CPSR_M | CPSR_E | CPSR_IL); ...forgot to actually check the write type. Should be: bool rebuild_hflags = (write_type != CPSRWriteRaw) && (mask & (CPSR_M | CPSR_E | CPSR_IL)); > if (mask & CPSR_NZCV) { > env->ZF = (~val) & CPSR_Z; > @@ -9334,6 +9335,9 @@ void cpsr_write(CPUARMState *env, uint32_t val, uint32_t mask, > } > mask &= ~CACHED_CPSR_BITS; > env->uncached_cpsr = (env->uncached_cpsr & ~mask) | (val & mask); > + if (rebuild_hflags) { > + arm_rebuild_hflags(env); > + } > } > > /* Sign/zero extend */ > -- -- PMM
On 8/17/21 10:26 AM, Peter Maydell wrote: > On Tue, 17 Aug 2021 at 21:18, Peter Maydell <peter.maydell@linaro.org> wrote: >> >> Currently we rely on all the callsites of cpsr_write() to rebuild the >> cached hflags if they change one of the CPSR bits which we use as a >> TB flag and cache in hflags. This is a bit awkward when we want to >> change the set of CPSR bits that we cache, because it means we need >> to re-audit all the cpsr_write() callsites to see which flags they >> are writing and whether they now need to rebuild the hflags. >> >> Switch instead to making cpsr_write() call arm_rebuild_hflags() >> itself if one of the bits being changed is a cached bit. >> >> We don't do the rebuild for the CPSRWriteRaw write type, > > Doh. I said this, but then... > >> diff --git a/target/arm/helper.c b/target/arm/helper.c >> index 201ecf8c67f..cdd6e0858fc 100644 >> --- a/target/arm/helper.c >> +++ b/target/arm/helper.c >> @@ -9215,6 +9215,7 @@ void cpsr_write(CPUARMState *env, uint32_t val, uint32_t mask, >> CPSRWriteType write_type) >> { >> uint32_t changed_daif; >> + bool rebuild_hflags = mask & (CPSR_M | CPSR_E | CPSR_IL); > > ...forgot to actually check the write type. > > Should be: > > bool rebuild_hflags = (write_type != CPSRWriteRaw) && > (mask & (CPSR_M | CPSR_E | CPSR_IL)); with the fix, Reviewed-by: Richard Henderson <richard.henderson@linaro.org> r~
diff --git a/target/arm/cpu.h b/target/arm/cpu.h index be557bf5d83..4c0568f85a8 100644 --- a/target/arm/cpu.h +++ b/target/arm/cpu.h @@ -1393,11 +1393,17 @@ uint32_t cpsr_read(CPUARMState *env); typedef enum CPSRWriteType { CPSRWriteByInstr = 0, /* from guest MSR or CPS */ CPSRWriteExceptionReturn = 1, /* from guest exception return insn */ - CPSRWriteRaw = 2, /* trust values, do not switch reg banks */ + CPSRWriteRaw = 2, + /* trust values, no reg bank switch, no hflags rebuild */ CPSRWriteByGDBStub = 3, /* from the GDB stub */ } CPSRWriteType; -/* Set the CPSR. Note that some bits of mask must be all-set or all-clear.*/ +/* + * Set the CPSR. Note that some bits of mask must be all-set or all-clear. + * This will do an arm_rebuild_hflags() if any of the bits in @mask + * correspond to TB flags bits cached in the hflags, unless @write_type + * is CPSRWriteRaw. + */ void cpsr_write(CPUARMState *env, uint32_t val, uint32_t mask, CPSRWriteType write_type); diff --git a/linux-user/arm/signal.c b/linux-user/arm/signal.c index 32b68ee302b..1dfcfd2d57b 100644 --- a/linux-user/arm/signal.c +++ b/linux-user/arm/signal.c @@ -289,7 +289,6 @@ setup_return(CPUARMState *env, struct target_sigaction *ka, env->regs[14] = retcode; env->regs[15] = handler & (thumb ? ~1 : ~3); cpsr_write(env, cpsr, CPSR_IT | CPSR_T | CPSR_E, CPSRWriteByInstr); - arm_rebuild_hflags(env); return 0; } @@ -547,7 +546,6 @@ restore_sigcontext(CPUARMState *env, struct target_sigcontext *sc) __get_user(env->regs[15], &sc->arm_pc); __get_user(cpsr, &sc->arm_cpsr); cpsr_write(env, cpsr, CPSR_USER | CPSR_EXEC, CPSRWriteByInstr); - arm_rebuild_hflags(env); err |= !valid_user_regs(env); diff --git a/target/arm/helper.c b/target/arm/helper.c index 201ecf8c67f..cdd6e0858fc 100644 --- a/target/arm/helper.c +++ b/target/arm/helper.c @@ -9215,6 +9215,7 @@ void cpsr_write(CPUARMState *env, uint32_t val, uint32_t mask, CPSRWriteType write_type) { uint32_t changed_daif; + bool rebuild_hflags = mask & (CPSR_M | CPSR_E | CPSR_IL); if (mask & CPSR_NZCV) { env->ZF = (~val) & CPSR_Z; @@ -9334,6 +9335,9 @@ void cpsr_write(CPUARMState *env, uint32_t val, uint32_t mask, } mask &= ~CACHED_CPSR_BITS; env->uncached_cpsr = (env->uncached_cpsr & ~mask) | (val & mask); + if (rebuild_hflags) { + arm_rebuild_hflags(env); + } } /* Sign/zero extend */
Currently we rely on all the callsites of cpsr_write() to rebuild the cached hflags if they change one of the CPSR bits which we use as a TB flag and cache in hflags. This is a bit awkward when we want to change the set of CPSR bits that we cache, because it means we need to re-audit all the cpsr_write() callsites to see which flags they are writing and whether they now need to rebuild the hflags. Switch instead to making cpsr_write() call arm_rebuild_hflags() itself if one of the bits being changed is a cached bit. We don't do the rebuild for the CPSRWriteRaw write type, because that kind of write is generally doing something special anyway. For the CPSRWriteRaw callsites in the KVM code and inbound migration we definitely don't want to recalculate the hflags; the callsites in boot.c and arm-powerctl.c have to do a rebuild-hflags call themselves anyway because of other CPU state changes they make. This allows us to drop explicit arm_rebuild_hflags() calls in a couple of places where the only reason we needed to call it was the CPSR write. This fixes a bug where we were incorrectly failing to rebuild hflags in the code path for a gdbstub write to CPSR, which meant that you could make QEMU assert by breaking into a running guest, altering the CPSR to change the value of, for example, CPSR.E, and then continuing. Signed-off-by: Peter Maydell <peter.maydell@linaro.org> --- I've included CPSR.IL on the list of "cached bits" on the assumption that my "honour PSTATE.IL" patch is going in, but this patch doesn't strictly depend on that one. Just in case, though: Based-on: id:20210817162118.24319-1-peter.maydell@linaro.org "[PATCH] target/arm: Take an exception if PSTATE.IL is set" target/arm/cpu.h | 10 ++++++++-- linux-user/arm/signal.c | 2 -- target/arm/helper.c | 4 ++++ 3 files changed, 12 insertions(+), 4 deletions(-)