1369de9828
CVE-2014-3689 vmware_vga: insufficient parameter validation in rectangle functions (bz #1153038, bz #1153035)
197 lines
7.6 KiB
Diff
197 lines
7.6 KiB
Diff
From: Christoffer Dall <christoffer.dall@linaro.org>
|
|
Date: Mon, 18 Nov 2013 20:32:00 -0800
|
|
Subject: [PATCH] arm_gic: Fix GIC pending behavior
|
|
|
|
The existing implementation of the pending behavior in gic_set_irq,
|
|
gic_complete_irq, and the distributor pending set/clear registers does
|
|
not follow the semantics of the GICv2.0 specs, but may implement the
|
|
11MPCore support. Therefore, maintain the existing semantics for
|
|
11MPCore and v7M NVIC and change the behavior to be in accordance with
|
|
the GICv2.0 specs for "generic implementations" (s->revision == 1 ||
|
|
s->revision == 2).
|
|
|
|
Generic implementations distinguish between setting a level-triggered
|
|
interrupt pending through writes to the GICD_ISPENDR and when hardware
|
|
raises the interrupt line. Writing to the GICD_ICPENDR will not cause
|
|
the interrupt to become non-pending if the line is still active, and
|
|
conversely, if the line is deactivated but the interrupt is marked as
|
|
pending through a write to GICD_ISPENDR, the interrupt remains pending.
|
|
Handle this situation in the GIC_TEST_PENDING (which now becomes a
|
|
static inline named gic_test_pending) and let the 'pending' field
|
|
correspond only to the latched state of the D-flip flop in the GICv2.0
|
|
specs Figure 4-10.
|
|
|
|
The following changes are added:
|
|
|
|
gic_test_pending:
|
|
Make this a static inline and split out the 11MPCore from the generic
|
|
behavior. For the generic behavior, consider interrupts pending if:
|
|
((s->irq_state[irq].pending & (cm) != 0) ||
|
|
(!GIC_TEST_EDGE_TRIGGER(irq) && GIC_TEST_LEVEL(irq, cm))
|
|
|
|
gic_set_irq:
|
|
Split out the 11MPCore from the generic behavior. For the generic
|
|
behavior, always GIC_SET_LEVEL() on positive level, but only
|
|
GIC_SET_PENDING for edge-triggered interrupts and always simply
|
|
GIC_CLEAR_LEVEL() on negative level.
|
|
|
|
gic_complete_irq:
|
|
Only resample the line for line-triggered interrupts on an 11MPCore.
|
|
Generic implementations will sample the line directly in
|
|
gic_test_pending().
|
|
|
|
Signed-off-by: Christoffer Dall <christoffer.dall@linaro.org>
|
|
Reviewed-by: Peter Maydell <peter.maydell@linaro.org>
|
|
Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
|
|
(cherry picked from commit 8d999995e45c1002aa11f269c98f2e93e6f8c42a)
|
|
---
|
|
hw/intc/arm_gic.c | 64 ++++++++++++++++++++++++++++++++++++--------------
|
|
hw/intc/gic_internal.h | 16 ++++++++++++-
|
|
2 files changed, 62 insertions(+), 18 deletions(-)
|
|
|
|
diff --git a/hw/intc/arm_gic.c b/hw/intc/arm_gic.c
|
|
index 62153fd..11fe3c4 100644
|
|
--- a/hw/intc/arm_gic.c
|
|
+++ b/hw/intc/arm_gic.c
|
|
@@ -66,7 +66,7 @@ void gic_update(GICState *s)
|
|
best_prio = 0x100;
|
|
best_irq = 1023;
|
|
for (irq = 0; irq < s->num_irq; irq++) {
|
|
- if (GIC_TEST_ENABLED(irq, cm) && GIC_TEST_PENDING(irq, cm)) {
|
|
+ if (GIC_TEST_ENABLED(irq, cm) && gic_test_pending(s, irq, cm)) {
|
|
if (GIC_GET_PRIORITY(irq, cpu) < best_prio) {
|
|
best_prio = GIC_GET_PRIORITY(irq, cpu);
|
|
best_irq = irq;
|
|
@@ -89,14 +89,43 @@ void gic_set_pending_private(GICState *s, int cpu, int irq)
|
|
{
|
|
int cm = 1 << cpu;
|
|
|
|
- if (GIC_TEST_PENDING(irq, cm))
|
|
+ if (gic_test_pending(s, irq, cm)) {
|
|
return;
|
|
+ }
|
|
|
|
DPRINTF("Set %d pending cpu %d\n", irq, cpu);
|
|
GIC_SET_PENDING(irq, cm);
|
|
gic_update(s);
|
|
}
|
|
|
|
+static void gic_set_irq_11mpcore(GICState *s, int irq, int level,
|
|
+ int cm, int target)
|
|
+{
|
|
+ if (level) {
|
|
+ GIC_SET_LEVEL(irq, cm);
|
|
+ if (GIC_TEST_EDGE_TRIGGER(irq) || GIC_TEST_ENABLED(irq, cm)) {
|
|
+ DPRINTF("Set %d pending mask %x\n", irq, target);
|
|
+ GIC_SET_PENDING(irq, target);
|
|
+ }
|
|
+ } else {
|
|
+ GIC_CLEAR_LEVEL(irq, cm);
|
|
+ }
|
|
+}
|
|
+
|
|
+static void gic_set_irq_generic(GICState *s, int irq, int level,
|
|
+ int cm, int target)
|
|
+{
|
|
+ if (level) {
|
|
+ GIC_SET_LEVEL(irq, cm);
|
|
+ DPRINTF("Set %d pending mask %x\n", irq, target);
|
|
+ if (GIC_TEST_EDGE_TRIGGER(irq)) {
|
|
+ GIC_SET_PENDING(irq, target);
|
|
+ }
|
|
+ } else {
|
|
+ GIC_CLEAR_LEVEL(irq, cm);
|
|
+ }
|
|
+}
|
|
+
|
|
/* Process a change in an external IRQ input. */
|
|
static void gic_set_irq(void *opaque, int irq, int level)
|
|
{
|
|
@@ -126,15 +155,12 @@ static void gic_set_irq(void *opaque, int irq, int level)
|
|
return;
|
|
}
|
|
|
|
- if (level) {
|
|
- GIC_SET_LEVEL(irq, cm);
|
|
- if (GIC_TEST_EDGE_TRIGGER(irq) || GIC_TEST_ENABLED(irq, cm)) {
|
|
- DPRINTF("Set %d pending mask %x\n", irq, target);
|
|
- GIC_SET_PENDING(irq, target);
|
|
- }
|
|
+ if (s->revision == REV_11MPCORE || s->revision == REV_NVIC) {
|
|
+ gic_set_irq_11mpcore(s, irq, level, cm, target);
|
|
} else {
|
|
- GIC_CLEAR_LEVEL(irq, cm);
|
|
+ gic_set_irq_generic(s, irq, level, cm, target);
|
|
}
|
|
+
|
|
gic_update(s);
|
|
}
|
|
|
|
@@ -195,14 +221,18 @@ void gic_complete_irq(GICState *s, int cpu, int irq)
|
|
}
|
|
if (s->running_irq[cpu] == 1023)
|
|
return; /* No active IRQ. */
|
|
- /* Mark level triggered interrupts as pending if they are still
|
|
- raised. */
|
|
- if (!GIC_TEST_EDGE_TRIGGER(irq) && GIC_TEST_ENABLED(irq, cm)
|
|
- && GIC_TEST_LEVEL(irq, cm) && (GIC_TARGET(irq) & cm) != 0) {
|
|
- DPRINTF("Set %d pending mask %x\n", irq, cm);
|
|
- GIC_SET_PENDING(irq, cm);
|
|
- update = 1;
|
|
+
|
|
+ if (s->revision == REV_11MPCORE || s->revision == REV_NVIC) {
|
|
+ /* Mark level triggered interrupts as pending if they are still
|
|
+ raised. */
|
|
+ if (!GIC_TEST_EDGE_TRIGGER(irq) && GIC_TEST_ENABLED(irq, cm)
|
|
+ && GIC_TEST_LEVEL(irq, cm) && (GIC_TARGET(irq) & cm) != 0) {
|
|
+ DPRINTF("Set %d pending mask %x\n", irq, cm);
|
|
+ GIC_SET_PENDING(irq, cm);
|
|
+ update = 1;
|
|
+ }
|
|
}
|
|
+
|
|
if (irq != s->running_irq[cpu]) {
|
|
/* Complete an IRQ that is not currently running. */
|
|
int tmp = s->running_irq[cpu];
|
|
@@ -273,7 +303,7 @@ static uint32_t gic_dist_readb(void *opaque, hwaddr offset)
|
|
res = 0;
|
|
mask = (irq < GIC_INTERNAL) ? cm : ALL_CPU_MASK;
|
|
for (i = 0; i < 8; i++) {
|
|
- if (GIC_TEST_PENDING(irq + i, mask)) {
|
|
+ if (gic_test_pending(s, irq + i, mask)) {
|
|
res |= (1 << i);
|
|
}
|
|
}
|
|
diff --git a/hw/intc/gic_internal.h b/hw/intc/gic_internal.h
|
|
index 8c02d58..92a6f7a 100644
|
|
--- a/hw/intc/gic_internal.h
|
|
+++ b/hw/intc/gic_internal.h
|
|
@@ -34,7 +34,6 @@
|
|
#define GIC_TEST_ENABLED(irq, cm) ((s->irq_state[irq].enabled & (cm)) != 0)
|
|
#define GIC_SET_PENDING(irq, cm) s->irq_state[irq].pending |= (cm)
|
|
#define GIC_CLEAR_PENDING(irq, cm) s->irq_state[irq].pending &= ~(cm)
|
|
-#define GIC_TEST_PENDING(irq, cm) ((s->irq_state[irq].pending & (cm)) != 0)
|
|
#define GIC_SET_ACTIVE(irq, cm) s->irq_state[irq].active |= (cm)
|
|
#define GIC_CLEAR_ACTIVE(irq, cm) s->irq_state[irq].active &= ~(cm)
|
|
#define GIC_TEST_ACTIVE(irq, cm) ((s->irq_state[irq].active & (cm)) != 0)
|
|
@@ -63,4 +62,19 @@ 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);
|
|
|
|
+static inline bool gic_test_pending(GICState *s, int irq, int cm)
|
|
+{
|
|
+ if (s->revision == REV_NVIC || s->revision == REV_11MPCORE) {
|
|
+ return s->irq_state[irq].pending & cm;
|
|
+ } else {
|
|
+ /* Edge-triggered interrupts are marked pending on a rising edge, but
|
|
+ * level-triggered interrupts are either considered pending when the
|
|
+ * level is active or if software has explicitly written to
|
|
+ * GICD_ISPENDR to set the state pending.
|
|
+ */
|
|
+ return (s->irq_state[irq].pending & cm) ||
|
|
+ (!GIC_TEST_EDGE_TRIGGER(irq) && GIC_TEST_LEVEL(irq, cm));
|
|
+ }
|
|
+}
|
|
+
|
|
#endif /* !QEMU_ARM_GIC_INTERNAL_H */
|