For A64, any input to an indirect branch can cause this.
For A32, many indirect branch paths force the branch to be aligned,
but BXWritePC does not. This includes the BX instruction but also
other interworking changes to PC. Prior to v8, this case is UNDEFINED.
With v8, this is CONSTRAINED UNPREDICTABLE and may either raise an
exception or force align the PC.
We choose to raise an exception because we have the infrastructure,
it makes the generated code for gen_bx simpler, and it has the
possibility of catching more guest bugs.
Signed-off-by: Richard Henderson <richard.henderson@linaro.org>
---
target/arm/helper.h | 1 +
target/arm/syndrome.h | 5 +++++
target/arm/tlb_helper.c | 24 +++++++++++++++++++++++
target/arm/translate-a64.c | 21 ++++++++++++++++++--
target/arm/translate.c | 39 +++++++++++++++++++++++++++++++-------
5 files changed, 81 insertions(+), 9 deletions(-)
diff --git a/target/arm/helper.h b/target/arm/helper.h
index 248569b0cd..d629ee6859 100644
--- a/target/arm/helper.h
+++ b/target/arm/helper.h
@@ -47,6 +47,7 @@ DEF_HELPER_FLAGS_3(sel_flags, TCG_CALL_NO_RWG_SE,
DEF_HELPER_2(exception_internal, void, env, i32)
DEF_HELPER_4(exception_with_syndrome, void, env, i32, i32, i32)
DEF_HELPER_2(exception_bkpt_insn, void, env, i32)
+DEF_HELPER_2(exception_pc_alignment, noreturn, env, tl)
DEF_HELPER_1(setend, void, env)
DEF_HELPER_2(wfi, void, env, i32)
DEF_HELPER_1(wfe, void, env)
diff --git a/target/arm/syndrome.h b/target/arm/syndrome.h
index 54d135897b..e9d97fac6e 100644
--- a/target/arm/syndrome.h
+++ b/target/arm/syndrome.h
@@ -275,4 +275,9 @@ static inline uint32_t syn_illegalstate(void)
return (EC_ILLEGALSTATE << ARM_EL_EC_SHIFT) | ARM_EL_IL;
}
+static inline uint32_t syn_pcalignment(void)
+{
+ return (EC_PCALIGNMENT << ARM_EL_EC_SHIFT) | ARM_EL_IL;
+}
+
#endif /* TARGET_ARM_SYNDROME_H */
diff --git a/target/arm/tlb_helper.c b/target/arm/tlb_helper.c
index 3107f9823e..25c422976e 100644
--- a/target/arm/tlb_helper.c
+++ b/target/arm/tlb_helper.c
@@ -9,6 +9,7 @@
#include "cpu.h"
#include "internals.h"
#include "exec/exec-all.h"
+#include "exec/helper-proto.h"
static inline uint32_t merge_syn_data_abort(uint32_t template_syn,
unsigned int target_el,
@@ -123,6 +124,29 @@ void arm_cpu_do_unaligned_access(CPUState *cs, vaddr vaddr,
arm_deliver_fault(cpu, vaddr, access_type, mmu_idx, &fi);
}
+void helper_exception_pc_alignment(CPUARMState *env, target_ulong pc)
+{
+ int target_el = exception_target_el(env);
+
+ if (target_el == 2 || arm_el_is_aa64(env, target_el)) {
+ /*
+ * To aarch64 and aarch32 el2, pc alignment has a
+ * special exception class.
+ */
+ env->exception.vaddress = pc;
+ env->exception.fsr = 0;
+ raise_exception(env, EXCP_PREFETCH_ABORT, syn_pcalignment(),
target_el);
+ } else {
+ /*
+ * To aarch32 el1, pc alignment is like data alignment
+ * except with a prefetch abort.
+ */
+ ARMMMUFaultInfo fi = { .type = ARMFault_Alignment };
+ arm_deliver_fault(env_archcpu(env), pc, MMU_INST_FETCH,
+ cpu_mmu_index(env, true), &fi);