Message ID | 20190615154352.26824-9-philmd@redhat.com |
---|---|
State | New |
Headers | show |
Series | Support disabling TCG on ARM | expand |
Philippe Mathieu-Daudé <philmd@redhat.com> writes: > From: Samuel Ortiz <sameo@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 <sameo@linux.intel.com> > [PMD: Patch rewritten] > Signed-off-by: Philippe Mathieu-Daudé <philmd@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
On 6/17/19 1:42 PM, Alex Bennée wrote: > Philippe Mathieu-Daudé <philmd@redhat.com> writes: > >> From: Samuel Ortiz <sameo@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 <sameo@linux.intel.com> >> [PMD: Patch rewritten] >> Signed-off-by: Philippe Mathieu-Daudé <philmd@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? No :( If I use this diff: -- >8 -- diff --git a/target/arm/helper.h b/target/arm/helper.h @@ -58,24 +58,26 @@ DEF_HELPER_2(pre_smc, void, env, i32) DEF_HELPER_1(check_breakpoints, void, env) DEF_HELPER_3(cpsr_write, void, env, i32, i32) DEF_HELPER_2(cpsr_write_eret, void, env, i32) DEF_HELPER_1(cpsr_read, i32, env) +#ifndef CONFIG_USER_ONLY DEF_HELPER_3(v7m_msr, void, env, i32, i32) DEF_HELPER_2(v7m_mrs, i32, env, i32) DEF_HELPER_2(v7m_bxns, void, env, i32) DEF_HELPER_2(v7m_blxns, void, env, i32) DEF_HELPER_3(v7m_tt, i32, env, i32, i32) DEF_HELPER_1(v7m_preserve_fp_state, void, env) DEF_HELPER_2(v7m_vlstm, void, env, i32) DEF_HELPER_2(v7m_vlldm, void, env, i32) +#endif /* CONFIG_USER_ONLY */ DEF_HELPER_2(v8m_stackcheck, void, env, i32) --- I get: target/arm/translate.c:10607:29: error: nested extern declaration of ‘gen_helper_v7m_mrs’ [-Werror=nested-externs] target/arm/translate.c: In function ‘disas_thumb_insn’: target/arm/translate.c:11224:25: error: implicit declaration of function ‘gen_blxns’; did you mean ‘gen_bx’? [-Werror=implicit-function-declaration] gen_blxns(s, rm); ^~~~~~~~~ Because: static void disas_thumb_insn(DisasContext *s, uint32_t insn) { ... switch (insn >> 12) { ... case 4: ... if (insn & (1 << 10)) { ... case 3: { /* 0b0100_0111_xxxx_xxxx * - branch [and link] exchange thumb register */ bool link = insn & (1 << 7); if (insn & 3) { goto undef; } if (link) { ARCH(5); } if ((insn & 4)) { /* BXNS/BLXNS: only exists for v8M with the * security extensions, and always UNDEF if NonSecure. * We don't implement these in the user-only mode * either (in theory you can use them from Secure User * mode but they are too tied in to system emulation.) */ if (!s->v8m_secure || IS_USER_ONLY) { goto undef; } if (link) { gen_blxns(s, rm); } else { gen_bxns(s, rm); } break; } Should we add "#ifndef CONFIG_USER_ONLY" all around? I believe we rather not... For 'cps' and 'mrs' we have: /* Implemented as NOP in user mode. */ if (IS_USER(s)) break; but not for 'msr'.
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 */