mirror of https://github.com/xemu-project/xemu.git
target-arm: Implement ARMv8 single-stepping for AArch32 code
ARMv8 single-stepping requires the exception level that controls the single-stepping to be in AArch64 execution state, but the code being stepped may be in AArch64 or AArch32. Implement the necessary support code for single-stepping AArch32 code. Signed-off-by: Peter Maydell <peter.maydell@linaro.org> Reviewed-by: Edgar E. Iglesias <edgar.iglesias@xilinx.com>
This commit is contained in:
parent
7ea47fe7be
commit
50225ad0c1
|
@ -1205,6 +1205,10 @@ static inline bool arm_singlestep_active(CPUARMState *env)
|
||||||
#define ARM_TBFLAG_BSWAP_CODE_MASK (1 << ARM_TBFLAG_BSWAP_CODE_SHIFT)
|
#define ARM_TBFLAG_BSWAP_CODE_MASK (1 << ARM_TBFLAG_BSWAP_CODE_SHIFT)
|
||||||
#define ARM_TBFLAG_CPACR_FPEN_SHIFT 17
|
#define ARM_TBFLAG_CPACR_FPEN_SHIFT 17
|
||||||
#define ARM_TBFLAG_CPACR_FPEN_MASK (1 << ARM_TBFLAG_CPACR_FPEN_SHIFT)
|
#define ARM_TBFLAG_CPACR_FPEN_MASK (1 << ARM_TBFLAG_CPACR_FPEN_SHIFT)
|
||||||
|
#define ARM_TBFLAG_SS_ACTIVE_SHIFT 18
|
||||||
|
#define ARM_TBFLAG_SS_ACTIVE_MASK (1 << ARM_TBFLAG_SS_ACTIVE_SHIFT)
|
||||||
|
#define ARM_TBFLAG_PSTATE_SS_SHIFT 19
|
||||||
|
#define ARM_TBFLAG_PSTATE_SS_MASK (1 << ARM_TBFLAG_PSTATE_SS_SHIFT)
|
||||||
|
|
||||||
/* Bit usage when in AArch64 state */
|
/* Bit usage when in AArch64 state */
|
||||||
#define ARM_TBFLAG_AA64_EL_SHIFT 0
|
#define ARM_TBFLAG_AA64_EL_SHIFT 0
|
||||||
|
@ -1235,6 +1239,10 @@ static inline bool arm_singlestep_active(CPUARMState *env)
|
||||||
(((F) & ARM_TBFLAG_BSWAP_CODE_MASK) >> ARM_TBFLAG_BSWAP_CODE_SHIFT)
|
(((F) & ARM_TBFLAG_BSWAP_CODE_MASK) >> ARM_TBFLAG_BSWAP_CODE_SHIFT)
|
||||||
#define ARM_TBFLAG_CPACR_FPEN(F) \
|
#define ARM_TBFLAG_CPACR_FPEN(F) \
|
||||||
(((F) & ARM_TBFLAG_CPACR_FPEN_MASK) >> ARM_TBFLAG_CPACR_FPEN_SHIFT)
|
(((F) & ARM_TBFLAG_CPACR_FPEN_MASK) >> ARM_TBFLAG_CPACR_FPEN_SHIFT)
|
||||||
|
#define ARM_TBFLAG_SS_ACTIVE(F) \
|
||||||
|
(((F) & ARM_TBFLAG_SS_ACTIVE_MASK) >> ARM_TBFLAG_SS_ACTIVE_SHIFT)
|
||||||
|
#define ARM_TBFLAG_PSTATE_SS(F) \
|
||||||
|
(((F) & ARM_TBFLAG_PSTATE_SS_MASK) >> ARM_TBFLAG_PSTATE_SS_SHIFT)
|
||||||
#define ARM_TBFLAG_AA64_EL(F) \
|
#define ARM_TBFLAG_AA64_EL(F) \
|
||||||
(((F) & ARM_TBFLAG_AA64_EL_MASK) >> ARM_TBFLAG_AA64_EL_SHIFT)
|
(((F) & ARM_TBFLAG_AA64_EL_MASK) >> ARM_TBFLAG_AA64_EL_SHIFT)
|
||||||
#define ARM_TBFLAG_AA64_FPEN(F) \
|
#define ARM_TBFLAG_AA64_FPEN(F) \
|
||||||
|
@ -1292,6 +1300,19 @@ static inline void cpu_get_tb_cpu_state(CPUARMState *env, target_ulong *pc,
|
||||||
if (fpen == 3 || (fpen == 1 && arm_current_pl(env) != 0)) {
|
if (fpen == 3 || (fpen == 1 && arm_current_pl(env) != 0)) {
|
||||||
*flags |= ARM_TBFLAG_CPACR_FPEN_MASK;
|
*flags |= ARM_TBFLAG_CPACR_FPEN_MASK;
|
||||||
}
|
}
|
||||||
|
/* The SS_ACTIVE and PSTATE_SS bits correspond to the state machine
|
||||||
|
* states defined in the ARM ARM for software singlestep:
|
||||||
|
* SS_ACTIVE PSTATE.SS State
|
||||||
|
* 0 x Inactive (the TB flag for SS is always 0)
|
||||||
|
* 1 0 Active-pending
|
||||||
|
* 1 1 Active-not-pending
|
||||||
|
*/
|
||||||
|
if (arm_singlestep_active(env)) {
|
||||||
|
*flags |= ARM_TBFLAG_SS_ACTIVE_MASK;
|
||||||
|
if (env->uncached_cpsr & PSTATE_SS) {
|
||||||
|
*flags |= ARM_TBFLAG_PSTATE_SS_MASK;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
*cs_base = 0;
|
*cs_base = 0;
|
||||||
|
|
|
@ -205,6 +205,33 @@ static void gen_exception(int excp, uint32_t syndrome)
|
||||||
tcg_temp_free_i32(tcg_excp);
|
tcg_temp_free_i32(tcg_excp);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void gen_ss_advance(DisasContext *s)
|
||||||
|
{
|
||||||
|
/* If the singlestep state is Active-not-pending, advance to
|
||||||
|
* Active-pending.
|
||||||
|
*/
|
||||||
|
if (s->ss_active) {
|
||||||
|
s->pstate_ss = 0;
|
||||||
|
gen_helper_clear_pstate_ss(cpu_env);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void gen_step_complete_exception(DisasContext *s)
|
||||||
|
{
|
||||||
|
/* We just completed step of an insn. Move from Active-not-pending
|
||||||
|
* to Active-pending, and then also take the swstep exception.
|
||||||
|
* This corresponds to making the (IMPDEF) choice to prioritize
|
||||||
|
* swstep exceptions over asynchronous exceptions taken to an exception
|
||||||
|
* level where debug is disabled. This choice has the advantage that
|
||||||
|
* we do not need to maintain internal state corresponding to the
|
||||||
|
* ISV/EX syndrome bits between completion of the step and generation
|
||||||
|
* of the exception, and our syndrome information is always correct.
|
||||||
|
*/
|
||||||
|
gen_ss_advance(s);
|
||||||
|
gen_exception(EXCP_UDEF, syn_swstep(s->ss_same_el, 1, s->is_ldex));
|
||||||
|
s->is_jmp = DISAS_EXC;
|
||||||
|
}
|
||||||
|
|
||||||
static void gen_smul_dual(TCGv_i32 a, TCGv_i32 b)
|
static void gen_smul_dual(TCGv_i32 a, TCGv_i32 b)
|
||||||
{
|
{
|
||||||
TCGv_i32 tmp1 = tcg_temp_new_i32();
|
TCGv_i32 tmp1 = tcg_temp_new_i32();
|
||||||
|
@ -3860,7 +3887,7 @@ static inline void gen_goto_tb(DisasContext *s, int n, target_ulong dest)
|
||||||
|
|
||||||
static inline void gen_jmp (DisasContext *s, uint32_t dest)
|
static inline void gen_jmp (DisasContext *s, uint32_t dest)
|
||||||
{
|
{
|
||||||
if (unlikely(s->singlestep_enabled)) {
|
if (unlikely(s->singlestep_enabled || s->ss_active)) {
|
||||||
/* An indirect jump so that we still trigger the debug exception. */
|
/* An indirect jump so that we still trigger the debug exception. */
|
||||||
if (s->thumb)
|
if (s->thumb)
|
||||||
dest |= 1;
|
dest |= 1;
|
||||||
|
@ -7281,6 +7308,8 @@ static void gen_load_exclusive(DisasContext *s, int rt, int rt2,
|
||||||
{
|
{
|
||||||
TCGv_i32 tmp = tcg_temp_new_i32();
|
TCGv_i32 tmp = tcg_temp_new_i32();
|
||||||
|
|
||||||
|
s->is_ldex = true;
|
||||||
|
|
||||||
switch (size) {
|
switch (size) {
|
||||||
case 0:
|
case 0:
|
||||||
gen_aa32_ld8u(tmp, addr, get_mem_index(s));
|
gen_aa32_ld8u(tmp, addr, get_mem_index(s));
|
||||||
|
@ -10917,6 +10946,26 @@ static inline void gen_intermediate_code_internal(ARMCPU *cpu,
|
||||||
dc->current_pl = arm_current_pl(env);
|
dc->current_pl = arm_current_pl(env);
|
||||||
dc->features = env->features;
|
dc->features = env->features;
|
||||||
|
|
||||||
|
/* Single step state. The code-generation logic here is:
|
||||||
|
* SS_ACTIVE == 0:
|
||||||
|
* generate code with no special handling for single-stepping (except
|
||||||
|
* that anything that can make us go to SS_ACTIVE == 1 must end the TB;
|
||||||
|
* this happens anyway because those changes are all system register or
|
||||||
|
* PSTATE writes).
|
||||||
|
* SS_ACTIVE == 1, PSTATE.SS == 1: (active-not-pending)
|
||||||
|
* emit code for one insn
|
||||||
|
* emit code to clear PSTATE.SS
|
||||||
|
* emit code to generate software step exception for completed step
|
||||||
|
* end TB (as usual for having generated an exception)
|
||||||
|
* SS_ACTIVE == 1, PSTATE.SS == 0: (active-pending)
|
||||||
|
* emit code to generate a software step exception
|
||||||
|
* end the TB
|
||||||
|
*/
|
||||||
|
dc->ss_active = ARM_TBFLAG_SS_ACTIVE(tb->flags);
|
||||||
|
dc->pstate_ss = ARM_TBFLAG_PSTATE_SS(tb->flags);
|
||||||
|
dc->is_ldex = false;
|
||||||
|
dc->ss_same_el = false; /* Can't be true since EL_d must be AArch64 */
|
||||||
|
|
||||||
cpu_F0s = tcg_temp_new_i32();
|
cpu_F0s = tcg_temp_new_i32();
|
||||||
cpu_F1s = tcg_temp_new_i32();
|
cpu_F1s = tcg_temp_new_i32();
|
||||||
cpu_F0d = tcg_temp_new_i64();
|
cpu_F0d = tcg_temp_new_i64();
|
||||||
|
@ -11026,6 +11075,22 @@ static inline void gen_intermediate_code_internal(ARMCPU *cpu,
|
||||||
tcg_gen_debug_insn_start(dc->pc);
|
tcg_gen_debug_insn_start(dc->pc);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (dc->ss_active && !dc->pstate_ss) {
|
||||||
|
/* Singlestep state is Active-pending.
|
||||||
|
* If we're in this state at the start of a TB then either
|
||||||
|
* a) we just took an exception to an EL which is being debugged
|
||||||
|
* and this is the first insn in the exception handler
|
||||||
|
* b) debug exceptions were masked and we just unmasked them
|
||||||
|
* without changing EL (eg by clearing PSTATE.D)
|
||||||
|
* In either case we're going to take a swstep exception in the
|
||||||
|
* "did not step an insn" case, and so the syndrome ISV and EX
|
||||||
|
* bits should be zero.
|
||||||
|
*/
|
||||||
|
assert(num_insns == 0);
|
||||||
|
gen_exception(EXCP_UDEF, syn_swstep(dc->ss_same_el, 0, 0));
|
||||||
|
goto done_generating;
|
||||||
|
}
|
||||||
|
|
||||||
if (dc->thumb) {
|
if (dc->thumb) {
|
||||||
disas_thumb_insn(env, dc);
|
disas_thumb_insn(env, dc);
|
||||||
if (dc->condexec_mask) {
|
if (dc->condexec_mask) {
|
||||||
|
@ -11058,6 +11123,7 @@ static inline void gen_intermediate_code_internal(ARMCPU *cpu,
|
||||||
} while (!dc->is_jmp && tcg_ctx.gen_opc_ptr < gen_opc_end &&
|
} while (!dc->is_jmp && tcg_ctx.gen_opc_ptr < gen_opc_end &&
|
||||||
!cs->singlestep_enabled &&
|
!cs->singlestep_enabled &&
|
||||||
!singlestep &&
|
!singlestep &&
|
||||||
|
!dc->ss_active &&
|
||||||
dc->pc < next_page_start &&
|
dc->pc < next_page_start &&
|
||||||
num_insns < max_insns);
|
num_insns < max_insns);
|
||||||
|
|
||||||
|
@ -11073,12 +11139,15 @@ static inline void gen_intermediate_code_internal(ARMCPU *cpu,
|
||||||
/* At this stage dc->condjmp will only be set when the skipped
|
/* At this stage dc->condjmp will only be set when the skipped
|
||||||
instruction was a conditional branch or trap, and the PC has
|
instruction was a conditional branch or trap, and the PC has
|
||||||
already been written. */
|
already been written. */
|
||||||
if (unlikely(cs->singlestep_enabled)) {
|
if (unlikely(cs->singlestep_enabled || dc->ss_active)) {
|
||||||
/* Make sure the pc is updated, and raise a debug exception. */
|
/* Make sure the pc is updated, and raise a debug exception. */
|
||||||
if (dc->condjmp) {
|
if (dc->condjmp) {
|
||||||
gen_set_condexec(dc);
|
gen_set_condexec(dc);
|
||||||
if (dc->is_jmp == DISAS_SWI) {
|
if (dc->is_jmp == DISAS_SWI) {
|
||||||
|
gen_ss_advance(dc);
|
||||||
gen_exception(EXCP_SWI, syn_aa32_svc(dc->svc_imm, dc->thumb));
|
gen_exception(EXCP_SWI, syn_aa32_svc(dc->svc_imm, dc->thumb));
|
||||||
|
} else if (dc->ss_active) {
|
||||||
|
gen_step_complete_exception(dc);
|
||||||
} else {
|
} else {
|
||||||
gen_exception_internal(EXCP_DEBUG);
|
gen_exception_internal(EXCP_DEBUG);
|
||||||
}
|
}
|
||||||
|
@ -11090,7 +11159,10 @@ static inline void gen_intermediate_code_internal(ARMCPU *cpu,
|
||||||
}
|
}
|
||||||
gen_set_condexec(dc);
|
gen_set_condexec(dc);
|
||||||
if (dc->is_jmp == DISAS_SWI && !dc->condjmp) {
|
if (dc->is_jmp == DISAS_SWI && !dc->condjmp) {
|
||||||
|
gen_ss_advance(dc);
|
||||||
gen_exception(EXCP_SWI, syn_aa32_svc(dc->svc_imm, dc->thumb));
|
gen_exception(EXCP_SWI, syn_aa32_svc(dc->svc_imm, dc->thumb));
|
||||||
|
} else if (dc->ss_active) {
|
||||||
|
gen_step_complete_exception(dc);
|
||||||
} else {
|
} else {
|
||||||
/* FIXME: Single stepping a WFI insn will not halt
|
/* FIXME: Single stepping a WFI insn will not halt
|
||||||
the CPU. */
|
the CPU. */
|
||||||
|
|
Loading…
Reference in New Issue