target/arm: Take an exception if PC is misaligned
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> Reviewed-by: Peter Maydell <peter.maydell@linaro.org> Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
This commit is contained in:
parent
936a6b8603
commit
ee03027a2c
@ -113,10 +113,10 @@ void cpu_loop(CPUARMState *env)
|
|||||||
break;
|
break;
|
||||||
case EXCP_PREFETCH_ABORT:
|
case EXCP_PREFETCH_ABORT:
|
||||||
case EXCP_DATA_ABORT:
|
case EXCP_DATA_ABORT:
|
||||||
/* We should only arrive here with EC in {DATAABORT, INSNABORT}. */
|
|
||||||
ec = syn_get_ec(env->exception.syndrome);
|
ec = syn_get_ec(env->exception.syndrome);
|
||||||
assert(ec == EC_DATAABORT || ec == EC_INSNABORT);
|
switch (ec) {
|
||||||
|
case EC_DATAABORT:
|
||||||
|
case EC_INSNABORT:
|
||||||
/* Both EC have the same format for FSC, or close enough. */
|
/* Both EC have the same format for FSC, or close enough. */
|
||||||
fsc = extract32(env->exception.syndrome, 0, 6);
|
fsc = extract32(env->exception.syndrome, 0, 6);
|
||||||
switch (fsc) {
|
switch (fsc) {
|
||||||
@ -140,6 +140,14 @@ void cpu_loop(CPUARMState *env)
|
|||||||
default:
|
default:
|
||||||
g_assert_not_reached();
|
g_assert_not_reached();
|
||||||
}
|
}
|
||||||
|
break;
|
||||||
|
case EC_PCALIGNMENT:
|
||||||
|
si_signo = TARGET_SIGBUS;
|
||||||
|
si_code = TARGET_BUS_ADRALN;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
g_assert_not_reached();
|
||||||
|
}
|
||||||
force_sig_fault(si_signo, si_code, env->exception.vaddress);
|
force_sig_fault(si_signo, si_code, env->exception.vaddress);
|
||||||
break;
|
break;
|
||||||
case EXCP_DEBUG:
|
case EXCP_DEBUG:
|
||||||
|
@ -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_2(exception_internal, void, env, i32)
|
||||||
DEF_HELPER_4(exception_with_syndrome, void, env, i32, i32, 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_bkpt_insn, void, env, i32)
|
||||||
|
DEF_HELPER_2(exception_pc_alignment, noreturn, env, tl)
|
||||||
DEF_HELPER_1(setend, void, env)
|
DEF_HELPER_1(setend, void, env)
|
||||||
DEF_HELPER_2(wfi, void, env, i32)
|
DEF_HELPER_2(wfi, void, env, i32)
|
||||||
DEF_HELPER_1(wfe, void, env)
|
DEF_HELPER_1(wfe, void, env)
|
||||||
|
@ -282,4 +282,9 @@ static inline uint32_t syn_illegalstate(void)
|
|||||||
return (EC_ILLEGALSTATE << ARM_EL_EC_SHIFT) | ARM_EL_IL;
|
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 */
|
#endif /* TARGET_ARM_SYNDROME_H */
|
||||||
|
@ -9,6 +9,7 @@
|
|||||||
#include "cpu.h"
|
#include "cpu.h"
|
||||||
#include "internals.h"
|
#include "internals.h"
|
||||||
#include "exec/exec-all.h"
|
#include "exec/exec-all.h"
|
||||||
|
#include "exec/helper-proto.h"
|
||||||
|
|
||||||
static inline uint32_t merge_syn_data_abort(uint32_t template_syn,
|
static inline uint32_t merge_syn_data_abort(uint32_t template_syn,
|
||||||
unsigned int target_el,
|
unsigned int target_el,
|
||||||
@ -134,6 +135,23 @@ void arm_cpu_do_unaligned_access(CPUState *cs, vaddr vaddr,
|
|||||||
arm_deliver_fault(cpu, vaddr, access_type, mmu_idx, &fi);
|
arm_deliver_fault(cpu, vaddr, access_type, mmu_idx, &fi);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void helper_exception_pc_alignment(CPUARMState *env, target_ulong pc)
|
||||||
|
{
|
||||||
|
ARMMMUFaultInfo fi = { .type = ARMFault_Alignment };
|
||||||
|
int target_el = exception_target_el(env);
|
||||||
|
int mmu_idx = cpu_mmu_index(env, true);
|
||||||
|
uint32_t fsc;
|
||||||
|
|
||||||
|
env->exception.vaddress = pc;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Note that the fsc is not applicable to this exception,
|
||||||
|
* since any syndrome is pcalignment not insn_abort.
|
||||||
|
*/
|
||||||
|
env->exception.fsr = compute_fsr_fsc(env, &fi, target_el, mmu_idx, &fsc);
|
||||||
|
raise_exception(env, EXCP_PREFETCH_ABORT, syn_pcalignment(), target_el);
|
||||||
|
}
|
||||||
|
|
||||||
#if !defined(CONFIG_USER_ONLY)
|
#if !defined(CONFIG_USER_ONLY)
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -14753,6 +14753,7 @@ static void aarch64_tr_translate_insn(DisasContextBase *dcbase, CPUState *cpu)
|
|||||||
uint64_t pc = s->base.pc_next;
|
uint64_t pc = s->base.pc_next;
|
||||||
uint32_t insn;
|
uint32_t insn;
|
||||||
|
|
||||||
|
/* Singlestep exceptions have the highest priority. */
|
||||||
if (s->ss_active && !s->pstate_ss) {
|
if (s->ss_active && !s->pstate_ss) {
|
||||||
/* Singlestep state is Active-pending.
|
/* Singlestep state is Active-pending.
|
||||||
* If we're in this state at the start of a TB then either
|
* If we're in this state at the start of a TB then either
|
||||||
@ -14771,6 +14772,20 @@ static void aarch64_tr_translate_insn(DisasContextBase *dcbase, CPUState *cpu)
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (pc & 3) {
|
||||||
|
/*
|
||||||
|
* PC alignment fault. This has priority over the instruction abort
|
||||||
|
* that we would receive from a translation fault via arm_ldl_code.
|
||||||
|
* This should only be possible after an indirect branch, at the
|
||||||
|
* start of the TB.
|
||||||
|
*/
|
||||||
|
assert(s->base.num_insns == 1);
|
||||||
|
gen_helper_exception_pc_alignment(cpu_env, tcg_constant_tl(pc));
|
||||||
|
s->base.is_jmp = DISAS_NORETURN;
|
||||||
|
s->base.pc_next = QEMU_ALIGN_UP(pc, 4);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
s->pc_curr = pc;
|
s->pc_curr = pc;
|
||||||
insn = arm_ldl_code(env, &s->base, pc, s->sctlr_b);
|
insn = arm_ldl_code(env, &s->base, pc, s->sctlr_b);
|
||||||
s->insn = insn;
|
s->insn = insn;
|
||||||
|
@ -9555,7 +9555,27 @@ static void arm_tr_translate_insn(DisasContextBase *dcbase, CPUState *cpu)
|
|||||||
uint32_t pc = dc->base.pc_next;
|
uint32_t pc = dc->base.pc_next;
|
||||||
unsigned int insn;
|
unsigned int insn;
|
||||||
|
|
||||||
if (arm_check_ss_active(dc) || arm_check_kernelpage(dc)) {
|
/* Singlestep exceptions have the highest priority. */
|
||||||
|
if (arm_check_ss_active(dc)) {
|
||||||
|
dc->base.pc_next = pc + 4;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (pc & 3) {
|
||||||
|
/*
|
||||||
|
* PC alignment fault. This has priority over the instruction abort
|
||||||
|
* that we would receive from a translation fault via arm_ldl_code
|
||||||
|
* (or the execution of the kernelpage entrypoint). This should only
|
||||||
|
* be possible after an indirect branch, at the start of the TB.
|
||||||
|
*/
|
||||||
|
assert(dc->base.num_insns == 1);
|
||||||
|
gen_helper_exception_pc_alignment(cpu_env, tcg_constant_tl(pc));
|
||||||
|
dc->base.is_jmp = DISAS_NORETURN;
|
||||||
|
dc->base.pc_next = QEMU_ALIGN_UP(pc, 4);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (arm_check_kernelpage(dc)) {
|
||||||
dc->base.pc_next = pc + 4;
|
dc->base.pc_next = pc + 4;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user