summaryrefslogtreecommitdiffstats
path: root/qemu/hw/intc
diff options
context:
space:
mode:
authorJosé Pekkarinen <jose.pekkarinen@nokia.com>2016-05-18 13:18:31 +0300
committerJosé Pekkarinen <jose.pekkarinen@nokia.com>2016-05-18 13:42:15 +0300
commit437fd90c0250dee670290f9b714253671a990160 (patch)
treeb871786c360704244a07411c69fb58da9ead4a06 /qemu/hw/intc
parent5bbd6fe9b8bab2a93e548c5a53b032d1939eec05 (diff)
These changes are the raw update to qemu-2.6.
Collission happened in the following patches: migration: do cleanup operation after completion(738df5b9) Bug fix.(1750c932f86) kvmclock: add a new function to update env->tsc.(b52baab2) The code provided by the patches was already in the upstreamed version. Change-Id: I3cc11841a6a76ae20887b2e245710199e1ea7f9a Signed-off-by: José Pekkarinen <jose.pekkarinen@nokia.com>
Diffstat (limited to 'qemu/hw/intc')
-rw-r--r--qemu/hw/intc/Makefile.objs4
-rw-r--r--qemu/hw/intc/allwinner-a10-pic.c1
-rw-r--r--qemu/hw/intc/apic.c25
-rw-r--r--qemu/hw/intc/apic_common.c13
-rw-r--r--qemu/hw/intc/arm_gic.c393
-rw-r--r--qemu/hw/intc/arm_gic_common.c102
-rw-r--r--qemu/hw/intc/arm_gic_kvm.c148
-rw-r--r--qemu/hw/intc/arm_gicv2m.c4
-rw-r--r--qemu/hw/intc/arm_gicv3_common.c142
-rw-r--r--qemu/hw/intc/arm_gicv3_kvm.c151
-rw-r--r--qemu/hw/intc/armv7m_nvic.c25
-rw-r--r--qemu/hw/intc/aspeed_vic.c339
-rw-r--r--qemu/hw/intc/bcm2835_ic.c237
-rw-r--r--qemu/hw/intc/bcm2836_control.c304
-rw-r--r--qemu/hw/intc/etraxfs_pic.c1
-rw-r--r--qemu/hw/intc/exynos4210_combiner.c1
-rw-r--r--qemu/hw/intc/exynos4210_gic.c1
-rw-r--r--qemu/hw/intc/grlib_irqmp.c1
-rw-r--r--qemu/hw/intc/heathrow_pic.c1
-rw-r--r--qemu/hw/intc/i8259.c3
-rw-r--r--qemu/hw/intc/i8259_common.c1
-rw-r--r--qemu/hw/intc/imx_avic.c89
-rw-r--r--qemu/hw/intc/ioapic.c89
-rw-r--r--qemu/hw/intc/ioapic_common.c57
-rw-r--r--qemu/hw/intc/lm32_pic.c2
-rw-r--r--qemu/hw/intc/omap_intc.c11
-rw-r--r--qemu/hw/intc/openpic.c5
-rw-r--r--qemu/hw/intc/openpic_kvm.c5
-rw-r--r--qemu/hw/intc/pl190.c1
-rw-r--r--qemu/hw/intc/puv3_intc.c1
-rw-r--r--qemu/hw/intc/realview_gic.c2
-rw-r--r--qemu/hw/intc/s390_flic.c1
-rw-r--r--qemu/hw/intc/s390_flic_kvm.c3
-rw-r--r--qemu/hw/intc/sh_intc.c3
-rw-r--r--qemu/hw/intc/slavio_intctl.c1
-rw-r--r--qemu/hw/intc/vgic_common.h35
-rw-r--r--qemu/hw/intc/xics.c41
-rw-r--r--qemu/hw/intc/xics_kvm.c4
-rw-r--r--qemu/hw/intc/xilinx_intc.c1
39 files changed, 1921 insertions, 327 deletions
diff --git a/qemu/hw/intc/Makefile.objs b/qemu/hw/intc/Makefile.objs
index 092d8a80a..0e47f0f9e 100644
--- a/qemu/hw/intc/Makefile.objs
+++ b/qemu/hw/intc/Makefile.objs
@@ -12,19 +12,23 @@ common-obj-$(CONFIG_IOAPIC) += ioapic_common.o
common-obj-$(CONFIG_ARM_GIC) += arm_gic_common.o
common-obj-$(CONFIG_ARM_GIC) += arm_gic.o
common-obj-$(CONFIG_ARM_GIC) += arm_gicv2m.o
+common-obj-$(CONFIG_ARM_GIC) += arm_gicv3_common.o
common-obj-$(CONFIG_OPENPIC) += openpic.o
obj-$(CONFIG_APIC) += apic.o apic_common.o
obj-$(CONFIG_ARM_GIC_KVM) += arm_gic_kvm.o
+obj-$(call land,$(CONFIG_ARM_GIC_KVM),$(TARGET_AARCH64)) += arm_gicv3_kvm.o
obj-$(CONFIG_STELLARIS) += armv7m_nvic.o
obj-$(CONFIG_EXYNOS4) += exynos4210_gic.o exynos4210_combiner.o
obj-$(CONFIG_GRLIB) += grlib_irqmp.o
obj-$(CONFIG_IOAPIC) += ioapic.o
obj-$(CONFIG_OMAP) += omap_intc.o
obj-$(CONFIG_OPENPIC_KVM) += openpic_kvm.o
+obj-$(CONFIG_RASPI) += bcm2835_ic.o bcm2836_control.o
obj-$(CONFIG_SH4) += sh_intc.o
obj-$(CONFIG_XICS) += xics.o
obj-$(CONFIG_XICS_KVM) += xics_kvm.o
obj-$(CONFIG_ALLWINNER_A10_PIC) += allwinner-a10-pic.o
obj-$(CONFIG_S390_FLIC) += s390_flic.o
obj-$(CONFIG_S390_FLIC_KVM) += s390_flic_kvm.o
+obj-$(CONFIG_ASPEED_SOC) += aspeed_vic.o
diff --git a/qemu/hw/intc/allwinner-a10-pic.c b/qemu/hw/intc/allwinner-a10-pic.c
index eed7621f1..dc971a160 100644
--- a/qemu/hw/intc/allwinner-a10-pic.c
+++ b/qemu/hw/intc/allwinner-a10-pic.c
@@ -15,6 +15,7 @@
* for more details.
*/
+#include "qemu/osdep.h"
#include "hw/sysbus.h"
#include "hw/devices.h"
#include "sysemu/sysemu.h"
diff --git a/qemu/hw/intc/apic.c b/qemu/hw/intc/apic.c
index 77b639cce..28c2ea540 100644
--- a/qemu/hw/intc/apic.c
+++ b/qemu/hw/intc/apic.c
@@ -16,6 +16,7 @@
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, see <http://www.gnu.org/licenses/>
*/
+#include "qemu/osdep.h"
#include "qemu/thread.h"
#include "hw/i386/apic_internal.h"
#include "hw/i386/apic.h"
@@ -51,14 +52,6 @@ static int apic_ffs_bit(uint32_t value)
return ctz32(value);
}
-static inline void apic_set_bit(uint32_t *tab, int index)
-{
- int i, mask;
- i = index >> 5;
- mask = 1 << (index & 0x1f);
- tab[i] |= mask;
-}
-
static inline void apic_reset_bit(uint32_t *tab, int index)
{
int i, mask;
@@ -67,14 +60,6 @@ static inline void apic_reset_bit(uint32_t *tab, int index)
tab[i] &= ~mask;
}
-static inline int apic_get_bit(uint32_t *tab, int index)
-{
- int i, mask;
- i = index >> 5;
- mask = 1 << (index & 0x1f);
- return !!(tab[i] & mask);
-}
-
/* return -1 if no bit is set */
static int get_highest_priority_int(uint32_t *tab)
{
@@ -318,7 +303,7 @@ static uint8_t apic_get_tpr(APICCommonState *s)
return s->tpr >> 4;
}
-static int apic_get_ppr(APICCommonState *s)
+int apic_get_ppr(APICCommonState *s)
{
int tpr, isrv, ppr;
@@ -739,7 +724,7 @@ static uint32_t apic_mem_readl(void *opaque, hwaddr addr)
val = s->divide_conf;
break;
default:
- s->esr |= ESR_ILLEGAL_ADDRESS;
+ s->esr |= APIC_ESR_ILLEGAL_ADDRESS;
val = 0;
break;
}
@@ -852,7 +837,7 @@ static void apic_mem_writel(void *opaque, hwaddr addr, uint32_t val)
}
break;
default:
- s->esr |= ESR_ILLEGAL_ADDRESS;
+ s->esr |= APIC_ESR_ILLEGAL_ADDRESS;
break;
}
}
@@ -889,7 +874,7 @@ static void apic_realize(DeviceState *dev, Error **errp)
s->timer = timer_new_ns(QEMU_CLOCK_VIRTUAL, apic_timer, s);
local_apics[s->idx] = s;
- msi_supported = true;
+ msi_nonbroken = true;
}
static void apic_class_init(ObjectClass *klass, void *data)
diff --git a/qemu/hw/intc/apic_common.c b/qemu/hw/intc/apic_common.c
index 0032b97c5..4abe145c6 100644
--- a/qemu/hw/intc/apic_common.c
+++ b/qemu/hw/intc/apic_common.c
@@ -17,6 +17,8 @@
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, see <http://www.gnu.org/licenses/>
*/
+#include "qemu/osdep.h"
+#include "qapi/error.h"
#include "hw/i386/apic.h"
#include "hw/i386/apic_internal.h"
#include "trace.h"
@@ -296,7 +298,6 @@ static void apic_common_realize(DeviceState *dev, Error **errp)
APICCommonClass *info;
static DeviceState *vapic;
static int apic_no;
- static bool mmio_registered;
if (apic_no >= MAX_APICS) {
error_setg(errp, "%s initialization failed.",
@@ -307,11 +308,6 @@ static void apic_common_realize(DeviceState *dev, Error **errp)
info = APIC_COMMON_GET_CLASS(s);
info->realize(dev, errp);
- if (!mmio_registered) {
- ICCBus *b = ICC_BUS(qdev_get_parent_bus(dev));
- memory_region_add_subregion(b->apic_address_space, 0, &s->io_memory);
- mmio_registered = true;
- }
/* Note: We need at least 1M to map the VAPIC option ROM */
if (!vapic && s->vapic_control & VAPIC_ENABLE_MASK &&
@@ -425,13 +421,12 @@ static Property apic_properties_common[] = {
static void apic_common_class_init(ObjectClass *klass, void *data)
{
- ICCDeviceClass *idc = ICC_DEVICE_CLASS(klass);
DeviceClass *dc = DEVICE_CLASS(klass);
dc->vmsd = &vmstate_apic_common;
dc->reset = apic_reset_common;
dc->props = apic_properties_common;
- idc->realize = apic_common_realize;
+ dc->realize = apic_common_realize;
/*
* Reason: APIC and CPU need to be wired up by
* x86_cpu_apic_create()
@@ -441,7 +436,7 @@ static void apic_common_class_init(ObjectClass *klass, void *data)
static const TypeInfo apic_common_type = {
.name = TYPE_APIC_COMMON,
- .parent = TYPE_ICC_DEVICE,
+ .parent = TYPE_DEVICE,
.instance_size = sizeof(APICCommonState),
.class_size = sizeof(APICCommonClass),
.class_init = apic_common_class_init,
diff --git a/qemu/hw/intc/arm_gic.c b/qemu/hw/intc/arm_gic.c
index 454bfd7df..f55124174 100644
--- a/qemu/hw/intc/arm_gic.c
+++ b/qemu/hw/intc/arm_gic.c
@@ -18,8 +18,10 @@
* armv7m_nvic device.
*/
+#include "qemu/osdep.h"
#include "hw/sysbus.h"
#include "gic_internal.h"
+#include "qapi/error.h"
#include "qom/cpu.h"
//#define DEBUG_GIC
@@ -31,11 +33,17 @@ do { fprintf(stderr, "arm_gic: " fmt , ## __VA_ARGS__); } while (0)
#define DPRINTF(fmt, ...) do {} while(0)
#endif
-static const uint8_t gic_id[] = {
- 0x90, 0x13, 0x04, 0x00, 0x0d, 0xf0, 0x05, 0xb1
+static const uint8_t gic_id_11mpcore[] = {
+ 0x00, 0x00, 0x00, 0x00, 0x90, 0x13, 0x04, 0x00, 0x0d, 0xf0, 0x05, 0xb1
};
-#define NUM_CPU(s) ((s)->num_cpu)
+static const uint8_t gic_id_gicv1[] = {
+ 0x04, 0x00, 0x00, 0x00, 0x90, 0xb3, 0x1b, 0x00, 0x0d, 0xf0, 0x05, 0xb1
+};
+
+static const uint8_t gic_id_gicv2[] = {
+ 0x04, 0x00, 0x00, 0x00, 0x90, 0xb4, 0x2b, 0x00, 0x0d, 0xf0, 0x05, 0xb1
+};
static inline int gic_get_current_cpu(GICState *s)
{
@@ -64,7 +72,7 @@ void gic_update(GICState *s)
int cpu;
int cm;
- for (cpu = 0; cpu < NUM_CPU(s); cpu++) {
+ for (cpu = 0; cpu < s->num_cpu; cpu++) {
cm = 1 << cpu;
s->current_pending[cpu] = 1023;
if (!(s->ctlr & (GICD_CTLR_EN_GRP0 | GICD_CTLR_EN_GRP1))
@@ -219,15 +227,99 @@ static uint16_t gic_get_current_pending_irq(GICState *s, int cpu,
return pending_irq;
}
-static void gic_set_running_irq(GICState *s, int cpu, int irq)
+static int gic_get_group_priority(GICState *s, int cpu, int irq)
{
- s->running_irq[cpu] = irq;
- if (irq == 1023) {
- s->running_priority[cpu] = 0x100;
+ /* Return the group priority of the specified interrupt
+ * (which is the top bits of its priority, with the number
+ * of bits masked determined by the applicable binary point register).
+ */
+ int bpr;
+ uint32_t mask;
+
+ if (gic_has_groups(s) &&
+ !(s->cpu_ctlr[cpu] & GICC_CTLR_CBPR) &&
+ GIC_TEST_GROUP(irq, (1 << cpu))) {
+ bpr = s->abpr[cpu];
} else {
- s->running_priority[cpu] = GIC_GET_PRIORITY(irq, cpu);
+ bpr = s->bpr[cpu];
}
- gic_update(s);
+
+ /* a BPR of 0 means the group priority bits are [7:1];
+ * a BPR of 1 means they are [7:2], and so on down to
+ * a BPR of 7 meaning no group priority bits at all.
+ */
+ mask = ~0U << ((bpr & 7) + 1);
+
+ return GIC_GET_PRIORITY(irq, cpu) & mask;
+}
+
+static void gic_activate_irq(GICState *s, int cpu, int irq)
+{
+ /* Set the appropriate Active Priority Register bit for this IRQ,
+ * and update the running priority.
+ */
+ int prio = gic_get_group_priority(s, cpu, irq);
+ int preemption_level = prio >> (GIC_MIN_BPR + 1);
+ int regno = preemption_level / 32;
+ int bitno = preemption_level % 32;
+
+ if (gic_has_groups(s) && GIC_TEST_GROUP(irq, (1 << cpu))) {
+ s->nsapr[regno][cpu] |= (1 << bitno);
+ } else {
+ s->apr[regno][cpu] |= (1 << bitno);
+ }
+
+ s->running_priority[cpu] = prio;
+ GIC_SET_ACTIVE(irq, 1 << cpu);
+}
+
+static int gic_get_prio_from_apr_bits(GICState *s, int cpu)
+{
+ /* Recalculate the current running priority for this CPU based
+ * on the set bits in the Active Priority Registers.
+ */
+ int i;
+ for (i = 0; i < GIC_NR_APRS; i++) {
+ uint32_t apr = s->apr[i][cpu] | s->nsapr[i][cpu];
+ if (!apr) {
+ continue;
+ }
+ return (i * 32 + ctz32(apr)) << (GIC_MIN_BPR + 1);
+ }
+ return 0x100;
+}
+
+static void gic_drop_prio(GICState *s, int cpu, int group)
+{
+ /* Drop the priority of the currently active interrupt in the
+ * specified group.
+ *
+ * Note that we can guarantee (because of the requirement to nest
+ * GICC_IAR reads [which activate an interrupt and raise priority]
+ * with GICC_EOIR writes [which drop the priority for the interrupt])
+ * that the interrupt we're being called for is the highest priority
+ * active interrupt, meaning that it has the lowest set bit in the
+ * APR registers.
+ *
+ * If the guest does not honour the ordering constraints then the
+ * behaviour of the GIC is UNPREDICTABLE, which for us means that
+ * the values of the APR registers might become incorrect and the
+ * running priority will be wrong, so interrupts that should preempt
+ * might not do so, and interrupts that should not preempt might do so.
+ */
+ int i;
+
+ for (i = 0; i < GIC_NR_APRS; i++) {
+ uint32_t *papr = group ? &s->nsapr[i][cpu] : &s->apr[i][cpu];
+ if (!*papr) {
+ continue;
+ }
+ /* Clear lowest set bit */
+ *papr &= *papr - 1;
+ break;
+ }
+
+ s->running_priority[cpu] = gic_get_prio_from_apr_bits(s, cpu);
}
uint32_t gic_acknowledge_irq(GICState *s, int cpu, MemTxAttrs attrs)
@@ -239,7 +331,7 @@ uint32_t gic_acknowledge_irq(GICState *s, int cpu, MemTxAttrs attrs)
* for the case where this GIC supports grouping and the pending interrupt
* is in the wrong group.
*/
- irq = gic_get_current_pending_irq(s, cpu, attrs);;
+ irq = gic_get_current_pending_irq(s, cpu, attrs);
if (irq >= GIC_MAXIRQ) {
DPRINTF("ACK, no pending interrupt or it is hidden: %d\n", irq);
@@ -250,7 +342,6 @@ uint32_t gic_acknowledge_irq(GICState *s, int cpu, MemTxAttrs attrs)
DPRINTF("ACK, pending interrupt (%d) has insufficient priority\n", irq);
return 1023;
}
- s->last_active[irq][cpu] = s->running_irq[cpu];
if (s->revision == REV_11MPCORE || s->revision == REV_NVIC) {
/* Clear pending flags for both level and edge triggered interrupts.
@@ -281,7 +372,8 @@ uint32_t gic_acknowledge_irq(GICState *s, int cpu, MemTxAttrs attrs)
}
}
- gic_set_running_irq(s, cpu, irq);
+ gic_activate_irq(s, cpu, irq);
+ gic_update(s);
DPRINTF("ACK %d\n", irq);
return ret;
}
@@ -409,10 +501,46 @@ static uint8_t gic_get_running_priority(GICState *s, int cpu, MemTxAttrs attrs)
}
}
+/* Return true if we should split priority drop and interrupt deactivation,
+ * ie whether the relevant EOIMode bit is set.
+ */
+static bool gic_eoi_split(GICState *s, int cpu, MemTxAttrs attrs)
+{
+ if (s->revision != 2) {
+ /* Before GICv2 prio-drop and deactivate are not separable */
+ return false;
+ }
+ if (s->security_extn && !attrs.secure) {
+ return s->cpu_ctlr[cpu] & GICC_CTLR_EOIMODE_NS;
+ }
+ return s->cpu_ctlr[cpu] & GICC_CTLR_EOIMODE;
+}
+
+static void gic_deactivate_irq(GICState *s, int cpu, int irq, MemTxAttrs attrs)
+{
+ int cm = 1 << cpu;
+ int group = gic_has_groups(s) && GIC_TEST_GROUP(irq, cm);
+
+ if (!gic_eoi_split(s, cpu, attrs)) {
+ /* This is UNPREDICTABLE; we choose to ignore it */
+ qemu_log_mask(LOG_GUEST_ERROR,
+ "gic_deactivate_irq: GICC_DIR write when EOIMode clear");
+ return;
+ }
+
+ if (s->security_extn && !attrs.secure && !group) {
+ DPRINTF("Non-secure DI for Group0 interrupt %d ignored\n", irq);
+ return;
+ }
+
+ GIC_CLEAR_ACTIVE(irq, cm);
+}
+
void gic_complete_irq(GICState *s, int cpu, int irq, MemTxAttrs attrs)
{
- int update = 0;
int cm = 1 << cpu;
+ int group;
+
DPRINTF("EOI %d\n", irq);
if (irq >= s->num_irq) {
/* This handles two cases:
@@ -425,8 +553,9 @@ void gic_complete_irq(GICState *s, int cpu, int irq, MemTxAttrs attrs)
*/
return;
}
- if (s->running_irq[cpu] == 1023)
+ if (s->running_priority[cpu] == 0x100) {
return; /* No active IRQ. */
+ }
if (s->revision == REV_11MPCORE || s->revision == REV_NVIC) {
/* Mark level triggered interrupts as pending if they are still
@@ -435,11 +564,12 @@ void gic_complete_irq(GICState *s, int cpu, int irq, MemTxAttrs attrs)
&& 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->security_extn && !attrs.secure && !GIC_TEST_GROUP(irq, cm)) {
+ group = gic_has_groups(s) && GIC_TEST_GROUP(irq, cm);
+
+ if (s->security_extn && !attrs.secure && !group) {
DPRINTF("Non-secure EOI for Group0 interrupt %d ignored\n", irq);
return;
}
@@ -449,23 +579,13 @@ void gic_complete_irq(GICState *s, int cpu, int irq, MemTxAttrs attrs)
* i.e. go ahead and complete the irq anyway.
*/
- if (irq != s->running_irq[cpu]) {
- /* Complete an IRQ that is not currently running. */
- int tmp = s->running_irq[cpu];
- while (s->last_active[tmp][cpu] != 1023) {
- if (s->last_active[tmp][cpu] == irq) {
- s->last_active[tmp][cpu] = s->last_active[irq][cpu];
- break;
- }
- tmp = s->last_active[tmp][cpu];
- }
- if (update) {
- gic_update(s);
- }
- } else {
- /* Complete the current running IRQ. */
- gic_set_running_irq(s, cpu, s->last_active[s->running_irq[cpu]][cpu]);
+ gic_drop_prio(s, cpu, group);
+
+ /* In GICv2 the guest can choose to split priority-drop and deactivate */
+ if (!gic_eoi_split(s, cpu, attrs)) {
+ GIC_CLEAR_ACTIVE(irq, cm);
}
+ gic_update(s);
}
static uint32_t gic_dist_readb(void *opaque, hwaddr offset, MemTxAttrs attrs)
@@ -494,7 +614,7 @@ static uint32_t gic_dist_readb(void *opaque, hwaddr offset, MemTxAttrs attrs)
if (offset == 4)
/* Interrupt Controller Type Register */
return ((s->num_irq / 32) - 1)
- | ((NUM_CPU(s) - 1) << 5)
+ | ((s->num_cpu - 1) << 5)
| (s->security_extn << 10);
if (offset < 0x08)
return 0;
@@ -612,14 +732,31 @@ static uint32_t gic_dist_readb(void *opaque, hwaddr offset, MemTxAttrs attrs)
}
res = s->sgi_pending[irq][cpu];
- } else if (offset < 0xfe0) {
+ } else if (offset < 0xfd0) {
goto bad_reg;
- } else /* offset >= 0xfe0 */ {
+ } else if (offset < 0x1000) {
if (offset & 3) {
res = 0;
} else {
- res = gic_id[(offset - 0xfe0) >> 2];
+ switch (s->revision) {
+ case REV_11MPCORE:
+ res = gic_id_11mpcore[(offset - 0xfd0) >> 2];
+ break;
+ case 1:
+ res = gic_id_gicv1[(offset - 0xfd0) >> 2];
+ break;
+ case 2:
+ res = gic_id_gicv2[(offset - 0xfd0) >> 2];
+ break;
+ case REV_NVIC:
+ /* Shouldn't be able to get here */
+ abort();
+ default:
+ res = 0;
+ }
}
+ } else {
+ g_assert_not_reached();
}
return res;
bad_reg:
@@ -922,11 +1059,67 @@ static MemTxResult gic_dist_write(void *opaque, hwaddr offset, uint64_t data,
}
}
-static const MemoryRegionOps gic_dist_ops = {
- .read_with_attrs = gic_dist_read,
- .write_with_attrs = gic_dist_write,
- .endianness = DEVICE_NATIVE_ENDIAN,
-};
+static inline uint32_t gic_apr_ns_view(GICState *s, int cpu, int regno)
+{
+ /* Return the Nonsecure view of GICC_APR<regno>. This is the
+ * second half of GICC_NSAPR.
+ */
+ switch (GIC_MIN_BPR) {
+ case 0:
+ if (regno < 2) {
+ return s->nsapr[regno + 2][cpu];
+ }
+ break;
+ case 1:
+ if (regno == 0) {
+ return s->nsapr[regno + 1][cpu];
+ }
+ break;
+ case 2:
+ if (regno == 0) {
+ return extract32(s->nsapr[0][cpu], 16, 16);
+ }
+ break;
+ case 3:
+ if (regno == 0) {
+ return extract32(s->nsapr[0][cpu], 8, 8);
+ }
+ break;
+ default:
+ g_assert_not_reached();
+ }
+ return 0;
+}
+
+static inline void gic_apr_write_ns_view(GICState *s, int cpu, int regno,
+ uint32_t value)
+{
+ /* Write the Nonsecure view of GICC_APR<regno>. */
+ switch (GIC_MIN_BPR) {
+ case 0:
+ if (regno < 2) {
+ s->nsapr[regno + 2][cpu] = value;
+ }
+ break;
+ case 1:
+ if (regno == 0) {
+ s->nsapr[regno + 1][cpu] = value;
+ }
+ break;
+ case 2:
+ if (regno == 0) {
+ s->nsapr[0][cpu] = deposit32(s->nsapr[0][cpu], 16, 16, value);
+ }
+ break;
+ case 3:
+ if (regno == 0) {
+ s->nsapr[0][cpu] = deposit32(s->nsapr[0][cpu], 8, 8, value);
+ }
+ break;
+ default:
+ g_assert_not_reached();
+ }
+}
static MemTxResult gic_cpu_read(GICState *s, int cpu, int offset,
uint64_t *data, MemTxAttrs attrs)
@@ -968,8 +1161,31 @@ static MemTxResult gic_cpu_read(GICState *s, int cpu, int offset,
}
break;
case 0xd0: case 0xd4: case 0xd8: case 0xdc:
- *data = s->apr[(offset - 0xd0) / 4][cpu];
+ {
+ int regno = (offset - 0xd0) / 4;
+
+ if (regno >= GIC_NR_APRS || s->revision != 2) {
+ *data = 0;
+ } else if (s->security_extn && !attrs.secure) {
+ /* NS view of GICC_APR<n> is the top half of GIC_NSAPR<n> */
+ *data = gic_apr_ns_view(s, regno, cpu);
+ } else {
+ *data = s->apr[regno][cpu];
+ }
+ break;
+ }
+ case 0xe0: case 0xe4: case 0xe8: case 0xec:
+ {
+ int regno = (offset - 0xe0) / 4;
+
+ if (regno >= GIC_NR_APRS || s->revision != 2 || !gic_has_groups(s) ||
+ (s->security_extn && !attrs.secure)) {
+ *data = 0;
+ } else {
+ *data = s->nsapr[regno][cpu];
+ }
break;
+ }
default:
qemu_log_mask(LOG_GUEST_ERROR,
"gic_cpu_read: Bad offset %x\n", (int)offset);
@@ -1007,7 +1223,36 @@ static MemTxResult gic_cpu_write(GICState *s, int cpu, int offset,
}
break;
case 0xd0: case 0xd4: case 0xd8: case 0xdc:
- qemu_log_mask(LOG_UNIMP, "Writing APR not implemented\n");
+ {
+ int regno = (offset - 0xd0) / 4;
+
+ if (regno >= GIC_NR_APRS || s->revision != 2) {
+ return MEMTX_OK;
+ }
+ if (s->security_extn && !attrs.secure) {
+ /* NS view of GICC_APR<n> is the top half of GIC_NSAPR<n> */
+ gic_apr_write_ns_view(s, regno, cpu, value);
+ } else {
+ s->apr[regno][cpu] = value;
+ }
+ break;
+ }
+ case 0xe0: case 0xe4: case 0xe8: case 0xec:
+ {
+ int regno = (offset - 0xe0) / 4;
+
+ if (regno >= GIC_NR_APRS || s->revision != 2) {
+ return MEMTX_OK;
+ }
+ if (!gic_has_groups(s) || (s->security_extn && !attrs.secure)) {
+ return MEMTX_OK;
+ }
+ s->nsapr[regno][cpu] = value;
+ break;
+ }
+ case 0x1000:
+ /* GICC_DIR */
+ gic_deactivate_irq(s, cpu, value & 0x3ff, attrs);
break;
default:
qemu_log_mask(LOG_GUEST_ERROR,
@@ -1056,10 +1301,17 @@ static MemTxResult gic_do_cpu_write(void *opaque, hwaddr addr,
return gic_cpu_write(s, id, addr, value, attrs);
}
-static const MemoryRegionOps gic_thiscpu_ops = {
- .read_with_attrs = gic_thiscpu_read,
- .write_with_attrs = gic_thiscpu_write,
- .endianness = DEVICE_NATIVE_ENDIAN,
+static const MemoryRegionOps gic_ops[2] = {
+ {
+ .read_with_attrs = gic_dist_read,
+ .write_with_attrs = gic_dist_write,
+ .endianness = DEVICE_NATIVE_ENDIAN,
+ },
+ {
+ .read_with_attrs = gic_thiscpu_read,
+ .write_with_attrs = gic_thiscpu_write,
+ .endianness = DEVICE_NATIVE_ENDIAN,
+ }
};
static const MemoryRegionOps gic_cpu_ops = {
@@ -1068,31 +1320,10 @@ static const MemoryRegionOps gic_cpu_ops = {
.endianness = DEVICE_NATIVE_ENDIAN,
};
+/* This function is used by nvic model */
void gic_init_irqs_and_distributor(GICState *s)
{
- SysBusDevice *sbd = SYS_BUS_DEVICE(s);
- int i;
-
- i = s->num_irq - GIC_INTERNAL;
- /* For the GIC, also expose incoming GPIO lines for PPIs for each CPU.
- * GPIO array layout is thus:
- * [0..N-1] SPIs
- * [N..N+31] PPIs for CPU 0
- * [N+32..N+63] PPIs for CPU 1
- * ...
- */
- if (s->revision != REV_NVIC) {
- i += (GIC_INTERNAL * s->num_cpu);
- }
- qdev_init_gpio_in(DEVICE(s), gic_set_irq, i);
- for (i = 0; i < NUM_CPU(s); i++) {
- sysbus_init_irq(sbd, &s->parent_irq[i]);
- }
- for (i = 0; i < NUM_CPU(s); i++) {
- sysbus_init_irq(sbd, &s->parent_fiq[i]);
- }
- memory_region_init_io(&s->iomem, OBJECT(s), &gic_dist_ops, s,
- "gic_dist", 0x1000);
+ gic_init_irqs_and_mmio(s, gic_set_irq, gic_ops);
}
static void arm_gic_realize(DeviceState *dev, Error **errp)
@@ -1110,28 +1341,22 @@ static void arm_gic_realize(DeviceState *dev, Error **errp)
return;
}
- gic_init_irqs_and_distributor(s);
+ /* This creates distributor and main CPU interface (s->cpuiomem[0]) */
+ gic_init_irqs_and_mmio(s, gic_set_irq, gic_ops);
- /* Memory regions for the CPU interfaces (NVIC doesn't have these):
- * a region for "CPU interface for this core", then a region for
- * "CPU interface for core 0", "for core 1", ...
+ /* Extra core-specific regions for the CPU interfaces. This is
+ * necessary for "franken-GIC" implementations, for example on
+ * Exynos 4.
* NB that the memory region size of 0x100 applies for the 11MPCore
* and also cores following the GIC v1 spec (ie A9).
* GIC v2 defines a larger memory region (0x1000) so this will need
* to be extended when we implement A15.
*/
- memory_region_init_io(&s->cpuiomem[0], OBJECT(s), &gic_thiscpu_ops, s,
- "gic_cpu", 0x100);
- for (i = 0; i < NUM_CPU(s); i++) {
+ for (i = 0; i < s->num_cpu; i++) {
s->backref[i] = s;
memory_region_init_io(&s->cpuiomem[i+1], OBJECT(s), &gic_cpu_ops,
&s->backref[i], "gic_cpu", 0x100);
- }
- /* Distributor */
- sysbus_init_mmio(sbd, &s->iomem);
- /* cpu interfaces (one for "current cpu" plus one per cpu) */
- for (i = 0; i <= NUM_CPU(s); i++) {
- sysbus_init_mmio(sbd, &s->cpuiomem[i]);
+ sysbus_init_mmio(sbd, &s->cpuiomem[i+1]);
}
}
diff --git a/qemu/hw/intc/arm_gic_common.c b/qemu/hw/intc/arm_gic_common.c
index a64d0714e..0a1f56af1 100644
--- a/qemu/hw/intc/arm_gic_common.c
+++ b/qemu/hw/intc/arm_gic_common.c
@@ -18,7 +18,10 @@
* with this program; if not, see <http://www.gnu.org/licenses/>.
*/
+#include "qemu/osdep.h"
+#include "qapi/error.h"
#include "gic_internal.h"
+#include "hw/arm/linux-boot-if.h"
static void gic_pre_save(void *opaque)
{
@@ -59,8 +62,8 @@ static const VMStateDescription vmstate_gic_irq_state = {
static const VMStateDescription vmstate_gic = {
.name = "arm_gic",
- .version_id = 10,
- .minimum_version_id = 10,
+ .version_id = 12,
+ .minimum_version_id = 12,
.pre_save = gic_pre_save,
.post_load = gic_post_load,
.fields = (VMStateField[]) {
@@ -71,19 +74,59 @@ static const VMStateDescription vmstate_gic = {
VMSTATE_UINT8_ARRAY(irq_target, GICState, GIC_MAXIRQ),
VMSTATE_UINT8_2DARRAY(priority1, GICState, GIC_INTERNAL, GIC_NCPU),
VMSTATE_UINT8_ARRAY(priority2, GICState, GIC_MAXIRQ - GIC_INTERNAL),
- VMSTATE_UINT16_2DARRAY(last_active, GICState, GIC_MAXIRQ, GIC_NCPU),
VMSTATE_UINT8_2DARRAY(sgi_pending, GICState, GIC_NR_SGIS, GIC_NCPU),
VMSTATE_UINT16_ARRAY(priority_mask, GICState, GIC_NCPU),
- VMSTATE_UINT16_ARRAY(running_irq, GICState, GIC_NCPU),
VMSTATE_UINT16_ARRAY(running_priority, GICState, GIC_NCPU),
VMSTATE_UINT16_ARRAY(current_pending, GICState, GIC_NCPU),
VMSTATE_UINT8_ARRAY(bpr, GICState, GIC_NCPU),
VMSTATE_UINT8_ARRAY(abpr, GICState, GIC_NCPU),
VMSTATE_UINT32_2DARRAY(apr, GICState, GIC_NR_APRS, GIC_NCPU),
+ VMSTATE_UINT32_2DARRAY(nsapr, GICState, GIC_NR_APRS, GIC_NCPU),
VMSTATE_END_OF_LIST()
}
};
+void gic_init_irqs_and_mmio(GICState *s, qemu_irq_handler handler,
+ const MemoryRegionOps *ops)
+{
+ SysBusDevice *sbd = SYS_BUS_DEVICE(s);
+ int i = s->num_irq - GIC_INTERNAL;
+
+ /* For the GIC, also expose incoming GPIO lines for PPIs for each CPU.
+ * GPIO array layout is thus:
+ * [0..N-1] SPIs
+ * [N..N+31] PPIs for CPU 0
+ * [N+32..N+63] PPIs for CPU 1
+ * ...
+ */
+ if (s->revision != REV_NVIC) {
+ i += (GIC_INTERNAL * s->num_cpu);
+ }
+ qdev_init_gpio_in(DEVICE(s), handler, i);
+
+ for (i = 0; i < s->num_cpu; i++) {
+ sysbus_init_irq(sbd, &s->parent_irq[i]);
+ }
+ for (i = 0; i < s->num_cpu; i++) {
+ sysbus_init_irq(sbd, &s->parent_fiq[i]);
+ }
+
+ /* Distributor */
+ memory_region_init_io(&s->iomem, OBJECT(s), ops, s, "gic_dist", 0x1000);
+ sysbus_init_mmio(sbd, &s->iomem);
+
+ if (s->revision != REV_NVIC) {
+ /* This is the main CPU interface "for this core". It is always
+ * present because it is required by both software emulation and KVM.
+ * NVIC is not handled here because its CPU interface is different,
+ * neither it can use KVM.
+ */
+ memory_region_init_io(&s->cpuiomem[0], OBJECT(s), ops ? &ops[1] : NULL,
+ s, "gic_cpu", s->revision == 2 ? 0x2000 : 0x100);
+ sysbus_init_mmio(sbd, &s->cpuiomem[0]);
+ }
+}
+
static void arm_gic_common_realize(DeviceState *dev, Error **errp)
{
GICState *s = ARM_GIC_COMMON(dev);
@@ -124,21 +167,35 @@ static void arm_gic_common_reset(DeviceState *dev)
{
GICState *s = ARM_GIC_COMMON(dev);
int i, j;
+ int resetprio;
+
+ /* If we're resetting a TZ-aware GIC as if secure firmware
+ * had set it up ready to start a kernel in non-secure,
+ * we need to set interrupt priorities to a "zero for the
+ * NS view" value. This is particularly critical for the
+ * priority_mask[] values, because if they are zero then NS
+ * code cannot ever rewrite the priority to anything else.
+ */
+ if (s->security_extn && s->irq_reset_nonsecure) {
+ resetprio = 0x80;
+ } else {
+ resetprio = 0;
+ }
+
memset(s->irq_state, 0, GIC_MAXIRQ * sizeof(gic_irq_state));
for (i = 0 ; i < s->num_cpu; i++) {
if (s->revision == REV_11MPCORE) {
s->priority_mask[i] = 0xf0;
} else {
- s->priority_mask[i] = 0;
+ s->priority_mask[i] = resetprio;
}
s->current_pending[i] = 1023;
- s->running_irq[i] = 1023;
s->running_priority[i] = 0x100;
s->cpu_ctlr[i] = 0;
s->bpr[i] = GIC_MIN_BPR;
s->abpr[i] = GIC_MIN_ABPR;
for (j = 0; j < GIC_INTERNAL; j++) {
- s->priority1[j][i] = 0;
+ s->priority1[j][i] = resetprio;
}
for (j = 0; j < GIC_NR_SGIS; j++) {
s->sgi_pending[j][i] = 0;
@@ -150,7 +207,7 @@ static void arm_gic_common_reset(DeviceState *dev)
}
for (i = 0; i < ARRAY_SIZE(s->priority2); i++) {
- s->priority2[i] = 0;
+ s->priority2[i] = resetprio;
}
for (i = 0; i < GIC_MAXIRQ; i++) {
@@ -161,9 +218,32 @@ static void arm_gic_common_reset(DeviceState *dev)
s->irq_target[i] = 0;
}
}
+ if (s->security_extn && s->irq_reset_nonsecure) {
+ for (i = 0; i < GIC_MAXIRQ; i++) {
+ GIC_SET_GROUP(i, ALL_CPU_MASK);
+ }
+ }
+
s->ctlr = 0;
}
+static void arm_gic_common_linux_init(ARMLinuxBootIf *obj,
+ bool secure_boot)
+{
+ GICState *s = ARM_GIC_COMMON(obj);
+
+ if (s->security_extn && !secure_boot) {
+ /* We're directly booting a kernel into NonSecure. If this GIC
+ * implements the security extensions then we must configure it
+ * to have all the interrupts be NonSecure (this is a job that
+ * is done by the Secure boot firmware in real hardware, and in
+ * this mode QEMU is acting as a minimalist firmware-and-bootloader
+ * equivalent).
+ */
+ s->irq_reset_nonsecure = true;
+ }
+}
+
static Property arm_gic_common_properties[] = {
DEFINE_PROP_UINT32("num-cpu", GICState, num_cpu, 1),
DEFINE_PROP_UINT32("num-irq", GICState, num_irq, 32),
@@ -180,11 +260,13 @@ static Property arm_gic_common_properties[] = {
static void arm_gic_common_class_init(ObjectClass *klass, void *data)
{
DeviceClass *dc = DEVICE_CLASS(klass);
+ ARMLinuxBootIfClass *albifc = ARM_LINUX_BOOT_IF_CLASS(klass);
dc->reset = arm_gic_common_reset;
dc->realize = arm_gic_common_realize;
dc->props = arm_gic_common_properties;
dc->vmsd = &vmstate_gic;
+ albifc->arm_linux_init = arm_gic_common_linux_init;
}
static const TypeInfo arm_gic_common_type = {
@@ -194,6 +276,10 @@ static const TypeInfo arm_gic_common_type = {
.class_size = sizeof(ARMGICCommonClass),
.class_init = arm_gic_common_class_init,
.abstract = true,
+ .interfaces = (InterfaceInfo []) {
+ { TYPE_ARM_LINUX_BOOT_IF },
+ { },
+ },
};
static void register_types(void)
diff --git a/qemu/hw/intc/arm_gic_kvm.c b/qemu/hw/intc/arm_gic_kvm.c
index f56bff1af..bc85ab769 100644
--- a/qemu/hw/intc/arm_gic_kvm.c
+++ b/qemu/hw/intc/arm_gic_kvm.c
@@ -19,10 +19,14 @@
* with this program; if not, see <http://www.gnu.org/licenses/>.
*/
+#include "qemu/osdep.h"
+#include "qapi/error.h"
#include "hw/sysbus.h"
+#include "migration/migration.h"
#include "sysemu/kvm.h"
#include "kvm_arm.h"
#include "gic_internal.h"
+#include "vgic_common.h"
//#define DEBUG_GIC_KVM
@@ -52,7 +56,7 @@ typedef struct KVMARMGICClass {
void (*parent_reset)(DeviceState *dev);
} KVMARMGICClass;
-static void kvm_arm_gic_set_irq(void *opaque, int irq, int level)
+void kvm_arm_gic_set_irq(uint32_t num_irq, int irq, int level)
{
/* Meaning of the 'irq' parameter:
* [0..N-1] : external interrupts
@@ -63,10 +67,9 @@ static void kvm_arm_gic_set_irq(void *opaque, int irq, int level)
* has separate fields in the irq number for type,
* CPU number and interrupt number.
*/
- GICState *s = (GICState *)opaque;
int kvm_irq, irqtype, cpu;
- if (irq < (s->num_irq - GIC_INTERNAL)) {
+ if (irq < (num_irq - GIC_INTERNAL)) {
/* External interrupt. The kernel numbers these like the GIC
* hardware, with external interrupt IDs starting after the
* internal ones.
@@ -77,7 +80,7 @@ static void kvm_arm_gic_set_irq(void *opaque, int irq, int level)
} else {
/* Internal interrupt: decode into (cpu, interrupt id) */
irqtype = KVM_ARM_IRQ_TYPE_PPI;
- irq -= (s->num_irq - GIC_INTERNAL);
+ irq -= (num_irq - GIC_INTERNAL);
cpu = irq / GIC_INTERNAL;
irq %= GIC_INTERNAL;
}
@@ -87,69 +90,36 @@ static void kvm_arm_gic_set_irq(void *opaque, int irq, int level)
kvm_set_irq(kvm_state, kvm_irq, !!level);
}
-static bool kvm_arm_gic_can_save_restore(GICState *s)
-{
- return s->dev_fd >= 0;
-}
-
-static bool kvm_gic_supports_attr(GICState *s, int group, int attrnum)
+static void kvm_arm_gicv2_set_irq(void *opaque, int irq, int level)
{
- struct kvm_device_attr attr = {
- .group = group,
- .attr = attrnum,
- .flags = 0,
- };
-
- if (s->dev_fd == -1) {
- return false;
- }
+ GICState *s = (GICState *)opaque;
- return kvm_device_ioctl(s->dev_fd, KVM_HAS_DEVICE_ATTR, &attr) == 0;
+ kvm_arm_gic_set_irq(s->num_irq, irq, level);
}
-static void kvm_gic_access(GICState *s, int group, int offset,
- int cpu, uint32_t *val, bool write)
+static bool kvm_arm_gic_can_save_restore(GICState *s)
{
- struct kvm_device_attr attr;
- int type;
- int err;
-
- cpu = cpu & 0xff;
-
- attr.flags = 0;
- attr.group = group;
- attr.attr = (((uint64_t)cpu << KVM_DEV_ARM_VGIC_CPUID_SHIFT) &
- KVM_DEV_ARM_VGIC_CPUID_MASK) |
- (((uint64_t)offset << KVM_DEV_ARM_VGIC_OFFSET_SHIFT) &
- KVM_DEV_ARM_VGIC_OFFSET_MASK);
- attr.addr = (uintptr_t)val;
-
- if (write) {
- type = KVM_SET_DEVICE_ATTR;
- } else {
- type = KVM_GET_DEVICE_ATTR;
- }
-
- err = kvm_device_ioctl(s->dev_fd, type, &attr);
- if (err < 0) {
- fprintf(stderr, "KVM_{SET/GET}_DEVICE_ATTR failed: %s\n",
- strerror(-err));
- abort();
- }
+ return s->dev_fd >= 0;
}
+#define KVM_VGIC_ATTR(offset, cpu) \
+ ((((uint64_t)(cpu) << KVM_DEV_ARM_VGIC_CPUID_SHIFT) & \
+ KVM_DEV_ARM_VGIC_CPUID_MASK) | \
+ (((uint64_t)(offset) << KVM_DEV_ARM_VGIC_OFFSET_SHIFT) & \
+ KVM_DEV_ARM_VGIC_OFFSET_MASK))
+
static void kvm_gicd_access(GICState *s, int offset, int cpu,
uint32_t *val, bool write)
{
- kvm_gic_access(s, KVM_DEV_ARM_VGIC_GRP_DIST_REGS,
- offset, cpu, val, write);
+ kvm_device_access(s->dev_fd, KVM_DEV_ARM_VGIC_GRP_DIST_REGS,
+ KVM_VGIC_ATTR(offset, cpu), val, write);
}
static void kvm_gicc_access(GICState *s, int offset, int cpu,
uint32_t *val, bool write)
{
- kvm_gic_access(s, KVM_DEV_ARM_VGIC_GRP_CPU_REGS,
- offset, cpu, val, write);
+ kvm_device_access(s->dev_fd, KVM_DEV_ARM_VGIC_GRP_CPU_REGS,
+ KVM_VGIC_ATTR(offset, cpu), val, write);
}
#define for_each_irq_reg(_ctr, _max_irq, _field_width) \
@@ -340,11 +310,6 @@ static void kvm_arm_gic_put(GICState *s)
int num_cpu;
int num_irq;
- if (!kvm_arm_gic_can_save_restore(s)) {
- DPRINTF("Cannot put kernel gic state, no kernel interface");
- return;
- }
-
/* Note: We do the restore in a slightly different order than the save
* (where the order doesn't matter and is simply ordered according to the
* register offset values */
@@ -444,11 +409,6 @@ static void kvm_arm_gic_get(GICState *s)
int i;
int cpu;
- if (!kvm_arm_gic_can_save_restore(s)) {
- DPRINTF("Cannot get kernel gic state, no kernel interface");
- return;
- }
-
/*****************************************************************
* Distributor State
*/
@@ -536,14 +496,16 @@ static void kvm_arm_gic_reset(DeviceState *dev)
KVMARMGICClass *kgc = KVM_ARM_GIC_GET_CLASS(s);
kgc->parent_reset(dev);
- kvm_arm_gic_put(s);
+
+ if (kvm_arm_gic_can_save_restore(s)) {
+ kvm_arm_gic_put(s);
+ }
}
static void kvm_arm_gic_realize(DeviceState *dev, Error **errp)
{
int i;
GICState *s = KVM_ARM_GIC(dev);
- SysBusDevice *sbd = SYS_BUS_DEVICE(dev);
KVMARMGICClass *kgc = KVM_ARM_GIC_GET_CLASS(s);
Error *local_err = NULL;
int ret;
@@ -560,58 +522,37 @@ static void kvm_arm_gic_realize(DeviceState *dev, Error **errp)
return;
}
- i = s->num_irq - GIC_INTERNAL;
- /* For the GIC, also expose incoming GPIO lines for PPIs for each CPU.
- * GPIO array layout is thus:
- * [0..N-1] SPIs
- * [N..N+31] PPIs for CPU 0
- * [N+32..N+63] PPIs for CPU 1
- * ...
- */
- i += (GIC_INTERNAL * s->num_cpu);
- qdev_init_gpio_in(dev, kvm_arm_gic_set_irq, i);
+ gic_init_irqs_and_mmio(s, kvm_arm_gicv2_set_irq, NULL);
for (i = 0; i < s->num_irq - GIC_INTERNAL; i++) {
qemu_irq irq = qdev_get_gpio_in(dev, i);
kvm_irqchip_set_qemuirq_gsi(kvm_state, irq, i);
}
- /* We never use our outbound IRQ/FIQ lines but provide them so that
- * we maintain the same interface as the non-KVM GIC.
- */
- for (i = 0; i < s->num_cpu; i++) {
- sysbus_init_irq(sbd, &s->parent_irq[i]);
- }
- for (i = 0; i < s->num_cpu; i++) {
- sysbus_init_irq(sbd, &s->parent_fiq[i]);
- }
-
/* Try to create the device via the device control API */
s->dev_fd = -1;
ret = kvm_create_device(kvm_state, KVM_DEV_TYPE_ARM_VGIC_V2, false);
if (ret >= 0) {
s->dev_fd = ret;
+
+ /* Newstyle API is used, we may have attributes */
+ if (kvm_device_check_attr(s->dev_fd, KVM_DEV_ARM_VGIC_GRP_NR_IRQS, 0)) {
+ uint32_t numirqs = s->num_irq;
+ kvm_device_access(s->dev_fd, KVM_DEV_ARM_VGIC_GRP_NR_IRQS, 0,
+ &numirqs, true);
+ }
+ /* Tell the kernel to complete VGIC initialization now */
+ if (kvm_device_check_attr(s->dev_fd, KVM_DEV_ARM_VGIC_GRP_CTRL,
+ KVM_DEV_ARM_VGIC_CTRL_INIT)) {
+ kvm_device_access(s->dev_fd, KVM_DEV_ARM_VGIC_GRP_CTRL,
+ KVM_DEV_ARM_VGIC_CTRL_INIT, NULL, true);
+ }
} else if (ret != -ENODEV && ret != -ENOTSUP) {
error_setg_errno(errp, -ret, "error creating in-kernel VGIC");
return;
}
- if (kvm_gic_supports_attr(s, KVM_DEV_ARM_VGIC_GRP_NR_IRQS, 0)) {
- uint32_t numirqs = s->num_irq;
- kvm_gic_access(s, KVM_DEV_ARM_VGIC_GRP_NR_IRQS, 0, 0, &numirqs, 1);
- }
-
- /* Tell the kernel to complete VGIC initialization now */
- if (kvm_gic_supports_attr(s, KVM_DEV_ARM_VGIC_GRP_CTRL,
- KVM_DEV_ARM_VGIC_CTRL_INIT)) {
- kvm_gic_access(s, KVM_DEV_ARM_VGIC_GRP_CTRL,
- KVM_DEV_ARM_VGIC_CTRL_INIT, 0, 0, 1);
- }
-
/* Distributor */
- memory_region_init_reservation(&s->iomem, OBJECT(s),
- "kvm-gic_dist", 0x1000);
- sysbus_init_mmio(sbd, &s->iomem);
kvm_arm_register_device(&s->iomem,
(KVM_ARM_DEVICE_VGIC_V2 << KVM_ARM_DEVICE_ID_SHIFT)
| KVM_VGIC_V2_ADDR_TYPE_DIST,
@@ -622,15 +563,18 @@ static void kvm_arm_gic_realize(DeviceState *dev, Error **errp)
* provide the "interface for core #N" memory regions, because
* cores with a VGIC don't have those.
*/
- memory_region_init_reservation(&s->cpuiomem[0], OBJECT(s),
- "kvm-gic_cpu", 0x1000);
- sysbus_init_mmio(sbd, &s->cpuiomem[0]);
kvm_arm_register_device(&s->cpuiomem[0],
(KVM_ARM_DEVICE_VGIC_V2 << KVM_ARM_DEVICE_ID_SHIFT)
| KVM_VGIC_V2_ADDR_TYPE_CPU,
KVM_DEV_ARM_VGIC_GRP_ADDR,
KVM_VGIC_V2_ADDR_TYPE_CPU,
s->dev_fd);
+
+ if (!kvm_arm_gic_can_save_restore(s)) {
+ error_setg(&s->migration_blocker, "This operating system kernel does "
+ "not support vGICv2 migration");
+ migrate_add_blocker(s->migration_blocker);
+ }
}
static void kvm_arm_gic_class_init(ObjectClass *klass, void *data)
diff --git a/qemu/hw/intc/arm_gicv2m.c b/qemu/hw/intc/arm_gicv2m.c
index 43d1976c4..e8b5177dc 100644
--- a/qemu/hw/intc/arm_gicv2m.c
+++ b/qemu/hw/intc/arm_gicv2m.c
@@ -25,6 +25,8 @@
* identification registers and with a single non-secure MSI register frame.
*/
+#include "qemu/osdep.h"
+#include "qapi/error.h"
#include "hw/sysbus.h"
#include "hw/pci/msi.h"
@@ -147,7 +149,7 @@ static void gicv2m_realize(DeviceState *dev, Error **errp)
sysbus_init_irq(SYS_BUS_DEVICE(dev), &s->spi[i]);
}
- msi_supported = true;
+ msi_nonbroken = true;
kvm_gsi_direct_mapping = true;
kvm_msi_via_irqfd_allowed = kvm_irqfds_enabled();
}
diff --git a/qemu/hw/intc/arm_gicv3_common.c b/qemu/hw/intc/arm_gicv3_common.c
new file mode 100644
index 000000000..b9d3824f2
--- /dev/null
+++ b/qemu/hw/intc/arm_gicv3_common.c
@@ -0,0 +1,142 @@
+/*
+ * ARM GICv3 support - common bits of emulated and KVM kernel model
+ *
+ * Copyright (c) 2012 Linaro Limited
+ * Copyright (c) 2015 Huawei.
+ * Written by Peter Maydell
+ * Extended to 64 cores by Shlomo Pongratz
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "qemu/osdep.h"
+#include "qapi/error.h"
+#include "hw/intc/arm_gicv3_common.h"
+
+static void gicv3_pre_save(void *opaque)
+{
+ GICv3State *s = (GICv3State *)opaque;
+ ARMGICv3CommonClass *c = ARM_GICV3_COMMON_GET_CLASS(s);
+
+ if (c->pre_save) {
+ c->pre_save(s);
+ }
+}
+
+static int gicv3_post_load(void *opaque, int version_id)
+{
+ GICv3State *s = (GICv3State *)opaque;
+ ARMGICv3CommonClass *c = ARM_GICV3_COMMON_GET_CLASS(s);
+
+ if (c->post_load) {
+ c->post_load(s);
+ }
+ return 0;
+}
+
+static const VMStateDescription vmstate_gicv3 = {
+ .name = "arm_gicv3",
+ .unmigratable = 1,
+ .pre_save = gicv3_pre_save,
+ .post_load = gicv3_post_load,
+};
+
+void gicv3_init_irqs_and_mmio(GICv3State *s, qemu_irq_handler handler,
+ const MemoryRegionOps *ops)
+{
+ SysBusDevice *sbd = SYS_BUS_DEVICE(s);
+ int i;
+
+ /* For the GIC, also expose incoming GPIO lines for PPIs for each CPU.
+ * GPIO array layout is thus:
+ * [0..N-1] spi
+ * [N..N+31] PPIs for CPU 0
+ * [N+32..N+63] PPIs for CPU 1
+ * ...
+ */
+ i = s->num_irq - GIC_INTERNAL + GIC_INTERNAL * s->num_cpu;
+ qdev_init_gpio_in(DEVICE(s), handler, i);
+
+ s->parent_irq = g_malloc(s->num_cpu * sizeof(qemu_irq));
+ s->parent_fiq = g_malloc(s->num_cpu * sizeof(qemu_irq));
+
+ for (i = 0; i < s->num_cpu; i++) {
+ sysbus_init_irq(sbd, &s->parent_irq[i]);
+ }
+ for (i = 0; i < s->num_cpu; i++) {
+ sysbus_init_irq(sbd, &s->parent_fiq[i]);
+ }
+
+ memory_region_init_io(&s->iomem_dist, OBJECT(s), ops, s,
+ "gicv3_dist", 0x10000);
+ memory_region_init_io(&s->iomem_redist, OBJECT(s), ops ? &ops[1] : NULL, s,
+ "gicv3_redist", 0x20000 * s->num_cpu);
+
+ sysbus_init_mmio(sbd, &s->iomem_dist);
+ sysbus_init_mmio(sbd, &s->iomem_redist);
+}
+
+static void arm_gicv3_common_realize(DeviceState *dev, Error **errp)
+{
+ GICv3State *s = ARM_GICV3_COMMON(dev);
+
+ /* revision property is actually reserved and currently used only in order
+ * to keep the interface compatible with GICv2 code, avoiding extra
+ * conditions. However, in future it could be used, for example, if we
+ * implement GICv4.
+ */
+ if (s->revision != 3) {
+ error_setg(errp, "unsupported GIC revision %d", s->revision);
+ return;
+ }
+}
+
+static void arm_gicv3_common_reset(DeviceState *dev)
+{
+ /* TODO */
+}
+
+static Property arm_gicv3_common_properties[] = {
+ DEFINE_PROP_UINT32("num-cpu", GICv3State, num_cpu, 1),
+ DEFINE_PROP_UINT32("num-irq", GICv3State, num_irq, 32),
+ DEFINE_PROP_UINT32("revision", GICv3State, revision, 3),
+ DEFINE_PROP_BOOL("has-security-extensions", GICv3State, security_extn, 0),
+ DEFINE_PROP_END_OF_LIST(),
+};
+
+static void arm_gicv3_common_class_init(ObjectClass *klass, void *data)
+{
+ DeviceClass *dc = DEVICE_CLASS(klass);
+
+ dc->reset = arm_gicv3_common_reset;
+ dc->realize = arm_gicv3_common_realize;
+ dc->props = arm_gicv3_common_properties;
+ dc->vmsd = &vmstate_gicv3;
+}
+
+static const TypeInfo arm_gicv3_common_type = {
+ .name = TYPE_ARM_GICV3_COMMON,
+ .parent = TYPE_SYS_BUS_DEVICE,
+ .instance_size = sizeof(GICv3State),
+ .class_size = sizeof(ARMGICv3CommonClass),
+ .class_init = arm_gicv3_common_class_init,
+ .abstract = true,
+};
+
+static void register_types(void)
+{
+ type_register_static(&arm_gicv3_common_type);
+}
+
+type_init(register_types)
diff --git a/qemu/hw/intc/arm_gicv3_kvm.c b/qemu/hw/intc/arm_gicv3_kvm.c
new file mode 100644
index 000000000..acc173004
--- /dev/null
+++ b/qemu/hw/intc/arm_gicv3_kvm.c
@@ -0,0 +1,151 @@
+/*
+ * ARM Generic Interrupt Controller using KVM in-kernel support
+ *
+ * Copyright (c) 2015 Samsung Electronics Co., Ltd.
+ * Written by Pavel Fedin
+ * Based on vGICv2 code by Peter Maydell
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "qemu/osdep.h"
+#include "qapi/error.h"
+#include "hw/intc/arm_gicv3_common.h"
+#include "hw/sysbus.h"
+#include "sysemu/kvm.h"
+#include "kvm_arm.h"
+#include "vgic_common.h"
+
+#ifdef DEBUG_GICV3_KVM
+#define DPRINTF(fmt, ...) \
+ do { fprintf(stderr, "kvm_gicv3: " fmt, ## __VA_ARGS__); } while (0)
+#else
+#define DPRINTF(fmt, ...) \
+ do { } while (0)
+#endif
+
+#define TYPE_KVM_ARM_GICV3 "kvm-arm-gicv3"
+#define KVM_ARM_GICV3(obj) \
+ OBJECT_CHECK(GICv3State, (obj), TYPE_KVM_ARM_GICV3)
+#define KVM_ARM_GICV3_CLASS(klass) \
+ OBJECT_CLASS_CHECK(KVMARMGICv3Class, (klass), TYPE_KVM_ARM_GICV3)
+#define KVM_ARM_GICV3_GET_CLASS(obj) \
+ OBJECT_GET_CLASS(KVMARMGICv3Class, (obj), TYPE_KVM_ARM_GICV3)
+
+typedef struct KVMARMGICv3Class {
+ ARMGICv3CommonClass parent_class;
+ DeviceRealize parent_realize;
+ void (*parent_reset)(DeviceState *dev);
+} KVMARMGICv3Class;
+
+static void kvm_arm_gicv3_set_irq(void *opaque, int irq, int level)
+{
+ GICv3State *s = (GICv3State *)opaque;
+
+ kvm_arm_gic_set_irq(s->num_irq, irq, level);
+}
+
+static void kvm_arm_gicv3_put(GICv3State *s)
+{
+ /* TODO */
+ DPRINTF("Cannot put kernel gic state, no kernel interface\n");
+}
+
+static void kvm_arm_gicv3_get(GICv3State *s)
+{
+ /* TODO */
+ DPRINTF("Cannot get kernel gic state, no kernel interface\n");
+}
+
+static void kvm_arm_gicv3_reset(DeviceState *dev)
+{
+ GICv3State *s = ARM_GICV3_COMMON(dev);
+ KVMARMGICv3Class *kgc = KVM_ARM_GICV3_GET_CLASS(s);
+
+ DPRINTF("Reset\n");
+
+ kgc->parent_reset(dev);
+ kvm_arm_gicv3_put(s);
+}
+
+static void kvm_arm_gicv3_realize(DeviceState *dev, Error **errp)
+{
+ GICv3State *s = KVM_ARM_GICV3(dev);
+ KVMARMGICv3Class *kgc = KVM_ARM_GICV3_GET_CLASS(s);
+ Error *local_err = NULL;
+
+ DPRINTF("kvm_arm_gicv3_realize\n");
+
+ kgc->parent_realize(dev, &local_err);
+ if (local_err) {
+ error_propagate(errp, local_err);
+ return;
+ }
+
+ if (s->security_extn) {
+ error_setg(errp, "the in-kernel VGICv3 does not implement the "
+ "security extensions");
+ return;
+ }
+
+ gicv3_init_irqs_and_mmio(s, kvm_arm_gicv3_set_irq, NULL);
+
+ /* Try to create the device via the device control API */
+ s->dev_fd = kvm_create_device(kvm_state, KVM_DEV_TYPE_ARM_VGIC_V3, false);
+ if (s->dev_fd < 0) {
+ error_setg_errno(errp, -s->dev_fd, "error creating in-kernel VGIC");
+ return;
+ }
+
+ kvm_device_access(s->dev_fd, KVM_DEV_ARM_VGIC_GRP_NR_IRQS,
+ 0, &s->num_irq, true);
+
+ /* Tell the kernel to complete VGIC initialization now */
+ kvm_device_access(s->dev_fd, KVM_DEV_ARM_VGIC_GRP_CTRL,
+ KVM_DEV_ARM_VGIC_CTRL_INIT, NULL, true);
+
+ kvm_arm_register_device(&s->iomem_dist, -1, KVM_DEV_ARM_VGIC_GRP_ADDR,
+ KVM_VGIC_V3_ADDR_TYPE_DIST, s->dev_fd);
+ kvm_arm_register_device(&s->iomem_redist, -1, KVM_DEV_ARM_VGIC_GRP_ADDR,
+ KVM_VGIC_V3_ADDR_TYPE_REDIST, s->dev_fd);
+}
+
+static void kvm_arm_gicv3_class_init(ObjectClass *klass, void *data)
+{
+ DeviceClass *dc = DEVICE_CLASS(klass);
+ ARMGICv3CommonClass *agcc = ARM_GICV3_COMMON_CLASS(klass);
+ KVMARMGICv3Class *kgc = KVM_ARM_GICV3_CLASS(klass);
+
+ agcc->pre_save = kvm_arm_gicv3_get;
+ agcc->post_load = kvm_arm_gicv3_put;
+ kgc->parent_realize = dc->realize;
+ kgc->parent_reset = dc->reset;
+ dc->realize = kvm_arm_gicv3_realize;
+ dc->reset = kvm_arm_gicv3_reset;
+}
+
+static const TypeInfo kvm_arm_gicv3_info = {
+ .name = TYPE_KVM_ARM_GICV3,
+ .parent = TYPE_ARM_GICV3_COMMON,
+ .instance_size = sizeof(GICv3State),
+ .class_init = kvm_arm_gicv3_class_init,
+ .class_size = sizeof(KVMARMGICv3Class),
+};
+
+static void kvm_arm_gicv3_register_types(void)
+{
+ type_register_static(&kvm_arm_gicv3_info);
+}
+
+type_init(kvm_arm_gicv3_register_types)
diff --git a/qemu/hw/intc/armv7m_nvic.c b/qemu/hw/intc/armv7m_nvic.c
index e13b729e1..669e82adf 100644
--- a/qemu/hw/intc/armv7m_nvic.c
+++ b/qemu/hw/intc/armv7m_nvic.c
@@ -10,6 +10,9 @@
* NVIC. Much of that is also implemented here.
*/
+#include "qemu/osdep.h"
+#include "qapi/error.h"
+#include "qemu-common.h"
#include "hw/sysbus.h"
#include "qemu/timer.h"
#include "hw/arm/arm.h"
@@ -28,6 +31,7 @@ typedef struct {
MemoryRegion gic_iomem_alias;
MemoryRegion container;
uint32_t num_irq;
+ qemu_irq sysresetreq;
} nvic_state;
#define TYPE_NVIC "armv7m_nvic"
@@ -185,26 +189,25 @@ static uint32_t nvic_readl(nvic_state *s, uint32_t offset)
return cpu->midr;
case 0xd04: /* Interrupt Control State. */
/* VECTACTIVE */
- val = s->gic.running_irq[0];
+ cpu = ARM_CPU(current_cpu);
+ val = cpu->env.v7m.exception;
if (val == 1023) {
val = 0;
} else if (val >= 32) {
val -= 16;
}
- /* RETTOBASE */
- if (s->gic.running_irq[0] == 1023
- || s->gic.last_active[s->gic.running_irq[0]][0] == 1023) {
- val |= (1 << 11);
- }
/* VECTPENDING */
if (s->gic.current_pending[0] != 1023)
val |= (s->gic.current_pending[0] << 12);
- /* ISRPENDING */
+ /* ISRPENDING and RETTOBASE */
for (irq = 32; irq < s->num_irq; irq++) {
if (s->gic.irq_state[irq].pending) {
val |= (1 << 22);
break;
}
+ if (irq != cpu->env.v7m.exception && s->gic.irq_state[irq].active) {
+ val |= (1 << 11);
+ }
}
/* PENDSTSET */
if (s->gic.irq_state[ARMV7M_EXCP_SYSTICK].pending)
@@ -349,10 +352,13 @@ static void nvic_writel(nvic_state *s, uint32_t offset, uint32_t value)
break;
case 0xd0c: /* Application Interrupt/Reset Control. */
if ((value >> 16) == 0x05fa) {
+ if (value & 4) {
+ qemu_irq_pulse(s->sysresetreq);
+ }
if (value & 2) {
qemu_log_mask(LOG_UNIMP, "VECTCLRACTIVE unimplemented\n");
}
- if (value & 5) {
+ if (value & 1) {
qemu_log_mask(LOG_UNIMP, "AIRCR system reset unimplemented\n");
}
if (value & 0x700) {
@@ -536,11 +542,14 @@ static void armv7m_nvic_instance_init(Object *obj)
* value in the GICState struct.
*/
GICState *s = ARM_GIC_COMMON(obj);
+ DeviceState *dev = DEVICE(obj);
+ nvic_state *nvic = NVIC(obj);
/* The ARM v7m may have anything from 0 to 496 external interrupt
* IRQ lines. We default to 64. Other boards may differ and should
* set the num-irq property appropriately.
*/
s->num_irq = 64;
+ qdev_init_gpio_out_named(dev, &nvic->sysresetreq, "SYSRESETREQ", 1);
}
static void armv7m_nvic_class_init(ObjectClass *klass, void *data)
diff --git a/qemu/hw/intc/aspeed_vic.c b/qemu/hw/intc/aspeed_vic.c
new file mode 100644
index 000000000..19a0ff748
--- /dev/null
+++ b/qemu/hw/intc/aspeed_vic.c
@@ -0,0 +1,339 @@
+/*
+ * ASPEED Interrupt Controller (New)
+ *
+ * Andrew Jeffery <andrew@aj.id.au>
+ *
+ * Copyright 2015, 2016 IBM Corp.
+ *
+ * This code is licensed under the GPL version 2 or later. See
+ * the COPYING file in the top-level directory.
+ */
+
+/* The hardware exposes two register sets, a legacy set and a 'new' set. The
+ * model implements the 'new' register set, and logs warnings on accesses to
+ * the legacy IO space.
+ *
+ * The hardware uses 32bit registers to manage 51 IRQs, with low and high
+ * registers for each conceptual register. The device model's implementation
+ * uses 64bit data types to store both low and high register values (in the one
+ * member), but must cope with access offset values in multiples of 4 passed to
+ * the callbacks. As such the read() and write() implementations process the
+ * provided offset to understand whether the access is requesting the lower or
+ * upper 32 bits of the 64bit member.
+ *
+ * Additionally, the "Interrupt Enable", "Edge Status" and "Software Interrupt"
+ * fields have separate "enable"/"status" and "clear" registers, where set bits
+ * are written to one or the other to change state (avoiding a
+ * read-modify-write sequence).
+ */
+
+#include "qemu/osdep.h"
+#include <inttypes.h>
+#include "hw/intc/aspeed_vic.h"
+#include "qemu/bitops.h"
+#include "trace.h"
+
+#define AVIC_NEW_BASE_OFFSET 0x80
+
+#define AVIC_L_MASK 0xFFFFFFFFU
+#define AVIC_H_MASK 0x0007FFFFU
+#define AVIC_EVENT_W_MASK (0x78000ULL << 32)
+
+static void aspeed_vic_update(AspeedVICState *s)
+{
+ uint64_t new = (s->raw & s->enable);
+ uint64_t flags;
+
+ flags = new & s->select;
+ trace_aspeed_vic_update_fiq(!!flags);
+ qemu_set_irq(s->fiq, !!flags);
+
+ flags = new & ~s->select;
+ trace_aspeed_vic_update_irq(!!flags);
+ qemu_set_irq(s->irq, !!flags);
+}
+
+static void aspeed_vic_set_irq(void *opaque, int irq, int level)
+{
+ uint64_t irq_mask;
+ bool raise;
+ AspeedVICState *s = (AspeedVICState *)opaque;
+
+ if (irq > ASPEED_VIC_NR_IRQS) {
+ qemu_log_mask(LOG_GUEST_ERROR, "%s: Invalid interrupt number: %d\n",
+ __func__, irq);
+ return;
+ }
+
+ trace_aspeed_vic_set_irq(irq, level);
+
+ irq_mask = BIT(irq);
+ if (s->sense & irq_mask) {
+ /* level-triggered */
+ if (s->event & irq_mask) {
+ /* high-sensitive */
+ raise = level;
+ } else {
+ /* low-sensitive */
+ raise = !level;
+ }
+ s->raw = deposit64(s->raw, irq, 1, raise);
+ } else {
+ uint64_t old_level = s->level & irq_mask;
+
+ /* edge-triggered */
+ if (s->dual_edge & irq_mask) {
+ raise = (!!old_level) != (!!level);
+ } else {
+ if (s->event & irq_mask) {
+ /* rising-sensitive */
+ raise = !old_level && level;
+ } else {
+ /* falling-sensitive */
+ raise = old_level && !level;
+ }
+ }
+ if (raise) {
+ s->raw = deposit64(s->raw, irq, 1, raise);
+ }
+ }
+ s->level = deposit64(s->level, irq, 1, level);
+ aspeed_vic_update(s);
+}
+
+static uint64_t aspeed_vic_read(void *opaque, hwaddr offset, unsigned size)
+{
+ uint64_t val;
+ const bool high = !!(offset & 0x4);
+ hwaddr n_offset = (offset & ~0x4);
+ AspeedVICState *s = (AspeedVICState *)opaque;
+
+ if (offset < AVIC_NEW_BASE_OFFSET) {
+ qemu_log_mask(LOG_UNIMP, "%s: Ignoring read from legacy registers "
+ "at 0x%" HWADDR_PRIx "[%u]\n", __func__, offset, size);
+ return 0;
+ }
+
+ n_offset -= AVIC_NEW_BASE_OFFSET;
+
+ switch (n_offset) {
+ case 0x0: /* IRQ Status */
+ val = s->raw & ~s->select & s->enable;
+ break;
+ case 0x08: /* FIQ Status */
+ val = s->raw & s->select & s->enable;
+ break;
+ case 0x10: /* Raw Interrupt Status */
+ val = s->raw;
+ break;
+ case 0x18: /* Interrupt Selection */
+ val = s->select;
+ break;
+ case 0x20: /* Interrupt Enable */
+ val = s->enable;
+ break;
+ case 0x30: /* Software Interrupt */
+ val = s->trigger;
+ break;
+ case 0x40: /* Interrupt Sensitivity */
+ val = s->sense;
+ break;
+ case 0x48: /* Interrupt Both Edge Trigger Control */
+ val = s->dual_edge;
+ break;
+ case 0x50: /* Interrupt Event */
+ val = s->event;
+ break;
+ case 0x60: /* Edge Triggered Interrupt Status */
+ val = s->raw & ~s->sense;
+ break;
+ /* Illegal */
+ case 0x28: /* Interrupt Enable Clear */
+ case 0x38: /* Software Interrupt Clear */
+ case 0x58: /* Edge Triggered Interrupt Clear */
+ qemu_log_mask(LOG_GUEST_ERROR,
+ "%s: Read of write-only register with offset 0x%"
+ HWADDR_PRIx "\n", __func__, offset);
+ val = 0;
+ break;
+ default:
+ qemu_log_mask(LOG_GUEST_ERROR,
+ "%s: Bad register at offset 0x%" HWADDR_PRIx "\n",
+ __func__, offset);
+ val = 0;
+ break;
+ }
+ if (high) {
+ val = extract64(val, 32, 19);
+ }
+ trace_aspeed_vic_read(offset, size, val);
+ return val;
+}
+
+static void aspeed_vic_write(void *opaque, hwaddr offset, uint64_t data,
+ unsigned size)
+{
+ const bool high = !!(offset & 0x4);
+ hwaddr n_offset = (offset & ~0x4);
+ AspeedVICState *s = (AspeedVICState *)opaque;
+
+ if (offset < AVIC_NEW_BASE_OFFSET) {
+ qemu_log_mask(LOG_UNIMP,
+ "%s: Ignoring write to legacy registers at 0x%"
+ HWADDR_PRIx "[%u] <- 0x%" PRIx64 "\n", __func__, offset,
+ size, data);
+ return;
+ }
+
+ n_offset -= AVIC_NEW_BASE_OFFSET;
+ trace_aspeed_vic_write(offset, size, data);
+
+ /* Given we have members using separate enable/clear registers, deposit64()
+ * isn't quite the tool for the job. Instead, relocate the incoming bits to
+ * the required bit offset based on the provided access address
+ */
+ if (high) {
+ data &= AVIC_H_MASK;
+ data <<= 32;
+ } else {
+ data &= AVIC_L_MASK;
+ }
+
+ switch (n_offset) {
+ case 0x18: /* Interrupt Selection */
+ /* Register has deposit64() semantics - overwrite requested 32 bits */
+ if (high) {
+ s->select &= AVIC_L_MASK;
+ } else {
+ s->select &= ((uint64_t) AVIC_H_MASK) << 32;
+ }
+ s->select |= data;
+ break;
+ case 0x20: /* Interrupt Enable */
+ s->enable |= data;
+ break;
+ case 0x28: /* Interrupt Enable Clear */
+ s->enable &= ~data;
+ break;
+ case 0x30: /* Software Interrupt */
+ qemu_log_mask(LOG_UNIMP, "%s: Software interrupts unavailable. "
+ "IRQs requested: 0x%016" PRIx64 "\n", __func__, data);
+ break;
+ case 0x38: /* Software Interrupt Clear */
+ qemu_log_mask(LOG_UNIMP, "%s: Software interrupts unavailable. "
+ "IRQs to be cleared: 0x%016" PRIx64 "\n", __func__, data);
+ break;
+ case 0x50: /* Interrupt Event */
+ /* Register has deposit64() semantics - overwrite the top four valid
+ * IRQ bits, as only the top four IRQs (GPIOs) can change their event
+ * type */
+ if (high) {
+ s->event &= ~AVIC_EVENT_W_MASK;
+ s->event |= (data & AVIC_EVENT_W_MASK);
+ } else {
+ qemu_log_mask(LOG_GUEST_ERROR,
+ "Ignoring invalid write to interrupt event register");
+ }
+ break;
+ case 0x58: /* Edge Triggered Interrupt Clear */
+ s->raw &= ~(data & ~s->sense);
+ break;
+ case 0x00: /* IRQ Status */
+ case 0x08: /* FIQ Status */
+ case 0x10: /* Raw Interrupt Status */
+ case 0x40: /* Interrupt Sensitivity */
+ case 0x48: /* Interrupt Both Edge Trigger Control */
+ case 0x60: /* Edge Triggered Interrupt Status */
+ qemu_log_mask(LOG_GUEST_ERROR,
+ "%s: Write of read-only register with offset 0x%"
+ HWADDR_PRIx "\n", __func__, offset);
+ break;
+
+ default:
+ qemu_log_mask(LOG_GUEST_ERROR,
+ "%s: Bad register at offset 0x%" HWADDR_PRIx "\n",
+ __func__, offset);
+ break;
+ }
+ aspeed_vic_update(s);
+}
+
+static const MemoryRegionOps aspeed_vic_ops = {
+ .read = aspeed_vic_read,
+ .write = aspeed_vic_write,
+ .endianness = DEVICE_LITTLE_ENDIAN,
+ .valid.min_access_size = 4,
+ .valid.max_access_size = 4,
+ .valid.unaligned = false,
+};
+
+static void aspeed_vic_reset(DeviceState *dev)
+{
+ AspeedVICState *s = ASPEED_VIC(dev);
+
+ s->level = 0;
+ s->raw = 0;
+ s->select = 0;
+ s->enable = 0;
+ s->trigger = 0;
+ s->sense = 0x1F07FFF8FFFFULL;
+ s->dual_edge = 0xF800070000ULL;
+ s->event = 0x5F07FFF8FFFFULL;
+}
+
+#define AVIC_IO_REGION_SIZE 0x20000
+
+static void aspeed_vic_realize(DeviceState *dev, Error **errp)
+{
+ SysBusDevice *sbd = SYS_BUS_DEVICE(dev);
+ AspeedVICState *s = ASPEED_VIC(dev);
+
+ memory_region_init_io(&s->iomem, OBJECT(s), &aspeed_vic_ops, s,
+ TYPE_ASPEED_VIC, AVIC_IO_REGION_SIZE);
+
+ sysbus_init_mmio(sbd, &s->iomem);
+
+ qdev_init_gpio_in(dev, aspeed_vic_set_irq, ASPEED_VIC_NR_IRQS);
+ sysbus_init_irq(sbd, &s->irq);
+ sysbus_init_irq(sbd, &s->fiq);
+}
+
+static const VMStateDescription vmstate_aspeed_vic = {
+ .name = "aspeed.new-vic",
+ .version_id = 1,
+ .minimum_version_id = 1,
+ .fields = (VMStateField[]) {
+ VMSTATE_UINT64(level, AspeedVICState),
+ VMSTATE_UINT64(raw, AspeedVICState),
+ VMSTATE_UINT64(select, AspeedVICState),
+ VMSTATE_UINT64(enable, AspeedVICState),
+ VMSTATE_UINT64(trigger, AspeedVICState),
+ VMSTATE_UINT64(sense, AspeedVICState),
+ VMSTATE_UINT64(dual_edge, AspeedVICState),
+ VMSTATE_UINT64(event, AspeedVICState),
+ VMSTATE_END_OF_LIST()
+ }
+};
+
+static void aspeed_vic_class_init(ObjectClass *klass, void *data)
+{
+ DeviceClass *dc = DEVICE_CLASS(klass);
+ dc->realize = aspeed_vic_realize;
+ dc->reset = aspeed_vic_reset;
+ dc->desc = "ASPEED Interrupt Controller (New)";
+ dc->vmsd = &vmstate_aspeed_vic;
+}
+
+static const TypeInfo aspeed_vic_info = {
+ .name = TYPE_ASPEED_VIC,
+ .parent = TYPE_SYS_BUS_DEVICE,
+ .instance_size = sizeof(AspeedVICState),
+ .class_init = aspeed_vic_class_init,
+};
+
+static void aspeed_vic_register_types(void)
+{
+ type_register_static(&aspeed_vic_info);
+}
+
+type_init(aspeed_vic_register_types);
diff --git a/qemu/hw/intc/bcm2835_ic.c b/qemu/hw/intc/bcm2835_ic.c
new file mode 100644
index 000000000..80513b28f
--- /dev/null
+++ b/qemu/hw/intc/bcm2835_ic.c
@@ -0,0 +1,237 @@
+/*
+ * Raspberry Pi emulation (c) 2012 Gregory Estrade
+ * Refactoring for Pi2 Copyright (c) 2015, Microsoft. Written by Andrew Baumann.
+ * This code is licensed under the GNU GPLv2 and later.
+ * Heavily based on pl190.c, copyright terms below:
+ *
+ * Arm PrimeCell PL190 Vector Interrupt Controller
+ *
+ * Copyright (c) 2006 CodeSourcery.
+ * Written by Paul Brook
+ *
+ * This code is licensed under the GPL.
+ */
+
+#include "qemu/osdep.h"
+#include "hw/intc/bcm2835_ic.h"
+
+#define GPU_IRQS 64
+#define ARM_IRQS 8
+
+#define IRQ_PENDING_BASIC 0x00 /* IRQ basic pending */
+#define IRQ_PENDING_1 0x04 /* IRQ pending 1 */
+#define IRQ_PENDING_2 0x08 /* IRQ pending 2 */
+#define FIQ_CONTROL 0x0C /* FIQ register */
+#define IRQ_ENABLE_1 0x10 /* Interrupt enable register 1 */
+#define IRQ_ENABLE_2 0x14 /* Interrupt enable register 2 */
+#define IRQ_ENABLE_BASIC 0x18 /* Base interrupt enable register */
+#define IRQ_DISABLE_1 0x1C /* Interrupt disable register 1 */
+#define IRQ_DISABLE_2 0x20 /* Interrupt disable register 2 */
+#define IRQ_DISABLE_BASIC 0x24 /* Base interrupt disable register */
+
+/* Update interrupts. */
+static void bcm2835_ic_update(BCM2835ICState *s)
+{
+ bool set = false;
+
+ if (s->fiq_enable) {
+ if (s->fiq_select >= GPU_IRQS) {
+ /* ARM IRQ */
+ set = extract32(s->arm_irq_level, s->fiq_select - GPU_IRQS, 1);
+ } else {
+ set = extract64(s->gpu_irq_level, s->fiq_select, 1);
+ }
+ }
+ qemu_set_irq(s->fiq, set);
+
+ set = (s->gpu_irq_level & s->gpu_irq_enable)
+ || (s->arm_irq_level & s->arm_irq_enable);
+ qemu_set_irq(s->irq, set);
+
+}
+
+static void bcm2835_ic_set_gpu_irq(void *opaque, int irq, int level)
+{
+ BCM2835ICState *s = opaque;
+
+ assert(irq >= 0 && irq < 64);
+ s->gpu_irq_level = deposit64(s->gpu_irq_level, irq, 1, level != 0);
+ bcm2835_ic_update(s);
+}
+
+static void bcm2835_ic_set_arm_irq(void *opaque, int irq, int level)
+{
+ BCM2835ICState *s = opaque;
+
+ assert(irq >= 0 && irq < 8);
+ s->arm_irq_level = deposit32(s->arm_irq_level, irq, 1, level != 0);
+ bcm2835_ic_update(s);
+}
+
+static const int irq_dups[] = { 7, 9, 10, 18, 19, 53, 54, 55, 56, 57, 62 };
+
+static uint64_t bcm2835_ic_read(void *opaque, hwaddr offset, unsigned size)
+{
+ BCM2835ICState *s = opaque;
+ uint32_t res = 0;
+ uint64_t gpu_pending = s->gpu_irq_level & s->gpu_irq_enable;
+ int i;
+
+ switch (offset) {
+ case IRQ_PENDING_BASIC:
+ /* bits 0-7: ARM irqs */
+ res = s->arm_irq_level & s->arm_irq_enable;
+
+ /* bits 8 & 9: pending registers 1 & 2 */
+ res |= (((uint32_t)gpu_pending) != 0) << 8;
+ res |= ((gpu_pending >> 32) != 0) << 9;
+
+ /* bits 10-20: selected GPU IRQs */
+ for (i = 0; i < ARRAY_SIZE(irq_dups); i++) {
+ res |= extract64(gpu_pending, irq_dups[i], 1) << (i + 10);
+ }
+ break;
+ case IRQ_PENDING_1:
+ res = gpu_pending;
+ break;
+ case IRQ_PENDING_2:
+ res = gpu_pending >> 32;
+ break;
+ case FIQ_CONTROL:
+ res = (s->fiq_enable << 7) | s->fiq_select;
+ break;
+ case IRQ_ENABLE_1:
+ res = s->gpu_irq_enable;
+ break;
+ case IRQ_ENABLE_2:
+ res = s->gpu_irq_enable >> 32;
+ break;
+ case IRQ_ENABLE_BASIC:
+ res = s->arm_irq_enable;
+ break;
+ case IRQ_DISABLE_1:
+ res = ~s->gpu_irq_enable;
+ break;
+ case IRQ_DISABLE_2:
+ res = ~s->gpu_irq_enable >> 32;
+ break;
+ case IRQ_DISABLE_BASIC:
+ res = ~s->arm_irq_enable;
+ break;
+ default:
+ qemu_log_mask(LOG_GUEST_ERROR, "%s: Bad offset %"HWADDR_PRIx"\n",
+ __func__, offset);
+ return 0;
+ }
+
+ return res;
+}
+
+static void bcm2835_ic_write(void *opaque, hwaddr offset, uint64_t val,
+ unsigned size)
+{
+ BCM2835ICState *s = opaque;
+
+ switch (offset) {
+ case FIQ_CONTROL:
+ s->fiq_select = extract32(val, 0, 7);
+ s->fiq_enable = extract32(val, 7, 1);
+ break;
+ case IRQ_ENABLE_1:
+ s->gpu_irq_enable |= val;
+ break;
+ case IRQ_ENABLE_2:
+ s->gpu_irq_enable |= val << 32;
+ break;
+ case IRQ_ENABLE_BASIC:
+ s->arm_irq_enable |= val & 0xff;
+ break;
+ case IRQ_DISABLE_1:
+ s->gpu_irq_enable &= ~val;
+ break;
+ case IRQ_DISABLE_2:
+ s->gpu_irq_enable &= ~(val << 32);
+ break;
+ case IRQ_DISABLE_BASIC:
+ s->arm_irq_enable &= ~val & 0xff;
+ break;
+ default:
+ qemu_log_mask(LOG_GUEST_ERROR, "%s: Bad offset %"HWADDR_PRIx"\n",
+ __func__, offset);
+ return;
+ }
+ bcm2835_ic_update(s);
+}
+
+static const MemoryRegionOps bcm2835_ic_ops = {
+ .read = bcm2835_ic_read,
+ .write = bcm2835_ic_write,
+ .endianness = DEVICE_NATIVE_ENDIAN,
+ .valid.min_access_size = 4,
+ .valid.max_access_size = 4,
+};
+
+static void bcm2835_ic_reset(DeviceState *d)
+{
+ BCM2835ICState *s = BCM2835_IC(d);
+
+ s->gpu_irq_enable = 0;
+ s->arm_irq_enable = 0;
+ s->fiq_enable = false;
+ s->fiq_select = 0;
+}
+
+static void bcm2835_ic_init(Object *obj)
+{
+ BCM2835ICState *s = BCM2835_IC(obj);
+
+ memory_region_init_io(&s->iomem, obj, &bcm2835_ic_ops, s, TYPE_BCM2835_IC,
+ 0x200);
+ sysbus_init_mmio(SYS_BUS_DEVICE(s), &s->iomem);
+
+ qdev_init_gpio_in_named(DEVICE(s), bcm2835_ic_set_gpu_irq,
+ BCM2835_IC_GPU_IRQ, GPU_IRQS);
+ qdev_init_gpio_in_named(DEVICE(s), bcm2835_ic_set_arm_irq,
+ BCM2835_IC_ARM_IRQ, ARM_IRQS);
+
+ sysbus_init_irq(SYS_BUS_DEVICE(s), &s->irq);
+ sysbus_init_irq(SYS_BUS_DEVICE(s), &s->fiq);
+}
+
+static const VMStateDescription vmstate_bcm2835_ic = {
+ .name = TYPE_BCM2835_IC,
+ .version_id = 1,
+ .minimum_version_id = 1,
+ .fields = (VMStateField[]) {
+ VMSTATE_UINT64(gpu_irq_level, BCM2835ICState),
+ VMSTATE_UINT64(gpu_irq_enable, BCM2835ICState),
+ VMSTATE_UINT8(arm_irq_level, BCM2835ICState),
+ VMSTATE_UINT8(arm_irq_enable, BCM2835ICState),
+ VMSTATE_BOOL(fiq_enable, BCM2835ICState),
+ VMSTATE_UINT8(fiq_select, BCM2835ICState),
+ VMSTATE_END_OF_LIST()
+ }
+};
+
+static void bcm2835_ic_class_init(ObjectClass *klass, void *data)
+{
+ DeviceClass *dc = DEVICE_CLASS(klass);
+
+ dc->reset = bcm2835_ic_reset;
+ dc->vmsd = &vmstate_bcm2835_ic;
+}
+
+static TypeInfo bcm2835_ic_info = {
+ .name = TYPE_BCM2835_IC,
+ .parent = TYPE_SYS_BUS_DEVICE,
+ .instance_size = sizeof(BCM2835ICState),
+ .class_init = bcm2835_ic_class_init,
+ .instance_init = bcm2835_ic_init,
+};
+
+static void bcm2835_ic_register_types(void)
+{
+ type_register_static(&bcm2835_ic_info);
+}
+
+type_init(bcm2835_ic_register_types)
diff --git a/qemu/hw/intc/bcm2836_control.c b/qemu/hw/intc/bcm2836_control.c
new file mode 100644
index 000000000..d0271810c
--- /dev/null
+++ b/qemu/hw/intc/bcm2836_control.c
@@ -0,0 +1,304 @@
+/*
+ * Rasperry Pi 2 emulation ARM control logic module.
+ * Copyright (c) 2015, Microsoft
+ * Written by Andrew Baumann
+ *
+ * Based on bcm2835_ic.c (Raspberry Pi emulation) (c) 2012 Gregory Estrade
+ * This code is licensed under the GNU GPLv2 and later.
+ *
+ * At present, only implements interrupt routing, and mailboxes (i.e.,
+ * not local timer, PMU interrupt, or AXI counters).
+ *
+ * Ref:
+ * https://www.raspberrypi.org/documentation/hardware/raspberrypi/bcm2836/QA7_rev3.4.pdf
+ */
+
+#include "qemu/osdep.h"
+#include "hw/intc/bcm2836_control.h"
+
+#define REG_GPU_ROUTE 0x0c
+#define REG_TIMERCONTROL 0x40
+#define REG_MBOXCONTROL 0x50
+#define REG_IRQSRC 0x60
+#define REG_FIQSRC 0x70
+#define REG_MBOX0_WR 0x80
+#define REG_MBOX0_RDCLR 0xc0
+#define REG_LIMIT 0x100
+
+#define IRQ_BIT(cntrl, num) (((cntrl) & (1 << (num))) != 0)
+#define FIQ_BIT(cntrl, num) (((cntrl) & (1 << ((num) + 4))) != 0)
+
+#define IRQ_CNTPSIRQ 0
+#define IRQ_CNTPNSIRQ 1
+#define IRQ_CNTHPIRQ 2
+#define IRQ_CNTVIRQ 3
+#define IRQ_MAILBOX0 4
+#define IRQ_MAILBOX1 5
+#define IRQ_MAILBOX2 6
+#define IRQ_MAILBOX3 7
+#define IRQ_GPU 8
+#define IRQ_PMU 9
+#define IRQ_AXI 10
+#define IRQ_TIMER 11
+#define IRQ_MAX IRQ_TIMER
+
+static void deliver_local(BCM2836ControlState *s, uint8_t core, uint8_t irq,
+ uint32_t controlreg, uint8_t controlidx)
+{
+ if (FIQ_BIT(controlreg, controlidx)) {
+ /* deliver a FIQ */
+ s->fiqsrc[core] |= (uint32_t)1 << irq;
+ } else if (IRQ_BIT(controlreg, controlidx)) {
+ /* deliver an IRQ */
+ s->irqsrc[core] |= (uint32_t)1 << irq;
+ } else {
+ /* the interrupt is masked */
+ }
+}
+
+/* Update interrupts. */
+static void bcm2836_control_update(BCM2836ControlState *s)
+{
+ int i, j;
+
+ /* reset pending IRQs/FIQs */
+ for (i = 0; i < BCM2836_NCORES; i++) {
+ s->irqsrc[i] = s->fiqsrc[i] = 0;
+ }
+
+ /* apply routing logic, update status regs */
+ if (s->gpu_irq) {
+ assert(s->route_gpu_irq < BCM2836_NCORES);
+ s->irqsrc[s->route_gpu_irq] |= (uint32_t)1 << IRQ_GPU;
+ }
+
+ if (s->gpu_fiq) {
+ assert(s->route_gpu_fiq < BCM2836_NCORES);
+ s->fiqsrc[s->route_gpu_fiq] |= (uint32_t)1 << IRQ_GPU;
+ }
+
+ for (i = 0; i < BCM2836_NCORES; i++) {
+ /* handle local timer interrupts for this core */
+ if (s->timerirqs[i]) {
+ assert(s->timerirqs[i] < (1 << (IRQ_CNTVIRQ + 1))); /* sane mask? */
+ for (j = 0; j <= IRQ_CNTVIRQ; j++) {
+ if ((s->timerirqs[i] & (1 << j)) != 0) {
+ /* local interrupt j is set */
+ deliver_local(s, i, j, s->timercontrol[i], j);
+ }
+ }
+ }
+
+ /* handle mailboxes for this core */
+ for (j = 0; j < BCM2836_MBPERCORE; j++) {
+ if (s->mailboxes[i * BCM2836_MBPERCORE + j] != 0) {
+ /* mailbox j is set */
+ deliver_local(s, i, j + IRQ_MAILBOX0, s->mailboxcontrol[i], j);
+ }
+ }
+ }
+
+ /* call set_irq appropriately for each output */
+ for (i = 0; i < BCM2836_NCORES; i++) {
+ qemu_set_irq(s->irq[i], s->irqsrc[i] != 0);
+ qemu_set_irq(s->fiq[i], s->fiqsrc[i] != 0);
+ }
+}
+
+static void bcm2836_control_set_local_irq(void *opaque, int core, int local_irq,
+ int level)
+{
+ BCM2836ControlState *s = opaque;
+
+ assert(core >= 0 && core < BCM2836_NCORES);
+ assert(local_irq >= 0 && local_irq <= IRQ_CNTVIRQ);
+
+ s->timerirqs[core] = deposit32(s->timerirqs[core], local_irq, 1, !!level);
+
+ bcm2836_control_update(s);
+}
+
+/* XXX: the following wrapper functions are a kludgy workaround,
+ * needed because I can't seem to pass useful information in the "irq"
+ * parameter when using named interrupts. Feel free to clean this up!
+ */
+
+static void bcm2836_control_set_local_irq0(void *opaque, int core, int level)
+{
+ bcm2836_control_set_local_irq(opaque, core, 0, level);
+}
+
+static void bcm2836_control_set_local_irq1(void *opaque, int core, int level)
+{
+ bcm2836_control_set_local_irq(opaque, core, 1, level);
+}
+
+static void bcm2836_control_set_local_irq2(void *opaque, int core, int level)
+{
+ bcm2836_control_set_local_irq(opaque, core, 2, level);
+}
+
+static void bcm2836_control_set_local_irq3(void *opaque, int core, int level)
+{
+ bcm2836_control_set_local_irq(opaque, core, 3, level);
+}
+
+static void bcm2836_control_set_gpu_irq(void *opaque, int irq, int level)
+{
+ BCM2836ControlState *s = opaque;
+
+ s->gpu_irq = level;
+
+ bcm2836_control_update(s);
+}
+
+static void bcm2836_control_set_gpu_fiq(void *opaque, int irq, int level)
+{
+ BCM2836ControlState *s = opaque;
+
+ s->gpu_fiq = level;
+
+ bcm2836_control_update(s);
+}
+
+static uint64_t bcm2836_control_read(void *opaque, hwaddr offset, unsigned size)
+{
+ BCM2836ControlState *s = opaque;
+
+ if (offset == REG_GPU_ROUTE) {
+ assert(s->route_gpu_fiq < BCM2836_NCORES
+ && s->route_gpu_irq < BCM2836_NCORES);
+ return ((uint32_t)s->route_gpu_fiq << 2) | s->route_gpu_irq;
+ } else if (offset >= REG_TIMERCONTROL && offset < REG_MBOXCONTROL) {
+ return s->timercontrol[(offset - REG_TIMERCONTROL) >> 2];
+ } else if (offset >= REG_MBOXCONTROL && offset < REG_IRQSRC) {
+ return s->mailboxcontrol[(offset - REG_MBOXCONTROL) >> 2];
+ } else if (offset >= REG_IRQSRC && offset < REG_FIQSRC) {
+ return s->irqsrc[(offset - REG_IRQSRC) >> 2];
+ } else if (offset >= REG_FIQSRC && offset < REG_MBOX0_WR) {
+ return s->fiqsrc[(offset - REG_FIQSRC) >> 2];
+ } else if (offset >= REG_MBOX0_RDCLR && offset < REG_LIMIT) {
+ return s->mailboxes[(offset - REG_MBOX0_RDCLR) >> 2];
+ } else {
+ qemu_log_mask(LOG_GUEST_ERROR, "%s: Bad offset %"HWADDR_PRIx"\n",
+ __func__, offset);
+ return 0;
+ }
+}
+
+static void bcm2836_control_write(void *opaque, hwaddr offset,
+ uint64_t val, unsigned size)
+{
+ BCM2836ControlState *s = opaque;
+
+ if (offset == REG_GPU_ROUTE) {
+ s->route_gpu_irq = val & 0x3;
+ s->route_gpu_fiq = (val >> 2) & 0x3;
+ } else if (offset >= REG_TIMERCONTROL && offset < REG_MBOXCONTROL) {
+ s->timercontrol[(offset - REG_TIMERCONTROL) >> 2] = val & 0xff;
+ } else if (offset >= REG_MBOXCONTROL && offset < REG_IRQSRC) {
+ s->mailboxcontrol[(offset - REG_MBOXCONTROL) >> 2] = val & 0xff;
+ } else if (offset >= REG_MBOX0_WR && offset < REG_MBOX0_RDCLR) {
+ s->mailboxes[(offset - REG_MBOX0_WR) >> 2] |= val;
+ } else if (offset >= REG_MBOX0_RDCLR && offset < REG_LIMIT) {
+ s->mailboxes[(offset - REG_MBOX0_RDCLR) >> 2] &= ~val;
+ } else {
+ qemu_log_mask(LOG_GUEST_ERROR, "%s: Bad offset %"HWADDR_PRIx"\n",
+ __func__, offset);
+ return;
+ }
+
+ bcm2836_control_update(s);
+}
+
+static const MemoryRegionOps bcm2836_control_ops = {
+ .read = bcm2836_control_read,
+ .write = bcm2836_control_write,
+ .endianness = DEVICE_NATIVE_ENDIAN,
+ .valid.min_access_size = 4,
+ .valid.max_access_size = 4,
+};
+
+static void bcm2836_control_reset(DeviceState *d)
+{
+ BCM2836ControlState *s = BCM2836_CONTROL(d);
+ int i;
+
+ s->route_gpu_irq = s->route_gpu_fiq = 0;
+
+ for (i = 0; i < BCM2836_NCORES; i++) {
+ s->timercontrol[i] = 0;
+ s->mailboxcontrol[i] = 0;
+ }
+
+ for (i = 0; i < BCM2836_NCORES * BCM2836_MBPERCORE; i++) {
+ s->mailboxes[i] = 0;
+ }
+}
+
+static void bcm2836_control_init(Object *obj)
+{
+ BCM2836ControlState *s = BCM2836_CONTROL(obj);
+ DeviceState *dev = DEVICE(obj);
+
+ memory_region_init_io(&s->iomem, obj, &bcm2836_control_ops, s,
+ TYPE_BCM2836_CONTROL, REG_LIMIT);
+ sysbus_init_mmio(SYS_BUS_DEVICE(s), &s->iomem);
+
+ /* inputs from each CPU core */
+ qdev_init_gpio_in_named(dev, bcm2836_control_set_local_irq0, "cntpsirq",
+ BCM2836_NCORES);
+ qdev_init_gpio_in_named(dev, bcm2836_control_set_local_irq1, "cntpnsirq",
+ BCM2836_NCORES);
+ qdev_init_gpio_in_named(dev, bcm2836_control_set_local_irq2, "cnthpirq",
+ BCM2836_NCORES);
+ qdev_init_gpio_in_named(dev, bcm2836_control_set_local_irq3, "cntvirq",
+ BCM2836_NCORES);
+
+ /* IRQ and FIQ inputs from upstream bcm2835 controller */
+ qdev_init_gpio_in_named(dev, bcm2836_control_set_gpu_irq, "gpu-irq", 1);
+ qdev_init_gpio_in_named(dev, bcm2836_control_set_gpu_fiq, "gpu-fiq", 1);
+
+ /* outputs to CPU cores */
+ qdev_init_gpio_out_named(dev, s->irq, "irq", BCM2836_NCORES);
+ qdev_init_gpio_out_named(dev, s->fiq, "fiq", BCM2836_NCORES);
+}
+
+static const VMStateDescription vmstate_bcm2836_control = {
+ .name = TYPE_BCM2836_CONTROL,
+ .version_id = 1,
+ .minimum_version_id = 1,
+ .fields = (VMStateField[]) {
+ VMSTATE_UINT32_ARRAY(mailboxes, BCM2836ControlState,
+ BCM2836_NCORES * BCM2836_MBPERCORE),
+ VMSTATE_UINT8(route_gpu_irq, BCM2836ControlState),
+ VMSTATE_UINT8(route_gpu_fiq, BCM2836ControlState),
+ VMSTATE_UINT32_ARRAY(timercontrol, BCM2836ControlState, BCM2836_NCORES),
+ VMSTATE_UINT32_ARRAY(mailboxcontrol, BCM2836ControlState,
+ BCM2836_NCORES),
+ VMSTATE_END_OF_LIST()
+ }
+};
+
+static void bcm2836_control_class_init(ObjectClass *klass, void *data)
+{
+ DeviceClass *dc = DEVICE_CLASS(klass);
+
+ dc->reset = bcm2836_control_reset;
+ dc->vmsd = &vmstate_bcm2836_control;
+}
+
+static TypeInfo bcm2836_control_info = {
+ .name = TYPE_BCM2836_CONTROL,
+ .parent = TYPE_SYS_BUS_DEVICE,
+ .instance_size = sizeof(BCM2836ControlState),
+ .class_init = bcm2836_control_class_init,
+ .instance_init = bcm2836_control_init,
+};
+
+static void bcm2836_control_register_types(void)
+{
+ type_register_static(&bcm2836_control_info);
+}
+
+type_init(bcm2836_control_register_types)
diff --git a/qemu/hw/intc/etraxfs_pic.c b/qemu/hw/intc/etraxfs_pic.c
index bd588681e..48f947706 100644
--- a/qemu/hw/intc/etraxfs_pic.c
+++ b/qemu/hw/intc/etraxfs_pic.c
@@ -22,6 +22,7 @@
* THE SOFTWARE.
*/
+#include "qemu/osdep.h"
#include "hw/sysbus.h"
#include "hw/hw.h"
//#include "pc.h"
diff --git a/qemu/hw/intc/exynos4210_combiner.c b/qemu/hw/intc/exynos4210_combiner.c
index a6b702890..dc0c90326 100644
--- a/qemu/hw/intc/exynos4210_combiner.c
+++ b/qemu/hw/intc/exynos4210_combiner.c
@@ -27,6 +27,7 @@
* IRQs are passed to GIC through Combiner.
*/
+#include "qemu/osdep.h"
#include "hw/sysbus.h"
#include "hw/arm/exynos4210.h"
diff --git a/qemu/hw/intc/exynos4210_gic.c b/qemu/hw/intc/exynos4210_gic.c
index b2a4950bc..4f7e89f7b 100644
--- a/qemu/hw/intc/exynos4210_gic.c
+++ b/qemu/hw/intc/exynos4210_gic.c
@@ -20,6 +20,7 @@
* with this program; if not, see <http://www.gnu.org/licenses/>.
*/
+#include "qemu/osdep.h"
#include "hw/sysbus.h"
#include "qemu-common.h"
#include "hw/irq.h"
diff --git a/qemu/hw/intc/grlib_irqmp.c b/qemu/hw/intc/grlib_irqmp.c
index d1813f76b..f5ca8f752 100644
--- a/qemu/hw/intc/grlib_irqmp.c
+++ b/qemu/hw/intc/grlib_irqmp.c
@@ -24,6 +24,7 @@
* THE SOFTWARE.
*/
+#include "qemu/osdep.h"
#include "hw/sysbus.h"
#include "cpu.h"
diff --git a/qemu/hw/intc/heathrow_pic.c b/qemu/hw/intc/heathrow_pic.c
index 9ff3119ed..171f5ed81 100644
--- a/qemu/hw/intc/heathrow_pic.c
+++ b/qemu/hw/intc/heathrow_pic.c
@@ -22,6 +22,7 @@
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
+#include "qemu/osdep.h"
#include "hw/hw.h"
#include "hw/ppc/mac.h"
diff --git a/qemu/hw/intc/i8259.c b/qemu/hw/intc/i8259.c
index 0f5c0259a..bb43669b9 100644
--- a/qemu/hw/intc/i8259.c
+++ b/qemu/hw/intc/i8259.c
@@ -21,6 +21,7 @@
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
+#include "qemu/osdep.h"
#include "hw/hw.h"
#include "hw/i386/pc.h"
#include "hw/isa/isa.h"
@@ -229,7 +230,7 @@ int pic_read_irq(DeviceState *d)
printf("IRQ%d latency=%0.3fus\n",
irq,
(double)(qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) -
- irq_time[irq]) * 1000000.0 / get_ticks_per_sec());
+ irq_time[irq]) * 1000000.0 / NANOSECONDS_PER_SECOND);
#endif
DPRINTF("pic_interrupt: irq=%d\n", irq);
return intno;
diff --git a/qemu/hw/intc/i8259_common.c b/qemu/hw/intc/i8259_common.c
index fbf26e557..3a850b0c6 100644
--- a/qemu/hw/intc/i8259_common.c
+++ b/qemu/hw/intc/i8259_common.c
@@ -22,6 +22,7 @@
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
+#include "qemu/osdep.h"
#include "hw/i386/pc.h"
#include "hw/isa/i8259_internal.h"
diff --git a/qemu/hw/intc/imx_avic.c b/qemu/hw/intc/imx_avic.c
index e48f66c8f..702765577 100644
--- a/qemu/hw/intc/imx_avic.c
+++ b/qemu/hw/intc/imx_avic.c
@@ -7,6 +7,7 @@
* Copyright (c) 2008 OKL
* Copyright (c) 2011 NICTA Pty Ltd
* Originally written by Hans Jiang
+ * Updated by Jean-Christophe Dubois <jcd@tribudubois.net>
*
* This code is licensed under the GPL version 2 or later. See
* the COPYING file in the top-level directory.
@@ -14,67 +15,23 @@
* TODO: implement vectors.
*/
-#include "hw/hw.h"
-#include "hw/sysbus.h"
-#include "qemu/host-utils.h"
+#include "qemu/osdep.h"
+#include "hw/intc/imx_avic.h"
-#define DEBUG_INT 1
-#undef DEBUG_INT /* comment out for debugging */
-
-#ifdef DEBUG_INT
-#define DPRINTF(fmt, args...) \
-do { printf("imx_avic: " fmt , ##args); } while (0)
-#else
-#define DPRINTF(fmt, args...) do {} while (0)
+#ifndef DEBUG_IMX_AVIC
+#define DEBUG_IMX_AVIC 0
#endif
-/*
- * Define to 1 for messages about attempts to
- * access unimplemented registers or similar.
- */
-#define DEBUG_IMPLEMENTATION 1
-#if DEBUG_IMPLEMENTATION
-# define IPRINTF(fmt, args...) \
- do { fprintf(stderr, "imx_avic: " fmt, ##args); } while (0)
-#else
-# define IPRINTF(fmt, args...) do {} while (0)
-#endif
-
-#define IMX_AVIC_NUM_IRQS 64
-
-/* Interrupt Control Bits */
-#define ABFLAG (1<<25)
-#define ABFEN (1<<24)
-#define NIDIS (1<<22) /* Normal Interrupt disable */
-#define FIDIS (1<<21) /* Fast interrupt disable */
-#define NIAD (1<<20) /* Normal Interrupt Arbiter Rise ARM level */
-#define FIAD (1<<19) /* Fast Interrupt Arbiter Rise ARM level */
-#define NM (1<<18) /* Normal interrupt mode */
-
-
-#define PRIO_PER_WORD (sizeof(uint32_t) * 8 / 4)
-#define PRIO_WORDS (IMX_AVIC_NUM_IRQS/PRIO_PER_WORD)
-
-#define TYPE_IMX_AVIC "imx_avic"
-#define IMX_AVIC(obj) \
- OBJECT_CHECK(IMXAVICState, (obj), TYPE_IMX_AVIC)
-
-typedef struct IMXAVICState {
- SysBusDevice parent_obj;
-
- MemoryRegion iomem;
- uint64_t pending;
- uint64_t enabled;
- uint64_t is_fiq;
- uint32_t intcntl;
- uint32_t intmask;
- qemu_irq irq;
- qemu_irq fiq;
- uint32_t prio[PRIO_WORDS]; /* Priorities are 4-bits each */
-} IMXAVICState;
+#define DPRINTF(fmt, args...) \
+ do { \
+ if (DEBUG_IMX_AVIC) { \
+ fprintf(stderr, "[%s]%s: " fmt , TYPE_IMX_AVIC, \
+ __func__, ##args); \
+ } \
+ } while (0)
static const VMStateDescription vmstate_imx_avic = {
- .name = "imx-avic",
+ .name = TYPE_IMX_AVIC,
.version_id = 1,
.minimum_version_id = 1,
.fields = (VMStateField[]) {
@@ -88,8 +45,6 @@ static const VMStateDescription vmstate_imx_avic = {
},
};
-
-
static inline int imx_avic_prio(IMXAVICState *s, int irq)
{
uint32_t word = irq / PRIO_PER_WORD;
@@ -151,8 +106,8 @@ static uint64_t imx_avic_read(void *opaque,
{
IMXAVICState *s = (IMXAVICState *)opaque;
+ DPRINTF("read(offset = 0x%" HWADDR_PRIx ")\n", offset);
- DPRINTF("read(offset = 0x%x)\n", offset >> 2);
switch (offset >> 2) {
case 0: /* INTCNTL */
return s->intcntl;
@@ -249,7 +204,8 @@ static uint64_t imx_avic_read(void *opaque,
return 0x4;
default:
- IPRINTF("imx_avic_read: Bad offset 0x%x\n", (int)offset);
+ qemu_log_mask(LOG_GUEST_ERROR, "[%s]%s: Bad register at offset 0x%"
+ HWADDR_PRIx "\n", TYPE_IMX_AVIC, __func__, offset);
return 0;
}
}
@@ -261,13 +217,13 @@ static void imx_avic_write(void *opaque, hwaddr offset,
/* Vector Registers not yet supported */
if (offset >= 0x100 && offset <= 0x2fc) {
- IPRINTF("imx_avic_write to vector register %d ignored\n",
- (unsigned int)((offset - 0x100) >> 2));
+ qemu_log_mask(LOG_UNIMP, "[%s]%s: vector %d ignored\n",
+ TYPE_IMX_AVIC, __func__, (int)((offset - 0x100) >> 2));
return;
}
- DPRINTF("imx_avic_write(0x%x) = %x\n",
- (unsigned int)offset>>2, (unsigned int)val);
+ DPRINTF("(0x%" HWADDR_PRIx ") = 0x%x\n", offset, (unsigned int)val);
+
switch (offset >> 2) {
case 0: /* Interrupt Control Register, INTCNTL */
s->intcntl = val & (ABFEN | NIDIS | FIDIS | NIAD | FIAD | NM);
@@ -341,7 +297,8 @@ static void imx_avic_write(void *opaque, hwaddr offset,
return;
default:
- IPRINTF("imx_avic_write: Bad offset %x\n", (int)offset);
+ qemu_log_mask(LOG_GUEST_ERROR, "[%s]%s: Bad register at offset 0x%"
+ HWADDR_PRIx "\n", TYPE_IMX_AVIC, __func__, offset);
}
imx_avic_update(s);
}
@@ -370,7 +327,7 @@ static int imx_avic_init(SysBusDevice *sbd)
IMXAVICState *s = IMX_AVIC(dev);
memory_region_init_io(&s->iomem, OBJECT(s), &imx_avic_ops, s,
- "imx_avic", 0x1000);
+ TYPE_IMX_AVIC, 0x1000);
sysbus_init_mmio(sbd, &s->iomem);
qdev_init_gpio_in(dev, imx_avic_set_irq, IMX_AVIC_NUM_IRQS);
diff --git a/qemu/hw/intc/ioapic.c b/qemu/hw/intc/ioapic.c
index b52793238..378e663f6 100644
--- a/qemu/hw/intc/ioapic.c
+++ b/qemu/hw/intc/ioapic.c
@@ -20,10 +20,14 @@
* License along with this library; if not, see <http://www.gnu.org/licenses/>.
*/
+#include "qemu/osdep.h"
+#include "monitor/monitor.h"
#include "hw/hw.h"
#include "hw/i386/pc.h"
#include "hw/i386/ioapic.h"
#include "hw/i386/ioapic_internal.h"
+#include "include/hw/pci/msi.h"
+#include "sysemu/kvm.h"
//#define DEBUG_IOAPIC
@@ -34,6 +38,10 @@
#define DPRINTF(fmt, ...)
#endif
+#define APIC_DELIVERY_MODE_SHIFT 8
+#define APIC_POLARITY_SHIFT 14
+#define APIC_TRIG_MODE_SHIFT 15
+
static IOAPICCommonState *ioapics[MAX_IOAPICS];
/* global variable from ioapic_common.c */
@@ -53,6 +61,8 @@ static void ioapic_service(IOAPICCommonState *s)
for (i = 0; i < IOAPIC_NUM_PINS; i++) {
mask = 1 << i;
if (s->irr & mask) {
+ int coalesce = 0;
+
entry = s->ioredtbl[i];
if (!(entry & IOAPIC_LVT_MASKED)) {
trig_mode = ((entry >> IOAPIC_LVT_TRIGGER_MODE_SHIFT) & 1);
@@ -63,6 +73,7 @@ static void ioapic_service(IOAPICCommonState *s)
if (trig_mode == IOAPIC_TRIGGER_EDGE) {
s->irr &= ~mask;
} else {
+ coalesce = s->ioredtbl[i] & IOAPIC_LVT_REMOTE_IRR;
s->ioredtbl[i] |= IOAPIC_LVT_REMOTE_IRR;
}
if (delivery_mode == IOAPIC_DM_EXTINT) {
@@ -70,8 +81,23 @@ static void ioapic_service(IOAPICCommonState *s)
} else {
vector = entry & IOAPIC_VECTOR_MASK;
}
- apic_deliver_irq(dest, dest_mode, delivery_mode,
- vector, trig_mode);
+#ifdef CONFIG_KVM
+ if (kvm_irqchip_is_split()) {
+ if (trig_mode == IOAPIC_TRIGGER_EDGE) {
+ kvm_set_irq(kvm_state, i, 1);
+ kvm_set_irq(kvm_state, i, 0);
+ } else {
+ if (!coalesce) {
+ kvm_set_irq(kvm_state, i, 1);
+ }
+ }
+ continue;
+ }
+#else
+ (void)coalesce;
+#endif
+ apic_deliver_irq(dest, dest_mode, delivery_mode, vector,
+ trig_mode);
}
}
}
@@ -98,7 +124,9 @@ static void ioapic_set_irq(void *opaque, int vector, int level)
/* level triggered */
if (level) {
s->irr |= mask;
- ioapic_service(s);
+ if (!(entry & IOAPIC_LVT_REMOTE_IRR)) {
+ ioapic_service(s);
+ }
} else {
s->irr &= ~mask;
}
@@ -113,6 +141,44 @@ static void ioapic_set_irq(void *opaque, int vector, int level)
}
}
+static void ioapic_update_kvm_routes(IOAPICCommonState *s)
+{
+#ifdef CONFIG_KVM
+ int i;
+
+ if (kvm_irqchip_is_split()) {
+ for (i = 0; i < IOAPIC_NUM_PINS; i++) {
+ uint64_t entry = s->ioredtbl[i];
+ uint8_t trig_mode;
+ uint8_t delivery_mode;
+ uint8_t dest;
+ uint8_t dest_mode;
+ uint64_t pin_polarity;
+ MSIMessage msg;
+
+ trig_mode = ((entry >> IOAPIC_LVT_TRIGGER_MODE_SHIFT) & 1);
+ dest = entry >> IOAPIC_LVT_DEST_SHIFT;
+ dest_mode = (entry >> IOAPIC_LVT_DEST_MODE_SHIFT) & 1;
+ pin_polarity = (entry >> IOAPIC_LVT_TRIGGER_MODE_SHIFT) & 1;
+ delivery_mode =
+ (entry >> IOAPIC_LVT_DELIV_MODE_SHIFT) & IOAPIC_DM_MASK;
+
+ msg.address = APIC_DEFAULT_ADDRESS;
+ msg.address |= dest_mode << 2;
+ msg.address |= dest << 12;
+
+ msg.data = entry & IOAPIC_VECTOR_MASK;
+ msg.data |= delivery_mode << APIC_DELIVERY_MODE_SHIFT;
+ msg.data |= pin_polarity << APIC_POLARITY_SHIFT;
+ msg.data |= trig_mode << APIC_TRIG_MODE_SHIFT;
+
+ kvm_irqchip_update_msi_route(kvm_state, i, msg, NULL);
+ }
+ kvm_irqchip_commit_routes(kvm_state);
+ }
+#endif
+}
+
void ioapic_eoi_broadcast(int vector)
{
IOAPICCommonState *s;
@@ -137,6 +203,17 @@ void ioapic_eoi_broadcast(int vector)
}
}
+void ioapic_dump_state(Monitor *mon, const QDict *qdict)
+{
+ int i;
+
+ for (i = 0; i < MAX_IOAPICS; i++) {
+ if (ioapics[i] != 0) {
+ ioapic_print_redtbl(mon, ioapics[i]);
+ }
+ }
+}
+
static uint64_t
ioapic_mem_read(void *opaque, hwaddr addr, unsigned int size)
{
@@ -154,15 +231,13 @@ ioapic_mem_read(void *opaque, hwaddr addr, unsigned int size)
}
switch (s->ioregsel) {
case IOAPIC_REG_ID:
+ case IOAPIC_REG_ARB:
val = s->id << IOAPIC_ID_SHIFT;
break;
case IOAPIC_REG_VER:
val = IOAPIC_VERSION |
((IOAPIC_NUM_PINS - 1) << IOAPIC_VER_ENTRIES_SHIFT);
break;
- case IOAPIC_REG_ARB:
- val = 0;
- break;
default:
index = (s->ioregsel - IOAPIC_REG_REDTBL_BASE) >> 1;
if (index >= 0 && index < IOAPIC_NUM_PINS) {
@@ -217,6 +292,8 @@ ioapic_mem_write(void *opaque, hwaddr addr, uint64_t val,
}
break;
}
+
+ ioapic_update_kvm_routes(s);
}
static const MemoryRegionOps ioapic_io_ops = {
diff --git a/qemu/hw/intc/ioapic_common.c b/qemu/hw/intc/ioapic_common.c
index 8b7d11806..1b7ec5ec2 100644
--- a/qemu/hw/intc/ioapic_common.c
+++ b/qemu/hw/intc/ioapic_common.c
@@ -19,6 +19,9 @@
* License along with this library; if not, see <http://www.gnu.org/licenses/>.
*/
+#include "qemu/osdep.h"
+#include "qapi/error.h"
+#include "monitor/monitor.h"
#include "hw/i386/ioapic.h"
#include "hw/i386/ioapic_internal.h"
#include "hw/sysbus.h"
@@ -31,6 +34,60 @@
*/
int ioapic_no;
+static void ioapic_irr_dump(Monitor *mon, const char *name, uint32_t bitmap)
+{
+ int i;
+
+ monitor_printf(mon, "%-10s ", name);
+ if (bitmap == 0) {
+ monitor_printf(mon, "(none)\n");
+ return;
+ }
+ for (i = 0; i < IOAPIC_NUM_PINS; i++) {
+ if (bitmap & (1 << i)) {
+ monitor_printf(mon, "%-2u ", i);
+ }
+ }
+ monitor_printf(mon, "\n");
+}
+
+void ioapic_print_redtbl(Monitor *mon, IOAPICCommonState *s)
+{
+ static const char *delm_str[] = {
+ "fixed", "lowest", "SMI", "...", "NMI", "INIT", "...", "extINT"};
+ uint32_t remote_irr = 0;
+ int i;
+
+ monitor_printf(mon, "ioapic id=0x%02x sel=0x%02x", s->id, s->ioregsel);
+ if (s->ioregsel) {
+ monitor_printf(mon, " (redir[%u])\n",
+ (s->ioregsel - IOAPIC_REG_REDTBL_BASE) >> 1);
+ } else {
+ monitor_printf(mon, "\n");
+ }
+ for (i = 0; i < IOAPIC_NUM_PINS; i++) {
+ uint64_t entry = s->ioredtbl[i];
+ uint32_t delm = (uint32_t)((entry & IOAPIC_LVT_DELIV_MODE) >>
+ IOAPIC_LVT_DELIV_MODE_SHIFT);
+ monitor_printf(mon, "pin %-2u 0x%016"PRIx64" dest=%"PRIx64
+ " vec=%-3"PRIu64" %s %-5s %-6s %-6s %s\n",
+ i, entry,
+ (entry >> IOAPIC_LVT_DEST_SHIFT) &
+ (entry & IOAPIC_LVT_DEST_MODE ? 0xff : 0xf),
+ entry & IOAPIC_VECTOR_MASK,
+ entry & IOAPIC_LVT_POLARITY ? "active-lo" : "active-hi",
+ entry & IOAPIC_LVT_TRIGGER_MODE ? "level" : "edge",
+ entry & IOAPIC_LVT_MASKED ? "masked" : "",
+ delm_str[delm],
+ entry & IOAPIC_LVT_DEST_MODE ? "logical" : "physical");
+
+ remote_irr |= entry & IOAPIC_LVT_TRIGGER_MODE ?
+ (entry & IOAPIC_LVT_REMOTE_IRR ? (1 << i) : 0) : 0;
+ }
+ ioapic_irr_dump(mon, "IRR", s->irr);
+ ioapic_irr_dump(mon, "Remote IRR", remote_irr);
+}
+
void ioapic_reset_common(DeviceState *dev)
{
IOAPICCommonState *s = IOAPIC_COMMON(dev);
diff --git a/qemu/hw/intc/lm32_pic.c b/qemu/hw/intc/lm32_pic.c
index 641ee4727..edc08f184 100644
--- a/qemu/hw/intc/lm32_pic.c
+++ b/qemu/hw/intc/lm32_pic.c
@@ -17,7 +17,7 @@
* License along with this library; if not, see <http://www.gnu.org/licenses/>.
*/
-#include <assert.h>
+#include "qemu/osdep.h"
#include "hw/hw.h"
#include "hw/i386/pc.h"
diff --git a/qemu/hw/intc/omap_intc.c b/qemu/hw/intc/omap_intc.c
index e9b38a3c6..336882510 100644
--- a/qemu/hw/intc/omap_intc.c
+++ b/qemu/hw/intc/omap_intc.c
@@ -17,9 +17,11 @@
* You should have received a copy of the GNU General Public License along
* with this program; if not, see <http://www.gnu.org/licenses/>.
*/
+#include "qemu/osdep.h"
#include "hw/hw.h"
#include "hw/arm/omap.h"
#include "hw/sysbus.h"
+#include "qemu/error-report.h"
/* Interrupt Handlers */
struct omap_intr_handler_bank_s {
@@ -367,7 +369,8 @@ static int omap_intc_init(SysBusDevice *sbd)
struct omap_intr_handler_s *s = OMAP_INTC(dev);
if (!s->iclk) {
- hw_error("omap-intc: clk not connected\n");
+ error_report("omap-intc: clk not connected");
+ return -1;
}
s->nbanks = 1;
sysbus_init_irq(sbd, &s->parent_intr[0]);
@@ -608,10 +611,12 @@ static int omap2_intc_init(SysBusDevice *sbd)
struct omap_intr_handler_s *s = OMAP_INTC(dev);
if (!s->iclk) {
- hw_error("omap2-intc: iclk not connected\n");
+ error_report("omap2-intc: iclk not connected");
+ return -1;
}
if (!s->fclk) {
- hw_error("omap2-intc: fclk not connected\n");
+ error_report("omap2-intc: fclk not connected");
+ return -1;
}
s->level_only = 1;
s->nbanks = 3;
diff --git a/qemu/hw/intc/openpic.c b/qemu/hw/intc/openpic.c
index 14ab0e31b..2d3769310 100644
--- a/qemu/hw/intc/openpic.c
+++ b/qemu/hw/intc/openpic.c
@@ -33,6 +33,7 @@
* Serial interrupts, as implemented in Raven chipset are not supported yet.
*
*/
+#include "qemu/osdep.h"
#include "hw/hw.h"
#include "hw/ppc/mac.h"
#include "hw/pci/pci.h"
@@ -40,6 +41,7 @@
#include "hw/ppc/ppc_e500.h"
#include "hw/sysbus.h"
#include "hw/pci/msi.h"
+#include "qapi/error.h"
#include "qemu/bitops.h"
#include "qapi/qmp/qerror.h"
@@ -1374,7 +1376,7 @@ static void fsl_common_init(OpenPICState *opp)
opp->irq_msi = 224;
- msi_supported = true;
+ msi_nonbroken = true;
for (i = 0; i < opp->fsl->max_ext; i++) {
opp->src[i].level = false;
}
@@ -1643,6 +1645,7 @@ static void openpic_class_init(ObjectClass *oc, void *data)
dc->props = openpic_properties;
dc->reset = openpic_reset;
dc->vmsd = &vmstate_openpic;
+ set_bit(DEVICE_CATEGORY_MISC, dc->categories);
}
static const TypeInfo openpic_info = {
diff --git a/qemu/hw/intc/openpic_kvm.c b/qemu/hw/intc/openpic_kvm.c
index f7cac585a..e47e94f2c 100644
--- a/qemu/hw/intc/openpic_kvm.c
+++ b/qemu/hw/intc/openpic_kvm.c
@@ -22,6 +22,8 @@
* THE SOFTWARE.
*/
+#include "qemu/osdep.h"
+#include "qapi/error.h"
#include <sys/ioctl.h>
#include "exec/address-spaces.h"
#include "hw/hw.h"
@@ -238,7 +240,7 @@ static void kvm_openpic_realize(DeviceState *dev, Error **errp)
memory_listener_register(&opp->mem_listener, &address_space_memory);
/* indicate pic capabilities */
- msi_supported = true;
+ msi_nonbroken = true;
kvm_kernel_irqchip = true;
kvm_async_interrupts_allowed = true;
@@ -275,6 +277,7 @@ static void kvm_openpic_class_init(ObjectClass *oc, void *data)
dc->realize = kvm_openpic_realize;
dc->props = kvm_openpic_properties;
dc->reset = kvm_openpic_reset;
+ set_bit(DEVICE_CATEGORY_MISC, dc->categories);
}
static const TypeInfo kvm_openpic_info = {
diff --git a/qemu/hw/intc/pl190.c b/qemu/hw/intc/pl190.c
index 2bf359a76..5ecbc4a48 100644
--- a/qemu/hw/intc/pl190.c
+++ b/qemu/hw/intc/pl190.c
@@ -7,6 +7,7 @@
* This code is licensed under the GPL.
*/
+#include "qemu/osdep.h"
#include "hw/sysbus.h"
/* The number of virtual priority levels. 16 user vectors plus the
diff --git a/qemu/hw/intc/puv3_intc.c b/qemu/hw/intc/puv3_intc.c
index c2803d07d..ef8488aac 100644
--- a/qemu/hw/intc/puv3_intc.c
+++ b/qemu/hw/intc/puv3_intc.c
@@ -8,6 +8,7 @@
* published by the Free Software Foundation, or any later version.
* See the COPYING file in the top-level directory.
*/
+#include "qemu/osdep.h"
#include "hw/sysbus.h"
#undef DEBUG_PUV3
diff --git a/qemu/hw/intc/realview_gic.c b/qemu/hw/intc/realview_gic.c
index 6c812961c..50bbab66e 100644
--- a/qemu/hw/intc/realview_gic.c
+++ b/qemu/hw/intc/realview_gic.c
@@ -7,6 +7,8 @@
* This code is licensed under the GPL.
*/
+#include "qemu/osdep.h"
+#include "qapi/error.h"
#include "hw/intc/realview_gic.h"
static void realview_gic_set_irq(void *opaque, int irq, int level)
diff --git a/qemu/hw/intc/s390_flic.c b/qemu/hw/intc/s390_flic.c
index 02e10b750..bc75fa7d9 100644
--- a/qemu/hw/intc/s390_flic.c
+++ b/qemu/hw/intc/s390_flic.c
@@ -10,6 +10,7 @@
* directory.
*/
+#include "qemu/osdep.h"
#include "qemu/error-report.h"
#include "hw/sysbus.h"
#include "migration/qemu-file.h"
diff --git a/qemu/hw/intc/s390_flic_kvm.c b/qemu/hw/intc/s390_flic_kvm.c
index b471e7a41..02449b390 100644
--- a/qemu/hw/intc/s390_flic_kvm.c
+++ b/qemu/hw/intc/s390_flic_kvm.c
@@ -10,6 +10,7 @@
* directory.
*/
+#include "qemu/osdep.h"
#include <sys/ioctl.h>
#include "qemu/error-report.h"
#include "hw/sysbus.h"
@@ -228,6 +229,8 @@ static int kvm_s390_add_adapter_routes(S390FLICState *fs,
routes->gsi[i] = ret;
routes->adapter.ind_offset++;
}
+ kvm_irqchip_commit_routes(kvm_state);
+
/* Restore passed-in structure to original state. */
routes->adapter.ind_offset = ind_offset;
return 0;
diff --git a/qemu/hw/intc/sh_intc.c b/qemu/hw/intc/sh_intc.c
index 55c76e4af..6ce2a8084 100644
--- a/qemu/hw/intc/sh_intc.c
+++ b/qemu/hw/intc/sh_intc.c
@@ -8,6 +8,9 @@
* This code is licensed under the GPL.
*/
+#include "qemu/osdep.h"
+#include "qemu-common.h"
+#include "cpu.h"
#include "hw/sh4/sh_intc.h"
#include "hw/hw.h"
#include "hw/sh4/sh.h"
diff --git a/qemu/hw/intc/slavio_intctl.c b/qemu/hw/intc/slavio_intctl.c
index f22aba031..c9486ed99 100644
--- a/qemu/hw/intc/slavio_intctl.c
+++ b/qemu/hw/intc/slavio_intctl.c
@@ -22,6 +22,7 @@
* THE SOFTWARE.
*/
+#include "qemu/osdep.h"
#include "hw/sparc/sun4m.h"
#include "monitor/monitor.h"
#include "hw/sysbus.h"
diff --git a/qemu/hw/intc/vgic_common.h b/qemu/hw/intc/vgic_common.h
new file mode 100644
index 000000000..80d919eb9
--- /dev/null
+++ b/qemu/hw/intc/vgic_common.h
@@ -0,0 +1,35 @@
+/*
+ * ARM KVM vGIC utility functions
+ *
+ * Copyright (c) 2015 Samsung Electronics
+ * Written by Pavel Fedin
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef QEMU_ARM_VGIC_COMMON_H
+#define QEMU_ARM_VGIC_COMMON_H
+
+/**
+ * kvm_arm_gic_set_irq - Send an IRQ to the in-kernel vGIC
+ * @num_irq: Total number of IRQs configured for the GIC instance
+ * @irq: qemu internal IRQ line number:
+ * [0..N-1] : external interrupts
+ * [N..N+31] : PPI (internal) interrupts for CPU 0
+ * [N+32..N+63] : PPI (internal interrupts for CPU 1
+ * @level: level of the IRQ line.
+ */
+void kvm_arm_gic_set_irq(uint32_t num_irq, int irq, int level);
+
+#endif
diff --git a/qemu/hw/intc/xics.c b/qemu/hw/intc/xics.c
index 924b1ae3c..8659be017 100644
--- a/qemu/hw/intc/xics.c
+++ b/qemu/hw/intc/xics.c
@@ -25,6 +25,10 @@
*
*/
+#include "qemu/osdep.h"
+#include "qapi/error.h"
+#include "qemu-common.h"
+#include "cpu.h"
#include "hw/hw.h"
#include "trace.h"
#include "qemu/timer.h"
@@ -88,24 +92,24 @@ static void xics_common_reset(DeviceState *d)
device_reset(DEVICE(icp->ics));
}
-static void xics_prop_get_nr_irqs(Object *obj, Visitor *v,
- void *opaque, const char *name, Error **errp)
+static void xics_prop_get_nr_irqs(Object *obj, Visitor *v, const char *name,
+ void *opaque, Error **errp)
{
XICSState *icp = XICS_COMMON(obj);
int64_t value = icp->nr_irqs;
- visit_type_int(v, &value, name, errp);
+ visit_type_int(v, name, &value, errp);
}
-static void xics_prop_set_nr_irqs(Object *obj, Visitor *v,
- void *opaque, const char *name, Error **errp)
+static void xics_prop_set_nr_irqs(Object *obj, Visitor *v, const char *name,
+ void *opaque, Error **errp)
{
XICSState *icp = XICS_COMMON(obj);
XICSStateClass *info = XICS_COMMON_GET_CLASS(icp);
Error *error = NULL;
int64_t value;
- visit_type_int(v, &value, name, &error);
+ visit_type_int(v, name, &value, &error);
if (error) {
error_propagate(errp, error);
return;
@@ -122,17 +126,17 @@ static void xics_prop_set_nr_irqs(Object *obj, Visitor *v,
}
static void xics_prop_get_nr_servers(Object *obj, Visitor *v,
- void *opaque, const char *name,
+ const char *name, void *opaque,
Error **errp)
{
XICSState *icp = XICS_COMMON(obj);
int64_t value = icp->nr_servers;
- visit_type_int(v, &value, name, errp);
+ visit_type_int(v, name, &value, errp);
}
static void xics_prop_set_nr_servers(Object *obj, Visitor *v,
- void *opaque, const char *name,
+ const char *name, void *opaque,
Error **errp)
{
XICSState *icp = XICS_COMMON(obj);
@@ -140,7 +144,7 @@ static void xics_prop_set_nr_servers(Object *obj, Visitor *v,
Error *error = NULL;
int64_t value;
- visit_type_int(v, &value, name, &error);
+ visit_type_int(v, name, &value, &error);
if (error) {
error_propagate(errp, error);
return;
@@ -711,7 +715,7 @@ static int ics_find_free_block(ICSState *ics, int num, int alignnum)
return -1;
}
-int xics_alloc(XICSState *icp, int src, int irq_hint, bool lsi)
+int xics_alloc(XICSState *icp, int src, int irq_hint, bool lsi, Error **errp)
{
ICSState *ics = &icp->ics[src];
int irq;
@@ -719,14 +723,14 @@ int xics_alloc(XICSState *icp, int src, int irq_hint, bool lsi)
if (irq_hint) {
assert(src == xics_find_source(icp, irq_hint));
if (!ICS_IRQ_FREE(ics, irq_hint - ics->offset)) {
- trace_xics_alloc_failed_hint(src, irq_hint);
+ error_setg(errp, "can't allocate IRQ %d: already in use", irq_hint);
return -1;
}
irq = irq_hint;
} else {
irq = ics_find_free_block(ics, 1, 1);
if (irq < 0) {
- trace_xics_alloc_failed_no_left(src);
+ error_setg(errp, "can't allocate IRQ: no IRQ left");
return -1;
}
irq += ics->offset;
@@ -739,10 +743,11 @@ int xics_alloc(XICSState *icp, int src, int irq_hint, bool lsi)
}
/*
- * Allocate block of consequtive IRQs, returns a number of the first.
+ * Allocate block of consecutive IRQs, and return the number of the first IRQ in the block.
* If align==true, aligns the first IRQ number to num.
*/
-int xics_alloc_block(XICSState *icp, int src, int num, bool lsi, bool align)
+int xics_alloc_block(XICSState *icp, int src, int num, bool lsi, bool align,
+ Error **errp)
{
int i, first = -1;
ICSState *ics = &icp->ics[src];
@@ -762,6 +767,10 @@ int xics_alloc_block(XICSState *icp, int src, int num, bool lsi, bool align)
} else {
first = ics_find_free_block(ics, num, 1);
}
+ if (first < 0) {
+ error_setg(errp, "can't find a free %d-IRQ block", num);
+ return -1;
+ }
if (first >= 0) {
for (i = first; i < first + num; ++i) {
@@ -848,7 +857,7 @@ static target_ulong h_xirr_x(PowerPCCPU *cpu, sPAPRMachineState *spapr,
uint32_t xirr = icp_accept(ss);
args[0] = xirr;
- args[1] = cpu_get_real_ticks();
+ args[1] = cpu_get_host_ticks();
return H_SUCCESS;
}
diff --git a/qemu/hw/intc/xics_kvm.c b/qemu/hw/intc/xics_kvm.c
index d58729cfa..9029d9ee0 100644
--- a/qemu/hw/intc/xics_kvm.c
+++ b/qemu/hw/intc/xics_kvm.c
@@ -25,6 +25,10 @@
*
*/
+#include "qemu/osdep.h"
+#include "qapi/error.h"
+#include "qemu-common.h"
+#include "cpu.h"
#include "hw/hw.h"
#include "trace.h"
#include "hw/ppc/spapr.h"
diff --git a/qemu/hw/intc/xilinx_intc.c b/qemu/hw/intc/xilinx_intc.c
index 12804ab72..9d8139bc6 100644
--- a/qemu/hw/intc/xilinx_intc.c
+++ b/qemu/hw/intc/xilinx_intc.c
@@ -22,6 +22,7 @@
* THE SOFTWARE.
*/
+#include "qemu/osdep.h"
#include "hw/sysbus.h"
#include "hw/hw.h"