target-arm:

* v8M: more preparatory work
  * nvic: reset properly rather than leaving the nvic in a weird state
  * xlnx-zynqmp: Mark the "xlnx, zynqmp" device with user_creatable = false
  * sd: fix out-of-bounds check for multi block reads
  * arm: Fix SMC reporting to EL2 when QEMU provides PSCI
 -----BEGIN PGP SIGNATURE-----
 Version: GnuPG v1
 
 iQIcBAABCAAGBQJZ16gXAAoJEDwlJe0UNgzefwgQAKXtjBjEFiZCfjf1sgviC5EX
 Er55ZK5pGWzI0LJ24b1bxZfWpeyzZZP9/J0O+5BGc+G1zfWBDgPCic/at0Yp947W
 KFmHwQgtSpcTR1EA0EDnjcxfXEdYUziOyasSqLIrD4xHw9wZYGk2pKz31s5Yuryx
 AxdNMJ4xn0/u3PZFCNkEMDnPfodIjz63fcoIkLK/NU4Dx3YvjR2EJ+ig0rxXBVdi
 0RTpJnZCDy62ORE8/dE65hXI1qR/6V5MEWvJkHVn+8dlr/6C4oiJJGzohuClDcqF
 nfmMOD99da9+pod51B0+CDnjVxWm5vbibMRDm6lyVpS8ddmpcx8hgDJqg+sDsg2z
 k552VyjtP5bPcDEI/e/ibx1QPU+dmTzR3dO4zTMRp4oxHTL+oYdq5oGY3Lu+5H0G
 DFcVlQrgmfXhNm7/LNJBcT2cvbmw2EIu26k5imsnVKMB+kMVoO5Me6O0BplHuQsQ
 aFj1YWNgos24/SJ98p9PGdUTcgmjr7wgtrwVRXjgnMCxgXcT37ph8SBDfB5v+qMZ
 g+zApYlsJwW5UrbETD/Xj6QbEd7ucijKx+r4KTaPoidniJV9VdDr22UKdp4r5MFs
 w2yeMYRH5ln7uWsjJx9tkzjDfDaGavyLqknOQ+8Y/IXeerYvIzBlalB7SgmFEPrt
 kaInDESHP1Is7JGoAoW0
 =hOvM
 -----END PGP SIGNATURE-----

Merge remote-tracking branch 'remotes/pmaydell/tags/pull-target-arm-20171006' into staging

target-arm:
 * v8M: more preparatory work
 * nvic: reset properly rather than leaving the nvic in a weird state
 * xlnx-zynqmp: Mark the "xlnx, zynqmp" device with user_creatable = false
 * sd: fix out-of-bounds check for multi block reads
 * arm: Fix SMC reporting to EL2 when QEMU provides PSCI

# gpg: Signature made Fri 06 Oct 2017 16:58:15 BST
# gpg:                using RSA key 0x3C2525ED14360CDE
# gpg: Good signature from "Peter Maydell <peter.maydell@linaro.org>"
# gpg:                 aka "Peter Maydell <pmaydell@gmail.com>"
# gpg:                 aka "Peter Maydell <pmaydell@chiark.greenend.org.uk>"
# Primary key fingerprint: E1A5 C593 CD41 9DE2 8E83  15CF 3C25 25ED 1436 0CDE

* remotes/pmaydell/tags/pull-target-arm-20171006:
  nvic: Add missing code for writing SHCSR.HARDFAULTPENDED bit
  target/arm: Factor out "get mmuidx for specified security state"
  target/arm: Fix calculation of secure mm_idx values
  target/arm: Implement security attribute lookups for memory accesses
  nvic: Implement Security Attribution Unit registers
  target/arm: Add v8M support to exception entry code
  target/arm: Add support for restoring v8M additional state context
  target/arm: Update excret sanity checks for v8M
  target/arm: Add new-in-v8M SFSR and SFAR
  target/arm: Don't warn about exception return with PC low bit set for v8M
  target/arm: Warn about restoring to unaligned stack
  target/arm: Check for xPSR mismatch usage faults earlier for v8M
  target/arm: Restore SPSEL to correct CONTROL register on exception return
  target/arm: Restore security state on exception return
  target/arm: Prepare for CONTROL.SPSEL being nonzero in Handler mode
  target/arm: Don't switch to target stack early in v7M exception return
  nvic: Clear the vector arrays and prigroup on reset
  hw/arm/xlnx-zynqmp: Mark the "xlnx, zynqmp" device with user_creatable = false
  hw/sd: fix out-of-bounds check for multi block reads
  arm: Fix SMC reporting to EL2 when QEMU provides PSCI

Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
This commit is contained in:
Peter Maydell 2017-10-06 17:00:42 +01:00
commit 5121d81e38
9 changed files with 899 additions and 111 deletions

View File

@ -440,6 +440,8 @@ static void xlnx_zynqmp_class_init(ObjectClass *oc, void *data)
dc->props = xlnx_zynqmp_props;
dc->realize = xlnx_zynqmp_realize;
/* Reason: Uses serial_hds in realize function, thus can't be used twice */
dc->user_creatable = false;
}
static const TypeInfo xlnx_zynqmp_type_info = {

View File

@ -616,7 +616,7 @@ bool armv7m_nvic_acknowledge_irq(void *opaque)
vec->active = 1;
vec->pending = 0;
env->v7m.exception = s->vectpending;
write_v7m_exception(env, s->vectpending);
nvic_irq_update(s);
@ -1017,6 +1017,76 @@ static uint32_t nvic_readl(NVICState *s, uint32_t offset, MemTxAttrs attrs)
goto bad_offset;
}
return cpu->env.pmsav8.mair1[attrs.secure];
case 0xdd0: /* SAU_CTRL */
if (!arm_feature(&cpu->env, ARM_FEATURE_V8)) {
goto bad_offset;
}
if (!attrs.secure) {
return 0;
}
return cpu->env.sau.ctrl;
case 0xdd4: /* SAU_TYPE */
if (!arm_feature(&cpu->env, ARM_FEATURE_V8)) {
goto bad_offset;
}
if (!attrs.secure) {
return 0;
}
return cpu->sau_sregion;
case 0xdd8: /* SAU_RNR */
if (!arm_feature(&cpu->env, ARM_FEATURE_V8)) {
goto bad_offset;
}
if (!attrs.secure) {
return 0;
}
return cpu->env.sau.rnr;
case 0xddc: /* SAU_RBAR */
{
int region = cpu->env.sau.rnr;
if (!arm_feature(&cpu->env, ARM_FEATURE_V8)) {
goto bad_offset;
}
if (!attrs.secure) {
return 0;
}
if (region >= cpu->sau_sregion) {
return 0;
}
return cpu->env.sau.rbar[region];
}
case 0xde0: /* SAU_RLAR */
{
int region = cpu->env.sau.rnr;
if (!arm_feature(&cpu->env, ARM_FEATURE_V8)) {
goto bad_offset;
}
if (!attrs.secure) {
return 0;
}
if (region >= cpu->sau_sregion) {
return 0;
}
return cpu->env.sau.rlar[region];
}
case 0xde4: /* SFSR */
if (!arm_feature(&cpu->env, ARM_FEATURE_V8)) {
goto bad_offset;
}
if (!attrs.secure) {
return 0;
}
return cpu->env.v7m.sfsr;
case 0xde8: /* SFAR */
if (!arm_feature(&cpu->env, ARM_FEATURE_V8)) {
goto bad_offset;
}
if (!attrs.secure) {
return 0;
}
return cpu->env.v7m.sfar;
default:
bad_offset:
qemu_log_mask(LOG_GUEST_ERROR, "NVIC: Bad read offset 0x%x\n", offset);
@ -1160,6 +1230,7 @@ static void nvic_writel(NVICState *s, uint32_t offset, uint32_t value,
s->sec_vectors[ARMV7M_EXCP_BUS].enabled = (value & (1 << 17)) != 0;
s->sec_vectors[ARMV7M_EXCP_USAGE].enabled =
(value & (1 << 18)) != 0;
s->sec_vectors[ARMV7M_EXCP_HARD].pending = (value & (1 << 21)) != 0;
/* SecureFault not banked, but RAZ/WI to NS */
s->vectors[ARMV7M_EXCP_SECURE].active = (value & (1 << 4)) != 0;
s->vectors[ARMV7M_EXCP_SECURE].enabled = (value & (1 << 19)) != 0;
@ -1368,6 +1439,86 @@ static void nvic_writel(NVICState *s, uint32_t offset, uint32_t value,
* only affect cacheability, and we don't implement caching.
*/
break;
case 0xdd0: /* SAU_CTRL */
if (!arm_feature(&cpu->env, ARM_FEATURE_V8)) {
goto bad_offset;
}
if (!attrs.secure) {
return;
}
cpu->env.sau.ctrl = value & 3;
case 0xdd4: /* SAU_TYPE */
if (!arm_feature(&cpu->env, ARM_FEATURE_V8)) {
goto bad_offset;
}
break;
case 0xdd8: /* SAU_RNR */
if (!arm_feature(&cpu->env, ARM_FEATURE_V8)) {
goto bad_offset;
}
if (!attrs.secure) {
return;
}
if (value >= cpu->sau_sregion) {
qemu_log_mask(LOG_GUEST_ERROR, "SAU region out of range %"
PRIu32 "/%" PRIu32 "\n",
value, cpu->sau_sregion);
} else {
cpu->env.sau.rnr = value;
}
break;
case 0xddc: /* SAU_RBAR */
{
int region = cpu->env.sau.rnr;
if (!arm_feature(&cpu->env, ARM_FEATURE_V8)) {
goto bad_offset;
}
if (!attrs.secure) {
return;
}
if (region >= cpu->sau_sregion) {
return;
}
cpu->env.sau.rbar[region] = value & ~0x1f;
tlb_flush(CPU(cpu));
break;
}
case 0xde0: /* SAU_RLAR */
{
int region = cpu->env.sau.rnr;
if (!arm_feature(&cpu->env, ARM_FEATURE_V8)) {
goto bad_offset;
}
if (!attrs.secure) {
return;
}
if (region >= cpu->sau_sregion) {
return;
}
cpu->env.sau.rlar[region] = value & ~0x1c;
tlb_flush(CPU(cpu));
break;
}
case 0xde4: /* SFSR */
if (!arm_feature(&cpu->env, ARM_FEATURE_V8)) {
goto bad_offset;
}
if (!attrs.secure) {
return;
}
cpu->env.v7m.sfsr &= ~value; /* W1C */
break;
case 0xde8: /* SFAR */
if (!arm_feature(&cpu->env, ARM_FEATURE_V8)) {
goto bad_offset;
}
if (!attrs.secure) {
return;
}
cpu->env.v7m.sfsr = value;
break;
case 0xf00: /* Software Triggered Interrupt Register */
{
int excnum = (value & 0x1ff) + NVIC_FIRST_IRQ;
@ -1782,6 +1933,11 @@ static void armv7m_nvic_reset(DeviceState *dev)
int resetprio;
NVICState *s = NVIC(dev);
memset(s->vectors, 0, sizeof(s->vectors));
memset(s->sec_vectors, 0, sizeof(s->sec_vectors));
s->prigroup[M_REG_NS] = 0;
s->prigroup[M_REG_S] = 0;
s->vectors[ARMV7M_EXCP_NMI].enabled = 1;
/* MEM, BUS, and USAGE are enabled through
* the System Handler Control register

View File

@ -1797,8 +1797,13 @@ uint8_t sd_read_data(SDState *sd)
break;
case 18: /* CMD18: READ_MULTIPLE_BLOCK */
if (sd->data_offset == 0)
if (sd->data_offset == 0) {
if (sd->data_start + io_len > sd->size) {
sd->card_status |= ADDRESS_ERROR;
return 0x00;
}
BLK_READ_BLOCK(sd->data_start, io_len);
}
ret = sd->data[sd->data_offset ++];
if (sd->data_offset >= io_len) {
@ -1812,11 +1817,6 @@ uint8_t sd_read_data(SDState *sd)
break;
}
}
if (sd->data_start + io_len > sd->size) {
sd->card_status |= ADDRESS_ERROR;
break;
}
}
break;

View File

@ -285,6 +285,18 @@ static void arm_cpu_reset(CPUState *s)
env->pmsav8.mair1[M_REG_S] = 0;
}
if (arm_feature(env, ARM_FEATURE_M_SECURITY)) {
if (cpu->sau_sregion > 0) {
memset(env->sau.rbar, 0, sizeof(*env->sau.rbar) * cpu->sau_sregion);
memset(env->sau.rlar, 0, sizeof(*env->sau.rlar) * cpu->sau_sregion);
}
env->sau.rnr = 0;
/* SAU_CTRL reset value is IMPDEF; we choose 0, which is what
* the Cortex-M33 does.
*/
env->sau.ctrl = 0;
}
set_flush_to_zero(1, &env->vfp.standard_fp_status);
set_flush_inputs_to_zero(1, &env->vfp.standard_fp_status);
set_default_nan_mode(1, &env->vfp.standard_fp_status);
@ -873,6 +885,20 @@ static void arm_cpu_realizefn(DeviceState *dev, Error **errp)
}
}
if (arm_feature(env, ARM_FEATURE_M_SECURITY)) {
uint32_t nr = cpu->sau_sregion;
if (nr > 0xff) {
error_setg(errp, "v8M SAU #regions invalid %" PRIu32, nr);
return;
}
if (nr) {
env->sau.rbar = g_new0(uint32_t, nr);
env->sau.rlar = g_new0(uint32_t, nr);
}
}
if (arm_feature(env, ARM_FEATURE_EL3)) {
set_feature(env, ARM_FEATURE_VBAR);
}
@ -1141,6 +1167,7 @@ static void cortex_m4_initfn(Object *obj)
cpu->midr = 0x410fc240; /* r0p0 */
cpu->pmsav7_dregion = 8;
}
static void arm_v7m_class_init(ObjectClass *oc, void *data)
{
CPUClass *cc = CPU_CLASS(oc);

View File

@ -443,8 +443,10 @@ typedef struct CPUARMState {
uint32_t cfsr[M_REG_NUM_BANKS]; /* Configurable Fault Status */
uint32_t hfsr; /* HardFault Status */
uint32_t dfsr; /* Debug Fault Status Register */
uint32_t sfsr; /* Secure Fault Status Register */
uint32_t mmfar[M_REG_NUM_BANKS]; /* MemManage Fault Address */
uint32_t bfar; /* BusFault Address */
uint32_t sfar; /* Secure Fault Address Register */
unsigned mpu_ctrl[M_REG_NUM_BANKS]; /* MPU_CTRL */
int exception;
uint32_t primask[M_REG_NUM_BANKS];
@ -566,6 +568,14 @@ typedef struct CPUARMState {
uint32_t mair1[M_REG_NUM_BANKS];
} pmsav8;
/* v8M SAU */
struct {
uint32_t *rbar;
uint32_t *rlar;
uint32_t rnr;
uint32_t ctrl;
} sau;
void *nvic;
const struct arm_boot_info *boot_info;
/* Store GICv3CPUState to access from this struct */
@ -661,6 +671,8 @@ struct ARMCPU {
bool has_mpu;
/* PMSAv7 MPU number of supported regions */
uint32_t pmsav7_dregion;
/* v8M SAU number of supported regions */
uint32_t sau_sregion;
/* PSCI conduit used to invoke PSCI methods
* 0 - disabled, 1 - smc, 2 - hvc
@ -991,6 +1003,11 @@ void pmccntr_sync(CPUARMState *env);
#define PSTATE_MODE_EL1t 4
#define PSTATE_MODE_EL0t 0
/* Write a new value to v7m.exception, thus transitioning into or out
* of Handler mode; this may result in a change of active stack pointer.
*/
void write_v7m_exception(CPUARMState *env, uint32_t new_exc);
/* Map EL and handler into a PSTATE_MODE. */
static inline unsigned int aarch64_pstate_mode(unsigned int el, bool handler)
{
@ -1071,7 +1088,8 @@ static inline void xpsr_write(CPUARMState *env, uint32_t val, uint32_t mask)
env->condexec_bits |= (val >> 8) & 0xfc;
}
if (mask & XPSR_EXCP) {
env->v7m.exception = val & XPSR_EXCP;
/* Note that this only happens on exception exit */
write_v7m_exception(env, val & XPSR_EXCP);
}
}
@ -1254,6 +1272,16 @@ FIELD(V7M_DFSR, DWTTRAP, 2, 1)
FIELD(V7M_DFSR, VCATCH, 3, 1)
FIELD(V7M_DFSR, EXTERNAL, 4, 1)
/* V7M SFSR bits */
FIELD(V7M_SFSR, INVEP, 0, 1)
FIELD(V7M_SFSR, INVIS, 1, 1)
FIELD(V7M_SFSR, INVER, 2, 1)
FIELD(V7M_SFSR, AUVIOL, 3, 1)
FIELD(V7M_SFSR, INVTRAN, 4, 1)
FIELD(V7M_SFSR, LSPERR, 5, 1)
FIELD(V7M_SFSR, SFARVALID, 6, 1)
FIELD(V7M_SFSR, LSERR, 7, 1)
/* v7M MPU_CTRL bits */
FIELD(V7M_MPU_CTRL, ENABLE, 0, 1)
FIELD(V7M_MPU_CTRL, HFNMIENA, 1, 1)
@ -2301,21 +2329,33 @@ static inline int arm_mmu_idx_to_el(ARMMMUIdx mmu_idx)
}
}
/* Return the MMU index for a v7M CPU in the specified security state */
static inline ARMMMUIdx arm_v7m_mmu_idx_for_secstate(CPUARMState *env,
bool secstate)
{
int el = arm_current_el(env);
ARMMMUIdx mmu_idx;
if (el == 0) {
mmu_idx = secstate ? ARMMMUIdx_MSUser : ARMMMUIdx_MUser;
} else {
mmu_idx = secstate ? ARMMMUIdx_MSPriv : ARMMMUIdx_MPriv;
}
if (armv7m_nvic_neg_prio_requested(env->nvic, secstate)) {
mmu_idx = secstate ? ARMMMUIdx_MSNegPri : ARMMMUIdx_MNegPri;
}
return mmu_idx;
}
/* Determine the current mmu_idx to use for normal loads/stores */
static inline int cpu_mmu_index(CPUARMState *env, bool ifetch)
{
int el = arm_current_el(env);
if (arm_feature(env, ARM_FEATURE_M)) {
ARMMMUIdx mmu_idx = el == 0 ? ARMMMUIdx_MUser : ARMMMUIdx_MPriv;
if (armv7m_nvic_neg_prio_requested(env->nvic, env->v7m.secure)) {
mmu_idx = ARMMMUIdx_MNegPri;
}
if (env->v7m.secure) {
mmu_idx += ARMMMUIdx_MSUser;
}
ARMMMUIdx mmu_idx = arm_v7m_mmu_idx_for_secstate(env, env->v7m.secure);
return arm_to_core_mmu_idx(mmu_idx);
}

View File

@ -31,6 +31,16 @@ static bool get_phys_addr_lpae(CPUARMState *env, target_ulong address,
target_ulong *page_size_ptr, uint32_t *fsr,
ARMMMUFaultInfo *fi);
/* Security attributes for an address, as returned by v8m_security_lookup. */
typedef struct V8M_SAttributes {
bool ns;
bool nsc;
uint8_t sregion;
bool srvalid;
uint8_t iregion;
bool irvalid;
} V8M_SAttributes;
/* Definitions for the PMCCNTR and PMCR registers */
#define PMCRD 0x8
#define PMCRC 0x4
@ -3717,7 +3727,14 @@ static void hcr_write(CPUARMState *env, const ARMCPRegInfo *ri, uint64_t value)
if (arm_feature(env, ARM_FEATURE_EL3)) {
valid_mask &= ~HCR_HCD;
} else {
} else if (cpu->psci_conduit != QEMU_PSCI_CONDUIT_SMC) {
/* Architecturally HCR.TSC is RES0 if EL3 is not implemented.
* However, if we're using the SMC PSCI conduit then QEMU is
* effectively acting like EL3 firmware and so the guest at
* EL2 should retain the ability to prevent EL1 from being
* able to make SMC calls into the ersatz firmware, so in
* that case HCR.TSC should be read/write.
*/
valid_mask &= ~HCR_TSC;
}
@ -6040,16 +6057,6 @@ static void v7m_push(CPUARMState *env, uint32_t val)
stl_phys(cs->as, env->regs[13], val);
}
static uint32_t v7m_pop(CPUARMState *env)
{
CPUState *cs = CPU(arm_env_get_cpu(env));
uint32_t val;
val = ldl_phys(cs->as, env->regs[13]);
env->regs[13] += 4;
return val;
}
/* Return true if we're using the process stack pointer (not the MSP) */
static bool v7m_using_psp(CPUARMState *env)
{
@ -6062,21 +6069,58 @@ static bool v7m_using_psp(CPUARMState *env)
env->v7m.control[env->v7m.secure] & R_V7M_CONTROL_SPSEL_MASK;
}
/* Switch to V7M main or process stack pointer. */
static void switch_v7m_sp(CPUARMState *env, bool new_spsel)
/* Write to v7M CONTROL.SPSEL bit for the specified security bank.
* This may change the current stack pointer between Main and Process
* stack pointers if it is done for the CONTROL register for the current
* security state.
*/
static void write_v7m_control_spsel_for_secstate(CPUARMState *env,
bool new_spsel,
bool secstate)
{
uint32_t tmp;
uint32_t old_control = env->v7m.control[env->v7m.secure];
bool old_spsel = old_control & R_V7M_CONTROL_SPSEL_MASK;
bool old_is_psp = v7m_using_psp(env);
if (old_spsel != new_spsel) {
env->v7m.control[secstate] =
deposit32(env->v7m.control[secstate],
R_V7M_CONTROL_SPSEL_SHIFT,
R_V7M_CONTROL_SPSEL_LENGTH, new_spsel);
if (secstate == env->v7m.secure) {
bool new_is_psp = v7m_using_psp(env);
uint32_t tmp;
if (old_is_psp != new_is_psp) {
tmp = env->v7m.other_sp;
env->v7m.other_sp = env->regs[13];
env->regs[13] = tmp;
}
}
}
/* Write to v7M CONTROL.SPSEL bit. This may change the current
* stack pointer between Main and Process stack pointers.
*/
static void write_v7m_control_spsel(CPUARMState *env, bool new_spsel)
{
write_v7m_control_spsel_for_secstate(env, new_spsel, env->v7m.secure);
}
void write_v7m_exception(CPUARMState *env, uint32_t new_exc)
{
/* Write a new value to v7m.exception, thus transitioning into or out
* of Handler mode; this may result in a change of active stack pointer.
*/
bool new_is_psp, old_is_psp = v7m_using_psp(env);
uint32_t tmp;
env->v7m.exception = new_exc;
new_is_psp = v7m_using_psp(env);
if (old_is_psp != new_is_psp) {
tmp = env->v7m.other_sp;
env->v7m.other_sp = env->regs[13];
env->regs[13] = tmp;
env->v7m.control[env->v7m.secure] = deposit32(old_control,
R_V7M_CONTROL_SPSEL_SHIFT,
R_V7M_CONTROL_SPSEL_LENGTH, new_spsel);
}
}
@ -6141,12 +6185,47 @@ void HELPER(v7m_bxns)(CPUARMState *env, uint32_t dest)
env->regs[15] = dest & ~1;
}
static uint32_t arm_v7m_load_vector(ARMCPU *cpu)
static uint32_t *get_v7m_sp_ptr(CPUARMState *env, bool secure, bool threadmode,
bool spsel)
{
/* Return a pointer to the location where we currently store the
* stack pointer for the requested security state and thread mode.
* This pointer will become invalid if the CPU state is updated
* such that the stack pointers are switched around (eg changing
* the SPSEL control bit).
* Compare the v8M ARM ARM pseudocode LookUpSP_with_security_mode().
* Unlike that pseudocode, we require the caller to pass us in the
* SPSEL control bit value; this is because we also use this
* function in handling of pushing of the callee-saves registers
* part of the v8M stack frame (pseudocode PushCalleeStack()),
* and in the tailchain codepath the SPSEL bit comes from the exception
* return magic LR value from the previous exception. The pseudocode
* opencodes the stack-selection in PushCalleeStack(), but we prefer
* to make this utility function generic enough to do the job.
*/
bool want_psp = threadmode && spsel;
if (secure == env->v7m.secure) {
if (want_psp == v7m_using_psp(env)) {
return &env->regs[13];
} else {
return &env->v7m.other_sp;
}
} else {
if (want_psp) {
return &env->v7m.other_ss_psp;
} else {
return &env->v7m.other_ss_msp;
}
}
}
static uint32_t arm_v7m_load_vector(ARMCPU *cpu, bool targets_secure)
{
CPUState *cs = CPU(cpu);
CPUARMState *env = &cpu->env;
MemTxResult result;
hwaddr vec = env->v7m.vecbase[env->v7m.secure] + env->v7m.exception * 4;
hwaddr vec = env->v7m.vecbase[targets_secure] + env->v7m.exception * 4;
uint32_t addr;
addr = address_space_ldl(cs->as, vec,
@ -6158,13 +6237,48 @@ static uint32_t arm_v7m_load_vector(ARMCPU *cpu)
* Since we don't model Lockup, we just report this guest error
* via cpu_abort().
*/
cpu_abort(cs, "Failed to read from exception vector table "
"entry %08x\n", (unsigned)vec);
cpu_abort(cs, "Failed to read from %s exception vector table "
"entry %08x\n", targets_secure ? "secure" : "nonsecure",
(unsigned)vec);
}
return addr;
}
static void v7m_exception_taken(ARMCPU *cpu, uint32_t lr)
static void v7m_push_callee_stack(ARMCPU *cpu, uint32_t lr, bool dotailchain)
{
/* 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;
if (dotailchain) {
frame_sp_p = get_v7m_sp_ptr(env, true,
lr & R_V7M_EXCRET_MODE_MASK,
lr & R_V7M_EXCRET_SPSEL_MASK);
} else {
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]);
*frame_sp_p = frameptr;
}
static void v7m_exception_taken(ARMCPU *cpu, uint32_t lr, bool dotailchain)
{
/* Do the "take the exception" parts of exception entry,
* but not the pushing of state to the stack. This is
@ -6172,14 +6286,84 @@ static void v7m_exception_taken(ARMCPU *cpu, uint32_t lr)
*/
CPUARMState *env = &cpu->env;
uint32_t addr;
bool targets_secure;
armv7m_nvic_acknowledge_irq(env->nvic);
switch_v7m_sp(env, 0);
targets_secure = armv7m_nvic_acknowledge_irq(env->nvic);
if (arm_feature(env, ARM_FEATURE_V8)) {
if (arm_feature(env, ARM_FEATURE_M_SECURITY) &&
(lr & R_V7M_EXCRET_S_MASK)) {
/* The background code (the owner of the registers in the
* exception frame) is Secure. This means it may either already
* have or now needs to push callee-saves registers.
*/
if (targets_secure) {
if (dotailchain && !(lr & R_V7M_EXCRET_ES_MASK)) {
/* We took an exception from Secure to NonSecure
* (which means the callee-saved registers got stacked)
* and are now tailchaining to a Secure exception.
* Clear DCRS so eventual return from this Secure
* exception unstacks the callee-saved registers.
*/
lr &= ~R_V7M_EXCRET_DCRS_MASK;
}
} else {
/* We're going to a non-secure exception; push the
* callee-saves registers to the stack now, if they're
* not already saved.
*/
if (lr & R_V7M_EXCRET_DCRS_MASK &&
!(dotailchain && (lr & R_V7M_EXCRET_ES_MASK))) {
v7m_push_callee_stack(cpu, lr, dotailchain);
}
lr |= R_V7M_EXCRET_DCRS_MASK;
}
}
lr &= ~R_V7M_EXCRET_ES_MASK;
if (targets_secure || !arm_feature(env, ARM_FEATURE_M_SECURITY)) {
lr |= R_V7M_EXCRET_ES_MASK;
}
lr &= ~R_V7M_EXCRET_SPSEL_MASK;
if (env->v7m.control[targets_secure] & R_V7M_CONTROL_SPSEL_MASK) {
lr |= R_V7M_EXCRET_SPSEL_MASK;
}
/* Clear registers if necessary to prevent non-secure exception
* code being able to see register values from secure code.
* Where register values become architecturally UNKNOWN we leave
* them with their previous values.
*/
if (arm_feature(env, ARM_FEATURE_M_SECURITY)) {
if (!targets_secure) {
/* Always clear the caller-saved registers (they have been
* pushed to the stack earlier in v7m_push_stack()).
* Clear callee-saved registers if the background code is
* Secure (in which case these regs were saved in
* v7m_push_callee_stack()).
*/
int i;
for (i = 0; i < 13; i++) {
/* r4..r11 are callee-saves, zero only if EXCRET.S == 1 */
if (i < 4 || i > 11 || (lr & R_V7M_EXCRET_S_MASK)) {
env->regs[i] = 0;
}
}
/* Clear EAPSR */
xpsr_write(env, 0, XPSR_NZCV | XPSR_Q | XPSR_GE | XPSR_IT);
}
}
}
/* Switch to target security state -- must do this before writing SPSEL */
switch_v7m_security_state(env, targets_secure);
write_v7m_control_spsel(env, 0);
arm_clear_exclusive(env);
/* Clear IT bits */
env->condexec_bits = 0;
env->regs[14] = lr;
addr = arm_v7m_load_vector(cpu);
addr = arm_v7m_load_vector(cpu, targets_secure);
env->regs[15] = addr & 0xfffffffe;
env->thumb = addr & 1;
}
@ -6212,13 +6396,16 @@ static void v7m_push_stack(ARMCPU *cpu)
static void do_v7m_exception_exit(ARMCPU *cpu)
{
CPUARMState *env = &cpu->env;
CPUState *cs = CPU(cpu);
uint32_t excret;
uint32_t xpsr;
bool ufault = false;
bool return_to_sp_process = false;
bool return_to_handler = false;
bool sfault = false;
bool return_to_sp_process;
bool return_to_handler;
bool rettobase = false;
bool exc_secure = false;
bool return_to_secure;
/* We can only get here from an EXCP_EXCEPTION_EXIT, and
* gen_bx_excret() enforces the architectural rule
@ -6249,6 +6436,19 @@ static void do_v7m_exception_exit(ARMCPU *cpu)
excret);
}
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.
*/
if (!env->v7m.secure &&
((excret & R_V7M_EXCRET_ES_MASK) ||
!(excret & R_V7M_EXCRET_DCRS_MASK))) {
sfault = 1;
/* For all other purposes, treat ES as 0 (R_HXSR) */
excret &= ~R_V7M_EXCRET_ES_MASK;
}
}
if (env->v7m.exception != ARMV7M_EXCP_NMI) {
/* Auto-clear FAULTMASK on return from other than NMI.
* If the security extension is implemented then this only
@ -6286,21 +6486,53 @@ static void do_v7m_exception_exit(ARMCPU *cpu)
g_assert_not_reached();
}
switch (excret & 0xf) {
case 1: /* Return to Handler */
return_to_handler = true;
break;
case 13: /* Return to Thread using Process stack */
return_to_sp_process = true;
/* fall through */
case 9: /* Return to Thread using Main stack */
if (!rettobase &&
!(env->v7m.ccr[env->v7m.secure] & R_V7M_CCR_NONBASETHRDENA_MASK)) {
return_to_handler = !(excret & R_V7M_EXCRET_MODE_MASK);
return_to_sp_process = excret & R_V7M_EXCRET_SPSEL_MASK;
return_to_secure = arm_feature(env, ARM_FEATURE_M_SECURITY) &&
(excret & R_V7M_EXCRET_S_MASK);
if (arm_feature(env, ARM_FEATURE_V8)) {
if (!arm_feature(env, ARM_FEATURE_M_SECURITY)) {
/* UNPREDICTABLE if S == 1 or DCRS == 0 or ES == 1 (R_XLCP);
* we choose to take the UsageFault.
*/
if ((excret & R_V7M_EXCRET_S_MASK) ||
(excret & R_V7M_EXCRET_ES_MASK) ||
!(excret & R_V7M_EXCRET_DCRS_MASK)) {
ufault = true;
}
}
if (excret & R_V7M_EXCRET_RES0_MASK) {
ufault = true;
}
break;
default:
ufault = true;
} else {
/* For v7M we only recognize certain combinations of the low bits */
switch (excret & 0xf) {
case 1: /* Return to Handler */
break;
case 13: /* Return to Thread using Process stack */
case 9: /* Return to Thread using Main stack */
/* We only need to check NONBASETHRDENA for v7M, because in
* v8M this bit does not exist (it is RES1).
*/
if (!rettobase &&
!(env->v7m.ccr[env->v7m.secure] &
R_V7M_CCR_NONBASETHRDENA_MASK)) {
ufault = true;
}
break;
default:
ufault = true;
}
}
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);
qemu_log_mask(CPU_LOG_INT, "...taking SecureFault on existing "
"stackframe: failed EXC_RETURN.ES validity check\n");
return;
}
if (ufault) {
@ -6309,52 +6541,157 @@ static void do_v7m_exception_exit(ARMCPU *cpu)
*/
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);
v7m_exception_taken(cpu, excret, true);
qemu_log_mask(CPU_LOG_INT, "...taking UsageFault on existing "
"stackframe: failed exception return integrity check\n");
return;
}
/* Switch to the target stack. */
switch_v7m_sp(env, return_to_sp_process);
/* Pop registers. */
env->regs[0] = v7m_pop(env);
env->regs[1] = v7m_pop(env);
env->regs[2] = v7m_pop(env);
env->regs[3] = v7m_pop(env);
env->regs[12] = v7m_pop(env);
env->regs[14] = v7m_pop(env);
env->regs[15] = v7m_pop(env);
if (env->regs[15] & 1) {
qemu_log_mask(LOG_GUEST_ERROR,
"M profile return from interrupt with misaligned "
"PC is UNPREDICTABLE\n");
/* Actual hardware seems to ignore the lsbit, and there are several
* RTOSes out there which incorrectly assume the r15 in the stack
* frame should be a Thumb-style "lsbit indicates ARM/Thumb" value.
/* Set CONTROL.SPSEL from excret.SPSEL. Since we're still in
* Handler mode (and will be until we write the new XPSR.Interrupt
* field) this does not switch around the current stack pointer.
*/
write_v7m_control_spsel_for_secstate(env, return_to_sp_process, exc_secure);
switch_v7m_security_state(env, return_to_secure);
{
/* The stack pointer we should be reading the exception frame from
* depends on bits in the magic exception return type value (and
* for v8M isn't necessarily the stack pointer we will eventually
* end up resuming execution with). Get a pointer to the location
* in the CPU state struct where the SP we need is currently being
* stored; we will use and modify it in place.
* We use this limited C variable scope so we don't accidentally
* use 'frame_sp_p' after we do something that makes it invalid.
*/
env->regs[15] &= ~1U;
uint32_t *frame_sp_p = get_v7m_sp_ptr(env,
return_to_secure,
!return_to_handler,
return_to_sp_process);
uint32_t frameptr = *frame_sp_p;
if (!QEMU_IS_ALIGNED(frameptr, 8) &&
arm_feature(env, ARM_FEATURE_V8)) {
qemu_log_mask(LOG_GUEST_ERROR,
"M profile exception return with non-8-aligned SP "
"for destination state is UNPREDICTABLE\n");
}
/* Do we need to pop callee-saved registers? */
if (return_to_secure &&
((excret & R_V7M_EXCRET_ES_MASK) == 0 ||
(excret & R_V7M_EXCRET_DCRS_MASK) == 0)) {
uint32_t expected_sig = 0xfefa125b;
uint32_t actual_sig = ldl_phys(cs->as, frameptr);
if (expected_sig != actual_sig) {
/* 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);
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);
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);
/* 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
* to be UNPREDICTABLE. In practice actual v7M hardware seems to ignore
* the lsbit, and there are several RTOSes out there which incorrectly
* assume the r15 in the stack frame should be a Thumb-style "lsbit
* indicates ARM/Thumb" value, so ignore the bit on v7M as well, but
* complain about the badly behaved guest.
*/
if (env->regs[15] & 1) {
env->regs[15] &= ~1U;
if (!arm_feature(env, ARM_FEATURE_V8)) {
qemu_log_mask(LOG_GUEST_ERROR,
"M profile return from interrupt with misaligned "
"PC is UNPREDICTABLE on v7M\n");
}
}
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
* before we commit to changing the SP and xPSR.
*/
bool will_be_handler = (xpsr & XPSR_EXCP) != 0;
if (return_to_handler != will_be_handler) {
/* Take an INVPC UsageFault on the current stack.
* By this point we will have switched to the security state
* for the background state, so this UsageFault will target
* that state.
*/
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);
qemu_log_mask(CPU_LOG_INT, "...taking UsageFault on existing "
"stackframe: failed exception return integrity "
"check\n");
return;
}
}
/* Commit to consuming the stack frame */
frameptr += 0x20;
/* 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
* from the current 8-aligned value to the 8-unaligned value. (Adding 4
* would work too but a logical OR is how the pseudocode specifies it.)
*/
if (xpsr & XPSR_SPREALIGN) {
frameptr |= 4;
}
*frame_sp_p = frameptr;
}
xpsr = v7m_pop(env);
/* This xpsr_write() will invalidate frame_sp_p as it may switch stack */
xpsr_write(env, xpsr, ~XPSR_SPREALIGN);
/* Undo stack alignment. */
if (xpsr & XPSR_SPREALIGN) {
env->regs[13] |= 4;
}
/* The restored xPSR exception field will be zero if we're
* resuming in Thread mode. If that doesn't match what the
* exception return excret specified then this is a UsageFault.
* v7M requires we make this check here; v8M did it earlier.
*/
if (return_to_handler != arm_v7m_is_handler_mode(env)) {
/* Take an INVPC UsageFault by pushing the stack again.
* TODO: the v8M version of this code should target the
* background state for this exception.
/* Take an INVPC UsageFault by pushing the stack again;
* we know we're v7M so this is never a Secure UsageFault.
*/
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);
v7m_exception_taken(cpu, excret, false);
qemu_log_mask(CPU_LOG_INT, "...taking UsageFault on new stackframe: "
"failed exception return integrity check\n");
return;
@ -6433,6 +6770,46 @@ void arm_v7m_cpu_do_interrupt(CPUState *cs)
* raises the fault, in the A profile short-descriptor format.
*/
switch (env->exception.fsr & 0xf) {
case M_FAKE_FSR_NSC_EXEC:
/* Exception generated when we try to execute code at an address
* which is marked as Secure & Non-Secure Callable and the CPU
* is in the Non-Secure state. The only instruction which can
* be executed like this is SG (and that only if both halves of
* the SG instruction have the same security attributes.)
* Everything else must generate an INVEP SecureFault, so we
* emulate the SG instruction here.
* TODO: actually emulate SG.
*/
env->v7m.sfsr |= R_V7M_SFSR_INVEP_MASK;
armv7m_nvic_set_pending(env->nvic, ARMV7M_EXCP_SECURE, false);
qemu_log_mask(CPU_LOG_INT,
"...really SecureFault with SFSR.INVEP\n");
break;
case M_FAKE_FSR_SFAULT:
/* Various flavours of SecureFault for attempts to execute or
* access data in the wrong security state.
*/
switch (cs->exception_index) {
case EXCP_PREFETCH_ABORT:
if (env->v7m.secure) {
env->v7m.sfsr |= R_V7M_SFSR_INVTRAN_MASK;
qemu_log_mask(CPU_LOG_INT,
"...really SecureFault with SFSR.INVTRAN\n");
} else {
env->v7m.sfsr |= R_V7M_SFSR_INVEP_MASK;
qemu_log_mask(CPU_LOG_INT,
"...really SecureFault with SFSR.INVEP\n");
}
break;
case EXCP_DATA_ABORT:
/* This must be an NS access to S memory */
env->v7m.sfsr |= R_V7M_SFSR_AUVIOL_MASK;
qemu_log_mask(CPU_LOG_INT,
"...really SecureFault with SFSR.AUVIOL\n");
break;
}
armv7m_nvic_set_pending(env->nvic, ARMV7M_EXCP_SECURE, false);
break;
case 0x8: /* External Abort */
switch (cs->exception_index) {
case EXCP_PREFETCH_ABORT:
@ -6498,20 +6875,40 @@ void arm_v7m_cpu_do_interrupt(CPUState *cs)
return; /* Never happens. Keep compiler happy. */
}
lr = R_V7M_EXCRET_RES1_MASK |
R_V7M_EXCRET_S_MASK |
R_V7M_EXCRET_DCRS_MASK |
R_V7M_EXCRET_FTYPE_MASK |
R_V7M_EXCRET_ES_MASK;
if (env->v7m.control[env->v7m.secure] & R_V7M_CONTROL_SPSEL_MASK) {
lr |= R_V7M_EXCRET_SPSEL_MASK;
if (arm_feature(env, ARM_FEATURE_V8)) {
lr = R_V7M_EXCRET_RES1_MASK |
R_V7M_EXCRET_DCRS_MASK |
R_V7M_EXCRET_FTYPE_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
* to Secure or NonSecure (ie our target state). We set it
* later, in v7m_exception_taken().
* The SPSEL bit is also set in v7m_exception_taken() for v8M.
* This corresponds to the ARM ARM pseudocode for v8M setting
* some LR bits in PushStack() and some in ExceptionTaken();
* the distinction matters for the tailchain cases where we
* can take an exception without pushing the stack.
*/
if (env->v7m.secure) {
lr |= R_V7M_EXCRET_S_MASK;
}
} else {
lr = R_V7M_EXCRET_RES1_MASK |
R_V7M_EXCRET_S_MASK |
R_V7M_EXCRET_DCRS_MASK |
R_V7M_EXCRET_FTYPE_MASK |
R_V7M_EXCRET_ES_MASK;
if (env->v7m.control[M_REG_NS] & R_V7M_CONTROL_SPSEL_MASK) {
lr |= R_V7M_EXCRET_SPSEL_MASK;
}
}
if (!arm_v7m_is_handler_mode(env)) {
lr |= R_V7M_EXCRET_MODE_MASK;
}
v7m_push_stack(cpu);
v7m_exception_taken(cpu, lr);
v7m_exception_taken(cpu, lr, false);
qemu_log_mask(CPU_LOG_INT, "... as %d\n", env->v7m.exception);
}
@ -8499,9 +8896,89 @@ static bool get_phys_addr_pmsav7(CPUARMState *env, uint32_t address,
return !(*prot & (1 << access_type));
}
static bool v8m_is_sau_exempt(CPUARMState *env,
uint32_t address, MMUAccessType access_type)
{
/* The architecture specifies that certain address ranges are
* exempt from v8M SAU/IDAU checks.
*/
return
(access_type == MMU_INST_FETCH && m_is_system_region(env, address)) ||
(address >= 0xe0000000 && address <= 0xe0002fff) ||
(address >= 0xe000e000 && address <= 0xe000efff) ||
(address >= 0xe002e000 && address <= 0xe002efff) ||
(address >= 0xe0040000 && address <= 0xe0041fff) ||
(address >= 0xe00ff000 && address <= 0xe00fffff);
}
static void v8m_security_lookup(CPUARMState *env, uint32_t address,
MMUAccessType access_type, ARMMMUIdx mmu_idx,
V8M_SAttributes *sattrs)
{
/* Look up the security attributes for this address. Compare the
* pseudocode SecurityCheck() function.
* We assume the caller has zero-initialized *sattrs.
*/
ARMCPU *cpu = arm_env_get_cpu(env);
int r;
/* TODO: implement IDAU */
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)) {
sattrs->ns = !regime_is_secure(env, mmu_idx);
return;
}
switch (env->sau.ctrl & 3) {
case 0: /* SAU.ENABLE == 0, SAU.ALLNS == 0 */
break;
case 2: /* SAU.ENABLE == 0, SAU.ALLNS == 1 */
sattrs->ns = true;
break;
default: /* SAU.ENABLE == 1 */
for (r = 0; r < cpu->sau_sregion; r++) {
if (env->sau.rlar[r] & 1) {
uint32_t base = env->sau.rbar[r] & ~0x1f;
uint32_t limit = env->sau.rlar[r] | 0x1f;
if (base <= address && limit >= address) {
if (sattrs->srvalid) {
/* If we hit in more than one region then we must report
* as Secure, not NS-Callable, with no valid region
* number info.
*/
sattrs->ns = false;
sattrs->nsc = false;
sattrs->sregion = 0;
sattrs->srvalid = false;
break;
} else {
if (env->sau.rlar[r] & 2) {
sattrs->nsc = true;
} else {
sattrs->ns = true;
}
sattrs->srvalid = true;
sattrs->sregion = r;
}
}
}
}
/* TODO when we support the IDAU then it may override the result here */
break;
}
}
static bool get_phys_addr_pmsav8(CPUARMState *env, uint32_t address,
MMUAccessType access_type, ARMMMUIdx mmu_idx,
hwaddr *phys_ptr, int *prot, uint32_t *fsr)
hwaddr *phys_ptr, MemTxAttrs *txattrs,
int *prot, uint32_t *fsr)
{
ARMCPU *cpu = arm_env_get_cpu(env);
bool is_user = regime_is_user(env, mmu_idx);
@ -8509,10 +8986,58 @@ static bool get_phys_addr_pmsav8(CPUARMState *env, uint32_t address,
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;
}
}
}
/* Unlike the ARM ARM pseudocode, we don't need to check whether this
* was an exception vector read from the vector table (which is always
* done using the default system address map), because those accesses
@ -8777,7 +9302,7 @@ static bool get_phys_addr(CPUARMState *env, target_ulong address,
if (arm_feature(env, ARM_FEATURE_V8)) {
/* PMSAv8 */
ret = get_phys_addr_pmsav8(env, address, access_type, mmu_idx,
phys_ptr, prot, fsr);
phys_ptr, attrs, prot, fsr);
} else if (arm_feature(env, ARM_FEATURE_V7)) {
/* PMSAv7 */
ret = get_phys_addr_pmsav7(env, address, access_type, mmu_idx,
@ -9100,11 +9625,11 @@ void HELPER(v7m_msr)(CPUARMState *env, uint32_t maskreg, uint32_t val)
case 20: /* CONTROL */
/* Writing to the SPSEL bit only has an effect if we are in
* thread mode; other bits can be updated by any privileged code.
* switch_v7m_sp() deals with updating the SPSEL bit in
* write_v7m_control_spsel() deals with updating the SPSEL bit in
* env->v7m.control, so we only need update the others.
*/
if (!arm_v7m_is_handler_mode(env)) {
switch_v7m_sp(env, (val & R_V7M_CONTROL_SPSEL_MASK) != 0);
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;

View File

@ -71,6 +71,21 @@ FIELD(V7M_EXCRET, DCRS, 5, 1)
FIELD(V7M_EXCRET, S, 6, 1)
FIELD(V7M_EXCRET, RES1, 7, 25) /* including the must-be-1 prefix */
/* We use a few fake FSR values for internal purposes in M profile.
* M profile cores don't have A/R format FSRs, but currently our
* get_phys_addr() code assumes A/R profile and reports failures via
* an A/R format FSR value. We then translate that into the proper
* M profile exception and FSR status bit in arm_v7m_cpu_do_interrupt().
* Mostly the FSR values we use for this are those defined for v7PMSA,
* since we share some of that codepath. A few kinds of fault are
* only for M profile and have no A/R equivalent, though, so we have
* to pick a value from the reserved range (which we never otherwise
* generate) to use for these.
* These values will never be visible to the guest.
*/
#define M_FAKE_FSR_NSC_EXEC 0xf /* NS executing in S&NSC memory */
#define M_FAKE_FSR_SFAULT 0xe /* SecureFault INVTRAN, INVEP or AUVIOL */
/*
* For AArch64, map a given EL to an index in the banked_spsr array.
* Note that this mapping and the AArch32 mapping defined in bank_number()

View File

@ -242,6 +242,13 @@ static bool s_rnr_vmstate_validate(void *opaque, int version_id)
return cpu->env.pmsav7.rnr[M_REG_S] < cpu->pmsav7_dregion;
}
static bool sau_rnr_vmstate_validate(void *opaque, int version_id)
{
ARMCPU *cpu = opaque;
return cpu->env.sau.rnr < cpu->sau_sregion;
}
static bool m_security_needed(void *opaque)
{
ARMCPU *cpu = opaque;
@ -276,6 +283,15 @@ static const VMStateDescription vmstate_m_security = {
VMSTATE_UINT32(env.v7m.ccr[M_REG_S], ARMCPU),
VMSTATE_UINT32(env.v7m.mmfar[M_REG_S], ARMCPU),
VMSTATE_UINT32(env.v7m.cfsr[M_REG_S], ARMCPU),
VMSTATE_UINT32(env.v7m.sfsr, ARMCPU),
VMSTATE_UINT32(env.v7m.sfar, ARMCPU),
VMSTATE_VARRAY_UINT32(env.sau.rbar, ARMCPU, sau_sregion, 0,
vmstate_info_uint32, uint32_t),
VMSTATE_VARRAY_UINT32(env.sau.rlar, ARMCPU, sau_sregion, 0,
vmstate_info_uint32, uint32_t),
VMSTATE_UINT32(env.sau.rnr, ARMCPU),
VMSTATE_VALIDATE("SAU_RNR is valid", sau_rnr_vmstate_validate),
VMSTATE_UINT32(env.sau.ctrl, ARMCPU),
VMSTATE_END_OF_LIST()
}
};

View File

@ -953,22 +953,29 @@ void HELPER(pre_smc)(CPUARMState *env, uint32_t syndrome)
*/
bool undef = arm_feature(env, ARM_FEATURE_AARCH64) ? smd : smd && !secure;
if (arm_is_psci_call(cpu, EXCP_SMC)) {
/* If PSCI is enabled and this looks like a valid PSCI call then
* that overrides the architecturally mandated SMC behaviour.
if (!arm_feature(env, ARM_FEATURE_EL3) &&
cpu->psci_conduit != QEMU_PSCI_CONDUIT_SMC) {
/* If we have no EL3 then SMC always UNDEFs and can't be
* trapped to EL2. PSCI-via-SMC is a sort of ersatz EL3
* firmware within QEMU, and we want an EL2 guest to be able
* to forbid its EL1 from making PSCI calls into QEMU's
* "firmware" via HCR.TSC, so for these purposes treat
* PSCI-via-SMC as implying an EL3.
*/
return;
}
if (!arm_feature(env, ARM_FEATURE_EL3)) {
/* If we have no EL3 then SMC always UNDEFs */
undef = true;
} else if (!secure && cur_el == 1 && (env->cp15.hcr_el2 & HCR_TSC)) {
/* In NS EL1, HCR controlled routing to EL2 has priority over SMD. */
/* In NS EL1, HCR controlled routing to EL2 has priority over SMD.
* We also want an EL2 guest to be able to forbid its EL1 from
* making PSCI calls into QEMU's "firmware" via HCR.TSC.
*/
raise_exception(env, EXCP_HYP_TRAP, syndrome, 2);
}
if (undef) {
/* If PSCI is enabled and this looks like a valid PSCI call then
* suppress the UNDEF -- we'll catch the SMC exception and
* implement the PSCI call behaviour there.
*/
if (undef && !arm_is_psci_call(cpu, EXCP_SMC)) {
raise_exception(env, EXCP_UDEF, syn_uncategorized(),
exception_target_el(env));
}