summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--hw/intc/arm_gic_kvm.c22
-rw-r--r--include/hw/intc/arm_gic_common.h1
-rw-r--r--target-arm/kvm.c55
-rw-r--r--target-arm/kvm_arm.h17
4 files changed, 80 insertions, 15 deletions
diff --git a/hw/intc/arm_gic_kvm.c b/hw/intc/arm_gic_kvm.c
index 59a3da5a6b..964d4d7222 100644
--- a/hw/intc/arm_gic_kvm.c
+++ b/hw/intc/arm_gic_kvm.c
@@ -97,6 +97,7 @@ static void kvm_arm_gic_realize(DeviceState *dev, Error **errp)
GICState *s = KVM_ARM_GIC(dev);
SysBusDevice *sbd = SYS_BUS_DEVICE(dev);
KVMARMGICClass *kgc = KVM_ARM_GIC_GET_CLASS(s);
+ int ret;
kgc->parent_realize(dev, errp);
if (error_is_set(errp)) {
@@ -119,13 +120,27 @@ static void kvm_arm_gic_realize(DeviceState *dev, Error **errp)
for (i = 0; i < s->num_cpu; i++) {
sysbus_init_irq(sbd, &s->parent_irq[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;
+ } else if (ret != -ENODEV && ret != -ENOTSUP) {
+ error_setg_errno(errp, -ret, "error creating in-kernel VGIC");
+ return;
+ }
+
/* 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);
+ | KVM_VGIC_V2_ADDR_TYPE_DIST,
+ KVM_DEV_ARM_VGIC_GRP_ADDR,
+ KVM_VGIC_V2_ADDR_TYPE_DIST,
+ s->dev_fd);
/* 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.
@@ -135,7 +150,10 @@ static void kvm_arm_gic_realize(DeviceState *dev, Error **errp)
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_VGIC_V2_ADDR_TYPE_CPU,
+ KVM_DEV_ARM_VGIC_GRP_ADDR,
+ KVM_VGIC_V2_ADDR_TYPE_CPU,
+ s->dev_fd);
}
static void kvm_arm_gic_class_init(ObjectClass *klass, void *data)
diff --git a/include/hw/intc/arm_gic_common.h b/include/hw/intc/arm_gic_common.h
index 89384c2bb4..f6887ed92b 100644
--- a/include/hw/intc/arm_gic_common.h
+++ b/include/hw/intc/arm_gic_common.h
@@ -104,6 +104,7 @@ typedef struct GICState {
MemoryRegion cpuiomem[GIC_NCPU + 1]; /* CPU interfaces */
uint32_t num_irq;
uint32_t revision;
+ int dev_fd; /* kvm device fd if backed by kvm vgic support */
} GICState;
#define TYPE_ARM_GIC_COMMON "arm_gic_common"
diff --git a/target-arm/kvm.c b/target-arm/kvm.c
index 1d2688dda7..39202d7eea 100644
--- a/target-arm/kvm.c
+++ b/target-arm/kvm.c
@@ -165,8 +165,10 @@ unsigned long kvm_arch_vcpu_id(CPUState *cpu)
*/
typedef struct KVMDevice {
struct kvm_arm_device_addr kda;
+ struct kvm_device_attr kdattr;
MemoryRegion *mr;
QSLIST_ENTRY(KVMDevice) entries;
+ int dev_fd;
} KVMDevice;
static QSLIST_HEAD(kvm_devices_head, KVMDevice) kvm_devices_head;
@@ -200,6 +202,29 @@ static MemoryListener devlistener = {
.region_del = kvm_arm_devlistener_del,
};
+static void kvm_arm_set_device_addr(KVMDevice *kd)
+{
+ struct kvm_device_attr *attr = &kd->kdattr;
+ int ret;
+
+ /* If the device control API is available and we have a device fd on the
+ * KVMDevice struct, let's use the newer API
+ */
+ if (kd->dev_fd >= 0) {
+ uint64_t addr = kd->kda.addr;
+ attr->addr = (uintptr_t)&addr;
+ ret = kvm_device_ioctl(kd->dev_fd, KVM_SET_DEVICE_ATTR, attr);
+ } else {
+ ret = kvm_vm_ioctl(kvm_state, KVM_ARM_SET_DEVICE_ADDR, &kd->kda);
+ }
+
+ if (ret < 0) {
+ fprintf(stderr, "Failed to set device address: %s\n",
+ strerror(-ret));
+ abort();
+ }
+}
+
static void kvm_arm_machine_init_done(Notifier *notifier, void *data)
{
KVMDevice *kd, *tkd;
@@ -207,12 +232,7 @@ static void kvm_arm_machine_init_done(Notifier *notifier, void *data)
memory_listener_unregister(&devlistener);
QSLIST_FOREACH_SAFE(kd, &kvm_devices_head, entries, tkd) {
if (kd->kda.addr != -1) {
- if (kvm_vm_ioctl(kvm_state, KVM_ARM_SET_DEVICE_ADDR,
- &kd->kda) < 0) {
- fprintf(stderr, "KVM_ARM_SET_DEVICE_ADDRESS failed: %s\n",
- strerror(errno));
- abort();
- }
+ kvm_arm_set_device_addr(kd);
}
memory_region_unref(kd->mr);
g_free(kd);
@@ -223,7 +243,8 @@ static Notifier notify = {
.notify = kvm_arm_machine_init_done,
};
-void kvm_arm_register_device(MemoryRegion *mr, uint64_t devid)
+void kvm_arm_register_device(MemoryRegion *mr, uint64_t devid, uint64_t group,
+ uint64_t attr, int dev_fd)
{
KVMDevice *kd;
@@ -239,6 +260,10 @@ void kvm_arm_register_device(MemoryRegion *mr, uint64_t devid)
kd->mr = mr;
kd->kda.id = devid;
kd->kda.addr = -1;
+ kd->kdattr.flags = 0;
+ kd->kdattr.group = group;
+ kd->kdattr.attr = attr;
+ kd->dev_fd = dev_fd;
QSLIST_INSERT_HEAD(&kvm_devices_head, kd, entries);
memory_region_ref(kd->mr);
}
@@ -389,3 +414,19 @@ void kvm_arch_remove_all_hw_breakpoints(void)
void kvm_arch_init_irq_routing(KVMState *s)
{
}
+
+int kvm_arch_irqchip_create(KVMState *s)
+{
+ int ret;
+
+ /* If we can create the VGIC using the newer device control API, we
+ * let the device do this when it initializes itself, otherwise we
+ * fall back to the old API */
+
+ ret = kvm_create_device(s, KVM_DEV_TYPE_ARM_VGIC_V2, true);
+ if (ret == 0) {
+ return 1;
+ }
+
+ return 0;
+}
diff --git a/target-arm/kvm_arm.h b/target-arm/kvm_arm.h
index cd3d13ca2d..137c5671e9 100644
--- a/target-arm/kvm_arm.h
+++ b/target-arm/kvm_arm.h
@@ -18,16 +18,21 @@
* kvm_arm_register_device:
* @mr: memory region for this device
* @devid: the KVM device ID
+ * @group: device control API group for setting addresses
+ * @attr: device control API address type
+ * @dev_fd: device control device file descriptor (or -1 if not supported)
*
* Remember the memory region @mr, and when it is mapped by the
* machine model, tell the kernel that base address using the
- * KVM_SET_DEVICE_ADDRESS ioctl. @devid should be the ID of
- * the device as defined by KVM_SET_DEVICE_ADDRESS.
- * The machine model may map and unmap the device multiple times;
- * the kernel will only be told the final address at the point
- * where machine init is complete.
+ * KVM_ARM_SET_DEVICE_ADDRESS ioctl or the newer device control API. @devid
+ * should be the ID of the device as defined by KVM_ARM_SET_DEVICE_ADDRESS or
+ * the arm-vgic device in the device control API.
+ * The machine model may map
+ * and unmap the device multiple times; the kernel will only be told the final
+ * address at the point where machine init is complete.
*/
-void kvm_arm_register_device(MemoryRegion *mr, uint64_t devid);
+void kvm_arm_register_device(MemoryRegion *mr, uint64_t devid, uint64_t group,
+ uint64_t attr, int dev_fd);
/**
* write_list_to_kvmstate: