blob: e0f9a1ef7ec19e8e8a2c5a456d07bf60e56c693d [file] [log] [blame]
From ec2838a7609d8b671bfb44ed90d15c1467cc6f32 Mon Sep 17 00:00:00 2001
From: Yonattan Louise <yonattan.a.louise.mendoza@intel.com>
Date: Tue, 14 Apr 2015 16:38:42 -0500
Subject: [PATCH] armv7m support basepri + primask interrupt locking
Interrupt locking is not implemented as part of QEMU for ARM. Until
we are able to properly upstream a patch to the QEMU project, this
change provides a partial solution that enables the Cortex-M3 core.
This has not been tested with other ARM based platforms.
This solution is for internal use. It is not ready for QEMU upstream.
Solution created by Benjamin Walsh for qemu v1.5, and ported by
Yonattan Louise for qemu v2.x.
Signed-off-by: Yonattan Louise <yonattan.a.louise.mendoza@linux.intel.com>
Signed-off-by: Dirk Brandewie <dirk.j.brandewie@intel.com>
---
cpu-exec.c | 16 +++++++++++++---
hw/intc/arm_gic.c | 12 ++++++++++++
hw/intc/armv7m_nvic.c | 12 ++++++++++++
hw/intc/gic_internal.h | 2 ++
pixman | 2 +-
target-arm/cpu-qom.h | 1 +
target-arm/cpu.h | 2 ++
target-arm/helper.c | 28 +++++++++++++++++++++++++++-
8 files changed, 70 insertions(+), 5 deletions(-)
diff --git a/cpu-exec.c b/cpu-exec.c
index 38e5f02..2252f85 100644
--- a/cpu-exec.c
+++ b/cpu-exec.c
@@ -492,9 +492,19 @@ int cpu_exec(CPUArchState *env)
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
- && ((IS_M(env) && env->regs[15] < 0xfffffff0)
- || !(env->daif & PSTATE_I))) {
+
+ int is_m_do_irq = !IS_M(env) || (
+ (env->regs[15] < 0xfffffff0)
+#if !defined(TARGET_AARCH64) && !defined(CONFIG_USER_ONLY)
+ && arm_v7m_basepri_check(cpu) );
+#else
+ );
+#endif
+ int handle_irq = (
+ (interrupt_request & CPU_INTERRUPT_HARD) &&
+ is_m_do_irq && !(env->daif & PSTATE_I) );
+
+ if (handle_irq) {
cpu->exception_index = EXCP_IRQ;
cc->do_interrupt(cpu);
next_tb = 0;
diff --git a/hw/intc/arm_gic.c b/hw/intc/arm_gic.c
index 1532ef9..fabe948 100644
--- a/hw/intc/arm_gic.c
+++ b/hw/intc/arm_gic.c
@@ -177,6 +177,18 @@ static void gic_set_running_irq(GICState *s, int cpu, int irq)
gic_update(s);
}
+int gic_get_next_irq(GICState *s, int cpu)
+{
+ int irq = s->current_pending[cpu];
+ int prio_check = GIC_GET_PRIORITY(irq, cpu) < s->running_priority[cpu];
+ return prio_check ? irq : 1023;
+}
+
+int gic_get_priority(GICState *s, int irq, int cpu)
+{
+ return GIC_GET_PRIORITY(irq, cpu);
+}
+
uint32_t gic_acknowledge_irq(GICState *s, int cpu)
{
int ret, irq, src;
diff --git a/hw/intc/armv7m_nvic.c b/hw/intc/armv7m_nvic.c
index 1a7af45..3ce8733 100644
--- a/hw/intc/armv7m_nvic.c
+++ b/hw/intc/armv7m_nvic.c
@@ -116,6 +116,12 @@ void armv7m_nvic_set_pending(void *opaque, int irq)
gic_set_pending_private(&s->gic, 0, irq);
}
+int armv7m_nvic_get_priority(void *opaque, int irq, int cpu)
+{
+ nvic_state *s = (nvic_state *)opaque;
+ return gic_get_priority(&s->gic, irq, cpu);
+}
+
/* Make pending IRQ active. */
int armv7m_nvic_acknowledge_irq(void *opaque)
{
@@ -138,6 +144,12 @@ void armv7m_nvic_complete_irq(void *opaque, int irq)
gic_complete_irq(&s->gic, 0, irq);
}
+int armv7m_nvic_get_next_irq(void *opaque, int cpu)
+{
+ nvic_state *s = (nvic_state *)opaque;
+ return gic_get_next_irq(&s->gic, cpu);
+}
+
static uint32_t nvic_readl(nvic_state *s, uint32_t offset)
{
ARMCPU *cpu;
diff --git a/hw/intc/gic_internal.h b/hw/intc/gic_internal.h
index 48a58d7..f1a919f 100644
--- a/hw/intc/gic_internal.h
+++ b/hw/intc/gic_internal.h
@@ -61,6 +61,8 @@ void gic_complete_irq(GICState *s, int cpu, int irq);
void gic_update(GICState *s);
void gic_init_irqs_and_distributor(GICState *s, int num_irq);
void gic_set_priority(GICState *s, int cpu, int irq, uint8_t val);
+int gic_get_next_irq(GICState *s, int cpu);
+int gic_get_priority(GICState *s, int irq, int cpu);
static inline bool gic_test_pending(GICState *s, int irq, int cm)
{
diff --git a/pixman b/pixman
index 97336fa..87eea99 160000
--- a/pixman
+++ b/pixman
@@ -1 +1 @@
-Subproject commit 97336fad32acf802003855cd8bd6477fa49a12e3
+Subproject commit 87eea99e443b389c978cf37efc52788bf03a0ee0
diff --git a/target-arm/cpu-qom.h b/target-arm/cpu-qom.h
index ee4fbb1..860fb6d 100644
--- a/target-arm/cpu-qom.h
+++ b/target-arm/cpu-qom.h
@@ -191,6 +191,7 @@ void init_cpreg_list(ARMCPU *cpu);
void arm_cpu_do_interrupt(CPUState *cpu);
void arm_v7m_cpu_do_interrupt(CPUState *cpu);
+int arm_v7m_basepri_check(CPUState *cs);
void arm_cpu_dump_state(CPUState *cs, FILE *f, fprintf_function cpu_fprintf,
int flags);
diff --git a/target-arm/cpu.h b/target-arm/cpu.h
index f101880..1c1ed5b 100644
--- a/target-arm/cpu.h
+++ b/target-arm/cpu.h
@@ -683,6 +683,8 @@ void arm_cpu_list(FILE *f, fprintf_function cpu_fprintf);
void armv7m_nvic_set_pending(void *opaque, int irq);
int armv7m_nvic_acknowledge_irq(void *opaque);
void armv7m_nvic_complete_irq(void *opaque, int irq);
+int armv7m_nvic_get_priority(void *opaque, int irq, int cpu);
+int armv7m_nvic_get_next_irq(void *opaque, int cpu);
/* Interface for defining coprocessor registers.
* Registers are defined in tables of arm_cp_reginfo structs
diff --git a/target-arm/helper.c b/target-arm/helper.c
index d343856..6a2a907 100644
--- a/target-arm/helper.c
+++ b/target-arm/helper.c
@@ -3243,6 +3243,29 @@ static void switch_v7m_sp(CPUARMState *env, int process)
}
}
+static inline int _get_cur_basepri(CPUARMState *env)
+{
+ return (int)helper_v7m_mrs(env, 0x11);
+}
+
+int arm_v7m_basepri_check(CPUState *cs)
+{
+ ARMCPU *cpu = ARM_CPU(cs);
+ CPUARMState *env = &cpu->env;
+ int irq = armv7m_nvic_get_next_irq(env->nvic, 0);
+
+ if (irq == 1023)
+ return 0;
+
+ int basepri = _get_cur_basepri(env);
+ int irqpri = armv7m_nvic_get_priority(env->nvic, irq, 0);
+
+ if (!basepri || (irqpri < basepri))
+ return 1;
+ else
+ return 0;
+}
+
static void do_v7m_exception_exit(CPUARMState *env)
{
uint32_t type;
@@ -3323,8 +3346,11 @@ void arm_v7m_cpu_do_interrupt(CPUState *cs)
armv7m_nvic_set_pending(env->nvic, ARMV7M_EXCP_DEBUG);
return;
case EXCP_IRQ:
- env->v7m.exception = armv7m_nvic_acknowledge_irq(env->nvic);
+ {
+ int exc = armv7m_nvic_acknowledge_irq(env->nvic);
+ env->v7m.exception = exc;
break;
+ }
case EXCP_EXCEPTION_EXIT:
do_v7m_exception_exit(env);
return;
--
1.9.1