aboutsummaryrefslogtreecommitdiff
path: root/target/arm/translate.c
diff options
context:
space:
mode:
Diffstat (limited to 'target/arm/translate.c')
-rw-r--r--target/arm/translate.c21
1 files changed, 20 insertions, 1 deletions
diff --git a/target/arm/translate.c b/target/arm/translate.c
index cfebd35d26..615859e23c 100644
--- a/target/arm/translate.c
+++ b/target/arm/translate.c
@@ -915,10 +915,27 @@ static inline void gen_bx(DisasContext *s, TCGv_i32 var)
store_cpu_field(var, thumb);
}
-/* Set PC and Thumb state from var. var is marked as dead.
+/*
+ * Set PC and Thumb state from var. var is marked as dead.
* For M-profile CPUs, include logic to detect exception-return
* branches and handle them. This is needed for Thumb POP/LDM to PC, LDR to PC,
* and BX reg, and no others, and happens only for code in Handler mode.
+ * The Security Extension also requires us to check for the FNC_RETURN
+ * which signals a function return from non-secure state; this can happen
+ * in both Handler and Thread mode.
+ * To avoid having to do multiple comparisons in inline generated code,
+ * we make the check we do here loose, so it will match for EXC_RETURN
+ * in Thread mode. For system emulation do_v7m_exception_exit() checks
+ * for these spurious cases and returns without doing anything (giving
+ * the same behaviour as for a branch to a non-magic address).
+ *
+ * In linux-user mode it is unclear what the right behaviour for an
+ * attempted FNC_RETURN should be, because in real hardware this will go
+ * directly to Secure code (ie not the Linux kernel) which will then treat
+ * the error in any way it chooses. For QEMU we opt to make the FNC_RETURN
+ * attempt behave the way it would on a CPU without the security extension,
+ * which is to say "like a normal branch". That means we can simply treat
+ * all branches as normal with no magic address behaviour.
*/
static inline void gen_bx_excret(DisasContext *s, TCGv_i32 var)
{
@@ -926,10 +943,12 @@ static inline void gen_bx_excret(DisasContext *s, TCGv_i32 var)
* s->base.is_jmp that we need to do the rest of the work later.
*/
gen_bx(s, var);
+#ifndef CONFIG_USER_ONLY
if (arm_dc_feature(s, ARM_FEATURE_M_SECURITY) ||
(s->v7m_handler_mode && arm_dc_feature(s, ARM_FEATURE_M))) {
s->base.is_jmp = DISAS_BX_EXCRET;
}
+#endif
}
static inline void gen_bx_excret_final_code(DisasContext *s)