Skip to content

Commit

Permalink
target/arm: Handle floating point registers in exception return
Browse files Browse the repository at this point in the history
Handle floating point registers in exception return.
This corresponds to pseudocode functions ValidateExceptionReturn(),
ExceptionReturn(), PopStack() and ConsumeExcStackFrame().

Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
Reviewed-by: Richard Henderson <richard.henderson@linaro.org>
Message-id: 20190416125744.27770-16-peter.maydell@linaro.org
  • Loading branch information
pm215 committed Apr 29, 2019
1 parent 0dc51d6 commit 6808c4d
Showing 1 changed file with 141 additions and 1 deletion.
142 changes: 141 additions & 1 deletion target/arm/helper.c
Expand Up @@ -8447,6 +8447,8 @@ static void do_v7m_exception_exit(ARMCPU *cpu)
bool rettobase = false;
bool exc_secure = false;
bool return_to_secure;
bool ftype;
bool restore_s16_s31;

/* If we're not in Handler mode then jumps to magic exception-exit
* addresses don't have magic behaviour. However for the v8M
Expand Down Expand Up @@ -8484,6 +8486,16 @@ static void do_v7m_exception_exit(ARMCPU *cpu)
excret);
}

ftype = excret & R_V7M_EXCRET_FTYPE_MASK;

if (!arm_feature(env, ARM_FEATURE_VFP) && !ftype) {
qemu_log_mask(LOG_GUEST_ERROR, "M profile: zero FTYPE in exception "
"exit PC value 0x%" PRIx32 " is UNPREDICTABLE "
"if FPU not present\n",
excret);
ftype = true;
}

if (arm_feature(env, ARM_FEATURE_M_SECURITY)) {
/* EXC_RETURN.ES validation check (R_SMFL). We must do this before
* we pick which FAULTMASK to clear.
Expand Down Expand Up @@ -8584,6 +8596,30 @@ static void do_v7m_exception_exit(ARMCPU *cpu)
*/
write_v7m_control_spsel_for_secstate(env, return_to_sp_process, exc_secure);

/*
* Clear scratch FP values left in caller saved registers; this
* must happen before any kind of tail chaining.
*/
if ((env->v7m.fpccr[M_REG_S] & R_V7M_FPCCR_CLRONRET_MASK) &&
(env->v7m.control[M_REG_S] & R_V7M_CONTROL_FPCA_MASK)) {
if (env->v7m.fpccr[M_REG_S] & R_V7M_FPCCR_LSPACT_MASK) {
env->v7m.sfsr |= R_V7M_SFSR_LSERR_MASK;
armv7m_nvic_set_pending(env->nvic, ARMV7M_EXCP_SECURE, false);
qemu_log_mask(CPU_LOG_INT, "...taking SecureFault on existing "
"stackframe: error during lazy state deactivation\n");
v7m_exception_taken(cpu, excret, true, false);
return;
} else {
/* Clear s0..s15 and FPSCR */
int i;

for (i = 0; i < 16; i += 2) {
*aa32_vfp_dreg(env, i / 2) = 0;
}
vfp_set_fpscr(env, 0);
}
}

if (sfault) {
env->v7m.sfsr |= R_V7M_SFSR_INVER_MASK;
armv7m_nvic_set_pending(env->nvic, ARMV7M_EXCP_SECURE, false);
Expand Down Expand Up @@ -8745,8 +8781,105 @@ static void do_v7m_exception_exit(ARMCPU *cpu)
}
}

if (!ftype) {
/* FP present and we need to handle it */
if (!return_to_secure &&
(env->v7m.fpccr[M_REG_S] & R_V7M_FPCCR_LSPACT_MASK)) {
armv7m_nvic_set_pending(env->nvic, ARMV7M_EXCP_SECURE, false);
env->v7m.sfsr |= R_V7M_SFSR_LSERR_MASK;
qemu_log_mask(CPU_LOG_INT,
"...taking SecureFault on existing stackframe: "
"Secure LSPACT set but exception return is "
"not to secure state\n");
v7m_exception_taken(cpu, excret, true, false);
return;
}

restore_s16_s31 = return_to_secure &&
(env->v7m.fpccr[M_REG_S] & R_V7M_FPCCR_TS_MASK);

if (env->v7m.fpccr[return_to_secure] & R_V7M_FPCCR_LSPACT_MASK) {
/* State in FPU is still valid, just clear LSPACT */
env->v7m.fpccr[return_to_secure] &= ~R_V7M_FPCCR_LSPACT_MASK;
} else {
int i;
uint32_t fpscr;
bool cpacr_pass, nsacr_pass;

cpacr_pass = v7m_cpacr_pass(env, return_to_secure,
return_to_priv);
nsacr_pass = return_to_secure ||
extract32(env->v7m.nsacr, 10, 1);

if (!cpacr_pass) {
armv7m_nvic_set_pending(env->nvic, ARMV7M_EXCP_USAGE,
return_to_secure);
env->v7m.cfsr[return_to_secure] |= R_V7M_CFSR_NOCP_MASK;
qemu_log_mask(CPU_LOG_INT,
"...taking UsageFault on existing "
"stackframe: CPACR.CP10 prevents unstacking "
"FP regs\n");
v7m_exception_taken(cpu, excret, true, false);
return;
} else if (!nsacr_pass) {
armv7m_nvic_set_pending(env->nvic, ARMV7M_EXCP_USAGE, true);
env->v7m.cfsr[M_REG_S] |= R_V7M_CFSR_INVPC_MASK;
qemu_log_mask(CPU_LOG_INT,
"...taking Secure UsageFault on existing "
"stackframe: NSACR.CP10 prevents unstacking "
"FP regs\n");
v7m_exception_taken(cpu, excret, true, false);
return;
}

for (i = 0; i < (restore_s16_s31 ? 32 : 16); i += 2) {
uint32_t slo, shi;
uint64_t dn;
uint32_t faddr = frameptr + 0x20 + 4 * i;

if (i >= 16) {
faddr += 8; /* Skip the slot for the FPSCR */
}

pop_ok = pop_ok &&
v7m_stack_read(cpu, &slo, faddr, mmu_idx) &&
v7m_stack_read(cpu, &shi, faddr + 4, mmu_idx);

if (!pop_ok) {
break;
}

dn = (uint64_t)shi << 32 | slo;
*aa32_vfp_dreg(env, i / 2) = dn;
}
pop_ok = pop_ok &&
v7m_stack_read(cpu, &fpscr, frameptr + 0x60, mmu_idx);
if (pop_ok) {
vfp_set_fpscr(env, fpscr);
}
if (!pop_ok) {
/*
* These regs are 0 if security extension present;
* otherwise merely UNKNOWN. We zero always.
*/
for (i = 0; i < (restore_s16_s31 ? 32 : 16); i += 2) {
*aa32_vfp_dreg(env, i / 2) = 0;
}
vfp_set_fpscr(env, 0);
}
}
}
env->v7m.control[M_REG_S] = FIELD_DP32(env->v7m.control[M_REG_S],
V7M_CONTROL, FPCA, !ftype);

/* Commit to consuming the stack frame */
frameptr += 0x20;
if (!ftype) {
frameptr += 0x48;
if (restore_s16_s31) {
frameptr += 0x40;
}
}
/* Undo stack alignment (the SPREALIGN bit indicates that the original
* pre-exception SP was not 8-aligned and we added a padding word to
* align it, so we undo this by ORing in the bit that increases it
Expand All @@ -8759,7 +8892,14 @@ static void do_v7m_exception_exit(ARMCPU *cpu)
*frame_sp_p = frameptr;
}
/* This xpsr_write() will invalidate frame_sp_p as it may switch stack */
xpsr_write(env, xpsr, ~XPSR_SPREALIGN);
xpsr_write(env, xpsr, ~(XPSR_SPREALIGN | XPSR_SFPA));

if (env->v7m.secure) {
bool sfpa = xpsr & XPSR_SFPA;

env->v7m.control[M_REG_S] = FIELD_DP32(env->v7m.control[M_REG_S],
V7M_CONTROL, SFPA, sfpa);
}

/* The restored xPSR exception field will be zero if we're
* resuming in Thread mode. If that doesn't match what the
Expand Down

0 comments on commit 6808c4d

Please sign in to comment.