#include "qemu/osdep.h"
+#include "target/arm/idau.h"
#include "trace.h"
#include "cpu.h"
#include "internals.h"
#include <zlib.h> /* For crc32 */
#include "exec/semihost.h"
#include "sysemu/kvm.h"
+#include "fpu/softfloat.h"
#define ARM_CPU_FREQ 1000000000 /* FIXME: 1 GHz, should be configurable */
static bool get_phys_addr(CPUARMState *env, target_ulong address,
MMUAccessType access_type, ARMMMUIdx mmu_idx,
hwaddr *phys_ptr, MemTxAttrs *attrs, int *prot,
- target_ulong *page_size, uint32_t *fsr,
+ target_ulong *page_size,
ARMMMUFaultInfo *fi, ARMCacheAttrs *cacheattrs);
static bool get_phys_addr_lpae(CPUARMState *env, target_ulong address,
MMUAccessType access_type, ARMMMUIdx mmu_idx,
hwaddr *phys_ptr, MemTxAttrs *txattrs, int *prot,
- target_ulong *page_size_ptr, uint32_t *fsr,
+ target_ulong *page_size_ptr,
ARMMMUFaultInfo *fi, ARMCacheAttrs *cacheattrs);
/* Security attributes for an address, as returned by v8m_security_lookup. */
/* VFP data registers are always little-endian. */
nregs = arm_feature(env, ARM_FEATURE_VFP3) ? 32 : 16;
if (reg < nregs) {
- stfq_le_p(buf, env->vfp.regs[reg]);
+ stq_le_p(buf, *aa32_vfp_dreg(env, reg));
return 8;
}
if (arm_feature(env, ARM_FEATURE_NEON)) {
/* Aliases for Q regs. */
nregs += 16;
if (reg < nregs) {
- stfq_le_p(buf, env->vfp.regs[(reg - 32) * 2]);
- stfq_le_p(buf + 8, env->vfp.regs[(reg - 32) * 2 + 1]);
+ uint64_t *q = aa32_vfp_qreg(env, reg - 32);
+ stq_le_p(buf, q[0]);
+ stq_le_p(buf + 8, q[1]);
return 16;
}
}
nregs = arm_feature(env, ARM_FEATURE_VFP3) ? 32 : 16;
if (reg < nregs) {
- env->vfp.regs[reg] = ldfq_le_p(buf);
+ *aa32_vfp_dreg(env, reg) = ldq_le_p(buf);
return 8;
}
if (arm_feature(env, ARM_FEATURE_NEON)) {
nregs += 16;
if (reg < nregs) {
- env->vfp.regs[(reg - 32) * 2] = ldfq_le_p(buf);
- env->vfp.regs[(reg - 32) * 2 + 1] = ldfq_le_p(buf + 8);
+ uint64_t *q = aa32_vfp_qreg(env, reg - 32);
+ q[0] = ldq_le_p(buf);
+ q[1] = ldq_le_p(buf + 8);
return 16;
}
}
switch (reg) {
case 0 ... 31:
/* 128 bit FP register */
- stfq_le_p(buf, env->vfp.regs[reg * 2]);
- stfq_le_p(buf + 8, env->vfp.regs[reg * 2 + 1]);
- return 16;
+ {
+ uint64_t *q = aa64_vfp_qreg(env, reg);
+ stq_le_p(buf, q[0]);
+ stq_le_p(buf + 8, q[1]);
+ return 16;
+ }
case 32:
/* FPSR */
stl_p(buf, vfp_get_fpsr(env));
switch (reg) {
case 0 ... 31:
/* 128 bit FP register */
- env->vfp.regs[reg * 2] = ldfq_le_p(buf);
- env->vfp.regs[reg * 2 + 1] = ldfq_le_p(buf + 8);
- return 16;
+ {
+ uint64_t *q = aa64_vfp_qreg(env, reg);
+ q[0] = ldq_le_p(buf);
+ q[1] = ldq_le_p(buf + 8);
+ return 16;
+ }
case 32:
/* FPSR */
vfp_set_fpsr(env, ldl_p(buf));
hwaddr phys_addr;
target_ulong page_size;
int prot;
- uint32_t fsr;
bool ret;
uint64_t par64;
+ bool format64 = false;
MemTxAttrs attrs = {};
ARMMMUFaultInfo fi = {};
ARMCacheAttrs cacheattrs = {};
ret = get_phys_addr(env, value, access_type, mmu_idx, &phys_addr, &attrs,
- &prot, &page_size, &fsr, &fi, &cacheattrs);
- if (arm_s1_regime_using_lpae_format(env, mmu_idx)) {
- /* fsr is a DFSR/IFSR value for the long descriptor
- * translation table format, but with WnR always clear.
- * Convert it to a 64-bit PAR.
+ &prot, &page_size, &fi, &cacheattrs);
+
+ if (is_a64(env)) {
+ format64 = true;
+ } else if (arm_feature(env, ARM_FEATURE_LPAE)) {
+ /*
+ * ATS1Cxx:
+ * * TTBCR.EAE determines whether the result is returned using the
+ * 32-bit or the 64-bit PAR format
+ * * Instructions executed in Hyp mode always use the 64bit format
+ *
+ * ATS1S2NSOxx uses the 64bit format if any of the following is true:
+ * * The Non-secure TTBCR.EAE bit is set to 1
+ * * The implementation includes EL2, and the value of HCR.VM is 1
+ *
+ * ATS1Hx always uses the 64bit format (not supported yet).
*/
+ format64 = arm_s1_regime_using_lpae_format(env, mmu_idx);
+
+ if (arm_feature(env, ARM_FEATURE_EL2)) {
+ if (mmu_idx == ARMMMUIdx_S12NSE0 || mmu_idx == ARMMMUIdx_S12NSE1) {
+ format64 |= env->cp15.hcr_el2 & HCR_VM;
+ } else {
+ format64 |= arm_current_el(env) == 2;
+ }
+ }
+ }
+
+ if (format64) {
+ /* Create a 64-bit PAR */
par64 = (1 << 11); /* LPAE bit always set */
if (!ret) {
par64 |= phys_addr & ~0xfffULL;
par64 |= (uint64_t)cacheattrs.attrs << 56; /* ATTR */
par64 |= cacheattrs.shareability << 7; /* SH */
} else {
+ uint32_t fsr = arm_fi_to_lfsc(&fi);
+
par64 |= 1; /* F */
par64 |= (fsr & 0x3f) << 1; /* FS */
/* Note that S2WLK and FSTAGE are always zero, because we don't
par64 |= (1 << 9); /* NS */
}
} else {
+ uint32_t fsr = arm_fi_to_sfsc(&fi);
+
par64 = ((fsr & (1 << 10)) >> 5) | ((fsr & (1 << 12)) >> 6) |
((fsr & 0xf) << 1) | 1;
}
.writefn = aa64_daif_write, .resetfn = arm_cp_reset_ignore },
{ .name = "FPCR", .state = ARM_CP_STATE_AA64,
.opc0 = 3, .opc1 = 3, .opc2 = 0, .crn = 4, .crm = 4,
- .access = PL0_RW, .readfn = aa64_fpcr_read, .writefn = aa64_fpcr_write },
+ .access = PL0_RW, .type = ARM_CP_FPU | ARM_CP_SUPPRESS_TB_END,
+ .readfn = aa64_fpcr_read, .writefn = aa64_fpcr_write },
{ .name = "FPSR", .state = ARM_CP_STATE_AA64,
.opc0 = 3, .opc1 = 3, .opc2 = 1, .crn = 4, .crm = 4,
- .access = PL0_RW, .readfn = aa64_fpsr_read, .writefn = aa64_fpsr_write },
+ .access = PL0_RW, .type = ARM_CP_FPU | ARM_CP_SUPPRESS_TB_END,
+ .readfn = aa64_fpsr_read, .writefn = aa64_fpsr_write },
{ .name = "DCZID_EL0", .state = ARM_CP_STATE_AA64,
.opc0 = 3, .opc1 = 3, .opc2 = 7, .crn = 0, .crm = 0,
.access = PL0_R, .type = ARM_CP_NO_RAW,
REGINFO_SENTINEL
};
+/* Return the exception level to which SVE-disabled exceptions should
+ * be taken, or 0 if SVE is enabled.
+ */
+static int sve_exception_el(CPUARMState *env)
+{
+#ifndef CONFIG_USER_ONLY
+ unsigned current_el = arm_current_el(env);
+
+ /* The CPACR.ZEN controls traps to EL1:
+ * 0, 2 : trap EL0 and EL1 accesses
+ * 1 : trap only EL0 accesses
+ * 3 : trap no accesses
+ */
+ switch (extract32(env->cp15.cpacr_el1, 16, 2)) {
+ default:
+ if (current_el <= 1) {
+ /* Trap to PL1, which might be EL1 or EL3 */
+ if (arm_is_secure(env) && !arm_el_is_aa64(env, 3)) {
+ return 3;
+ }
+ return 1;
+ }
+ break;
+ case 1:
+ if (current_el == 0) {
+ return 1;
+ }
+ break;
+ case 3:
+ break;
+ }
+
+ /* Similarly for CPACR.FPEN, after having checked ZEN. */
+ switch (extract32(env->cp15.cpacr_el1, 20, 2)) {
+ default:
+ if (current_el <= 1) {
+ if (arm_is_secure(env) && !arm_el_is_aa64(env, 3)) {
+ return 3;
+ }
+ return 1;
+ }
+ break;
+ case 1:
+ if (current_el == 0) {
+ return 1;
+ }
+ break;
+ case 3:
+ break;
+ }
+
+ /* CPTR_EL2. Check both TZ and TFP. */
+ if (current_el <= 2
+ && (env->cp15.cptr_el[2] & (CPTR_TFP | CPTR_TZ))
+ && !arm_is_secure_below_el3(env)) {
+ return 2;
+ }
+
+ /* CPTR_EL3. Check both EZ and TFP. */
+ if (!(env->cp15.cptr_el[3] & CPTR_EZ)
+ || (env->cp15.cptr_el[3] & CPTR_TFP)) {
+ return 3;
+ }
+#endif
+ return 0;
+}
+
+static void zcr_write(CPUARMState *env, const ARMCPRegInfo *ri,
+ uint64_t value)
+{
+ /* Bits other than [3:0] are RAZ/WI. */
+ raw_write(env, ri, value & 0xf);
+}
+
+static const ARMCPRegInfo zcr_el1_reginfo = {
+ .name = "ZCR_EL1", .state = ARM_CP_STATE_AA64,
+ .opc0 = 3, .opc1 = 0, .crn = 1, .crm = 2, .opc2 = 0,
+ .access = PL1_RW, .type = ARM_CP_SVE | ARM_CP_FPU,
+ .fieldoffset = offsetof(CPUARMState, vfp.zcr_el[1]),
+ .writefn = zcr_write, .raw_writefn = raw_write
+};
+
+static const ARMCPRegInfo zcr_el2_reginfo = {
+ .name = "ZCR_EL2", .state = ARM_CP_STATE_AA64,
+ .opc0 = 3, .opc1 = 4, .crn = 1, .crm = 2, .opc2 = 0,
+ .access = PL2_RW, .type = ARM_CP_SVE | ARM_CP_FPU,
+ .fieldoffset = offsetof(CPUARMState, vfp.zcr_el[2]),
+ .writefn = zcr_write, .raw_writefn = raw_write
+};
+
+static const ARMCPRegInfo zcr_no_el2_reginfo = {
+ .name = "ZCR_EL2", .state = ARM_CP_STATE_AA64,
+ .opc0 = 3, .opc1 = 4, .crn = 1, .crm = 2, .opc2 = 0,
+ .access = PL2_RW, .type = ARM_CP_SVE | ARM_CP_FPU,
+ .readfn = arm_cp_read_zero, .writefn = arm_cp_write_ignore
+};
+
+static const ARMCPRegInfo zcr_el3_reginfo = {
+ .name = "ZCR_EL3", .state = ARM_CP_STATE_AA64,
+ .opc0 = 3, .opc1 = 6, .crn = 1, .crm = 2, .opc2 = 0,
+ .access = PL3_RW, .type = ARM_CP_SVE | ARM_CP_FPU,
+ .fieldoffset = offsetof(CPUARMState, vfp.zcr_el[3]),
+ .writefn = zcr_write, .raw_writefn = raw_write
+};
+
void hw_watchpoint_update(ARMCPU *cpu, int n)
{
CPUARMState *env = &cpu->env;
{ .name = "VPIDR", .state = ARM_CP_STATE_AA32,
.cp = 15, .opc1 = 4, .crn = 0, .crm = 0, .opc2 = 0,
.access = PL2_RW, .accessfn = access_el3_aa32ns,
- .resetvalue = cpu->midr,
- .fieldoffset = offsetof(CPUARMState, cp15.vpidr_el2) },
+ .resetvalue = cpu->midr, .type = ARM_CP_ALIAS,
+ .fieldoffset = offsetoflow32(CPUARMState, cp15.vpidr_el2) },
{ .name = "VPIDR_EL2", .state = ARM_CP_STATE_AA64,
.opc0 = 3, .opc1 = 4, .crn = 0, .crm = 0, .opc2 = 0,
.access = PL2_RW, .resetvalue = cpu->midr,
{ .name = "VMPIDR", .state = ARM_CP_STATE_AA32,
.cp = 15, .opc1 = 4, .crn = 0, .crm = 0, .opc2 = 5,
.access = PL2_RW, .accessfn = access_el3_aa32ns,
- .resetvalue = vmpidr_def,
- .fieldoffset = offsetof(CPUARMState, cp15.vmpidr_el2) },
+ .resetvalue = vmpidr_def, .type = ARM_CP_ALIAS,
+ .fieldoffset = offsetoflow32(CPUARMState, cp15.vmpidr_el2) },
{ .name = "VMPIDR_EL2", .state = ARM_CP_STATE_AA64,
.opc0 = 3, .opc1 = 4, .crn = 0, .crm = 0, .opc2 = 5,
.access = PL2_RW,
}
define_one_arm_cp_reg(cpu, &sctlr);
}
+
+ if (arm_feature(env, ARM_FEATURE_SVE)) {
+ define_one_arm_cp_reg(cpu, &zcr_el1_reginfo);
+ if (arm_feature(env, ARM_FEATURE_EL2)) {
+ define_one_arm_cp_reg(cpu, &zcr_el2_reginfo);
+ } else {
+ define_one_arm_cp_reg(cpu, &zcr_no_el2_reginfo);
+ }
+ if (arm_feature(env, ARM_FEATURE_EL3)) {
+ define_one_arm_cp_reg(cpu, &zcr_el3_reginfo);
+ }
+ }
}
void arm_cpu_register_gdb_regs_for_features(ARMCPU *cpu)
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 = arm_env_get_cpu(env);
return target_el;
}
-static void v7m_push(CPUARMState *env, uint32_t val)
+static bool v7m_stack_write(ARMCPU *cpu, uint32_t addr, uint32_t value,
+ ARMMMUIdx mmu_idx, bool ignfault)
{
- CPUState *cs = CPU(arm_env_get_cpu(env));
+ CPUState *cs = CPU(cpu);
+ CPUARMState *env = &cpu->env;
+ MemTxAttrs attrs = {};
+ MemTxResult txres;
+ target_ulong page_size;
+ hwaddr physaddr;
+ int prot;
+ ARMMMUFaultInfo fi;
+ bool secure = mmu_idx & ARM_MMU_IDX_M_S;
+ int exc;
+ bool exc_secure;
+
+ if (get_phys_addr(env, addr, MMU_DATA_STORE, mmu_idx, &physaddr,
+ &attrs, &prot, &page_size, &fi, NULL)) {
+ /* MPU/SAU lookup failed */
+ if (fi.type == ARMFault_QEMU_SFault) {
+ qemu_log_mask(CPU_LOG_INT,
+ "...SecureFault with SFSR.AUVIOL during stacking\n");
+ env->v7m.sfsr |= R_V7M_SFSR_AUVIOL_MASK | R_V7M_SFSR_SFARVALID_MASK;
+ env->v7m.sfar = addr;
+ exc = ARMV7M_EXCP_SECURE;
+ exc_secure = false;
+ } else {
+ qemu_log_mask(CPU_LOG_INT, "...MemManageFault with CFSR.MSTKERR\n");
+ env->v7m.cfsr[secure] |= R_V7M_CFSR_MSTKERR_MASK;
+ exc = ARMV7M_EXCP_MEM;
+ exc_secure = secure;
+ }
+ goto pend_fault;
+ }
+ address_space_stl_le(arm_addressspace(cs, attrs), physaddr, value,
+ attrs, &txres);
+ if (txres != MEMTX_OK) {
+ /* BusFault trying to write the data */
+ qemu_log_mask(CPU_LOG_INT, "...BusFault with BFSR.STKERR\n");
+ env->v7m.cfsr[M_REG_NS] |= R_V7M_CFSR_STKERR_MASK;
+ exc = ARMV7M_EXCP_BUS;
+ exc_secure = false;
+ goto pend_fault;
+ }
+ return true;
+
+pend_fault:
+ /* By pending the exception at this point we are making
+ * the IMPDEF choice "overridden exceptions pended" (see the
+ * MergeExcInfo() pseudocode). The other choice would be to not
+ * pend them now and then make a choice about which to throw away
+ * later if we have two derived exceptions.
+ * The only case when we must not pend the exception but instead
+ * throw it away is if we are doing the push of the callee registers
+ * and we've already generated a derived exception. Even in this
+ * case we will still update the fault status registers.
+ */
+ if (!ignfault) {
+ armv7m_nvic_set_pending_derived(env->nvic, exc, exc_secure);
+ }
+ return false;
+}
+
+static bool v7m_stack_read(ARMCPU *cpu, uint32_t *dest, uint32_t addr,
+ ARMMMUIdx mmu_idx)
+{
+ CPUState *cs = CPU(cpu);
+ CPUARMState *env = &cpu->env;
+ MemTxAttrs attrs = {};
+ MemTxResult txres;
+ target_ulong page_size;
+ hwaddr physaddr;
+ int prot;
+ ARMMMUFaultInfo fi;
+ bool secure = mmu_idx & ARM_MMU_IDX_M_S;
+ int exc;
+ bool exc_secure;
+ uint32_t value;
+
+ if (get_phys_addr(env, addr, MMU_DATA_LOAD, mmu_idx, &physaddr,
+ &attrs, &prot, &page_size, &fi, NULL)) {
+ /* MPU/SAU lookup failed */
+ if (fi.type == ARMFault_QEMU_SFault) {
+ qemu_log_mask(CPU_LOG_INT,
+ "...SecureFault with SFSR.AUVIOL during unstack\n");
+ env->v7m.sfsr |= R_V7M_SFSR_AUVIOL_MASK | R_V7M_SFSR_SFARVALID_MASK;
+ env->v7m.sfar = addr;
+ exc = ARMV7M_EXCP_SECURE;
+ exc_secure = false;
+ } else {
+ qemu_log_mask(CPU_LOG_INT,
+ "...MemManageFault with CFSR.MUNSTKERR\n");
+ env->v7m.cfsr[secure] |= R_V7M_CFSR_MUNSTKERR_MASK;
+ exc = ARMV7M_EXCP_MEM;
+ exc_secure = secure;
+ }
+ goto pend_fault;
+ }
+
+ value = address_space_ldl(arm_addressspace(cs, attrs), physaddr,
+ attrs, &txres);
+ if (txres != MEMTX_OK) {
+ /* BusFault trying to read the data */
+ qemu_log_mask(CPU_LOG_INT, "...BusFault with BFSR.UNSTKERR\n");
+ env->v7m.cfsr[M_REG_NS] |= R_V7M_CFSR_UNSTKERR_MASK;
+ exc = ARMV7M_EXCP_BUS;
+ exc_secure = false;
+ goto pend_fault;
+ }
+
+ *dest = value;
+ return true;
- env->regs[13] -= 4;
- stl_phys(cs->as, env->regs[13], val);
+pend_fault:
+ /* By pending the exception at this point we are making
+ * the IMPDEF choice "overridden exceptions pended" (see the
+ * MergeExcInfo() pseudocode). The other choice would be to not
+ * pend them now and then make a choice about which to throw away
+ * later if we have two derived exceptions.
+ */
+ armv7m_nvic_set_pending(env->nvic, exc, exc_secure);
+ return false;
}
/* Return true if we're using the process stack pointer (not the MSP) */
}
}
-static uint32_t arm_v7m_load_vector(ARMCPU *cpu, bool targets_secure)
+static bool arm_v7m_load_vector(ARMCPU *cpu, int exc, bool targets_secure,
+ uint32_t *pvec)
{
CPUState *cs = CPU(cpu);
CPUARMState *env = &cpu->env;
MemTxResult result;
- hwaddr vec = env->v7m.vecbase[targets_secure] + env->v7m.exception * 4;
- uint32_t addr;
+ uint32_t addr = env->v7m.vecbase[targets_secure] + exc * 4;
+ uint32_t vector_entry;
+ MemTxAttrs attrs = {};
+ ARMMMUIdx mmu_idx;
+ bool exc_secure;
+
+ mmu_idx = arm_v7m_mmu_idx_for_secstate_and_priv(env, targets_secure, true);
+
+ /* We don't do a get_phys_addr() here because the rules for vector
+ * loads are special: they always use the default memory map, and
+ * the default memory map permits reads from all addresses.
+ * Since there's no easy way to pass through to pmsav8_mpu_lookup()
+ * that we want this special case which would always say "yes",
+ * we just do the SAU lookup here followed by a direct physical load.
+ */
+ attrs.secure = targets_secure;
+ attrs.user = false;
+
+ if (arm_feature(env, ARM_FEATURE_M_SECURITY)) {
+ V8M_SAttributes sattrs = {};
+
+ v8m_security_lookup(env, addr, MMU_DATA_LOAD, mmu_idx, &sattrs);
+ if (sattrs.ns) {
+ attrs.secure = false;
+ } else if (!targets_secure) {
+ /* NS access to S memory */
+ goto load_fail;
+ }
+ }
- addr = address_space_ldl(cs->as, vec,
- MEMTXATTRS_UNSPECIFIED, &result);
+ vector_entry = address_space_ldl(arm_addressspace(cs, attrs), addr,
+ attrs, &result);
if (result != MEMTX_OK) {
- /* Architecturally this should cause a HardFault setting HSFR.VECTTBL,
- * which would then be immediately followed by our failing to load
- * the entry vector for that HardFault, which is a Lockup case.
- * Since we don't model Lockup, we just report this guest error
- * via cpu_abort().
- */
- cpu_abort(cs, "Failed to read from %s exception vector table "
- "entry %08x\n", targets_secure ? "secure" : "nonsecure",
- (unsigned)vec);
+ goto load_fail;
}
- return addr;
+ *pvec = vector_entry;
+ return true;
+
+load_fail:
+ /* All vector table fetch fails are reported as HardFault, with
+ * HFSR.VECTTBL and .FORCED set. (FORCED is set because
+ * technically the underlying exception is a MemManage or BusFault
+ * that is escalated to HardFault.) This is a terminal exception,
+ * so we will either take the HardFault immediately or else enter
+ * lockup (the latter case is handled in armv7m_nvic_set_pending_derived()).
+ */
+ exc_secure = targets_secure ||
+ !(cpu->env.v7m.aircr & R_V7M_AIRCR_BFHFNMINS_MASK);
+ env->v7m.hfsr |= R_V7M_HFSR_VECTTBL_MASK | R_V7M_HFSR_FORCED_MASK;
+ armv7m_nvic_set_pending_derived(env->nvic, ARMV7M_EXCP_HARD, exc_secure);
+ return false;
}
-static void v7m_push_callee_stack(ARMCPU *cpu, uint32_t lr, bool dotailchain)
+static bool v7m_push_callee_stack(ARMCPU *cpu, uint32_t lr, bool dotailchain,
+ bool ignore_faults)
{
/* For v8M, push the callee-saves register part of the stack frame.
* Compare the v8M pseudocode PushCalleeStack().
* In the tailchaining case this may not be the current stack.
*/
CPUARMState *env = &cpu->env;
- CPUState *cs = CPU(cpu);
uint32_t *frame_sp_p;
uint32_t frameptr;
+ ARMMMUIdx mmu_idx;
+ bool stacked_ok;
if (dotailchain) {
- frame_sp_p = get_v7m_sp_ptr(env, true,
- lr & R_V7M_EXCRET_MODE_MASK,
+ bool mode = lr & R_V7M_EXCRET_MODE_MASK;
+ bool priv = !(env->v7m.control[M_REG_S] & R_V7M_CONTROL_NPRIV_MASK) ||
+ !mode;
+
+ mmu_idx = arm_v7m_mmu_idx_for_secstate_and_priv(env, M_REG_S, priv);
+ frame_sp_p = get_v7m_sp_ptr(env, M_REG_S, mode,
lr & R_V7M_EXCRET_SPSEL_MASK);
} else {
+ mmu_idx = core_to_arm_mmu_idx(env, cpu_mmu_index(env, false));
frame_sp_p = &env->regs[13];
}
frameptr = *frame_sp_p - 0x28;
- stl_phys(cs->as, frameptr, 0xfefa125b);
- stl_phys(cs->as, frameptr + 0x8, env->regs[4]);
- stl_phys(cs->as, frameptr + 0xc, env->regs[5]);
- stl_phys(cs->as, frameptr + 0x10, env->regs[6]);
- stl_phys(cs->as, frameptr + 0x14, env->regs[7]);
- stl_phys(cs->as, frameptr + 0x18, env->regs[8]);
- stl_phys(cs->as, frameptr + 0x1c, env->regs[9]);
- stl_phys(cs->as, frameptr + 0x20, env->regs[10]);
- stl_phys(cs->as, frameptr + 0x24, env->regs[11]);
-
+ /* Write as much of the stack frame as we can. A write failure may
+ * cause us to pend a derived exception.
+ */
+ stacked_ok =
+ v7m_stack_write(cpu, frameptr, 0xfefa125b, mmu_idx, ignore_faults) &&
+ v7m_stack_write(cpu, frameptr + 0x8, env->regs[4], mmu_idx,
+ ignore_faults) &&
+ v7m_stack_write(cpu, frameptr + 0xc, env->regs[5], mmu_idx,
+ ignore_faults) &&
+ v7m_stack_write(cpu, frameptr + 0x10, env->regs[6], mmu_idx,
+ ignore_faults) &&
+ v7m_stack_write(cpu, frameptr + 0x14, env->regs[7], mmu_idx,
+ ignore_faults) &&
+ v7m_stack_write(cpu, frameptr + 0x18, env->regs[8], mmu_idx,
+ ignore_faults) &&
+ v7m_stack_write(cpu, frameptr + 0x1c, env->regs[9], mmu_idx,
+ ignore_faults) &&
+ v7m_stack_write(cpu, frameptr + 0x20, env->regs[10], mmu_idx,
+ ignore_faults) &&
+ v7m_stack_write(cpu, frameptr + 0x24, env->regs[11], mmu_idx,
+ ignore_faults);
+
+ /* Update SP regardless of whether any of the stack accesses failed.
+ * When we implement v8M stack limit checking then this attempt to
+ * update SP might also fail and result in a derived exception.
+ */
*frame_sp_p = frameptr;
+
+ return !stacked_ok;
}
-static void v7m_exception_taken(ARMCPU *cpu, uint32_t lr, bool dotailchain)
+static void v7m_exception_taken(ARMCPU *cpu, uint32_t lr, bool dotailchain,
+ bool ignore_stackfaults)
{
/* Do the "take the exception" parts of exception entry,
* but not the pushing of state to the stack. This is
CPUARMState *env = &cpu->env;
uint32_t addr;
bool targets_secure;
+ int exc;
+ bool push_failed = false;
- targets_secure = armv7m_nvic_acknowledge_irq(env->nvic);
+ armv7m_nvic_get_pending_irq_info(env->nvic, &exc, &targets_secure);
if (arm_feature(env, ARM_FEATURE_V8)) {
if (arm_feature(env, ARM_FEATURE_M_SECURITY) &&
*/
if (lr & R_V7M_EXCRET_DCRS_MASK &&
!(dotailchain && (lr & R_V7M_EXCRET_ES_MASK))) {
- v7m_push_callee_stack(cpu, lr, dotailchain);
+ push_failed = v7m_push_callee_stack(cpu, lr, dotailchain,
+ ignore_stackfaults);
}
lr |= R_V7M_EXCRET_DCRS_MASK;
}
}
}
+ if (push_failed && !ignore_stackfaults) {
+ /* Derived exception on callee-saves register stacking:
+ * we might now want to take a different exception which
+ * targets a different security state, so try again from the top.
+ */
+ v7m_exception_taken(cpu, lr, true, true);
+ return;
+ }
+
+ if (!arm_v7m_load_vector(cpu, exc, targets_secure, &addr)) {
+ /* Vector load failed: derived exception */
+ v7m_exception_taken(cpu, lr, true, true);
+ return;
+ }
+
+ /* Now we've done everything that might cause a derived exception
+ * we can go ahead and activate whichever exception we're going to
+ * take (which might now be the derived exception).
+ */
+ armv7m_nvic_acknowledge_irq(env->nvic);
+
/* Switch to target security state -- must do this before writing SPSEL */
switch_v7m_security_state(env, targets_secure);
write_v7m_control_spsel(env, 0);
/* Clear IT bits */
env->condexec_bits = 0;
env->regs[14] = lr;
- addr = arm_v7m_load_vector(cpu, targets_secure);
env->regs[15] = addr & 0xfffffffe;
env->thumb = addr & 1;
}
-static void v7m_push_stack(ARMCPU *cpu)
+static bool v7m_push_stack(ARMCPU *cpu)
{
/* Do the "set up stack frame" part of exception entry,
* similar to pseudocode PushStack().
+ * Return true if we generate a derived exception (and so
+ * should ignore further stack faults trying to process
+ * that derived exception.)
*/
+ bool stacked_ok;
CPUARMState *env = &cpu->env;
uint32_t xpsr = xpsr_read(env);
+ uint32_t frameptr = env->regs[13];
+ ARMMMUIdx mmu_idx = core_to_arm_mmu_idx(env, cpu_mmu_index(env, false));
/* Align stack pointer if the guest wants that */
- if ((env->regs[13] & 4) &&
+ if ((frameptr & 4) &&
(env->v7m.ccr[env->v7m.secure] & R_V7M_CCR_STKALIGN_MASK)) {
- env->regs[13] -= 4;
+ frameptr -= 4;
xpsr |= XPSR_SPREALIGN;
}
- /* Switch to the handler mode. */
- v7m_push(env, xpsr);
- v7m_push(env, env->regs[15]);
- v7m_push(env, env->regs[14]);
- v7m_push(env, env->regs[12]);
- v7m_push(env, env->regs[3]);
- v7m_push(env, env->regs[2]);
- v7m_push(env, env->regs[1]);
- v7m_push(env, env->regs[0]);
+
+ frameptr -= 0x20;
+
+ /* Write as much of the stack frame as we can. If we fail a stack
+ * write this will result in a derived exception being pended
+ * (which may be taken in preference to the one we started with
+ * if it has higher priority).
+ */
+ stacked_ok =
+ v7m_stack_write(cpu, frameptr, env->regs[0], mmu_idx, false) &&
+ v7m_stack_write(cpu, frameptr + 4, env->regs[1], mmu_idx, false) &&
+ v7m_stack_write(cpu, frameptr + 8, env->regs[2], mmu_idx, false) &&
+ v7m_stack_write(cpu, frameptr + 12, env->regs[3], mmu_idx, false) &&
+ v7m_stack_write(cpu, frameptr + 16, env->regs[12], mmu_idx, false) &&
+ v7m_stack_write(cpu, frameptr + 20, env->regs[14], mmu_idx, false) &&
+ v7m_stack_write(cpu, frameptr + 24, env->regs[15], mmu_idx, false) &&
+ v7m_stack_write(cpu, frameptr + 28, xpsr, mmu_idx, false);
+
+ /* Update SP regardless of whether any of the stack accesses failed.
+ * When we implement v8M stack limit checking then this attempt to
+ * update SP might also fail and result in a derived exception.
+ */
+ env->regs[13] = frameptr;
+
+ return !stacked_ok;
}
static void do_v7m_exception_exit(ARMCPU *cpu)
if (sfault) {
env->v7m.sfsr |= R_V7M_SFSR_INVER_MASK;
armv7m_nvic_set_pending(env->nvic, ARMV7M_EXCP_SECURE, false);
- v7m_exception_taken(cpu, excret, true);
+ v7m_exception_taken(cpu, excret, true, false);
qemu_log_mask(CPU_LOG_INT, "...taking SecureFault on existing "
"stackframe: failed EXC_RETURN.ES validity check\n");
return;
*/
env->v7m.cfsr[env->v7m.secure] |= R_V7M_CFSR_INVPC_MASK;
armv7m_nvic_set_pending(env->nvic, ARMV7M_EXCP_USAGE, env->v7m.secure);
- v7m_exception_taken(cpu, excret, true);
+ v7m_exception_taken(cpu, excret, true, false);
qemu_log_mask(CPU_LOG_INT, "...taking UsageFault on existing "
"stackframe: failed exception return integrity check\n");
return;
!return_to_handler,
return_to_sp_process);
uint32_t frameptr = *frame_sp_p;
+ bool pop_ok = true;
+ ARMMMUIdx mmu_idx;
+
+ mmu_idx = arm_v7m_mmu_idx_for_secstate_and_priv(env, return_to_secure,
+ !return_to_handler);
if (!QEMU_IS_ALIGNED(frameptr, 8) &&
arm_feature(env, ARM_FEATURE_V8)) {
/* Take a SecureFault on the current stack */
env->v7m.sfsr |= R_V7M_SFSR_INVIS_MASK;
armv7m_nvic_set_pending(env->nvic, ARMV7M_EXCP_SECURE, false);
- v7m_exception_taken(cpu, excret, true);
+ v7m_exception_taken(cpu, excret, true, false);
qemu_log_mask(CPU_LOG_INT, "...taking SecureFault on existing "
"stackframe: failed exception return integrity "
"signature check\n");
return;
}
- env->regs[4] = ldl_phys(cs->as, frameptr + 0x8);
- env->regs[5] = ldl_phys(cs->as, frameptr + 0xc);
- env->regs[6] = ldl_phys(cs->as, frameptr + 0x10);
- env->regs[7] = ldl_phys(cs->as, frameptr + 0x14);
- env->regs[8] = ldl_phys(cs->as, frameptr + 0x18);
- env->regs[9] = ldl_phys(cs->as, frameptr + 0x1c);
- env->regs[10] = ldl_phys(cs->as, frameptr + 0x20);
- env->regs[11] = ldl_phys(cs->as, frameptr + 0x24);
+ pop_ok =
+ v7m_stack_read(cpu, &env->regs[4], frameptr + 0x8, mmu_idx) &&
+ v7m_stack_read(cpu, &env->regs[4], frameptr + 0x8, mmu_idx) &&
+ v7m_stack_read(cpu, &env->regs[5], frameptr + 0xc, mmu_idx) &&
+ v7m_stack_read(cpu, &env->regs[6], frameptr + 0x10, mmu_idx) &&
+ v7m_stack_read(cpu, &env->regs[7], frameptr + 0x14, mmu_idx) &&
+ v7m_stack_read(cpu, &env->regs[8], frameptr + 0x18, mmu_idx) &&
+ v7m_stack_read(cpu, &env->regs[9], frameptr + 0x1c, mmu_idx) &&
+ v7m_stack_read(cpu, &env->regs[10], frameptr + 0x20, mmu_idx) &&
+ v7m_stack_read(cpu, &env->regs[11], frameptr + 0x24, mmu_idx);
frameptr += 0x28;
}
- /* Pop registers. TODO: make these accesses use the correct
- * attributes and address space (S/NS, priv/unpriv) and handle
- * memory transaction failures.
- */
- env->regs[0] = ldl_phys(cs->as, frameptr);
- env->regs[1] = ldl_phys(cs->as, frameptr + 0x4);
- env->regs[2] = ldl_phys(cs->as, frameptr + 0x8);
- env->regs[3] = ldl_phys(cs->as, frameptr + 0xc);
- env->regs[12] = ldl_phys(cs->as, frameptr + 0x10);
- env->regs[14] = ldl_phys(cs->as, frameptr + 0x14);
- env->regs[15] = ldl_phys(cs->as, frameptr + 0x18);
+ /* Pop registers */
+ pop_ok = pop_ok &&
+ v7m_stack_read(cpu, &env->regs[0], frameptr, mmu_idx) &&
+ v7m_stack_read(cpu, &env->regs[1], frameptr + 0x4, mmu_idx) &&
+ v7m_stack_read(cpu, &env->regs[2], frameptr + 0x8, mmu_idx) &&
+ v7m_stack_read(cpu, &env->regs[3], frameptr + 0xc, mmu_idx) &&
+ v7m_stack_read(cpu, &env->regs[12], frameptr + 0x10, mmu_idx) &&
+ v7m_stack_read(cpu, &env->regs[14], frameptr + 0x14, mmu_idx) &&
+ v7m_stack_read(cpu, &env->regs[15], frameptr + 0x18, mmu_idx) &&
+ v7m_stack_read(cpu, &xpsr, frameptr + 0x1c, mmu_idx);
+
+ if (!pop_ok) {
+ /* v7m_stack_read() pended a fault, so take it (as a tail
+ * chained exception on the same stack frame)
+ */
+ v7m_exception_taken(cpu, excret, true, false);
+ return;
+ }
/* Returning from an exception with a PC with bit 0 set is defined
* behaviour on v8M (bit 0 is ignored), but for v7M it was specified
}
}
- xpsr = ldl_phys(cs->as, frameptr + 0x1c);
-
if (arm_feature(env, ARM_FEATURE_V8)) {
/* For v8M we have to check whether the xPSR exception field
* matches the EXCRET value for return to handler/thread
armv7m_nvic_set_pending(env->nvic, ARMV7M_EXCP_USAGE,
env->v7m.secure);
env->v7m.cfsr[env->v7m.secure] |= R_V7M_CFSR_INVPC_MASK;
- v7m_exception_taken(cpu, excret, true);
+ v7m_exception_taken(cpu, excret, true, false);
qemu_log_mask(CPU_LOG_INT, "...taking UsageFault on existing "
"stackframe: failed exception return integrity "
"check\n");
/* Take an INVPC UsageFault by pushing the stack again;
* we know we're v7M so this is never a Secure UsageFault.
*/
+ bool ignore_stackfaults;
+
assert(!arm_feature(env, ARM_FEATURE_V8));
armv7m_nvic_set_pending(env->nvic, ARMV7M_EXCP_USAGE, false);
env->v7m.cfsr[env->v7m.secure] |= R_V7M_CFSR_INVPC_MASK;
- v7m_push_stack(cpu);
- v7m_exception_taken(cpu, excret, false);
+ ignore_stackfaults = v7m_push_stack(cpu);
+ v7m_exception_taken(cpu, excret, false, ignore_stackfaults);
qemu_log_mask(CPU_LOG_INT, "...taking UsageFault on new stackframe: "
"failed exception return integrity check\n");
return;
target_ulong page_size;
hwaddr physaddr;
int prot;
- uint32_t fsr;
v8m_security_lookup(env, addr, MMU_INST_FETCH, mmu_idx, &sattrs);
if (!sattrs.nsc || sattrs.ns) {
return false;
}
if (get_phys_addr(env, addr, MMU_INST_FETCH, mmu_idx,
- &physaddr, &attrs, &prot, &page_size, &fsr, &fi, NULL)) {
+ &physaddr, &attrs, &prot, &page_size, &fi, NULL)) {
/* the MPU lookup failed */
env->v7m.cfsr[env->v7m.secure] |= R_V7M_CFSR_IACCVIOL_MASK;
armv7m_nvic_set_pending(env->nvic, ARMV7M_EXCP_MEM, env->v7m.secure);
ARMCPU *cpu = ARM_CPU(cs);
CPUARMState *env = &cpu->env;
uint32_t lr;
+ bool ignore_stackfaults;
arm_log_exception(cs->exception_index);
lr |= R_V7M_EXCRET_MODE_MASK;
}
- v7m_push_stack(cpu);
- v7m_exception_taken(cpu, lr, false);
+ ignore_stackfaults = v7m_push_stack(cpu);
+ v7m_exception_taken(cpu, lr, false, ignore_stackfaults);
qemu_log_mask(CPU_LOG_INT, "... as %d\n", env->v7m.exception);
}
offset = 0;
break;
case EXCP_BKPT:
- env->exception.fsr = 2;
/* Fall through to prefetch abort. */
case EXCP_PREFETCH_ABORT:
A32_BANKED_CURRENT_REG_SET(env, ifsr, env->exception.fsr);
case ARMMMUIdx_S1SE1:
case ARMMMUIdx_S1NSE0:
case ARMMMUIdx_S1NSE1:
+ case ARMMMUIdx_MPrivNegPri:
+ case ARMMMUIdx_MUserNegPri:
case ARMMMUIdx_MPriv:
- case ARMMMUIdx_MNegPri:
case ARMMMUIdx_MUser:
+ case ARMMMUIdx_MSPrivNegPri:
+ case ARMMMUIdx_MSUserNegPri:
case ARMMMUIdx_MSPriv:
- case ARMMMUIdx_MSNegPri:
case ARMMMUIdx_MSUser:
return 1;
default:
(R_V7M_MPU_CTRL_ENABLE_MASK | R_V7M_MPU_CTRL_HFNMIENA_MASK)) {
case R_V7M_MPU_CTRL_ENABLE_MASK:
/* Enabled, but not for HardFault and NMI */
- return mmu_idx == ARMMMUIdx_MNegPri ||
- mmu_idx == ARMMMUIdx_MSNegPri;
+ return mmu_idx & ARM_MMU_IDX_M_NEGPRI;
case R_V7M_MPU_CTRL_ENABLE_MASK | R_V7M_MPU_CTRL_HFNMIENA_MASK:
/* Enabled for all cases */
return false;
case ARMMMUIdx_S1SE0:
case ARMMMUIdx_S1NSE0:
case ARMMMUIdx_MUser:
+ case ARMMMUIdx_MSUser:
+ case ARMMMUIdx_MUserNegPri:
+ case ARMMMUIdx_MSUserNegPri:
return true;
default:
return false;
/* Translate a S1 pagetable walk through S2 if needed. */
static hwaddr S1_ptw_translate(CPUARMState *env, ARMMMUIdx mmu_idx,
hwaddr addr, MemTxAttrs txattrs,
- uint32_t *fsr,
ARMMMUFaultInfo *fi)
{
if ((mmu_idx == ARMMMUIdx_S1NSE0 || mmu_idx == ARMMMUIdx_S1NSE1) &&
int ret;
ret = get_phys_addr_lpae(env, addr, 0, ARMMMUIdx_S2NS, &s2pa,
- &txattrs, &s2prot, &s2size, fsr, fi, NULL);
+ &txattrs, &s2prot, &s2size, fi, NULL);
if (ret) {
+ assert(fi->type != ARMFault_None);
fi->s2addr = addr;
fi->stage2 = true;
fi->s1ptw = true;
* (but not if it was for a debug access).
*/
static uint32_t arm_ldl_ptw(CPUState *cs, hwaddr addr, bool is_secure,
- ARMMMUIdx mmu_idx, uint32_t *fsr,
- ARMMMUFaultInfo *fi)
+ ARMMMUIdx mmu_idx, ARMMMUFaultInfo *fi)
{
ARMCPU *cpu = ARM_CPU(cs);
CPUARMState *env = &cpu->env;
MemTxAttrs attrs = {};
+ MemTxResult result = MEMTX_OK;
AddressSpace *as;
+ uint32_t data;
attrs.secure = is_secure;
as = arm_addressspace(cs, attrs);
- addr = S1_ptw_translate(env, mmu_idx, addr, attrs, fsr, fi);
+ addr = S1_ptw_translate(env, mmu_idx, addr, attrs, fi);
if (fi->s1ptw) {
return 0;
}
if (regime_translation_big_endian(env, mmu_idx)) {
- return address_space_ldl_be(as, addr, attrs, NULL);
+ data = address_space_ldl_be(as, addr, attrs, &result);
} else {
- return address_space_ldl_le(as, addr, attrs, NULL);
+ data = address_space_ldl_le(as, addr, attrs, &result);
+ }
+ if (result == MEMTX_OK) {
+ return data;
}
+ fi->type = ARMFault_SyncExternalOnWalk;
+ fi->ea = arm_extabort_type(result);
+ return 0;
}
static uint64_t arm_ldq_ptw(CPUState *cs, hwaddr addr, bool is_secure,
- ARMMMUIdx mmu_idx, uint32_t *fsr,
- ARMMMUFaultInfo *fi)
+ ARMMMUIdx mmu_idx, ARMMMUFaultInfo *fi)
{
ARMCPU *cpu = ARM_CPU(cs);
CPUARMState *env = &cpu->env;
MemTxAttrs attrs = {};
+ MemTxResult result = MEMTX_OK;
AddressSpace *as;
+ uint64_t data;
attrs.secure = is_secure;
as = arm_addressspace(cs, attrs);
- addr = S1_ptw_translate(env, mmu_idx, addr, attrs, fsr, fi);
+ addr = S1_ptw_translate(env, mmu_idx, addr, attrs, fi);
if (fi->s1ptw) {
return 0;
}
if (regime_translation_big_endian(env, mmu_idx)) {
- return address_space_ldq_be(as, addr, attrs, NULL);
+ data = address_space_ldq_be(as, addr, attrs, &result);
} else {
- return address_space_ldq_le(as, addr, attrs, NULL);
+ data = address_space_ldq_le(as, addr, attrs, &result);
+ }
+ if (result == MEMTX_OK) {
+ return data;
}
+ fi->type = ARMFault_SyncExternalOnWalk;
+ fi->ea = arm_extabort_type(result);
+ return 0;
}
static bool get_phys_addr_v5(CPUARMState *env, uint32_t address,
MMUAccessType access_type, ARMMMUIdx mmu_idx,
hwaddr *phys_ptr, int *prot,
- target_ulong *page_size, uint32_t *fsr,
+ target_ulong *page_size,
ARMMMUFaultInfo *fi)
{
CPUState *cs = CPU(arm_env_get_cpu(env));
- int code;
+ int level = 1;
uint32_t table;
uint32_t desc;
int type;
/* Lookup l1 descriptor. */
if (!get_level1_table_address(env, mmu_idx, &table, address)) {
/* Section translation fault if page walk is disabled by PD0 or PD1 */
- code = 5;
+ fi->type = ARMFault_Translation;
goto do_fault;
}
desc = arm_ldl_ptw(cs, table, regime_is_secure(env, mmu_idx),
- mmu_idx, fsr, fi);
+ mmu_idx, fi);
+ if (fi->type != ARMFault_None) {
+ goto do_fault;
+ }
type = (desc & 3);
domain = (desc >> 5) & 0x0f;
if (regime_el(env, mmu_idx) == 1) {
domain_prot = (dacr >> (domain * 2)) & 3;
if (type == 0) {
/* Section translation fault. */
- code = 5;
+ fi->type = ARMFault_Translation;
goto do_fault;
}
+ if (type != 2) {
+ level = 2;
+ }
if (domain_prot == 0 || domain_prot == 2) {
- if (type == 2)
- code = 9; /* Section domain fault. */
- else
- code = 11; /* Page domain fault. */
+ fi->type = ARMFault_Domain;
goto do_fault;
}
if (type == 2) {
/* 1Mb section. */
phys_addr = (desc & 0xfff00000) | (address & 0x000fffff);
ap = (desc >> 10) & 3;
- code = 13;
*page_size = 1024 * 1024;
} else {
/* Lookup l2 entry. */
table = (desc & 0xfffff000) | ((address >> 8) & 0xffc);
}
desc = arm_ldl_ptw(cs, table, regime_is_secure(env, mmu_idx),
- mmu_idx, fsr, fi);
+ mmu_idx, fi);
+ if (fi->type != ARMFault_None) {
+ goto do_fault;
+ }
switch (desc & 3) {
case 0: /* Page translation fault. */
- code = 7;
+ fi->type = ARMFault_Translation;
goto do_fault;
case 1: /* 64k page. */
phys_addr = (desc & 0xffff0000) | (address & 0xffff);
/* UNPREDICTABLE in ARMv5; we choose to take a
* page translation fault.
*/
- code = 7;
+ fi->type = ARMFault_Translation;
goto do_fault;
}
} else {
/* Never happens, but compiler isn't smart enough to tell. */
abort();
}
- code = 15;
}
*prot = ap_to_rw_prot(env, mmu_idx, ap, domain_prot);
*prot |= *prot ? PAGE_EXEC : 0;
if (!(*prot & (1 << access_type))) {
/* Access permission fault. */
+ fi->type = ARMFault_Permission;
goto do_fault;
}
*phys_ptr = phys_addr;
return false;
do_fault:
- *fsr = code | (domain << 4);
+ fi->domain = domain;
+ fi->level = level;
return true;
}
static bool get_phys_addr_v6(CPUARMState *env, uint32_t address,
MMUAccessType access_type, ARMMMUIdx mmu_idx,
hwaddr *phys_ptr, MemTxAttrs *attrs, int *prot,
- target_ulong *page_size, uint32_t *fsr,
- ARMMMUFaultInfo *fi)
+ target_ulong *page_size, ARMMMUFaultInfo *fi)
{
CPUState *cs = CPU(arm_env_get_cpu(env));
- int code;
+ int level = 1;
uint32_t table;
uint32_t desc;
uint32_t xn;
/* Lookup l1 descriptor. */
if (!get_level1_table_address(env, mmu_idx, &table, address)) {
/* Section translation fault if page walk is disabled by PD0 or PD1 */
- code = 5;
+ fi->type = ARMFault_Translation;
goto do_fault;
}
desc = arm_ldl_ptw(cs, table, regime_is_secure(env, mmu_idx),
- mmu_idx, fsr, fi);
+ mmu_idx, fi);
+ if (fi->type != ARMFault_None) {
+ goto do_fault;
+ }
type = (desc & 3);
if (type == 0 || (type == 3 && !arm_feature(env, ARM_FEATURE_PXN))) {
/* Section translation fault, or attempt to use the encoding
* which is Reserved on implementations without PXN.
*/
- code = 5;
+ fi->type = ARMFault_Translation;
goto do_fault;
}
if ((type == 1) || !(desc & (1 << 18))) {
} else {
dacr = env->cp15.dacr_s;
}
+ if (type == 1) {
+ level = 2;
+ }
domain_prot = (dacr >> (domain * 2)) & 3;
if (domain_prot == 0 || domain_prot == 2) {
- if (type != 1) {
- code = 9; /* Section domain fault. */
- } else {
- code = 11; /* Page domain fault. */
- }
+ /* Section or Page domain fault */
+ fi->type = ARMFault_Domain;
goto do_fault;
}
if (type != 1) {
ap = ((desc >> 10) & 3) | ((desc >> 13) & 4);
xn = desc & (1 << 4);
pxn = desc & 1;
- code = 13;
ns = extract32(desc, 19, 1);
} else {
if (arm_feature(env, ARM_FEATURE_PXN)) {
/* Lookup l2 entry. */
table = (desc & 0xfffffc00) | ((address >> 10) & 0x3fc);
desc = arm_ldl_ptw(cs, table, regime_is_secure(env, mmu_idx),
- mmu_idx, fsr, fi);
+ mmu_idx, fi);
+ if (fi->type != ARMFault_None) {
+ goto do_fault;
+ }
ap = ((desc >> 4) & 3) | ((desc >> 7) & 4);
switch (desc & 3) {
case 0: /* Page translation fault. */
- code = 7;
+ fi->type = ARMFault_Translation;
goto do_fault;
case 1: /* 64k page. */
phys_addr = (desc & 0xffff0000) | (address & 0xffff);
/* Never happens, but compiler isn't smart enough to tell. */
abort();
}
- code = 15;
}
if (domain_prot == 3) {
*prot = PAGE_READ | PAGE_WRITE | PAGE_EXEC;
if (pxn && !regime_is_user(env, mmu_idx)) {
xn = 1;
}
- if (xn && access_type == MMU_INST_FETCH)
+ if (xn && access_type == MMU_INST_FETCH) {
+ fi->type = ARMFault_Permission;
goto do_fault;
+ }
if (arm_feature(env, ARM_FEATURE_V6K) &&
(regime_sctlr(env, mmu_idx) & SCTLR_AFE)) {
/* The simplified model uses AP[0] as an access control bit. */
if ((ap & 1) == 0) {
/* Access flag fault. */
- code = (code == 15) ? 6 : 3;
+ fi->type = ARMFault_AccessFlag;
goto do_fault;
}
*prot = simple_ap_to_rw_prot(env, mmu_idx, ap >> 1);
}
if (!(*prot & (1 << access_type))) {
/* Access permission fault. */
+ fi->type = ARMFault_Permission;
goto do_fault;
}
}
*phys_ptr = phys_addr;
return false;
do_fault:
- *fsr = code | (domain << 4);
+ fi->domain = domain;
+ fi->level = level;
return true;
}
-/* Fault type for long-descriptor MMU fault reporting; this corresponds
- * to bits [5..2] in the STATUS field in long-format DFSR/IFSR.
- */
-typedef enum {
- translation_fault = 1,
- access_fault = 2,
- permission_fault = 3,
-} MMUFaultType;
-
/*
* check_s2_mmu_setup
* @cpu: ARMCPU
static bool get_phys_addr_lpae(CPUARMState *env, target_ulong address,
MMUAccessType access_type, ARMMMUIdx mmu_idx,
hwaddr *phys_ptr, MemTxAttrs *txattrs, int *prot,
- target_ulong *page_size_ptr, uint32_t *fsr,
+ target_ulong *page_size_ptr,
ARMMMUFaultInfo *fi, ARMCacheAttrs *cacheattrs)
{
ARMCPU *cpu = arm_env_get_cpu(env);
CPUState *cs = CPU(cpu);
/* Read an LPAE long-descriptor translation table. */
- MMUFaultType fault_type = translation_fault;
+ ARMFaultType fault_type = ARMFault_Translation;
uint32_t level;
uint32_t epd = 0;
int32_t t0sz, t1sz;
ttbr_select = 1;
} else {
/* in the gap between the two regions, this is a Translation fault */
- fault_type = translation_fault;
+ fault_type = ARMFault_Translation;
goto do_fault;
}
ok = check_s2_mmu_setup(cpu, aarch64, startlevel,
inputsize, stride);
if (!ok) {
- fault_type = translation_fault;
+ fault_type = ARMFault_Translation;
goto do_fault;
}
level = startlevel;
descaddr |= (address >> (stride * (4 - level))) & indexmask;
descaddr &= ~7ULL;
nstable = extract32(tableattrs, 4, 1);
- descriptor = arm_ldq_ptw(cs, descaddr, !nstable, mmu_idx, fsr, fi);
- if (fi->s1ptw) {
+ descriptor = arm_ldq_ptw(cs, descaddr, !nstable, mmu_idx, fi);
+ if (fi->type != ARMFault_None) {
goto do_fault;
}
/* Here descaddr is the final physical address, and attributes
* are all in attrs.
*/
- fault_type = access_fault;
+ fault_type = ARMFault_AccessFlag;
if ((attrs & (1 << 8)) == 0) {
/* Access flag */
goto do_fault;
*prot = get_S1prot(env, mmu_idx, aarch64, ap, ns, xn, pxn);
}
- fault_type = permission_fault;
+ fault_type = ARMFault_Permission;
if (!(*prot & (1 << access_type))) {
goto do_fault;
}
return false;
do_fault:
- /* Long-descriptor format IFSR/DFSR value */
- *fsr = (1 << 9) | (fault_type << 2) | level;
+ fi->type = fault_type;
+ fi->level = level;
/* Tag the error as S2 for failed S1 PTW at S2 or ordinary S2. */
fi->stage2 = fi->s1ptw || (mmu_idx == ARMMMUIdx_S2NS);
return true;
static bool get_phys_addr_pmsav7(CPUARMState *env, uint32_t address,
MMUAccessType access_type, ARMMMUIdx mmu_idx,
- hwaddr *phys_ptr, int *prot, uint32_t *fsr)
+ hwaddr *phys_ptr, int *prot,
+ ARMMMUFaultInfo *fi)
{
ARMCPU *cpu = arm_env_get_cpu(env);
int n;
}
if (rsize < TARGET_PAGE_BITS) {
qemu_log_mask(LOG_UNIMP,
- "DRSR[%d]: No support for MPU (sub)region "
- "alignment of %" PRIu32 " bits. Minimum is %d\n",
- n, rsize, TARGET_PAGE_BITS);
+ "DRSR[%d]: No support for MPU (sub)region size of"
+ " %" PRIu32 " bytes. Minimum is %d.\n",
+ n, (1 << rsize), TARGET_PAGE_SIZE);
continue;
}
if (srdis) {
if (n == -1) { /* no hits */
if (!pmsav7_use_background_region(cpu, mmu_idx, is_user)) {
/* background fault */
- *fsr = 0;
+ fi->type = ARMFault_Background;
return true;
}
get_phys_addr_pmsav7_default(env, mmu_idx, address, prot);
case 6:
*prot |= PAGE_READ | PAGE_EXEC;
break;
+ case 7:
+ /* for v7M, same as 6; for R profile a reserved value */
+ if (arm_feature(env, ARM_FEATURE_M)) {
+ *prot |= PAGE_READ | PAGE_EXEC;
+ break;
+ }
+ /* fall through */
default:
qemu_log_mask(LOG_GUEST_ERROR,
"DRACR[%d]: Bad value for AP bits: 0x%"
case 6:
*prot |= PAGE_READ | PAGE_EXEC;
break;
+ case 7:
+ /* for v7M, same as 6; for R profile a reserved value */
+ if (arm_feature(env, ARM_FEATURE_M)) {
+ *prot |= PAGE_READ | PAGE_EXEC;
+ break;
+ }
+ /* fall through */
default:
qemu_log_mask(LOG_GUEST_ERROR,
"DRACR[%d]: Bad value for AP bits: 0x%"
}
}
- *fsr = 0x00d; /* Permission fault */
+ fi->type = ARMFault_Permission;
+ fi->level = 1;
return !(*prot & (1 << access_type));
}
*/
ARMCPU *cpu = arm_env_get_cpu(env);
int r;
+ bool idau_exempt = false, idau_ns = true, idau_nsc = true;
+ int idau_region = IREGION_NOTVALID;
- /* TODO: implement IDAU */
+ if (cpu->idau) {
+ IDAUInterfaceClass *iic = IDAU_INTERFACE_GET_CLASS(cpu->idau);
+ IDAUInterface *ii = IDAU_INTERFACE(cpu->idau);
+
+ iic->check(ii, address, &idau_region, &idau_exempt, &idau_ns,
+ &idau_nsc);
+ }
if (access_type == MMU_INST_FETCH && extract32(address, 28, 4) == 0xf) {
/* 0xf0000000..0xffffffff is always S for insn fetches */
return;
}
- if (v8m_is_sau_exempt(env, address, access_type)) {
+ if (idau_exempt || v8m_is_sau_exempt(env, address, access_type)) {
sattrs->ns = !regime_is_secure(env, mmu_idx);
return;
}
+ if (idau_region != IREGION_NOTVALID) {
+ sattrs->irvalid = true;
+ sattrs->iregion = idau_region;
+ }
+
switch (env->sau.ctrl & 3) {
case 0: /* SAU.ENABLE == 0, SAU.ALLNS == 0 */
break;
}
}
- /* TODO when we support the IDAU then it may override the result here */
+ /* The IDAU will override the SAU lookup results if it specifies
+ * higher security than the SAU does.
+ */
+ if (!idau_ns) {
+ if (sattrs->ns || (!idau_nsc && sattrs->nsc)) {
+ sattrs->ns = false;
+ sattrs->nsc = idau_nsc;
+ }
+ }
break;
}
}
-static bool get_phys_addr_pmsav8(CPUARMState *env, uint32_t address,
- MMUAccessType access_type, ARMMMUIdx mmu_idx,
- hwaddr *phys_ptr, MemTxAttrs *txattrs,
- int *prot, uint32_t *fsr)
+static bool pmsav8_mpu_lookup(CPUARMState *env, uint32_t address,
+ MMUAccessType access_type, ARMMMUIdx mmu_idx,
+ hwaddr *phys_ptr, MemTxAttrs *txattrs,
+ int *prot, ARMMMUFaultInfo *fi, uint32_t *mregion)
{
+ /* Perform a PMSAv8 MPU lookup (without also doing the SAU check
+ * that a full phys-to-virt translation does).
+ * mregion is (if not NULL) set to the region number which matched,
+ * or -1 if no region number is returned (MPU off, address did not
+ * hit a region, address hit in multiple regions).
+ */
ARMCPU *cpu = arm_env_get_cpu(env);
bool is_user = regime_is_user(env, mmu_idx);
uint32_t secure = regime_is_secure(env, mmu_idx);
int n;
int matchregion = -1;
bool hit = false;
- V8M_SAttributes sattrs = {};
*phys_ptr = address;
*prot = 0;
-
- if (arm_feature(env, ARM_FEATURE_M_SECURITY)) {
- v8m_security_lookup(env, address, access_type, mmu_idx, &sattrs);
- if (access_type == MMU_INST_FETCH) {
- /* Instruction fetches always use the MMU bank and the
- * transaction attribute determined by the fetch address,
- * regardless of CPU state. This is painful for QEMU
- * to handle, because it would mean we need to encode
- * into the mmu_idx not just the (user, negpri) information
- * for the current security state but also that for the
- * other security state, which would balloon the number
- * of mmu_idx values needed alarmingly.
- * Fortunately we can avoid this because it's not actually
- * possible to arbitrarily execute code from memory with
- * the wrong security attribute: it will always generate
- * an exception of some kind or another, apart from the
- * special case of an NS CPU executing an SG instruction
- * in S&NSC memory. So we always just fail the translation
- * here and sort things out in the exception handler
- * (including possibly emulating an SG instruction).
- */
- if (sattrs.ns != !secure) {
- *fsr = sattrs.nsc ? M_FAKE_FSR_NSC_EXEC : M_FAKE_FSR_SFAULT;
- return true;
- }
- } else {
- /* For data accesses we always use the MMU bank indicated
- * by the current CPU state, but the security attributes
- * might downgrade a secure access to nonsecure.
- */
- if (sattrs.ns) {
- txattrs->secure = false;
- } else if (!secure) {
- /* NS access to S memory must fault.
- * Architecturally we should first check whether the
- * MPU information for this address indicates that we
- * are doing an unaligned access to Device memory, which
- * should generate a UsageFault instead. QEMU does not
- * currently check for that kind of unaligned access though.
- * If we added it we would need to do so as a special case
- * for M_FAKE_FSR_SFAULT in arm_v7m_cpu_do_interrupt().
- */
- *fsr = M_FAKE_FSR_SFAULT;
- return true;
- }
- }
+ if (mregion) {
+ *mregion = -1;
}
/* Unlike the ARM ARM pseudocode, we don't need to check whether this
/* Multiple regions match -- always a failure (unlike
* PMSAv7 where highest-numbered-region wins)
*/
- *fsr = 0x00d; /* permission fault */
+ fi->type = ARMFault_Permission;
+ fi->level = 1;
return true;
}
if (!hit) {
/* background fault */
- *fsr = 0;
+ fi->type = ARMFault_Background;
return true;
}
/* We don't need to look the attribute up in the MAIR0/MAIR1
* registers because that only tells us about cacheability.
*/
+ if (mregion) {
+ *mregion = matchregion;
+ }
}
- *fsr = 0x00d; /* Permission fault */
+ fi->type = ARMFault_Permission;
+ fi->level = 1;
return !(*prot & (1 << access_type));
}
+
+static bool get_phys_addr_pmsav8(CPUARMState *env, uint32_t address,
+ MMUAccessType access_type, ARMMMUIdx mmu_idx,
+ hwaddr *phys_ptr, MemTxAttrs *txattrs,
+ int *prot, ARMMMUFaultInfo *fi)
+{
+ uint32_t secure = regime_is_secure(env, mmu_idx);
+ V8M_SAttributes sattrs = {};
+
+ if (arm_feature(env, ARM_FEATURE_M_SECURITY)) {
+ v8m_security_lookup(env, address, access_type, mmu_idx, &sattrs);
+ if (access_type == MMU_INST_FETCH) {
+ /* Instruction fetches always use the MMU bank and the
+ * transaction attribute determined by the fetch address,
+ * regardless of CPU state. This is painful for QEMU
+ * to handle, because it would mean we need to encode
+ * into the mmu_idx not just the (user, negpri) information
+ * for the current security state but also that for the
+ * other security state, which would balloon the number
+ * of mmu_idx values needed alarmingly.
+ * Fortunately we can avoid this because it's not actually
+ * possible to arbitrarily execute code from memory with
+ * the wrong security attribute: it will always generate
+ * an exception of some kind or another, apart from the
+ * special case of an NS CPU executing an SG instruction
+ * in S&NSC memory. So we always just fail the translation
+ * here and sort things out in the exception handler
+ * (including possibly emulating an SG instruction).
+ */
+ if (sattrs.ns != !secure) {
+ if (sattrs.nsc) {
+ fi->type = ARMFault_QEMU_NSCExec;
+ } else {
+ fi->type = ARMFault_QEMU_SFault;
+ }
+ *phys_ptr = address;
+ *prot = 0;
+ return true;
+ }
+ } else {
+ /* For data accesses we always use the MMU bank indicated
+ * by the current CPU state, but the security attributes
+ * might downgrade a secure access to nonsecure.
+ */
+ if (sattrs.ns) {
+ txattrs->secure = false;
+ } else if (!secure) {
+ /* NS access to S memory must fault.
+ * Architecturally we should first check whether the
+ * MPU information for this address indicates that we
+ * are doing an unaligned access to Device memory, which
+ * should generate a UsageFault instead. QEMU does not
+ * currently check for that kind of unaligned access though.
+ * If we added it we would need to do so as a special case
+ * for M_FAKE_FSR_SFAULT in arm_v7m_cpu_do_interrupt().
+ */
+ fi->type = ARMFault_QEMU_SFault;
+ *phys_ptr = address;
+ *prot = 0;
+ return true;
+ }
+ }
+ }
+
+ return pmsav8_mpu_lookup(env, address, access_type, mmu_idx, phys_ptr,
+ txattrs, prot, fi, NULL);
+}
+
static bool get_phys_addr_pmsav5(CPUARMState *env, uint32_t address,
MMUAccessType access_type, ARMMMUIdx mmu_idx,
- hwaddr *phys_ptr, int *prot, uint32_t *fsr)
+ hwaddr *phys_ptr, int *prot,
+ ARMMMUFaultInfo *fi)
{
int n;
uint32_t mask;
}
}
if (n < 0) {
- *fsr = 2;
+ fi->type = ARMFault_Background;
return true;
}
mask = (mask >> (n * 4)) & 0xf;
switch (mask) {
case 0:
- *fsr = 1;
+ fi->type = ARMFault_Permission;
+ fi->level = 1;
return true;
case 1:
if (is_user) {
- *fsr = 1;
+ fi->type = ARMFault_Permission;
+ fi->level = 1;
return true;
}
*prot = PAGE_READ | PAGE_WRITE;
break;
case 5:
if (is_user) {
- *fsr = 1;
+ fi->type = ARMFault_Permission;
+ fi->level = 1;
return true;
}
*prot = PAGE_READ;
break;
default:
/* Bad permission. */
- *fsr = 1;
+ fi->type = ARMFault_Permission;
+ fi->level = 1;
return true;
}
*prot |= PAGE_EXEC;
* @attrs: set to the memory transaction attributes to use
* @prot: set to the permissions for the page containing phys_ptr
* @page_size: set to the size of the page containing phys_ptr
- * @fsr: set to the DFSR/IFSR value on failure
* @fi: set to fault info if the translation fails
* @cacheattrs: (if non-NULL) set to the cacheability/shareability attributes
*/
static bool get_phys_addr(CPUARMState *env, target_ulong address,
MMUAccessType access_type, ARMMMUIdx mmu_idx,
hwaddr *phys_ptr, MemTxAttrs *attrs, int *prot,
- target_ulong *page_size, uint32_t *fsr,
+ target_ulong *page_size,
ARMMMUFaultInfo *fi, ARMCacheAttrs *cacheattrs)
{
if (mmu_idx == ARMMMUIdx_S12NSE0 || mmu_idx == ARMMMUIdx_S12NSE1) {
ret = get_phys_addr(env, address, access_type,
stage_1_mmu_idx(mmu_idx), &ipa, attrs,
- prot, page_size, fsr, fi, cacheattrs);
+ prot, page_size, fi, cacheattrs);
/* If S1 fails or S2 is disabled, return early. */
if (ret || regime_translation_disabled(env, ARMMMUIdx_S2NS)) {
/* S1 is done. Now do S2 translation. */
ret = get_phys_addr_lpae(env, ipa, access_type, ARMMMUIdx_S2NS,
phys_ptr, attrs, &s2_prot,
- page_size, fsr, fi,
+ page_size, fi,
cacheattrs != NULL ? &cacheattrs2 : NULL);
fi->s2addr = ipa;
/* Combine the S1 and S2 perms. */
if (arm_feature(env, ARM_FEATURE_V8)) {
/* PMSAv8 */
ret = get_phys_addr_pmsav8(env, address, access_type, mmu_idx,
- phys_ptr, attrs, prot, fsr);
+ phys_ptr, attrs, prot, fi);
} else if (arm_feature(env, ARM_FEATURE_V7)) {
/* PMSAv7 */
ret = get_phys_addr_pmsav7(env, address, access_type, mmu_idx,
- phys_ptr, prot, fsr);
+ phys_ptr, prot, fi);
} else {
/* Pre-v7 MPU */
ret = get_phys_addr_pmsav5(env, address, access_type, mmu_idx,
- phys_ptr, prot, fsr);
+ phys_ptr, prot, fi);
}
qemu_log_mask(CPU_LOG_MMU, "PMSA MPU lookup for %s at 0x%08" PRIx32
" mmu_idx %u -> %s (prot %c%c%c)\n",
}
if (regime_using_lpae_format(env, mmu_idx)) {
- return get_phys_addr_lpae(env, address, access_type, mmu_idx, phys_ptr,
- attrs, prot, page_size, fsr, fi, cacheattrs);
+ return get_phys_addr_lpae(env, address, access_type, mmu_idx,
+ phys_ptr, attrs, prot, page_size,
+ fi, cacheattrs);
} else if (regime_sctlr(env, mmu_idx) & SCTLR_XP) {
- return get_phys_addr_v6(env, address, access_type, mmu_idx, phys_ptr,
- attrs, prot, page_size, fsr, fi);
+ return get_phys_addr_v6(env, address, access_type, mmu_idx,
+ phys_ptr, attrs, prot, page_size, fi);
} else {
- return get_phys_addr_v5(env, address, access_type, mmu_idx, phys_ptr,
- prot, page_size, fsr, fi);
+ return get_phys_addr_v5(env, address, access_type, mmu_idx,
+ phys_ptr, prot, page_size, fi);
}
}
* fsr with ARM DFSR/IFSR fault register format value on failure.
*/
bool arm_tlb_fill(CPUState *cs, vaddr address,
- MMUAccessType access_type, int mmu_idx, uint32_t *fsr,
+ MMUAccessType access_type, int mmu_idx,
ARMMMUFaultInfo *fi)
{
ARMCPU *cpu = ARM_CPU(cs);
ret = get_phys_addr(env, address, access_type,
core_to_arm_mmu_idx(env, mmu_idx), &phys_addr,
- &attrs, &prot, &page_size, fsr, fi, NULL);
+ &attrs, &prot, &page_size, fi, NULL);
if (!ret) {
/* Map a single [sub]page. */
phys_addr &= TARGET_PAGE_MASK;
target_ulong page_size;
int prot;
bool ret;
- uint32_t fsr;
ARMMMUFaultInfo fi = {};
ARMMMUIdx mmu_idx = core_to_arm_mmu_idx(env, cpu_mmu_index(env, false));
*attrs = (MemTxAttrs) {};
ret = get_phys_addr(env, addr, 0, mmu_idx, &phys_addr,
- attrs, &prot, &page_size, &fsr, &fi, NULL);
+ attrs, &prot, &page_size, &fi, NULL);
if (ret) {
return -1;
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;
switch (reg) {
case 8: /* MSP */
- return (env->v7m.control[env->v7m.secure] & R_V7M_CONTROL_SPSEL_MASK) ?
- env->v7m.other_sp : env->regs[13];
+ return v7m_using_psp(env) ? env->v7m.other_sp : env->regs[13];
case 9: /* PSP */
- return (env->v7m.control[env->v7m.secure] & R_V7M_CONTROL_SPSEL_MASK) ?
- env->regs[13] : env->v7m.other_sp;
+ 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 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;
}
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.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);
+ env->v7m.control[M_REG_NS] &= ~R_V7M_CONTROL_NPRIV_MASK;
+ env->v7m.control[M_REG_NS] |= val & R_V7M_CONTROL_NPRIV_MASK;
+ return;
case 0x98: /* SP_NS */
{
/* This gives the non-secure SP selected based on whether we're
}
break;
case 8: /* MSP */
- if (env->v7m.control[env->v7m.secure] & R_V7M_CONTROL_SPSEL_MASK) {
+ if (v7m_using_psp(env)) {
env->v7m.other_sp = val;
} else {
env->regs[13] = val;
}
break;
case 9: /* PSP */
- if (env->v7m.control[env->v7m.secure] & R_V7M_CONTROL_SPSEL_MASK) {
+ 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;
* 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.
*/
- if (!arm_v7m_is_handler_mode(env)) {
+ if (arm_feature(env, ARM_FEATURE_V8) ||
+ !arm_v7m_is_handler_mode(env)) {
write_v7m_control_spsel(env, (val & R_V7M_CONTROL_SPSEL_MASK) != 0);
}
env->v7m.control[env->v7m.secure] &= ~R_V7M_CONTROL_NPRIV_MASK;
env->v7m.control[env->v7m.secure] |= val & R_V7M_CONTROL_NPRIV_MASK;
break;
default:
+ 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;
+
+ /* 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, &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
void HELPER(dc_zva)(CPUARMState *env, uint64_t vaddr_in)
| (env->vfp.vec_stride << 20);
i = get_float_exception_flags(&env->vfp.fp_status);
i |= get_float_exception_flags(&env->vfp.standard_fp_status);
+ i |= get_float_exception_flags(&env->vfp.fp_status_f16);
fpscr |= vfp_exceptbits_from_host(i);
return fpscr;
}
break;
}
set_float_rounding_mode(i, &env->vfp.fp_status);
+ set_float_rounding_mode(i, &env->vfp.fp_status_f16);
}
- if (changed & (1 << 24)) {
- set_flush_to_zero((val & (1 << 24)) != 0, &env->vfp.fp_status);
- set_flush_inputs_to_zero((val & (1 << 24)) != 0, &env->vfp.fp_status);
+ if (changed & FPCR_FZ16) {
+ bool ftz_enabled = val & FPCR_FZ16;
+ set_flush_to_zero(ftz_enabled, &env->vfp.fp_status_f16);
+ set_flush_inputs_to_zero(ftz_enabled, &env->vfp.fp_status_f16);
+ }
+ if (changed & FPCR_FZ) {
+ bool ftz_enabled = val & FPCR_FZ;
+ set_flush_to_zero(ftz_enabled, &env->vfp.fp_status);
+ set_flush_inputs_to_zero(ftz_enabled, &env->vfp.fp_status);
+ }
+ if (changed & FPCR_DN) {
+ bool dnan_enabled = val & FPCR_DN;
+ set_default_nan_mode(dnan_enabled, &env->vfp.fp_status);
+ set_default_nan_mode(dnan_enabled, &env->vfp.fp_status_f16);
}
- if (changed & (1 << 25))
- set_default_nan_mode((val & (1 << 25)) != 0, &env->vfp.fp_status);
+ /* The exception flags are ORed together when we read fpscr so we
+ * only need to preserve the current state in one of our
+ * float_status values.
+ */
i = vfp_exceptbits_to_host(val);
set_float_exception_flags(i, &env->vfp.fp_status);
+ set_float_exception_flags(0, &env->vfp.fp_status_f16);
set_float_exception_flags(0, &env->vfp.standard_fp_status);
}
CONV_FTOI(vfp_to##name##p, fsz, sign, ) \
CONV_FTOI(vfp_to##name##z##p, fsz, sign, _round_to_zero)
+FLOAT_CONVS(si, h, 16, )
FLOAT_CONVS(si, s, 32, )
FLOAT_CONVS(si, d, 64, )
+FLOAT_CONVS(ui, h, 16, u)
FLOAT_CONVS(ui, s, 32, u)
FLOAT_CONVS(ui, d, 64, u)
VFP_CONV_FIX(uh, s, 32, 32, uint16)
VFP_CONV_FIX(ul, s, 32, 32, uint32)
VFP_CONV_FIX_A64(uq, s, 32, 64, uint64)
+
#undef VFP_CONV_FIX
#undef VFP_CONV_FIX_FLOAT
#undef VFP_CONV_FLOAT_FIX_ROUND
+#undef VFP_CONV_FIX_A64
+
+/* Conversion to/from f16 can overflow to infinity before/after scaling.
+ * Therefore we convert to f64 (which does not round), scale,
+ * and then convert f64 to f16 (which may round).
+ */
+
+static float16 do_postscale_fp16(float64 f, int shift, float_status *fpst)
+{
+ return float64_to_float16(float64_scalbn(f, -shift, fpst), true, fpst);
+}
+
+float16 HELPER(vfp_sltoh)(uint32_t x, uint32_t shift, void *fpst)
+{
+ return do_postscale_fp16(int32_to_float64(x, fpst), shift, fpst);
+}
+
+float16 HELPER(vfp_ultoh)(uint32_t x, uint32_t shift, void *fpst)
+{
+ return do_postscale_fp16(uint32_to_float64(x, fpst), shift, fpst);
+}
+
+static float64 do_prescale_fp16(float16 f, int shift, float_status *fpst)
+{
+ if (unlikely(float16_is_any_nan(f))) {
+ float_raise(float_flag_invalid, fpst);
+ return 0;
+ } else {
+ int old_exc_flags = get_float_exception_flags(fpst);
+ float64 ret;
+
+ ret = float16_to_float64(f, true, fpst);
+ ret = float64_scalbn(ret, shift, fpst);
+ old_exc_flags |= get_float_exception_flags(fpst)
+ & float_flag_input_denormal;
+ set_float_exception_flags(old_exc_flags, fpst);
+
+ return ret;
+ }
+}
+
+uint32_t HELPER(vfp_toshh)(float16 x, uint32_t shift, void *fpst)
+{
+ return float64_to_int16(do_prescale_fp16(x, shift, fpst), fpst);
+}
+
+uint32_t HELPER(vfp_touhh)(float16 x, uint32_t shift, void *fpst)
+{
+ return float64_to_uint16(do_prescale_fp16(x, shift, fpst), fpst);
+}
/* Set the current fp rounding mode and return the old one.
* The argument is a softfloat float_round_ value.
*/
-uint32_t HELPER(set_rmode)(uint32_t rmode, CPUARMState *env)
+uint32_t HELPER(set_rmode)(uint32_t rmode, void *fpstp)
{
- float_status *fp_status = &env->vfp.fp_status;
+ float_status *fp_status = fpstp;
uint32_t prev_rmode = get_float_rounding_mode(fp_status);
set_float_rounding_mode(rmode, fp_status);
* int->float conversions at run-time. */
#define float64_256 make_float64(0x4070000000000000LL)
#define float64_512 make_float64(0x4080000000000000LL)
+#define float16_maxnorm make_float16(0x7bff)
#define float32_maxnorm make_float32(0x7f7fffff)
#define float64_maxnorm make_float64(0x7fefffffffffffffLL)
/* Reciprocal functions
*
* The algorithm that must be used to calculate the estimate
- * is specified by the ARM ARM, see FPRecipEstimate()
+ * is specified by the ARM ARM, see FPRecipEstimate()/RecipEstimate
*/
-static float64 recip_estimate(float64 a, float_status *real_fp_status)
-{
- /* These calculations mustn't set any fp exception flags,
- * so we use a local copy of the fp_status.
- */
- float_status dummy_status = *real_fp_status;
- float_status *s = &dummy_status;
- /* q = (int)(a * 512.0) */
- float64 q = float64_mul(float64_512, a, s);
- int64_t q_int = float64_to_int64_round_to_zero(q, s);
-
- /* r = 1.0 / (((double)q + 0.5) / 512.0) */
- q = int64_to_float64(q_int, s);
- q = float64_add(q, float64_half, s);
- q = float64_div(q, float64_512, s);
- q = float64_div(float64_one, q, s);
-
- /* s = (int)(256.0 * r + 0.5) */
- q = float64_mul(q, float64_256, s);
- q = float64_add(q, float64_half, s);
- q_int = float64_to_int64_round_to_zero(q, s);
+/* See RecipEstimate()
+ *
+ * input is a 9 bit fixed point number
+ * input range 256 .. 511 for a number from 0.5 <= x < 1.0.
+ * result range 256 .. 511 for a number from 1.0 to 511/256.
+ */
- /* return (double)s / 256.0 */
- return float64_div(int64_to_float64(q_int, s), float64_256, s);
+static int recip_estimate(int input)
+{
+ int a, b, r;
+ assert(256 <= input && input < 512);
+ a = (input * 2) + 1;
+ b = (1 << 19) / a;
+ r = (b + 1) >> 1;
+ assert(256 <= r && r < 512);
+ return r;
}
-/* Common wrapper to call recip_estimate */
-static float64 call_recip_estimate(float64 num, int off, float_status *fpst)
+/*
+ * Common wrapper to call recip_estimate
+ *
+ * The parameters are exponent and 64 bit fraction (without implicit
+ * bit) where the binary point is nominally at bit 52. Returns a
+ * float64 which can then be rounded to the appropriate size by the
+ * callee.
+ */
+
+static uint64_t call_recip_estimate(int *exp, int exp_off, uint64_t frac)
{
- uint64_t val64 = float64_val(num);
- uint64_t frac = extract64(val64, 0, 52);
- int64_t exp = extract64(val64, 52, 11);
- uint64_t sbit;
- float64 scaled, estimate;
+ uint32_t scaled, estimate;
+ uint64_t result_frac;
+ int result_exp;
- /* Generate the scaled number for the estimate function */
- if (exp == 0) {
+ /* Handle sub-normals */
+ if (*exp == 0) {
if (extract64(frac, 51, 1) == 0) {
- exp = -1;
- frac = extract64(frac, 0, 50) << 2;
+ *exp = -1;
+ frac <<= 2;
} else {
- frac = extract64(frac, 0, 51) << 1;
+ frac <<= 1;
}
}
- /* scaled = '0' : '01111111110' : fraction<51:44> : Zeros(44); */
- scaled = make_float64((0x3feULL << 52)
- | extract64(frac, 44, 8) << 44);
-
- estimate = recip_estimate(scaled, fpst);
-
- /* Build new result */
- val64 = float64_val(estimate);
- sbit = 0x8000000000000000ULL & val64;
- exp = off - exp;
- frac = extract64(val64, 0, 52);
+ /* scaled = UInt('1':fraction<51:44>) */
+ scaled = deposit32(1 << 8, 0, 8, extract64(frac, 44, 8));
+ estimate = recip_estimate(scaled);
- if (exp == 0) {
- frac = 1ULL << 51 | extract64(frac, 1, 51);
- } else if (exp == -1) {
- frac = 1ULL << 50 | extract64(frac, 2, 50);
- exp = 0;
+ result_exp = exp_off - *exp;
+ result_frac = deposit64(0, 44, 8, estimate);
+ if (result_exp == 0) {
+ result_frac = deposit64(result_frac >> 1, 51, 1, 1);
+ } else if (result_exp == -1) {
+ result_frac = deposit64(result_frac >> 2, 50, 2, 1);
+ result_exp = 0;
}
- return make_float64(sbit | (exp << 52) | frac);
+ *exp = result_exp;
+
+ return result_frac;
}
static bool round_to_inf(float_status *fpst, bool sign_bit)
g_assert_not_reached();
}
+float16 HELPER(recpe_f16)(float16 input, void *fpstp)
+{
+ float_status *fpst = fpstp;
+ float16 f16 = float16_squash_input_denormal(input, fpst);
+ uint32_t f16_val = float16_val(f16);
+ uint32_t f16_sign = float16_is_neg(f16);
+ int f16_exp = extract32(f16_val, 10, 5);
+ uint32_t f16_frac = extract32(f16_val, 0, 10);
+ uint64_t f64_frac;
+
+ if (float16_is_any_nan(f16)) {
+ float16 nan = f16;
+ if (float16_is_signaling_nan(f16, fpst)) {
+ float_raise(float_flag_invalid, fpst);
+ nan = float16_maybe_silence_nan(f16, fpst);
+ }
+ if (fpst->default_nan_mode) {
+ nan = float16_default_nan(fpst);
+ }
+ return nan;
+ } else if (float16_is_infinity(f16)) {
+ return float16_set_sign(float16_zero, float16_is_neg(f16));
+ } else if (float16_is_zero(f16)) {
+ float_raise(float_flag_divbyzero, fpst);
+ return float16_set_sign(float16_infinity, float16_is_neg(f16));
+ } else if (float16_abs(f16) < (1 << 8)) {
+ /* Abs(value) < 2.0^-16 */
+ float_raise(float_flag_overflow | float_flag_inexact, fpst);
+ if (round_to_inf(fpst, f16_sign)) {
+ return float16_set_sign(float16_infinity, f16_sign);
+ } else {
+ return float16_set_sign(float16_maxnorm, f16_sign);
+ }
+ } else if (f16_exp >= 29 && fpst->flush_to_zero) {
+ float_raise(float_flag_underflow, fpst);
+ return float16_set_sign(float16_zero, float16_is_neg(f16));
+ }
+
+ f64_frac = call_recip_estimate(&f16_exp, 29,
+ ((uint64_t) f16_frac) << (52 - 10));
+
+ /* result = sign : result_exp<4:0> : fraction<51:42> */
+ f16_val = deposit32(0, 15, 1, f16_sign);
+ f16_val = deposit32(f16_val, 10, 5, f16_exp);
+ f16_val = deposit32(f16_val, 0, 10, extract64(f64_frac, 52 - 10, 10));
+ return make_float16(f16_val);
+}
+
float32 HELPER(recpe_f32)(float32 input, void *fpstp)
{
float_status *fpst = fpstp;
float32 f32 = float32_squash_input_denormal(input, fpst);
uint32_t f32_val = float32_val(f32);
- uint32_t f32_sbit = 0x80000000ULL & f32_val;
- int32_t f32_exp = extract32(f32_val, 23, 8);
+ bool f32_sign = float32_is_neg(f32);
+ int f32_exp = extract32(f32_val, 23, 8);
uint32_t f32_frac = extract32(f32_val, 0, 23);
- float64 f64, r64;
- uint64_t r64_val;
- int64_t r64_exp;
- uint64_t r64_frac;
+ uint64_t f64_frac;
if (float32_is_any_nan(f32)) {
float32 nan = f32;
} else if (float32_is_zero(f32)) {
float_raise(float_flag_divbyzero, fpst);
return float32_set_sign(float32_infinity, float32_is_neg(f32));
- } else if ((f32_val & ~(1ULL << 31)) < (1ULL << 21)) {
+ } else if (float32_abs(f32) < (1ULL << 21)) {
/* Abs(value) < 2.0^-128 */
float_raise(float_flag_overflow | float_flag_inexact, fpst);
- if (round_to_inf(fpst, f32_sbit)) {
- return float32_set_sign(float32_infinity, float32_is_neg(f32));
+ if (round_to_inf(fpst, f32_sign)) {
+ return float32_set_sign(float32_infinity, f32_sign);
} else {
- return float32_set_sign(float32_maxnorm, float32_is_neg(f32));
+ return float32_set_sign(float32_maxnorm, f32_sign);
}
} else if (f32_exp >= 253 && fpst->flush_to_zero) {
float_raise(float_flag_underflow, fpst);
return float32_set_sign(float32_zero, float32_is_neg(f32));
}
+ f64_frac = call_recip_estimate(&f32_exp, 253,
+ ((uint64_t) f32_frac) << (52 - 23));
- f64 = make_float64(((int64_t)(f32_exp) << 52) | (int64_t)(f32_frac) << 29);
- r64 = call_recip_estimate(f64, 253, fpst);
- r64_val = float64_val(r64);
- r64_exp = extract64(r64_val, 52, 11);
- r64_frac = extract64(r64_val, 0, 52);
-
- /* result = sign : result_exp<7:0> : fraction<51:29>; */
- return make_float32(f32_sbit |
- (r64_exp & 0xff) << 23 |
- extract64(r64_frac, 29, 24));
+ /* result = sign : result_exp<7:0> : fraction<51:29> */
+ f32_val = deposit32(0, 31, 1, f32_sign);
+ f32_val = deposit32(f32_val, 23, 8, f32_exp);
+ f32_val = deposit32(f32_val, 0, 23, extract64(f64_frac, 52 - 23, 23));
+ return make_float32(f32_val);
}
float64 HELPER(recpe_f64)(float64 input, void *fpstp)
float_status *fpst = fpstp;
float64 f64 = float64_squash_input_denormal(input, fpst);
uint64_t f64_val = float64_val(f64);
- uint64_t f64_sbit = 0x8000000000000000ULL & f64_val;
- int64_t f64_exp = extract64(f64_val, 52, 11);
- float64 r64;
- uint64_t r64_val;
- int64_t r64_exp;
- uint64_t r64_frac;
+ bool f64_sign = float64_is_neg(f64);
+ int f64_exp = extract64(f64_val, 52, 11);
+ uint64_t f64_frac = extract64(f64_val, 0, 52);
/* Deal with any special cases */
if (float64_is_any_nan(f64)) {
} else if ((f64_val & ~(1ULL << 63)) < (1ULL << 50)) {
/* Abs(value) < 2.0^-1024 */
float_raise(float_flag_overflow | float_flag_inexact, fpst);
- if (round_to_inf(fpst, f64_sbit)) {
- return float64_set_sign(float64_infinity, float64_is_neg(f64));
+ if (round_to_inf(fpst, f64_sign)) {
+ return float64_set_sign(float64_infinity, f64_sign);
} else {
- return float64_set_sign(float64_maxnorm, float64_is_neg(f64));
+ return float64_set_sign(float64_maxnorm, f64_sign);
}
} else if (f64_exp >= 2045 && fpst->flush_to_zero) {
float_raise(float_flag_underflow, fpst);
return float64_set_sign(float64_zero, float64_is_neg(f64));
}
- r64 = call_recip_estimate(f64, 2045, fpst);
- r64_val = float64_val(r64);
- r64_exp = extract64(r64_val, 52, 11);
- r64_frac = extract64(r64_val, 0, 52);
+ f64_frac = call_recip_estimate(&f64_exp, 2045, f64_frac);
- /* result = sign : result_exp<10:0> : fraction<51:0> */
- return make_float64(f64_sbit |
- ((r64_exp & 0x7ff) << 52) |
- r64_frac);
+ /* result = sign : result_exp<10:0> : fraction<51:0>; */
+ f64_val = deposit64(0, 63, 1, f64_sign);
+ f64_val = deposit64(f64_val, 52, 11, f64_exp);
+ f64_val = deposit64(f64_val, 0, 52, f64_frac);
+ return make_float64(f64_val);
}
/* The algorithm that must be used to calculate the estimate
* is specified by the ARM ARM.
*/
-static float64 recip_sqrt_estimate(float64 a, float_status *real_fp_status)
-{
- /* These calculations mustn't set any fp exception flags,
- * so we use a local copy of the fp_status.
- */
- float_status dummy_status = *real_fp_status;
- float_status *s = &dummy_status;
- float64 q;
- int64_t q_int;
-
- if (float64_lt(a, float64_half, s)) {
- /* range 0.25 <= a < 0.5 */
-
- /* a in units of 1/512 rounded down */
- /* q0 = (int)(a * 512.0); */
- q = float64_mul(float64_512, a, s);
- q_int = float64_to_int64_round_to_zero(q, s);
-
- /* reciprocal root r */
- /* r = 1.0 / sqrt(((double)q0 + 0.5) / 512.0); */
- q = int64_to_float64(q_int, s);
- q = float64_add(q, float64_half, s);
- q = float64_div(q, float64_512, s);
- q = float64_sqrt(q, s);
- q = float64_div(float64_one, q, s);
+
+static int do_recip_sqrt_estimate(int a)
+{
+ int b, estimate;
+
+ assert(128 <= a && a < 512);
+ if (a < 256) {
+ a = a * 2 + 1;
+ } else {
+ a = (a >> 1) << 1;
+ a = (a + 1) * 2;
+ }
+ b = 512;
+ while (a * (b + 1) * (b + 1) < (1 << 28)) {
+ b += 1;
+ }
+ estimate = (b + 1) / 2;
+ assert(256 <= estimate && estimate < 512);
+
+ return estimate;
+}
+
+
+static uint64_t recip_sqrt_estimate(int *exp , int exp_off, uint64_t frac)
+{
+ int estimate;
+ uint32_t scaled;
+
+ if (*exp == 0) {
+ while (extract64(frac, 51, 1) == 0) {
+ frac = frac << 1;
+ *exp -= 1;
+ }
+ frac = extract64(frac, 0, 51) << 1;
+ }
+
+ if (*exp & 1) {
+ /* scaled = UInt('01':fraction<51:45>) */
+ scaled = deposit32(1 << 7, 0, 7, extract64(frac, 45, 7));
} else {
- /* range 0.5 <= a < 1.0 */
+ /* scaled = UInt('1':fraction<51:44>) */
+ scaled = deposit32(1 << 8, 0, 8, extract64(frac, 44, 8));
+ }
+ estimate = do_recip_sqrt_estimate(scaled);
- /* a in units of 1/256 rounded down */
- /* q1 = (int)(a * 256.0); */
- q = float64_mul(float64_256, a, s);
- int64_t q_int = float64_to_int64_round_to_zero(q, s);
+ *exp = (exp_off - *exp) / 2;
+ return extract64(estimate, 0, 8) << 44;
+}
- /* reciprocal root r */
- /* r = 1.0 /sqrt(((double)q1 + 0.5) / 256); */
- q = int64_to_float64(q_int, s);
- q = float64_add(q, float64_half, s);
- q = float64_div(q, float64_256, s);
- q = float64_sqrt(q, s);
- q = float64_div(float64_one, q, s);
+float16 HELPER(rsqrte_f16)(float16 input, void *fpstp)
+{
+ float_status *s = fpstp;
+ float16 f16 = float16_squash_input_denormal(input, s);
+ uint16_t val = float16_val(f16);
+ bool f16_sign = float16_is_neg(f16);
+ int f16_exp = extract32(val, 10, 5);
+ uint16_t f16_frac = extract32(val, 0, 10);
+ uint64_t f64_frac;
+
+ if (float16_is_any_nan(f16)) {
+ float16 nan = f16;
+ if (float16_is_signaling_nan(f16, s)) {
+ float_raise(float_flag_invalid, s);
+ nan = float16_maybe_silence_nan(f16, s);
+ }
+ if (s->default_nan_mode) {
+ nan = float16_default_nan(s);
+ }
+ return nan;
+ } else if (float16_is_zero(f16)) {
+ float_raise(float_flag_divbyzero, s);
+ return float16_set_sign(float16_infinity, f16_sign);
+ } else if (f16_sign) {
+ float_raise(float_flag_invalid, s);
+ return float16_default_nan(s);
+ } else if (float16_is_infinity(f16)) {
+ return float16_zero;
}
- /* r in units of 1/256 rounded to nearest */
- /* s = (int)(256.0 * r + 0.5); */
- q = float64_mul(q, float64_256,s );
- q = float64_add(q, float64_half, s);
- q_int = float64_to_int64_round_to_zero(q, s);
+ /* Scale and normalize to a double-precision value between 0.25 and 1.0,
+ * preserving the parity of the exponent. */
+
+ f64_frac = ((uint64_t) f16_frac) << (52 - 10);
- /* return (double)s / 256.0;*/
- return float64_div(int64_to_float64(q_int, s), float64_256, s);
+ f64_frac = recip_sqrt_estimate(&f16_exp, 44, f64_frac);
+
+ /* result = sign : result_exp<4:0> : estimate<7:0> : Zeros(2) */
+ val = deposit32(0, 15, 1, f16_sign);
+ val = deposit32(val, 10, 5, f16_exp);
+ val = deposit32(val, 2, 8, extract64(f64_frac, 52 - 8, 8));
+ return make_float16(val);
}
float32 HELPER(rsqrte_f32)(float32 input, void *fpstp)
float_status *s = fpstp;
float32 f32 = float32_squash_input_denormal(input, s);
uint32_t val = float32_val(f32);
- uint32_t f32_sbit = 0x80000000 & val;
- int32_t f32_exp = extract32(val, 23, 8);
+ uint32_t f32_sign = float32_is_neg(f32);
+ int f32_exp = extract32(val, 23, 8);
uint32_t f32_frac = extract32(val, 0, 23);
uint64_t f64_frac;
- uint64_t val64;
- int result_exp;
- float64 f64;
if (float32_is_any_nan(f32)) {
float32 nan = f32;
* preserving the parity of the exponent. */
f64_frac = ((uint64_t) f32_frac) << 29;
- if (f32_exp == 0) {
- while (extract64(f64_frac, 51, 1) == 0) {
- f64_frac = f64_frac << 1;
- f32_exp = f32_exp-1;
- }
- f64_frac = extract64(f64_frac, 0, 51) << 1;
- }
-
- if (extract64(f32_exp, 0, 1) == 0) {
- f64 = make_float64(((uint64_t) f32_sbit) << 32
- | (0x3feULL << 52)
- | f64_frac);
- } else {
- f64 = make_float64(((uint64_t) f32_sbit) << 32
- | (0x3fdULL << 52)
- | f64_frac);
- }
- result_exp = (380 - f32_exp) / 2;
+ f64_frac = recip_sqrt_estimate(&f32_exp, 380, f64_frac);
- f64 = recip_sqrt_estimate(f64, s);
-
- val64 = float64_val(f64);
-
- val = ((result_exp & 0xff) << 23)
- | ((val64 >> 29) & 0x7fffff);
+ /* result = sign : result_exp<4:0> : estimate<7:0> : Zeros(15) */
+ val = deposit32(0, 31, 1, f32_sign);
+ val = deposit32(val, 23, 8, f32_exp);
+ val = deposit32(val, 15, 8, extract64(f64_frac, 52 - 8, 8));
return make_float32(val);
}
float_status *s = fpstp;
float64 f64 = float64_squash_input_denormal(input, s);
uint64_t val = float64_val(f64);
- uint64_t f64_sbit = 0x8000000000000000ULL & val;
- int64_t f64_exp = extract64(val, 52, 11);
+ bool f64_sign = float64_is_neg(f64);
+ int f64_exp = extract64(val, 52, 11);
uint64_t f64_frac = extract64(val, 0, 52);
- int64_t result_exp;
- uint64_t result_frac;
if (float64_is_any_nan(f64)) {
float64 nan = f64;
return float64_zero;
}
- /* Scale and normalize to a double-precision value between 0.25 and 1.0,
- * preserving the parity of the exponent. */
-
- if (f64_exp == 0) {
- while (extract64(f64_frac, 51, 1) == 0) {
- f64_frac = f64_frac << 1;
- f64_exp = f64_exp - 1;
- }
- f64_frac = extract64(f64_frac, 0, 51) << 1;
- }
-
- if (extract64(f64_exp, 0, 1) == 0) {
- f64 = make_float64(f64_sbit
- | (0x3feULL << 52)
- | f64_frac);
- } else {
- f64 = make_float64(f64_sbit
- | (0x3fdULL << 52)
- | f64_frac);
- }
-
- result_exp = (3068 - f64_exp) / 2;
-
- f64 = recip_sqrt_estimate(f64, s);
-
- result_frac = extract64(float64_val(f64), 0, 52);
+ f64_frac = recip_sqrt_estimate(&f64_exp, 3068, f64_frac);
- return make_float64(f64_sbit |
- ((result_exp & 0x7ff) << 52) |
- result_frac);
+ /* result = sign : result_exp<4:0> : estimate<7:0> : Zeros(44) */
+ val = deposit64(0, 61, 1, f64_sign);
+ val = deposit64(val, 52, 11, f64_exp);
+ val = deposit64(val, 44, 8, extract64(f64_frac, 52 - 8, 8));
+ return make_float64(val);
}
uint32_t HELPER(recpe_u32)(uint32_t a, void *fpstp)
{
- float_status *s = fpstp;
- float64 f64;
+ /* float_status *s = fpstp; */
+ int input, estimate;
if ((a & 0x80000000) == 0) {
return 0xffffffff;
}
- f64 = make_float64((0x3feULL << 52)
- | ((int64_t)(a & 0x7fffffff) << 21));
-
- f64 = recip_estimate(f64, s);
+ input = extract32(a, 23, 9);
+ estimate = recip_estimate(input);
- return 0x80000000 | ((float64_val(f64) >> 21) & 0x7fffffff);
+ return deposit32(0, (32 - 9), 9, estimate);
}
uint32_t HELPER(rsqrte_u32)(uint32_t a, void *fpstp)
{
- float_status *fpst = fpstp;
- float64 f64;
+ int estimate;
if ((a & 0xc0000000) == 0) {
return 0xffffffff;
}
- if (a & 0x80000000) {
- f64 = make_float64((0x3feULL << 52)
- | ((uint64_t)(a & 0x7fffffff) << 21));
- } else { /* bits 31-30 == '01' */
- f64 = make_float64((0x3fdULL << 52)
- | ((uint64_t)(a & 0x3fffffff) << 22));
- }
-
- f64 = recip_sqrt_estimate(f64, fpst);
+ estimate = do_recip_sqrt_estimate(extract32(a, 23, 9));
- return 0x80000000 | ((float64_val(f64) >> 21) & 0x7fffffff);
+ return deposit32(0, 23, 9, estimate);
}
/* VFPv4 fused multiply-accumulate */
/* Linux crc32c converts the output to one's complement. */
return crc32c(acc, buf, bytes) ^ 0xffffffff;
}
+
+/* Return the exception level to which FP-disabled exceptions should
+ * be taken, or 0 if FP is enabled.
+ */
+static inline int fp_exception_el(CPUARMState *env)
+{
+#ifndef CONFIG_USER_ONLY
+ int fpen;
+ int cur_el = arm_current_el(env);
+
+ /* CPACR and the CPTR registers don't exist before v6, so FP is
+ * always accessible
+ */
+ if (!arm_feature(env, ARM_FEATURE_V6)) {
+ return 0;
+ }
+
+ /* The CPACR controls traps to EL1, or PL1 if we're 32 bit:
+ * 0, 2 : trap EL0 and EL1/PL1 accesses
+ * 1 : trap only EL0 accesses
+ * 3 : trap no accesses
+ */
+ fpen = extract32(env->cp15.cpacr_el1, 20, 2);
+ switch (fpen) {
+ case 0:
+ case 2:
+ if (cur_el == 0 || cur_el == 1) {
+ /* Trap to PL1, which might be EL1 or EL3 */
+ if (arm_is_secure(env) && !arm_el_is_aa64(env, 3)) {
+ return 3;
+ }
+ return 1;
+ }
+ if (cur_el == 3 && !is_a64(env)) {
+ /* Secure PL1 running at EL3 */
+ return 3;
+ }
+ break;
+ case 1:
+ if (cur_el == 0) {
+ return 1;
+ }
+ break;
+ case 3:
+ break;
+ }
+
+ /* For the CPTR registers we don't need to guard with an ARM_FEATURE
+ * check because zero bits in the registers mean "don't trap".
+ */
+
+ /* CPTR_EL2 : present in v7VE or v8 */
+ if (cur_el <= 2 && extract32(env->cp15.cptr_el[2], 10, 1)
+ && !arm_is_secure_below_el3(env)) {
+ /* Trap FP ops at EL2, NS-EL1 or NS-EL0 to EL2 */
+ return 2;
+ }
+
+ /* CPTR_EL3 : present in v8 */
+ if (extract32(env->cp15.cptr_el[3], 10, 1)) {
+ /* Trap all FP ops to EL3 */
+ return 3;
+ }
+#endif
+ return 0;
+}
+
+void cpu_get_tb_cpu_state(CPUARMState *env, target_ulong *pc,
+ target_ulong *cs_base, uint32_t *pflags)
+{
+ ARMMMUIdx mmu_idx = core_to_arm_mmu_idx(env, cpu_mmu_index(env, false));
+ int fp_el = fp_exception_el(env);
+ uint32_t flags;
+
+ if (is_a64(env)) {
+ int sve_el = sve_exception_el(env);
+ uint32_t zcr_len;
+
+ *pc = env->pc;
+ flags = ARM_TBFLAG_AARCH64_STATE_MASK;
+ /* Get control bits for tagged addresses */
+ flags |= (arm_regime_tbi0(env, mmu_idx) << ARM_TBFLAG_TBI0_SHIFT);
+ flags |= (arm_regime_tbi1(env, mmu_idx) << ARM_TBFLAG_TBI1_SHIFT);
+ flags |= sve_el << ARM_TBFLAG_SVEEXC_EL_SHIFT;
+
+ /* If SVE is disabled, but FP is enabled,
+ then the effective len is 0. */
+ if (sve_el != 0 && fp_el == 0) {
+ zcr_len = 0;
+ } else {
+ int current_el = arm_current_el(env);
+
+ zcr_len = env->vfp.zcr_el[current_el <= 1 ? 1 : current_el];
+ zcr_len &= 0xf;
+ if (current_el < 2 && arm_feature(env, ARM_FEATURE_EL2)) {
+ zcr_len = MIN(zcr_len, 0xf & (uint32_t)env->vfp.zcr_el[2]);
+ }
+ if (current_el < 3 && arm_feature(env, ARM_FEATURE_EL3)) {
+ zcr_len = MIN(zcr_len, 0xf & (uint32_t)env->vfp.zcr_el[3]);
+ }
+ }
+ flags |= zcr_len << ARM_TBFLAG_ZCR_LEN_SHIFT;
+ } else {
+ *pc = env->regs[15];
+ flags = (env->thumb << ARM_TBFLAG_THUMB_SHIFT)
+ | (env->vfp.vec_len << ARM_TBFLAG_VECLEN_SHIFT)
+ | (env->vfp.vec_stride << ARM_TBFLAG_VECSTRIDE_SHIFT)
+ | (env->condexec_bits << ARM_TBFLAG_CONDEXEC_SHIFT)
+ | (arm_sctlr_b(env) << ARM_TBFLAG_SCTLR_B_SHIFT);
+ if (!(access_secure_reg(env))) {
+ flags |= ARM_TBFLAG_NS_MASK;
+ }
+ if (env->vfp.xregs[ARM_VFP_FPEXC] & (1 << 30)
+ || arm_el_is_aa64(env, 1)) {
+ flags |= ARM_TBFLAG_VFPEN_MASK;
+ }
+ flags |= (extract32(env->cp15.c15_cpar, 0, 2)
+ << ARM_TBFLAG_XSCALE_CPAR_SHIFT);
+ }
+
+ flags |= (arm_to_core_mmu_idx(mmu_idx) << ARM_TBFLAG_MMUIDX_SHIFT);
+
+ /* The SS_ACTIVE and PSTATE_SS bits correspond to the state machine
+ * states defined in the ARM ARM for software singlestep:
+ * SS_ACTIVE PSTATE.SS State
+ * 0 x Inactive (the TB flag for SS is always 0)
+ * 1 0 Active-pending
+ * 1 1 Active-not-pending
+ */
+ if (arm_singlestep_active(env)) {
+ flags |= ARM_TBFLAG_SS_ACTIVE_MASK;
+ if (is_a64(env)) {
+ if (env->pstate & PSTATE_SS) {
+ flags |= ARM_TBFLAG_PSTATE_SS_MASK;
+ }
+ } else {
+ if (env->uncached_cpsr & PSTATE_SS) {
+ flags |= ARM_TBFLAG_PSTATE_SS_MASK;
+ }
+ }
+ }
+ if (arm_cpu_data_is_big_endian(env)) {
+ flags |= ARM_TBFLAG_BE_DATA_MASK;
+ }
+ flags |= fp_el << ARM_TBFLAG_FPEXC_EL_SHIFT;
+
+ if (arm_v7m_is_handler_mode(env)) {
+ flags |= ARM_TBFLAG_HANDLER_MASK;
+ }
+
+ *pflags = flags;
+ *cs_base = 0;
+}