aboutsummaryrefslogtreecommitdiff
path: root/hw
diff options
context:
space:
mode:
Diffstat (limited to 'hw')
-rw-r--r--hw/a15mpcore.c8
-rw-r--r--hw/arm/Makefile.objs1
-rw-r--r--hw/arm_gic.c23
-rw-r--r--hw/arm_gic_common.c36
-rw-r--r--hw/arm_gic_internal.h4
-rw-r--r--hw/arm_pic.c26
-rw-r--r--hw/armv7m_nvic.c15
-rw-r--r--hw/kvm/arm_gic.c167
8 files changed, 250 insertions, 30 deletions
diff --git a/hw/a15mpcore.c b/hw/a15mpcore.c
index fe6c34ca53..97abe413c8 100644
--- a/hw/a15mpcore.c
+++ b/hw/a15mpcore.c
@@ -19,6 +19,7 @@
*/
#include "sysbus.h"
+#include "sysemu/kvm.h"
/* A15MP private memory region. */
@@ -40,8 +41,13 @@ static int a15mp_priv_init(SysBusDevice *dev)
{
A15MPPrivState *s = FROM_SYSBUS(A15MPPrivState, dev);
SysBusDevice *busdev;
+ const char *gictype = "arm_gic";
- s->gic = qdev_create(NULL, "arm_gic");
+ if (kvm_irqchip_in_kernel()) {
+ gictype = "kvm-arm-gic";
+ }
+
+ s->gic = qdev_create(NULL, gictype);
qdev_prop_set_uint32(s->gic, "num-cpu", s->num_cpu);
qdev_prop_set_uint32(s->gic, "num-irq", s->num_irq);
qdev_prop_set_uint32(s->gic, "revision", 2);
diff --git a/hw/arm/Makefile.objs b/hw/arm/Makefile.objs
index 4c109858fd..0e7df6098a 100644
--- a/hw/arm/Makefile.objs
+++ b/hw/arm/Makefile.objs
@@ -32,5 +32,6 @@ obj-y += collie.o
obj-y += imx_serial.o imx_ccm.o imx_timer.o imx_avic.o
obj-y += kzm.o
obj-$(CONFIG_FDT) += ../device_tree.o
+obj-$(CONFIG_KVM) += kvm/arm_gic.o
obj-y := $(addprefix ../,$(obj-y))
diff --git a/hw/arm_gic.c b/hw/arm_gic.c
index 90e43d0728..250e720d18 100644
--- a/hw/arm_gic.c
+++ b/hw/arm_gic.c
@@ -659,14 +659,18 @@ void gic_init_irqs_and_distributor(GICState *s, int num_irq)
memory_region_init_io(&s->iomem, &gic_dist_ops, s, "gic_dist", 0x1000);
}
-static int arm_gic_init(SysBusDevice *dev)
+static void arm_gic_realize(DeviceState *dev, Error **errp)
{
- /* Device instance init function for the GIC sysbus device */
+ /* Device instance realize function for the GIC sysbus device */
int i;
- GICState *s = FROM_SYSBUS(GICState, dev);
+ GICState *s = ARM_GIC(dev);
+ SysBusDevice *sbd = SYS_BUS_DEVICE(dev);
ARMGICClass *agc = ARM_GIC_GET_CLASS(s);
- agc->parent_init(dev);
+ agc->parent_realize(dev, errp);
+ if (error_is_set(errp)) {
+ return;
+ }
gic_init_irqs_and_distributor(s, s->num_irq);
@@ -686,22 +690,21 @@ static int arm_gic_init(SysBusDevice *dev)
"gic_cpu", 0x100);
}
/* Distributor */
- sysbus_init_mmio(dev, &s->iomem);
+ 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(dev, &s->cpuiomem[i]);
+ sysbus_init_mmio(sbd, &s->cpuiomem[i]);
}
- return 0;
}
static void arm_gic_class_init(ObjectClass *klass, void *data)
{
DeviceClass *dc = DEVICE_CLASS(klass);
- SysBusDeviceClass *sbc = SYS_BUS_DEVICE_CLASS(klass);
ARMGICClass *agc = ARM_GIC_CLASS(klass);
- agc->parent_init = sbc->init;
- sbc->init = arm_gic_init;
+
dc->no_user = 1;
+ agc->parent_realize = dc->realize;
+ dc->realize = arm_gic_realize;
}
static const TypeInfo arm_gic_info = {
diff --git a/hw/arm_gic_common.c b/hw/arm_gic_common.c
index 40e8dd7045..20da9d2b18 100644
--- a/hw/arm_gic_common.c
+++ b/hw/arm_gic_common.c
@@ -23,9 +23,14 @@
static void gic_save(QEMUFile *f, void *opaque)
{
GICState *s = (GICState *)opaque;
+ ARMGICCommonClass *c = ARM_GIC_COMMON_GET_CLASS(s);
int i;
int j;
+ if (c->pre_save) {
+ c->pre_save(s);
+ }
+
qemu_put_be32(f, s->enabled);
for (i = 0; i < s->num_cpu; i++) {
qemu_put_be32(f, s->cpu_enabled[i]);
@@ -57,6 +62,7 @@ static void gic_save(QEMUFile *f, void *opaque)
static int gic_load(QEMUFile *f, void *opaque, int version_id)
{
GICState *s = (GICState *)opaque;
+ ARMGICCommonClass *c = ARM_GIC_COMMON_GET_CLASS(s);
int i;
int j;
@@ -91,34 +97,42 @@ static int gic_load(QEMUFile *f, void *opaque, int version_id)
s->irq_state[i].trigger = qemu_get_byte(f);
}
+ if (c->post_load) {
+ c->post_load(s);
+ }
+
return 0;
}
-static int arm_gic_common_init(SysBusDevice *dev)
+static void arm_gic_common_realize(DeviceState *dev, Error **errp)
{
- GICState *s = FROM_SYSBUS(GICState, dev);
+ GICState *s = ARM_GIC_COMMON(dev);
int num_irq = s->num_irq;
if (s->num_cpu > NCPU) {
- hw_error("requested %u CPUs exceeds GIC maximum %d\n",
- s->num_cpu, NCPU);
+ error_setg(errp, "requested %u CPUs exceeds GIC maximum %d",
+ s->num_cpu, NCPU);
+ return;
}
s->num_irq += GIC_BASE_IRQ;
if (s->num_irq > GIC_MAXIRQ) {
- hw_error("requested %u interrupt lines exceeds GIC maximum %d\n",
- num_irq, GIC_MAXIRQ);
+ error_setg(errp,
+ "requested %u interrupt lines exceeds GIC maximum %d",
+ num_irq, GIC_MAXIRQ);
+ return;
}
/* ITLinesNumber is represented as (N / 32) - 1 (see
* gic_dist_readb) so this is an implementation imposed
* restriction, not an architectural one:
*/
if (s->num_irq < 32 || (s->num_irq % 32)) {
- hw_error("%d interrupt lines unsupported: not divisible by 32\n",
- num_irq);
+ error_setg(errp,
+ "%d interrupt lines unsupported: not divisible by 32",
+ num_irq);
+ return;
}
register_savevm(NULL, "arm_gic", -1, 3, gic_save, gic_load, s);
- return 0;
}
static void arm_gic_common_reset(DeviceState *dev)
@@ -163,12 +177,12 @@ static Property arm_gic_common_properties[] = {
static void arm_gic_common_class_init(ObjectClass *klass, void *data)
{
- SysBusDeviceClass *sc = SYS_BUS_DEVICE_CLASS(klass);
DeviceClass *dc = DEVICE_CLASS(klass);
+
dc->reset = arm_gic_common_reset;
+ dc->realize = arm_gic_common_realize;
dc->props = arm_gic_common_properties;
dc->no_user = 1;
- sc->init = arm_gic_common_init;
}
static const TypeInfo arm_gic_common_type = {
diff --git a/hw/arm_gic_internal.h b/hw/arm_gic_internal.h
index 699352ca8b..3ba37f30f5 100644
--- a/hw/arm_gic_internal.h
+++ b/hw/arm_gic_internal.h
@@ -118,6 +118,8 @@ void gic_init_irqs_and_distributor(GICState *s, int num_irq);
typedef struct ARMGICCommonClass {
SysBusDeviceClass parent_class;
+ void (*pre_save)(GICState *s);
+ void (*post_load)(GICState *s);
} ARMGICCommonClass;
#define TYPE_ARM_GIC "arm_gic"
@@ -130,7 +132,7 @@ typedef struct ARMGICCommonClass {
typedef struct ARMGICClass {
ARMGICCommonClass parent_class;
- int (*parent_init)(SysBusDevice *dev);
+ DeviceRealize parent_realize;
} ARMGICClass;
#endif /* !QEMU_ARM_GIC_INTERNAL_H */
diff --git a/hw/arm_pic.c b/hw/arm_pic.c
index ffb4d4171a..394bc93cb9 100644
--- a/hw/arm_pic.c
+++ b/hw/arm_pic.c
@@ -9,6 +9,7 @@
#include "hw.h"
#include "arm-misc.h"
+#include "sysemu/kvm.h"
/* Input 0 is IRQ and input 1 is FIQ. */
static void arm_pic_cpu_handler(void *opaque, int irq, int level)
@@ -34,7 +35,32 @@ static void arm_pic_cpu_handler(void *opaque, int irq, int level)
}
}
+static void kvm_arm_pic_cpu_handler(void *opaque, int irq, int level)
+{
+#ifdef CONFIG_KVM
+ ARMCPU *cpu = opaque;
+ CPUState *cs = CPU(cpu);
+ int kvm_irq = KVM_ARM_IRQ_TYPE_CPU << KVM_ARM_IRQ_TYPE_SHIFT;
+
+ switch (irq) {
+ case ARM_PIC_CPU_IRQ:
+ kvm_irq |= KVM_ARM_IRQ_CPU_IRQ;
+ break;
+ case ARM_PIC_CPU_FIQ:
+ kvm_irq |= KVM_ARM_IRQ_CPU_FIQ;
+ break;
+ default:
+ hw_error("kvm_arm_pic_cpu_handler: Bad interrupt line %d\n", irq);
+ }
+ kvm_irq |= cs->cpu_index << KVM_ARM_IRQ_VCPU_SHIFT;
+ kvm_set_irq(kvm_state, kvm_irq, level ? 1 : 0);
+#endif
+}
+
qemu_irq *arm_pic_init_cpu(ARMCPU *cpu)
{
+ if (kvm_enabled()) {
+ return qemu_allocate_irqs(kvm_arm_pic_cpu_handler, cpu, 2);
+ }
return qemu_allocate_irqs(arm_pic_cpu_handler, cpu, 2);
}
diff --git a/hw/armv7m_nvic.c b/hw/armv7m_nvic.c
index d5798d0309..3c7967464a 100644
--- a/hw/armv7m_nvic.c
+++ b/hw/armv7m_nvic.c
@@ -41,7 +41,7 @@ typedef struct NVICClass {
/*< private >*/
ARMGICClass parent_class;
/*< public >*/
- int (*parent_init)(SysBusDevice *dev);
+ DeviceRealize parent_realize;
void (*parent_reset)(DeviceState *dev);
} NVICClass;
@@ -465,7 +465,7 @@ static void armv7m_nvic_reset(DeviceState *dev)
systick_reset(s);
}
-static int armv7m_nvic_init(SysBusDevice *dev)
+static void armv7m_nvic_realize(DeviceState *dev, Error **errp)
{
nvic_state *s = NVIC(dev);
NVICClass *nc = NVIC_GET_CLASS(s);
@@ -475,7 +475,10 @@ static int armv7m_nvic_init(SysBusDevice *dev)
/* Tell the common code we're an NVIC */
s->gic.revision = 0xffffffff;
s->num_irq = s->gic.num_irq;
- nc->parent_init(dev);
+ nc->parent_realize(dev, errp);
+ if (error_is_set(errp)) {
+ return;
+ }
gic_init_irqs_and_distributor(&s->gic, s->num_irq);
/* The NVIC and system controller register area looks like this:
* 0..0xff : system control registers, including systick
@@ -503,7 +506,6 @@ static int armv7m_nvic_init(SysBusDevice *dev)
*/
memory_region_add_subregion(get_system_memory(), 0xe000e000, &s->container);
s->systick.timer = qemu_new_timer_ns(vm_clock, systick_timer_tick, s);
- return 0;
}
static void armv7m_nvic_instance_init(Object *obj)
@@ -526,13 +528,12 @@ static void armv7m_nvic_class_init(ObjectClass *klass, void *data)
{
NVICClass *nc = NVIC_CLASS(klass);
DeviceClass *dc = DEVICE_CLASS(klass);
- SysBusDeviceClass *sdc = SYS_BUS_DEVICE_CLASS(klass);
nc->parent_reset = dc->reset;
- nc->parent_init = sdc->init;
- sdc->init = armv7m_nvic_init;
+ nc->parent_realize = dc->realize;
dc->vmsd = &vmstate_nvic;
dc->reset = armv7m_nvic_reset;
+ dc->realize = armv7m_nvic_realize;
}
static const TypeInfo armv7m_nvic_info = {
diff --git a/hw/kvm/arm_gic.c b/hw/kvm/arm_gic.c
new file mode 100644
index 0000000000..22b40b4f84
--- /dev/null
+++ b/hw/kvm/arm_gic.c
@@ -0,0 +1,167 @@
+/*
+ * ARM Generic Interrupt Controller using KVM in-kernel support
+ *
+ * Copyright (c) 2012 Linaro Limited
+ * Written 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 "hw/sysbus.h"
+#include "sysemu/kvm.h"
+#include "kvm_arm.h"
+#include "hw/arm_gic_internal.h"
+
+#define TYPE_KVM_ARM_GIC "kvm-arm-gic"
+#define KVM_ARM_GIC(obj) \
+ OBJECT_CHECK(GICState, (obj), TYPE_KVM_ARM_GIC)
+#define KVM_ARM_GIC_CLASS(klass) \
+ OBJECT_CLASS_CHECK(KVMARMGICClass, (klass), TYPE_KVM_ARM_GIC)
+#define KVM_ARM_GIC_GET_CLASS(obj) \
+ OBJECT_GET_CLASS(KVMARMGICClass, (obj), TYPE_KVM_ARM_GIC)
+
+typedef struct KVMARMGICClass {
+ ARMGICCommonClass parent_class;
+ DeviceRealize parent_realize;
+ void (*parent_reset)(DeviceState *dev);
+} KVMARMGICClass;
+
+static void kvm_arm_gic_set_irq(void *opaque, int irq, int level)
+{
+ /* Meaning of the 'irq' parameter:
+ * [0..N-1] : external interrupts
+ * [N..N+31] : PPI (internal) interrupts for CPU 0
+ * [N+32..N+63] : PPI (internal interrupts for CPU 1
+ * ...
+ * Convert this to the kernel's desired encoding, which
+ * 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)) {
+ /* External interrupt. The kernel numbers these like the GIC
+ * hardware, with external interrupt IDs starting after the
+ * internal ones.
+ */
+ irqtype = KVM_ARM_IRQ_TYPE_SPI;
+ cpu = 0;
+ irq += GIC_INTERNAL;
+ } else {
+ /* Internal interrupt: decode into (cpu, interrupt id) */
+ irqtype = KVM_ARM_IRQ_TYPE_PPI;
+ irq -= (s->num_irq - GIC_INTERNAL);
+ cpu = irq / GIC_INTERNAL;
+ irq %= GIC_INTERNAL;
+ }
+ kvm_irq = (irqtype << KVM_ARM_IRQ_TYPE_SHIFT)
+ | (cpu << KVM_ARM_IRQ_VCPU_SHIFT) | irq;
+
+ kvm_set_irq(kvm_state, kvm_irq, !!level);
+}
+
+static void kvm_arm_gic_put(GICState *s)
+{
+ /* TODO: there isn't currently a kernel interface to set the GIC state */
+}
+
+static void kvm_arm_gic_get(GICState *s)
+{
+ /* TODO: there isn't currently a kernel interface to get the GIC state */
+}
+
+static void kvm_arm_gic_reset(DeviceState *dev)
+{
+ GICState *s = ARM_GIC_COMMON(dev);
+ KVMARMGICClass *kgc = KVM_ARM_GIC_GET_CLASS(s);
+
+ kgc->parent_reset(dev);
+ 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);
+
+ kgc->parent_realize(dev, errp);
+ if (error_is_set(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);
+ /* We never use our outbound IRQ 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]);
+ }
+ /* Distributor */
+ memory_region_init_reservation(&s->iomem, "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);
+ /* CPU interface for current core. Unlike arm_gic, we don't
+ * provide the "interface for core #N" memory regions, because
+ * cores with a VGIC don't have those.
+ */
+ memory_region_init_reservation(&s->cpuiomem[0], "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);
+}
+
+static void kvm_arm_gic_class_init(ObjectClass *klass, void *data)
+{
+ DeviceClass *dc = DEVICE_CLASS(klass);
+ ARMGICCommonClass *agcc = ARM_GIC_COMMON_CLASS(klass);
+ KVMARMGICClass *kgc = KVM_ARM_GIC_CLASS(klass);
+
+ agcc->pre_save = kvm_arm_gic_get;
+ agcc->post_load = kvm_arm_gic_put;
+ kgc->parent_realize = dc->realize;
+ kgc->parent_reset = dc->reset;
+ dc->realize = kvm_arm_gic_realize;
+ dc->reset = kvm_arm_gic_reset;
+ dc->no_user = 1;
+}
+
+static const TypeInfo kvm_arm_gic_info = {
+ .name = TYPE_KVM_ARM_GIC,
+ .parent = TYPE_ARM_GIC_COMMON,
+ .instance_size = sizeof(GICState),
+ .class_init = kvm_arm_gic_class_init,
+ .class_size = sizeof(KVMARMGICClass),
+};
+
+static void kvm_arm_gic_register_types(void)
+{
+ type_register_static(&kvm_arm_gic_info);
+}
+
+type_init(kvm_arm_gic_register_types)