diff --git a/qemu/target/arm/cpu.h b/qemu/target/arm/cpu.h index e127487c..ca1f2692 100644 --- a/qemu/target/arm/cpu.h +++ b/qemu/target/arm/cpu.h @@ -2023,6 +2023,18 @@ static inline CPUARMState *cpu_init(struct uc_struct *uc, const char *cpu_model) * for the accesses done as part of a stage 1 page table walk, rather than * having to walk the stage 2 page table over and over.) * + * R profile CPUs have an MPU, but can use the same set of MMU indexes + * as A profile. They only need to distinguish NS EL0 and NS EL1 (and + * NS EL2 if we ever model a Cortex-R52). + * + * M profile CPUs are rather different as they do not have a true MMU. + * They have the following different MMU indexes: + * User + * Privileged + * Execution priority negative (this is like privileged, but the + * MPU HFNMIENA bit means that it may have different access permission + * check results to normal privileged code, so can't share a TLB). + * * The ARMMMUIdx and the mmu index value used by the core QEMU TLB code * are not quite the same -- different CPU types (most notably M profile * vs A/R profile) would like to use MMU indexes with different semantics, @@ -2058,6 +2070,7 @@ typedef enum ARMMMUIdx { ARMMMUIdx_S2NS = 6 | ARM_MMU_IDX_A, ARMMMUIdx_MUser = 0 | ARM_MMU_IDX_M, ARMMMUIdx_MPriv = 1 | ARM_MMU_IDX_M, + ARMMMUIdx_MNegPri = 2 | ARM_MMU_IDX_M, /* Indexes below here don't have TLBs and are used only for AT system * instructions or for the first stage of an S12 page table walk. */ @@ -2078,6 +2091,7 @@ typedef enum ARMMMUIdxBit { ARMMMUIdxBit_S2NS = 1 << 6, ARMMMUIdxBit_MUser = 1 << 0, ARMMMUIdxBit_MPriv = 1 << 1, + ARMMMUIdxBit_MNegPri = 1 << 2, } ARMMMUIdxBit; #define MMU_USER_IDX 0 @@ -2109,7 +2123,7 @@ static inline int arm_mmu_idx_to_el(ARMMMUIdx mmu_idx) case ARM_MMU_IDX_A: return mmu_idx & 3; case ARM_MMU_IDX_M: - return mmu_idx & 1; + return mmu_idx == ARMMMUIdx_MUser ? 0 : 1; default: g_assert_not_reached(); } @@ -2123,6 +2137,14 @@ static inline int cpu_mmu_index(CPUARMState *env, bool ifetch) if (arm_feature(env, ARM_FEATURE_M)) { ARMMMUIdx mmu_idx = el == 0 ? ARMMMUIdx_MUser : ARMMMUIdx_MPriv; + /* Execution priority is negative if FAULTMASK is set or + * we're in a HardFault or NMI handler. + */ + if ((env->v7m.exception > 0 && env->v7m.exception <= 3) + || env->daif & PSTATE_F) { + return arm_to_core_mmu_idx(ARMMMUIdx_MNegPri); + } + return arm_to_core_mmu_idx(mmu_idx); } diff --git a/qemu/target/arm/helper.c b/qemu/target/arm/helper.c index c197d3f7..afc5b412 100644 --- a/qemu/target/arm/helper.c +++ b/qemu/target/arm/helper.c @@ -6187,6 +6187,7 @@ static inline uint32_t regime_el(CPUARMState *env, ARMMMUIdx mmu_idx) case ARMMMUIdx_S1NSE0: case ARMMMUIdx_S1NSE1: case ARMMMUIdx_MPriv: + case ARMMMUIdx_MNegPri: case ARMMMUIdx_MUser: return 1; default: @@ -6206,6 +6207,7 @@ static inline bool regime_is_secure(CPUARMState *env, ARMMMUIdx mmu_idx) case ARMMMUIdx_S1E2: case ARMMMUIdx_S2NS: case ARMMMUIdx_MPriv: + case ARMMMUIdx_MNegPri: case ARMMMUIdx_MUser: return false; case ARMMMUIdx_S1E3: @@ -6228,7 +6230,21 @@ static inline bool regime_translation_disabled(CPUARMState *env, ARMMMUIdx mmu_idx) { if (arm_feature(env, ARM_FEATURE_M)) { - return !(env->v7m.mpu_ctrl & R_V7M_MPU_CTRL_ENABLE_MASK); + switch (env->v7m.mpu_ctrl & + (R_V7M_MPU_CTRL_ENABLE_MASK | R_V7M_MPU_CTRL_HFNMIENA_MASK)) { + case R_V7M_MPU_CTRL_ENABLE_MASK: + /* Enabled, but not for HardFault and NMI */ + return mmu_idx == ARMMMUIdx_MNegPri; + case R_V7M_MPU_CTRL_ENABLE_MASK | R_V7M_MPU_CTRL_HFNMIENA_MASK: + /* Enabled for all cases */ + return false; + case 0: + default: + /* HFNMIENA set and ENABLE clear is UNPREDICTABLE, but + * we warned about that in armv7m_nvic.c when the guest set it. + */ + return true; + } } if (mmu_idx == ARMMMUIdx_S2NS) { diff --git a/qemu/target/arm/translate.c b/qemu/target/arm/translate.c index 3615d99c..75e38f4e 100644 --- a/qemu/target/arm/translate.c +++ b/qemu/target/arm/translate.c @@ -149,6 +149,7 @@ static inline int get_a32_user_mem_index(DisasContext *s) return arm_to_core_mmu_idx(ARMMMUIdx_S1SE0); case ARMMMUIdx_MUser: case ARMMMUIdx_MPriv: + case ARMMMUIdx_MNegPri: return arm_to_core_mmu_idx(ARMMMUIdx_MUser); case ARMMMUIdx_S2NS: default: