target/arm: Implement SG instruction
Implement the SG instruction, which we emulate 'by hand' in the exception handling code path. Signed-off-by: Peter Maydell <peter.maydell@linaro.org> Reviewed-by: Richard Henderson <richard.henderson@linaro.org> Message-id: 1507556919-24992-3-git-send-email-peter.maydell@linaro.org
This commit is contained in:
parent
b9f587d62c
commit
333e10c51e
@ -41,6 +41,10 @@ typedef struct V8M_SAttributes {
|
|||||||
bool irvalid;
|
bool irvalid;
|
||||||
} V8M_SAttributes;
|
} V8M_SAttributes;
|
||||||
|
|
||||||
|
static void v8m_security_lookup(CPUARMState *env, uint32_t address,
|
||||||
|
MMUAccessType access_type, ARMMMUIdx mmu_idx,
|
||||||
|
V8M_SAttributes *sattrs);
|
||||||
|
|
||||||
/* 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
|
||||||
@ -6736,6 +6740,126 @@ static void arm_log_exception(int idx)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static bool v7m_read_half_insn(ARMCPU *cpu, ARMMMUIdx mmu_idx,
|
||||||
|
uint32_t addr, uint16_t *insn)
|
||||||
|
{
|
||||||
|
/* Load a 16-bit portion of a v7M instruction, returning true on success,
|
||||||
|
* or false on failure (in which case we will have pended the appropriate
|
||||||
|
* exception).
|
||||||
|
* We need to do the instruction fetch's MPU and SAU checks
|
||||||
|
* like this because there is no MMU index that would allow
|
||||||
|
* doing the load with a single function call. Instead we must
|
||||||
|
* first check that the security attributes permit the load
|
||||||
|
* and that they don't mismatch on the two halves of the instruction,
|
||||||
|
* and then we do the load as a secure load (ie using the security
|
||||||
|
* attributes of the address, not the CPU, as architecturally required).
|
||||||
|
*/
|
||||||
|
CPUState *cs = CPU(cpu);
|
||||||
|
CPUARMState *env = &cpu->env;
|
||||||
|
V8M_SAttributes sattrs = {};
|
||||||
|
MemTxAttrs attrs = {};
|
||||||
|
ARMMMUFaultInfo fi = {};
|
||||||
|
MemTxResult txres;
|
||||||
|
target_ulong page_size;
|
||||||
|
hwaddr physaddr;
|
||||||
|
int prot;
|
||||||
|
uint32_t fsr;
|
||||||
|
|
||||||
|
v8m_security_lookup(env, addr, MMU_INST_FETCH, mmu_idx, &sattrs);
|
||||||
|
if (!sattrs.nsc || sattrs.ns) {
|
||||||
|
/* This must be the second half of the insn, and it straddles a
|
||||||
|
* region boundary with the second half not being S&NSC.
|
||||||
|
*/
|
||||||
|
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");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (get_phys_addr(env, addr, MMU_INST_FETCH, mmu_idx,
|
||||||
|
&physaddr, &attrs, &prot, &page_size, &fsr, &fi)) {
|
||||||
|
/* the MPU lookup failed */
|
||||||
|
env->v7m.cfsr[env->v7m.secure] |= R_V7M_CFSR_IACCVIOL_MASK;
|
||||||
|
armv7m_nvic_set_pending(env->nvic, ARMV7M_EXCP_MEM, env->v7m.secure);
|
||||||
|
qemu_log_mask(CPU_LOG_INT, "...really MemManage with CFSR.IACCVIOL\n");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
*insn = address_space_lduw_le(arm_addressspace(cs, attrs), physaddr,
|
||||||
|
attrs, &txres);
|
||||||
|
if (txres != MEMTX_OK) {
|
||||||
|
env->v7m.cfsr[M_REG_NS] |= R_V7M_CFSR_IBUSERR_MASK;
|
||||||
|
armv7m_nvic_set_pending(env->nvic, ARMV7M_EXCP_BUS, false);
|
||||||
|
qemu_log_mask(CPU_LOG_INT, "...really BusFault with CFSR.IBUSERR\n");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool v7m_handle_execute_nsc(ARMCPU *cpu)
|
||||||
|
{
|
||||||
|
/* Check whether this attempt to execute code in a Secure & NS-Callable
|
||||||
|
* memory region is for an SG instruction; if so, then emulate the
|
||||||
|
* effect of the SG instruction and return true. Otherwise pend
|
||||||
|
* the correct kind of exception and return false.
|
||||||
|
*/
|
||||||
|
CPUARMState *env = &cpu->env;
|
||||||
|
ARMMMUIdx mmu_idx;
|
||||||
|
uint16_t insn;
|
||||||
|
|
||||||
|
/* We should never get here unless get_phys_addr_pmsav8() caused
|
||||||
|
* an exception for NS executing in S&NSC memory.
|
||||||
|
*/
|
||||||
|
assert(!env->v7m.secure);
|
||||||
|
assert(arm_feature(env, ARM_FEATURE_M_SECURITY));
|
||||||
|
|
||||||
|
/* We want to do the MPU lookup as secure; work out what mmu_idx that is */
|
||||||
|
mmu_idx = arm_v7m_mmu_idx_for_secstate(env, true);
|
||||||
|
|
||||||
|
if (!v7m_read_half_insn(cpu, mmu_idx, env->regs[15], &insn)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!env->thumb) {
|
||||||
|
goto gen_invep;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (insn != 0xe97f) {
|
||||||
|
/* Not an SG instruction first half (we choose the IMPDEF
|
||||||
|
* early-SG-check option).
|
||||||
|
*/
|
||||||
|
goto gen_invep;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!v7m_read_half_insn(cpu, mmu_idx, env->regs[15] + 2, &insn)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (insn != 0xe97f) {
|
||||||
|
/* Not an SG instruction second half (yes, both halves of the SG
|
||||||
|
* insn have the same hex value)
|
||||||
|
*/
|
||||||
|
goto gen_invep;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* OK, we have confirmed that we really have an SG instruction.
|
||||||
|
* We know we're NS in S memory so don't need to repeat those checks.
|
||||||
|
*/
|
||||||
|
qemu_log_mask(CPU_LOG_INT, "...really an SG instruction at 0x%08" PRIx32
|
||||||
|
", executing it\n", env->regs[15]);
|
||||||
|
env->regs[14] &= ~1;
|
||||||
|
switch_v7m_security_state(env, true);
|
||||||
|
xpsr_write(env, 0, XPSR_IT);
|
||||||
|
env->regs[15] += 4;
|
||||||
|
return true;
|
||||||
|
|
||||||
|
gen_invep:
|
||||||
|
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");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
void arm_v7m_cpu_do_interrupt(CPUState *cs)
|
void arm_v7m_cpu_do_interrupt(CPUState *cs)
|
||||||
{
|
{
|
||||||
ARMCPU *cpu = ARM_CPU(cs);
|
ARMCPU *cpu = ARM_CPU(cs);
|
||||||
@ -6778,12 +6902,10 @@ void arm_v7m_cpu_do_interrupt(CPUState *cs)
|
|||||||
* the SG instruction have the same security attributes.)
|
* the SG instruction have the same security attributes.)
|
||||||
* Everything else must generate an INVEP SecureFault, so we
|
* Everything else must generate an INVEP SecureFault, so we
|
||||||
* emulate the SG instruction here.
|
* emulate the SG instruction here.
|
||||||
* TODO: actually emulate SG.
|
|
||||||
*/
|
*/
|
||||||
env->v7m.sfsr |= R_V7M_SFSR_INVEP_MASK;
|
if (v7m_handle_execute_nsc(cpu)) {
|
||||||
armv7m_nvic_set_pending(env->nvic, ARMV7M_EXCP_SECURE, false);
|
return;
|
||||||
qemu_log_mask(CPU_LOG_INT,
|
}
|
||||||
"...really SecureFault with SFSR.INVEP\n");
|
|
||||||
break;
|
break;
|
||||||
case M_FAKE_FSR_SFAULT:
|
case M_FAKE_FSR_SFAULT:
|
||||||
/* Various flavours of SecureFault for attempts to execute or
|
/* Various flavours of SecureFault for attempts to execute or
|
||||||
|
Loading…
Reference in New Issue
Block a user