diff options
author | Peter Maydell <peter.maydell@linaro.org> | 2012-10-18 11:49:09 +0100 |
---|---|---|
committer | Peter Maydell <peter.maydell@linaro.org> | 2012-10-18 11:49:09 +0100 |
commit | 334a663a459ec9de7c241fb0aab2a54abe680b17 (patch) | |
tree | d502a6e1826179473bdcc8e885df224f6225984f | |
parent | bdd7f9c38e93426c9a50d13856998df30f2b41dd (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.c | 10 | ||||
-rw-r--r-- | target-arm/Makefile.objs | 1 | ||||
-rw-r--r-- | target-arm/kvm-stub.c | 17 | ||||
-rw-r--r-- | target-arm/kvm.c | 119 | ||||
-rw-r--r-- | target-arm/kvm_arm.h | 32 |
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(¬ify); + } + 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 |