[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
Re: [Qemu-devel] [PATCH 09/11] target-arm: Implement ARMv8 single-step h
From: |
Edgar E. Iglesias |
Subject: |
Re: [Qemu-devel] [PATCH 09/11] target-arm: Implement ARMv8 single-step handling for A64 code |
Date: |
Tue, 19 Aug 2014 19:56:17 +1000 |
User-agent: |
Mutt/1.5.21 (2010-09-15) |
On Fri, Aug 08, 2014 at 01:18:12PM +0100, Peter Maydell wrote:
> Implement ARMv8 software single-step handling for A64 code:
> correctly update the single-step state machine and generate
> debug exceptions when stepping A64 code.
>
> This patch has no behavioural change since MDSCR_EL1.SS can't
> be set by the guest yet.
Hi Peter,
>
> Signed-off-by: Peter Maydell <address@hidden>
> ---
> target-arm/cpu.h | 21 +++++++++++
> target-arm/helper.h | 1 +
> target-arm/internals.h | 6 +++
> target-arm/op_helper.c | 5 +++
> target-arm/translate-a64.c | 91
> +++++++++++++++++++++++++++++++++++++++++++---
> target-arm/translate.h | 12 ++++++
> 6 files changed, 131 insertions(+), 5 deletions(-)
>
> diff --git a/target-arm/cpu.h b/target-arm/cpu.h
> index 74f7b15..ac01524 100644
> --- a/target-arm/cpu.h
> +++ b/target-arm/cpu.h
> @@ -1211,6 +1211,10 @@ static inline bool arm_singlestep_active(CPUARMState
> *env)
> #define ARM_TBFLAG_AA64_EL_MASK (0x3 << ARM_TBFLAG_AA64_EL_SHIFT)
> #define ARM_TBFLAG_AA64_FPEN_SHIFT 2
> #define ARM_TBFLAG_AA64_FPEN_MASK (1 << ARM_TBFLAG_AA64_FPEN_SHIFT)
> +#define ARM_TBFLAG_AA64_SS_ACTIVE_SHIFT 3
> +#define ARM_TBFLAG_AA64_SS_ACTIVE_MASK (1 << ARM_TBFLAG_AA64_SS_ACTIVE_SHIFT)
> +#define ARM_TBFLAG_AA64_PSTATE_SS_SHIFT 3
> +#define ARM_TBFLAG_AA64_PSTATE_SS_MASK (1 << ARM_TBFLAG_AA64_PSTATE_SS_SHIFT)
Shouldn't these shifts/masks differ?
>
> /* some convenience accessor macros */
> #define ARM_TBFLAG_AARCH64_STATE(F) \
> @@ -1235,6 +1239,10 @@ static inline bool arm_singlestep_active(CPUARMState
> *env)
> (((F) & ARM_TBFLAG_AA64_EL_MASK) >> ARM_TBFLAG_AA64_EL_SHIFT)
> #define ARM_TBFLAG_AA64_FPEN(F) \
> (((F) & ARM_TBFLAG_AA64_FPEN_MASK) >> ARM_TBFLAG_AA64_FPEN_SHIFT)
> +#define ARM_TBFLAG_AA64_SS_ACTIVE(F) \
> + (((F) & ARM_TBFLAG_AA64_SS_ACTIVE_MASK) >>
> ARM_TBFLAG_AA64_SS_ACTIVE_SHIFT)
> +#define ARM_TBFLAG_AA64_PSTATE_SS(F) \
> + (((F) & ARM_TBFLAG_AA64_PSTATE_SS_MASK) >>
> ARM_TBFLAG_AA64_PSTATE_SS_SHIFT)
>
> static inline void cpu_get_tb_cpu_state(CPUARMState *env, target_ulong *pc,
> target_ulong *cs_base, int *flags)
> @@ -1248,6 +1256,19 @@ static inline void cpu_get_tb_cpu_state(CPUARMState
> *env, target_ulong *pc,
> if (fpen == 3 || (fpen == 1 && arm_current_pl(env) != 0)) {
> *flags |= ARM_TBFLAG_AA64_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_AA64_SS_ACTIVE_MASK;
> + if (env->pstate & PSTATE_SS) {
> + *flags |= ARM_TBFLAG_AA64_PSTATE_SS_MASK;
> + }
> + }
> } else {
> int privmode;
> *pc = env->regs[15];
> diff --git a/target-arm/helper.h b/target-arm/helper.h
> index facfcd2..1d7003b 100644
> --- a/target-arm/helper.h
> +++ b/target-arm/helper.h
> @@ -64,6 +64,7 @@ DEF_HELPER_3(set_cp_reg64, void, env, ptr, i64)
> DEF_HELPER_2(get_cp_reg64, i64, env, ptr)
>
> DEF_HELPER_3(msr_i_pstate, void, env, i32, i32)
> +DEF_HELPER_1(clear_pstate_ss, void, env)
> DEF_HELPER_1(exception_return, void, env)
>
> DEF_HELPER_2(get_r13_banked, i32, env, i32)
> diff --git a/target-arm/internals.h b/target-arm/internals.h
> index 08fa697..53c2e3c 100644
> --- a/target-arm/internals.h
> +++ b/target-arm/internals.h
> @@ -290,4 +290,10 @@ static inline uint32_t syn_data_abort(int same_el, int
> ea, int cm, int s1ptw,
> | (ea << 9) | (cm << 8) | (s1ptw << 7) | (wnr << 6) | fsc;
> }
>
> +static inline uint32_t syn_swstep(int same_el, int isv, int ex)
> +{
> + return (EC_SOFTWARESTEP << ARM_EL_EC_SHIFT) | (same_el <<
> ARM_EL_EC_SHIFT)
> + | (isv << 24) | (ex << 6) | 0x22;
> +}
> +
> #endif
> diff --git a/target-arm/op_helper.c b/target-arm/op_helper.c
> index 62cc07d..fe40358 100644
> --- a/target-arm/op_helper.c
> +++ b/target-arm/op_helper.c
> @@ -369,6 +369,11 @@ void HELPER(msr_i_pstate)(CPUARMState *env, uint32_t op,
> uint32_t imm)
> }
> }
>
> +void HELPER(clear_pstate_ss)(CPUARMState *env)
> +{
> + env->pstate &= ~PSTATE_SS;
> +}
> +
> void HELPER(exception_return)(CPUARMState *env)
> {
> int cur_el = arm_current_pl(env);
> diff --git a/target-arm/translate-a64.c b/target-arm/translate-a64.c
> index aa731bf..21cb12b 100644
> --- a/target-arm/translate-a64.c
> +++ b/target-arm/translate-a64.c
> @@ -203,10 +203,39 @@ static void gen_exception_insn(DisasContext *s, int
> offset, int excp,
> s->is_jmp = DISAS_EXC;
> }
>
> +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 inline bool use_goto_tb(DisasContext *s, int n, uint64_t dest)
> {
> - /* No direct tb linking with singlestep or deterministic io */
> - if (s->singlestep_enabled || (s->tb->cflags & CF_LAST_IO)) {
> + /* No direct tb linking with singlestep (either QEMU's or the ARM
> + * debug architecture kind) or deterministic io
> + */
> + if (s->singlestep_enabled || s->ss_active || (s->tb->cflags &
> CF_LAST_IO)) {
> return false;
> }
>
> @@ -230,7 +259,9 @@ static inline void gen_goto_tb(DisasContext *s, int n,
> uint64_t dest)
> s->is_jmp = DISAS_TB_JUMP;
> } else {
> gen_a64_set_pc_im(dest);
> - if (s->singlestep_enabled) {
> + if (s->ss_active) {
> + gen_step_complete_exception(s);
> + } else if (s->singlestep_enabled) {
> gen_exception_internal(EXCP_DEBUG);
> } else {
> tcg_gen_exit_tb(0);
> @@ -1447,6 +1478,12 @@ static void disas_exc(DisasContext *s, uint32_t insn)
> unallocated_encoding(s);
> break;
> }
> + /* For SVC, HVC and SMC we advance the single-step state
> + * machine before taking the exception. This is architecturally
> + * mandated, to ensure that single-stepping a system call
> + * instruction works properly.
> + */
> + gen_ss_advance(s);
> gen_exception_insn(s, 0, EXCP_SWI, syn_aa64_svc(imm16));
> break;
> case 1:
> @@ -1727,6 +1764,7 @@ static void disas_ldst_excl(DisasContext *s, uint32_t
> insn)
>
> if (is_excl) {
> if (!is_store) {
> + s->is_ldex = true;
> gen_load_exclusive(s, rt, rt2, tcg_addr, size, is_pair);
> } else {
> gen_store_exclusive(s, rs, rt, rt2, tcg_addr, size, is_pair);
> @@ -10867,6 +10905,26 @@ void gen_intermediate_code_internal_a64(ARMCPU *cpu,
> dc->current_pl = arm_current_pl(env);
> 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_AA64_SS_ACTIVE(tb->flags);
> + dc->pstate_ss = ARM_TBFLAG_AA64_PSTATE_SS(tb->flags);
> + dc->is_ldex = false;
> + dc->ss_same_el = (arm_debug_target_el(env) == dc->current_pl);
> +
> init_tmp_a64_array(dc);
>
> next_page_start = (pc_start & TARGET_PAGE_MASK) + TARGET_PAGE_SIZE;
> @@ -10915,6 +10973,23 @@ void gen_intermediate_code_internal_a64(ARMCPU *cpu,
> 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));
> + dc->is_jmp = DISAS_EXC;
> + break;
> + }
> +
> disas_a64_insn(env, dc);
>
> if (tcg_check_temp_count()) {
> @@ -10931,6 +11006,7 @@ void gen_intermediate_code_internal_a64(ARMCPU *cpu,
> } while (!dc->is_jmp && tcg_ctx.gen_opc_ptr < gen_opc_end &&
> !cs->singlestep_enabled &&
> !singlestep &&
> + !dc->ss_active &&
> dc->pc < next_page_start &&
> num_insns < max_insns);
>
> @@ -10938,7 +11014,8 @@ void gen_intermediate_code_internal_a64(ARMCPU *cpu,
> gen_io_end();
> }
>
> - if (unlikely(cs->singlestep_enabled) && dc->is_jmp != DISAS_EXC) {
> + if (unlikely(cs->singlestep_enabled || dc->ss_active)
> + && dc->is_jmp != DISAS_EXC) {
> /* Note that this means single stepping WFI doesn't halt the CPU.
> * For conditional branch insns this is harmless unreachable code as
> * gen_goto_tb() has already handled emitting the debug exception
> @@ -10948,7 +11025,11 @@ void gen_intermediate_code_internal_a64(ARMCPU *cpu,
> if (dc->is_jmp != DISAS_JUMP) {
> gen_a64_set_pc_im(dc->pc);
> }
> - gen_exception_internal(EXCP_DEBUG);
> + if (cs->singlestep_enabled) {
> + gen_exception_internal(EXCP_DEBUG);
> + } else {
> + gen_step_complete_exception(dc);
> + }
> } else {
> switch (dc->is_jmp) {
> case DISAS_NEXT:
> diff --git a/target-arm/translate.h b/target-arm/translate.h
> index 31a0104..b90d275 100644
> --- a/target-arm/translate.h
> +++ b/target-arm/translate.h
> @@ -40,6 +40,18 @@ typedef struct DisasContext {
> * that it is set at the point where we actually touch the FP regs.
> */
> bool fp_access_checked;
> + /* ARMv8 single-step state (this is distinct from the QEMU gdbstub
> + * single-step support).
> + */
> + bool ss_active;
> + bool pstate_ss;
> + /* True if the insn just emitted was a load-exclusive instruction
> + * (necessary for syndrome information for single step exceptions),
> + * ie A64 LDX*, LDAX*, A32/T32 LDREX*, LDAEX*.
> + */
> + bool is_ldex;
> + /* True if a single-step exception will be taken to the current EL */
> + bool ss_same_el;
> #define TMP_A64_MAX 16
> int tmp_a64_count;
> TCGv_i64 tmp_a64[TMP_A64_MAX];
> --
> 1.9.1
>
>
- [Qemu-devel] [PATCH 00/11] target-arm: Implement ARMv8 debug single-stepping, Peter Maydell, 2014/08/08
- [Qemu-devel] [PATCH 05/11] target-arm: Don't allow AArch32 to access RES0 CPSR bits, Peter Maydell, 2014/08/08
- [Qemu-devel] [PATCH 08/11] target-arm: A64: Avoid duplicate exit_tb(0) in non-linked goto_tb, Peter Maydell, 2014/08/08
- [Qemu-devel] [PATCH 07/11] target-arm: Set PSTATE.SS correctly on exception return from AArch64, Peter Maydell, 2014/08/08
- [Qemu-devel] [PATCH 06/11] target-arm: Correctly handle PSTATE.SS when taking exception to AArch32, Peter Maydell, 2014/08/08
- [Qemu-devel] [PATCH 04/11] target-arm: Adjust debug ID registers per-CPU, Peter Maydell, 2014/08/08
- [Qemu-devel] [PATCH 03/11] target-arm: Provide both 32 and 64 bit versions of debug registers, Peter Maydell, 2014/08/08
- [Qemu-devel] [PATCH 01/11] target-arm: Collect up the debug cp register definitions, Peter Maydell, 2014/08/08
- [Qemu-devel] [PATCH 09/11] target-arm: Implement ARMv8 single-step handling for A64 code, Peter Maydell, 2014/08/08
- Re: [Qemu-devel] [PATCH 09/11] target-arm: Implement ARMv8 single-step handling for A64 code,
Edgar E. Iglesias <=
[Qemu-devel] [PATCH 02/11] target-arm: Allow STATE_BOTH reginfo descriptions for more than cp14, Peter Maydell, 2014/08/08
[Qemu-devel] [PATCH 10/11] target-arm: Implement ARMv8 single-stepping for AArch32 code, Peter Maydell, 2014/08/08
[Qemu-devel] [PATCH 11/11] target-arm: Implement MDSCR_EL1 as having state, Peter Maydell, 2014/08/08
Re: [Qemu-devel] [PATCH 00/11] target-arm: Implement ARMv8 debug single-stepping, Peter Maydell, 2014/08/18