diff options
author | Peter Maydell | 2016-10-10 17:26:03 +0200 |
---|---|---|
committer | Peter Maydell | 2016-10-17 20:29:03 +0200 |
commit | fb0e8e79a9d77ee240dbca036fa8698ce654e5d1 (patch) | |
tree | 9de784284190dd03427f560273d4ac44928e6f11 /target-arm/op_helper.c | |
parent | target-arm: Comments added to identify cases in a switch (diff) | |
download | qemu-fb0e8e79a9d77ee240dbca036fa8698ce654e5d1.tar.gz qemu-fb0e8e79a9d77ee240dbca036fa8698ce654e5d1.tar.xz qemu-fb0e8e79a9d77ee240dbca036fa8698ce654e5d1.zip |
Fix masking of PC lower bits when doing exception returns
In commit 9b6a3ea7a699594 store_reg() was changed to mask
both bits 0 and 1 of the new PC value when in ARM mode.
Unfortunately this broke the exception return code paths
when doing a return from ARM mode to Thumb mode: in some
of these we write a new CPSR including new Thumb mode
bit via gen_helper_cpsr_write_eret(), and then use store_reg()
to write the new PC. In this case if the new CPSR specified
Thumb mode then masking bit 1 of the PC is incorrect
(these code paths correspond to the v8 ARM ARM pseudocode
function AArch32.ExceptionReturn(), which always aligns the
new PC appropriately for the new instruction set state).
Instead of using store_reg() in exception-return code paths,
call a new store_pc_exc_ret() which stores the raw new PC
value to env->regs[15], and then mask it appropriately in
the subsequent helper_cpsr_write_eret() where the new
env->thumb state is available.
This fixes a bug introduced by 9b6a3ea7a699594 which caused
crashes/hangs or otherwise bad behaviour for Linux when
userspace was using Thumb.
Reported-by: Jerome Forissier <jerome.forissier@linaro.org>
Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
Message-id: 1476113163-24578-1-git-send-email-peter.maydell@linaro.org
Diffstat (limited to 'target-arm/op_helper.c')
-rw-r--r-- | target-arm/op_helper.c | 7 |
1 files changed, 7 insertions, 0 deletions
diff --git a/target-arm/op_helper.c b/target-arm/op_helper.c index be27b21d52..cd94216591 100644 --- a/target-arm/op_helper.c +++ b/target-arm/op_helper.c @@ -479,6 +479,13 @@ void HELPER(cpsr_write_eret)(CPUARMState *env, uint32_t val) { cpsr_write(env, val, CPSR_ERET_MASK, CPSRWriteExceptionReturn); + /* Generated code has already stored the new PC value, but + * without masking out its low bits, because which bits need + * masking depends on whether we're returning to Thumb or ARM + * state. Do the masking now. + */ + env->regs[15] &= (env->thumb ? ~1 : ~3); + arm_call_el_change_hook(arm_env_get_cpu(env)); } |