From aad821ac4faad369fad8941d25e59edf2514246b Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Mon, 7 Jan 2019 15:23:45 +0000 Subject: [PATCH 01/37] target/arm: Convert ARM_TBFLAG_* to FIELDs MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Use "register" TBFLAG_ANY to indicate shared state between A32 and A64, and "registers" TBFLAG_A32 & TBFLAG_A64 for fields that are specific to the given cpu state. Move ARM_TBFLAG_BE_DATA to shared state, instead of its current placement within "Bit usage when in AArch32 state". Signed-off-by: Richard Henderson Reviewed-by: Philippe Mathieu-Daudé Message-id: 20181218164348.7127-1-richard.henderson@linaro.org [PMM: removed the renaming of BE_DATA flag to BE] Reviewed-by: Peter Maydell Signed-off-by: Peter Maydell --- target/arm/cpu.h | 102 ++++++++----------------------------- target/arm/helper.c | 49 +++++++++--------- target/arm/translate-a64.c | 22 ++++---- target/arm/translate.c | 40 ++++++++------- 4 files changed, 78 insertions(+), 135 deletions(-) diff --git a/target/arm/cpu.h b/target/arm/cpu.h index c943f35dd9..fba1c578d3 100644 --- a/target/arm/cpu.h +++ b/target/arm/cpu.h @@ -2944,102 +2944,40 @@ static inline bool arm_cpu_data_is_big_endian(CPUARMState *env) * We put flags which are shared between 32 and 64 bit mode at the top * of the word, and flags which apply to only one mode at the bottom. */ -#define ARM_TBFLAG_AARCH64_STATE_SHIFT 31 -#define ARM_TBFLAG_AARCH64_STATE_MASK (1U << ARM_TBFLAG_AARCH64_STATE_SHIFT) -#define ARM_TBFLAG_MMUIDX_SHIFT 28 -#define ARM_TBFLAG_MMUIDX_MASK (0x7 << ARM_TBFLAG_MMUIDX_SHIFT) -#define ARM_TBFLAG_SS_ACTIVE_SHIFT 27 -#define ARM_TBFLAG_SS_ACTIVE_MASK (1 << ARM_TBFLAG_SS_ACTIVE_SHIFT) -#define ARM_TBFLAG_PSTATE_SS_SHIFT 26 -#define ARM_TBFLAG_PSTATE_SS_MASK (1 << ARM_TBFLAG_PSTATE_SS_SHIFT) +FIELD(TBFLAG_ANY, AARCH64_STATE, 31, 1) +FIELD(TBFLAG_ANY, MMUIDX, 28, 3) +FIELD(TBFLAG_ANY, SS_ACTIVE, 27, 1) +FIELD(TBFLAG_ANY, PSTATE_SS, 26, 1) /* Target EL if we take a floating-point-disabled exception */ -#define ARM_TBFLAG_FPEXC_EL_SHIFT 24 -#define ARM_TBFLAG_FPEXC_EL_MASK (0x3 << ARM_TBFLAG_FPEXC_EL_SHIFT) +FIELD(TBFLAG_ANY, FPEXC_EL, 24, 2) +FIELD(TBFLAG_ANY, BE_DATA, 23, 1) /* Bit usage when in AArch32 state: */ -#define ARM_TBFLAG_THUMB_SHIFT 0 -#define ARM_TBFLAG_THUMB_MASK (1 << ARM_TBFLAG_THUMB_SHIFT) -#define ARM_TBFLAG_VECLEN_SHIFT 1 -#define ARM_TBFLAG_VECLEN_MASK (0x7 << ARM_TBFLAG_VECLEN_SHIFT) -#define ARM_TBFLAG_VECSTRIDE_SHIFT 4 -#define ARM_TBFLAG_VECSTRIDE_MASK (0x3 << ARM_TBFLAG_VECSTRIDE_SHIFT) -#define ARM_TBFLAG_VFPEN_SHIFT 7 -#define ARM_TBFLAG_VFPEN_MASK (1 << ARM_TBFLAG_VFPEN_SHIFT) -#define ARM_TBFLAG_CONDEXEC_SHIFT 8 -#define ARM_TBFLAG_CONDEXEC_MASK (0xff << ARM_TBFLAG_CONDEXEC_SHIFT) -#define ARM_TBFLAG_SCTLR_B_SHIFT 16 -#define ARM_TBFLAG_SCTLR_B_MASK (1 << ARM_TBFLAG_SCTLR_B_SHIFT) +FIELD(TBFLAG_A32, THUMB, 0, 1) +FIELD(TBFLAG_A32, VECLEN, 1, 3) +FIELD(TBFLAG_A32, VECSTRIDE, 4, 2) +FIELD(TBFLAG_A32, VFPEN, 7, 1) +FIELD(TBFLAG_A32, CONDEXEC, 8, 8) +FIELD(TBFLAG_A32, SCTLR_B, 16, 1) /* We store the bottom two bits of the CPAR as TB flags and handle * checks on the other bits at runtime */ -#define ARM_TBFLAG_XSCALE_CPAR_SHIFT 17 -#define ARM_TBFLAG_XSCALE_CPAR_MASK (3 << ARM_TBFLAG_XSCALE_CPAR_SHIFT) +FIELD(TBFLAG_A32, XSCALE_CPAR, 17, 2) /* Indicates whether cp register reads and writes by guest code should access * the secure or nonsecure bank of banked registers; note that this is not * the same thing as the current security state of the processor! */ -#define ARM_TBFLAG_NS_SHIFT 19 -#define ARM_TBFLAG_NS_MASK (1 << ARM_TBFLAG_NS_SHIFT) -#define ARM_TBFLAG_BE_DATA_SHIFT 20 -#define ARM_TBFLAG_BE_DATA_MASK (1 << ARM_TBFLAG_BE_DATA_SHIFT) +FIELD(TBFLAG_A32, NS, 19, 1) /* For M profile only, Handler (ie not Thread) mode */ -#define ARM_TBFLAG_HANDLER_SHIFT 21 -#define ARM_TBFLAG_HANDLER_MASK (1 << ARM_TBFLAG_HANDLER_SHIFT) +FIELD(TBFLAG_A32, HANDLER, 21, 1) /* For M profile only, whether we should generate stack-limit checks */ -#define ARM_TBFLAG_STACKCHECK_SHIFT 22 -#define ARM_TBFLAG_STACKCHECK_MASK (1 << ARM_TBFLAG_STACKCHECK_SHIFT) +FIELD(TBFLAG_A32, STACKCHECK, 22, 1) /* Bit usage when in AArch64 state */ -#define ARM_TBFLAG_TBI0_SHIFT 0 /* TBI0 for EL0/1 or TBI for EL2/3 */ -#define ARM_TBFLAG_TBI0_MASK (0x1ull << ARM_TBFLAG_TBI0_SHIFT) -#define ARM_TBFLAG_TBI1_SHIFT 1 /* TBI1 for EL0/1 */ -#define ARM_TBFLAG_TBI1_MASK (0x1ull << ARM_TBFLAG_TBI1_SHIFT) -#define ARM_TBFLAG_SVEEXC_EL_SHIFT 2 -#define ARM_TBFLAG_SVEEXC_EL_MASK (0x3 << ARM_TBFLAG_SVEEXC_EL_SHIFT) -#define ARM_TBFLAG_ZCR_LEN_SHIFT 4 -#define ARM_TBFLAG_ZCR_LEN_MASK (0xf << ARM_TBFLAG_ZCR_LEN_SHIFT) - -/* some convenience accessor macros */ -#define ARM_TBFLAG_AARCH64_STATE(F) \ - (((F) & ARM_TBFLAG_AARCH64_STATE_MASK) >> ARM_TBFLAG_AARCH64_STATE_SHIFT) -#define ARM_TBFLAG_MMUIDX(F) \ - (((F) & ARM_TBFLAG_MMUIDX_MASK) >> ARM_TBFLAG_MMUIDX_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_FPEXC_EL(F) \ - (((F) & ARM_TBFLAG_FPEXC_EL_MASK) >> ARM_TBFLAG_FPEXC_EL_SHIFT) -#define ARM_TBFLAG_THUMB(F) \ - (((F) & ARM_TBFLAG_THUMB_MASK) >> ARM_TBFLAG_THUMB_SHIFT) -#define ARM_TBFLAG_VECLEN(F) \ - (((F) & ARM_TBFLAG_VECLEN_MASK) >> ARM_TBFLAG_VECLEN_SHIFT) -#define ARM_TBFLAG_VECSTRIDE(F) \ - (((F) & ARM_TBFLAG_VECSTRIDE_MASK) >> ARM_TBFLAG_VECSTRIDE_SHIFT) -#define ARM_TBFLAG_VFPEN(F) \ - (((F) & ARM_TBFLAG_VFPEN_MASK) >> ARM_TBFLAG_VFPEN_SHIFT) -#define ARM_TBFLAG_CONDEXEC(F) \ - (((F) & ARM_TBFLAG_CONDEXEC_MASK) >> ARM_TBFLAG_CONDEXEC_SHIFT) -#define ARM_TBFLAG_SCTLR_B(F) \ - (((F) & ARM_TBFLAG_SCTLR_B_MASK) >> ARM_TBFLAG_SCTLR_B_SHIFT) -#define ARM_TBFLAG_XSCALE_CPAR(F) \ - (((F) & ARM_TBFLAG_XSCALE_CPAR_MASK) >> ARM_TBFLAG_XSCALE_CPAR_SHIFT) -#define ARM_TBFLAG_NS(F) \ - (((F) & ARM_TBFLAG_NS_MASK) >> ARM_TBFLAG_NS_SHIFT) -#define ARM_TBFLAG_BE_DATA(F) \ - (((F) & ARM_TBFLAG_BE_DATA_MASK) >> ARM_TBFLAG_BE_DATA_SHIFT) -#define ARM_TBFLAG_HANDLER(F) \ - (((F) & ARM_TBFLAG_HANDLER_MASK) >> ARM_TBFLAG_HANDLER_SHIFT) -#define ARM_TBFLAG_STACKCHECK(F) \ - (((F) & ARM_TBFLAG_STACKCHECK_MASK) >> ARM_TBFLAG_STACKCHECK_SHIFT) -#define ARM_TBFLAG_TBI0(F) \ - (((F) & ARM_TBFLAG_TBI0_MASK) >> ARM_TBFLAG_TBI0_SHIFT) -#define ARM_TBFLAG_TBI1(F) \ - (((F) & ARM_TBFLAG_TBI1_MASK) >> ARM_TBFLAG_TBI1_SHIFT) -#define ARM_TBFLAG_SVEEXC_EL(F) \ - (((F) & ARM_TBFLAG_SVEEXC_EL_MASK) >> ARM_TBFLAG_SVEEXC_EL_SHIFT) -#define ARM_TBFLAG_ZCR_LEN(F) \ - (((F) & ARM_TBFLAG_ZCR_LEN_MASK) >> ARM_TBFLAG_ZCR_LEN_SHIFT) +FIELD(TBFLAG_A64, TBI0, 0, 1) +FIELD(TBFLAG_A64, TBI1, 1, 1) +FIELD(TBFLAG_A64, SVEEXC_EL, 2, 2) +FIELD(TBFLAG_A64, ZCR_LEN, 4, 4) static inline bool bswap_code(bool sctlr_b) { diff --git a/target/arm/helper.c b/target/arm/helper.c index 644599b29d..f00c141ef9 100644 --- a/target/arm/helper.c +++ b/target/arm/helper.c @@ -12955,16 +12955,18 @@ void cpu_get_tb_cpu_state(CPUARMState *env, target_ulong *pc, ARMMMUIdx mmu_idx = core_to_arm_mmu_idx(env, cpu_mmu_index(env, false)); int current_el = arm_current_el(env); int fp_el = fp_exception_el(env, current_el); - uint32_t flags; + uint32_t flags = 0; if (is_a64(env)) { ARMCPU *cpu = arm_env_get_cpu(env); *pc = env->pc; - flags = ARM_TBFLAG_AARCH64_STATE_MASK; + flags = FIELD_DP32(flags, TBFLAG_ANY, AARCH64_STATE, 1); /* Get control bits for tagged addresses */ - flags |= (arm_regime_tbi0(env, mmu_idx) << ARM_TBFLAG_TBI0_SHIFT); - flags |= (arm_regime_tbi1(env, mmu_idx) << ARM_TBFLAG_TBI1_SHIFT); + flags = FIELD_DP32(flags, TBFLAG_A64, TBI0, + arm_regime_tbi0(env, mmu_idx)); + flags = FIELD_DP32(flags, TBFLAG_A64, TBI1, + arm_regime_tbi1(env, mmu_idx)); if (cpu_isar_feature(aa64_sve, cpu)) { int sve_el = sve_exception_el(env, current_el); @@ -12978,28 +12980,25 @@ void cpu_get_tb_cpu_state(CPUARMState *env, target_ulong *pc, } else { zcr_len = sve_zcr_len_for_el(env, current_el); } - flags |= sve_el << ARM_TBFLAG_SVEEXC_EL_SHIFT; - flags |= zcr_len << ARM_TBFLAG_ZCR_LEN_SHIFT; + flags = FIELD_DP32(flags, TBFLAG_A64, SVEEXC_EL, sve_el); + flags = FIELD_DP32(flags, TBFLAG_A64, ZCR_LEN, zcr_len); } } else { *pc = env->regs[15]; - flags = (env->thumb << ARM_TBFLAG_THUMB_SHIFT) - | (env->vfp.vec_len << ARM_TBFLAG_VECLEN_SHIFT) - | (env->vfp.vec_stride << ARM_TBFLAG_VECSTRIDE_SHIFT) - | (env->condexec_bits << ARM_TBFLAG_CONDEXEC_SHIFT) - | (arm_sctlr_b(env) << ARM_TBFLAG_SCTLR_B_SHIFT); - if (!(access_secure_reg(env))) { - flags |= ARM_TBFLAG_NS_MASK; - } + flags = FIELD_DP32(flags, TBFLAG_A32, THUMB, env->thumb); + flags = FIELD_DP32(flags, TBFLAG_A32, VECLEN, env->vfp.vec_len); + flags = FIELD_DP32(flags, TBFLAG_A32, VECSTRIDE, env->vfp.vec_stride); + flags = FIELD_DP32(flags, TBFLAG_A32, CONDEXEC, env->condexec_bits); + flags = FIELD_DP32(flags, TBFLAG_A32, SCTLR_B, arm_sctlr_b(env)); + flags = FIELD_DP32(flags, TBFLAG_A32, NS, !access_secure_reg(env)); if (env->vfp.xregs[ARM_VFP_FPEXC] & (1 << 30) || arm_el_is_aa64(env, 1)) { - flags |= ARM_TBFLAG_VFPEN_MASK; + flags = FIELD_DP32(flags, TBFLAG_A32, VFPEN, 1); } - flags |= (extract32(env->cp15.c15_cpar, 0, 2) - << ARM_TBFLAG_XSCALE_CPAR_SHIFT); + flags = FIELD_DP32(flags, TBFLAG_A32, XSCALE_CPAR, env->cp15.c15_cpar); } - flags |= (arm_to_core_mmu_idx(mmu_idx) << ARM_TBFLAG_MMUIDX_SHIFT); + flags = FIELD_DP32(flags, TBFLAG_ANY, MMUIDX, arm_to_core_mmu_idx(mmu_idx)); /* The SS_ACTIVE and PSTATE_SS bits correspond to the state machine * states defined in the ARM ARM for software singlestep: @@ -13009,24 +13008,24 @@ void cpu_get_tb_cpu_state(CPUARMState *env, target_ulong *pc, * 1 1 Active-not-pending */ if (arm_singlestep_active(env)) { - flags |= ARM_TBFLAG_SS_ACTIVE_MASK; + flags = FIELD_DP32(flags, TBFLAG_ANY, SS_ACTIVE, 1); if (is_a64(env)) { if (env->pstate & PSTATE_SS) { - flags |= ARM_TBFLAG_PSTATE_SS_MASK; + flags = FIELD_DP32(flags, TBFLAG_ANY, PSTATE_SS, 1); } } else { if (env->uncached_cpsr & PSTATE_SS) { - flags |= ARM_TBFLAG_PSTATE_SS_MASK; + flags = FIELD_DP32(flags, TBFLAG_ANY, PSTATE_SS, 1); } } } if (arm_cpu_data_is_big_endian(env)) { - flags |= ARM_TBFLAG_BE_DATA_MASK; + flags = FIELD_DP32(flags, TBFLAG_ANY, BE_DATA, 1); } - flags |= fp_el << ARM_TBFLAG_FPEXC_EL_SHIFT; + flags = FIELD_DP32(flags, TBFLAG_ANY, FPEXC_EL, fp_el); if (arm_v7m_is_handler_mode(env)) { - flags |= ARM_TBFLAG_HANDLER_MASK; + flags = FIELD_DP32(flags, TBFLAG_A32, HANDLER, 1); } /* v8M always applies stack limit checks unless CCR.STKOFHFNMIGN is @@ -13036,7 +13035,7 @@ void cpu_get_tb_cpu_state(CPUARMState *env, target_ulong *pc, arm_feature(env, ARM_FEATURE_M) && !((mmu_idx & ARM_MMU_IDX_M_NEGPRI) && (env->v7m.ccr[env->v7m.secure] & R_V7M_CCR_STKOFHFNMIGN_MASK))) { - flags |= ARM_TBFLAG_STACKCHECK_MASK; + flags = FIELD_DP32(flags, TBFLAG_A32, STACKCHECK, 1); } *pflags = flags; diff --git a/target/arm/translate-a64.c b/target/arm/translate-a64.c index e1da1e4d6f..b7b6ab6371 100644 --- a/target/arm/translate-a64.c +++ b/target/arm/translate-a64.c @@ -13380,7 +13380,8 @@ static void aarch64_tr_init_disas_context(DisasContextBase *dcbase, DisasContext *dc = container_of(dcbase, DisasContext, base); CPUARMState *env = cpu->env_ptr; ARMCPU *arm_cpu = arm_env_get_cpu(env); - int bound; + uint32_t tb_flags = dc->base.tb->flags; + int bound, core_mmu_idx; dc->isar = &arm_cpu->isar; dc->pc = dc->base.pc_first; @@ -13394,19 +13395,20 @@ static void aarch64_tr_init_disas_context(DisasContextBase *dcbase, !arm_el_is_aa64(env, 3); dc->thumb = 0; dc->sctlr_b = 0; - dc->be_data = ARM_TBFLAG_BE_DATA(dc->base.tb->flags) ? MO_BE : MO_LE; + dc->be_data = FIELD_EX32(tb_flags, TBFLAG_ANY, BE_DATA) ? MO_BE : MO_LE; dc->condexec_mask = 0; dc->condexec_cond = 0; - dc->mmu_idx = core_to_arm_mmu_idx(env, ARM_TBFLAG_MMUIDX(dc->base.tb->flags)); - dc->tbi0 = ARM_TBFLAG_TBI0(dc->base.tb->flags); - dc->tbi1 = ARM_TBFLAG_TBI1(dc->base.tb->flags); + core_mmu_idx = FIELD_EX32(tb_flags, TBFLAG_ANY, MMUIDX); + dc->mmu_idx = core_to_arm_mmu_idx(env, core_mmu_idx); + dc->tbi0 = FIELD_EX32(tb_flags, TBFLAG_A64, TBI0); + dc->tbi1 = FIELD_EX32(tb_flags, TBFLAG_A64, TBI1); dc->current_el = arm_mmu_idx_to_el(dc->mmu_idx); #if !defined(CONFIG_USER_ONLY) dc->user = (dc->current_el == 0); #endif - dc->fp_excp_el = ARM_TBFLAG_FPEXC_EL(dc->base.tb->flags); - dc->sve_excp_el = ARM_TBFLAG_SVEEXC_EL(dc->base.tb->flags); - dc->sve_len = (ARM_TBFLAG_ZCR_LEN(dc->base.tb->flags) + 1) * 16; + dc->fp_excp_el = FIELD_EX32(tb_flags, TBFLAG_ANY, FPEXC_EL); + dc->sve_excp_el = FIELD_EX32(tb_flags, TBFLAG_A64, SVEEXC_EL); + dc->sve_len = (FIELD_EX32(tb_flags, TBFLAG_A64, ZCR_LEN) + 1) * 16; dc->vec_len = 0; dc->vec_stride = 0; dc->cp_regs = arm_cpu->cp_regs; @@ -13427,8 +13429,8 @@ static void aarch64_tr_init_disas_context(DisasContextBase *dcbase, * emit code to generate a software step exception * end the TB */ - dc->ss_active = ARM_TBFLAG_SS_ACTIVE(dc->base.tb->flags); - dc->pstate_ss = ARM_TBFLAG_PSTATE_SS(dc->base.tb->flags); + dc->ss_active = FIELD_EX32(tb_flags, TBFLAG_ANY, SS_ACTIVE); + dc->pstate_ss = FIELD_EX32(tb_flags, TBFLAG_ANY, PSTATE_SS); dc->is_ldex = false; dc->ss_same_el = (arm_debug_target_el(env) == dc->current_el); diff --git a/target/arm/translate.c b/target/arm/translate.c index 7c4675ffd8..ed3db0c394 100644 --- a/target/arm/translate.c +++ b/target/arm/translate.c @@ -13021,6 +13021,8 @@ static void arm_tr_init_disas_context(DisasContextBase *dcbase, CPUState *cs) DisasContext *dc = container_of(dcbase, DisasContext, base); CPUARMState *env = cs->env_ptr; ARMCPU *cpu = arm_env_get_cpu(env); + uint32_t tb_flags = dc->base.tb->flags; + uint32_t condexec, core_mmu_idx; dc->isar = &cpu->isar; dc->pc = dc->base.pc_first; @@ -13032,26 +13034,28 @@ static void arm_tr_init_disas_context(DisasContextBase *dcbase, CPUState *cs) */ dc->secure_routed_to_el3 = arm_feature(env, ARM_FEATURE_EL3) && !arm_el_is_aa64(env, 3); - dc->thumb = ARM_TBFLAG_THUMB(dc->base.tb->flags); - dc->sctlr_b = ARM_TBFLAG_SCTLR_B(dc->base.tb->flags); - dc->be_data = ARM_TBFLAG_BE_DATA(dc->base.tb->flags) ? MO_BE : MO_LE; - dc->condexec_mask = (ARM_TBFLAG_CONDEXEC(dc->base.tb->flags) & 0xf) << 1; - dc->condexec_cond = ARM_TBFLAG_CONDEXEC(dc->base.tb->flags) >> 4; - dc->mmu_idx = core_to_arm_mmu_idx(env, ARM_TBFLAG_MMUIDX(dc->base.tb->flags)); + dc->thumb = FIELD_EX32(tb_flags, TBFLAG_A32, THUMB); + dc->sctlr_b = FIELD_EX32(tb_flags, TBFLAG_A32, SCTLR_B); + dc->be_data = FIELD_EX32(tb_flags, TBFLAG_ANY, BE_DATA) ? MO_BE : MO_LE; + condexec = FIELD_EX32(tb_flags, TBFLAG_A32, CONDEXEC); + dc->condexec_mask = (condexec & 0xf) << 1; + dc->condexec_cond = condexec >> 4; + core_mmu_idx = FIELD_EX32(tb_flags, TBFLAG_ANY, MMUIDX); + dc->mmu_idx = core_to_arm_mmu_idx(env, core_mmu_idx); dc->current_el = arm_mmu_idx_to_el(dc->mmu_idx); #if !defined(CONFIG_USER_ONLY) dc->user = (dc->current_el == 0); #endif - dc->ns = ARM_TBFLAG_NS(dc->base.tb->flags); - dc->fp_excp_el = ARM_TBFLAG_FPEXC_EL(dc->base.tb->flags); - dc->vfp_enabled = ARM_TBFLAG_VFPEN(dc->base.tb->flags); - dc->vec_len = ARM_TBFLAG_VECLEN(dc->base.tb->flags); - dc->vec_stride = ARM_TBFLAG_VECSTRIDE(dc->base.tb->flags); - dc->c15_cpar = ARM_TBFLAG_XSCALE_CPAR(dc->base.tb->flags); - dc->v7m_handler_mode = ARM_TBFLAG_HANDLER(dc->base.tb->flags); + dc->ns = FIELD_EX32(tb_flags, TBFLAG_A32, NS); + dc->fp_excp_el = FIELD_EX32(tb_flags, TBFLAG_ANY, FPEXC_EL); + dc->vfp_enabled = FIELD_EX32(tb_flags, TBFLAG_A32, VFPEN); + dc->vec_len = FIELD_EX32(tb_flags, TBFLAG_A32, VECLEN); + dc->vec_stride = FIELD_EX32(tb_flags, TBFLAG_A32, VECSTRIDE); + dc->c15_cpar = FIELD_EX32(tb_flags, TBFLAG_A32, XSCALE_CPAR); + dc->v7m_handler_mode = FIELD_EX32(tb_flags, TBFLAG_A32, HANDLER); dc->v8m_secure = arm_feature(env, ARM_FEATURE_M_SECURITY) && regime_is_secure(env, dc->mmu_idx); - dc->v8m_stackcheck = ARM_TBFLAG_STACKCHECK(dc->base.tb->flags); + dc->v8m_stackcheck = FIELD_EX32(tb_flags, TBFLAG_A32, STACKCHECK); dc->cp_regs = cpu->cp_regs; dc->features = env->features; @@ -13070,8 +13074,8 @@ static void arm_tr_init_disas_context(DisasContextBase *dcbase, CPUState *cs) * emit code to generate a software step exception * end the TB */ - dc->ss_active = ARM_TBFLAG_SS_ACTIVE(dc->base.tb->flags); - dc->pstate_ss = ARM_TBFLAG_PSTATE_SS(dc->base.tb->flags); + dc->ss_active = FIELD_EX32(tb_flags, TBFLAG_ANY, SS_ACTIVE); + dc->pstate_ss = FIELD_EX32(tb_flags, TBFLAG_ANY, PSTATE_SS); dc->is_ldex = false; dc->ss_same_el = false; /* Can't be true since EL_d must be AArch64 */ @@ -13516,11 +13520,11 @@ void gen_intermediate_code(CPUState *cpu, TranslationBlock *tb) DisasContext dc; const TranslatorOps *ops = &arm_translator_ops; - if (ARM_TBFLAG_THUMB(tb->flags)) { + if (FIELD_EX32(tb->flags, TBFLAG_A32, THUMB)) { ops = &thumb_translator_ops; } #ifdef TARGET_AARCH64 - if (ARM_TBFLAG_AARCH64_STATE(tb->flags)) { + if (FIELD_EX32(tb->flags, TBFLAG_ANY, AARCH64_STATE)) { ops = &aarch64_translator_ops; } #endif From 407e6ce7f1f428cb242d424cd35381a77b5b2071 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Mon, 7 Jan 2019 15:23:45 +0000 Subject: [PATCH 02/37] target/arm: SVE brk[ab] merging does not have s bit While brk[ab] zeroing has a flags setting option, the merging variant does not. Retain the same argument structure, to share expansion but force the flag zero and do not decode bit 22. Reported-by: Laurent Desnogues Signed-off-by: Richard Henderson Message-id: 20181226215003.31438-1-richard.henderson@linaro.org Reviewed-by: Peter Maydell Signed-off-by: Peter Maydell --- target/arm/sve.decode | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/target/arm/sve.decode b/target/arm/sve.decode index e10b689454..4f580a25e7 100644 --- a/target/arm/sve.decode +++ b/target/arm/sve.decode @@ -99,6 +99,7 @@ # Two operand with governing predicate, flags setting @pd_pg_pn_s ........ . s:1 ...... .. pg:4 . rn:4 . rd:4 &rpr_s +@pd_pg_pn_s0 ........ . . ...... .. pg:4 . rn:4 . rd:4 &rpr_s s=0 # Three operand with unused vector element size @rd_rn_rm_e0 ........ ... rm:5 ... ... rn:5 rd:5 &rrr_esz esz=0 @@ -667,8 +668,8 @@ BRKPB 00100101 0. 00 .... 11 .... 0 .... 1 .... @pd_pg_pn_pm_s # SVE partition break condition BRKA_z 00100101 0. 01000001 .... 0 .... 0 .... @pd_pg_pn_s BRKB_z 00100101 1. 01000001 .... 0 .... 0 .... @pd_pg_pn_s -BRKA_m 00100101 0. 01000001 .... 0 .... 1 .... @pd_pg_pn_s -BRKB_m 00100101 1. 01000001 .... 0 .... 1 .... @pd_pg_pn_s +BRKA_m 00100101 00 01000001 .... 0 .... 1 .... @pd_pg_pn_s0 +BRKB_m 00100101 10 01000001 .... 0 .... 1 .... @pd_pg_pn_s0 # SVE propagate break to next partition BRKN 00100101 0. 01100001 .... 0 .... 0 .... @pd_pg_pn_s From 335d52f457e7b3d959b2ddc93f83a36d75484d88 Mon Sep 17 00:00:00 2001 From: Luc Michel Date: Mon, 7 Jan 2019 15:23:45 +0000 Subject: [PATCH 03/37] hw/cpu: introduce CPU clusters MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This commit adds the cpu-cluster type. It aims at gathering CPUs from the same cluster in a machine. For now it only has a `cluster-id` property. Documentation in cluster.h written with the help of Peter Maydell. Signed-off-by: Luc Michel Reviewed-by: Alistair Francis Reviewed-by: Philippe Mathieu-Daudé Tested-by: Philippe Mathieu-Daudé Reviewed-by: Edgar E. Iglesias Message-id: 20181207090135.7651-2-luc.michel@greensocs.com Signed-off-by: Peter Maydell --- MAINTAINERS | 2 ++ hw/cpu/Makefile.objs | 2 +- hw/cpu/cluster.c | 50 ++++++++++++++++++++++++++++++++++ include/hw/cpu/cluster.h | 58 ++++++++++++++++++++++++++++++++++++++++ 4 files changed, 111 insertions(+), 1 deletion(-) create mode 100644 hw/cpu/cluster.c create mode 100644 include/hw/cpu/cluster.h diff --git a/MAINTAINERS b/MAINTAINERS index 19792cfb2d..471161742d 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -1195,7 +1195,9 @@ M: Marcel Apfelbaum S: Supported F: hw/core/machine.c F: hw/core/null-machine.c +F: hw/cpu/cluster.c F: include/hw/boards.h +F: include/hw/cpu/cluster.h T: git https://github.com/ehabkost/qemu.git machine-next Xtensa Machines diff --git a/hw/cpu/Makefile.objs b/hw/cpu/Makefile.objs index cd52d20b65..8db9e8a7b3 100644 --- a/hw/cpu/Makefile.objs +++ b/hw/cpu/Makefile.objs @@ -2,4 +2,4 @@ obj-$(CONFIG_ARM11MPCORE) += arm11mpcore.o obj-$(CONFIG_REALVIEW) += realview_mpcore.o obj-$(CONFIG_A9MPCORE) += a9mpcore.o obj-$(CONFIG_A15MPCORE) += a15mpcore.o -common-obj-y += core.o +common-obj-y += core.o cluster.o diff --git a/hw/cpu/cluster.c b/hw/cpu/cluster.c new file mode 100644 index 0000000000..9d50a235d5 --- /dev/null +++ b/hw/cpu/cluster.c @@ -0,0 +1,50 @@ +/* + * QEMU CPU cluster + * + * Copyright (c) 2018 GreenSocs SAS + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see + * + */ + +#include "qemu/osdep.h" +#include "hw/cpu/cluster.h" +#include "qapi/error.h" +#include "qemu/module.h" + +static Property cpu_cluster_properties[] = { + DEFINE_PROP_UINT32("cluster-id", CPUClusterState, cluster_id, 0), + DEFINE_PROP_END_OF_LIST() +}; + +static void cpu_cluster_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + + dc->props = cpu_cluster_properties; +} + +static const TypeInfo cpu_cluster_type_info = { + .name = TYPE_CPU_CLUSTER, + .parent = TYPE_DEVICE, + .instance_size = sizeof(CPUClusterState), + .class_init = cpu_cluster_class_init, +}; + +static void cpu_cluster_register_types(void) +{ + type_register_static(&cpu_cluster_type_info); +} + +type_init(cpu_cluster_register_types) diff --git a/include/hw/cpu/cluster.h b/include/hw/cpu/cluster.h new file mode 100644 index 0000000000..7381823243 --- /dev/null +++ b/include/hw/cpu/cluster.h @@ -0,0 +1,58 @@ +/* + * QEMU CPU cluster + * + * Copyright (c) 2018 GreenSocs SAS + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see + * + */ +#ifndef HW_CPU_CLUSTER_H +#define HW_CPU_CLUSTER_H + +#include "qemu/osdep.h" +#include "hw/qdev.h" + +/* + * CPU Cluster type + * + * A cluster is a group of CPUs which are all identical and have the same view + * of the rest of the system. It is mainly an internal QEMU representation and + * does not necessarily match with the notion of clusters on the real hardware. + * + * If CPUs are not identical (for example, Cortex-A53 and Cortex-A57 CPUs in an + * Arm big.LITTLE system) they should be in different clusters. If the CPUs do + * not have the same view of memory (for example the main CPU and a management + * controller processor) they should be in different clusters. + */ + +#define TYPE_CPU_CLUSTER "cpu-cluster" +#define CPU_CLUSTER(obj) \ + OBJECT_CHECK(CPUClusterState, (obj), TYPE_CPU_CLUSTER) + +/** + * CPUClusterState: + * @cluster_id: The cluster ID. This value is for internal use only and should + * not be exposed directly to the user or to the guest. + * + * State of a CPU cluster. + */ +typedef struct CPUClusterState { + /*< private >*/ + DeviceState parent_obj; + + /*< public >*/ + uint32_t cluster_id; +} CPUClusterState; + +#endif From 8f468636189303358da3b365c53cb03ae5497eca Mon Sep 17 00:00:00 2001 From: Luc Michel Date: Mon, 7 Jan 2019 15:23:45 +0000 Subject: [PATCH 04/37] gdbstub: introduce GDB processes MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add a structure GDBProcess that represents processes from the GDB semantic point of view. CPUs can be split into different processes, by grouping them under different cpu-cluster objects. Each occurrence of a cpu-cluster object implies the existence of the corresponding process in the GDB stub. The GDB process ID is derived from the corresponding cluster ID as follows: GDB PID = cluster ID + 1 This is because PIDs -1 and 0 are reserved in GDB and cannot be used by processes. A default process is created to handle CPUs that are not in a cluster. This process gets the PID of the last process PID + 1. Signed-off-by: Luc Michel Acked-by: Alistair Francis Reviewed-by: Edgar E. Iglesias Reviewed-by: Philippe Mathieu-Daudé Message-id: 20181207090135.7651-3-luc.michel@greensocs.com [PMM: fixed checkpatch nit about block comment style] Signed-off-by: Peter Maydell --- gdbstub.c | 97 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 97 insertions(+) diff --git a/gdbstub.c b/gdbstub.c index c4e4f9f082..9ac6f19a18 100644 --- a/gdbstub.c +++ b/gdbstub.c @@ -29,6 +29,7 @@ #include "chardev/char-fe.h" #include "sysemu/sysemu.h" #include "exec/gdbstub.h" +#include "hw/cpu/cluster.h" #endif #define MAX_PACKET_LENGTH 4096 @@ -296,6 +297,11 @@ typedef struct GDBRegisterState { struct GDBRegisterState *next; } GDBRegisterState; +typedef struct GDBProcess { + uint32_t pid; + bool attached; +} GDBProcess; + enum RSState { RS_INACTIVE, RS_IDLE, @@ -324,6 +330,9 @@ typedef struct GDBState { CharBackend chr; Chardev *mon_chr; #endif + bool multiprocess; + GDBProcess *processes; + int process_num; char syscall_buf[256]; gdb_syscall_complete_cb current_syscall_cb; } GDBState; @@ -1751,6 +1760,30 @@ void gdb_exit(CPUArchState *env, int code) #endif } +/* + * Create the process that will contain all the "orphan" CPUs (that are not + * part of a CPU cluster). Note that if this process contains no CPUs, it won't + * be attachable and thus will be invisible to the user. + */ +static void create_default_process(GDBState *s) +{ + GDBProcess *process; + int max_pid = 0; + + if (s->process_num) { + max_pid = s->processes[s->process_num - 1].pid; + } + + s->processes = g_renew(GDBProcess, s->processes, ++s->process_num); + process = &s->processes[s->process_num - 1]; + + /* We need an available PID slot for this process */ + assert(max_pid < UINT32_MAX); + + process->pid = max_pid + 1; + process->attached = false; +} + #ifdef CONFIG_USER_ONLY int gdb_handlesig(CPUState *cpu, int sig) @@ -1848,6 +1881,7 @@ static bool gdb_accept(void) s = g_malloc0(sizeof(GDBState)); s->c_cpu = first_cpu; s->g_cpu = first_cpu; + create_default_process(s); s->fd = fd; gdb_has_xml = false; @@ -2004,6 +2038,65 @@ static const TypeInfo char_gdb_type_info = { .class_init = char_gdb_class_init, }; +static int find_cpu_clusters(Object *child, void *opaque) +{ + if (object_dynamic_cast(child, TYPE_CPU_CLUSTER)) { + GDBState *s = (GDBState *) opaque; + CPUClusterState *cluster = CPU_CLUSTER(child); + GDBProcess *process; + + s->processes = g_renew(GDBProcess, s->processes, ++s->process_num); + + process = &s->processes[s->process_num - 1]; + + /* + * GDB process IDs -1 and 0 are reserved. To avoid subtle errors at + * runtime, we enforce here that the machine does not use a cluster ID + * that would lead to PID 0. + */ + assert(cluster->cluster_id != UINT32_MAX); + process->pid = cluster->cluster_id + 1; + process->attached = false; + + return 0; + } + + return object_child_foreach(child, find_cpu_clusters, opaque); +} + +static int pid_order(const void *a, const void *b) +{ + GDBProcess *pa = (GDBProcess *) a; + GDBProcess *pb = (GDBProcess *) b; + + if (pa->pid < pb->pid) { + return -1; + } else if (pa->pid > pb->pid) { + return 1; + } else { + return 0; + } +} + +static void create_processes(GDBState *s) +{ + object_child_foreach(object_get_root(), find_cpu_clusters, s); + + if (s->processes) { + /* Sort by PID */ + qsort(s->processes, s->process_num, sizeof(s->processes[0]), pid_order); + } + + create_default_process(s); +} + +static void cleanup_processes(GDBState *s) +{ + g_free(s->processes); + s->process_num = 0; + s->processes = NULL; +} + int gdbserver_start(const char *device) { trace_gdbstub_op_start(device); @@ -2060,11 +2153,15 @@ int gdbserver_start(const char *device) } else { qemu_chr_fe_deinit(&s->chr, true); mon_chr = s->mon_chr; + cleanup_processes(s); memset(s, 0, sizeof(GDBState)); s->mon_chr = mon_chr; } s->c_cpu = first_cpu; s->g_cpu = first_cpu; + + create_processes(s); + if (chr) { qemu_chr_fe_init(&s->chr, chr, &error_abort); qemu_chr_fe_set_handlers(&s->chr, gdb_chr_can_receive, gdb_chr_receive, From 1a2273369725111a2bbdb602ca7c679d466618b0 Mon Sep 17 00:00:00 2001 From: Luc Michel Date: Mon, 7 Jan 2019 15:23:45 +0000 Subject: [PATCH 05/37] gdbstub: add multiprocess support to '?' packets The gdb_get_cpu_pid() function does the PID lookup for the given CPU. It checks if the CPU is a direct child of a CPU cluster. If it is, the returned PID is the cluster ID plus one (cluster IDs start at 0, GDB PIDs at 1). When the CPU is not a child of such a container, the PID of the default process is returned. The gdb_fmt_thread_id() function generates the string to be used to identify a given thread, in a response packet for the peer. This function supports generating thread IDs when multiprocess mode is enabled (in the form `p.'). Use them in the reply to a '?' request. Signed-off-by: Luc Michel Acked-by: Alistair Francis Reviewed-by: Edgar E. Iglesias Message-id: 20181207090135.7651-4-luc.michel@greensocs.com [PMM: fixed checkpatch blockquote style nit] Signed-off-by: Peter Maydell --- gdbstub.c | 66 +++++++++++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 64 insertions(+), 2 deletions(-) diff --git a/gdbstub.c b/gdbstub.c index 9ac6f19a18..02beb44d97 100644 --- a/gdbstub.c +++ b/gdbstub.c @@ -640,6 +640,54 @@ static int memtox(char *buf, const char *mem, int len) return p - buf; } +static uint32_t gdb_get_cpu_pid(const GDBState *s, CPUState *cpu) +{ +#ifndef CONFIG_USER_ONLY + gchar *path, *name = NULL; + Object *obj; + CPUClusterState *cluster; + uint32_t ret; + + path = object_get_canonical_path(OBJECT(cpu)); + + if (path == NULL) { + /* Return the default process' PID */ + ret = s->processes[s->process_num - 1].pid; + goto out; + } + + name = object_get_canonical_path_component(OBJECT(cpu)); + assert(name != NULL); + + /* + * Retrieve the CPU parent path by removing the last '/' and the CPU name + * from the CPU canonical path. + */ + path[strlen(path) - strlen(name) - 1] = '\0'; + + obj = object_resolve_path_type(path, TYPE_CPU_CLUSTER, NULL); + + if (obj == NULL) { + /* Return the default process' PID */ + ret = s->processes[s->process_num - 1].pid; + goto out; + } + + cluster = CPU_CLUSTER(obj); + ret = cluster->cluster_id + 1; + +out: + g_free(name); + g_free(path); + + return ret; + +#else + /* TODO: In user mode, we should use the task state PID */ + return s->processes[s->process_num - 1].pid; +#endif +} + static const char *get_feature_xml(const char *p, const char **newp, CPUClass *cc) { @@ -909,6 +957,19 @@ static CPUState *find_cpu(uint32_t thread_id) return NULL; } +static char *gdb_fmt_thread_id(const GDBState *s, CPUState *cpu, + char *buf, size_t buf_size) +{ + if (s->multiprocess) { + snprintf(buf, buf_size, "p%02x.%02x", + gdb_get_cpu_pid(s, cpu), cpu_gdb_index(cpu)); + } else { + snprintf(buf, buf_size, "%02x", cpu_gdb_index(cpu)); + } + + return buf; +} + static int is_query_packet(const char *p, const char *query, char separator) { unsigned int query_len = strlen(query); @@ -1020,6 +1081,7 @@ static int gdb_handle_packet(GDBState *s, const char *line_buf) int ch, reg_size, type, res; uint8_t mem_buf[MAX_PACKET_LENGTH]; char buf[sizeof(mem_buf) + 1 /* trailing NUL */]; + char thread_id[16]; uint8_t *registers; target_ulong addr, len; @@ -1030,8 +1092,8 @@ static int gdb_handle_packet(GDBState *s, const char *line_buf) switch(ch) { case '?': /* TODO: Make this return the correct value for user-mode. */ - snprintf(buf, sizeof(buf), "T%02xthread:%02x;", GDB_SIGNAL_TRAP, - cpu_gdb_index(s->c_cpu)); + snprintf(buf, sizeof(buf), "T%02xthread:%s;", GDB_SIGNAL_TRAP, + gdb_fmt_thread_id(s, s->c_cpu, thread_id, sizeof(thread_id))); put_packet(s, buf); /* Remove all the breakpoints when this query is issued, * because gdb is doing and initial connect and the state From 7d8c87da79c23b2caa8f68a0a9cd0b15cedf8b41 Mon Sep 17 00:00:00 2001 From: Luc Michel Date: Mon, 7 Jan 2019 15:23:45 +0000 Subject: [PATCH 06/37] gdbstub: add multiprocess support to 'H' and 'T' packets MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add a couple of helper functions to cope with GDB threads and processes. The gdb_get_process() function looks for a process given a pid. The gdb_get_cpu() function returns the CPU corresponding to the (pid, tid) pair given as parameters. The read_thread_id() function parses the thread-id sent by the peer. This function supports the multiprocess extension thread-id syntax. The return value specifies if the parsing failed, or if a special case was encountered (all processes or all threads). Use them in 'H' and 'T' packets handling to support the multiprocess extension. Signed-off-by: Luc Michel Reviewed-by: Philippe Mathieu-Daudé Reviewed-by: Edgar E. Iglesias Acked-by: Alistair Francis Message-id: 20181207090135.7651-5-luc.michel@greensocs.com Signed-off-by: Peter Maydell --- gdbstub.c | 154 +++++++++++++++++++++++++++++++++++++++++++++++------- 1 file changed, 136 insertions(+), 18 deletions(-) diff --git a/gdbstub.c b/gdbstub.c index 02beb44d97..644377db9f 100644 --- a/gdbstub.c +++ b/gdbstub.c @@ -688,6 +688,71 @@ out: #endif } +static GDBProcess *gdb_get_process(const GDBState *s, uint32_t pid) +{ + int i; + + if (!pid) { + /* 0 means any process, we take the first one */ + return &s->processes[0]; + } + + for (i = 0; i < s->process_num; i++) { + if (s->processes[i].pid == pid) { + return &s->processes[i]; + } + } + + return NULL; +} + +static GDBProcess *gdb_get_cpu_process(const GDBState *s, CPUState *cpu) +{ + return gdb_get_process(s, gdb_get_cpu_pid(s, cpu)); +} + +static CPUState *find_cpu(uint32_t thread_id) +{ + CPUState *cpu; + + CPU_FOREACH(cpu) { + if (cpu_gdb_index(cpu) == thread_id) { + return cpu; + } + } + + return NULL; +} + +static CPUState *gdb_get_cpu(const GDBState *s, uint32_t pid, uint32_t tid) +{ + GDBProcess *process; + CPUState *cpu; + + if (!tid) { + /* 0 means any thread, we take the first one */ + tid = 1; + } + + cpu = find_cpu(tid); + + if (cpu == NULL) { + return NULL; + } + + process = gdb_get_cpu_process(s, cpu); + + if (process->pid != pid) { + return NULL; + } + + if (!process->attached) { + return NULL; + } + + return cpu; +} + static const char *get_feature_xml(const char *p, const char **newp, CPUClass *cc) { @@ -944,19 +1009,6 @@ static void gdb_set_cpu_pc(GDBState *s, target_ulong pc) cpu_set_pc(cpu, pc); } -static CPUState *find_cpu(uint32_t thread_id) -{ - CPUState *cpu; - - CPU_FOREACH(cpu) { - if (cpu_gdb_index(cpu) == thread_id) { - return cpu; - } - } - - return NULL; -} - static char *gdb_fmt_thread_id(const GDBState *s, CPUState *cpu, char *buf, size_t buf_size) { @@ -970,6 +1022,60 @@ static char *gdb_fmt_thread_id(const GDBState *s, CPUState *cpu, return buf; } +typedef enum GDBThreadIdKind { + GDB_ONE_THREAD = 0, + GDB_ALL_THREADS, /* One process, all threads */ + GDB_ALL_PROCESSES, + GDB_READ_THREAD_ERR +} GDBThreadIdKind; + +static GDBThreadIdKind read_thread_id(const char *buf, const char **end_buf, + uint32_t *pid, uint32_t *tid) +{ + unsigned long p, t; + int ret; + + if (*buf == 'p') { + buf++; + ret = qemu_strtoul(buf, &buf, 16, &p); + + if (ret) { + return GDB_READ_THREAD_ERR; + } + + /* Skip '.' */ + buf++; + } else { + p = 1; + } + + ret = qemu_strtoul(buf, &buf, 16, &t); + + if (ret) { + return GDB_READ_THREAD_ERR; + } + + *end_buf = buf; + + if (p == -1) { + return GDB_ALL_PROCESSES; + } + + if (pid) { + *pid = p; + } + + if (t == -1) { + return GDB_ALL_THREADS; + } + + if (tid) { + *tid = t; + } + + return GDB_ONE_THREAD; +} + static int is_query_packet(const char *p, const char *query, char separator) { unsigned int query_len = strlen(query); @@ -1078,12 +1184,14 @@ static int gdb_handle_packet(GDBState *s, const char *line_buf) CPUClass *cc; const char *p; uint32_t thread; + uint32_t pid, tid; int ch, reg_size, type, res; uint8_t mem_buf[MAX_PACKET_LENGTH]; char buf[sizeof(mem_buf) + 1 /* trailing NUL */]; char thread_id[16]; uint8_t *registers; target_ulong addr, len; + GDBThreadIdKind thread_kind; trace_gdbstub_io_command(line_buf); @@ -1291,12 +1399,18 @@ static int gdb_handle_packet(GDBState *s, const char *line_buf) break; case 'H': type = *p++; - thread = strtoull(p, (char **)&p, 16); - if (thread == -1 || thread == 0) { + + thread_kind = read_thread_id(p, &p, &pid, &tid); + if (thread_kind == GDB_READ_THREAD_ERR) { + put_packet(s, "E22"); + break; + } + + if (thread_kind != GDB_ONE_THREAD) { put_packet(s, "OK"); break; } - cpu = find_cpu(thread); + cpu = gdb_get_cpu(s, pid, tid); if (cpu == NULL) { put_packet(s, "E22"); break; @@ -1316,8 +1430,12 @@ static int gdb_handle_packet(GDBState *s, const char *line_buf) } break; case 'T': - thread = strtoull(p, (char **)&p, 16); - cpu = find_cpu(thread); + thread_kind = read_thread_id(p, &p, &pid, &tid); + if (thread_kind == GDB_READ_THREAD_ERR) { + put_packet(s, "E22"); + break; + } + cpu = gdb_get_cpu(s, pid, tid); if (cpu != NULL) { put_packet(s, "OK"); From e40e5204af8388e605df2325d9b562c05919350e Mon Sep 17 00:00:00 2001 From: Luc Michel Date: Mon, 7 Jan 2019 15:23:46 +0000 Subject: [PATCH 07/37] gdbstub: add multiprocess support to vCont packets MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add the gdb_first_attached_cpu() and gdb_next_attached_cpu() to iterate over all the CPUs in currently attached processes. Add the gdb_first_cpu_in_process() and gdb_next_cpu_in_process() to iterate over CPUs of a given process. Use them to add multiprocess extension support to vCont packets. Signed-off-by: Luc Michel Reviewed-by: Philippe Mathieu-Daudé Reviewed-by: Edgar E. Iglesias Acked-by: Alistair Francis Message-id: 20181207090135.7651-6-luc.michel@greensocs.com [PMM: corrected checkpatch comment style nit] Signed-off-by: Peter Maydell --- gdbstub.c | 115 +++++++++++++++++++++++++++++++++++++++++++++++------- 1 file changed, 100 insertions(+), 15 deletions(-) diff --git a/gdbstub.c b/gdbstub.c index 644377db9f..09114ea776 100644 --- a/gdbstub.c +++ b/gdbstub.c @@ -724,6 +724,36 @@ static CPUState *find_cpu(uint32_t thread_id) return NULL; } +static CPUState *get_first_cpu_in_process(const GDBState *s, + GDBProcess *process) +{ + CPUState *cpu; + + CPU_FOREACH(cpu) { + if (gdb_get_cpu_pid(s, cpu) == process->pid) { + return cpu; + } + } + + return NULL; +} + +static CPUState *gdb_next_cpu_in_process(const GDBState *s, CPUState *cpu) +{ + uint32_t pid = gdb_get_cpu_pid(s, cpu); + cpu = CPU_NEXT(cpu); + + while (cpu) { + if (gdb_get_cpu_pid(s, cpu) == pid) { + break; + } + + cpu = CPU_NEXT(cpu); + } + + return cpu; +} + static CPUState *gdb_get_cpu(const GDBState *s, uint32_t pid, uint32_t tid) { GDBProcess *process; @@ -753,6 +783,35 @@ static CPUState *gdb_get_cpu(const GDBState *s, uint32_t pid, uint32_t tid) return cpu; } +/* Return the cpu following @cpu, while ignoring unattached processes. */ +static CPUState *gdb_next_attached_cpu(const GDBState *s, CPUState *cpu) +{ + cpu = CPU_NEXT(cpu); + + while (cpu) { + if (gdb_get_cpu_process(s, cpu)->attached) { + break; + } + + cpu = CPU_NEXT(cpu); + } + + return cpu; +} + +/* Return the first attached cpu */ +static CPUState *gdb_first_attached_cpu(const GDBState *s) +{ + CPUState *cpu = first_cpu; + GDBProcess *process = gdb_get_cpu_process(s, cpu); + + if (!process->attached) { + return gdb_next_attached_cpu(s, cpu); + } + + return cpu; +} + static const char *get_feature_xml(const char *p, const char **newp, CPUClass *cc) { @@ -1091,10 +1150,12 @@ static int is_query_packet(const char *p, const char *query, char separator) */ static int gdb_handle_vcont(GDBState *s, const char *p) { - int res, idx, signal = 0; + int res, signal = 0; char cur_action; char *newstates; unsigned long tmp; + uint32_t pid, tid; + GDBProcess *process; CPUState *cpu; #ifdef CONFIG_USER_ONLY int max_cpus = 1; /* global variable max_cpus exists only in system mode */ @@ -1137,25 +1198,48 @@ static int gdb_handle_vcont(GDBState *s, const char *p) res = -ENOTSUP; goto out; } - /* thread specification. special values: (none), -1 = all; 0 = any */ - if ((p[0] == ':' && p[1] == '-' && p[2] == '1') || (p[0] != ':')) { - if (*p == ':') { - p += 3; - } - for (idx = 0; idx < max_cpus; idx++) { - if (newstates[idx] == 1) { - newstates[idx] = cur_action; + + if (*p++ != ':') { + res = -ENOTSUP; + goto out; + } + + switch (read_thread_id(p, &p, &pid, &tid)) { + case GDB_READ_THREAD_ERR: + res = -EINVAL; + goto out; + + case GDB_ALL_PROCESSES: + cpu = gdb_first_attached_cpu(s); + while (cpu) { + if (newstates[cpu->cpu_index] == 1) { + newstates[cpu->cpu_index] = cur_action; } + + cpu = gdb_next_attached_cpu(s, cpu); } - } else if (*p == ':') { - p++; - res = qemu_strtoul(p, &p, 16, &tmp); - if (res) { + break; + + case GDB_ALL_THREADS: + process = gdb_get_process(s, pid); + + if (!process->attached) { + res = -EINVAL; goto out; } - /* 0 means any thread, so we pick the first valid CPU */ - cpu = tmp ? find_cpu(tmp) : first_cpu; + cpu = get_first_cpu_in_process(s, process); + while (cpu) { + if (newstates[cpu->cpu_index] == 1) { + newstates[cpu->cpu_index] = cur_action; + } + + cpu = gdb_next_cpu_in_process(s, cpu); + } + break; + + case GDB_ONE_THREAD: + cpu = gdb_get_cpu(s, pid, tid); /* invalid CPU/thread specified */ if (!cpu) { @@ -1167,6 +1251,7 @@ static int gdb_handle_vcont(GDBState *s, const char *p) if (newstates[cpu->cpu_index] == 1) { newstates[cpu->cpu_index] = cur_action; } + break; } } s->signal = signal; From 8dbbe9ac7f9c838d80fdf06b133daa12ffc3f264 Mon Sep 17 00:00:00 2001 From: Luc Michel Date: Mon, 7 Jan 2019 15:23:46 +0000 Subject: [PATCH 08/37] gdbstub: add multiprocess support to 'sC' packets MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Change the sC packet handling to support the multiprocess extension. Instead of returning the first thread, we return the first thread of the current process. Signed-off-by: Luc Michel Reviewed-by: Philippe Mathieu-Daudé Reviewed-by: Alistair Francis Reviewed-by: Edgar E. Iglesias Message-id: 20181207090135.7651-7-luc.michel@greensocs.com [PMM: corrected checkpatch comment style nit] Signed-off-by: Peter Maydell --- gdbstub.c | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/gdbstub.c b/gdbstub.c index 09114ea776..1ba7aa6a28 100644 --- a/gdbstub.c +++ b/gdbstub.c @@ -1554,9 +1554,15 @@ static int gdb_handle_packet(GDBState *s, const char *line_buf) put_packet(s, "OK"); break; } else if (strcmp(p,"C") == 0) { - /* "Current thread" remains vague in the spec, so always return - * the first CPU (gdb returns the first thread). */ - put_packet(s, "QC1"); + /* + * "Current thread" remains vague in the spec, so always return + * the first thread of the current process (gdb returns the + * first thread). + */ + cpu = get_first_cpu_in_process(s, gdb_get_cpu_process(s, s->g_cpu)); + snprintf(buf, sizeof(buf), "QC%s", + gdb_fmt_thread_id(s, cpu, thread_id, sizeof(thread_id))); + put_packet(s, buf); break; } else if (strcmp(p,"fThreadInfo") == 0) { s->query_cpu = first_cpu; From 7cf48f6752e5ab7e10426264a4cf5d7c70f62284 Mon Sep 17 00:00:00 2001 From: Luc Michel Date: Mon, 7 Jan 2019 15:23:46 +0000 Subject: [PATCH 09/37] gdbstub: add multiprocess support to (f|s)ThreadInfo and ThreadExtraInfo MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Change the thread info related packets handling to support multiprocess extension. Add the CPUs class name in the extra info to help differentiate them in multiprocess mode. Signed-off-by: Luc Michel Reviewed-by: Philippe Mathieu-Daudé Reviewed-by: Edgar E. Iglesias Reviewed-by: Alistair Francis Message-id: 20181207090135.7651-8-luc.michel@greensocs.com Signed-off-by: Peter Maydell --- gdbstub.c | 37 +++++++++++++++++++++++++++---------- 1 file changed, 27 insertions(+), 10 deletions(-) diff --git a/gdbstub.c b/gdbstub.c index 1ba7aa6a28..f70b5a326f 100644 --- a/gdbstub.c +++ b/gdbstub.c @@ -1268,7 +1268,6 @@ static int gdb_handle_packet(GDBState *s, const char *line_buf) CPUState *cpu; CPUClass *cc; const char *p; - uint32_t thread; uint32_t pid, tid; int ch, reg_size, type, res; uint8_t mem_buf[MAX_PACKET_LENGTH]; @@ -1565,26 +1564,44 @@ static int gdb_handle_packet(GDBState *s, const char *line_buf) put_packet(s, buf); break; } else if (strcmp(p,"fThreadInfo") == 0) { - s->query_cpu = first_cpu; + s->query_cpu = gdb_first_attached_cpu(s); goto report_cpuinfo; } else if (strcmp(p,"sThreadInfo") == 0) { report_cpuinfo: if (s->query_cpu) { - snprintf(buf, sizeof(buf), "m%x", cpu_gdb_index(s->query_cpu)); + snprintf(buf, sizeof(buf), "m%s", + gdb_fmt_thread_id(s, s->query_cpu, + thread_id, sizeof(thread_id))); put_packet(s, buf); - s->query_cpu = CPU_NEXT(s->query_cpu); + s->query_cpu = gdb_next_attached_cpu(s, s->query_cpu); } else put_packet(s, "l"); break; } else if (strncmp(p,"ThreadExtraInfo,", 16) == 0) { - thread = strtoull(p+16, (char **)&p, 16); - cpu = find_cpu(thread); + if (read_thread_id(p + 16, &p, &pid, &tid) == GDB_READ_THREAD_ERR) { + put_packet(s, "E22"); + break; + } + cpu = gdb_get_cpu(s, pid, tid); if (cpu != NULL) { cpu_synchronize_state(cpu); - /* memtohex() doubles the required space */ - len = snprintf((char *)mem_buf, sizeof(buf) / 2, - "CPU#%d [%s]", cpu->cpu_index, - cpu->halted ? "halted " : "running"); + + if (s->multiprocess && (s->process_num > 1)) { + /* Print the CPU model and name in multiprocess mode */ + ObjectClass *oc = object_get_class(OBJECT(cpu)); + const char *cpu_model = object_class_get_name(oc); + char *cpu_name = + object_get_canonical_path_component(OBJECT(cpu)); + len = snprintf((char *)mem_buf, sizeof(buf) / 2, + "%s %s [%s]", cpu_model, cpu_name, + cpu->halted ? "halted " : "running"); + g_free(cpu_name); + } else { + /* memtohex() doubles the required space */ + len = snprintf((char *)mem_buf, sizeof(buf) / 2, + "CPU#%d [%s]", cpu->cpu_index, + cpu->halted ? "halted " : "running"); + } trace_gdbstub_op_extra_info((char *)mem_buf); memtohex(buf, mem_buf, len); put_packet(s, buf); From c145eeae1cc7bb826fd60056bbd072e3881dc128 Mon Sep 17 00:00:00 2001 From: Luc Michel Date: Mon, 7 Jan 2019 15:23:46 +0000 Subject: [PATCH 10/37] gdbstub: add multiprocess support to Xfer:features:read: MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Change the Xfer:features:read: packet handling to support the multiprocess extension. This packet is used to request the XML description of the CPU. In multiprocess mode, different descriptions can be sent for different processes. This function now takes the process to send the description for as a parameter, and use a buffer in the process structure to store the generated description. It takes the first CPU of the process to generate the description. Signed-off-by: Luc Michel Reviewed-by: Philippe Mathieu-Daudé Reviewed-by: Edgar E. Iglesias Reviewed-by: Alistair Francis Message-id: 20181207090135.7651-9-luc.michel@greensocs.com Signed-off-by: Peter Maydell --- gdbstub.c | 52 ++++++++++++++++++++++++++++++---------------------- 1 file changed, 30 insertions(+), 22 deletions(-) diff --git a/gdbstub.c b/gdbstub.c index f70b5a326f..1f2b155490 100644 --- a/gdbstub.c +++ b/gdbstub.c @@ -300,6 +300,8 @@ typedef struct GDBRegisterState { typedef struct GDBProcess { uint32_t pid; bool attached; + + char target_xml[1024]; } GDBProcess; enum RSState { @@ -812,13 +814,14 @@ static CPUState *gdb_first_attached_cpu(const GDBState *s) return cpu; } -static const char *get_feature_xml(const char *p, const char **newp, - CPUClass *cc) +static const char *get_feature_xml(const GDBState *s, const char *p, + const char **newp, GDBProcess *process) { size_t len; int i; const char *name; - static char target_xml[1024]; + CPUState *cpu = get_first_cpu_in_process(s, process); + CPUClass *cc = CPU_GET_CLASS(cpu); len = 0; while (p[len] && p[len] != ':') @@ -827,36 +830,37 @@ static const char *get_feature_xml(const char *p, const char **newp, name = NULL; if (strncmp(p, "target.xml", len) == 0) { - /* Generate the XML description for this CPU. */ - if (!target_xml[0]) { - GDBRegisterState *r; - CPUState *cpu = first_cpu; + char *buf = process->target_xml; + const size_t buf_sz = sizeof(process->target_xml); - pstrcat(target_xml, sizeof(target_xml), + /* Generate the XML description for this CPU. */ + if (!buf[0]) { + GDBRegisterState *r; + + pstrcat(buf, buf_sz, "" "" ""); if (cc->gdb_arch_name) { gchar *arch = cc->gdb_arch_name(cpu); - pstrcat(target_xml, sizeof(target_xml), ""); - pstrcat(target_xml, sizeof(target_xml), arch); - pstrcat(target_xml, sizeof(target_xml), ""); + pstrcat(buf, buf_sz, ""); + pstrcat(buf, buf_sz, arch); + pstrcat(buf, buf_sz, ""); g_free(arch); } - pstrcat(target_xml, sizeof(target_xml), "gdb_core_xml_file); - pstrcat(target_xml, sizeof(target_xml), "\"/>"); + pstrcat(buf, buf_sz, "gdb_core_xml_file); + pstrcat(buf, buf_sz, "\"/>"); for (r = cpu->gdb_regs; r; r = r->next) { - pstrcat(target_xml, sizeof(target_xml), "xml); - pstrcat(target_xml, sizeof(target_xml), "\"/>"); + pstrcat(buf, buf_sz, "xml); + pstrcat(buf, buf_sz, "\"/>"); } - pstrcat(target_xml, sizeof(target_xml), ""); + pstrcat(buf, buf_sz, ""); } - return target_xml; + return buf; } if (cc->gdb_get_dynamic_xml) { - CPUState *cpu = first_cpu; char *xmlname = g_strndup(p, len); const char *xml = cc->gdb_get_dynamic_xml(cpu, xmlname); @@ -1266,6 +1270,7 @@ out: static int gdb_handle_packet(GDBState *s, const char *line_buf) { CPUState *cpu; + GDBProcess *process; CPUClass *cc; const char *p; uint32_t pid, tid; @@ -1650,14 +1655,15 @@ static int gdb_handle_packet(GDBState *s, const char *line_buf) const char *xml; target_ulong total_len; - cc = CPU_GET_CLASS(first_cpu); + process = gdb_get_cpu_process(s, s->g_cpu); + cc = CPU_GET_CLASS(s->g_cpu); if (cc->gdb_core_xml_file == NULL) { goto unknown_command; } gdb_has_xml = true; p += 19; - xml = get_feature_xml(p, &p, cc); + xml = get_feature_xml(s, p, &p, process); if (!xml) { snprintf(buf, sizeof(buf), "E00"); put_packet(s, buf); @@ -2070,6 +2076,7 @@ static void create_default_process(GDBState *s) process->pid = max_pid + 1; process->attached = false; + process->target_xml[0] = '\0'; } #ifdef CONFIG_USER_ONLY @@ -2345,6 +2352,7 @@ static int find_cpu_clusters(Object *child, void *opaque) assert(cluster->cluster_id != UINT32_MAX); process->pid = cluster->cluster_id + 1; process->attached = false; + process->target_xml[0] = '\0'; return 0; } From 95567c27121e58795949c77fd7503ac03451a62b Mon Sep 17 00:00:00 2001 From: Luc Michel Date: Mon, 7 Jan 2019 15:23:46 +0000 Subject: [PATCH 11/37] gdbstub: add multiprocess support to gdb_vm_state_change() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add support for multiprocess extension in gdb_vm_state_change() function. Signed-off-by: Luc Michel Reviewed-by: Philippe Mathieu-Daudé Reviewed-by: Edgar E. Iglesias Acked-by: Alistair Francis Message-id: 20181207090135.7651-10-luc.michel@greensocs.com Signed-off-by: Peter Maydell --- gdbstub.c | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/gdbstub.c b/gdbstub.c index 1f2b155490..edee38b613 100644 --- a/gdbstub.c +++ b/gdbstub.c @@ -1724,6 +1724,7 @@ static void gdb_vm_state_change(void *opaque, int running, RunState state) GDBState *s = gdbserver_state; CPUState *cpu = s->c_cpu; char buf[256]; + char thread_id[16]; const char *type; int ret; @@ -1735,6 +1736,14 @@ static void gdb_vm_state_change(void *opaque, int running, RunState state) put_packet(s, s->syscall_buf); return; } + + if (cpu == NULL) { + /* No process attached */ + return; + } + + gdb_fmt_thread_id(s, cpu, thread_id, sizeof(thread_id)); + switch (state) { case RUN_STATE_DEBUG: if (cpu->watchpoint_hit) { @@ -1752,8 +1761,8 @@ static void gdb_vm_state_change(void *opaque, int running, RunState state) trace_gdbstub_hit_watchpoint(type, cpu_gdb_index(cpu), (target_ulong)cpu->watchpoint_hit->vaddr); snprintf(buf, sizeof(buf), - "T%02xthread:%02x;%swatch:" TARGET_FMT_lx ";", - GDB_SIGNAL_TRAP, cpu_gdb_index(cpu), type, + "T%02xthread:%s;%swatch:" TARGET_FMT_lx ";", + GDB_SIGNAL_TRAP, thread_id, type, (target_ulong)cpu->watchpoint_hit->vaddr); cpu->watchpoint_hit = NULL; goto send_packet; @@ -1795,7 +1804,7 @@ static void gdb_vm_state_change(void *opaque, int running, RunState state) break; } gdb_set_stop_cpu(cpu); - snprintf(buf, sizeof(buf), "T%02xthread:%02x;", ret, cpu_gdb_index(cpu)); + snprintf(buf, sizeof(buf), "T%02xthread:%s;", ret, thread_id); send_packet: put_packet(s, buf); From 546f3c676c2824a3718a7d74dfb2f663f9348760 Mon Sep 17 00:00:00 2001 From: Luc Michel Date: Mon, 7 Jan 2019 15:23:46 +0000 Subject: [PATCH 12/37] gdbstub: add multiprocess support to 'D' packets MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 'D' packets are used by GDB to detach from a process. In multiprocess mode, the PID to detach from is sent in the request. Signed-off-by: Luc Michel Reviewed-by: Philippe Mathieu-Daudé Reviewed-by: Edgar E. Iglesias Acked-by: Alistair Francis Message-id: 20181207090135.7651-11-luc.michel@greensocs.com Signed-off-by: Peter Maydell --- gdbstub.c | 60 ++++++++++++++++++++++++++++++++++++++++++++++++------- 1 file changed, 53 insertions(+), 7 deletions(-) diff --git a/gdbstub.c b/gdbstub.c index edee38b613..2c7032f53a 100644 --- a/gdbstub.c +++ b/gdbstub.c @@ -1047,6 +1047,24 @@ static int gdb_breakpoint_remove(target_ulong addr, target_ulong len, int type) } } +static inline void gdb_cpu_breakpoint_remove_all(CPUState *cpu) +{ + cpu_breakpoint_remove_all(cpu, BP_GDB); +#ifndef CONFIG_USER_ONLY + cpu_watchpoint_remove_all(cpu, BP_GDB); +#endif +} + +static void gdb_process_breakpoint_remove_all(const GDBState *s, GDBProcess *p) +{ + CPUState *cpu = get_first_cpu_in_process(s, p); + + while (cpu) { + gdb_cpu_breakpoint_remove_all(cpu); + cpu = gdb_next_cpu_in_process(s, cpu); + } +} + static void gdb_breakpoint_remove_all(void) { CPUState *cpu; @@ -1057,10 +1075,7 @@ static void gdb_breakpoint_remove_all(void) } CPU_FOREACH(cpu) { - cpu_breakpoint_remove_all(cpu, BP_GDB); -#ifndef CONFIG_USER_ONLY - cpu_watchpoint_remove_all(cpu, BP_GDB); -#endif + gdb_cpu_breakpoint_remove_all(cpu); } } @@ -1339,9 +1354,40 @@ static int gdb_handle_packet(GDBState *s, const char *line_buf) exit(0); case 'D': /* Detach packet */ - gdb_breakpoint_remove_all(); - gdb_syscall_mode = GDB_SYS_DISABLED; - gdb_continue(s); + pid = 1; + + if (s->multiprocess) { + unsigned long lpid; + if (*p != ';') { + put_packet(s, "E22"); + break; + } + + if (qemu_strtoul(p + 1, &p, 16, &lpid)) { + put_packet(s, "E22"); + break; + } + + pid = lpid; + } + + process = gdb_get_process(s, pid); + gdb_process_breakpoint_remove_all(s, process); + process->attached = false; + + if (pid == gdb_get_cpu_pid(s, s->c_cpu)) { + s->c_cpu = gdb_first_attached_cpu(s); + } + + if (pid == gdb_get_cpu_pid(s, s->g_cpu)) { + s->g_cpu = gdb_first_attached_cpu(s); + } + + if (s->c_cpu == NULL) { + /* No more process attached */ + gdb_syscall_mode = GDB_SYS_DISABLED; + gdb_continue(s); + } put_packet(s, "OK"); break; case 's': From 53fd65541511386564fb53a91b594459e7f06d7e Mon Sep 17 00:00:00 2001 From: Luc Michel Date: Mon, 7 Jan 2019 15:23:46 +0000 Subject: [PATCH 13/37] gdbstub: add support for extended mode packet Add support for the '!' extended mode packet. This is required for the multiprocess extension. Signed-off-by: Luc Michel Reviewed-by: Edgar E. Iglesias Acked-by: Alistair Francis Message-id: 20181207090135.7651-12-luc.michel@greensocs.com Signed-off-by: Peter Maydell --- gdbstub.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/gdbstub.c b/gdbstub.c index 2c7032f53a..9b6b4d20a0 100644 --- a/gdbstub.c +++ b/gdbstub.c @@ -1302,6 +1302,9 @@ static int gdb_handle_packet(GDBState *s, const char *line_buf) p = line_buf; ch = *p++; switch(ch) { + case '!': + put_packet(s, "OK"); + break; case '?': /* TODO: Make this return the correct value for user-mode. */ snprintf(buf, sizeof(buf), "T%02xthread:%s;", GDB_SIGNAL_TRAP, From 3f940dc988b8b958ad940a373cd4c1d1b32e503e Mon Sep 17 00:00:00 2001 From: Luc Michel Date: Mon, 7 Jan 2019 15:23:46 +0000 Subject: [PATCH 14/37] gdbstub: add support for vAttach packets MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add support for the vAttach packets. In multiprocess mode, GDB sends them to attach to additional processes. Signed-off-by: Luc Michel Reviewed-by: Edgar E. Iglesias Acked-by: Alistair Francis Reviewed-by: Philippe Mathieu-Daudé Message-id: 20181207090135.7651-13-luc.michel@greensocs.com Signed-off-by: Peter Maydell --- gdbstub.c | 35 +++++++++++++++++++++++++++++++++++ 1 file changed, 35 insertions(+) diff --git a/gdbstub.c b/gdbstub.c index 9b6b4d20a0..d80af955ce 100644 --- a/gdbstub.c +++ b/gdbstub.c @@ -1348,6 +1348,41 @@ static int gdb_handle_packet(GDBState *s, const char *line_buf) goto unknown_command; } break; + } else if (strncmp(p, "Attach;", 7) == 0) { + unsigned long pid; + + p += 7; + + if (qemu_strtoul(p, &p, 16, &pid)) { + put_packet(s, "E22"); + break; + } + + process = gdb_get_process(s, pid); + + if (process == NULL) { + put_packet(s, "E22"); + break; + } + + cpu = get_first_cpu_in_process(s, process); + + if (cpu == NULL) { + /* Refuse to attach an empty process */ + put_packet(s, "E22"); + break; + } + + process->attached = true; + + s->g_cpu = cpu; + s->c_cpu = cpu; + + snprintf(buf, sizeof(buf), "T%02xthread:%s;", GDB_SIGNAL_TRAP, + gdb_fmt_thread_id(s, cpu, thread_id, sizeof(thread_id))); + + put_packet(s, buf); + break; } else { goto unknown_command; } From 970ed9064aa9f997fd9e65d707dd5cafc7360703 Mon Sep 17 00:00:00 2001 From: Luc Michel Date: Mon, 7 Jan 2019 15:23:46 +0000 Subject: [PATCH 15/37] gdbstub: processes initialization on new peer connection MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When a new connection is established, we set the first process to be attached, and the others detached. The first CPU of the first process is selected as the current CPU. Signed-off-by: Luc Michel Reviewed-by: Alistair Francis Reviewed-by: Edgar E. Iglesias Reviewed-by: Philippe Mathieu-Daudé Message-id: 20181207090135.7651-14-luc.michel@greensocs.com Signed-off-by: Peter Maydell --- gdbstub.c | 20 +++++++++++++++----- 1 file changed, 15 insertions(+), 5 deletions(-) diff --git a/gdbstub.c b/gdbstub.c index d80af955ce..432e7d2f7d 100644 --- a/gdbstub.c +++ b/gdbstub.c @@ -2267,9 +2267,10 @@ static bool gdb_accept(void) } s = g_malloc0(sizeof(GDBState)); - s->c_cpu = first_cpu; - s->g_cpu = first_cpu; create_default_process(s); + s->processes[0].attached = true; + s->c_cpu = gdb_first_attached_cpu(s); + s->g_cpu = s->c_cpu; s->fd = fd; gdb_has_xml = false; @@ -2355,8 +2356,19 @@ static void gdb_chr_receive(void *opaque, const uint8_t *buf, int size) static void gdb_chr_event(void *opaque, int event) { + int i; + GDBState *s = (GDBState *) opaque; + switch (event) { case CHR_EVENT_OPENED: + /* Start with first process attached, others detached */ + for (i = 0; i < s->process_num; i++) { + s->processes[i].attached = !i; + } + + s->c_cpu = gdb_first_attached_cpu(s); + s->g_cpu = s->c_cpu; + vm_stop(RUN_STATE_PAUSED); gdb_has_xml = false; break; @@ -2546,15 +2558,13 @@ int gdbserver_start(const char *device) memset(s, 0, sizeof(GDBState)); s->mon_chr = mon_chr; } - s->c_cpu = first_cpu; - s->g_cpu = first_cpu; create_processes(s); if (chr) { qemu_chr_fe_init(&s->chr, chr, &error_abort); qemu_chr_fe_set_handlers(&s->chr, gdb_chr_can_receive, gdb_chr_receive, - gdb_chr_event, NULL, NULL, NULL, true); + gdb_chr_event, NULL, s, NULL, true); } s->state = chr ? RS_IDLE : RS_INACTIVE; s->mon_chr = mon_chr; From 160d858dd6c62c4b523350d89cfa75f212f49b92 Mon Sep 17 00:00:00 2001 From: Luc Michel Date: Mon, 7 Jan 2019 15:23:46 +0000 Subject: [PATCH 16/37] gdbstub: gdb_set_stop_cpu: ignore request when process is not attached When gdb_set_stop_cpu() is called with a CPU associated to a process currently not attached by the GDB client, return without modifying the stop CPU. Otherwise, GDB gets confused if it receives packets with a thread-id it does not know about. Signed-off-by: Luc Michel Acked-by: Alistair Francis Reviewed-by: Edgar E. Iglesias Message-id: 20181207090135.7651-15-luc.michel@greensocs.com [PMM: fix checkpatch comment style nit] Signed-off-by: Peter Maydell --- gdbstub.c | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/gdbstub.c b/gdbstub.c index 432e7d2f7d..1517563abe 100644 --- a/gdbstub.c +++ b/gdbstub.c @@ -1798,6 +1798,16 @@ static int gdb_handle_packet(GDBState *s, const char *line_buf) void gdb_set_stop_cpu(CPUState *cpu) { + GDBProcess *p = gdb_get_cpu_process(gdbserver_state, cpu); + + if (!p->attached) { + /* + * Having a stop CPU corresponding to a process that is not attached + * confuses GDB. So we ignore the request. + */ + return; + } + gdbserver_state->c_cpu = cpu; gdbserver_state->g_cpu = cpu; } From 364fce6f82b20537a803c6bfb3cf9bab5590f850 Mon Sep 17 00:00:00 2001 From: Luc Michel Date: Mon, 7 Jan 2019 15:23:46 +0000 Subject: [PATCH 17/37] gdbstub: add multiprocess extension support MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add multiprocess extension support by enabling multiprocess mode when the peer requests it, and by replying that we actually support it in the qSupported reply packet. Signed-off-by: Luc Michel Reviewed-by: Philippe Mathieu-Daudé Acked-by: Alistair Francis Reviewed-by: Edgar E. Iglesias Message-id: 20181207090135.7651-16-luc.michel@greensocs.com Signed-off-by: Peter Maydell --- gdbstub.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/gdbstub.c b/gdbstub.c index 1517563abe..bfc7afb509 100644 --- a/gdbstub.c +++ b/gdbstub.c @@ -1732,6 +1732,12 @@ static int gdb_handle_packet(GDBState *s, const char *line_buf) if (cc->gdb_core_xml_file != NULL) { pstrcat(buf, sizeof(buf), ";qXfer:features:read+"); } + + if (strstr(p, "multiprocess+")) { + s->multiprocess = true; + } + pstrcat(buf, sizeof(buf), ";multiprocess+"); + put_packet(s, buf); break; } From 816fd397a116b3a3850bcc4e3f66e15981a4bae8 Mon Sep 17 00:00:00 2001 From: Luc Michel Date: Mon, 7 Jan 2019 15:23:46 +0000 Subject: [PATCH 18/37] arm/xlnx-zynqmp: put APUs and RPUs in separate CPU clusters MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Create two separate CPU clusters for APUs and RPUs. Signed-off-by: Luc Michel Reviewed-by: Edgar E. Iglesias Reviewed-by: Philippe Mathieu-Daudé Reviewed-by: Alistair Francis Message-id: 20181207090135.7651-17-luc.michel@greensocs.com Signed-off-by: Peter Maydell --- hw/arm/xlnx-zynqmp.c | 23 +++++++++++++++++++---- include/hw/arm/xlnx-zynqmp.h | 3 +++ 2 files changed, 22 insertions(+), 4 deletions(-) diff --git a/hw/arm/xlnx-zynqmp.c b/hw/arm/xlnx-zynqmp.c index c195040350..c67ac2e64a 100644 --- a/hw/arm/xlnx-zynqmp.c +++ b/hw/arm/xlnx-zynqmp.c @@ -178,12 +178,19 @@ static void xlnx_zynqmp_create_rpu(XlnxZynqMPState *s, const char *boot_cpu, int i; int num_rpus = MIN(smp_cpus - XLNX_ZYNQMP_NUM_APU_CPUS, XLNX_ZYNQMP_NUM_RPU_CPUS); + object_initialize_child(OBJECT(s), "rpu-cluster", &s->rpu_cluster, + sizeof(s->rpu_cluster), TYPE_CPU_CLUSTER, + &error_abort, NULL); + qdev_prop_set_uint32(DEVICE(&s->rpu_cluster), "cluster-id", 1); + + qdev_init_nofail(DEVICE(&s->rpu_cluster)); + for (i = 0; i < num_rpus; i++) { char *name; object_initialize(&s->rpu_cpu[i], sizeof(s->rpu_cpu[i]), "cortex-r5f-" TYPE_ARM_CPU); - object_property_add_child(OBJECT(s), "rpu-cpu[*]", + object_property_add_child(OBJECT(&s->rpu_cluster), "rpu-cpu[*]", OBJECT(&s->rpu_cpu[i]), &error_abort); name = object_get_canonical_path_component(OBJECT(&s->rpu_cpu[i])); @@ -213,10 +220,16 @@ static void xlnx_zynqmp_init(Object *obj) int i; int num_apus = MIN(smp_cpus, XLNX_ZYNQMP_NUM_APU_CPUS); + object_initialize_child(obj, "apu-cluster", &s->apu_cluster, + sizeof(s->apu_cluster), TYPE_CPU_CLUSTER, + &error_abort, NULL); + qdev_prop_set_uint32(DEVICE(&s->apu_cluster), "cluster-id", 0); + for (i = 0; i < num_apus; i++) { - object_initialize_child(obj, "apu-cpu[*]", &s->apu_cpu[i], - sizeof(s->apu_cpu[i]), - "cortex-a53-" TYPE_ARM_CPU, &error_abort, NULL); + object_initialize_child(OBJECT(&s->apu_cluster), "apu-cpu[*]", + &s->apu_cpu[i], sizeof(s->apu_cpu[i]), + "cortex-a53-" TYPE_ARM_CPU, &error_abort, + NULL); } sysbus_init_child_obj(obj, "gic", &s->gic, sizeof(s->gic), @@ -333,6 +346,8 @@ static void xlnx_zynqmp_realize(DeviceState *dev, Error **errp) qdev_prop_set_bit(DEVICE(&s->gic), "has-virtualization-extensions", s->virt); + qdev_init_nofail(DEVICE(&s->apu_cluster)); + /* Realize APUs before realizing the GIC. KVM requires this. */ for (i = 0; i < num_apus; i++) { char *name; diff --git a/include/hw/arm/xlnx-zynqmp.h b/include/hw/arm/xlnx-zynqmp.h index 98f925ab84..591515c760 100644 --- a/include/hw/arm/xlnx-zynqmp.h +++ b/include/hw/arm/xlnx-zynqmp.h @@ -31,6 +31,7 @@ #include "hw/display/xlnx_dp.h" #include "hw/intc/xlnx-zynqmp-ipi.h" #include "hw/timer/xlnx-zynqmp-rtc.h" +#include "hw/cpu/cluster.h" #define TYPE_XLNX_ZYNQMP "xlnx,zynqmp" #define XLNX_ZYNQMP(obj) OBJECT_CHECK(XlnxZynqMPState, (obj), \ @@ -77,6 +78,8 @@ typedef struct XlnxZynqMPState { DeviceState parent_obj; /*< public >*/ + CPUClusterState apu_cluster; + CPUClusterState rpu_cluster; ARMCPU apu_cpu[XLNX_ZYNQMP_NUM_APU_CPUS]; ARMCPU rpu_cpu[XLNX_ZYNQMP_NUM_RPU_CPUS]; GICState gic; From c38c37ac979c54b09293eb11061aa0e534e0f3bf Mon Sep 17 00:00:00 2001 From: Stefan Hajnoczi Date: Mon, 7 Jan 2019 15:23:46 +0000 Subject: [PATCH 19/37] Revert "armv7m: Guard against no -kernel argument" This reverts commit 01fd41ab3fb69971c24a69ed49cde96086d81278. The generic loader device (-device loader,file=kernel.bin) can be used to load a kernel instead of the -kernel option. Some boards have flash memory (pflash) that is set via the -pflash or -drive options. Allow starting QEMU without the -kernel option to accommodate these scenarios. Suggested-by: Peter Maydell Signed-off-by: Stefan Hajnoczi Message-id: 20190103144124.18917-1-stefanha@redhat.com Reviewed-by: Peter Maydell Signed-off-by: Peter Maydell --- hw/arm/armv7m.c | 5 ----- 1 file changed, 5 deletions(-) diff --git a/hw/arm/armv7m.c b/hw/arm/armv7m.c index 4bf9131b81..f444652830 100644 --- a/hw/arm/armv7m.c +++ b/hw/arm/armv7m.c @@ -285,11 +285,6 @@ void armv7m_load_kernel(ARMCPU *cpu, const char *kernel_filename, int mem_size) big_endian = 0; #endif - if (!kernel_filename && !qtest_enabled()) { - error_report("Guest image must be specified (using -kernel)"); - exit(1); - } - if (arm_feature(&cpu->env, ARM_FEATURE_EL3)) { asidx = ARMASIdx_S; } else { From aee63b07fd1c2316d96dff0a6217288a630ce147 Mon Sep 17 00:00:00 2001 From: "Edgar E. Iglesias" Date: Mon, 7 Jan 2019 15:23:46 +0000 Subject: [PATCH 20/37] hw/arm: versal: Plug memory leaks MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Plug a couple of "board creation time" memory leaks. Fixes: 6f16da53ffe4567 ("hw/arm: versal: Add a virtual Xilinx Versal board") Reported-by: Peter Maydell Signed-off-by: Edgar E. Iglesias Reviewed-by: Philippe Mathieu-Daudé Message-id: 20190104104749.5314-2-edgar.iglesias@gmail.com Reviewed-by: Peter Maydell Signed-off-by: Peter Maydell --- hw/arm/xlnx-versal-virt.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/hw/arm/xlnx-versal-virt.c b/hw/arm/xlnx-versal-virt.c index c6feeac532..f95fde2309 100644 --- a/hw/arm/xlnx-versal-virt.c +++ b/hw/arm/xlnx-versal-virt.c @@ -130,6 +130,7 @@ static void fdt_add_gic_nodes(VersalVirt *s) 2, MM_GIC_APU_REDIST_0_SIZE); qemu_fdt_setprop_cell(s->fdt, nodename, "#interrupt-cells", 3); qemu_fdt_setprop_string(s->fdt, nodename, "compatible", "arm,gic-v3"); + g_free(nodename); } static void fdt_add_timer_nodes(VersalVirt *s) @@ -364,6 +365,7 @@ static void create_virtio_regions(VersalVirt *s) sysbus_connect_irq(SYS_BUS_DEVICE(dev), 0, pic_irq); mr = sysbus_mmio_get_region(SYS_BUS_DEVICE(dev), 0); memory_region_add_subregion(&s->soc.mr_ps, base, mr); + g_free(name); } for (i = 0; i < NUM_VIRTIO_TRANSPORT; i++) { From b7f301141d68be12f074db4ebfc51508f952ea61 Mon Sep 17 00:00:00 2001 From: Thomas Huth Date: Mon, 7 Jan 2019 15:23:47 +0000 Subject: [PATCH 21/37] MAINTAINERS: Add ARM-related files for hw/[misc|input|timer]/ MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Some of the files in hw/input/, hw/misc/ and hw/timer/ are only used by one of the ARM machines, so we can assign these files to the corresponding boards. Signed-off-by: Thomas Huth Reviewed-by: Philippe Mathieu-Daudé Message-id: 1546433583-18397-1-git-send-email-thuth@redhat.com Signed-off-by: Peter Maydell --- MAINTAINERS | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/MAINTAINERS b/MAINTAINERS index 471161742d..0bfd95a4ef 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -515,6 +515,7 @@ F: hw/intc/arm* F: hw/intc/gic_internal.h F: hw/misc/a9scu.c F: hw/misc/arm11scu.c +F: hw/misc/arm_l2x0.c F: hw/timer/a9gtimer* F: hw/timer/arm* F: include/hw/arm/arm*.h @@ -587,6 +588,7 @@ L: qemu-arm@nongnu.org S: Maintained F: hw/arm/integratorcp.c F: hw/misc/arm_integrator_debug.c +F: include/hw/misc/arm_integrator_debug.h MCIMX6UL EVK / i.MX6ul M: Peter Maydell @@ -606,7 +608,9 @@ L: qemu-arm@nongnu.org S: Odd Fixes F: hw/arm/mcimx7d-sabre.c F: hw/arm/fsl-imx7.c +F: hw/misc/imx7_*.c F: include/hw/arm/fsl-imx7.h +F: include/hw/misc/imx7_*.h F: hw/pci-host/designware.c F: include/hw/pci-host/designware.h @@ -640,6 +644,10 @@ M: Peter Maydell L: qemu-arm@nongnu.org S: Odd Fixes F: hw/arm/nseries.c +F: hw/input/lm832x.c +F: hw/input/tsc2005.c +F: hw/misc/cbus.c +F: hw/timer/twl92230.c Palm M: Andrzej Zaborowski @@ -647,6 +655,7 @@ M: Peter Maydell L: qemu-arm@nongnu.org S: Odd Fixes F: hw/arm/palm.c +F: hw/input/tsc210x.c Raspberry Pi M: Peter Maydell @@ -683,6 +692,7 @@ F: hw/display/tc6393xb.c F: hw/gpio/max7310.c F: hw/gpio/zaurus.c F: hw/misc/mst_fpga.c +F: hw/misc/max111x.c F: include/hw/arm/pxa.h F: include/hw/arm/sharpsl.h @@ -693,10 +703,10 @@ L: qemu-arm@nongnu.org S: Odd Fixes F: hw/arm/sabrelite.c F: hw/arm/fsl-imx6.c -F: hw/misc/imx6_src.c +F: hw/misc/imx6_*.c F: hw/ssi/imx_spi.c F: include/hw/arm/fsl-imx6.h -F: include/hw/misc/imx6_src.h +F: include/hw/misc/imx6_*.h F: include/hw/ssi/imx_spi.h Sharp SL-5500 (Collie) PDA @@ -807,7 +817,9 @@ R: Joel Stanley L: qemu-arm@nongnu.org S: Maintained F: hw/*/*aspeed* +F: hw/misc/pca9552.c F: include/hw/*/*aspeed* +F: include/hw/misc/pca9552*.h F: hw/net/ftgmac100.c F: include/hw/net/ftgmac100.h From 0ec7e6779fc830e5b4e6a448d75317fafcf69477 Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Mon, 7 Jan 2019 15:23:47 +0000 Subject: [PATCH 22/37] cpus.c: Fix race condition in cpu_stop_current() We use cpu_stop_current() to ensure the current CPU has stopped from places like qemu_system_reset_request(). Unfortunately its current implementation has a race. It calls qemu_cpu_stop(), which sets cpu->stopped to true even though the CPU hasn't actually stopped yet. The main thread will look at the flags set by qemu_system_reset_request() and call pause_all_vcpus(). pause_all_vcpus() waits for every cpu to have cpu->stopped true, so it can continue (and we will start the system reset operation) before the vcpu thread has got back to its top level loop. Instead, just set cpu->stop and call cpu_exit(). This will cause the vcpu to exit back to the top level loop, and there (as part of the wait_io_event code) it will call qemu_cpu_stop(). This fixes bugs where the reset request appeared to be ignored or the CPU misbehaved because the reset operation started to change vcpu state while the vcpu thread was still using it. Signed-off-by: Peter Maydell Reviewed-by: Emilio G. Cota Tested-by: Jaap Crezee Message-id: 20181207155911.12710-1-peter.maydell@linaro.org --- cpus.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/cpus.c b/cpus.c index 0ddeeefc14..b09b702712 100644 --- a/cpus.c +++ b/cpus.c @@ -2100,7 +2100,8 @@ void qemu_init_vcpu(CPUState *cpu) void cpu_stop_current(void) { if (current_cpu) { - qemu_cpu_stop(current_cpu, true); + current_cpu->stop = true; + cpu_exit(current_cpu); } } From ead07aa4ef753f0facd19a7218402f7d19eff60a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Mon, 7 Jan 2019 15:23:47 +0000 Subject: [PATCH 23/37] hw/arm/allwinner-a10: Add the 'A' SRAM and the SRAM controller MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit From the "A10 User Manual V1.20" p.29: "3.2. Memory Mapping" and: 7. System Control 7.1. Overview A10 embeds a high-speed SRAM which has been split into five segments. See detailed memory mapping in following table: Area Address Size (Bytes) A1 0x00000000-0x00003FFF 16K A2 0x00004000-0x00007FFF 16K A3 0x00008000-0x0000B3FF 13K A4 0x0000B400-0x0000BFFF 3K Since for emulation purpose we don't need the segmentations, we simply define the 'A' area as a single 48KB SRAM. We don't implement the following others areas: - 'B': 'Secure RAM' (64K), - 'C': Debug/ISP SRAM - 'D': USB SRAM (qemu) info mtree address-space: memory 0000000000000000-ffffffffffffffff (prio 0, i/o): system 0000000000000000-000000000000bfff (prio 0, ram): sram A 0000000001c00000-0000000001c00fff (prio -1000, i/o): a10-sram-ctrl 0000000001c0b000-0000000001c0bfff (prio 0, i/o): aw_emac 0000000001c18000-0000000001c18fff (prio 0, i/o): ahci 0000000001c18080-0000000001c180ff (prio 0, i/o): allwinner-ahci 0000000001c20400-0000000001c207ff (prio 0, i/o): allwinner-a10-pic 0000000001c20c00-0000000001c20fff (prio 0, i/o): allwinner-A10-timer 0000000001c28000-0000000001c2801f (prio 0, i/o): serial 0000000040000000-0000000047ffffff (prio 0, ram): cubieboard.ram Reported-by: Charlie Smurthwaite Tested-by: Charlie Smurthwaite Signed-off-by: Philippe Mathieu-Daudé Message-id: 20190104142921.878-1-f4bug@amsat.org Reviewed-by: Peter Maydell Signed-off-by: Peter Maydell --- hw/arm/allwinner-a10.c | 6 ++++++ include/hw/arm/allwinner-a10.h | 1 + 2 files changed, 7 insertions(+) diff --git a/hw/arm/allwinner-a10.c b/hw/arm/allwinner-a10.c index 9fe875cdb5..df0d079ad0 100644 --- a/hw/arm/allwinner-a10.c +++ b/hw/arm/allwinner-a10.c @@ -22,6 +22,7 @@ #include "hw/sysbus.h" #include "hw/devices.h" #include "hw/arm/allwinner-a10.h" +#include "hw/misc/unimp.h" static void aw_a10_init(Object *obj) { @@ -85,6 +86,11 @@ static void aw_a10_realize(DeviceState *dev, Error **errp) sysbus_connect_irq(sysbusdev, 4, s->irq[67]); sysbus_connect_irq(sysbusdev, 5, s->irq[68]); + memory_region_init_ram(&s->sram_a, OBJECT(dev), "sram A", 48 * KiB, + &error_fatal); + memory_region_add_subregion(get_system_memory(), 0x00000000, &s->sram_a); + create_unimplemented_device("a10-sram-ctrl", 0x01c00000, 4 * KiB); + /* FIXME use qdev NIC properties instead of nd_table[] */ if (nd_table[0].used) { qemu_check_nic_model(&nd_table[0], TYPE_AW_EMAC); diff --git a/include/hw/arm/allwinner-a10.h b/include/hw/arm/allwinner-a10.h index efb8fc8123..389e128d0f 100644 --- a/include/hw/arm/allwinner-a10.h +++ b/include/hw/arm/allwinner-a10.h @@ -35,6 +35,7 @@ typedef struct AwA10State { AwA10PICState intc; AwEmacState emac; AllwinnerAHCIState sata; + MemoryRegion sram_a; } AwA10State; #define ALLWINNER_H_ From 9813dc6ac3954d58ba16b3920556f106f97e1c67 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Steffen=20G=C3=B6rtz?= Date: Mon, 7 Jan 2019 15:23:47 +0000 Subject: [PATCH 24/37] qtest: Add set_irq_in command to set IRQ/GPIO level MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Adds a new qtest command "set_irq_in" which allows to set qemu gpio lines to a given level. Based on https://lists.gnu.org/archive/html/qemu-devel/2012-12/msg02363.html which never got merged. Signed-off-by: Steffen Görtz Reviewed-by: Stefan Hajnoczi Reviewed-by: Thomas Huth Reviewed-by: Laurent Vivier Signed-off-by: Stefan Hajnoczi Message-id: 20190103091119.9367-2-stefanha@redhat.com Originally-by: Matthew Ogilvie Reviewed-by: Stefan Hajnoczi Reviewed-by: Thomas Huth Reviewed-by: Laurent Vivier Signed-off-by: Stefan Hajnoczi Signed-off-by: Peter Maydell --- qtest.c | 43 +++++++++++++++++++++++++++++++++++++++++++ tests/libqtest.c | 10 ++++++++++ tests/libqtest.h | 13 +++++++++++++ 3 files changed, 66 insertions(+) diff --git a/qtest.c b/qtest.c index 69b9e9962b..451696b5da 100644 --- a/qtest.c +++ b/qtest.c @@ -164,6 +164,17 @@ static bool qtest_opened; * where NUM is an IRQ number. For the PC, interrupts can be intercepted * simply with "irq_intercept_in ioapic" (note that IRQ0 comes out with * NUM=0 even though it is remapped to GSI 2). + * + * Setting interrupt level: + * + * > set_irq_in QOM-PATH NAME NUM LEVEL + * < OK + * + * where NAME is the name of the irq/gpio list, NUM is an IRQ number and + * LEVEL is an signed integer IRQ level. + * + * Forcibly set the given interrupt pin to the given level. + * */ static int hex2nib(char ch) @@ -326,7 +337,39 @@ static void qtest_process_command(CharBackend *chr, gchar **words) irq_intercept_dev = dev; qtest_send_prefix(chr); qtest_send(chr, "OK\n"); + } else if (strcmp(words[0], "set_irq_in") == 0) { + DeviceState *dev; + qemu_irq irq; + char *name; + int ret; + int num; + int level; + g_assert(words[1] && words[2] && words[3] && words[4]); + + dev = DEVICE(object_resolve_path(words[1], NULL)); + if (!dev) { + qtest_send_prefix(chr); + qtest_send(chr, "FAIL Unknown device\n"); + return; + } + + if (strcmp(words[2], "unnamed-gpio-in") == 0) { + name = NULL; + } else { + name = words[2]; + } + + ret = qemu_strtoi(words[3], NULL, 0, &num); + g_assert(!ret); + ret = qemu_strtoi(words[4], NULL, 0, &level); + g_assert(!ret); + + irq = qdev_get_gpio_in_named(dev, name, num); + + qemu_set_irq(irq, level); + qtest_send_prefix(chr); + qtest_send(chr, "OK\n"); } else if (strcmp(words[0], "outb") == 0 || strcmp(words[0], "outw") == 0 || strcmp(words[0], "outl") == 0) { diff --git a/tests/libqtest.c b/tests/libqtest.c index 1d75d3c936..55750dd68d 100644 --- a/tests/libqtest.c +++ b/tests/libqtest.c @@ -753,6 +753,16 @@ void qtest_irq_intercept_in(QTestState *s, const char *qom_path) qtest_rsp(s, 0); } +void qtest_set_irq_in(QTestState *s, const char *qom_path, const char *name, + int num, int level) +{ + if (!name) { + name = "unnamed-gpio-in"; + } + qtest_sendf(s, "set_irq_in %s %s %d %d\n", qom_path, name, num, level); + qtest_rsp(s, 0); +} + static void qtest_out(QTestState *s, const char *cmd, uint16_t addr, uint32_t value) { qtest_sendf(s, "%s 0x%x 0x%x\n", cmd, addr, value); diff --git a/tests/libqtest.h b/tests/libqtest.h index 9758c51be6..7ea94139b0 100644 --- a/tests/libqtest.h +++ b/tests/libqtest.h @@ -230,6 +230,19 @@ void qtest_irq_intercept_in(QTestState *s, const char *string); */ void qtest_irq_intercept_out(QTestState *s, const char *string); +/** + * qtest_set_irq_in: + * @s: QTestState instance to operate on. + * @string: QOM path of a device + * @name: IRQ name + * @irq: IRQ number + * @level: IRQ level + * + * Force given device/irq GPIO-in pin to the given level. + */ +void qtest_set_irq_in(QTestState *s, const char *string, const char *name, + int irq, int level); + /** * qtest_outb: * @s: #QTestState instance to operate on. From 659b85e4133127b342ed191e9d97dc6ad7626113 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Steffen=20G=C3=B6rtz?= Date: Mon, 7 Jan 2019 15:23:47 +0000 Subject: [PATCH 25/37] arm: Add header to host common definition for nRF51 SOC peripherals MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Adds a header that provides definitions that are used across nRF51 peripherals Signed-off-by: Steffen Görtz Reviewed-by: Stefan Hajnoczi Reviewed-by: Peter Maydell Signed-off-by: Stefan Hajnoczi Message-id: 20190103091119.9367-3-stefanha@redhat.com Signed-off-by: Peter Maydell --- hw/arm/nrf51_soc.c | 33 ++++++++++---------------- include/hw/arm/nrf51.h | 45 ++++++++++++++++++++++++++++++++++++ include/hw/char/nrf51_uart.h | 1 - 3 files changed, 57 insertions(+), 22 deletions(-) create mode 100644 include/hw/arm/nrf51.h diff --git a/hw/arm/nrf51_soc.c b/hw/arm/nrf51_soc.c index b89c1bdea0..55f8eaafcb 100644 --- a/hw/arm/nrf51_soc.c +++ b/hw/arm/nrf51_soc.c @@ -21,27 +21,16 @@ #include "qemu/log.h" #include "cpu.h" +#include "hw/arm/nrf51.h" #include "hw/arm/nrf51_soc.h" -#define IOMEM_BASE 0x40000000 -#define IOMEM_SIZE 0x20000000 - -#define FICR_BASE 0x10000000 -#define FICR_SIZE 0x000000fc - -#define FLASH_BASE 0x00000000 -#define SRAM_BASE 0x20000000 - -#define PRIVATE_BASE 0xF0000000 -#define PRIVATE_SIZE 0x10000000 - /* * The size and base is for the NRF51822 part. If other parts * are supported in the future, add a sub-class of NRF51SoC for * the specific variants */ -#define NRF51822_FLASH_SIZE (256 * 1024) -#define NRF51822_SRAM_SIZE (16 * 1024) +#define NRF51822_FLASH_SIZE (256 * NRF51_PAGE_SIZE) +#define NRF51822_SRAM_SIZE (16 * NRF51_PAGE_SIZE) #define BASE_TO_IRQ(base) ((base >> 12) & 0x1F) @@ -76,14 +65,14 @@ static void nrf51_soc_realize(DeviceState *dev_soc, Error **errp) error_propagate(errp, err); return; } - memory_region_add_subregion(&s->container, FLASH_BASE, &s->flash); + memory_region_add_subregion(&s->container, NRF51_FLASH_BASE, &s->flash); memory_region_init_ram(&s->sram, NULL, "nrf51.sram", s->sram_size, &err); if (err) { error_propagate(errp, err); return; } - memory_region_add_subregion(&s->container, SRAM_BASE, &s->sram); + memory_region_add_subregion(&s->container, NRF51_SRAM_BASE, &s->sram); /* UART */ object_property_set_bool(OBJECT(&s->uart), true, "realized", &err); @@ -92,15 +81,17 @@ static void nrf51_soc_realize(DeviceState *dev_soc, Error **errp) return; } mr = sysbus_mmio_get_region(SYS_BUS_DEVICE(&s->uart), 0); - memory_region_add_subregion_overlap(&s->container, UART_BASE, mr, 0); + memory_region_add_subregion_overlap(&s->container, NRF51_UART_BASE, mr, 0); sysbus_connect_irq(SYS_BUS_DEVICE(&s->uart), 0, qdev_get_gpio_in(DEVICE(&s->cpu), - BASE_TO_IRQ(UART_BASE))); + BASE_TO_IRQ(NRF51_UART_BASE))); - create_unimplemented_device("nrf51_soc.io", IOMEM_BASE, IOMEM_SIZE); - create_unimplemented_device("nrf51_soc.ficr", FICR_BASE, FICR_SIZE); + create_unimplemented_device("nrf51_soc.io", NRF51_IOMEM_BASE, + NRF51_IOMEM_SIZE); + create_unimplemented_device("nrf51_soc.ficr", NRF51_FICR_BASE, + NRF51_FICR_SIZE); create_unimplemented_device("nrf51_soc.private", - PRIVATE_BASE, PRIVATE_SIZE); + NRF51_PRIVATE_BASE, NRF51_PRIVATE_SIZE); } static void nrf51_soc_init(Object *obj) diff --git a/include/hw/arm/nrf51.h b/include/hw/arm/nrf51.h new file mode 100644 index 0000000000..175bb6c301 --- /dev/null +++ b/include/hw/arm/nrf51.h @@ -0,0 +1,45 @@ +/* + * Nordic Semiconductor nRF51 Series SOC Common Defines + * + * This file hosts generic defines used in various nRF51 peripheral devices. + * + * Reference Manual: http://infocenter.nordicsemi.com/pdf/nRF51_RM_v3.0.pdf + * Product Spec: http://infocenter.nordicsemi.com/pdf/nRF51822_PS_v3.1.pdf + * + * Copyright 2018 Steffen Görtz + * + * This code is licensed under the GPL version 2 or later. See + * the COPYING file in the top-level directory. + */ + +#ifndef NRF51_H +#define NRF51_H + +#define NRF51_FLASH_BASE 0x00000000 +#define NRF51_FICR_BASE 0x10000000 +#define NRF51_FICR_SIZE 0x00000100 +#define NRF51_UICR_BASE 0x10001000 +#define NRF51_SRAM_BASE 0x20000000 + +#define NRF51_IOMEM_BASE 0x40000000 +#define NRF51_IOMEM_SIZE 0x20000000 + +#define NRF51_UART_BASE 0x40002000 +#define NRF51_TIMER_BASE 0x40008000 +#define NRF51_TIMER_SIZE 0x00001000 +#define NRF51_RNG_BASE 0x4000D000 +#define NRF51_NVMC_BASE 0x4001E000 +#define NRF51_GPIO_BASE 0x50000000 + +#define NRF51_PRIVATE_BASE 0xF0000000 +#define NRF51_PRIVATE_SIZE 0x10000000 + +#define NRF51_PAGE_SIZE 1024 + +/* Trigger */ +#define NRF51_TRIGGER_TASK 0x01 + +/* Events */ +#define NRF51_EVENT_CLEAR 0x00 + +#endif diff --git a/include/hw/char/nrf51_uart.h b/include/hw/char/nrf51_uart.h index e3ecb7c81c..eb1c15b490 100644 --- a/include/hw/char/nrf51_uart.h +++ b/include/hw/char/nrf51_uart.h @@ -16,7 +16,6 @@ #include "hw/registerfields.h" #define UART_FIFO_LENGTH 6 -#define UART_BASE 0x40002000 #define UART_SIZE 0x1000 #define TYPE_NRF51_UART "nrf51_soc.uart" From 90c58941f6ce05d03364de253902008299d3f9e8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Steffen=20G=C3=B6rtz?= Date: Mon, 7 Jan 2019 15:23:47 +0000 Subject: [PATCH 26/37] hw/misc/nrf51_rng: Add NRF51 random number generator peripheral MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add a model of the NRF51 random number generator peripheral. This is a simple random generator that continuously generates new random values after startup. Reference Manual: http://infocenter.nordicsemi.com/pdf/nRF51_RM_v3.0.pdf Signed-off-by: Steffen Görtz Reviewed-by: Stefan Hajnoczi Reviewed-by: Peter Maydell Signed-off-by: Stefan Hajnoczi Message-id: 20190103091119.9367-4-stefanha@redhat.com Signed-off-by: Peter Maydell --- hw/misc/Makefile.objs | 1 + hw/misc/nrf51_rng.c | 262 ++++++++++++++++++++++++++++++++++++ include/hw/misc/nrf51_rng.h | 83 ++++++++++++ 3 files changed, 346 insertions(+) create mode 100644 hw/misc/nrf51_rng.c create mode 100644 include/hw/misc/nrf51_rng.h diff --git a/hw/misc/Makefile.objs b/hw/misc/Makefile.objs index 680350b3c3..04f3bfa516 100644 --- a/hw/misc/Makefile.objs +++ b/hw/misc/Makefile.objs @@ -74,3 +74,4 @@ obj-$(CONFIG_PVPANIC) += pvpanic.o obj-$(CONFIG_AUX) += auxbus.o obj-$(CONFIG_ASPEED_SOC) += aspeed_scu.o aspeed_sdmc.o obj-$(CONFIG_MSF2) += msf2-sysreg.o +obj-$(CONFIG_NRF51_SOC) += nrf51_rng.o diff --git a/hw/misc/nrf51_rng.c b/hw/misc/nrf51_rng.c new file mode 100644 index 0000000000..d188f044f4 --- /dev/null +++ b/hw/misc/nrf51_rng.c @@ -0,0 +1,262 @@ +/* + * nRF51 Random Number Generator + * + * Reference Manual: http://infocenter.nordicsemi.com/pdf/nRF51_RM_v3.0.1.pdf + * + * Copyright 2018 Steffen Görtz + * + * This code is licensed under the GPL version 2 or later. See + * the COPYING file in the top-level directory. + */ + +#include "qemu/osdep.h" +#include "qemu/log.h" +#include "qapi/error.h" +#include "hw/arm/nrf51.h" +#include "hw/misc/nrf51_rng.h" +#include "crypto/random.h" + +static void update_irq(NRF51RNGState *s) +{ + bool irq = s->interrupt_enabled && s->event_valrdy; + qemu_set_irq(s->irq, irq); +} + +static uint64_t rng_read(void *opaque, hwaddr offset, unsigned int size) +{ + NRF51RNGState *s = NRF51_RNG(opaque); + uint64_t r = 0; + + switch (offset) { + case NRF51_RNG_EVENT_VALRDY: + r = s->event_valrdy; + break; + case NRF51_RNG_REG_SHORTS: + r = s->shortcut_stop_on_valrdy; + break; + case NRF51_RNG_REG_INTEN: + case NRF51_RNG_REG_INTENSET: + case NRF51_RNG_REG_INTENCLR: + r = s->interrupt_enabled; + break; + case NRF51_RNG_REG_CONFIG: + r = s->filter_enabled; + break; + case NRF51_RNG_REG_VALUE: + r = s->value; + break; + + default: + qemu_log_mask(LOG_GUEST_ERROR, + "%s: bad read offset 0x%" HWADDR_PRIx "\n", + __func__, offset); + } + + return r; +} + +static int64_t calc_next_timeout(NRF51RNGState *s) +{ + int64_t timeout = qemu_clock_get_us(QEMU_CLOCK_VIRTUAL); + if (s->filter_enabled) { + timeout += s->period_filtered_us; + } else { + timeout += s->period_unfiltered_us; + } + + return timeout; +} + + +static void rng_update_timer(NRF51RNGState *s) +{ + if (s->active) { + timer_mod(&s->timer, calc_next_timeout(s)); + } else { + timer_del(&s->timer); + } +} + + +static void rng_write(void *opaque, hwaddr offset, + uint64_t value, unsigned int size) +{ + NRF51RNGState *s = NRF51_RNG(opaque); + + switch (offset) { + case NRF51_RNG_TASK_START: + if (value == NRF51_TRIGGER_TASK) { + s->active = 1; + rng_update_timer(s); + } + break; + case NRF51_RNG_TASK_STOP: + if (value == NRF51_TRIGGER_TASK) { + s->active = 0; + rng_update_timer(s); + } + break; + case NRF51_RNG_EVENT_VALRDY: + if (value == NRF51_EVENT_CLEAR) { + s->event_valrdy = 0; + } + break; + case NRF51_RNG_REG_SHORTS: + s->shortcut_stop_on_valrdy = + (value & BIT_MASK(NRF51_RNG_REG_SHORTS_VALRDY_STOP)) ? 1 : 0; + break; + case NRF51_RNG_REG_INTEN: + s->interrupt_enabled = + (value & BIT_MASK(NRF51_RNG_REG_INTEN_VALRDY)) ? 1 : 0; + break; + case NRF51_RNG_REG_INTENSET: + if (value & BIT_MASK(NRF51_RNG_REG_INTEN_VALRDY)) { + s->interrupt_enabled = 1; + } + break; + case NRF51_RNG_REG_INTENCLR: + if (value & BIT_MASK(NRF51_RNG_REG_INTEN_VALRDY)) { + s->interrupt_enabled = 0; + } + break; + case NRF51_RNG_REG_CONFIG: + s->filter_enabled = + (value & BIT_MASK(NRF51_RNG_REG_CONFIG_DECEN)) ? 1 : 0; + break; + + default: + qemu_log_mask(LOG_GUEST_ERROR, + "%s: bad write offset 0x%" HWADDR_PRIx "\n", + __func__, offset); + } + + update_irq(s); +} + +static const MemoryRegionOps rng_ops = { + .read = rng_read, + .write = rng_write, + .endianness = DEVICE_LITTLE_ENDIAN, + .impl.min_access_size = 4, + .impl.max_access_size = 4 +}; + +static void nrf51_rng_timer_expire(void *opaque) +{ + NRF51RNGState *s = NRF51_RNG(opaque); + + qcrypto_random_bytes(&s->value, 1, &error_abort); + + s->event_valrdy = 1; + qemu_set_irq(s->eep_valrdy, 1); + + if (s->shortcut_stop_on_valrdy) { + s->active = 0; + } + + rng_update_timer(s); + update_irq(s); +} + +static void nrf51_rng_tep_start(void *opaque, int n, int level) +{ + NRF51RNGState *s = NRF51_RNG(opaque); + + if (level) { + s->active = 1; + rng_update_timer(s); + } +} + +static void nrf51_rng_tep_stop(void *opaque, int n, int level) +{ + NRF51RNGState *s = NRF51_RNG(opaque); + + if (level) { + s->active = 0; + rng_update_timer(s); + } +} + + +static void nrf51_rng_init(Object *obj) +{ + NRF51RNGState *s = NRF51_RNG(obj); + SysBusDevice *sbd = SYS_BUS_DEVICE(obj); + + memory_region_init_io(&s->mmio, obj, &rng_ops, s, + TYPE_NRF51_RNG, NRF51_RNG_SIZE); + sysbus_init_mmio(sbd, &s->mmio); + + timer_init_us(&s->timer, QEMU_CLOCK_VIRTUAL, nrf51_rng_timer_expire, s); + + sysbus_init_irq(sbd, &s->irq); + + /* Tasks */ + qdev_init_gpio_in_named(DEVICE(s), nrf51_rng_tep_start, "tep_start", 1); + qdev_init_gpio_in_named(DEVICE(s), nrf51_rng_tep_stop, "tep_stop", 1); + + /* Events */ + qdev_init_gpio_out_named(DEVICE(s), &s->eep_valrdy, "eep_valrdy", 1); +} + +static void nrf51_rng_reset(DeviceState *dev) +{ + NRF51RNGState *s = NRF51_RNG(dev); + + s->value = 0; + s->active = 0; + s->event_valrdy = 0; + s->shortcut_stop_on_valrdy = 0; + s->interrupt_enabled = 0; + s->filter_enabled = 0; + + rng_update_timer(s); +} + + +static Property nrf51_rng_properties[] = { + DEFINE_PROP_UINT16("period_unfiltered_us", NRF51RNGState, + period_unfiltered_us, 167), + DEFINE_PROP_UINT16("period_filtered_us", NRF51RNGState, + period_filtered_us, 660), + DEFINE_PROP_END_OF_LIST(), +}; + +static const VMStateDescription vmstate_rng = { + .name = "nrf51_soc.rng", + .version_id = 1, + .minimum_version_id = 1, + .fields = (VMStateField[]) { + VMSTATE_UINT32(active, NRF51RNGState), + VMSTATE_UINT32(event_valrdy, NRF51RNGState), + VMSTATE_UINT32(shortcut_stop_on_valrdy, NRF51RNGState), + VMSTATE_UINT32(interrupt_enabled, NRF51RNGState), + VMSTATE_UINT32(filter_enabled, NRF51RNGState), + VMSTATE_END_OF_LIST() + } +}; + +static void nrf51_rng_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + + dc->props = nrf51_rng_properties; + dc->vmsd = &vmstate_rng; + dc->reset = nrf51_rng_reset; +} + +static const TypeInfo nrf51_rng_info = { + .name = TYPE_NRF51_RNG, + .parent = TYPE_SYS_BUS_DEVICE, + .instance_size = sizeof(NRF51RNGState), + .instance_init = nrf51_rng_init, + .class_init = nrf51_rng_class_init +}; + +static void nrf51_rng_register_types(void) +{ + type_register_static(&nrf51_rng_info); +} + +type_init(nrf51_rng_register_types) diff --git a/include/hw/misc/nrf51_rng.h b/include/hw/misc/nrf51_rng.h new file mode 100644 index 0000000000..3d6bf79997 --- /dev/null +++ b/include/hw/misc/nrf51_rng.h @@ -0,0 +1,83 @@ +/* + * nRF51 Random Number Generator + * + * QEMU interface: + * + Property "period_unfiltered_us": Time between two biased values in + * microseconds. + * + Property "period_filtered_us": Time between two unbiased values in + * microseconds. + * + sysbus MMIO regions 0: Memory Region with tasks, events and registers + * to be mapped to the peripherals instance address by the SOC. + * + Named GPIO output "irq": Interrupt line of the peripheral. Must be + * connected to the associated peripheral interrupt line of the NVIC. + * + Named GPIO output "eep_valrdy": Event set when new random value is ready + * to be read. + * + Named GPIO input "tep_start": Task that triggers start of continuous + * generation of random values. + * + Named GPIO input "tep_stop": Task that ends continuous generation of + * random values. + * + * Accuracy of the peripheral model: + * + Stochastic properties of different configurations of the random source + * are not modeled. + * + Generation of unfiltered and filtered random values take at least the + * average generation time stated in the production specification; + * non-deterministic generation times are not modeled. + * + * Copyright 2018 Steffen Görtz + * + * This code is licensed under the GPL version 2 or later. See + * the COPYING file in the top-level directory. + * + */ +#ifndef NRF51_RNG_H +#define NRF51_RNG_H + +#include "hw/sysbus.h" +#include "qemu/timer.h" +#define TYPE_NRF51_RNG "nrf51_soc.rng" +#define NRF51_RNG(obj) OBJECT_CHECK(NRF51RNGState, (obj), TYPE_NRF51_RNG) + +#define NRF51_RNG_SIZE 0x1000 + +#define NRF51_RNG_TASK_START 0x000 +#define NRF51_RNG_TASK_STOP 0x004 +#define NRF51_RNG_EVENT_VALRDY 0x100 +#define NRF51_RNG_REG_SHORTS 0x200 +#define NRF51_RNG_REG_SHORTS_VALRDY_STOP 0 +#define NRF51_RNG_REG_INTEN 0x300 +#define NRF51_RNG_REG_INTEN_VALRDY 0 +#define NRF51_RNG_REG_INTENSET 0x304 +#define NRF51_RNG_REG_INTENCLR 0x308 +#define NRF51_RNG_REG_CONFIG 0x504 +#define NRF51_RNG_REG_CONFIG_DECEN 0 +#define NRF51_RNG_REG_VALUE 0x508 + +typedef struct { + SysBusDevice parent_obj; + + MemoryRegion mmio; + qemu_irq irq; + + /* Event End Points */ + qemu_irq eep_valrdy; + + QEMUTimer timer; + + /* Time between generation of successive unfiltered values in us */ + uint16_t period_unfiltered_us; + /* Time between generation of successive filtered values in us */ + uint16_t period_filtered_us; + + uint8_t value; + + uint32_t active; + uint32_t event_valrdy; + uint32_t shortcut_stop_on_valrdy; + uint32_t interrupt_enabled; + uint32_t filter_enabled; + +} NRF51RNGState; + + +#endif /* NRF51_RNG_H_ */ From f30890def51f777db1da350f9c737cc75615bdf0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Steffen=20G=C3=B6rtz?= Date: Mon, 7 Jan 2019 15:23:47 +0000 Subject: [PATCH 27/37] arm: Instantiate NRF51 random number generator MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Use RNG in SOC. Signed-off-by: Steffen Görtz Reviewed-by: Stefan Hajnoczi Reviewed-by: Peter Maydell Signed-off-by: Stefan Hajnoczi Message-id: 20190103091119.9367-5-stefanha@redhat.com Signed-off-by: Peter Maydell --- hw/arm/nrf51_soc.c | 16 ++++++++++++++++ include/hw/arm/nrf51_soc.h | 2 ++ 2 files changed, 18 insertions(+) diff --git a/hw/arm/nrf51_soc.c b/hw/arm/nrf51_soc.c index 55f8eaafcb..d2a19b8ead 100644 --- a/hw/arm/nrf51_soc.c +++ b/hw/arm/nrf51_soc.c @@ -86,6 +86,19 @@ static void nrf51_soc_realize(DeviceState *dev_soc, Error **errp) qdev_get_gpio_in(DEVICE(&s->cpu), BASE_TO_IRQ(NRF51_UART_BASE))); + /* RNG */ + object_property_set_bool(OBJECT(&s->rng), true, "realized", &err); + if (err) { + error_propagate(errp, err); + return; + } + + mr = sysbus_mmio_get_region(SYS_BUS_DEVICE(&s->rng), 0); + memory_region_add_subregion_overlap(&s->container, NRF51_RNG_BASE, mr, 0); + sysbus_connect_irq(SYS_BUS_DEVICE(&s->rng), 0, + qdev_get_gpio_in(DEVICE(&s->cpu), + BASE_TO_IRQ(NRF51_RNG_BASE))); + create_unimplemented_device("nrf51_soc.io", NRF51_IOMEM_BASE, NRF51_IOMEM_SIZE); create_unimplemented_device("nrf51_soc.ficr", NRF51_FICR_BASE, @@ -110,6 +123,9 @@ static void nrf51_soc_init(Object *obj) TYPE_NRF51_UART); object_property_add_alias(obj, "serial0", OBJECT(&s->uart), "chardev", &error_abort); + + sysbus_init_child_obj(obj, "rng", &s->rng, sizeof(s->rng), + TYPE_NRF51_RNG); } static Property nrf51_soc_properties[] = { diff --git a/include/hw/arm/nrf51_soc.h b/include/hw/arm/nrf51_soc.h index 73fc92e9a8..9e3ba916bd 100644 --- a/include/hw/arm/nrf51_soc.h +++ b/include/hw/arm/nrf51_soc.h @@ -13,6 +13,7 @@ #include "hw/sysbus.h" #include "hw/arm/armv7m.h" #include "hw/char/nrf51_uart.h" +#include "hw/misc/nrf51_rng.h" #define TYPE_NRF51_SOC "nrf51-soc" #define NRF51_SOC(obj) \ @@ -26,6 +27,7 @@ typedef struct NRF51State { ARMv7MState cpu; NRF51UARTState uart; + NRF51RNGState rng; MemoryRegion iomem; MemoryRegion sram; From 805f61bbb33ba24777a854df5d018858502d6d9c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Steffen=20G=C3=B6rtz?= Date: Mon, 7 Jan 2019 15:23:47 +0000 Subject: [PATCH 28/37] hw/gpio/nrf51_gpio: Add nRF51 GPIO peripheral MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This adds a model of the nRF51 GPIO peripheral. Reference Manual: http://infocenter.nordicsemi.com/pdf/nRF51_RM_v3.0.pdf The nRF51 series microcontrollers support up to 32 GPIO pins in various configurations. The pins can be used as input pins with pull-ups or pull-down. Furthermore, three different output driver modes per level are available (disconnected, standard, high-current). The GPIO-Peripheral has a mechanism for detecting level changes which is not featured in this model. Signed-off-by: Steffen Görtz Reviewed-by: Stefan Hajnoczi Reviewed-by: Peter Maydell Signed-off-by: Stefan Hajnoczi Message-id: 20190103091119.9367-6-stefanha@redhat.com Signed-off-by: Peter Maydell --- Makefile.objs | 1 + hw/gpio/Makefile.objs | 1 + hw/gpio/nrf51_gpio.c | 300 +++++++++++++++++++++++++++++++++++ hw/gpio/trace-events | 7 + include/hw/gpio/nrf51_gpio.h | 69 ++++++++ 5 files changed, 378 insertions(+) create mode 100644 hw/gpio/nrf51_gpio.c create mode 100644 hw/gpio/trace-events create mode 100644 include/hw/gpio/nrf51_gpio.h diff --git a/Makefile.objs b/Makefile.objs index bc5b8a8442..456115992a 100644 --- a/Makefile.objs +++ b/Makefile.objs @@ -184,6 +184,7 @@ trace-events-subdirs += hw/vfio trace-events-subdirs += hw/virtio trace-events-subdirs += hw/watchdog trace-events-subdirs += hw/xen +trace-events-subdirs += hw/gpio trace-events-subdirs += io trace-events-subdirs += linux-user trace-events-subdirs += migration diff --git a/hw/gpio/Makefile.objs b/hw/gpio/Makefile.objs index fa0a72e6d0..e5da0cb54f 100644 --- a/hw/gpio/Makefile.objs +++ b/hw/gpio/Makefile.objs @@ -8,3 +8,4 @@ common-obj-$(CONFIG_GPIO_KEY) += gpio_key.o obj-$(CONFIG_OMAP) += omap_gpio.o obj-$(CONFIG_IMX) += imx_gpio.o obj-$(CONFIG_RASPI) += bcm2835_gpio.o +obj-$(CONFIG_NRF51_SOC) += nrf51_gpio.o diff --git a/hw/gpio/nrf51_gpio.c b/hw/gpio/nrf51_gpio.c new file mode 100644 index 0000000000..86e047d649 --- /dev/null +++ b/hw/gpio/nrf51_gpio.c @@ -0,0 +1,300 @@ +/* + * nRF51 System-on-Chip general purpose input/output register definition + * + * Reference Manual: http://infocenter.nordicsemi.com/pdf/nRF51_RM_v3.0.pdf + * Product Spec: http://infocenter.nordicsemi.com/pdf/nRF51822_PS_v3.1.pdf + * + * Copyright 2018 Steffen Görtz + * + * This code is licensed under the GPL version 2 or later. See + * the COPYING file in the top-level directory. + */ + +#include "qemu/osdep.h" +#include "qemu/log.h" +#include "hw/gpio/nrf51_gpio.h" +#include "trace.h" + +/* + * Check if the output driver is connected to the direction switch + * given the current configuration and logic level. + * It is not differentiated between standard and "high"(-power) drive modes. + */ +static bool is_connected(uint32_t config, uint32_t level) +{ + bool state; + uint32_t drive_config = extract32(config, 8, 3); + + switch (drive_config) { + case 0 ... 3: + state = true; + break; + case 4 ... 5: + state = level != 0; + break; + case 6 ... 7: + state = level == 0; + break; + default: + g_assert_not_reached(); + break; + } + + return state; +} + +static void update_output_irq(NRF51GPIOState *s, size_t i, + bool connected, bool level) +{ + int64_t irq_level = connected ? level : -1; + bool old_connected = extract32(s->old_out_connected, i, 1); + bool old_level = extract32(s->old_out, i, 1); + + if ((old_connected != connected) || (old_level != level)) { + qemu_set_irq(s->output[i], irq_level); + trace_nrf51_gpio_update_output_irq(i, irq_level); + } + + s->old_out = deposit32(s->old_out, i, 1, level); + s->old_out_connected = deposit32(s->old_out_connected, i, 1, connected); +} + +static void update_state(NRF51GPIOState *s) +{ + uint32_t pull; + size_t i; + bool connected_out, dir, connected_in, out, input; + + for (i = 0; i < NRF51_GPIO_PINS; i++) { + pull = extract32(s->cnf[i], 2, 2); + dir = extract32(s->cnf[i], 0, 1); + connected_in = extract32(s->in_mask, i, 1); + out = extract32(s->out, i, 1); + input = !extract32(s->cnf[i], 1, 1); + connected_out = is_connected(s->cnf[i], out) && dir; + + update_output_irq(s, i, connected_out, out); + + /* Pin both driven externally and internally */ + if (connected_out && connected_in) { + qemu_log_mask(LOG_GUEST_ERROR, "GPIO pin %zu short circuited\n", i); + } + + /* + * Input buffer disconnected from internal/external drives, so + * pull-up/pull-down becomes relevant + */ + if (!input || (input && !connected_in && !connected_out)) { + if (pull == NRF51_GPIO_PULLDOWN) { + s->in = deposit32(s->in, i, 1, 0); + } else if (pull == NRF51_GPIO_PULLUP) { + s->in = deposit32(s->in, i, 1, 1); + } + } + + /* Self stimulation through internal output driver */ + if (connected_out && !connected_in && input) { + s->in = deposit32(s->in, i, 1, out); + } + } + +} + +/* + * Direction is exposed in both the DIR register and the DIR bit + * of each PINs CNF configuration register. Reflect bits for pins in DIR + * to individual pin configuration registers. + */ +static void reflect_dir_bit_in_cnf(NRF51GPIOState *s) +{ + size_t i; + + uint32_t value = s->dir; + + for (i = 0; i < NRF51_GPIO_PINS; i++) { + s->cnf[i] = (s->cnf[i] & ~(1UL)) | ((value >> i) & 0x01); + } +} + +static uint64_t nrf51_gpio_read(void *opaque, hwaddr offset, unsigned int size) +{ + NRF51GPIOState *s = NRF51_GPIO(opaque); + uint64_t r = 0; + size_t idx; + + switch (offset) { + case NRF51_GPIO_REG_OUT ... NRF51_GPIO_REG_OUTCLR: + r = s->out; + break; + + case NRF51_GPIO_REG_IN: + r = s->in; + break; + + case NRF51_GPIO_REG_DIR ... NRF51_GPIO_REG_DIRCLR: + r = s->dir; + break; + + case NRF51_GPIO_REG_CNF_START ... NRF51_GPIO_REG_CNF_END: + idx = (offset - NRF51_GPIO_REG_CNF_START) / 4; + r = s->cnf[idx]; + break; + + default: + qemu_log_mask(LOG_GUEST_ERROR, + "%s: bad read offset 0x%" HWADDR_PRIx "\n", + __func__, offset); + } + + trace_nrf51_gpio_read(offset, r); + + return r; +} + +static void nrf51_gpio_write(void *opaque, hwaddr offset, + uint64_t value, unsigned int size) +{ + NRF51GPIOState *s = NRF51_GPIO(opaque); + size_t idx; + + trace_nrf51_gpio_write(offset, value); + + switch (offset) { + case NRF51_GPIO_REG_OUT: + s->out = value; + break; + + case NRF51_GPIO_REG_OUTSET: + s->out |= value; + break; + + case NRF51_GPIO_REG_OUTCLR: + s->out &= ~value; + break; + + case NRF51_GPIO_REG_DIR: + s->dir = value; + reflect_dir_bit_in_cnf(s); + break; + + case NRF51_GPIO_REG_DIRSET: + s->dir |= value; + reflect_dir_bit_in_cnf(s); + break; + + case NRF51_GPIO_REG_DIRCLR: + s->dir &= ~value; + reflect_dir_bit_in_cnf(s); + break; + + case NRF51_GPIO_REG_CNF_START ... NRF51_GPIO_REG_CNF_END: + idx = (offset - NRF51_GPIO_REG_CNF_START) / 4; + s->cnf[idx] = value; + /* + * direction is exposed in both the DIR register and the DIR bit + * of each PINs CNF configuration register. + */ + s->dir = (s->dir & ~(1UL << idx)) | ((value & 0x01) << idx); + break; + + default: + qemu_log_mask(LOG_GUEST_ERROR, + "%s: bad write offset 0x%" HWADDR_PRIx "\n", + __func__, offset); + } + + update_state(s); +} + +static const MemoryRegionOps gpio_ops = { + .read = nrf51_gpio_read, + .write = nrf51_gpio_write, + .endianness = DEVICE_LITTLE_ENDIAN, + .impl.min_access_size = 4, + .impl.max_access_size = 4, +}; + +static void nrf51_gpio_set(void *opaque, int line, int value) +{ + NRF51GPIOState *s = NRF51_GPIO(opaque); + + trace_nrf51_gpio_set(line, value); + + assert(line >= 0 && line < NRF51_GPIO_PINS); + + s->in_mask = deposit32(s->in_mask, line, 1, value >= 0); + if (value >= 0) { + s->in = deposit32(s->in, line, 1, value != 0); + } + + update_state(s); +} + +static void nrf51_gpio_reset(DeviceState *dev) +{ + NRF51GPIOState *s = NRF51_GPIO(dev); + size_t i; + + s->out = 0; + s->old_out = 0; + s->old_out_connected = 0; + s->in = 0; + s->in_mask = 0; + s->dir = 0; + + for (i = 0; i < NRF51_GPIO_PINS; i++) { + s->cnf[i] = 0x00000002; + } +} + +static const VMStateDescription vmstate_nrf51_gpio = { + .name = TYPE_NRF51_GPIO, + .version_id = 1, + .minimum_version_id = 1, + .fields = (VMStateField[]) { + VMSTATE_UINT32(out, NRF51GPIOState), + VMSTATE_UINT32(in, NRF51GPIOState), + VMSTATE_UINT32(in_mask, NRF51GPIOState), + VMSTATE_UINT32(dir, NRF51GPIOState), + VMSTATE_UINT32_ARRAY(cnf, NRF51GPIOState, NRF51_GPIO_PINS), + VMSTATE_UINT32(old_out, NRF51GPIOState), + VMSTATE_UINT32(old_out_connected, NRF51GPIOState), + VMSTATE_END_OF_LIST() + } +}; + +static void nrf51_gpio_init(Object *obj) +{ + NRF51GPIOState *s = NRF51_GPIO(obj); + + memory_region_init_io(&s->mmio, obj, &gpio_ops, s, + TYPE_NRF51_GPIO, NRF51_GPIO_SIZE); + sysbus_init_mmio(SYS_BUS_DEVICE(obj), &s->mmio); + + qdev_init_gpio_in(DEVICE(s), nrf51_gpio_set, NRF51_GPIO_PINS); + qdev_init_gpio_out(DEVICE(s), s->output, NRF51_GPIO_PINS); +} + +static void nrf51_gpio_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + + dc->vmsd = &vmstate_nrf51_gpio; + dc->reset = nrf51_gpio_reset; + dc->desc = "nRF51 GPIO"; +} + +static const TypeInfo nrf51_gpio_info = { + .name = TYPE_NRF51_GPIO, + .parent = TYPE_SYS_BUS_DEVICE, + .instance_size = sizeof(NRF51GPIOState), + .instance_init = nrf51_gpio_init, + .class_init = nrf51_gpio_class_init +}; + +static void nrf51_gpio_register_types(void) +{ + type_register_static(&nrf51_gpio_info); +} + +type_init(nrf51_gpio_register_types) diff --git a/hw/gpio/trace-events b/hw/gpio/trace-events new file mode 100644 index 0000000000..cb41a89756 --- /dev/null +++ b/hw/gpio/trace-events @@ -0,0 +1,7 @@ +# See docs/devel/tracing.txt for syntax documentation. + +# hw/gpio/nrf51_gpio.c +nrf51_gpio_read(uint64_t offset, uint64_t r) "offset 0x%" PRIx64 " value 0x%" PRIx64 +nrf51_gpio_write(uint64_t offset, uint64_t value) "offset 0x%" PRIx64 " value 0x%" PRIx64 +nrf51_gpio_set(int64_t line, int64_t value) "line %" PRIi64 " value %" PRIi64 +nrf51_gpio_update_output_irq(int64_t line, int64_t value) "line %" PRIi64 " value %" PRIi64 \ No newline at end of file diff --git a/include/hw/gpio/nrf51_gpio.h b/include/hw/gpio/nrf51_gpio.h new file mode 100644 index 0000000000..337ee534bb --- /dev/null +++ b/include/hw/gpio/nrf51_gpio.h @@ -0,0 +1,69 @@ +/* + * nRF51 System-on-Chip general purpose input/output register definition + * + * QEMU interface: + * + sysbus MMIO regions 0: GPIO registers + * + Unnamed GPIO inputs 0-31: Set tri-state input level for GPIO pin. + * Level -1: Externally Disconnected/Floating; Pull-up/down will be regarded + * Level 0: Input externally driven LOW + * Level 1: Input externally driven HIGH + * + Unnamed GPIO outputs 0-31: + * Level -1: Disconnected/Floating + * Level 0: Driven LOW + * Level 1: Driven HIGH + * + * Accuracy of the peripheral model: + * + The nRF51 GPIO output driver supports two modes, standard and high-current + * mode. These different drive modes are not modeled and handled the same. + * + Pin SENSEing is not modeled/implemented. + * + * Copyright 2018 Steffen Görtz + * + * This code is licensed under the GPL version 2 or later. See + * the COPYING file in the top-level directory. + * + */ +#ifndef NRF51_GPIO_H +#define NRF51_GPIO_H + +#include "hw/sysbus.h" +#define TYPE_NRF51_GPIO "nrf51_soc.gpio" +#define NRF51_GPIO(obj) OBJECT_CHECK(NRF51GPIOState, (obj), TYPE_NRF51_GPIO) + +#define NRF51_GPIO_PINS 32 + +#define NRF51_GPIO_SIZE 0x1000 + +#define NRF51_GPIO_REG_OUT 0x504 +#define NRF51_GPIO_REG_OUTSET 0x508 +#define NRF51_GPIO_REG_OUTCLR 0x50C +#define NRF51_GPIO_REG_IN 0x510 +#define NRF51_GPIO_REG_DIR 0x514 +#define NRF51_GPIO_REG_DIRSET 0x518 +#define NRF51_GPIO_REG_DIRCLR 0x51C +#define NRF51_GPIO_REG_CNF_START 0x700 +#define NRF51_GPIO_REG_CNF_END 0x77F + +#define NRF51_GPIO_PULLDOWN 1 +#define NRF51_GPIO_PULLUP 3 + +typedef struct NRF51GPIOState { + SysBusDevice parent_obj; + + MemoryRegion mmio; + qemu_irq irq; + + uint32_t out; + uint32_t in; + uint32_t in_mask; + uint32_t dir; + uint32_t cnf[NRF51_GPIO_PINS]; + + uint32_t old_out; + uint32_t old_out_connected; + + qemu_irq output[NRF51_GPIO_PINS]; +} NRF51GPIOState; + + +#endif From bb42c4cb2aad60a5309228c95eda9381f080e50a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Steffen=20G=C3=B6rtz?= Date: Mon, 7 Jan 2019 15:23:47 +0000 Subject: [PATCH 29/37] arm: Instantiate NRF51 general purpose I/O MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Instantiates GPIO peripheral model Signed-off-by: Steffen Görtz Reviewed-by: Stefan Hajnoczi Reviewed-by: Peter Maydell Signed-off-by: Stefan Hajnoczi Message-id: 20190103091119.9367-7-stefanha@redhat.com Signed-off-by: Peter Maydell --- hw/arm/nrf51_soc.c | 16 ++++++++++++++++ include/hw/arm/nrf51_soc.h | 2 ++ 2 files changed, 18 insertions(+) diff --git a/hw/arm/nrf51_soc.c b/hw/arm/nrf51_soc.c index d2a19b8ead..db817fe506 100644 --- a/hw/arm/nrf51_soc.c +++ b/hw/arm/nrf51_soc.c @@ -99,6 +99,19 @@ static void nrf51_soc_realize(DeviceState *dev_soc, Error **errp) qdev_get_gpio_in(DEVICE(&s->cpu), BASE_TO_IRQ(NRF51_RNG_BASE))); + /* GPIO */ + object_property_set_bool(OBJECT(&s->gpio), true, "realized", &err); + if (err) { + error_propagate(errp, err); + return; + } + + mr = sysbus_mmio_get_region(SYS_BUS_DEVICE(&s->gpio), 0); + memory_region_add_subregion_overlap(&s->container, NRF51_GPIO_BASE, mr, 0); + + /* Pass all GPIOs to the SOC layer so they are available to the board */ + qdev_pass_gpios(DEVICE(&s->gpio), dev_soc, NULL); + create_unimplemented_device("nrf51_soc.io", NRF51_IOMEM_BASE, NRF51_IOMEM_SIZE); create_unimplemented_device("nrf51_soc.ficr", NRF51_FICR_BASE, @@ -126,6 +139,9 @@ static void nrf51_soc_init(Object *obj) sysbus_init_child_obj(obj, "rng", &s->rng, sizeof(s->rng), TYPE_NRF51_RNG); + + sysbus_init_child_obj(obj, "gpio", &s->gpio, sizeof(s->gpio), + TYPE_NRF51_GPIO); } static Property nrf51_soc_properties[] = { diff --git a/include/hw/arm/nrf51_soc.h b/include/hw/arm/nrf51_soc.h index 9e3ba916bd..84e0278881 100644 --- a/include/hw/arm/nrf51_soc.h +++ b/include/hw/arm/nrf51_soc.h @@ -14,6 +14,7 @@ #include "hw/arm/armv7m.h" #include "hw/char/nrf51_uart.h" #include "hw/misc/nrf51_rng.h" +#include "hw/gpio/nrf51_gpio.h" #define TYPE_NRF51_SOC "nrf51-soc" #define NRF51_SOC(obj) \ @@ -28,6 +29,7 @@ typedef struct NRF51State { NRF51UARTState uart; NRF51RNGState rng; + NRF51GPIOState gpio; MemoryRegion iomem; MemoryRegion sram; From 17ff8e18cc1a160737d72a0bf3dac2f6372524ea Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Steffen=20G=C3=B6rtz?= Date: Mon, 7 Jan 2019 15:23:47 +0000 Subject: [PATCH 30/37] tests/microbit-test: Add Tests for nRF51 GPIO MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The test suite for the nRF51 GPIO peripheral for now only tests initial state. Additionally a set of tests testing an implementation detail of the model are included. Signed-off-by: Steffen Görtz Reviewed-by: Stefan Hajnoczi Signed-off-by: Stefan Hajnoczi Message-id: 20190103091119.9367-8-stefanha@redhat.com [PMM: fixed stray space at start of file] Signed-off-by: Peter Maydell --- tests/Makefile.include | 2 + tests/microbit-test.c | 160 +++++++++++++++++++++++++++++++++++++++++ 2 files changed, 162 insertions(+) create mode 100644 tests/microbit-test.c diff --git a/tests/Makefile.include b/tests/Makefile.include index 3f5a1d0c30..9c84bbd829 100644 --- a/tests/Makefile.include +++ b/tests/Makefile.include @@ -277,6 +277,7 @@ check-qtest-sparc64-y += tests/boot-serial-test$(EXESUF) check-qtest-arm-y += tests/tmp105-test$(EXESUF) check-qtest-arm-y += tests/pca9552-test$(EXESUF) check-qtest-arm-y += tests/ds1338-test$(EXESUF) +check-qtest-arm-y += tests/microbit-test$(EXESUF) check-qtest-arm-y += tests/m25p80-test$(EXESUF) check-qtest-arm-y += tests/virtio-blk-test$(EXESUF) check-qtest-arm-y += tests/test-arm-mptimer$(EXESUF) @@ -708,6 +709,7 @@ tests/pxe-test$(EXESUF): tests/pxe-test.o tests/boot-sector.o $(libqos-obj-y) tests/tmp105-test$(EXESUF): tests/tmp105-test.o $(libqos-omap-obj-y) tests/pca9552-test$(EXESUF): tests/pca9552-test.o $(libqos-omap-obj-y) tests/ds1338-test$(EXESUF): tests/ds1338-test.o $(libqos-imx-obj-y) +tests/microbit-test$(EXESUF): tests/microbit-test.o tests/m25p80-test$(EXESUF): tests/m25p80-test.o tests/i440fx-test$(EXESUF): tests/i440fx-test.o $(libqos-pc-obj-y) tests/q35-test$(EXESUF): tests/q35-test.o $(libqos-pc-obj-y) diff --git a/tests/microbit-test.c b/tests/microbit-test.c new file mode 100644 index 0000000000..f0a180cbc6 --- /dev/null +++ b/tests/microbit-test.c @@ -0,0 +1,160 @@ +/* + * QTest testcase for Microbit board using the Nordic Semiconductor nRF51 SoC. + * + * nRF51: + * Reference Manual: http://infocenter.nordicsemi.com/pdf/nRF51_RM_v3.0.pdf + * Product Spec: http://infocenter.nordicsemi.com/pdf/nRF51822_PS_v3.1.pdf + * + * Microbit Board: http://microbit.org/ + * + * Copyright 2018 Steffen Görtz + * + * This code is licensed under the GPL version 2 or later. See + * the COPYING file in the top-level directory. + */ + + +#include "qemu/osdep.h" +#include "exec/hwaddr.h" +#include "libqtest.h" + +#include "hw/arm/nrf51.h" +#include "hw/gpio/nrf51_gpio.h" + +static void test_nrf51_gpio(void) +{ + size_t i; + uint32_t actual, expected; + + struct { + hwaddr addr; + uint32_t expected; + } const reset_state[] = { + {NRF51_GPIO_REG_OUT, 0x00000000}, {NRF51_GPIO_REG_OUTSET, 0x00000000}, + {NRF51_GPIO_REG_OUTCLR, 0x00000000}, {NRF51_GPIO_REG_IN, 0x00000000}, + {NRF51_GPIO_REG_DIR, 0x00000000}, {NRF51_GPIO_REG_DIRSET, 0x00000000}, + {NRF51_GPIO_REG_DIRCLR, 0x00000000} + }; + + /* Check reset state */ + for (i = 0; i < ARRAY_SIZE(reset_state); i++) { + expected = reset_state[i].expected; + actual = readl(NRF51_GPIO_BASE + reset_state[i].addr); + g_assert_cmpuint(actual, ==, expected); + } + + for (i = 0; i < NRF51_GPIO_PINS; i++) { + expected = 0x00000002; + actual = readl(NRF51_GPIO_BASE + NRF51_GPIO_REG_CNF_START + i * 4); + g_assert_cmpuint(actual, ==, expected); + } + + /* Check dir bit consistency between dir and cnf */ + /* Check set via DIRSET */ + expected = 0x80000001; + writel(NRF51_GPIO_BASE + NRF51_GPIO_REG_DIRSET, expected); + actual = readl(NRF51_GPIO_BASE + NRF51_GPIO_REG_DIR); + g_assert_cmpuint(actual, ==, expected); + actual = readl(NRF51_GPIO_BASE + NRF51_GPIO_REG_CNF_START) & 0x01; + g_assert_cmpuint(actual, ==, 0x01); + actual = readl(NRF51_GPIO_BASE + NRF51_GPIO_REG_CNF_END) & 0x01; + g_assert_cmpuint(actual, ==, 0x01); + + /* Check clear via DIRCLR */ + writel(NRF51_GPIO_BASE + NRF51_GPIO_REG_DIRCLR, 0x80000001); + actual = readl(NRF51_GPIO_BASE + NRF51_GPIO_REG_DIR); + g_assert_cmpuint(actual, ==, 0x00000000); + actual = readl(NRF51_GPIO_BASE + NRF51_GPIO_REG_CNF_START) & 0x01; + g_assert_cmpuint(actual, ==, 0x00); + actual = readl(NRF51_GPIO_BASE + NRF51_GPIO_REG_CNF_END) & 0x01; + g_assert_cmpuint(actual, ==, 0x00); + + /* Check set via DIR */ + expected = 0x80000001; + writel(NRF51_GPIO_BASE + NRF51_GPIO_REG_DIR, expected); + actual = readl(NRF51_GPIO_BASE + NRF51_GPIO_REG_DIR); + g_assert_cmpuint(actual, ==, expected); + actual = readl(NRF51_GPIO_BASE + NRF51_GPIO_REG_CNF_START) & 0x01; + g_assert_cmpuint(actual, ==, 0x01); + actual = readl(NRF51_GPIO_BASE + NRF51_GPIO_REG_CNF_END) & 0x01; + g_assert_cmpuint(actual, ==, 0x01); + + /* Reset DIR */ + writel(NRF51_GPIO_BASE + NRF51_GPIO_REG_DIR, 0x00000000); + + /* Check Input propagates */ + writel(NRF51_GPIO_BASE + NRF51_GPIO_REG_CNF_START, 0x00); + qtest_set_irq_in(global_qtest, "/machine/nrf51", "unnamed-gpio-in", 0, 0); + actual = readl(NRF51_GPIO_BASE + NRF51_GPIO_REG_IN) & 0x01; + g_assert_cmpuint(actual, ==, 0x00); + qtest_set_irq_in(global_qtest, "/machine/nrf51", "unnamed-gpio-in", 0, 1); + actual = readl(NRF51_GPIO_BASE + NRF51_GPIO_REG_IN) & 0x01; + g_assert_cmpuint(actual, ==, 0x01); + qtest_set_irq_in(global_qtest, "/machine/nrf51", "unnamed-gpio-in", 0, -1); + actual = readl(NRF51_GPIO_BASE + NRF51_GPIO_REG_IN) & 0x01; + g_assert_cmpuint(actual, ==, 0x01); + writel(NRF51_GPIO_BASE + NRF51_GPIO_REG_CNF_START, 0x02); + + /* Check pull-up working */ + qtest_set_irq_in(global_qtest, "/machine/nrf51", "unnamed-gpio-in", 0, 0); + writel(NRF51_GPIO_BASE + NRF51_GPIO_REG_CNF_START, 0b0000); + actual = readl(NRF51_GPIO_BASE + NRF51_GPIO_REG_IN) & 0x01; + g_assert_cmpuint(actual, ==, 0x00); + writel(NRF51_GPIO_BASE + NRF51_GPIO_REG_CNF_START, 0b1110); + actual = readl(NRF51_GPIO_BASE + NRF51_GPIO_REG_IN) & 0x01; + g_assert_cmpuint(actual, ==, 0x01); + writel(NRF51_GPIO_BASE + NRF51_GPIO_REG_CNF_START, 0x02); + + /* Check pull-down working */ + qtest_set_irq_in(global_qtest, "/machine/nrf51", "unnamed-gpio-in", 0, 1); + writel(NRF51_GPIO_BASE + NRF51_GPIO_REG_CNF_START, 0b0000); + actual = readl(NRF51_GPIO_BASE + NRF51_GPIO_REG_IN) & 0x01; + g_assert_cmpuint(actual, ==, 0x01); + writel(NRF51_GPIO_BASE + NRF51_GPIO_REG_CNF_START, 0b0110); + actual = readl(NRF51_GPIO_BASE + NRF51_GPIO_REG_IN) & 0x01; + g_assert_cmpuint(actual, ==, 0x00); + writel(NRF51_GPIO_BASE + NRF51_GPIO_REG_CNF_START, 0x02); + qtest_set_irq_in(global_qtest, "/machine/nrf51", "unnamed-gpio-in", 0, -1); + + /* Check Output propagates */ + irq_intercept_out("/machine/nrf51"); + writel(NRF51_GPIO_BASE + NRF51_GPIO_REG_CNF_START, 0b0011); + writel(NRF51_GPIO_BASE + NRF51_GPIO_REG_OUTSET, 0x01); + g_assert_true(get_irq(0)); + writel(NRF51_GPIO_BASE + NRF51_GPIO_REG_OUTCLR, 0x01); + g_assert_false(get_irq(0)); + + /* Check self-stimulation */ + writel(NRF51_GPIO_BASE + NRF51_GPIO_REG_CNF_START, 0b01); + writel(NRF51_GPIO_BASE + NRF51_GPIO_REG_OUTSET, 0x01); + actual = readl(NRF51_GPIO_BASE + NRF51_GPIO_REG_IN) & 0x01; + g_assert_cmpuint(actual, ==, 0x01); + + writel(NRF51_GPIO_BASE + NRF51_GPIO_REG_OUTCLR, 0x01); + actual = readl(NRF51_GPIO_BASE + NRF51_GPIO_REG_IN) & 0x01; + g_assert_cmpuint(actual, ==, 0x00); + + /* + * Check short-circuit - generates an guest_error which must be checked + * manually as long as qtest can not scan qemu_log messages + */ + writel(NRF51_GPIO_BASE + NRF51_GPIO_REG_CNF_START, 0b01); + writel(NRF51_GPIO_BASE + NRF51_GPIO_REG_OUTSET, 0x01); + qtest_set_irq_in(global_qtest, "/machine/nrf51", "unnamed-gpio-in", 0, 0); +} + +int main(int argc, char **argv) +{ + int ret; + + g_test_init(&argc, &argv, NULL); + + global_qtest = qtest_initf("-machine microbit"); + + qtest_add_func("/microbit/nrf51/gpio", test_nrf51_gpio); + + ret = g_test_run(); + + qtest_quit(global_qtest); + return ret; +} From c5a4829c080866c78b6e04b6e8198aa58e185878 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Steffen=20G=C3=B6rtz?= Date: Mon, 7 Jan 2019 15:23:47 +0000 Subject: [PATCH 31/37] hw/timer/nrf51_timer: Add nRF51 Timer peripheral MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This patch adds the model for the nRF51 timer peripheral. Currently, only the TIMER mode is implemented. Signed-off-by: Steffen Görtz Signed-off-by: Stefan Hajnoczi Message-id: 20190103091119.9367-9-stefanha@redhat.com Reviewed-by: Peter Maydell Signed-off-by: Peter Maydell --- hw/timer/Makefile.objs | 1 + hw/timer/nrf51_timer.c | 393 +++++++++++++++++++++++++++++++++ hw/timer/trace-events | 5 + include/hw/timer/nrf51_timer.h | 80 +++++++ 4 files changed, 479 insertions(+) create mode 100644 hw/timer/nrf51_timer.c create mode 100644 include/hw/timer/nrf51_timer.h diff --git a/hw/timer/Makefile.objs b/hw/timer/Makefile.objs index b32194d153..0e9a4530f8 100644 --- a/hw/timer/Makefile.objs +++ b/hw/timer/Makefile.objs @@ -23,6 +23,7 @@ common-obj-$(CONFIG_IMX) += imx_gpt.o common-obj-$(CONFIG_LM32) += lm32_timer.o common-obj-$(CONFIG_MILKYMIST) += milkymist-sysctl.o common-obj-$(CONFIG_XLNX_ZYNQMP) += xlnx-zynqmp-rtc.o +common-obj-$(CONFIG_NRF51_SOC) += nrf51_timer.o obj-$(CONFIG_ALTERA_TIMER) += altera_timer.o obj-$(CONFIG_EXYNOS4) += exynos4210_mct.o diff --git a/hw/timer/nrf51_timer.c b/hw/timer/nrf51_timer.c new file mode 100644 index 0000000000..0c90662896 --- /dev/null +++ b/hw/timer/nrf51_timer.c @@ -0,0 +1,393 @@ +/* + * nRF51 System-on-Chip Timer peripheral + * + * Reference Manual: http://infocenter.nordicsemi.com/pdf/nRF51_RM_v3.0.pdf + * Product Spec: http://infocenter.nordicsemi.com/pdf/nRF51822_PS_v3.1.pdf + * + * Copyright 2018 Steffen Görtz + * Copyright (c) 2019 Red Hat, Inc. + * + * This code is licensed under the GPL version 2 or later. See + * the COPYING file in the top-level directory. + */ + +#include "qemu/osdep.h" +#include "qemu/log.h" +#include "hw/arm/nrf51.h" +#include "hw/timer/nrf51_timer.h" +#include "trace.h" + +#define TIMER_CLK_FREQ 16000000UL + +static uint32_t const bitwidths[] = {16, 8, 24, 32}; + +static uint32_t ns_to_ticks(NRF51TimerState *s, int64_t ns) +{ + uint32_t freq = TIMER_CLK_FREQ >> s->prescaler; + + return muldiv64(ns, freq, NANOSECONDS_PER_SECOND); +} + +static int64_t ticks_to_ns(NRF51TimerState *s, uint32_t ticks) +{ + uint32_t freq = TIMER_CLK_FREQ >> s->prescaler; + + return muldiv64(ticks, NANOSECONDS_PER_SECOND, freq); +} + +/* Returns number of ticks since last call */ +static uint32_t update_counter(NRF51TimerState *s, int64_t now) +{ + uint32_t ticks = ns_to_ticks(s, now - s->update_counter_ns); + + s->counter = (s->counter + ticks) % BIT(bitwidths[s->bitmode]); + s->update_counter_ns = now; + return ticks; +} + +/* Assumes s->counter is up-to-date */ +static void rearm_timer(NRF51TimerState *s, int64_t now) +{ + int64_t min_ns = INT64_MAX; + size_t i; + + for (i = 0; i < NRF51_TIMER_REG_COUNT; i++) { + int64_t delta_ns; + + if (s->events_compare[i]) { + continue; /* already expired, ignore it for now */ + } + + if (s->cc[i] <= s->counter) { + delta_ns = ticks_to_ns(s, BIT(bitwidths[s->bitmode]) - + s->counter + s->cc[i]); + } else { + delta_ns = ticks_to_ns(s, s->cc[i] - s->counter); + } + + if (delta_ns < min_ns) { + min_ns = delta_ns; + } + } + + if (min_ns != INT64_MAX) { + timer_mod_ns(&s->timer, now + min_ns); + } +} + +static void update_irq(NRF51TimerState *s) +{ + bool flag = false; + size_t i; + + for (i = 0; i < NRF51_TIMER_REG_COUNT; i++) { + flag |= s->events_compare[i] && extract32(s->inten, 16 + i, 1); + } + qemu_set_irq(s->irq, flag); +} + +static void timer_expire(void *opaque) +{ + NRF51TimerState *s = NRF51_TIMER(opaque); + int64_t now = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL); + uint32_t cc_remaining[NRF51_TIMER_REG_COUNT]; + bool should_stop = false; + uint32_t ticks; + size_t i; + + for (i = 0; i < NRF51_TIMER_REG_COUNT; i++) { + if (s->cc[i] > s->counter) { + cc_remaining[i] = s->cc[i] - s->counter; + } else { + cc_remaining[i] = BIT(bitwidths[s->bitmode]) - + s->counter + s->cc[i]; + } + } + + ticks = update_counter(s, now); + + for (i = 0; i < NRF51_TIMER_REG_COUNT; i++) { + if (cc_remaining[i] <= ticks) { + s->events_compare[i] = 1; + + if (s->shorts & BIT(i)) { + s->timer_start_ns = now; + s->update_counter_ns = s->timer_start_ns; + s->counter = 0; + } + + should_stop |= s->shorts & BIT(i + 8); + } + } + + update_irq(s); + + if (should_stop) { + s->running = false; + timer_del(&s->timer); + } else { + rearm_timer(s, now); + } +} + +static void counter_compare(NRF51TimerState *s) +{ + uint32_t counter = s->counter; + size_t i; + + for (i = 0; i < NRF51_TIMER_REG_COUNT; i++) { + if (counter == s->cc[i]) { + s->events_compare[i] = 1; + + if (s->shorts & BIT(i)) { + s->counter = 0; + } + } + } +} + +static uint64_t nrf51_timer_read(void *opaque, hwaddr offset, unsigned int size) +{ + NRF51TimerState *s = NRF51_TIMER(opaque); + uint64_t r = 0; + + switch (offset) { + case NRF51_TIMER_EVENT_COMPARE_0 ... NRF51_TIMER_EVENT_COMPARE_3: + r = s->events_compare[(offset - NRF51_TIMER_EVENT_COMPARE_0) / 4]; + break; + case NRF51_TIMER_REG_SHORTS: + r = s->shorts; + break; + case NRF51_TIMER_REG_INTENSET: + r = s->inten; + break; + case NRF51_TIMER_REG_INTENCLR: + r = s->inten; + break; + case NRF51_TIMER_REG_MODE: + r = s->mode; + break; + case NRF51_TIMER_REG_BITMODE: + r = s->bitmode; + break; + case NRF51_TIMER_REG_PRESCALER: + r = s->prescaler; + break; + case NRF51_TIMER_REG_CC0 ... NRF51_TIMER_REG_CC3: + r = s->cc[(offset - NRF51_TIMER_REG_CC0) / 4]; + break; + default: + qemu_log_mask(LOG_GUEST_ERROR, + "%s: bad read offset 0x%" HWADDR_PRIx "\n", + __func__, offset); + } + + trace_nrf51_timer_read(offset, r, size); + + return r; +} + +static void nrf51_timer_write(void *opaque, hwaddr offset, + uint64_t value, unsigned int size) +{ + NRF51TimerState *s = NRF51_TIMER(opaque); + uint64_t now = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL); + size_t idx; + + trace_nrf51_timer_write(offset, value, size); + + switch (offset) { + case NRF51_TIMER_TASK_START: + if (value == NRF51_TRIGGER_TASK && s->mode == NRF51_TIMER_TIMER) { + s->running = true; + s->timer_start_ns = now - ticks_to_ns(s, s->counter); + s->update_counter_ns = s->timer_start_ns; + rearm_timer(s, now); + } + break; + case NRF51_TIMER_TASK_STOP: + case NRF51_TIMER_TASK_SHUTDOWN: + if (value == NRF51_TRIGGER_TASK) { + s->running = false; + timer_del(&s->timer); + } + break; + case NRF51_TIMER_TASK_COUNT: + if (value == NRF51_TRIGGER_TASK && s->mode == NRF51_TIMER_COUNTER) { + s->counter = (s->counter + 1) % BIT(bitwidths[s->bitmode]); + counter_compare(s); + } + break; + case NRF51_TIMER_TASK_CLEAR: + if (value == NRF51_TRIGGER_TASK) { + s->timer_start_ns = now; + s->update_counter_ns = s->timer_start_ns; + s->counter = 0; + if (s->running) { + rearm_timer(s, now); + } + } + break; + case NRF51_TIMER_TASK_CAPTURE_0 ... NRF51_TIMER_TASK_CAPTURE_3: + if (value == NRF51_TRIGGER_TASK) { + if (s->running) { + timer_expire(s); /* update counter and all state */ + } + + idx = (offset - NRF51_TIMER_TASK_CAPTURE_0) / 4; + s->cc[idx] = s->counter; + } + break; + case NRF51_TIMER_EVENT_COMPARE_0 ... NRF51_TIMER_EVENT_COMPARE_3: + if (value == NRF51_EVENT_CLEAR) { + s->events_compare[(offset - NRF51_TIMER_EVENT_COMPARE_0) / 4] = 0; + + if (s->running) { + timer_expire(s); /* update counter and all state */ + } + } + break; + case NRF51_TIMER_REG_SHORTS: + s->shorts = value & NRF51_TIMER_REG_SHORTS_MASK; + break; + case NRF51_TIMER_REG_INTENSET: + s->inten |= value & NRF51_TIMER_REG_INTEN_MASK; + break; + case NRF51_TIMER_REG_INTENCLR: + s->inten &= ~(value & NRF51_TIMER_REG_INTEN_MASK); + break; + case NRF51_TIMER_REG_MODE: + s->mode = value; + break; + case NRF51_TIMER_REG_BITMODE: + if (s->mode == NRF51_TIMER_TIMER && s->running) { + qemu_log_mask(LOG_GUEST_ERROR, + "%s: erroneous change of BITMODE while timer is running\n", + __func__); + } + s->bitmode = value & NRF51_TIMER_REG_BITMODE_MASK; + break; + case NRF51_TIMER_REG_PRESCALER: + if (s->mode == NRF51_TIMER_TIMER && s->running) { + qemu_log_mask(LOG_GUEST_ERROR, + "%s: erroneous change of PRESCALER while timer is running\n", + __func__); + } + s->prescaler = value & NRF51_TIMER_REG_PRESCALER_MASK; + break; + case NRF51_TIMER_REG_CC0 ... NRF51_TIMER_REG_CC3: + if (s->running) { + timer_expire(s); /* update counter */ + } + + idx = (offset - NRF51_TIMER_REG_CC0) / 4; + s->cc[idx] = value % BIT(bitwidths[s->bitmode]); + + if (s->running) { + rearm_timer(s, now); + } + break; + default: + qemu_log_mask(LOG_GUEST_ERROR, + "%s: bad write offset 0x%" HWADDR_PRIx "\n", + __func__, offset); + } + + update_irq(s); +} + +static const MemoryRegionOps rng_ops = { + .read = nrf51_timer_read, + .write = nrf51_timer_write, + .endianness = DEVICE_LITTLE_ENDIAN, + .impl.min_access_size = 4, + .impl.max_access_size = 4, +}; + +static void nrf51_timer_init(Object *obj) +{ + NRF51TimerState *s = NRF51_TIMER(obj); + SysBusDevice *sbd = SYS_BUS_DEVICE(obj); + + memory_region_init_io(&s->iomem, obj, &rng_ops, s, + TYPE_NRF51_TIMER, NRF51_TIMER_SIZE); + sysbus_init_mmio(sbd, &s->iomem); + sysbus_init_irq(sbd, &s->irq); + + timer_init_ns(&s->timer, QEMU_CLOCK_VIRTUAL, timer_expire, s); +} + +static void nrf51_timer_reset(DeviceState *dev) +{ + NRF51TimerState *s = NRF51_TIMER(dev); + + timer_del(&s->timer); + s->timer_start_ns = 0x00; + s->update_counter_ns = 0x00; + s->counter = 0x00; + s->running = false; + + memset(s->events_compare, 0x00, sizeof(s->events_compare)); + memset(s->cc, 0x00, sizeof(s->cc)); + + s->shorts = 0x00; + s->inten = 0x00; + s->mode = 0x00; + s->bitmode = 0x00; + s->prescaler = 0x00; +} + +static int nrf51_timer_post_load(void *opaque, int version_id) +{ + NRF51TimerState *s = NRF51_TIMER(opaque); + + if (s->running && s->mode == NRF51_TIMER_TIMER) { + timer_expire(s); + } + return 0; +} + +static const VMStateDescription vmstate_nrf51_timer = { + .name = TYPE_NRF51_TIMER, + .version_id = 1, + .post_load = nrf51_timer_post_load, + .fields = (VMStateField[]) { + VMSTATE_TIMER(timer, NRF51TimerState), + VMSTATE_INT64(timer_start_ns, NRF51TimerState), + VMSTATE_INT64(update_counter_ns, NRF51TimerState), + VMSTATE_UINT32(counter, NRF51TimerState), + VMSTATE_BOOL(running, NRF51TimerState), + VMSTATE_UINT8_ARRAY(events_compare, NRF51TimerState, + NRF51_TIMER_REG_COUNT), + VMSTATE_UINT32_ARRAY(cc, NRF51TimerState, NRF51_TIMER_REG_COUNT), + VMSTATE_UINT32(shorts, NRF51TimerState), + VMSTATE_UINT32(inten, NRF51TimerState), + VMSTATE_UINT32(mode, NRF51TimerState), + VMSTATE_UINT32(bitmode, NRF51TimerState), + VMSTATE_UINT32(prescaler, NRF51TimerState), + VMSTATE_END_OF_LIST() + } +}; + +static void nrf51_timer_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + + dc->reset = nrf51_timer_reset; + dc->vmsd = &vmstate_nrf51_timer; +} + +static const TypeInfo nrf51_timer_info = { + .name = TYPE_NRF51_TIMER, + .parent = TYPE_SYS_BUS_DEVICE, + .instance_size = sizeof(NRF51TimerState), + .instance_init = nrf51_timer_init, + .class_init = nrf51_timer_class_init +}; + +static void nrf51_timer_register_types(void) +{ + type_register_static(&nrf51_timer_info); +} + +type_init(nrf51_timer_register_types) diff --git a/hw/timer/trace-events b/hw/timer/trace-events index 75bd3b1042..0144a68951 100644 --- a/hw/timer/trace-events +++ b/hw/timer/trace-events @@ -72,3 +72,8 @@ sun4v_rtc_write(uint64_t addr, uint64_t value) "write: addr 0x%" PRIx64 " value # hw/timer/xlnx-zynqmp-rtc.c xlnx_zynqmp_rtc_gettime(int year, int month, int day, int hour, int min, int sec) "Get time from host: %d-%d-%d %2d:%02d:%02d" + +# hw/timer/nrf51_timer.c +nrf51_timer_read(uint64_t addr, uint32_t value, unsigned size) "read addr 0x%" PRIx64 " data 0x%" PRIx32 " size %u" +nrf51_timer_write(uint64_t addr, uint32_t value, unsigned size) "write addr 0x%" PRIx64 " data 0x%" PRIx32 " size %u" + diff --git a/include/hw/timer/nrf51_timer.h b/include/hw/timer/nrf51_timer.h new file mode 100644 index 0000000000..85cad2300d --- /dev/null +++ b/include/hw/timer/nrf51_timer.h @@ -0,0 +1,80 @@ +/* + * nRF51 System-on-Chip Timer peripheral + * + * QEMU interface: + * + sysbus MMIO regions 0: GPIO registers + * + sysbus irq + * + * Copyright 2018 Steffen Görtz + * + * This code is licensed under the GPL version 2 or later. See + * the COPYING file in the top-level directory. + */ +#ifndef NRF51_TIMER_H +#define NRF51_TIMER_H + +#include "hw/sysbus.h" +#include "qemu/timer.h" +#define TYPE_NRF51_TIMER "nrf51_soc.timer" +#define NRF51_TIMER(obj) OBJECT_CHECK(NRF51TimerState, (obj), TYPE_NRF51_TIMER) + +#define NRF51_TIMER_REG_COUNT 4 + +#define NRF51_TIMER_TASK_START 0x000 +#define NRF51_TIMER_TASK_STOP 0x004 +#define NRF51_TIMER_TASK_COUNT 0x008 +#define NRF51_TIMER_TASK_CLEAR 0x00C +#define NRF51_TIMER_TASK_SHUTDOWN 0x010 +#define NRF51_TIMER_TASK_CAPTURE_0 0x040 +#define NRF51_TIMER_TASK_CAPTURE_3 0x04C + +#define NRF51_TIMER_EVENT_COMPARE_0 0x140 +#define NRF51_TIMER_EVENT_COMPARE_1 0x144 +#define NRF51_TIMER_EVENT_COMPARE_2 0x148 +#define NRF51_TIMER_EVENT_COMPARE_3 0x14C + +#define NRF51_TIMER_REG_SHORTS 0x200 +#define NRF51_TIMER_REG_SHORTS_MASK 0xf0f +#define NRF51_TIMER_REG_INTENSET 0x304 +#define NRF51_TIMER_REG_INTENCLR 0x308 +#define NRF51_TIMER_REG_INTEN_MASK 0xf0000 +#define NRF51_TIMER_REG_MODE 0x504 +#define NRF51_TIMER_REG_MODE_MASK 0x01 +#define NRF51_TIMER_TIMER 0 +#define NRF51_TIMER_COUNTER 1 +#define NRF51_TIMER_REG_BITMODE 0x508 +#define NRF51_TIMER_REG_BITMODE_MASK 0x03 +#define NRF51_TIMER_WIDTH_16 0 +#define NRF51_TIMER_WIDTH_8 1 +#define NRF51_TIMER_WIDTH_24 2 +#define NRF51_TIMER_WIDTH_32 3 +#define NRF51_TIMER_REG_PRESCALER 0x510 +#define NRF51_TIMER_REG_PRESCALER_MASK 0x0F +#define NRF51_TIMER_REG_CC0 0x540 +#define NRF51_TIMER_REG_CC3 0x54C + +typedef struct NRF51TimerState { + SysBusDevice parent_obj; + + MemoryRegion iomem; + qemu_irq irq; + + QEMUTimer timer; + int64_t timer_start_ns; + int64_t update_counter_ns; + uint32_t counter; + + bool running; + + uint8_t events_compare[NRF51_TIMER_REG_COUNT]; + uint32_t cc[NRF51_TIMER_REG_COUNT]; + uint32_t shorts; + uint32_t inten; + uint32_t mode; + uint32_t bitmode; + uint32_t prescaler; + +} NRF51TimerState; + + +#endif From 60facd906bb98e3e5e6788341c97b198e87e7762 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Steffen=20G=C3=B6rtz?= Date: Mon, 7 Jan 2019 15:23:47 +0000 Subject: [PATCH 32/37] arm: Instantiate NRF51 Timers MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Instantiates TIMER0 - TIMER2 Signed-off-by: Steffen Görtz Reviewed-by: Stefan Hajnoczi Reviewed-by: Peter Maydell Signed-off-by: Stefan Hajnoczi Message-id: 20190103091119.9367-10-stefanha@redhat.com Signed-off-by: Peter Maydell --- hw/arm/nrf51_soc.c | 26 ++++++++++++++++++++++++++ include/hw/arm/nrf51_soc.h | 4 ++++ 2 files changed, 30 insertions(+) diff --git a/hw/arm/nrf51_soc.c b/hw/arm/nrf51_soc.c index db817fe506..ef70bd62fa 100644 --- a/hw/arm/nrf51_soc.c +++ b/hw/arm/nrf51_soc.c @@ -39,6 +39,8 @@ static void nrf51_soc_realize(DeviceState *dev_soc, Error **errp) NRF51State *s = NRF51_SOC(dev_soc); MemoryRegion *mr; Error *err = NULL; + uint8_t i = 0; + hwaddr base_addr = 0; if (!s->board_memory) { error_setg(errp, "memory property was not set"); @@ -112,6 +114,22 @@ static void nrf51_soc_realize(DeviceState *dev_soc, Error **errp) /* Pass all GPIOs to the SOC layer so they are available to the board */ qdev_pass_gpios(DEVICE(&s->gpio), dev_soc, NULL); + /* TIMER */ + for (i = 0; i < NRF51_NUM_TIMERS; i++) { + object_property_set_bool(OBJECT(&s->timer[i]), true, "realized", &err); + if (err) { + error_propagate(errp, err); + return; + } + + base_addr = NRF51_TIMER_BASE + i * NRF51_TIMER_SIZE; + + sysbus_mmio_map(SYS_BUS_DEVICE(&s->timer[i]), 0, base_addr); + sysbus_connect_irq(SYS_BUS_DEVICE(&s->timer[i]), 0, + qdev_get_gpio_in(DEVICE(&s->cpu), + BASE_TO_IRQ(base_addr))); + } + create_unimplemented_device("nrf51_soc.io", NRF51_IOMEM_BASE, NRF51_IOMEM_SIZE); create_unimplemented_device("nrf51_soc.ficr", NRF51_FICR_BASE, @@ -122,6 +140,8 @@ static void nrf51_soc_realize(DeviceState *dev_soc, Error **errp) static void nrf51_soc_init(Object *obj) { + uint8_t i = 0; + NRF51State *s = NRF51_SOC(obj); memory_region_init(&s->container, obj, "nrf51-container", UINT64_MAX); @@ -142,6 +162,12 @@ static void nrf51_soc_init(Object *obj) sysbus_init_child_obj(obj, "gpio", &s->gpio, sizeof(s->gpio), TYPE_NRF51_GPIO); + + for (i = 0; i < NRF51_NUM_TIMERS; i++) { + sysbus_init_child_obj(obj, "timer[*]", &s->timer[i], + sizeof(s->timer[i]), TYPE_NRF51_TIMER); + + } } static Property nrf51_soc_properties[] = { diff --git a/include/hw/arm/nrf51_soc.h b/include/hw/arm/nrf51_soc.h index 84e0278881..39e613e1c9 100644 --- a/include/hw/arm/nrf51_soc.h +++ b/include/hw/arm/nrf51_soc.h @@ -15,11 +15,14 @@ #include "hw/char/nrf51_uart.h" #include "hw/misc/nrf51_rng.h" #include "hw/gpio/nrf51_gpio.h" +#include "hw/timer/nrf51_timer.h" #define TYPE_NRF51_SOC "nrf51-soc" #define NRF51_SOC(obj) \ OBJECT_CHECK(NRF51State, (obj), TYPE_NRF51_SOC) +#define NRF51_NUM_TIMERS 3 + typedef struct NRF51State { /*< private >*/ SysBusDevice parent_obj; @@ -30,6 +33,7 @@ typedef struct NRF51State { NRF51UARTState uart; NRF51RNGState rng; NRF51GPIOState gpio; + NRF51TimerState timer[NRF51_NUM_TIMERS]; MemoryRegion iomem; MemoryRegion sram; From 7ec543e4b9d3113f0f7afa49a474d7098abe29f0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Steffen=20G=C3=B6rtz?= Date: Mon, 7 Jan 2019 15:23:47 +0000 Subject: [PATCH 33/37] tests/microbit-test: Add Tests for nRF51 Timer MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Basic tests for nRF51 Timer Peripheral. Signed-off-by: Steffen Görtz Reviewed-by: Stefan Hajnoczi Signed-off-by: Stefan Hajnoczi Message-id: 20190103091119.9367-11-stefanha@redhat.com Signed-off-by: Peter Maydell --- tests/microbit-test.c | 95 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 95 insertions(+) diff --git a/tests/microbit-test.c b/tests/microbit-test.c index f0a180cbc6..0c125535f6 100644 --- a/tests/microbit-test.c +++ b/tests/microbit-test.c @@ -20,6 +20,7 @@ #include "hw/arm/nrf51.h" #include "hw/gpio/nrf51_gpio.h" +#include "hw/timer/nrf51_timer.h" static void test_nrf51_gpio(void) { @@ -143,6 +144,99 @@ static void test_nrf51_gpio(void) qtest_set_irq_in(global_qtest, "/machine/nrf51", "unnamed-gpio-in", 0, 0); } +static void timer_task(hwaddr task) +{ + writel(NRF51_TIMER_BASE + task, NRF51_TRIGGER_TASK); +} + +static void timer_clear_event(hwaddr event) +{ + writel(NRF51_TIMER_BASE + event, NRF51_EVENT_CLEAR); +} + +static void timer_set_bitmode(uint8_t mode) +{ + writel(NRF51_TIMER_BASE + NRF51_TIMER_REG_BITMODE, mode); +} + +static void timer_set_prescaler(uint8_t prescaler) +{ + writel(NRF51_TIMER_BASE + NRF51_TIMER_REG_PRESCALER, prescaler); +} + +static void timer_set_cc(size_t idx, uint32_t value) +{ + writel(NRF51_TIMER_BASE + NRF51_TIMER_REG_CC0 + idx * 4, value); +} + +static void timer_assert_events(uint32_t ev0, uint32_t ev1, uint32_t ev2, + uint32_t ev3) +{ + g_assert(readl(NRF51_TIMER_BASE + NRF51_TIMER_EVENT_COMPARE_0) == ev0); + g_assert(readl(NRF51_TIMER_BASE + NRF51_TIMER_EVENT_COMPARE_1) == ev1); + g_assert(readl(NRF51_TIMER_BASE + NRF51_TIMER_EVENT_COMPARE_2) == ev2); + g_assert(readl(NRF51_TIMER_BASE + NRF51_TIMER_EVENT_COMPARE_3) == ev3); +} + +static void test_nrf51_timer(void) +{ + uint32_t steps_to_overflow = 408; + + /* Compare Match */ + timer_task(NRF51_TIMER_TASK_STOP); + timer_task(NRF51_TIMER_TASK_CLEAR); + + timer_clear_event(NRF51_TIMER_EVENT_COMPARE_0); + timer_clear_event(NRF51_TIMER_EVENT_COMPARE_1); + timer_clear_event(NRF51_TIMER_EVENT_COMPARE_2); + timer_clear_event(NRF51_TIMER_EVENT_COMPARE_3); + + timer_set_bitmode(NRF51_TIMER_WIDTH_16); /* 16 MHz Timer */ + timer_set_prescaler(0); + /* Swept over in first step */ + timer_set_cc(0, 2); + /* Barely miss on first step */ + timer_set_cc(1, 162); + /* Spot on on third step */ + timer_set_cc(2, 480); + + timer_assert_events(0, 0, 0, 0); + + timer_task(NRF51_TIMER_TASK_START); + clock_step(10000); + timer_assert_events(1, 0, 0, 0); + + /* Swept over on first overflow */ + timer_set_cc(3, 114); + + clock_step(10000); + timer_assert_events(1, 1, 0, 0); + + clock_step(10000); + timer_assert_events(1, 1, 1, 0); + + /* Wrap time until internal counter overflows */ + while (steps_to_overflow--) { + timer_assert_events(1, 1, 1, 0); + clock_step(10000); + } + + timer_assert_events(1, 1, 1, 1); + + timer_clear_event(NRF51_TIMER_EVENT_COMPARE_0); + timer_clear_event(NRF51_TIMER_EVENT_COMPARE_1); + timer_clear_event(NRF51_TIMER_EVENT_COMPARE_2); + timer_clear_event(NRF51_TIMER_EVENT_COMPARE_3); + timer_assert_events(0, 0, 0, 0); + + timer_task(NRF51_TIMER_TASK_STOP); + + /* Test Proposal: Stop/Shutdown */ + /* Test Proposal: Shortcut Compare -> Clear */ + /* Test Proposal: Shortcut Compare -> Stop */ + /* Test Proposal: Counter Mode */ +} + int main(int argc, char **argv) { int ret; @@ -152,6 +246,7 @@ int main(int argc, char **argv) global_qtest = qtest_initf("-machine microbit"); qtest_add_func("/microbit/nrf51/gpio", test_nrf51_gpio); + qtest_add_func("/microbit/nrf51/timer", test_nrf51_timer); ret = g_test_run(); From b39dced66ac2612e89c9c822b6cb7b63c0de7f43 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Steffen=20G=C3=B6rtz?= Date: Mon, 7 Jan 2019 15:23:47 +0000 Subject: [PATCH 34/37] arm: Add Clock peripheral stub to NRF51 SOC MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This stubs enables the microbit-micropython firmware to run on the microbit machine. Signed-off-by: Steffen Görtz Reviewed-by: Stefan Hajnoczi Signed-off-by: Stefan Hajnoczi Message-id: 20190103091119.9367-12-stefanha@redhat.com Signed-off-by: Peter Maydell --- hw/arm/nrf51_soc.c | 26 ++++++++++++++++++++++++++ include/hw/arm/nrf51_soc.h | 1 + 2 files changed, 27 insertions(+) diff --git a/hw/arm/nrf51_soc.c b/hw/arm/nrf51_soc.c index ef70bd62fa..1630c27594 100644 --- a/hw/arm/nrf51_soc.c +++ b/hw/arm/nrf51_soc.c @@ -34,6 +34,26 @@ #define BASE_TO_IRQ(base) ((base >> 12) & 0x1F) +static uint64_t clock_read(void *opaque, hwaddr addr, unsigned int size) +{ + qemu_log_mask(LOG_UNIMP, "%s: 0x%" HWADDR_PRIx " [%u]\n", + __func__, addr, size); + return 1; +} + +static void clock_write(void *opaque, hwaddr addr, uint64_t data, + unsigned int size) +{ + qemu_log_mask(LOG_UNIMP, "%s: 0x%" HWADDR_PRIx " <- 0x%" PRIx64 " [%u]\n", + __func__, addr, data, size); +} + +static const MemoryRegionOps clock_ops = { + .read = clock_read, + .write = clock_write +}; + + static void nrf51_soc_realize(DeviceState *dev_soc, Error **errp) { NRF51State *s = NRF51_SOC(dev_soc); @@ -130,6 +150,12 @@ static void nrf51_soc_realize(DeviceState *dev_soc, Error **errp) BASE_TO_IRQ(base_addr))); } + /* STUB Peripherals */ + memory_region_init_io(&s->clock, NULL, &clock_ops, NULL, + "nrf51_soc.clock", 0x1000); + memory_region_add_subregion_overlap(&s->container, + NRF51_IOMEM_BASE, &s->clock, -1); + create_unimplemented_device("nrf51_soc.io", NRF51_IOMEM_BASE, NRF51_IOMEM_SIZE); create_unimplemented_device("nrf51_soc.ficr", NRF51_FICR_BASE, diff --git a/include/hw/arm/nrf51_soc.h b/include/hw/arm/nrf51_soc.h index 39e613e1c9..e06f0304b4 100644 --- a/include/hw/arm/nrf51_soc.h +++ b/include/hw/arm/nrf51_soc.h @@ -38,6 +38,7 @@ typedef struct NRF51State { MemoryRegion iomem; MemoryRegion sram; MemoryRegion flash; + MemoryRegion clock; uint32_t sram_size; uint32_t flash_size; From 96c552958dbb63453b5f02bea6e704006d50e39a Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Mon, 7 Jan 2019 15:23:48 +0000 Subject: [PATCH 35/37] target/arm: Emit barriers for A32/T32 load-acquire/store-release insns MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Now that MTTCG is here, the comment in the 32-bit Arm decoder that "Since the emulation does not have barriers, the acquire/release semantics need no special handling" is no longer true. Emit the correct barriers for the load-acquire/store-release insns, as we already do in the A64 decoder. Signed-off-by: Peter Maydell Tested-by: Alex Bennée Reviewed-by: Alex Bennée --- target/arm/translate.c | 33 ++++++++++++++++++++++++++------- 1 file changed, 26 insertions(+), 7 deletions(-) diff --git a/target/arm/translate.c b/target/arm/translate.c index ed3db0c394..66cf28c8cb 100644 --- a/target/arm/translate.c +++ b/target/arm/translate.c @@ -9733,6 +9733,8 @@ static void disas_arm_insn(DisasContext *s, unsigned int insn) rd = (insn >> 12) & 0xf; if (insn & (1 << 23)) { /* load/store exclusive */ + bool is_ld = extract32(insn, 20, 1); + bool is_lasr = !extract32(insn, 8, 1); int op2 = (insn >> 8) & 3; op1 = (insn >> 21) & 0x3; @@ -9760,11 +9762,12 @@ static void disas_arm_insn(DisasContext *s, unsigned int insn) addr = tcg_temp_local_new_i32(); load_reg_var(s, addr, rn); - /* Since the emulation does not have barriers, - the acquire/release semantics need no special - handling */ + if (is_lasr && !is_ld) { + tcg_gen_mb(TCG_MO_ALL | TCG_BAR_STRL); + } + if (op2 == 0) { - if (insn & (1 << 20)) { + if (is_ld) { tmp = tcg_temp_new_i32(); switch (op1) { case 0: /* lda */ @@ -9810,7 +9813,7 @@ static void disas_arm_insn(DisasContext *s, unsigned int insn) } tcg_temp_free_i32(tmp); } - } else if (insn & (1 << 20)) { + } else if (is_ld) { switch (op1) { case 0: /* ldrex */ gen_load_exclusive(s, rd, 15, addr, 2); @@ -9847,6 +9850,10 @@ static void disas_arm_insn(DisasContext *s, unsigned int insn) } } tcg_temp_free_i32(addr); + + if (is_lasr && is_ld) { + tcg_gen_mb(TCG_MO_ALL | TCG_BAR_LDAQ); + } } else if ((insn & 0x00300f00) == 0) { /* 0bcccc_0001_0x00_xxxx_xxxx_0000_1001_xxxx * - SWP, SWPB @@ -10862,6 +10869,8 @@ static void disas_thumb2_insn(DisasContext *s, uint32_t insn) tcg_gen_addi_i32(tmp, tmp, s->pc); store_reg(s, 15, tmp); } else { + bool is_lasr = false; + bool is_ld = extract32(insn, 20, 1); int op2 = (insn >> 6) & 0x3; op = (insn >> 4) & 0x3; switch (op2) { @@ -10883,12 +10892,18 @@ static void disas_thumb2_insn(DisasContext *s, uint32_t insn) case 3: /* Load-acquire/store-release exclusive */ ARCH(8); + is_lasr = true; break; } + + if (is_lasr && !is_ld) { + tcg_gen_mb(TCG_MO_ALL | TCG_BAR_STRL); + } + addr = tcg_temp_local_new_i32(); load_reg_var(s, addr, rn); if (!(op2 & 1)) { - if (insn & (1 << 20)) { + if (is_ld) { tmp = tcg_temp_new_i32(); switch (op) { case 0: /* ldab */ @@ -10927,12 +10942,16 @@ static void disas_thumb2_insn(DisasContext *s, uint32_t insn) } tcg_temp_free_i32(tmp); } - } else if (insn & (1 << 20)) { + } else if (is_ld) { gen_load_exclusive(s, rs, rd, addr, op); } else { gen_store_exclusive(s, rm, rs, rd, addr, op); } tcg_temp_free_i32(addr); + + if (is_lasr && is_ld) { + tcg_gen_mb(TCG_MO_ALL | TCG_BAR_LDAQ); + } } } else { /* Load/store multiple, RFE, SRS. */ From 619d54a8d854e797bf5627843a5e31015deda202 Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Mon, 7 Jan 2019 15:23:48 +0000 Subject: [PATCH 36/37] hw/misc/tz-mpc: Fix value of BLK_MAX register In the TZ Memory Protection Controller, the BLK_MAX register is supposed to return the maximum permitted value of the BLK_IDX register. Our implementation incorrectly returned max+1 (ie the total number of valid index values, since BLK_IDX is zero-based). Correct this off-by-one error. Since we consistently initialize and use s->blk_max throughout the implementation as the 'size' of the LUT, just adjust the value we return when the guest reads the BLK_MAX register, rather than trying to change the semantics of the s->blk_max internal struct field. Fixes: https://bugs.launchpad.net/qemu/+bug/1806824 Signed-off-by: Peter Maydell Reviewed-by: Richard Henderson Message-id: 20181213183249.3468-1-peter.maydell@linaro.org --- hw/misc/tz-mpc.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hw/misc/tz-mpc.c b/hw/misc/tz-mpc.c index fb48a1540b..9a84be75ed 100644 --- a/hw/misc/tz-mpc.c +++ b/hw/misc/tz-mpc.c @@ -150,7 +150,7 @@ static MemTxResult tz_mpc_reg_read(void *opaque, hwaddr addr, r = s->ctrl; break; case A_BLK_MAX: - r = s->blk_max; + r = s->blk_max - 1; break; case A_BLK_CFG: /* We are never in "init in progress state", so this just indicates From f831f955d420966471f5f8b316ba50d2523b1ff0 Mon Sep 17 00:00:00 2001 From: Nick Hudson Date: Mon, 7 Jan 2019 08:31:50 +0000 Subject: [PATCH 37/37] Support u-boot noload images for arm as used by, NetBSD/evbarm GENERIC kernel. noload kernels are loaded with the u-boot image header and as a result the header size needs adding to the entry point. Fake up a hdr so the kernel image is loaded at the right address and the entry point is adjusted appropriately. The default location for the uboot file is 32MiB above bottom of DRAM. This matches the recommendation in Documentation/arm/Booting. Clarify the load_uimage API to state the passing of a load address when an image doesn't specify one, or when loading a ramdisk is expected. Adjust callers of load_uimage, etc. Signed-off-by: Nick Hudson Message-id: 11488a08-1fe0-a278-2210-deb64731107f@gmx.co.uk Reviewed-by: Peter Maydell Signed-off-by: Peter Maydell --- hw/arm/boot.c | 8 +++++--- hw/core/loader.c | 19 ++++++++++++++++--- hw/core/uboot_image.h | 1 + hw/microblaze/boot.c | 2 +- hw/nios2/boot.c | 2 +- hw/ppc/e500.c | 1 + hw/ppc/ppc440_bamboo.c | 2 +- hw/ppc/sam460ex.c | 2 +- include/hw/loader.h | 7 ++++++- 9 files changed, 33 insertions(+), 11 deletions(-) diff --git a/hw/arm/boot.c b/hw/arm/boot.c index 94fce12802..c7a67af7a9 100644 --- a/hw/arm/boot.c +++ b/hw/arm/boot.c @@ -30,8 +30,9 @@ * Documentation/arm/Booting and Documentation/arm64/booting.txt * They have different preferred image load offsets from system RAM base. */ -#define KERNEL_ARGS_ADDR 0x100 -#define KERNEL_LOAD_ADDR 0x00010000 +#define KERNEL_ARGS_ADDR 0x100 +#define KERNEL_NOLOAD_ADDR 0x02000000 +#define KERNEL_LOAD_ADDR 0x00010000 #define KERNEL64_LOAD_ADDR 0x00080000 #define ARM64_TEXT_OFFSET_OFFSET 8 @@ -1082,7 +1083,8 @@ void arm_load_kernel(ARMCPU *cpu, struct arm_boot_info *info) } entry = elf_entry; if (kernel_size < 0) { - kernel_size = load_uimage_as(info->kernel_filename, &entry, NULL, + uint64_t loadaddr = info->loader_start + KERNEL_NOLOAD_ADDR; + kernel_size = load_uimage_as(info->kernel_filename, &entry, &loadaddr, &is_linux, NULL, NULL, as); } if (arm_feature(&cpu->env, ARM_FEATURE_AARCH64) && kernel_size < 0) { diff --git a/hw/core/loader.c b/hw/core/loader.c index fa41842280..c7182dfa64 100644 --- a/hw/core/loader.c +++ b/hw/core/loader.c @@ -613,13 +613,26 @@ static int load_uboot_image(const char *filename, hwaddr *ep, hwaddr *loadaddr, goto out; if (hdr->ih_type != image_type) { - fprintf(stderr, "Wrong image type %d, expected %d\n", hdr->ih_type, - image_type); - goto out; + if (!(image_type == IH_TYPE_KERNEL && + hdr->ih_type == IH_TYPE_KERNEL_NOLOAD)) { + fprintf(stderr, "Wrong image type %d, expected %d\n", hdr->ih_type, + image_type); + goto out; + } } /* TODO: Implement other image types. */ switch (hdr->ih_type) { + case IH_TYPE_KERNEL_NOLOAD: + if (!loadaddr || *loadaddr == LOAD_UIMAGE_LOADADDR_INVALID) { + fprintf(stderr, "this image format (kernel_noload) cannot be " + "loaded on this machine type"); + goto out; + } + + hdr->ih_load = *loadaddr + sizeof(*hdr); + hdr->ih_ep += hdr->ih_load; + /* fall through */ case IH_TYPE_KERNEL: address = hdr->ih_load; if (translate_fn) { diff --git a/hw/core/uboot_image.h b/hw/core/uboot_image.h index 34c11a70a6..608022de6e 100644 --- a/hw/core/uboot_image.h +++ b/hw/core/uboot_image.h @@ -124,6 +124,7 @@ #define IH_TYPE_SCRIPT 6 /* Script file */ #define IH_TYPE_FILESYSTEM 7 /* Filesystem Image (any type) */ #define IH_TYPE_FLATDT 8 /* Binary Flat Device Tree Blob */ +#define IH_TYPE_KERNEL_NOLOAD 14 /* OS Kernel Image (noload) */ /* * Compression Types diff --git a/hw/microblaze/boot.c b/hw/microblaze/boot.c index 35bfeda7aa..489ab839b7 100644 --- a/hw/microblaze/boot.c +++ b/hw/microblaze/boot.c @@ -156,7 +156,7 @@ void microblaze_load_kernel(MicroBlazeCPU *cpu, hwaddr ddr_base, /* If it wasn't an ELF image, try an u-boot image. */ if (kernel_size < 0) { - hwaddr uentry, loadaddr; + hwaddr uentry, loadaddr = LOAD_UIMAGE_LOADADDR_INVALID; kernel_size = load_uimage(kernel_filename, &uentry, &loadaddr, 0, NULL, NULL); diff --git a/hw/nios2/boot.c b/hw/nios2/boot.c index 4bb5b601d3..ed5cb28e94 100644 --- a/hw/nios2/boot.c +++ b/hw/nios2/boot.c @@ -161,7 +161,7 @@ void nios2_load_kernel(Nios2CPU *cpu, hwaddr ddr_base, /* If it wasn't an ELF image, try an u-boot image. */ if (kernel_size < 0) { - hwaddr uentry, loadaddr; + hwaddr uentry, loadaddr = LOAD_UIMAGE_LOADADDR_INVALID; kernel_size = load_uimage(kernel_filename, &uentry, &loadaddr, 0, NULL, NULL); diff --git a/hw/ppc/e500.c b/hw/ppc/e500.c index b20fea0dfc..0581e9e3d4 100644 --- a/hw/ppc/e500.c +++ b/hw/ppc/e500.c @@ -995,6 +995,7 @@ void ppce500_init(MachineState *machine) * Hrm. No ELF image? Try a uImage, maybe someone is giving us an * ePAPR compliant kernel */ + loadaddr = LOAD_UIMAGE_LOADADDR_INVALID; payload_size = load_uimage(filename, &bios_entry, &loadaddr, NULL, NULL, NULL); if (payload_size < 0) { diff --git a/hw/ppc/ppc440_bamboo.c b/hw/ppc/ppc440_bamboo.c index b8aa55d526..fc06191588 100644 --- a/hw/ppc/ppc440_bamboo.c +++ b/hw/ppc/ppc440_bamboo.c @@ -179,7 +179,7 @@ static void bamboo_init(MachineState *machine) CPUPPCState *env; uint64_t elf_entry; uint64_t elf_lowaddr; - hwaddr loadaddr = 0; + hwaddr loadaddr = LOAD_UIMAGE_LOADADDR_INVALID; target_long initrd_size = 0; DeviceState *dev; int success; diff --git a/hw/ppc/sam460ex.c b/hw/ppc/sam460ex.c index 4b051c0950..84ea592749 100644 --- a/hw/ppc/sam460ex.c +++ b/hw/ppc/sam460ex.c @@ -402,7 +402,7 @@ static void sam460ex_init(MachineState *machine) CPUPPCState *env; PPC4xxI2CState *i2c[2]; hwaddr entry = UBOOT_ENTRY; - hwaddr loadaddr = 0; + hwaddr loadaddr = LOAD_UIMAGE_LOADADDR_INVALID; target_long initrd_size = 0; DeviceState *dev; SysBusDevice *sbdev; diff --git a/include/hw/loader.h b/include/hw/loader.h index 0a0ad808ea..de8a29603b 100644 --- a/include/hw/loader.h +++ b/include/hw/loader.h @@ -175,10 +175,15 @@ void load_elf_hdr(const char *filename, void *hdr, bool *is64, Error **errp); int load_aout(const char *filename, hwaddr addr, int max_sz, int bswap_needed, hwaddr target_page_size); +#define LOAD_UIMAGE_LOADADDR_INVALID (-1) + /** load_uimage_as: * @filename: Path of uimage file * @ep: Populated with program entry point. Ignored if NULL. - * @loadaddr: Populated with the load address. Ignored if NULL. + * @loadaddr: load address if none specified in the image or when loading a + * ramdisk. Populated with the load address. Ignored if NULL or + * LOAD_UIMAGE_LOADADDR_INVALID (images which do not specify a load + * address will not be loadable). * @is_linux: Is set to true if the image loaded is Linux. Ignored if NULL. * @translate_fn: optional function to translate load addresses * @translate_opaque: opaque data passed to @translate_fn