target-arm: Separate out M profile cpu_exec_interrupt handling

The M profile cpu_exec_interrupt handling is fairly simple
but does include an M profile specific oddity (disabling
interrupts for certain PC values). A/R profile handling
on the other hand is getting rapidly more complicated
with the support for EL2 and EL3. Split the M profile
code out into its own implementation of cpu_exec_interrupt
to keep these two things out of each others' way.

Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
Reviewed-by: Edgar E. Iglesias <edgar.iglesias@xilinx.com>
Message-id: 1414684132-23971-2-git-send-email-peter.maydell@linaro.org
master
Peter Maydell 2014-10-30 15:48:51 +00:00
parent f4df22102a
commit b5c633c5bd
2 changed files with 41 additions and 24 deletions

View File

@ -203,15 +203,6 @@ bool arm_cpu_exec_interrupt(CPUState *cs, int interrupt_request)
cc->do_interrupt(cs);
ret = true;
}
/* ARMv7-M interrupt return works by loading a magic value
into the PC. On real hardware the load causes the
return to occur. The qemu implementation performs the
jump normally, then does the exception return when the
CPU tries to execute code at the magic address.
This will cause the magic PC value to be pushed to
the stack if an interrupt occurred at the wrong time.
We avoid this by disabling interrupts when
pc contains a magic address. */
if (interrupt_request & CPU_INTERRUPT_HARD
&& arm_excp_unmasked(cs, EXCP_IRQ)) {
cs->exception_index = EXCP_IRQ;
@ -234,6 +225,42 @@ bool arm_cpu_exec_interrupt(CPUState *cs, int interrupt_request)
return ret;
}
#if !defined(CONFIG_USER_ONLY) || !defined(TARGET_AARCH64)
static bool arm_v7m_cpu_exec_interrupt(CPUState *cs, int interrupt_request)
{
CPUClass *cc = CPU_GET_CLASS(cs);
ARMCPU *cpu = ARM_CPU(cs);
CPUARMState *env = &cpu->env;
bool ret = false;
if (interrupt_request & CPU_INTERRUPT_FIQ
&& !(env->daif & PSTATE_F)) {
cs->exception_index = EXCP_FIQ;
cc->do_interrupt(cs);
ret = true;
}
/* ARMv7-M interrupt return works by loading a magic value
* into the PC. On real hardware the load causes the
* return to occur. The qemu implementation performs the
* jump normally, then does the exception return when the
* CPU tries to execute code at the magic address.
* This will cause the magic PC value to be pushed to
* the stack if an interrupt occurred at the wrong time.
* We avoid this by disabling interrupts when
* pc contains a magic address.
*/
if (interrupt_request & CPU_INTERRUPT_HARD
&& !(env->daif & PSTATE_I)
&& (env->regs[15] < 0xfffffff0)) {
cs->exception_index = EXCP_IRQ;
cc->do_interrupt(cs);
ret = true;
}
return ret;
}
#endif
#ifndef CONFIG_USER_ONLY
static void arm_cpu_set_irq(void *opaque, int irq, int level)
{
@ -670,11 +697,13 @@ static void cortex_m3_initfn(Object *obj)
static void arm_v7m_class_init(ObjectClass *oc, void *data)
{
#ifndef CONFIG_USER_ONLY
CPUClass *cc = CPU_CLASS(oc);
#ifndef CONFIG_USER_ONLY
cc->do_interrupt = arm_v7m_cpu_do_interrupt;
#endif
cc->cpu_exec_interrupt = arm_v7m_cpu_exec_interrupt;
}
static const ARMCPRegInfo cortexa8_cp_reginfo[] = {

View File

@ -1251,18 +1251,6 @@ static inline bool arm_excp_unmasked(CPUState *cs, unsigned int excp_idx)
bool secure = false;
/* If in EL1/0, Physical IRQ routing to EL2 only happens from NS state. */
bool irq_can_hyp = !secure && cur_el < 2 && target_el == 2;
/* ARMv7-M interrupt return works by loading a magic value
* into the PC. On real hardware the load causes the
* return to occur. The qemu implementation performs the
* jump normally, then does the exception return when the
* CPU tries to execute code at the magic address.
* This will cause the magic PC value to be pushed to
* the stack if an interrupt occurred at the wrong time.
* We avoid this by disabling interrupts when
* pc contains a magic address.
*/
bool irq_unmasked = !(env->daif & PSTATE_I)
&& (!IS_M(env) || env->regs[15] < 0xfffffff0);
/* Don't take exceptions if they target a lower EL. */
if (cur_el > target_el) {
@ -1279,7 +1267,7 @@ static inline bool arm_excp_unmasked(CPUState *cs, unsigned int excp_idx)
if (irq_can_hyp && (env->cp15.hcr_el2 & HCR_IMO)) {
return true;
}
return irq_unmasked;
return !(env->daif & PSTATE_I);
case EXCP_VFIQ:
if (!secure && !(env->cp15.hcr_el2 & HCR_FMO)) {
/* VFIQs are only taken when hypervized and non-secure. */
@ -1291,7 +1279,7 @@ static inline bool arm_excp_unmasked(CPUState *cs, unsigned int excp_idx)
/* VIRQs are only taken when hypervized and non-secure. */
return false;
}
return irq_unmasked;
return !(env->daif & PSTATE_I);
default:
g_assert_not_reached();
}