diff mbox

target-arm: Correct handling of writes to CPSR mode bits from gdb in usermode

Message ID 1456764438-30015-1-git-send-email-peter.maydell@linaro.org (mailing list archive)
State New, archived
Headers show

Commit Message

Peter Maydell Feb. 29, 2016, 4:47 p.m. UTC
In helper.c the expression
  (env->uncached_cpsr & CPSR_M) != CPSR_USER
is always true; the right hand side was supposed to be ARM_CPU_MODE_USR
(an error in commit cb01d391).

Since the incorrect expression was always true, this just meant that
commit cb01d391 had no effect.

However simply changing the RHS here would reveal a logic error: if
the mode is USR we wish to completely ignore the attempt to set the
mode bits, which means that we must clear the CPSR_M bits from mask
to avoid the uncached_cpsr bits being updated at the end of the
function.

Move the condition into the correct place in the code, fix its RHS
constant, and add a comment about the fact that we must be doing a
gdbstub write if we're in user mode.

Fixes: https://bugs.launchpad.net/qemu/+bug/1550503
Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
---
 target-arm/helper.c | 11 +++++++++--
 1 file changed, 9 insertions(+), 2 deletions(-)

Comments

Sergey Fedorov Feb. 29, 2016, 7:24 p.m. UTC | #1
On 29.02.2016 19:47, Peter Maydell wrote:
> In helper.c the expression
>    (env->uncached_cpsr & CPSR_M) != CPSR_USER
> is always true; the right hand side was supposed to be ARM_CPU_MODE_USR
> (an error in commit cb01d391).
>
> Since the incorrect expression was always true, this just meant that
> commit cb01d391 had no effect.
>
> However simply changing the RHS here would reveal a logic error: if
> the mode is USR we wish to completely ignore the attempt to set the
> mode bits, which means that we must clear the CPSR_M bits from mask
> to avoid the uncached_cpsr bits being updated at the end of the
> function.
>
> Move the condition into the correct place in the code, fix its RHS
> constant, and add a comment about the fact that we must be doing a
> gdbstub write if we're in user mode.
>
> Fixes: https://bugs.launchpad.net/qemu/+bug/1550503
> Signed-off-by: Peter Maydell <peter.maydell@linaro.org>

Reviewed-by: Sergey Fedorov <serge.fdrv@gmail.com>

> ---
>   target-arm/helper.c | 11 +++++++++--
>   1 file changed, 9 insertions(+), 2 deletions(-)
>
> diff --git a/target-arm/helper.c b/target-arm/helper.c
> index 18c8296..935f13b 100644
> --- a/target-arm/helper.c
> +++ b/target-arm/helper.c
> @@ -5490,9 +5490,16 @@ void cpsr_write(CPUARMState *env, uint32_t val, uint32_t mask,
>       env->daif |= val & CPSR_AIF & mask;
>   
>       if (write_type != CPSRWriteRaw &&
> -        (env->uncached_cpsr & CPSR_M) != CPSR_USER &&
>           ((env->uncached_cpsr ^ val) & mask & CPSR_M)) {
> -        if (bad_mode_switch(env, val & CPSR_M, write_type)) {
> +        if ((env->uncached_cpsr & CPSR_M) == ARM_CPU_MODE_USR) {
> +            /* Note that we can only get here in USR mode if this is a
> +             * gdb stub write; for this case we follow the architectural
> +             * behaviour for guest writes in USR mode of ignoring an attempt
> +             * to switch mode. (Those are caught by translate.c for writes
> +             * triggered by guest instructions.)
> +             */
> +            mask &= ~CPSR_M;
> +        } else if (bad_mode_switch(env, val & CPSR_M, write_type)) {
>               /* Attempt to switch to an invalid mode: this is UNPREDICTABLE in
>                * v7, and has defined behaviour in v8:
>                *  + leave CPSR.M untouched
diff mbox

Patch

diff --git a/target-arm/helper.c b/target-arm/helper.c
index 18c8296..935f13b 100644
--- a/target-arm/helper.c
+++ b/target-arm/helper.c
@@ -5490,9 +5490,16 @@  void cpsr_write(CPUARMState *env, uint32_t val, uint32_t mask,
     env->daif |= val & CPSR_AIF & mask;
 
     if (write_type != CPSRWriteRaw &&
-        (env->uncached_cpsr & CPSR_M) != CPSR_USER &&
         ((env->uncached_cpsr ^ val) & mask & CPSR_M)) {
-        if (bad_mode_switch(env, val & CPSR_M, write_type)) {
+        if ((env->uncached_cpsr & CPSR_M) == ARM_CPU_MODE_USR) {
+            /* Note that we can only get here in USR mode if this is a
+             * gdb stub write; for this case we follow the architectural
+             * behaviour for guest writes in USR mode of ignoring an attempt
+             * to switch mode. (Those are caught by translate.c for writes
+             * triggered by guest instructions.)
+             */
+            mask &= ~CPSR_M;
+        } else if (bad_mode_switch(env, val & CPSR_M, write_type)) {
             /* Attempt to switch to an invalid mode: this is UNPREDICTABLE in
              * v7, and has defined behaviour in v8:
              *  + leave CPSR.M untouched