Philippe Mathieu-Daudé <phi...@redhat.com> writes:
> From: Samuel Ortiz <sa...@linux.intel.com> > > In preparation for supporting TCG disablement on ARM, we move most > of TCG related v7m helpers and APIs into their own file. > > Signed-off-by: Samuel Ortiz <sa...@linux.intel.com> > [PMD: Patch rewritten] > Signed-off-by: Philippe Mathieu-Daudé <phi...@redhat.com> > --- > Is there a way to not use $CONFIG_USER_ONLY? Is this because the CONFIG_ARM_V7M symbol only appears for softmmu targets but we still want vXm -cpu's for user mode? > > target/arm/Makefile.objs | 1 + > target/arm/helper.c | 633 ------------------------------------- > target/arm/v7m_helper.c | 654 +++++++++++++++++++++++++++++++++++++++ > 3 files changed, 655 insertions(+), 633 deletions(-) > create mode 100644 target/arm/v7m_helper.c > > diff --git a/target/arm/Makefile.objs b/target/arm/Makefile.objs > index 72b42f825f..5f3f965cc6 100644 > --- a/target/arm/Makefile.objs > +++ b/target/arm/Makefile.objs > @@ -35,6 +35,7 @@ obj-y += translate.o op_helper.o > obj-y += crypto_helper.o > obj-y += iwmmxt_helper.o vec_helper.o > obj-y += neon_helper.o vfp_helper.o > +obj-$(call lor,$(CONFIG_USER_ONLY),$(CONFIG_ARM_V7M)) += v7m_helper.o > > obj-$(TARGET_AARCH64) += translate-a64.o helper-a64.o > obj-$(TARGET_AARCH64) += translate-sve.o sve_helper.o > diff --git a/target/arm/helper.c b/target/arm/helper.c > index a1e74cc471..a829086c6d 100644 > --- a/target/arm/helper.c > +++ b/target/arm/helper.c > @@ -20,7 +20,6 @@ > #include "qemu/crc32c.h" > #include "qemu/qemu-print.h" > #include "exec/exec-all.h" > -#include "exec/cpu_ldst.h" > #include "arm_ldst.h" > #include <zlib.h> /* For crc32 */ > #include "hw/semihosting/semihost.h" > @@ -7456,75 +7455,6 @@ uint32_t HELPER(rbit)(uint32_t x) > > #ifdef CONFIG_USER_ONLY > > -/* These should probably raise undefined insn exceptions. */ > -void HELPER(v7m_msr)(CPUARMState *env, uint32_t reg, uint32_t val) > -{ > - ARMCPU *cpu = env_archcpu(env); > - > - cpu_abort(CPU(cpu), "v7m_msr %d\n", reg); > -} > - > -uint32_t HELPER(v7m_mrs)(CPUARMState *env, uint32_t reg) > -{ > - ARMCPU *cpu = env_archcpu(env); > - > - cpu_abort(CPU(cpu), "v7m_mrs %d\n", 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 HELPER(v7m_blxns)(CPUARMState *env, uint32_t dest) > -{ > - /* translate.c should never generate calls here in user-only mode */ > - g_assert_not_reached(); > -} > - > -void HELPER(v7m_preserve_fp_state)(CPUARMState *env) > -{ > - /* translate.c should never generate calls here in user-only mode */ > - g_assert_not_reached(); > -} > - > -void HELPER(v7m_vlstm)(CPUARMState *env, uint32_t fptr) > -{ > - /* translate.c should never generate calls here in user-only mode */ > - g_assert_not_reached(); > -} > - > -void HELPER(v7m_vlldm)(CPUARMState *env, uint32_t fptr) > -{ > - /* translate.c should never generate calls here in user-only mode */ > - g_assert_not_reached(); > -} > - > -uint32_t HELPER(v7m_tt)(CPUARMState *env, uint32_t addr, uint32_t op) > -{ > - /* > - * The TT instructions can be used by unprivileged code, but in > - * user-only emulation we don't have the MPU. > - * Luckily since we know we are NonSecure unprivileged (and that in > - * turn means that the A flag wasn't specified), all the bits in the > - * register must be zero: > - * IREGION: 0 because IRVALID is 0 > - * IRVALID: 0 because NS > - * S: 0 because NS > - * NSRW: 0 because NS > - * NSR: 0 because NS > - * RW: 0 because unpriv and A flag not set > - * R: 0 because unpriv and A flag not set > - * SRVALID: 0 because NS > - * MRVALID: 0 because unpriv and A flag not set > - * SREGION: 0 becaus SRVALID is 0 > - * MREGION: 0 because MRVALID is 0 > - */ > - return 0; > -} > - > void switch_mode(CPUARMState *env, int mode) > { > ARMCPU *cpu = env_archcpu(env); > @@ -8048,109 +7978,6 @@ void switch_v7m_security_state(CPUARMState *env, bool > new_secstate) > } > } > > -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 > - */ > - uint32_t min_magic; > - > - if (arm_feature(env, ARM_FEATURE_M_SECURITY)) { > - /* Covers FNC_RETURN and EXC_RETURN magic */ > - min_magic = FNC_RETURN_MIN_MAGIC; > - } else { > - /* EXC_RETURN magic only */ > - min_magic = EXC_RETURN_MIN_MAGIC; > - } > - > - if (dest >= min_magic) { > - /* > - * 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); > - > - if (!(dest & 1)) { > - env->v7m.control[M_REG_S] &= ~R_V7M_CONTROL_SFPA_MASK; > - } > - switch_v7m_security_state(env, dest & 1); > - env->thumb = 1; > - env->regs[15] = dest & ~1; > -} > - > -void HELPER(v7m_blxns)(CPUARMState *env, uint32_t dest) > -{ > - /* > - * Handle v7M BLXNS: > - * - bit 0 of the destination address is the target security state > - */ > - > - /* At this point regs[15] is the address just after the BLXNS */ > - uint32_t nextinst = env->regs[15] | 1; > - uint32_t sp = env->regs[13] - 8; > - uint32_t saved_psr; > - > - /* translate.c will have made BLXNS UNDEF unless we're secure */ > - assert(env->v7m.secure); > - > - if (dest & 1) { > - /* > - * Target is Secure, so this is just a normal BLX, > - * except that the low bit doesn't indicate Thumb/not. > - */ > - env->regs[14] = nextinst; > - env->thumb = 1; > - env->regs[15] = dest & ~1; > - return; > - } > - > - /* Target is non-secure: first push a stack frame */ > - if (!QEMU_IS_ALIGNED(sp, 8)) { > - qemu_log_mask(LOG_GUEST_ERROR, > - "BLXNS with misaligned SP is UNPREDICTABLE\n"); > - } > - > - if (sp < v7m_sp_limit(env)) { > - raise_exception(env, EXCP_STKOF, 0, 1); > - } > - > - saved_psr = env->v7m.exception; > - if (env->v7m.control[M_REG_S] & R_V7M_CONTROL_SFPA_MASK) { > - saved_psr |= XPSR_SFPA; > - } > - > - /* Note that these stores can throw exceptions on MPU faults */ > - cpu_stl_data(env, sp, nextinst); > - cpu_stl_data(env, sp + 4, saved_psr); > - > - env->regs[13] = sp; > - env->regs[14] = 0xfeffffff; > - if (arm_v7m_is_handler_mode(env)) { > - /* > - * Write a dummy value to IPSR, to avoid leaking the current secure > - * exception number to non-secure code. This is guaranteed not > - * to cause write_v7m_exception() to actually change stacks. > - */ > - write_v7m_exception(env, 1); > - } > - env->v7m.control[M_REG_S] &= ~R_V7M_CONTROL_SFPA_MASK; > - switch_v7m_security_state(env, 0); > - env->thumb = 1; > - env->regs[15] = dest; > -} > - > static uint32_t *get_v7m_sp_ptr(CPUARMState *env, bool secure, bool > threadmode, > bool spsel) > { > @@ -12760,466 +12587,6 @@ hwaddr arm_cpu_get_phys_page_attrs_debug(CPUState > *cs, vaddr addr, > return phys_addr; > } > > -uint32_t HELPER(v7m_mrs)(CPUARMState *env, uint32_t reg) > -{ > - uint32_t mask; > - unsigned el = arm_current_el(env); > - > - /* First handle registers which unprivileged can read */ > - > - switch (reg) { > - case 0 ... 7: /* xPSR sub-fields */ > - mask = 0; > - if ((reg & 1) && el) { > - mask |= XPSR_EXCP; /* IPSR (unpriv. reads as zero) */ > - } > - if (!(reg & 4)) { > - mask |= XPSR_NZCV | XPSR_Q; /* APSR */ > - if (arm_feature(env, ARM_FEATURE_THUMB_DSP)) { > - mask |= XPSR_GE; > - } > - } > - /* EPSR reads as zero */ > - return xpsr_read(env) & mask; > - break; > - case 20: /* CONTROL */ > - { > - uint32_t value = env->v7m.control[env->v7m.secure]; > - if (!env->v7m.secure) { > - /* SFPA is RAZ/WI from NS; FPCA is stored in the M_REG_S bank */ > - value |= env->v7m.control[M_REG_S] & R_V7M_CONTROL_FPCA_MASK; > - } > - return value; > - } > - case 0x94: /* CONTROL_NS */ > - /* > - * We have to handle this here because unprivileged Secure code > - * can read the NS CONTROL register. > - */ > - if (!env->v7m.secure) { > - return 0; > - } > - return env->v7m.control[M_REG_NS] | > - (env->v7m.control[M_REG_S] & R_V7M_CONTROL_FPCA_MASK); > - } > - > - if (el == 0) { > - return 0; /* unprivileged reads others as zero */ > - } > - > - if (arm_feature(env, ARM_FEATURE_M_SECURITY)) { > - switch (reg) { > - case 0x88: /* MSP_NS */ > - if (!env->v7m.secure) { > - return 0; > - } > - return env->v7m.other_ss_msp; > - case 0x89: /* PSP_NS */ > - if (!env->v7m.secure) { > - return 0; > - } > - return env->v7m.other_ss_psp; > - case 0x8a: /* MSPLIM_NS */ > - if (!env->v7m.secure) { > - return 0; > - } > - return env->v7m.msplim[M_REG_NS]; > - case 0x8b: /* PSPLIM_NS */ > - if (!env->v7m.secure) { > - return 0; > - } > - return env->v7m.psplim[M_REG_NS]; > - case 0x90: /* PRIMASK_NS */ > - if (!env->v7m.secure) { > - return 0; > - } > - return env->v7m.primask[M_REG_NS]; > - case 0x91: /* BASEPRI_NS */ > - if (!env->v7m.secure) { > - return 0; > - } > - return env->v7m.basepri[M_REG_NS]; > - case 0x93: /* FAULTMASK_NS */ > - if (!env->v7m.secure) { > - return 0; > - } > - return env->v7m.faultmask[M_REG_NS]; > - case 0x98: /* SP_NS */ > - { > - /* > - * This gives the non-secure SP selected based on whether we're > - * currently in handler mode or not, using the NS CONTROL.SPSEL. > - */ > - bool spsel = env->v7m.control[M_REG_NS] & > R_V7M_CONTROL_SPSEL_MASK; > - > - if (!env->v7m.secure) { > - return 0; > - } > - if (!arm_v7m_is_handler_mode(env) && spsel) { > - return env->v7m.other_ss_psp; > - } else { > - return env->v7m.other_ss_msp; > - } > - } > - default: > - break; > - } > - } > - > - switch (reg) { > - case 8: /* MSP */ > - return v7m_using_psp(env) ? env->v7m.other_sp : env->regs[13]; > - case 9: /* PSP */ > - return v7m_using_psp(env) ? env->regs[13] : env->v7m.other_sp; > - case 10: /* MSPLIM */ > - if (!arm_feature(env, ARM_FEATURE_V8)) { > - goto bad_reg; > - } > - return env->v7m.msplim[env->v7m.secure]; > - case 11: /* PSPLIM */ > - if (!arm_feature(env, ARM_FEATURE_V8)) { > - goto bad_reg; > - } > - return env->v7m.psplim[env->v7m.secure]; > - case 16: /* PRIMASK */ > - return env->v7m.primask[env->v7m.secure]; > - case 17: /* BASEPRI */ > - case 18: /* BASEPRI_MAX */ > - return env->v7m.basepri[env->v7m.secure]; > - case 19: /* FAULTMASK */ > - return env->v7m.faultmask[env->v7m.secure]; > - default: > - bad_reg: > - qemu_log_mask(LOG_GUEST_ERROR, "Attempt to read unknown special" > - " register %d\n", reg); > - return 0; > - } > -} > - > -void HELPER(v7m_msr)(CPUARMState *env, uint32_t maskreg, uint32_t val) > -{ > - /* > - * We're passed bits [11..0] of the instruction; extract > - * SYSm and the mask bits. > - * Invalid combinations of SYSm and mask are UNPREDICTABLE; > - * we choose to treat them as if the mask bits were valid. > - * NB that the pseudocode 'mask' variable is bits [11..10], > - * whereas ours is [11..8]. > - */ > - uint32_t mask = extract32(maskreg, 8, 4); > - uint32_t reg = extract32(maskreg, 0, 8); > - int cur_el = arm_current_el(env); > - > - if (cur_el == 0 && reg > 7 && reg != 20) { > - /* > - * only xPSR sub-fields and CONTROL.SFPA may be written by > - * unprivileged code > - */ > - return; > - } > - > - if (arm_feature(env, ARM_FEATURE_M_SECURITY)) { > - switch (reg) { > - case 0x88: /* MSP_NS */ > - if (!env->v7m.secure) { > - return; > - } > - env->v7m.other_ss_msp = val; > - return; > - case 0x89: /* PSP_NS */ > - if (!env->v7m.secure) { > - return; > - } > - env->v7m.other_ss_psp = val; > - return; > - case 0x8a: /* MSPLIM_NS */ > - if (!env->v7m.secure) { > - return; > - } > - env->v7m.msplim[M_REG_NS] = val & ~7; > - return; > - case 0x8b: /* PSPLIM_NS */ > - if (!env->v7m.secure) { > - return; > - } > - env->v7m.psplim[M_REG_NS] = val & ~7; > - return; > - case 0x90: /* PRIMASK_NS */ > - if (!env->v7m.secure) { > - return; > - } > - env->v7m.primask[M_REG_NS] = val & 1; > - return; > - case 0x91: /* BASEPRI_NS */ > - if (!env->v7m.secure || !arm_feature(env, ARM_FEATURE_M_MAIN)) { > - return; > - } > - env->v7m.basepri[M_REG_NS] = val & 0xff; > - return; > - case 0x93: /* FAULTMASK_NS */ > - if (!env->v7m.secure || !arm_feature(env, ARM_FEATURE_M_MAIN)) { > - return; > - } > - env->v7m.faultmask[M_REG_NS] = val & 1; > - return; > - case 0x94: /* CONTROL_NS */ > - if (!env->v7m.secure) { > - return; > - } > - write_v7m_control_spsel_for_secstate(env, > - val & > R_V7M_CONTROL_SPSEL_MASK, > - M_REG_NS); > - if (arm_feature(env, ARM_FEATURE_M_MAIN)) { > - env->v7m.control[M_REG_NS] &= ~R_V7M_CONTROL_NPRIV_MASK; > - env->v7m.control[M_REG_NS] |= val & R_V7M_CONTROL_NPRIV_MASK; > - } > - /* > - * SFPA is RAZ/WI from NS. FPCA is RO if NSACR.CP10 == 0, > - * RES0 if the FPU is not present, and is stored in the S bank > - */ > - if (arm_feature(env, ARM_FEATURE_VFP) && > - extract32(env->v7m.nsacr, 10, 1)) { > - env->v7m.control[M_REG_S] &= ~R_V7M_CONTROL_FPCA_MASK; > - env->v7m.control[M_REG_S] |= val & R_V7M_CONTROL_FPCA_MASK; > - } > - return; > - case 0x98: /* SP_NS */ > - { > - /* > - * This gives the non-secure SP selected based on whether we're > - * currently in handler mode or not, using the NS CONTROL.SPSEL. > - */ > - bool spsel = env->v7m.control[M_REG_NS] & > R_V7M_CONTROL_SPSEL_MASK; > - bool is_psp = !arm_v7m_is_handler_mode(env) && spsel; > - uint32_t limit; > - > - if (!env->v7m.secure) { > - return; > - } > - > - limit = is_psp ? env->v7m.psplim[false] : env->v7m.msplim[false]; > - > - if (val < limit) { > - CPUState *cs = env_cpu(env); > - > - cpu_restore_state(cs, GETPC(), true); > - raise_exception(env, EXCP_STKOF, 0, 1); > - } > - > - if (is_psp) { > - env->v7m.other_ss_psp = val; > - } else { > - env->v7m.other_ss_msp = val; > - } > - return; > - } > - default: > - break; > - } > - } > - > - switch (reg) { > - case 0 ... 7: /* xPSR sub-fields */ > - /* only APSR is actually writable */ > - if (!(reg & 4)) { > - uint32_t apsrmask = 0; > - > - if (mask & 8) { > - apsrmask |= XPSR_NZCV | XPSR_Q; > - } > - if ((mask & 4) && arm_feature(env, ARM_FEATURE_THUMB_DSP)) { > - apsrmask |= XPSR_GE; > - } > - xpsr_write(env, val, apsrmask); > - } > - break; > - case 8: /* MSP */ > - if (v7m_using_psp(env)) { > - env->v7m.other_sp = val; > - } else { > - env->regs[13] = val; > - } > - break; > - case 9: /* PSP */ > - if (v7m_using_psp(env)) { > - env->regs[13] = val; > - } else { > - env->v7m.other_sp = val; > - } > - break; > - case 10: /* MSPLIM */ > - if (!arm_feature(env, ARM_FEATURE_V8)) { > - goto bad_reg; > - } > - env->v7m.msplim[env->v7m.secure] = val & ~7; > - break; > - case 11: /* PSPLIM */ > - if (!arm_feature(env, ARM_FEATURE_V8)) { > - goto bad_reg; > - } > - env->v7m.psplim[env->v7m.secure] = val & ~7; > - break; > - case 16: /* PRIMASK */ > - env->v7m.primask[env->v7m.secure] = val & 1; > - break; > - case 17: /* BASEPRI */ > - if (!arm_feature(env, ARM_FEATURE_M_MAIN)) { > - goto bad_reg; > - } > - env->v7m.basepri[env->v7m.secure] = val & 0xff; > - break; > - case 18: /* BASEPRI_MAX */ > - if (!arm_feature(env, ARM_FEATURE_M_MAIN)) { > - goto bad_reg; > - } > - val &= 0xff; > - 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 */ > - if (!arm_feature(env, ARM_FEATURE_M_MAIN)) { > - goto bad_reg; > - } > - 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 > - * thread mode; other bits can be updated by any privileged code. > - * write_v7m_control_spsel() deals with updating the SPSEL bit in > - * env->v7m.control, so we only need update the others. > - * For v7M, we must just ignore explicit writes to SPSEL in handler > - * mode; for v8M the write is permitted but will have no effect. > - * All these bits are writes-ignored from non-privileged code, > - * except for SFPA. > - */ > - if (cur_el > 0 && (arm_feature(env, ARM_FEATURE_V8) || > - !arm_v7m_is_handler_mode(env))) { > - write_v7m_control_spsel(env, (val & R_V7M_CONTROL_SPSEL_MASK) != > 0); > - } > - if (cur_el > 0 && arm_feature(env, ARM_FEATURE_M_MAIN)) { > - env->v7m.control[env->v7m.secure] &= ~R_V7M_CONTROL_NPRIV_MASK; > - env->v7m.control[env->v7m.secure] |= val & > R_V7M_CONTROL_NPRIV_MASK; > - } > - if (arm_feature(env, ARM_FEATURE_VFP)) { > - /* > - * SFPA is RAZ/WI from NS or if no FPU. > - * FPCA is RO if NSACR.CP10 == 0, RES0 if the FPU is not present. > - * Both are stored in the S bank. > - */ > - if (env->v7m.secure) { > - env->v7m.control[M_REG_S] &= ~R_V7M_CONTROL_SFPA_MASK; > - env->v7m.control[M_REG_S] |= val & R_V7M_CONTROL_SFPA_MASK; > - } > - if (cur_el > 0 && > - (env->v7m.secure || !arm_feature(env, > ARM_FEATURE_M_SECURITY) || > - extract32(env->v7m.nsacr, 10, 1))) { > - env->v7m.control[M_REG_S] &= ~R_V7M_CONTROL_FPCA_MASK; > - env->v7m.control[M_REG_S] |= val & R_V7M_CONTROL_FPCA_MASK; > - } > - } > - break; > - default: > - bad_reg: > - qemu_log_mask(LOG_GUEST_ERROR, "Attempt to write unknown special" > - " register %d\n", reg); > - return; > - } > -} > - > -uint32_t HELPER(v7m_tt)(CPUARMState *env, uint32_t addr, uint32_t op) > -{ > - /* Implement the TT instruction. op is bits [7:6] of the insn. */ > - bool forceunpriv = op & 1; > - bool alt = op & 2; > - V8M_SAttributes sattrs = {}; > - uint32_t tt_resp; > - bool r, rw, nsr, nsrw, mrvalid; > - int prot; > - ARMMMUFaultInfo fi = {}; > - MemTxAttrs attrs = {}; > - hwaddr phys_addr; > - ARMMMUIdx mmu_idx; > - uint32_t mregion; > - bool targetpriv; > - bool targetsec = env->v7m.secure; > - bool is_subpage; > - > - /* > - * Work out what the security state and privilege level we're > - * interested in is... > - */ > - if (alt) { > - targetsec = !targetsec; > - } > - > - if (forceunpriv) { > - targetpriv = false; > - } else { > - targetpriv = arm_v7m_is_handler_mode(env) || > - !(env->v7m.control[targetsec] & R_V7M_CONTROL_NPRIV_MASK); > - } > - > - /* ...and then figure out which MMU index this is */ > - mmu_idx = arm_v7m_mmu_idx_for_secstate_and_priv(env, targetsec, > targetpriv); > - > - /* > - * We know that the MPU and SAU don't care about the access type > - * for our purposes beyond that we don't want to claim to be > - * an insn fetch, so we arbitrarily call this a read. > - */ > - > - /* > - * MPU region info only available for privileged or if > - * inspecting the other MPU state. > - */ > - if (arm_current_el(env) != 0 || alt) { > - /* We can ignore the return value as prot is always set */ > - pmsav8_mpu_lookup(env, addr, MMU_DATA_LOAD, mmu_idx, > - &phys_addr, &attrs, &prot, &is_subpage, > - &fi, &mregion); > - if (mregion == -1) { > - mrvalid = false; > - mregion = 0; > - } else { > - mrvalid = true; > - } > - r = prot & PAGE_READ; > - rw = prot & PAGE_WRITE; > - } else { > - r = false; > - rw = false; > - mrvalid = false; > - mregion = 0; > - } > - > - if (env->v7m.secure) { > - v8m_security_lookup(env, addr, MMU_DATA_LOAD, mmu_idx, &sattrs); > - nsr = sattrs.ns && r; > - nsrw = sattrs.ns && rw; > - } else { > - sattrs.ns = true; > - nsr = false; > - nsrw = false; > - } > - > - tt_resp = (sattrs.iregion << 24) | > - (sattrs.irvalid << 23) | > - ((!sattrs.ns) << 22) | > - (nsrw << 21) | > - (nsr << 20) | > - (rw << 19) | > - (r << 18) | > - (sattrs.srvalid << 17) | > - (mrvalid << 16) | > - (sattrs.sregion << 8) | > - mregion; > - > - return tt_resp; > -} > - > #endif > > bool arm_cpu_tlb_fill(CPUState *cs, vaddr address, int size, > diff --git a/target/arm/v7m_helper.c b/target/arm/v7m_helper.c > new file mode 100644 > index 0000000000..321154966e > --- /dev/null > +++ b/target/arm/v7m_helper.c > @@ -0,0 +1,654 @@ > +/* > + * ARM v7-M helpers. > + * > + * This code is licensed under the GNU GPL v2 or later. > + * > + * SPDX-License-Identifier: GPL-2.0-or-later > + */ > +#include "qemu/osdep.h" > +#include "sysemu/sysemu.h" > +#include "cpu.h" > +#include "internals.h" > +#include "exec/helper-proto.h" > +#include "exec/exec-all.h" > +#include "arm_ldst.h" > +#include "hw/semihosting/semihost.h" > +#include "fpu/softfloat.h" > + > +#if defined(CONFIG_USER_ONLY) > + > +/* These should probably raise undefined insn exceptions. */ > +void HELPER(v7m_msr)(CPUARMState *env, uint32_t reg, uint32_t val) > +{ > + ARMCPU *cpu = env_archcpu(env); > + > + cpu_abort(CPU(cpu), "v7m_msr %d\n", reg); > +} > + > +uint32_t HELPER(v7m_mrs)(CPUARMState *env, uint32_t reg) > +{ > + ARMCPU *cpu = env_archcpu(env); > + > + cpu_abort(CPU(cpu), "v7m_mrs %d\n", 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 HELPER(v7m_blxns)(CPUARMState *env, uint32_t dest) > +{ > + /* translate.c should never generate calls here in user-only mode */ > + g_assert_not_reached(); > +} > + > +void HELPER(v7m_preserve_fp_state)(CPUARMState *env) > +{ > + /* translate.c should never generate calls here in user-only mode */ > + g_assert_not_reached(); > +} > + > +void HELPER(v7m_vlstm)(CPUARMState *env, uint32_t fptr) > +{ > + /* translate.c should never generate calls here in user-only mode */ > + g_assert_not_reached(); > +} > + > +void HELPER(v7m_vlldm)(CPUARMState *env, uint32_t fptr) > +{ > + /* translate.c should never generate calls here in user-only mode */ > + g_assert_not_reached(); > +} > + > +uint32_t HELPER(v7m_tt)(CPUARMState *env, uint32_t addr, uint32_t op) > +{ > + /* > + * The TT instructions can be used by unprivileged code, but in > + * user-only emulation we don't have the MPU. > + * Luckily since we know we are NonSecure unprivileged (and that in > + * turn means that the A flag wasn't specified), all the bits in the > + * register must be zero: > + * IREGION: 0 because IRVALID is 0 > + * IRVALID: 0 because NS > + * S: 0 because NS > + * NSRW: 0 because NS > + * NSR: 0 because NS > + * RW: 0 because unpriv and A flag not set > + * R: 0 because unpriv and A flag not set > + * SRVALID: 0 because NS > + * MRVALID: 0 because unpriv and A flag not set > + * SREGION: 0 becaus SRVALID is 0 > + * MREGION: 0 because MRVALID is 0 > + */ > + return 0; > +} > + > +#else > + > +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 > + */ > + uint32_t min_magic; > + > + if (arm_feature(env, ARM_FEATURE_M_SECURITY)) { > + /* Covers FNC_RETURN and EXC_RETURN magic */ > + min_magic = FNC_RETURN_MIN_MAGIC; > + } else { > + /* EXC_RETURN magic only */ > + min_magic = EXC_RETURN_MIN_MAGIC; > + } > + > + if (dest >= min_magic) { > + /* > + * 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); > + > + if (!(dest & 1)) { > + env->v7m.control[M_REG_S] &= ~R_V7M_CONTROL_SFPA_MASK; > + } > + switch_v7m_security_state(env, dest & 1); > + env->thumb = 1; > + env->regs[15] = dest & ~1; > +} > + > +void HELPER(v7m_blxns)(CPUARMState *env, uint32_t dest) > +{ > + /* > + * Handle v7M BLXNS: > + * - bit 0 of the destination address is the target security state > + */ > + > + /* At this point regs[15] is the address just after the BLXNS */ > + uint32_t nextinst = env->regs[15] | 1; > + uint32_t sp = env->regs[13] - 8; > + uint32_t saved_psr; > + > + /* translate.c will have made BLXNS UNDEF unless we're secure */ > + assert(env->v7m.secure); > + > + if (dest & 1) { > + /* > + * Target is Secure, so this is just a normal BLX, > + * except that the low bit doesn't indicate Thumb/not. > + */ > + env->regs[14] = nextinst; > + env->thumb = 1; > + env->regs[15] = dest & ~1; > + return; > + } > + > + /* Target is non-secure: first push a stack frame */ > + if (!QEMU_IS_ALIGNED(sp, 8)) { > + qemu_log_mask(LOG_GUEST_ERROR, > + "BLXNS with misaligned SP is UNPREDICTABLE\n"); > + } > + > + if (sp < v7m_sp_limit(env)) { > + raise_exception(env, EXCP_STKOF, 0, 1); > + } > + > + saved_psr = env->v7m.exception; > + if (env->v7m.control[M_REG_S] & R_V7M_CONTROL_SFPA_MASK) { > + saved_psr |= XPSR_SFPA; > + } > + > + /* Note that these stores can throw exceptions on MPU faults */ > + cpu_stl_data(env, sp, nextinst); > + cpu_stl_data(env, sp + 4, saved_psr); > + > + env->regs[13] = sp; > + env->regs[14] = 0xfeffffff; > + if (arm_v7m_is_handler_mode(env)) { > + /* > + * Write a dummy value to IPSR, to avoid leaking the current secure > + * exception number to non-secure code. This is guaranteed not > + * to cause write_v7m_exception() to actually change stacks. > + */ > + write_v7m_exception(env, 1); > + } > + env->v7m.control[M_REG_S] &= ~R_V7M_CONTROL_SFPA_MASK; > + switch_v7m_security_state(env, 0); > + env->thumb = 1; > + env->regs[15] = dest; > +} > + > +uint32_t HELPER(v7m_mrs)(CPUARMState *env, uint32_t reg) > +{ > + uint32_t mask; > + unsigned el = arm_current_el(env); > + > + /* First handle registers which unprivileged can read */ > + > + switch (reg) { > + case 0 ... 7: /* xPSR sub-fields */ > + mask = 0; > + if ((reg & 1) && el) { > + mask |= XPSR_EXCP; /* IPSR (unpriv. reads as zero) */ > + } > + if (!(reg & 4)) { > + mask |= XPSR_NZCV | XPSR_Q; /* APSR */ > + if (arm_feature(env, ARM_FEATURE_THUMB_DSP)) { > + mask |= XPSR_GE; > + } > + } > + /* EPSR reads as zero */ > + return xpsr_read(env) & mask; > + break; > + case 20: /* CONTROL */ > + { > + uint32_t value = env->v7m.control[env->v7m.secure]; > + if (!env->v7m.secure) { > + /* SFPA is RAZ/WI from NS; FPCA is stored in the M_REG_S bank */ > + value |= env->v7m.control[M_REG_S] & R_V7M_CONTROL_FPCA_MASK; > + } > + return value; > + } > + case 0x94: /* CONTROL_NS */ > + /* > + * We have to handle this here because unprivileged Secure code > + * can read the NS CONTROL register. > + */ > + if (!env->v7m.secure) { > + return 0; > + } > + return env->v7m.control[M_REG_NS] | > + (env->v7m.control[M_REG_S] & R_V7M_CONTROL_FPCA_MASK); > + } > + > + if (el == 0) { > + return 0; /* unprivileged reads others as zero */ > + } > + > + if (arm_feature(env, ARM_FEATURE_M_SECURITY)) { > + switch (reg) { > + case 0x88: /* MSP_NS */ > + if (!env->v7m.secure) { > + return 0; > + } > + return env->v7m.other_ss_msp; > + case 0x89: /* PSP_NS */ > + if (!env->v7m.secure) { > + return 0; > + } > + return env->v7m.other_ss_psp; > + case 0x8a: /* MSPLIM_NS */ > + if (!env->v7m.secure) { > + return 0; > + } > + return env->v7m.msplim[M_REG_NS]; > + case 0x8b: /* PSPLIM_NS */ > + if (!env->v7m.secure) { > + return 0; > + } > + return env->v7m.psplim[M_REG_NS]; > + case 0x90: /* PRIMASK_NS */ > + if (!env->v7m.secure) { > + return 0; > + } > + return env->v7m.primask[M_REG_NS]; > + case 0x91: /* BASEPRI_NS */ > + if (!env->v7m.secure) { > + return 0; > + } > + return env->v7m.basepri[M_REG_NS]; > + case 0x93: /* FAULTMASK_NS */ > + if (!env->v7m.secure) { > + return 0; > + } > + return env->v7m.faultmask[M_REG_NS]; > + case 0x98: /* SP_NS */ > + { > + /* > + * This gives the non-secure SP selected based on whether we're > + * currently in handler mode or not, using the NS CONTROL.SPSEL. > + */ > + bool spsel = env->v7m.control[M_REG_NS] & > R_V7M_CONTROL_SPSEL_MASK; > + > + if (!env->v7m.secure) { > + return 0; > + } > + if (!arm_v7m_is_handler_mode(env) && spsel) { > + return env->v7m.other_ss_psp; > + } else { > + return env->v7m.other_ss_msp; > + } > + } > + default: > + break; > + } > + } > + > + switch (reg) { > + case 8: /* MSP */ > + return v7m_using_psp(env) ? env->v7m.other_sp : env->regs[13]; > + case 9: /* PSP */ > + return v7m_using_psp(env) ? env->regs[13] : env->v7m.other_sp; > + case 10: /* MSPLIM */ > + if (!arm_feature(env, ARM_FEATURE_V8)) { > + goto bad_reg; > + } > + return env->v7m.msplim[env->v7m.secure]; > + case 11: /* PSPLIM */ > + if (!arm_feature(env, ARM_FEATURE_V8)) { > + goto bad_reg; > + } > + return env->v7m.psplim[env->v7m.secure]; > + case 16: /* PRIMASK */ > + return env->v7m.primask[env->v7m.secure]; > + case 17: /* BASEPRI */ > + case 18: /* BASEPRI_MAX */ > + return env->v7m.basepri[env->v7m.secure]; > + case 19: /* FAULTMASK */ > + return env->v7m.faultmask[env->v7m.secure]; > + default: > + bad_reg: > + qemu_log_mask(LOG_GUEST_ERROR, "Attempt to read unknown special" > + " register %d\n", reg); > + return 0; > + } > +} > + > +void HELPER(v7m_msr)(CPUARMState *env, uint32_t maskreg, uint32_t val) > +{ > + /* > + * We're passed bits [11..0] of the instruction; extract > + * SYSm and the mask bits. > + * Invalid combinations of SYSm and mask are UNPREDICTABLE; > + * we choose to treat them as if the mask bits were valid. > + * NB that the pseudocode 'mask' variable is bits [11..10], > + * whereas ours is [11..8]. > + */ > + uint32_t mask = extract32(maskreg, 8, 4); > + uint32_t reg = extract32(maskreg, 0, 8); > + int cur_el = arm_current_el(env); > + > + if (cur_el == 0 && reg > 7 && reg != 20) { > + /* > + * only xPSR sub-fields and CONTROL.SFPA may be written by > + * unprivileged code > + */ > + return; > + } > + > + if (arm_feature(env, ARM_FEATURE_M_SECURITY)) { > + switch (reg) { > + case 0x88: /* MSP_NS */ > + if (!env->v7m.secure) { > + return; > + } > + env->v7m.other_ss_msp = val; > + return; > + case 0x89: /* PSP_NS */ > + if (!env->v7m.secure) { > + return; > + } > + env->v7m.other_ss_psp = val; > + return; > + case 0x8a: /* MSPLIM_NS */ > + if (!env->v7m.secure) { > + return; > + } > + env->v7m.msplim[M_REG_NS] = val & ~7; > + return; > + case 0x8b: /* PSPLIM_NS */ > + if (!env->v7m.secure) { > + return; > + } > + env->v7m.psplim[M_REG_NS] = val & ~7; > + return; > + case 0x90: /* PRIMASK_NS */ > + if (!env->v7m.secure) { > + return; > + } > + env->v7m.primask[M_REG_NS] = val & 1; > + return; > + case 0x91: /* BASEPRI_NS */ > + if (!env->v7m.secure || !arm_feature(env, ARM_FEATURE_M_MAIN)) { > + return; > + } > + env->v7m.basepri[M_REG_NS] = val & 0xff; > + return; > + case 0x93: /* FAULTMASK_NS */ > + if (!env->v7m.secure || !arm_feature(env, ARM_FEATURE_M_MAIN)) { > + return; > + } > + env->v7m.faultmask[M_REG_NS] = val & 1; > + return; > + case 0x94: /* CONTROL_NS */ > + if (!env->v7m.secure) { > + return; > + } > + write_v7m_control_spsel_for_secstate(env, > + val & > R_V7M_CONTROL_SPSEL_MASK, > + M_REG_NS); > + if (arm_feature(env, ARM_FEATURE_M_MAIN)) { > + env->v7m.control[M_REG_NS] &= ~R_V7M_CONTROL_NPRIV_MASK; > + env->v7m.control[M_REG_NS] |= val & R_V7M_CONTROL_NPRIV_MASK; > + } > + /* > + * SFPA is RAZ/WI from NS. FPCA is RO if NSACR.CP10 == 0, > + * RES0 if the FPU is not present, and is stored in the S bank > + */ > + if (arm_feature(env, ARM_FEATURE_VFP) && > + extract32(env->v7m.nsacr, 10, 1)) { > + env->v7m.control[M_REG_S] &= ~R_V7M_CONTROL_FPCA_MASK; > + env->v7m.control[M_REG_S] |= val & R_V7M_CONTROL_FPCA_MASK; > + } > + return; > + case 0x98: /* SP_NS */ > + { > + /* > + * This gives the non-secure SP selected based on whether we're > + * currently in handler mode or not, using the NS CONTROL.SPSEL. > + */ > + bool spsel = env->v7m.control[M_REG_NS] & > R_V7M_CONTROL_SPSEL_MASK; > + bool is_psp = !arm_v7m_is_handler_mode(env) && spsel; > + uint32_t limit; > + > + if (!env->v7m.secure) { > + return; > + } > + > + limit = is_psp ? env->v7m.psplim[false] : env->v7m.msplim[false]; > + > + if (val < limit) { > + CPUState *cs = env_cpu(env); > + > + cpu_restore_state(cs, GETPC(), true); > + raise_exception(env, EXCP_STKOF, 0, 1); > + } > + > + if (is_psp) { > + env->v7m.other_ss_psp = val; > + } else { > + env->v7m.other_ss_msp = val; > + } > + return; > + } > + default: > + break; > + } > + } > + > + switch (reg) { > + case 0 ... 7: /* xPSR sub-fields */ > + /* only APSR is actually writable */ > + if (!(reg & 4)) { > + uint32_t apsrmask = 0; > + > + if (mask & 8) { > + apsrmask |= XPSR_NZCV | XPSR_Q; > + } > + if ((mask & 4) && arm_feature(env, ARM_FEATURE_THUMB_DSP)) { > + apsrmask |= XPSR_GE; > + } > + xpsr_write(env, val, apsrmask); > + } > + break; > + case 8: /* MSP */ > + if (v7m_using_psp(env)) { > + env->v7m.other_sp = val; > + } else { > + env->regs[13] = val; > + } > + break; > + case 9: /* PSP */ > + if (v7m_using_psp(env)) { > + env->regs[13] = val; > + } else { > + env->v7m.other_sp = val; > + } > + break; > + case 10: /* MSPLIM */ > + if (!arm_feature(env, ARM_FEATURE_V8)) { > + goto bad_reg; > + } > + env->v7m.msplim[env->v7m.secure] = val & ~7; > + break; > + case 11: /* PSPLIM */ > + if (!arm_feature(env, ARM_FEATURE_V8)) { > + goto bad_reg; > + } > + env->v7m.psplim[env->v7m.secure] = val & ~7; > + break; > + case 16: /* PRIMASK */ > + env->v7m.primask[env->v7m.secure] = val & 1; > + break; > + case 17: /* BASEPRI */ > + if (!arm_feature(env, ARM_FEATURE_M_MAIN)) { > + goto bad_reg; > + } > + env->v7m.basepri[env->v7m.secure] = val & 0xff; > + break; > + case 18: /* BASEPRI_MAX */ > + if (!arm_feature(env, ARM_FEATURE_M_MAIN)) { > + goto bad_reg; > + } > + val &= 0xff; > + 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 */ > + if (!arm_feature(env, ARM_FEATURE_M_MAIN)) { > + goto bad_reg; > + } > + 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 > + * thread mode; other bits can be updated by any privileged code. > + * write_v7m_control_spsel() deals with updating the SPSEL bit in > + * env->v7m.control, so we only need update the others. > + * For v7M, we must just ignore explicit writes to SPSEL in handler > + * mode; for v8M the write is permitted but will have no effect. > + * All these bits are writes-ignored from non-privileged code, > + * except for SFPA. > + */ > + if (cur_el > 0 && (arm_feature(env, ARM_FEATURE_V8) || > + !arm_v7m_is_handler_mode(env))) { > + write_v7m_control_spsel(env, (val & R_V7M_CONTROL_SPSEL_MASK) != > 0); > + } > + if (cur_el > 0 && arm_feature(env, ARM_FEATURE_M_MAIN)) { > + env->v7m.control[env->v7m.secure] &= ~R_V7M_CONTROL_NPRIV_MASK; > + env->v7m.control[env->v7m.secure] |= val & > R_V7M_CONTROL_NPRIV_MASK; > + } > + if (arm_feature(env, ARM_FEATURE_VFP)) { > + /* > + * SFPA is RAZ/WI from NS or if no FPU. > + * FPCA is RO if NSACR.CP10 == 0, RES0 if the FPU is not present. > + * Both are stored in the S bank. > + */ > + if (env->v7m.secure) { > + env->v7m.control[M_REG_S] &= ~R_V7M_CONTROL_SFPA_MASK; > + env->v7m.control[M_REG_S] |= val & R_V7M_CONTROL_SFPA_MASK; > + } > + if (cur_el > 0 && > + (env->v7m.secure || !arm_feature(env, > ARM_FEATURE_M_SECURITY) || > + extract32(env->v7m.nsacr, 10, 1))) { > + env->v7m.control[M_REG_S] &= ~R_V7M_CONTROL_FPCA_MASK; > + env->v7m.control[M_REG_S] |= val & R_V7M_CONTROL_FPCA_MASK; > + } > + } > + break; > + default: > + bad_reg: > + qemu_log_mask(LOG_GUEST_ERROR, "Attempt to write unknown special" > + " register %d\n", reg); > + return; > + } > +} > + > +uint32_t HELPER(v7m_tt)(CPUARMState *env, uint32_t addr, uint32_t op) > +{ > + /* Implement the TT instruction. op is bits [7:6] of the insn. */ > + bool forceunpriv = op & 1; > + bool alt = op & 2; > + V8M_SAttributes sattrs = {}; > + uint32_t tt_resp; > + bool r, rw, nsr, nsrw, mrvalid; > + int prot; > + ARMMMUFaultInfo fi = {}; > + MemTxAttrs attrs = {}; > + hwaddr phys_addr; > + ARMMMUIdx mmu_idx; > + uint32_t mregion; > + bool targetpriv; > + bool targetsec = env->v7m.secure; > + bool is_subpage; > + > + /* > + * Work out what the security state and privilege level we're > + * interested in is... > + */ > + if (alt) { > + targetsec = !targetsec; > + } > + > + if (forceunpriv) { > + targetpriv = false; > + } else { > + targetpriv = arm_v7m_is_handler_mode(env) || > + !(env->v7m.control[targetsec] & R_V7M_CONTROL_NPRIV_MASK); > + } > + > + /* ...and then figure out which MMU index this is */ > + mmu_idx = arm_v7m_mmu_idx_for_secstate_and_priv(env, targetsec, > targetpriv); > + > + /* > + * We know that the MPU and SAU don't care about the access type > + * for our purposes beyond that we don't want to claim to be > + * an insn fetch, so we arbitrarily call this a read. > + */ > + > + /* > + * MPU region info only available for privileged or if > + * inspecting the other MPU state. > + */ > + if (arm_current_el(env) != 0 || alt) { > + /* We can ignore the return value as prot is always set */ > + pmsav8_mpu_lookup(env, addr, MMU_DATA_LOAD, mmu_idx, > + &phys_addr, &attrs, &prot, &is_subpage, > + &fi, &mregion); > + if (mregion == -1) { > + mrvalid = false; > + mregion = 0; > + } else { > + mrvalid = true; > + } > + r = prot & PAGE_READ; > + rw = prot & PAGE_WRITE; > + } else { > + r = false; > + rw = false; > + mrvalid = false; > + mregion = 0; > + } > + > + if (env->v7m.secure) { > + v8m_security_lookup(env, addr, MMU_DATA_LOAD, mmu_idx, &sattrs); > + nsr = sattrs.ns && r; > + nsrw = sattrs.ns && rw; > + } else { > + sattrs.ns = true; > + nsr = false; > + nsrw = false; > + } > + > + tt_resp = (sattrs.iregion << 24) | > + (sattrs.irvalid << 23) | > + ((!sattrs.ns) << 22) | > + (nsrw << 21) | > + (nsr << 20) | > + (rw << 19) | > + (r << 18) | > + (sattrs.srvalid << 17) | > + (mrvalid << 16) | > + (sattrs.sregion << 8) | > + mregion; > + > + return tt_resp; > +} > + > +#endif /* CONFIG_USER_ONLY */ -- Alex Bennée