aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorPeter Maydell <peter.maydell@linaro.org>2012-10-18 11:49:09 +0100
committerPeter Maydell <peter.maydell@linaro.org>2012-10-18 11:49:09 +0100
commit334a663a459ec9de7c241fb0aab2a54abe680b17 (patch)
treed502a6e1826179473bdcc8e885df224f6225984f
parentbdd7f9c38e93426c9a50d13856998df30f2b41dd (diff)
target-arm: Use MemoryListener to identify GIC base address for KVMkvm-arm-dev-addr-test
When using an in-kernel GIC with KVM, we need to tell the kernel where the GIC's memory mapped registers live. Do this by registering a MemoryListener which tracks where the board model maps the A15's private peripherals, so we can finish the GIC initialisation when the GIC is actually mapped. Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
-rw-r--r--hw/a15mpcore.c10
-rw-r--r--target-arm/Makefile.objs1
-rw-r--r--target-arm/kvm-stub.c17
-rw-r--r--target-arm/kvm.c119
-rw-r--r--target-arm/kvm_arm.h32
5 files changed, 178 insertions, 1 deletions
diff --git a/hw/a15mpcore.c b/hw/a15mpcore.c
index a37fc612ab..b048518685 100644
--- a/hw/a15mpcore.c
+++ b/hw/a15mpcore.c
@@ -20,6 +20,7 @@
#include "sysbus.h"
#include "kvm.h"
+#include "kvm_arm.h"
/* A15MP private memory region. */
@@ -73,7 +74,14 @@ static int a15mp_priv_init(SysBusDevice *dev)
sysbus_mmio_get_region(busdev, 0));
memory_region_add_subregion(&s->container, 0x2000,
sysbus_mmio_get_region(busdev, 1));
-
+#ifdef CONFIG_KVM
+ kvm_arm_register_device(sysbus_mmio_get_region(busdev, 0),
+ (KVM_ARM_DEVICE_VGIC_V2 << KVM_DEVICE_ID_SHIFT) |
+ KVM_VGIC_V2_ADDR_TYPE_DIST);
+ kvm_arm_register_device(sysbus_mmio_get_region(busdev, 1),
+ (KVM_ARM_DEVICE_VGIC_V2 << KVM_DEVICE_ID_SHIFT) |
+ KVM_VGIC_V2_ADDR_TYPE_CPU);
+#endif
sysbus_init_mmio(dev, &s->container);
return 0;
}
diff --git a/target-arm/Makefile.objs b/target-arm/Makefile.objs
index d89b57c114..4a6e52e528 100644
--- a/target-arm/Makefile.objs
+++ b/target-arm/Makefile.objs
@@ -1,5 +1,6 @@
obj-y += arm-semi.o
obj-$(CONFIG_SOFTMMU) += machine.o
obj-$(CONFIG_KVM) += kvm.o
+obj-$(CONFIG_NO_KVM) += kvm-stub.o
obj-y += translate.o op_helper.o helper.o cpu.o
obj-y += neon_helper.o iwmmxt_helper.o
diff --git a/target-arm/kvm-stub.c b/target-arm/kvm-stub.c
new file mode 100644
index 0000000000..fbdcd0e1af
--- /dev/null
+++ b/target-arm/kvm-stub.c
@@ -0,0 +1,17 @@
+/*
+ * QEMU KVM ARM specific function stubs
+ *
+ * Copyright Linaro Limited 2012
+ *
+ * Author: Peter Maydell <peter.maydell@linaro.org>
+ *
+ * This work is licensed under the terms of the GNU GPL, version 2 or later.
+ * See the COPYING file in the top-level directory.
+ *
+ */
+#include "qemu-common.h"
+#include "kvm_arm.h"
+
+void kvm_arm_register_device(MemoryRegion *mr, uint32_t devid)
+{
+}
diff --git a/target-arm/kvm.c b/target-arm/kvm.c
index f17e7fdb94..71d2349453 100644
--- a/target-arm/kvm.c
+++ b/target-arm/kvm.c
@@ -19,6 +19,7 @@
#include "qemu-timer.h"
#include "sysemu.h"
#include "kvm.h"
+#include "kvm_arm.h"
#include "cpu.h"
#include "device_tree.h"
#include "hw/arm-misc.h"
@@ -63,6 +64,124 @@ int kvm_arch_init_vcpu(CPUARMState *env)
return ret;
}
+/* We track all the KVM devices which need their memory addresses
+ * passing to the kernel in a list of these structures.
+ * When board init is complete we run through the list and
+ * tell the kernel the base addresses of the memory regions.
+ * We use a MemoryListener to track mapping and unmapping of
+ * the regions during board creation, so the board models don't
+ * need to do anything special for the KVM case.
+ */
+typedef struct KVMDevice {
+ struct kvm_device_address kda;
+ MemoryRegion *mr;
+ QSLIST_ENTRY(KVMDevice) entries;
+} KVMDevice;
+
+static QSLIST_HEAD(kvm_devices_head, KVMDevice) kvm_devices_head;
+
+static void kvm_arm_devlistener_add(MemoryListener *listener,
+ MemoryRegionSection *section)
+{
+ KVMDevice *kd;
+ QSLIST_FOREACH(kd, &kvm_devices_head, entries) {
+ if (section->mr == kd->mr) {
+ kd->kda.addr = section->offset_within_address_space;
+ }
+ }
+}
+
+static void kvm_arm_devlistener_del(MemoryListener *listener,
+ MemoryRegionSection *section)
+{
+ KVMDevice *kd;
+ QSLIST_FOREACH(kd, &kvm_devices_head, entries) {
+ if (section->mr == kd->mr) {
+ kd->kda.addr = -1;
+ }
+ }
+}
+
+/* These no-op callbacks can go away once we rebase on
+ * a tree with Avi's memorylistener cleanups in it -- PMM
+ */
+static void kvm_arm_devlistener_noarg_nop(MemoryListener *listener)
+{
+}
+
+static void kvm_arm_devlistener_onearg_nop(MemoryListener *listener,
+ MemoryRegionSection *section)
+{
+}
+
+static void kvm_arm_devlistener_eventfd_nop(MemoryListener *listener,
+ MemoryRegionSection *section,
+ bool match_data, uint64_t data,
+ EventNotifier *e)
+{
+}
+
+static MemoryListener devlistener = {
+ .region_add = kvm_arm_devlistener_add,
+ .region_del = kvm_arm_devlistener_del,
+ .begin = kvm_arm_devlistener_noarg_nop,
+ .commit = kvm_arm_devlistener_noarg_nop,
+ .log_global_start = kvm_arm_devlistener_noarg_nop,
+ .log_global_stop = kvm_arm_devlistener_noarg_nop,
+ .region_nop = kvm_arm_devlistener_onearg_nop,
+ .log_start = kvm_arm_devlistener_onearg_nop,
+ .log_stop = kvm_arm_devlistener_onearg_nop,
+ .log_sync = kvm_arm_devlistener_onearg_nop,
+ .eventfd_add = kvm_arm_devlistener_eventfd_nop,
+ .eventfd_del = kvm_arm_devlistener_eventfd_nop,
+};
+
+static void kvm_arm_machine_init_done(Notifier *notifier, void *data)
+{
+ KVMDevice *kd, *tkd;
+ memory_listener_unregister(&devlistener);
+ QSLIST_FOREACH_SAFE(kd, &kvm_devices_head, entries, tkd) {
+ if (kd->kda.addr != -1) {
+ /* We ignore unmapped devices, trusting that the kernel
+ * will fail the INIT_IRQCHIP if the device is mandatory.
+ */
+ if (kvm_vm_ioctl(kvm_state, KVM_SET_DEVICE_ADDRESS, &kd->kda) < 0) {
+ fprintf(stderr, "KVM_SET_DEVICE_ADDRESS failed: %s\n",
+ strerror(errno));
+ abort();
+ }
+ }
+ g_free(kd);
+ }
+ if (kvm_vm_ioctl(kvm_state, KVM_INIT_IRQCHIP) < 0) {
+ fprintf(stderr, "KVM_INIT_IRQCHIP failed: %s\n", strerror(errno));
+ abort();
+ }
+}
+
+static Notifier notify = {
+ .notify = kvm_arm_machine_init_done,
+};
+
+void kvm_arm_register_device(MemoryRegion *mr, uint32_t devid)
+{
+ KVMDevice *kd;
+
+ if (!kvm_irqchip_in_kernel()) {
+ return;
+ }
+
+ if (QSLIST_EMPTY(&kvm_devices_head)) {
+ memory_listener_register(&devlistener, NULL);
+ qemu_add_machine_init_done_notifier(&notify);
+ }
+ kd = g_new0(KVMDevice, 1);
+ kd->mr = mr;
+ kd->kda.id = devid;
+ kd->kda.addr = -1;
+ QSLIST_INSERT_HEAD(&kvm_devices_head, kd, entries);
+}
+
struct reg {
uint64_t id;
int offset;
diff --git a/target-arm/kvm_arm.h b/target-arm/kvm_arm.h
new file mode 100644
index 0000000000..93babb2af2
--- /dev/null
+++ b/target-arm/kvm_arm.h
@@ -0,0 +1,32 @@
+/*
+ * QEMU KVM support -- ARM specific functions.
+ *
+ * Copyright (c) 2012 Linaro Limited
+ *
+ * This work is licensed under the terms of the GNU GPL, version 2 or later.
+ * See the COPYING file in the top-level directory.
+ *
+ */
+
+#ifndef QEMU_KVM_ARM_H
+#define QEMU_KVM_ARM_H
+
+#include "kvm.h"
+#include "memory.h"
+
+/**
+ * kvm_arm_register_device:
+ * @mr: memory region for this device
+ * @devid: the KVM device ID
+ *
+ * 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.
+ */
+void kvm_arm_register_device(MemoryRegion *mr, uint32_t devid);
+
+#endif