All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH] target/arm: Do hflags rebuild in cpsr_write()
@ 2021-08-17 20:18 Peter Maydell
  2021-08-17 20:26 ` Peter Maydell
  0 siblings, 1 reply; 3+ messages in thread
From: Peter Maydell @ 2021-08-17 20:18 UTC (permalink / raw)
  To: qemu-arm, qemu-devel; +Cc: Richard Henderson

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(-)

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 */
-- 
2.20.1



^ permalink raw reply related	[flat|nested] 3+ messages in thread

* Re: [PATCH] target/arm: Do hflags rebuild in cpsr_write()
  2021-08-17 20:18 [PATCH] target/arm: Do hflags rebuild in cpsr_write() Peter Maydell
@ 2021-08-17 20:26 ` Peter Maydell
  2021-08-17 20:41   ` Richard Henderson
  0 siblings, 1 reply; 3+ messages in thread
From: Peter Maydell @ 2021-08-17 20:26 UTC (permalink / raw)
  To: qemu-arm, QEMU Developers; +Cc: Richard Henderson

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


^ permalink raw reply	[flat|nested] 3+ messages in thread

* Re: [PATCH] target/arm: Do hflags rebuild in cpsr_write()
  2021-08-17 20:26 ` Peter Maydell
@ 2021-08-17 20:41   ` Richard Henderson
  0 siblings, 0 replies; 3+ messages in thread
From: Richard Henderson @ 2021-08-17 20:41 UTC (permalink / raw)
  To: Peter Maydell, qemu-arm, QEMU Developers

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~


^ permalink raw reply	[flat|nested] 3+ messages in thread

end of thread, other threads:[~2021-08-17 20:42 UTC | newest]

Thread overview: 3+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2021-08-17 20:18 [PATCH] target/arm: Do hflags rebuild in cpsr_write() Peter Maydell
2021-08-17 20:26 ` Peter Maydell
2021-08-17 20:41   ` Richard Henderson

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.