target/arm: Handle floating point registers in exception entry

Handle floating point registers in exception entry.
This corresponds to the FP-specific parts of the pseudocode
functions ActivateException() and PushStack().

We defer the code corresponding to UpdateFPCCR() to a later patch.

Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
Reviewed-by: Richard Henderson <richard.henderson@linaro.org>
Message-id: 20190416125744.27770-11-peter.maydell@linaro.org
This commit is contained in:
Peter Maydell 2019-04-29 17:36:00 +01:00
parent 3432c79a4e
commit 0ed377a801

View file

@ -8167,6 +8167,9 @@ static void v7m_exception_taken(ARMCPU *cpu, uint32_t lr, bool dotailchain,
switch_v7m_security_state(env, targets_secure);
write_v7m_control_spsel(env, 0);
arm_clear_exclusive(env);
/* Clear SFPA and FPCA (has no effect if no FPU) */
env->v7m.control[M_REG_S] &=
~(R_V7M_CONTROL_FPCA_MASK | R_V7M_CONTROL_SFPA_MASK);
/* Clear IT bits */
env->condexec_bits = 0;
env->regs[14] = lr;
@ -8187,6 +8190,20 @@ static bool v7m_push_stack(ARMCPU *cpu)
uint32_t xpsr = xpsr_read(env);
uint32_t frameptr = env->regs[13];
ARMMMUIdx mmu_idx = arm_mmu_idx(env);
uint32_t framesize;
bool nsacr_cp10 = extract32(env->v7m.nsacr, 10, 1);
if ((env->v7m.control[M_REG_S] & R_V7M_CONTROL_FPCA_MASK) &&
(env->v7m.secure || nsacr_cp10)) {
if (env->v7m.secure &&
env->v7m.fpccr[M_REG_S] & R_V7M_FPCCR_TS_MASK) {
framesize = 0xa8;
} else {
framesize = 0x68;
}
} else {
framesize = 0x20;
}
/* Align stack pointer if the guest wants that */
if ((frameptr & 4) &&
@ -8195,7 +8212,13 @@ static bool v7m_push_stack(ARMCPU *cpu)
xpsr |= XPSR_SPREALIGN;
}
frameptr -= 0x20;
xpsr &= ~XPSR_SFPA;
if (env->v7m.secure &&
(env->v7m.control[M_REG_S] & R_V7M_CONTROL_SFPA_MASK)) {
xpsr |= XPSR_SFPA;
}
frameptr -= framesize;
if (arm_feature(env, ARM_FEATURE_V8)) {
uint32_t limit = v7m_sp_limit(env);
@ -8239,6 +8262,73 @@ static bool v7m_push_stack(ARMCPU *cpu)
v7m_stack_write(cpu, frameptr + 24, env->regs[15], mmu_idx, false) &&
v7m_stack_write(cpu, frameptr + 28, xpsr, mmu_idx, false);
if (env->v7m.control[M_REG_S] & R_V7M_CONTROL_FPCA_MASK) {
/* FPU is active, try to save its registers */
bool fpccr_s = env->v7m.fpccr[M_REG_S] & R_V7M_FPCCR_S_MASK;
bool lspact = env->v7m.fpccr[fpccr_s] & R_V7M_FPCCR_LSPACT_MASK;
if (lspact && arm_feature(env, ARM_FEATURE_M_SECURITY)) {
qemu_log_mask(CPU_LOG_INT,
"...SecureFault because LSPACT and FPCA both set\n");
env->v7m.sfsr |= R_V7M_SFSR_LSERR_MASK;
armv7m_nvic_set_pending(env->nvic, ARMV7M_EXCP_SECURE, false);
} else if (!env->v7m.secure && !nsacr_cp10) {
qemu_log_mask(CPU_LOG_INT,
"...Secure UsageFault with CFSR.NOCP because "
"NSACR.CP10 prevents stacking FP regs\n");
armv7m_nvic_set_pending(env->nvic, ARMV7M_EXCP_USAGE, M_REG_S);
env->v7m.cfsr[M_REG_S] |= R_V7M_CFSR_NOCP_MASK;
} else {
if (!(env->v7m.fpccr[M_REG_S] & R_V7M_FPCCR_LSPEN_MASK)) {
/* Lazy stacking disabled, save registers now */
int i;
bool cpacr_pass = v7m_cpacr_pass(env, env->v7m.secure,
arm_current_el(env) != 0);
if (stacked_ok && !cpacr_pass) {
/*
* Take UsageFault if CPACR forbids access. The pseudocode
* here does a full CheckCPEnabled() but we know the NSACR
* check can never fail as we have already handled that.
*/
qemu_log_mask(CPU_LOG_INT,
"...UsageFault with CFSR.NOCP because "
"CPACR.CP10 prevents stacking FP regs\n");
armv7m_nvic_set_pending(env->nvic, ARMV7M_EXCP_USAGE,
env->v7m.secure);
env->v7m.cfsr[env->v7m.secure] |= R_V7M_CFSR_NOCP_MASK;
stacked_ok = false;
}
for (i = 0; i < ((framesize == 0xa8) ? 32 : 16); i += 2) {
uint64_t dn = *aa32_vfp_dreg(env, i / 2);
uint32_t faddr = frameptr + 0x20 + 4 * i;
uint32_t slo = extract64(dn, 0, 32);
uint32_t shi = extract64(dn, 32, 32);
if (i >= 16) {
faddr += 8; /* skip the slot for the FPSCR */
}
stacked_ok = stacked_ok &&
v7m_stack_write(cpu, faddr, slo, mmu_idx, false) &&
v7m_stack_write(cpu, faddr + 4, shi, mmu_idx, false);
}
stacked_ok = stacked_ok &&
v7m_stack_write(cpu, frameptr + 0x60,
vfp_get_fpscr(env), mmu_idx, false);
if (cpacr_pass) {
for (i = 0; i < ((framesize == 0xa8) ? 32 : 16); i += 2) {
*aa32_vfp_dreg(env, i / 2) = 0;
}
vfp_set_fpscr(env, 0);
}
} else {
/* Lazy stacking enabled, save necessary info to stack later */
/* TODO : equivalent of UpdateFPCCR() pseudocode */
}
}
}
/*
* If we broke a stack limit then SP was already updated earlier;
* otherwise we update SP regardless of whether any of the stack
@ -8999,8 +9089,7 @@ void arm_v7m_cpu_do_interrupt(CPUState *cs)
if (arm_feature(env, ARM_FEATURE_V8)) {
lr = R_V7M_EXCRET_RES1_MASK |
R_V7M_EXCRET_DCRS_MASK |
R_V7M_EXCRET_FTYPE_MASK;
R_V7M_EXCRET_DCRS_MASK;
/* The S bit indicates whether we should return to Secure
* or NonSecure (ie our current state).
* The ES bit indicates whether we're taking this exception
@ -9015,6 +9104,9 @@ void arm_v7m_cpu_do_interrupt(CPUState *cs)
if (env->v7m.secure) {
lr |= R_V7M_EXCRET_S_MASK;
}
if (!(env->v7m.control[M_REG_S] & R_V7M_CONTROL_FPCA_MASK)) {
lr |= R_V7M_EXCRET_FTYPE_MASK;
}
} else {
lr = R_V7M_EXCRET_RES1_MASK |
R_V7M_EXCRET_S_MASK |