diff options
Diffstat (limited to 'target/arm/helper.c')
-rw-r--r-- | target/arm/helper.c | 315 |
1 files changed, 251 insertions, 64 deletions
diff --git a/target/arm/helper.c b/target/arm/helper.c index 37e7fd980e..329e5178d8 100644 --- a/target/arm/helper.c +++ b/target/arm/helper.c @@ -2385,7 +2385,7 @@ static uint64_t pmsav7_read(CPUARMState *env, const ARMCPRegInfo *ri) return 0; } - u32p += env->pmsav7.rnr; + u32p += env->pmsav7.rnr[M_REG_NS]; return *u32p; } @@ -2399,7 +2399,7 @@ static void pmsav7_write(CPUARMState *env, const ARMCPRegInfo *ri, return; } - u32p += env->pmsav7.rnr; + u32p += env->pmsav7.rnr[M_REG_NS]; tlb_flush(CPU(cpu)); /* Mappings may have changed - purge! */ *u32p = value; } @@ -2442,7 +2442,7 @@ static const ARMCPRegInfo pmsav7_cp_reginfo[] = { .resetfn = arm_cp_reset_ignore }, { .name = "RGNR", .cp = 15, .crn = 6, .opc1 = 0, .crm = 2, .opc2 = 0, .access = PL1_RW, - .fieldoffset = offsetof(CPUARMState, pmsav7.rnr), + .fieldoffset = offsetof(CPUARMState, pmsav7.rnr[M_REG_NS]), .writefn = pmsav7_rgnr_write, .resetfn = arm_cp_reset_ignore }, REGINFO_SENTINEL @@ -5870,6 +5870,12 @@ uint32_t HELPER(v7m_mrs)(CPUARMState *env, uint32_t reg) return 0; } +void HELPER(v7m_bxns)(CPUARMState *env, uint32_t dest) +{ + /* translate.c should never generate calls here in user-only mode */ + g_assert_not_reached(); +} + void switch_mode(CPUARMState *env, int mode) { ARMCPU *cpu = arm_env_get_cpu(env); @@ -6044,29 +6050,103 @@ static uint32_t v7m_pop(CPUARMState *env) return val; } +/* Return true if we're using the process stack pointer (not the MSP) */ +static bool v7m_using_psp(CPUARMState *env) +{ + /* Handler mode always uses the main stack; for thread mode + * the CONTROL.SPSEL bit determines the answer. + * Note that in v7M it is not possible to be in Handler mode with + * CONTROL.SPSEL non-zero, but in v8M it is, so we must check both. + */ + return !arm_v7m_is_handler_mode(env) && + env->v7m.control[env->v7m.secure] & R_V7M_CONTROL_SPSEL_MASK; +} + /* Switch to V7M main or process stack pointer. */ static void switch_v7m_sp(CPUARMState *env, bool new_spsel) { uint32_t tmp; - bool old_spsel = env->v7m.control & R_V7M_CONTROL_SPSEL_MASK; + uint32_t old_control = env->v7m.control[env->v7m.secure]; + bool old_spsel = old_control & R_V7M_CONTROL_SPSEL_MASK; if (old_spsel != new_spsel) { tmp = env->v7m.other_sp; env->v7m.other_sp = env->regs[13]; env->regs[13] = tmp; - env->v7m.control = deposit32(env->v7m.control, + env->v7m.control[env->v7m.secure] = deposit32(old_control, R_V7M_CONTROL_SPSEL_SHIFT, R_V7M_CONTROL_SPSEL_LENGTH, new_spsel); } } +/* Switch M profile security state between NS and S */ +static void switch_v7m_security_state(CPUARMState *env, bool new_secstate) +{ + uint32_t new_ss_msp, new_ss_psp; + + if (env->v7m.secure == new_secstate) { + return; + } + + /* All the banked state is accessed by looking at env->v7m.secure + * except for the stack pointer; rearrange the SP appropriately. + */ + new_ss_msp = env->v7m.other_ss_msp; + new_ss_psp = env->v7m.other_ss_psp; + + if (v7m_using_psp(env)) { + env->v7m.other_ss_psp = env->regs[13]; + env->v7m.other_ss_msp = env->v7m.other_sp; + } else { + env->v7m.other_ss_msp = env->regs[13]; + env->v7m.other_ss_psp = env->v7m.other_sp; + } + + env->v7m.secure = new_secstate; + + if (v7m_using_psp(env)) { + env->regs[13] = new_ss_psp; + env->v7m.other_sp = new_ss_msp; + } else { + env->regs[13] = new_ss_msp; + env->v7m.other_sp = new_ss_psp; + } +} + +void HELPER(v7m_bxns)(CPUARMState *env, uint32_t dest) +{ + /* Handle v7M BXNS: + * - if the return value is a magic value, do exception return (like BX) + * - otherwise bit 0 of the return value is the target security state + */ + if (dest >= 0xff000000) { + /* This is an exception return magic value; put it where + * do_v7m_exception_exit() expects and raise EXCEPTION_EXIT. + * Note that if we ever add gen_ss_advance() singlestep support to + * M profile this should count as an "instruction execution complete" + * event (compare gen_bx_excret_final_code()). + */ + env->regs[15] = dest & ~1; + env->thumb = dest & 1; + HELPER(exception_internal)(env, EXCP_EXCEPTION_EXIT); + /* notreached */ + } + + /* translate.c should have made BXNS UNDEF unless we're secure */ + assert(env->v7m.secure); + + switch_v7m_security_state(env, dest & 1); + env->thumb = 1; + env->regs[15] = dest & ~1; +} + static uint32_t arm_v7m_load_vector(ARMCPU *cpu) { CPUState *cs = CPU(cpu); CPUARMState *env = &cpu->env; MemTxResult result; - hwaddr vec = env->v7m.vecbase + env->v7m.exception * 4; + hwaddr vec = env->v7m.vecbase[env->v7m.secure] + env->v7m.exception * 4; uint32_t addr; addr = address_space_ldl(cs->as, vec, @@ -6112,7 +6192,8 @@ static void v7m_push_stack(ARMCPU *cpu) uint32_t xpsr = xpsr_read(env); /* Align stack pointer if the guest wants that */ - if ((env->regs[13] & 4) && (env->v7m.ccr & R_V7M_CCR_STKALIGN_MASK)) { + if ((env->regs[13] & 4) && + (env->v7m.ccr[env->v7m.secure] & R_V7M_CCR_STKALIGN_MASK)) { env->regs[13] -= 4; xpsr |= XPSR_SPREALIGN; } @@ -6166,8 +6247,20 @@ static void do_v7m_exception_exit(ARMCPU *cpu) } if (env->v7m.exception != ARMV7M_EXCP_NMI) { - /* Auto-clear FAULTMASK on return from other than NMI */ - env->v7m.faultmask = 0; + /* Auto-clear FAULTMASK on return from other than NMI. + * If the security extension is implemented then this only + * happens if the raw execution priority is >= 0; the + * value of the ES bit in the exception return value indicates + * which security state's faultmask to clear. (v8M ARM ARM R_KBNF.) + */ + if (arm_feature(env, ARM_FEATURE_M_SECURITY)) { + int es = type & 1; + if (armv7m_nvic_raw_execution_priority(env->nvic) >= 0) { + env->v7m.faultmask[es] = 0; + } + } else { + env->v7m.faultmask[M_REG_NS] = 0; + } } switch (armv7m_nvic_complete_irq(env->nvic, env->v7m.exception)) { @@ -6198,7 +6291,7 @@ static void do_v7m_exception_exit(ARMCPU *cpu) /* fall through */ case 9: /* Return to Thread using Main stack */ if (!rettobase && - !(env->v7m.ccr & R_V7M_CCR_NONBASETHRDENA_MASK)) { + !(env->v7m.ccr[env->v7m.secure] & R_V7M_CCR_NONBASETHRDENA_MASK)) { ufault = true; } break; @@ -6210,7 +6303,7 @@ static void do_v7m_exception_exit(ARMCPU *cpu) /* Bad exception return: instead of popping the exception * stack, directly take a usage fault on the current stack. */ - env->v7m.cfsr |= R_V7M_CFSR_INVPC_MASK; + env->v7m.cfsr[env->v7m.secure] |= R_V7M_CFSR_INVPC_MASK; armv7m_nvic_set_pending(env->nvic, ARMV7M_EXCP_USAGE); v7m_exception_taken(cpu, type | 0xf0000000); qemu_log_mask(CPU_LOG_INT, "...taking UsageFault on existing " @@ -6252,7 +6345,7 @@ static void do_v7m_exception_exit(ARMCPU *cpu) if (return_to_handler != arm_v7m_is_handler_mode(env)) { /* Take an INVPC UsageFault by pushing the stack again. */ armv7m_nvic_set_pending(env->nvic, ARMV7M_EXCP_USAGE); - env->v7m.cfsr |= R_V7M_CFSR_INVPC_MASK; + env->v7m.cfsr[env->v7m.secure] |= R_V7M_CFSR_INVPC_MASK; v7m_push_stack(cpu); v7m_exception_taken(cpu, type | 0xf0000000); qemu_log_mask(CPU_LOG_INT, "...taking UsageFault on new stackframe: " @@ -6311,15 +6404,15 @@ void arm_v7m_cpu_do_interrupt(CPUState *cs) switch (cs->exception_index) { case EXCP_UDEF: armv7m_nvic_set_pending(env->nvic, ARMV7M_EXCP_USAGE); - env->v7m.cfsr |= R_V7M_CFSR_UNDEFINSTR_MASK; + env->v7m.cfsr[env->v7m.secure] |= R_V7M_CFSR_UNDEFINSTR_MASK; break; case EXCP_NOCP: armv7m_nvic_set_pending(env->nvic, ARMV7M_EXCP_USAGE); - env->v7m.cfsr |= R_V7M_CFSR_NOCP_MASK; + env->v7m.cfsr[env->v7m.secure] |= R_V7M_CFSR_NOCP_MASK; break; case EXCP_INVSTATE: armv7m_nvic_set_pending(env->nvic, ARMV7M_EXCP_USAGE); - env->v7m.cfsr |= R_V7M_CFSR_INVSTATE_MASK; + env->v7m.cfsr[env->v7m.secure] |= R_V7M_CFSR_INVSTATE_MASK; break; case EXCP_SWI: /* The PC already points to the next instruction. */ @@ -6335,11 +6428,11 @@ void arm_v7m_cpu_do_interrupt(CPUState *cs) case 0x8: /* External Abort */ switch (cs->exception_index) { case EXCP_PREFETCH_ABORT: - env->v7m.cfsr |= R_V7M_CFSR_PRECISERR_MASK; + env->v7m.cfsr[M_REG_NS] |= R_V7M_CFSR_PRECISERR_MASK; qemu_log_mask(CPU_LOG_INT, "...with CFSR.PRECISERR\n"); break; case EXCP_DATA_ABORT: - env->v7m.cfsr |= + env->v7m.cfsr[M_REG_NS] |= (R_V7M_CFSR_IBUSERR_MASK | R_V7M_CFSR_BFARVALID_MASK); env->v7m.bfar = env->exception.vaddress; qemu_log_mask(CPU_LOG_INT, @@ -6355,16 +6448,16 @@ void arm_v7m_cpu_do_interrupt(CPUState *cs) */ switch (cs->exception_index) { case EXCP_PREFETCH_ABORT: - env->v7m.cfsr |= R_V7M_CFSR_IACCVIOL_MASK; + env->v7m.cfsr[env->v7m.secure] |= R_V7M_CFSR_IACCVIOL_MASK; qemu_log_mask(CPU_LOG_INT, "...with CFSR.IACCVIOL\n"); break; case EXCP_DATA_ABORT: - env->v7m.cfsr |= + env->v7m.cfsr[env->v7m.secure] |= (R_V7M_CFSR_DACCVIOL_MASK | R_V7M_CFSR_MMARVALID_MASK); - env->v7m.mmfar = env->exception.vaddress; + env->v7m.mmfar[env->v7m.secure] = env->exception.vaddress; qemu_log_mask(CPU_LOG_INT, "...with CFSR.DACCVIOL and MMFAR 0x%x\n", - env->v7m.mmfar); + env->v7m.mmfar[env->v7m.secure]); break; } armv7m_nvic_set_pending(env->nvic, ARMV7M_EXCP_MEM); @@ -6397,7 +6490,7 @@ void arm_v7m_cpu_do_interrupt(CPUState *cs) } lr = 0xfffffff1; - if (env->v7m.control & R_V7M_CONTROL_SPSEL_MASK) { + if (env->v7m.control[env->v7m.secure] & R_V7M_CONTROL_SPSEL_MASK) { lr |= 4; } if (!arm_v7m_is_handler_mode(env)) { @@ -7032,35 +7125,15 @@ static inline uint32_t regime_el(CPUARMState *env, ARMMMUIdx mmu_idx) case ARMMMUIdx_MPriv: case ARMMMUIdx_MNegPri: case ARMMMUIdx_MUser: + case ARMMMUIdx_MSPriv: + case ARMMMUIdx_MSNegPri: + case ARMMMUIdx_MSUser: return 1; default: g_assert_not_reached(); } } -/* Return true if this address translation regime is secure */ -static inline bool regime_is_secure(CPUARMState *env, ARMMMUIdx mmu_idx) -{ - switch (mmu_idx) { - case ARMMMUIdx_S12NSE0: - case ARMMMUIdx_S12NSE1: - case ARMMMUIdx_S1NSE0: - case ARMMMUIdx_S1NSE1: - case ARMMMUIdx_S1E2: - case ARMMMUIdx_S2NS: - case ARMMMUIdx_MPriv: - case ARMMMUIdx_MNegPri: - case ARMMMUIdx_MUser: - return false; - case ARMMMUIdx_S1E3: - case ARMMMUIdx_S1SE0: - case ARMMMUIdx_S1SE1: - return true; - default: - g_assert_not_reached(); - } -} - /* Return the SCTLR value which controls this address translation regime */ static inline uint32_t regime_sctlr(CPUARMState *env, ARMMMUIdx mmu_idx) { @@ -7072,11 +7145,12 @@ static inline bool regime_translation_disabled(CPUARMState *env, ARMMMUIdx mmu_idx) { if (arm_feature(env, ARM_FEATURE_M)) { - switch (env->v7m.mpu_ctrl & + switch (env->v7m.mpu_ctrl[regime_is_secure(env, mmu_idx)] & (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; + return mmu_idx == ARMMMUIdx_MNegPri || + mmu_idx == ARMMMUIdx_MSNegPri; case R_V7M_MPU_CTRL_ENABLE_MASK | R_V7M_MPU_CTRL_HFNMIENA_MASK: /* Enabled for all cases */ return false; @@ -8231,7 +8305,8 @@ static bool pmsav7_use_background_region(ARMCPU *cpu, } if (arm_feature(env, ARM_FEATURE_M)) { - return env->v7m.mpu_ctrl & R_V7M_MPU_CTRL_PRIVDEFENA_MASK; + return env->v7m.mpu_ctrl[regime_is_secure(env, mmu_idx)] + & R_V7M_MPU_CTRL_PRIVDEFENA_MASK; } else { return regime_sctlr(env, mmu_idx) & SCTLR_BR; } @@ -8411,6 +8486,112 @@ static bool get_phys_addr_pmsav7(CPUARMState *env, uint32_t address, return !(*prot & (1 << access_type)); } +static bool get_phys_addr_pmsav8(CPUARMState *env, uint32_t address, + MMUAccessType access_type, ARMMMUIdx mmu_idx, + hwaddr *phys_ptr, int *prot, uint32_t *fsr) +{ + ARMCPU *cpu = arm_env_get_cpu(env); + bool is_user = regime_is_user(env, mmu_idx); + uint32_t secure = regime_is_secure(env, mmu_idx); + int n; + int matchregion = -1; + bool hit = false; + + *phys_ptr = address; + *prot = 0; + + /* Unlike the ARM ARM pseudocode, we don't need to check whether this + * was an exception vector read from the vector table (which is always + * done using the default system address map), because those accesses + * are done in arm_v7m_load_vector(), which always does a direct + * read using address_space_ldl(), rather than going via this function. + */ + if (regime_translation_disabled(env, mmu_idx)) { /* MPU disabled */ + hit = true; + } else if (m_is_ppb_region(env, address)) { + hit = true; + } else if (pmsav7_use_background_region(cpu, mmu_idx, is_user)) { + hit = true; + } else { + for (n = (int)cpu->pmsav7_dregion - 1; n >= 0; n--) { + /* region search */ + /* Note that the base address is bits [31:5] from the register + * with bits [4:0] all zeroes, but the limit address is bits + * [31:5] from the register with bits [4:0] all ones. + */ + uint32_t base = env->pmsav8.rbar[secure][n] & ~0x1f; + uint32_t limit = env->pmsav8.rlar[secure][n] | 0x1f; + + if (!(env->pmsav8.rlar[secure][n] & 0x1)) { + /* Region disabled */ + continue; + } + + if (address < base || address > limit) { + continue; + } + + if (hit) { + /* Multiple regions match -- always a failure (unlike + * PMSAv7 where highest-numbered-region wins) + */ + *fsr = 0x00d; /* permission fault */ + return true; + } + + matchregion = n; + hit = true; + + if (base & ~TARGET_PAGE_MASK) { + qemu_log_mask(LOG_UNIMP, + "MPU_RBAR[%d]: No support for MPU region base" + "address of 0x%" PRIx32 ". Minimum alignment is " + "%d\n", + n, base, TARGET_PAGE_BITS); + continue; + } + if ((limit + 1) & ~TARGET_PAGE_MASK) { + qemu_log_mask(LOG_UNIMP, + "MPU_RBAR[%d]: No support for MPU region limit" + "address of 0x%" PRIx32 ". Minimum alignment is " + "%d\n", + n, limit, TARGET_PAGE_BITS); + continue; + } + } + } + + if (!hit) { + /* background fault */ + *fsr = 0; + return true; + } + + if (matchregion == -1) { + /* hit using the background region */ + get_phys_addr_pmsav7_default(env, mmu_idx, address, prot); + } else { + uint32_t ap = extract32(env->pmsav8.rbar[secure][matchregion], 1, 2); + uint32_t xn = extract32(env->pmsav8.rbar[secure][matchregion], 0, 1); + + if (m_is_system_region(env, address)) { + /* System space is always execute never */ + xn = 1; + } + + *prot = simple_ap_to_rw_prot(env, mmu_idx, ap); + if (*prot && !xn) { + *prot |= PAGE_EXEC; + } + /* We don't need to look the attribute up in the MAIR0/MAIR1 + * registers because that only tells us about cacheability. + */ + } + + *fsr = 0x00d; /* Permission fault */ + return !(*prot & (1 << access_type)); +} + static bool get_phys_addr_pmsav5(CPUARMState *env, uint32_t address, MMUAccessType access_type, ARMMMUIdx mmu_idx, hwaddr *phys_ptr, int *prot, uint32_t *fsr) @@ -8580,7 +8761,11 @@ static bool get_phys_addr(CPUARMState *env, target_ulong address, bool ret; *page_size = TARGET_PAGE_SIZE; - if (arm_feature(env, ARM_FEATURE_V7)) { + if (arm_feature(env, ARM_FEATURE_V8)) { + /* PMSAv8 */ + ret = get_phys_addr_pmsav8(env, address, access_type, mmu_idx, + phys_ptr, prot, fsr); + } else if (arm_feature(env, ARM_FEATURE_V7)) { /* PMSAv7 */ ret = get_phys_addr_pmsav7(env, address, access_type, mmu_idx, phys_ptr, prot, fsr); @@ -8699,7 +8884,7 @@ uint32_t HELPER(v7m_mrs)(CPUARMState *env, uint32_t reg) return xpsr_read(env) & mask; break; case 20: /* CONTROL */ - return env->v7m.control; + return env->v7m.control[env->v7m.secure]; } if (el == 0) { @@ -8708,18 +8893,18 @@ uint32_t HELPER(v7m_mrs)(CPUARMState *env, uint32_t reg) switch (reg) { case 8: /* MSP */ - return (env->v7m.control & R_V7M_CONTROL_SPSEL_MASK) ? + return (env->v7m.control[env->v7m.secure] & R_V7M_CONTROL_SPSEL_MASK) ? env->v7m.other_sp : env->regs[13]; case 9: /* PSP */ - return (env->v7m.control & R_V7M_CONTROL_SPSEL_MASK) ? + return (env->v7m.control[env->v7m.secure] & R_V7M_CONTROL_SPSEL_MASK) ? env->regs[13] : env->v7m.other_sp; case 16: /* PRIMASK */ - return env->v7m.primask; + return env->v7m.primask[env->v7m.secure]; case 17: /* BASEPRI */ case 18: /* BASEPRI_MAX */ - return env->v7m.basepri; + return env->v7m.basepri[env->v7m.secure]; case 19: /* FAULTMASK */ - return env->v7m.faultmask; + return env->v7m.faultmask[env->v7m.secure]; default: qemu_log_mask(LOG_GUEST_ERROR, "Attempt to read unknown special" " register %d\n", reg); @@ -8760,32 +8945,34 @@ void HELPER(v7m_msr)(CPUARMState *env, uint32_t maskreg, uint32_t val) } break; case 8: /* MSP */ - if (env->v7m.control & R_V7M_CONTROL_SPSEL_MASK) { + if (env->v7m.control[env->v7m.secure] & R_V7M_CONTROL_SPSEL_MASK) { env->v7m.other_sp = val; } else { env->regs[13] = val; } break; case 9: /* PSP */ - if (env->v7m.control & R_V7M_CONTROL_SPSEL_MASK) { + if (env->v7m.control[env->v7m.secure] & R_V7M_CONTROL_SPSEL_MASK) { env->regs[13] = val; } else { env->v7m.other_sp = val; } break; case 16: /* PRIMASK */ - env->v7m.primask = val & 1; + env->v7m.primask[env->v7m.secure] = val & 1; break; case 17: /* BASEPRI */ - env->v7m.basepri = val & 0xff; + env->v7m.basepri[env->v7m.secure] = val & 0xff; break; case 18: /* BASEPRI_MAX */ val &= 0xff; - if (val != 0 && (val < env->v7m.basepri || env->v7m.basepri == 0)) - env->v7m.basepri = val; + if (val != 0 && (val < env->v7m.basepri[env->v7m.secure] + || env->v7m.basepri[env->v7m.secure] == 0)) { + env->v7m.basepri[env->v7m.secure] = val; + } break; case 19: /* FAULTMASK */ - env->v7m.faultmask = val & 1; + env->v7m.faultmask[env->v7m.secure] = val & 1; break; case 20: /* CONTROL */ /* Writing to the SPSEL bit only has an effect if we are in @@ -8796,8 +8983,8 @@ void HELPER(v7m_msr)(CPUARMState *env, uint32_t maskreg, uint32_t val) if (!arm_v7m_is_handler_mode(env)) { switch_v7m_sp(env, (val & R_V7M_CONTROL_SPSEL_MASK) != 0); } - env->v7m.control &= ~R_V7M_CONTROL_NPRIV_MASK; - env->v7m.control |= val & R_V7M_CONTROL_NPRIV_MASK; + env->v7m.control[env->v7m.secure] &= ~R_V7M_CONTROL_NPRIV_MASK; + env->v7m.control[env->v7m.secure] |= val & R_V7M_CONTROL_NPRIV_MASK; break; default: qemu_log_mask(LOG_GUEST_ERROR, "Attempt to write unknown special" |