diff options
author | Peter Maydell <peter.maydell@linaro.org> | 2016-10-04 13:28:10 +0100 |
---|---|---|
committer | Peter Maydell <peter.maydell@linaro.org> | 2016-10-04 13:28:10 +0100 |
commit | 9b6a3ea7a699594162ed3d11e4e04b98568dc5c0 (patch) | |
tree | 764d96006cedc488328f6c346115d4e2e423bd0e | |
parent | 173ff58580b383a7841b18fddb293038c9d40d1c (diff) |
target-arm: Correctly handle 'sub pc, pc, 1' for ARMv6pull-target-arm-20161004
In the ARM v6 architecture, 'sub pc, pc, 1' is not an interworking
branch, so the computed new value is written to r15 as a normal
value. The architecture says that in this case, bits [1:0] of
the value written must be ignored if we are in ARM mode (or
bit [0] ignored if in Thumb mode); this is a change from the
ARMv4/v5 specification that behaviour is UNPREDICTABLE.
Use the correct mask on the PC value when doing a non-interworking
store to PC.
A popular library used on RaspberryPi uses this instruction
as part of a trick to determine whether it is running on
ARMv6 or ARMv7, and we were mishandling the sequence.
Fixes bug: https://bugs.launchpad.net/bugs/1625295
Reported-by: <stu.axon@gmail.com>
Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
Message-id: 1474380941-4730-1-git-send-email-peter.maydell@linaro.org
-rw-r--r-- | target-arm/translate.c | 7 |
1 files changed, 6 insertions, 1 deletions
diff --git a/target-arm/translate.c b/target-arm/translate.c index 693d4bc6a2..8df24bf35a 100644 --- a/target-arm/translate.c +++ b/target-arm/translate.c @@ -180,7 +180,12 @@ static inline TCGv_i32 load_reg(DisasContext *s, int reg) static void store_reg(DisasContext *s, int reg, TCGv_i32 var) { if (reg == 15) { - tcg_gen_andi_i32(var, var, ~1); + /* In Thumb mode, we must ignore bit 0. + * In ARM mode, for ARMv4 and ARMv5, it is UNPREDICTABLE if bits [1:0] + * are not 0b00, but for ARMv6 and above, we must ignore bits [1:0]. + * We choose to ignore [1:0] in ARM mode for all architecture versions. + */ + tcg_gen_andi_i32(var, var, s->thumb ? ~1 : ~3); s->is_jmp = DISAS_JUMP; } tcg_gen_mov_i32(cpu_R[reg], var); |