mirror of https://github.com/xemu-project/xemu.git
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:
commit
5121d81e38
|
@ -440,6 +440,8 @@ static void xlnx_zynqmp_class_init(ObjectClass *oc, void *data)
|
||||||
|
|
||||||
dc->props = xlnx_zynqmp_props;
|
dc->props = xlnx_zynqmp_props;
|
||||||
dc->realize = xlnx_zynqmp_realize;
|
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 = {
|
static const TypeInfo xlnx_zynqmp_type_info = {
|
||||||
|
|
|
@ -616,7 +616,7 @@ bool armv7m_nvic_acknowledge_irq(void *opaque)
|
||||||
vec->active = 1;
|
vec->active = 1;
|
||||||
vec->pending = 0;
|
vec->pending = 0;
|
||||||
|
|
||||||
env->v7m.exception = s->vectpending;
|
write_v7m_exception(env, s->vectpending);
|
||||||
|
|
||||||
nvic_irq_update(s);
|
nvic_irq_update(s);
|
||||||
|
|
||||||
|
@ -1017,6 +1017,76 @@ static uint32_t nvic_readl(NVICState *s, uint32_t offset, MemTxAttrs attrs)
|
||||||
goto bad_offset;
|
goto bad_offset;
|
||||||
}
|
}
|
||||||
return cpu->env.pmsav8.mair1[attrs.secure];
|
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:
|
default:
|
||||||
bad_offset:
|
bad_offset:
|
||||||
qemu_log_mask(LOG_GUEST_ERROR, "NVIC: Bad read offset 0x%x\n", 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_BUS].enabled = (value & (1 << 17)) != 0;
|
||||||
s->sec_vectors[ARMV7M_EXCP_USAGE].enabled =
|
s->sec_vectors[ARMV7M_EXCP_USAGE].enabled =
|
||||||
(value & (1 << 18)) != 0;
|
(value & (1 << 18)) != 0;
|
||||||
|
s->sec_vectors[ARMV7M_EXCP_HARD].pending = (value & (1 << 21)) != 0;
|
||||||
/* SecureFault not banked, but RAZ/WI to NS */
|
/* SecureFault not banked, but RAZ/WI to NS */
|
||||||
s->vectors[ARMV7M_EXCP_SECURE].active = (value & (1 << 4)) != 0;
|
s->vectors[ARMV7M_EXCP_SECURE].active = (value & (1 << 4)) != 0;
|
||||||
s->vectors[ARMV7M_EXCP_SECURE].enabled = (value & (1 << 19)) != 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.
|
* only affect cacheability, and we don't implement caching.
|
||||||
*/
|
*/
|
||||||
break;
|
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 */
|
case 0xf00: /* Software Triggered Interrupt Register */
|
||||||
{
|
{
|
||||||
int excnum = (value & 0x1ff) + NVIC_FIRST_IRQ;
|
int excnum = (value & 0x1ff) + NVIC_FIRST_IRQ;
|
||||||
|
@ -1782,6 +1933,11 @@ static void armv7m_nvic_reset(DeviceState *dev)
|
||||||
int resetprio;
|
int resetprio;
|
||||||
NVICState *s = NVIC(dev);
|
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;
|
s->vectors[ARMV7M_EXCP_NMI].enabled = 1;
|
||||||
/* MEM, BUS, and USAGE are enabled through
|
/* MEM, BUS, and USAGE are enabled through
|
||||||
* the System Handler Control register
|
* the System Handler Control register
|
||||||
|
|
12
hw/sd/sd.c
12
hw/sd/sd.c
|
@ -1797,8 +1797,13 @@ uint8_t sd_read_data(SDState *sd)
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 18: /* CMD18: READ_MULTIPLE_BLOCK */
|
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);
|
BLK_READ_BLOCK(sd->data_start, io_len);
|
||||||
|
}
|
||||||
ret = sd->data[sd->data_offset ++];
|
ret = sd->data[sd->data_offset ++];
|
||||||
|
|
||||||
if (sd->data_offset >= io_len) {
|
if (sd->data_offset >= io_len) {
|
||||||
|
@ -1812,11 +1817,6 @@ uint8_t sd_read_data(SDState *sd)
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (sd->data_start + io_len > sd->size) {
|
|
||||||
sd->card_status |= ADDRESS_ERROR;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
|
|
@ -285,6 +285,18 @@ static void arm_cpu_reset(CPUState *s)
|
||||||
env->pmsav8.mair1[M_REG_S] = 0;
|
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_to_zero(1, &env->vfp.standard_fp_status);
|
||||||
set_flush_inputs_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);
|
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)) {
|
if (arm_feature(env, ARM_FEATURE_EL3)) {
|
||||||
set_feature(env, ARM_FEATURE_VBAR);
|
set_feature(env, ARM_FEATURE_VBAR);
|
||||||
}
|
}
|
||||||
|
@ -1141,6 +1167,7 @@ static void cortex_m4_initfn(Object *obj)
|
||||||
cpu->midr = 0x410fc240; /* r0p0 */
|
cpu->midr = 0x410fc240; /* r0p0 */
|
||||||
cpu->pmsav7_dregion = 8;
|
cpu->pmsav7_dregion = 8;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void arm_v7m_class_init(ObjectClass *oc, void *data)
|
static void arm_v7m_class_init(ObjectClass *oc, void *data)
|
||||||
{
|
{
|
||||||
CPUClass *cc = CPU_CLASS(oc);
|
CPUClass *cc = CPU_CLASS(oc);
|
||||||
|
|
|
@ -443,8 +443,10 @@ typedef struct CPUARMState {
|
||||||
uint32_t cfsr[M_REG_NUM_BANKS]; /* Configurable Fault Status */
|
uint32_t cfsr[M_REG_NUM_BANKS]; /* Configurable Fault Status */
|
||||||
uint32_t hfsr; /* HardFault Status */
|
uint32_t hfsr; /* HardFault Status */
|
||||||
uint32_t dfsr; /* Debug Fault Status Register */
|
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 mmfar[M_REG_NUM_BANKS]; /* MemManage Fault Address */
|
||||||
uint32_t bfar; /* BusFault Address */
|
uint32_t bfar; /* BusFault Address */
|
||||||
|
uint32_t sfar; /* Secure Fault Address Register */
|
||||||
unsigned mpu_ctrl[M_REG_NUM_BANKS]; /* MPU_CTRL */
|
unsigned mpu_ctrl[M_REG_NUM_BANKS]; /* MPU_CTRL */
|
||||||
int exception;
|
int exception;
|
||||||
uint32_t primask[M_REG_NUM_BANKS];
|
uint32_t primask[M_REG_NUM_BANKS];
|
||||||
|
@ -566,6 +568,14 @@ typedef struct CPUARMState {
|
||||||
uint32_t mair1[M_REG_NUM_BANKS];
|
uint32_t mair1[M_REG_NUM_BANKS];
|
||||||
} pmsav8;
|
} pmsav8;
|
||||||
|
|
||||||
|
/* v8M SAU */
|
||||||
|
struct {
|
||||||
|
uint32_t *rbar;
|
||||||
|
uint32_t *rlar;
|
||||||
|
uint32_t rnr;
|
||||||
|
uint32_t ctrl;
|
||||||
|
} sau;
|
||||||
|
|
||||||
void *nvic;
|
void *nvic;
|
||||||
const struct arm_boot_info *boot_info;
|
const struct arm_boot_info *boot_info;
|
||||||
/* Store GICv3CPUState to access from this struct */
|
/* Store GICv3CPUState to access from this struct */
|
||||||
|
@ -661,6 +671,8 @@ struct ARMCPU {
|
||||||
bool has_mpu;
|
bool has_mpu;
|
||||||
/* PMSAv7 MPU number of supported regions */
|
/* PMSAv7 MPU number of supported regions */
|
||||||
uint32_t pmsav7_dregion;
|
uint32_t pmsav7_dregion;
|
||||||
|
/* v8M SAU number of supported regions */
|
||||||
|
uint32_t sau_sregion;
|
||||||
|
|
||||||
/* PSCI conduit used to invoke PSCI methods
|
/* PSCI conduit used to invoke PSCI methods
|
||||||
* 0 - disabled, 1 - smc, 2 - hvc
|
* 0 - disabled, 1 - smc, 2 - hvc
|
||||||
|
@ -991,6 +1003,11 @@ void pmccntr_sync(CPUARMState *env);
|
||||||
#define PSTATE_MODE_EL1t 4
|
#define PSTATE_MODE_EL1t 4
|
||||||
#define PSTATE_MODE_EL0t 0
|
#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. */
|
/* Map EL and handler into a PSTATE_MODE. */
|
||||||
static inline unsigned int aarch64_pstate_mode(unsigned int el, bool handler)
|
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;
|
env->condexec_bits |= (val >> 8) & 0xfc;
|
||||||
}
|
}
|
||||||
if (mask & XPSR_EXCP) {
|
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, VCATCH, 3, 1)
|
||||||
FIELD(V7M_DFSR, EXTERNAL, 4, 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 */
|
/* v7M MPU_CTRL bits */
|
||||||
FIELD(V7M_MPU_CTRL, ENABLE, 0, 1)
|
FIELD(V7M_MPU_CTRL, ENABLE, 0, 1)
|
||||||
FIELD(V7M_MPU_CTRL, HFNMIENA, 1, 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 */
|
/* Determine the current mmu_idx to use for normal loads/stores */
|
||||||
static inline int cpu_mmu_index(CPUARMState *env, bool ifetch)
|
static inline int cpu_mmu_index(CPUARMState *env, bool ifetch)
|
||||||
{
|
{
|
||||||
int el = arm_current_el(env);
|
int el = arm_current_el(env);
|
||||||
|
|
||||||
if (arm_feature(env, ARM_FEATURE_M)) {
|
if (arm_feature(env, ARM_FEATURE_M)) {
|
||||||
ARMMMUIdx mmu_idx = el == 0 ? ARMMMUIdx_MUser : ARMMMUIdx_MPriv;
|
ARMMMUIdx mmu_idx = arm_v7m_mmu_idx_for_secstate(env, env->v7m.secure);
|
||||||
|
|
||||||
if (armv7m_nvic_neg_prio_requested(env->nvic, env->v7m.secure)) {
|
|
||||||
mmu_idx = ARMMMUIdx_MNegPri;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (env->v7m.secure) {
|
|
||||||
mmu_idx += ARMMMUIdx_MSUser;
|
|
||||||
}
|
|
||||||
|
|
||||||
return arm_to_core_mmu_idx(mmu_idx);
|
return arm_to_core_mmu_idx(mmu_idx);
|
||||||
}
|
}
|
||||||
|
|
|
@ -31,6 +31,16 @@ static bool get_phys_addr_lpae(CPUARMState *env, target_ulong address,
|
||||||
target_ulong *page_size_ptr, uint32_t *fsr,
|
target_ulong *page_size_ptr, uint32_t *fsr,
|
||||||
ARMMMUFaultInfo *fi);
|
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 */
|
/* Definitions for the PMCCNTR and PMCR registers */
|
||||||
#define PMCRD 0x8
|
#define PMCRD 0x8
|
||||||
#define PMCRC 0x4
|
#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)) {
|
if (arm_feature(env, ARM_FEATURE_EL3)) {
|
||||||
valid_mask &= ~HCR_HCD;
|
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;
|
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);
|
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) */
|
/* Return true if we're using the process stack pointer (not the MSP) */
|
||||||
static bool v7m_using_psp(CPUARMState *env)
|
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;
|
env->v7m.control[env->v7m.secure] & R_V7M_CONTROL_SPSEL_MASK;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Switch to V7M main or process stack pointer. */
|
/* Write to v7M CONTROL.SPSEL bit for the specified security bank.
|
||||||
static void switch_v7m_sp(CPUARMState *env, bool new_spsel)
|
* 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;
|
bool old_is_psp = v7m_using_psp(env);
|
||||||
uint32_t old_control = env->v7m.control[env->v7m.secure];
|
|
||||||
bool old_spsel = old_control & R_V7M_CONTROL_SPSEL_MASK;
|
|
||||||
|
|
||||||
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;
|
tmp = env->v7m.other_sp;
|
||||||
env->v7m.other_sp = env->regs[13];
|
env->v7m.other_sp = env->regs[13];
|
||||||
env->regs[13] = tmp;
|
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;
|
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);
|
CPUState *cs = CPU(cpu);
|
||||||
CPUARMState *env = &cpu->env;
|
CPUARMState *env = &cpu->env;
|
||||||
MemTxResult result;
|
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;
|
uint32_t addr;
|
||||||
|
|
||||||
addr = address_space_ldl(cs->as, vec,
|
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
|
* Since we don't model Lockup, we just report this guest error
|
||||||
* via cpu_abort().
|
* via cpu_abort().
|
||||||
*/
|
*/
|
||||||
cpu_abort(cs, "Failed to read from exception vector table "
|
cpu_abort(cs, "Failed to read from %s exception vector table "
|
||||||
"entry %08x\n", (unsigned)vec);
|
"entry %08x\n", targets_secure ? "secure" : "nonsecure",
|
||||||
|
(unsigned)vec);
|
||||||
}
|
}
|
||||||
return addr;
|
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,
|
/* Do the "take the exception" parts of exception entry,
|
||||||
* but not the pushing of state to the stack. This is
|
* 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;
|
CPUARMState *env = &cpu->env;
|
||||||
uint32_t addr;
|
uint32_t addr;
|
||||||
|
bool targets_secure;
|
||||||
|
|
||||||
armv7m_nvic_acknowledge_irq(env->nvic);
|
targets_secure = armv7m_nvic_acknowledge_irq(env->nvic);
|
||||||
switch_v7m_sp(env, 0);
|
|
||||||
|
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);
|
arm_clear_exclusive(env);
|
||||||
/* Clear IT bits */
|
/* Clear IT bits */
|
||||||
env->condexec_bits = 0;
|
env->condexec_bits = 0;
|
||||||
env->regs[14] = lr;
|
env->regs[14] = lr;
|
||||||
addr = arm_v7m_load_vector(cpu);
|
addr = arm_v7m_load_vector(cpu, targets_secure);
|
||||||
env->regs[15] = addr & 0xfffffffe;
|
env->regs[15] = addr & 0xfffffffe;
|
||||||
env->thumb = addr & 1;
|
env->thumb = addr & 1;
|
||||||
}
|
}
|
||||||
|
@ -6212,13 +6396,16 @@ static void v7m_push_stack(ARMCPU *cpu)
|
||||||
static void do_v7m_exception_exit(ARMCPU *cpu)
|
static void do_v7m_exception_exit(ARMCPU *cpu)
|
||||||
{
|
{
|
||||||
CPUARMState *env = &cpu->env;
|
CPUARMState *env = &cpu->env;
|
||||||
|
CPUState *cs = CPU(cpu);
|
||||||
uint32_t excret;
|
uint32_t excret;
|
||||||
uint32_t xpsr;
|
uint32_t xpsr;
|
||||||
bool ufault = false;
|
bool ufault = false;
|
||||||
bool return_to_sp_process = false;
|
bool sfault = false;
|
||||||
bool return_to_handler = false;
|
bool return_to_sp_process;
|
||||||
|
bool return_to_handler;
|
||||||
bool rettobase = false;
|
bool rettobase = false;
|
||||||
bool exc_secure = false;
|
bool exc_secure = false;
|
||||||
|
bool return_to_secure;
|
||||||
|
|
||||||
/* We can only get here from an EXCP_EXCEPTION_EXIT, and
|
/* We can only get here from an EXCP_EXCEPTION_EXIT, and
|
||||||
* gen_bx_excret() enforces the architectural rule
|
* gen_bx_excret() enforces the architectural rule
|
||||||
|
@ -6249,6 +6436,19 @@ static void do_v7m_exception_exit(ARMCPU *cpu)
|
||||||
excret);
|
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) {
|
if (env->v7m.exception != ARMV7M_EXCP_NMI) {
|
||||||
/* Auto-clear FAULTMASK on return from other than NMI.
|
/* Auto-clear FAULTMASK on return from other than NMI.
|
||||||
* If the security extension is implemented then this only
|
* 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();
|
g_assert_not_reached();
|
||||||
}
|
}
|
||||||
|
|
||||||
switch (excret & 0xf) {
|
return_to_handler = !(excret & R_V7M_EXCRET_MODE_MASK);
|
||||||
case 1: /* Return to Handler */
|
return_to_sp_process = excret & R_V7M_EXCRET_SPSEL_MASK;
|
||||||
return_to_handler = true;
|
return_to_secure = arm_feature(env, ARM_FEATURE_M_SECURITY) &&
|
||||||
break;
|
(excret & R_V7M_EXCRET_S_MASK);
|
||||||
case 13: /* Return to Thread using Process stack */
|
|
||||||
return_to_sp_process = true;
|
if (arm_feature(env, ARM_FEATURE_V8)) {
|
||||||
/* fall through */
|
if (!arm_feature(env, ARM_FEATURE_M_SECURITY)) {
|
||||||
case 9: /* Return to Thread using Main stack */
|
/* UNPREDICTABLE if S == 1 or DCRS == 0 or ES == 1 (R_XLCP);
|
||||||
if (!rettobase &&
|
* we choose to take the UsageFault.
|
||||||
!(env->v7m.ccr[env->v7m.secure] & R_V7M_CCR_NONBASETHRDENA_MASK)) {
|
*/
|
||||||
|
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;
|
ufault = true;
|
||||||
}
|
}
|
||||||
break;
|
} else {
|
||||||
default:
|
/* For v7M we only recognize certain combinations of the low bits */
|
||||||
ufault = true;
|
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) {
|
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;
|
env->v7m.cfsr[env->v7m.secure] |= R_V7M_CFSR_INVPC_MASK;
|
||||||
armv7m_nvic_set_pending(env->nvic, ARMV7M_EXCP_USAGE, env->v7m.secure);
|
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 "
|
qemu_log_mask(CPU_LOG_INT, "...taking UsageFault on existing "
|
||||||
"stackframe: failed exception return integrity check\n");
|
"stackframe: failed exception return integrity check\n");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Switch to the target stack. */
|
/* Set CONTROL.SPSEL from excret.SPSEL. Since we're still in
|
||||||
switch_v7m_sp(env, return_to_sp_process);
|
* Handler mode (and will be until we write the new XPSR.Interrupt
|
||||||
/* Pop registers. */
|
* field) this does not switch around the current stack pointer.
|
||||||
env->regs[0] = v7m_pop(env);
|
*/
|
||||||
env->regs[1] = v7m_pop(env);
|
write_v7m_control_spsel_for_secstate(env, return_to_sp_process, exc_secure);
|
||||||
env->regs[2] = v7m_pop(env);
|
|
||||||
env->regs[3] = v7m_pop(env);
|
switch_v7m_security_state(env, return_to_secure);
|
||||||
env->regs[12] = v7m_pop(env);
|
|
||||||
env->regs[14] = v7m_pop(env);
|
{
|
||||||
env->regs[15] = v7m_pop(env);
|
/* The stack pointer we should be reading the exception frame from
|
||||||
if (env->regs[15] & 1) {
|
* depends on bits in the magic exception return type value (and
|
||||||
qemu_log_mask(LOG_GUEST_ERROR,
|
* for v8M isn't necessarily the stack pointer we will eventually
|
||||||
"M profile return from interrupt with misaligned "
|
* end up resuming execution with). Get a pointer to the location
|
||||||
"PC is UNPREDICTABLE\n");
|
* in the CPU state struct where the SP we need is currently being
|
||||||
/* Actual hardware seems to ignore the lsbit, and there are several
|
* stored; we will use and modify it in place.
|
||||||
* RTOSes out there which incorrectly assume the r15 in the stack
|
* We use this limited C variable scope so we don't accidentally
|
||||||
* frame should be a Thumb-style "lsbit indicates ARM/Thumb" value.
|
* 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);
|
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
|
/* The restored xPSR exception field will be zero if we're
|
||||||
* resuming in Thread mode. If that doesn't match what the
|
* resuming in Thread mode. If that doesn't match what the
|
||||||
* exception return excret specified then this is a UsageFault.
|
* 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)) {
|
if (return_to_handler != arm_v7m_is_handler_mode(env)) {
|
||||||
/* Take an INVPC UsageFault by pushing the stack again.
|
/* Take an INVPC UsageFault by pushing the stack again;
|
||||||
* TODO: the v8M version of this code should target the
|
* we know we're v7M so this is never a Secure UsageFault.
|
||||||
* background state for this exception.
|
|
||||||
*/
|
*/
|
||||||
|
assert(!arm_feature(env, ARM_FEATURE_V8));
|
||||||
armv7m_nvic_set_pending(env->nvic, ARMV7M_EXCP_USAGE, false);
|
armv7m_nvic_set_pending(env->nvic, ARMV7M_EXCP_USAGE, false);
|
||||||
env->v7m.cfsr[env->v7m.secure] |= R_V7M_CFSR_INVPC_MASK;
|
env->v7m.cfsr[env->v7m.secure] |= R_V7M_CFSR_INVPC_MASK;
|
||||||
v7m_push_stack(cpu);
|
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: "
|
qemu_log_mask(CPU_LOG_INT, "...taking UsageFault on new stackframe: "
|
||||||
"failed exception return integrity check\n");
|
"failed exception return integrity check\n");
|
||||||
return;
|
return;
|
||||||
|
@ -6433,6 +6770,46 @@ void arm_v7m_cpu_do_interrupt(CPUState *cs)
|
||||||
* raises the fault, in the A profile short-descriptor format.
|
* raises the fault, in the A profile short-descriptor format.
|
||||||
*/
|
*/
|
||||||
switch (env->exception.fsr & 0xf) {
|
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 */
|
case 0x8: /* External Abort */
|
||||||
switch (cs->exception_index) {
|
switch (cs->exception_index) {
|
||||||
case EXCP_PREFETCH_ABORT:
|
case EXCP_PREFETCH_ABORT:
|
||||||
|
@ -6498,20 +6875,40 @@ void arm_v7m_cpu_do_interrupt(CPUState *cs)
|
||||||
return; /* Never happens. Keep compiler happy. */
|
return; /* Never happens. Keep compiler happy. */
|
||||||
}
|
}
|
||||||
|
|
||||||
lr = R_V7M_EXCRET_RES1_MASK |
|
if (arm_feature(env, ARM_FEATURE_V8)) {
|
||||||
R_V7M_EXCRET_S_MASK |
|
lr = R_V7M_EXCRET_RES1_MASK |
|
||||||
R_V7M_EXCRET_DCRS_MASK |
|
R_V7M_EXCRET_DCRS_MASK |
|
||||||
R_V7M_EXCRET_FTYPE_MASK |
|
R_V7M_EXCRET_FTYPE_MASK;
|
||||||
R_V7M_EXCRET_ES_MASK;
|
/* The S bit indicates whether we should return to Secure
|
||||||
if (env->v7m.control[env->v7m.secure] & R_V7M_CONTROL_SPSEL_MASK) {
|
* or NonSecure (ie our current state).
|
||||||
lr |= R_V7M_EXCRET_SPSEL_MASK;
|
* 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)) {
|
if (!arm_v7m_is_handler_mode(env)) {
|
||||||
lr |= R_V7M_EXCRET_MODE_MASK;
|
lr |= R_V7M_EXCRET_MODE_MASK;
|
||||||
}
|
}
|
||||||
|
|
||||||
v7m_push_stack(cpu);
|
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);
|
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));
|
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,
|
static bool get_phys_addr_pmsav8(CPUARMState *env, uint32_t address,
|
||||||
MMUAccessType access_type, ARMMMUIdx mmu_idx,
|
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);
|
ARMCPU *cpu = arm_env_get_cpu(env);
|
||||||
bool is_user = regime_is_user(env, mmu_idx);
|
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 n;
|
||||||
int matchregion = -1;
|
int matchregion = -1;
|
||||||
bool hit = false;
|
bool hit = false;
|
||||||
|
V8M_SAttributes sattrs = {};
|
||||||
|
|
||||||
*phys_ptr = address;
|
*phys_ptr = address;
|
||||||
*prot = 0;
|
*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
|
/* 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
|
* was an exception vector read from the vector table (which is always
|
||||||
* done using the default system address map), because those accesses
|
* 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)) {
|
if (arm_feature(env, ARM_FEATURE_V8)) {
|
||||||
/* PMSAv8 */
|
/* PMSAv8 */
|
||||||
ret = get_phys_addr_pmsav8(env, address, access_type, mmu_idx,
|
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)) {
|
} else if (arm_feature(env, ARM_FEATURE_V7)) {
|
||||||
/* PMSAv7 */
|
/* PMSAv7 */
|
||||||
ret = get_phys_addr_pmsav7(env, address, access_type, mmu_idx,
|
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 */
|
case 20: /* CONTROL */
|
||||||
/* Writing to the SPSEL bit only has an effect if we are in
|
/* Writing to the SPSEL bit only has an effect if we are in
|
||||||
* thread mode; other bits can be updated by any privileged code.
|
* 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.
|
* env->v7m.control, so we only need update the others.
|
||||||
*/
|
*/
|
||||||
if (!arm_v7m_is_handler_mode(env)) {
|
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] &= ~R_V7M_CONTROL_NPRIV_MASK;
|
||||||
env->v7m.control[env->v7m.secure] |= val & R_V7M_CONTROL_NPRIV_MASK;
|
env->v7m.control[env->v7m.secure] |= val & R_V7M_CONTROL_NPRIV_MASK;
|
||||||
|
|
|
@ -71,6 +71,21 @@ FIELD(V7M_EXCRET, DCRS, 5, 1)
|
||||||
FIELD(V7M_EXCRET, S, 6, 1)
|
FIELD(V7M_EXCRET, S, 6, 1)
|
||||||
FIELD(V7M_EXCRET, RES1, 7, 25) /* including the must-be-1 prefix */
|
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.
|
* 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()
|
* Note that this mapping and the AArch32 mapping defined in bank_number()
|
||||||
|
|
|
@ -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;
|
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)
|
static bool m_security_needed(void *opaque)
|
||||||
{
|
{
|
||||||
ARMCPU *cpu = 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.ccr[M_REG_S], ARMCPU),
|
||||||
VMSTATE_UINT32(env.v7m.mmfar[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.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()
|
VMSTATE_END_OF_LIST()
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
|
@ -953,22 +953,29 @@ void HELPER(pre_smc)(CPUARMState *env, uint32_t syndrome)
|
||||||
*/
|
*/
|
||||||
bool undef = arm_feature(env, ARM_FEATURE_AARCH64) ? smd : smd && !secure;
|
bool undef = arm_feature(env, ARM_FEATURE_AARCH64) ? smd : smd && !secure;
|
||||||
|
|
||||||
if (arm_is_psci_call(cpu, EXCP_SMC)) {
|
if (!arm_feature(env, ARM_FEATURE_EL3) &&
|
||||||
/* If PSCI is enabled and this looks like a valid PSCI call then
|
cpu->psci_conduit != QEMU_PSCI_CONDUIT_SMC) {
|
||||||
* that overrides the architecturally mandated SMC behaviour.
|
/* 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;
|
undef = true;
|
||||||
} else if (!secure && cur_el == 1 && (env->cp15.hcr_el2 & HCR_TSC)) {
|
} 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);
|
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(),
|
raise_exception(env, EXCP_UDEF, syn_uncategorized(),
|
||||||
exception_target_el(env));
|
exception_target_el(env));
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue