From ff8f06ee7680fb505079d33caaf8f5ebff0853bc Mon Sep 17 00:00:00 2001 From: Shlomo Pongratz Date: Thu, 24 Sep 2015 01:29:36 +0100 Subject: [PATCH 1/7] hw/intc: Implement GIC-500 base class This class is to be used by both software and KVM implementations of GICv3 Currently it is mostly a placeholder, but in future it is supposed to hold qemu's representation of GICv3 state, which is necessary for migration. The interface of this class is fully compatible with GICv2 one. This is done in order to simplify integration with existing code. Signed-off-by: Shlomo Pongratz Signed-off-by: Pavel Fedin Reviewed-by: Eric Auger Tested-by: Ashok kumar Reviewed-by: Peter Maydell Message-id: aff8baaee493cdcab0694b4a1d4dd5ff27c37ed2.1441784344.git.p.fedin@samsung.com Signed-off-by: Peter Maydell --- hw/intc/Makefile.objs | 1 + hw/intc/arm_gicv3_common.c | 140 +++++++++++++++++++++++++++++ include/hw/intc/arm_gicv3_common.h | 68 ++++++++++++++ 3 files changed, 209 insertions(+) create mode 100644 hw/intc/arm_gicv3_common.c create mode 100644 include/hw/intc/arm_gicv3_common.h diff --git a/hw/intc/Makefile.objs b/hw/intc/Makefile.objs index 092d8a80ac..1317e5a5b4 100644 --- a/hw/intc/Makefile.objs +++ b/hw/intc/Makefile.objs @@ -12,6 +12,7 @@ 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 diff --git a/hw/intc/arm_gicv3_common.c b/hw/intc/arm_gicv3_common.c new file mode 100644 index 0000000000..032ece2166 --- /dev/null +++ b/hw/intc/arm_gicv3_common.c @@ -0,0 +1,140 @@ +/* + * 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 . + */ + +#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/include/hw/intc/arm_gicv3_common.h b/include/hw/intc/arm_gicv3_common.h new file mode 100644 index 0000000000..c2fd8da4ef --- /dev/null +++ b/include/hw/intc/arm_gicv3_common.h @@ -0,0 +1,68 @@ +/* + * ARM GIC support + * + * 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 . + */ + +#ifndef HW_ARM_GICV3_COMMON_H +#define HW_ARM_GICV3_COMMON_H + +#include "hw/sysbus.h" +#include "hw/intc/arm_gic_common.h" + +typedef struct GICv3State { + /*< private >*/ + SysBusDevice parent_obj; + /*< public >*/ + + qemu_irq *parent_irq; + qemu_irq *parent_fiq; + + MemoryRegion iomem_dist; /* Distributor */ + MemoryRegion iomem_redist; /* Redistributors */ + + uint32_t num_cpu; + uint32_t num_irq; + uint32_t revision; + bool security_extn; + + int dev_fd; /* kvm device fd if backed by kvm vgic support */ +} GICv3State; + +#define TYPE_ARM_GICV3_COMMON "arm-gicv3-common" +#define ARM_GICV3_COMMON(obj) \ + OBJECT_CHECK(GICv3State, (obj), TYPE_ARM_GICV3_COMMON) +#define ARM_GICV3_COMMON_CLASS(klass) \ + OBJECT_CLASS_CHECK(ARMGICv3CommonClass, (klass), TYPE_ARM_GICV3_COMMON) +#define ARM_GICV3_COMMON_GET_CLASS(obj) \ + OBJECT_GET_CLASS(ARMGICv3CommonClass, (obj), TYPE_ARM_GICV3_COMMON) + +typedef struct ARMGICv3CommonClass { + /*< private >*/ + SysBusDeviceClass parent_class; + /*< public >*/ + + void (*pre_save)(GICv3State *s); + void (*post_load)(GICv3State *s); +} ARMGICv3CommonClass; + +void gicv3_init_irqs_and_mmio(GICv3State *s, qemu_irq_handler handler, + const MemoryRegionOps *ops); + +#endif From 4b3cfe72d9b9c53be31a88e7eebdda14f1757d3e Mon Sep 17 00:00:00 2001 From: Pavel Fedin Date: Thu, 24 Sep 2015 01:29:36 +0100 Subject: [PATCH 2/7] intc/gic: Extract some reusable vGIC code Some functions previously used only by vGICv2 are useful also for vGICv3 implementation. Untie them from GICState and make accessible from within other modules: - kvm_arm_gic_set_irq() - kvm_gic_supports_attr() - moved to common code and renamed to kvm_device_check_attr() - kvm_gic_access() - turned into GIC-independent kvm_device_access(). Data pointer changed to void * because some GICv3 registers are 64-bit wide Some of these changes are not used right now, but they will be helpful for implementing live migration. Actually kvm_dist_get() and kvm_dist_put() could also be made reusable, but they would require two extra parameters (s->dev_fd and s->num_cpu) as well as lots of typecasts of 's' to DeviceState * and back to GICState *. This makes the code very ugly so i decided to stop at this point. I tried also an approach with making a base class for all possible GICs, but it would contain only three variables (dev_fd, cpu_num and irq_num), and accessing them through the rest of the code would be again tedious (either ugly casts or qemu-style separate object pointer). So i disliked it too. Signed-off-by: Pavel Fedin Tested-by: Ashok kumar Reviewed-by: Peter Maydell Message-id: 2ef56d1dd64ffb75ed02a10dcdaf605e5b8ff4f8.1441784344.git.p.fedin@samsung.com Signed-off-by: Peter Maydell --- hw/intc/arm_gic_kvm.c | 100 ++++++++++++++---------------------------- hw/intc/vgic_common.h | 35 +++++++++++++++ include/sysemu/kvm.h | 26 +++++++++++ kvm-all.c | 34 ++++++++++++++ 4 files changed, 129 insertions(+), 66 deletions(-) create mode 100644 hw/intc/vgic_common.h diff --git a/hw/intc/arm_gic_kvm.c b/hw/intc/arm_gic_kvm.c index e5d0f67186..e8b2386908 100644 --- a/hw/intc/arm_gic_kvm.c +++ b/hw/intc/arm_gic_kvm.c @@ -23,6 +23,7 @@ #include "sysemu/kvm.h" #include "kvm_arm.h" #include "gic_internal.h" +#include "vgic_common.h" //#define DEBUG_GIC_KVM @@ -52,7 +53,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 +64,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 +77,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 +87,36 @@ static void kvm_arm_gic_set_irq(void *opaque, int irq, int level) kvm_set_irq(kvm_state, kvm_irq, !!level); } +static void kvm_arm_gicv2_set_irq(void *opaque, int irq, int level) +{ + GICState *s = (GICState *)opaque; + + kvm_arm_gic_set_irq(s->num_irq, 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) -{ - struct kvm_device_attr attr = { - .group = group, - .attr = attrnum, - .flags = 0, - }; - - if (s->dev_fd == -1) { - return false; - } - - return kvm_device_ioctl(s->dev_fd, KVM_HAS_DEVICE_ATTR, &attr) == 0; -} - -static void kvm_gic_access(GICState *s, int group, int offset, - int cpu, uint32_t *val, bool write) -{ - 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(); - } -} +#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) \ @@ -559,7 +526,7 @@ static void kvm_arm_gic_realize(DeviceState *dev, Error **errp) return; } - gic_init_irqs_and_mmio(s, kvm_arm_gic_set_irq, NULL); + 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); @@ -571,23 +538,24 @@ static void kvm_arm_gic_realize(DeviceState *dev, Error **errp) 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 */ kvm_arm_register_device(&s->iomem, (KVM_ARM_DEVICE_VGIC_V2 << KVM_ARM_DEVICE_ID_SHIFT) diff --git a/hw/intc/vgic_common.h b/hw/intc/vgic_common.h new file mode 100644 index 0000000000..80d919eb93 --- /dev/null +++ b/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 . + */ + +#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/include/sysemu/kvm.h b/include/sysemu/kvm.h index 983e99e1e7..2a58b4d19e 100644 --- a/include/sysemu/kvm.h +++ b/include/sysemu/kvm.h @@ -239,6 +239,32 @@ int kvm_device_ioctl(int fd, int type, ...); */ int kvm_vm_check_attr(KVMState *s, uint32_t group, uint64_t attr); +/** + * kvm_device_check_attr - check for existence of a specific device attribute + * @fd: The device file descriptor + * @group: the group + * @attr: the attribute of that group to query for + * + * Returns: 1 if the attribute exists + * 0 if the attribute either does not exist or if the vm device + * interface is unavailable + */ +int kvm_device_check_attr(int fd, uint32_t group, uint64_t attr); + +/** + * kvm_device_access - set or get value of a specific vm attribute + * @fd: The device file descriptor + * @group: the group + * @attr: the attribute of that group to set or get + * @val: pointer to a storage area for the value + * @write: true for set and false for get operation + * + * This function is not allowed to fail. Use kvm_device_check_attr() + * in order to check for the availability of optional attributes. + */ +void kvm_device_access(int fd, int group, uint64_t attr, + void *val, bool write); + /** * kvm_create_device - create a KVM device for the device control API * @KVMState: The KVMState pointer diff --git a/kvm-all.c b/kvm-all.c index de1924c467..0be4615cdb 100644 --- a/kvm-all.c +++ b/kvm-all.c @@ -24,6 +24,7 @@ #include "qemu/atomic.h" #include "qemu/option.h" #include "qemu/config-file.h" +#include "qemu/error-report.h" #include "hw/hw.h" #include "hw/pci/msi.h" #include "hw/s390x/adapter.h" @@ -2008,6 +2009,39 @@ int kvm_vm_check_attr(KVMState *s, uint32_t group, uint64_t attr) return ret ? 0 : 1; } +int kvm_device_check_attr(int dev_fd, uint32_t group, uint64_t attr) +{ + struct kvm_device_attr attribute = { + .group = group, + .attr = attr, + .flags = 0, + }; + + return kvm_device_ioctl(dev_fd, KVM_HAS_DEVICE_ATTR, &attribute) ? 0 : 1; +} + +void kvm_device_access(int fd, int group, uint64_t attr, + void *val, bool write) +{ + struct kvm_device_attr kvmattr; + int err; + + kvmattr.flags = 0; + kvmattr.group = group; + kvmattr.attr = attr; + kvmattr.addr = (uintptr_t)val; + + err = kvm_device_ioctl(fd, + write ? KVM_SET_DEVICE_ATTR : KVM_GET_DEVICE_ATTR, + &kvmattr); + if (err < 0) { + error_report("KVM_%s_DEVICE_ATTR failed: %s\n" + "Group %d attr 0x%016" PRIx64, write ? "SET" : "GET", + strerror(-err), group, attr); + abort(); + } +} + int kvm_has_sync_mmu(void) { return kvm_check_extension(kvm_state, KVM_CAP_SYNC_MMU); From 34e85cd9173816cd48f5578c7838c26afbe592c4 Mon Sep 17 00:00:00 2001 From: Pavel Fedin Date: Thu, 24 Sep 2015 01:29:37 +0100 Subject: [PATCH 3/7] arm_kvm: Do not assume particular GIC type in kvm_arch_irqchip_create() This allows us to use different GIC types from v2. There are no kernels which could advertise KVM_CAP_DEVICE_CTRL without the actual ability to create GIC with it. GIC version probe code moved to kvm_arm_vgic_probe() which will be used later. Signed-off-by: Pavel Fedin Reviewed-by: Eric Auger Tested-by: Ashok kumar Reviewed-by: Peter Maydell Message-id: 015f4d9e4a8a50dfbdd734c4730558e24a69c6dc.1441784344.git.p.fedin@samsung.com Signed-off-by: Peter Maydell --- target-arm/kvm.c | 19 ++++++++++++------- target-arm/kvm_arm.h | 9 +++++++++ 2 files changed, 21 insertions(+), 7 deletions(-) diff --git a/target-arm/kvm.c b/target-arm/kvm.c index b278542085..6aadcd88f7 100644 --- a/target-arm/kvm.c +++ b/target-arm/kvm.c @@ -585,18 +585,23 @@ 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 */ + return kvm_check_extension(s, KVM_CAP_DEVICE_CTRL); +} - ret = kvm_create_device(s, KVM_DEV_TYPE_ARM_VGIC_V2, true); - if (ret == 0) { - return 1; +int kvm_arm_vgic_probe(void) +{ + if (kvm_create_device(kvm_state, + KVM_DEV_TYPE_ARM_VGIC_V3, true) == 0) { + return 3; + } else if (kvm_create_device(kvm_state, + KVM_DEV_TYPE_ARM_VGIC_V2, true) == 0) { + return 2; + } else { + return 0; } - - return 0; } int kvm_arch_fixup_msi_route(struct kvm_irq_routing_entry *route, diff --git a/target-arm/kvm_arm.h b/target-arm/kvm_arm.h index b3e0ab7e7c..10ce771f65 100644 --- a/target-arm/kvm_arm.h +++ b/target-arm/kvm_arm.h @@ -189,6 +189,15 @@ int kvm_arm_sync_mpstate_to_kvm(ARMCPU *cpu); */ int kvm_arm_sync_mpstate_to_qemu(ARMCPU *cpu); +int kvm_arm_vgic_probe(void); + +#else + +static inline int kvm_arm_vgic_probe(void) +{ + return 0; +} + #endif static inline const char *gic_class_name(void) From a7bf30342e6a7924132a5c70047928261d3c7e42 Mon Sep 17 00:00:00 2001 From: Pavel Fedin Date: Thu, 24 Sep 2015 01:29:37 +0100 Subject: [PATCH 4/7] hw/intc: Initial implementation of vGICv3 This is the initial version of KVM-accelerated GICv3 support. State load and save are not yet supported, live migration is not possible. In order to get correct class name in a simpler way, gicv3_class_name() function is implemented, similar to gic_class_name(). Signed-off-by: Pavel Fedin Reviewed-by: Peter Maydell Tested-by: Ashok kumar Message-id: 69d8f01d14994d7a1a140e96aef59fd332d02293.1441784344.git.p.fedin@samsung.com Signed-off-by: Peter Maydell --- hw/intc/Makefile.objs | 1 + hw/intc/arm_gicv3_kvm.c | 149 ++++++++++++++++++++++++++++++++++++++++ target-arm/kvm_arm.h | 10 +++ target-arm/machine.c | 18 +++++ 4 files changed, 178 insertions(+) create mode 100644 hw/intc/arm_gicv3_kvm.c diff --git a/hw/intc/Makefile.objs b/hw/intc/Makefile.objs index 1317e5a5b4..004b0c25e4 100644 --- a/hw/intc/Makefile.objs +++ b/hw/intc/Makefile.objs @@ -17,6 +17,7 @@ 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 diff --git a/hw/intc/arm_gicv3_kvm.c b/hw/intc/arm_gicv3_kvm.c new file mode 100644 index 0000000000..b48f78f13d --- /dev/null +++ b/hw/intc/arm_gicv3_kvm.c @@ -0,0 +1,149 @@ +/* + * 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 . + */ + +#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/target-arm/kvm_arm.h b/target-arm/kvm_arm.h index 10ce771f65..b516041779 100644 --- a/target-arm/kvm_arm.h +++ b/target-arm/kvm_arm.h @@ -205,4 +205,14 @@ static inline const char *gic_class_name(void) return kvm_irqchip_in_kernel() ? "kvm-arm-gic" : "arm_gic"; } +/** + * gicv3_class_name + * + * Return name of GICv3 class to use depending on whether KVM acceleration is + * in use. May throw an error if the chosen implementation is not available. + * + * Returns: class name to use + */ +const char *gicv3_class_name(void); + #endif diff --git a/target-arm/machine.c b/target-arm/machine.c index 32adfe792e..36a0d159cc 100644 --- a/target-arm/machine.c +++ b/target-arm/machine.c @@ -1,5 +1,6 @@ #include "hw/hw.h" #include "hw/boards.h" +#include "qemu/error-report.h" #include "sysemu/kvm.h" #include "kvm_arm.h" #include "internals.h" @@ -328,3 +329,20 @@ const VMStateDescription vmstate_arm_cpu = { NULL } }; + +const char *gicv3_class_name(void) +{ + if (kvm_irqchip_in_kernel()) { +#ifdef TARGET_AARCH64 + return "kvm-arm-gicv3"; +#else + error_report("KVM GICv3 acceleration is not supported on this " + "platform\n"); +#endif + } else { + /* TODO: Software emulation is not implemented yet */ + error_report("KVM is currently required for GICv3 emulation\n"); + } + + exit(1); +} From b92ad3949bc9cacd1652b4e07e7f6003b9e512af Mon Sep 17 00:00:00 2001 From: Pavel Fedin Date: Thu, 24 Sep 2015 01:29:37 +0100 Subject: [PATCH 5/7] hw/arm/virt: Add gic-version option to virt machine Add gic_version to VirtMachineState, set it to value of the option and pass it around where necessary. Instantiate devices and fdt nodes according to the choice. max_cpus for virt machine increased to 123 (calculated from redistributor space available in the memory map). GICv2 compatibility check happens inside arm_gic_common_realize(). ITS region is added to the memory map too, however currently it not used, just reserved. Signed-off-by: Pavel Fedin Tested-by: Ashok kumar [PMM: Added missing cpu_to_le* calls, thanks to Shannon Zhao] Reviewed-by: Peter Maydell Signed-off-by: Peter Maydell --- hw/arm/virt-acpi-build.c | 54 ++++++++------ hw/arm/virt.c | 124 +++++++++++++++++++++++++------ include/hw/acpi/acpi-defs.h | 9 +++ include/hw/arm/virt-acpi-build.h | 1 + include/hw/arm/virt.h | 4 +- 5 files changed, 146 insertions(+), 46 deletions(-) diff --git a/hw/arm/virt-acpi-build.c b/hw/arm/virt-acpi-build.c index 9088248c3a..59c84ff27e 100644 --- a/hw/arm/virt-acpi-build.c +++ b/hw/arm/virt-acpi-build.c @@ -443,33 +443,43 @@ build_madt(GArray *table_data, GArray *linker, VirtGuestInfo *guest_info, madt = acpi_data_push(table_data, sizeof *madt); - for (i = 0; i < guest_info->smp_cpus; i++) { - AcpiMadtGenericInterrupt *gicc = acpi_data_push(table_data, - sizeof *gicc); - gicc->type = ACPI_APIC_GENERIC_INTERRUPT; - gicc->length = sizeof(*gicc); - gicc->base_address = memmap[VIRT_GIC_CPU].base; - gicc->cpu_interface_number = i; - gicc->arm_mpidr = i; - gicc->uid = i; - if (test_bit(i, cpuinfo->found_cpus)) { - gicc->flags = cpu_to_le32(ACPI_GICC_ENABLED); - } - } - gicd = acpi_data_push(table_data, sizeof *gicd); gicd->type = ACPI_APIC_GENERIC_DISTRIBUTOR; gicd->length = sizeof(*gicd); gicd->base_address = memmap[VIRT_GIC_DIST].base; - gic_msi = acpi_data_push(table_data, sizeof *gic_msi); - gic_msi->type = ACPI_APIC_GENERIC_MSI_FRAME; - gic_msi->length = sizeof(*gic_msi); - gic_msi->gic_msi_frame_id = 0; - gic_msi->base_address = cpu_to_le64(memmap[VIRT_GIC_V2M].base); - gic_msi->flags = cpu_to_le32(1); - gic_msi->spi_count = cpu_to_le16(NUM_GICV2M_SPIS); - gic_msi->spi_base = cpu_to_le16(irqmap[VIRT_GIC_V2M] + ARM_SPI_BASE); + if (guest_info->gic_version == 3) { + AcpiMadtGenericRedistributor *gicr = acpi_data_push(table_data, + sizeof *gicr); + + gicr->type = ACPI_APIC_GENERIC_REDISTRIBUTOR; + gicr->length = sizeof(*gicr); + gicr->base_address = cpu_to_le64(memmap[VIRT_GIC_REDIST].base); + gicr->range_length = cpu_to_le32(memmap[VIRT_GIC_REDIST].size); + } else { + for (i = 0; i < guest_info->smp_cpus; i++) { + AcpiMadtGenericInterrupt *gicc = acpi_data_push(table_data, + sizeof *gicc); + gicc->type = ACPI_APIC_GENERIC_INTERRUPT; + gicc->length = sizeof(*gicc); + gicc->base_address = memmap[VIRT_GIC_CPU].base; + gicc->cpu_interface_number = i; + gicc->arm_mpidr = i; + gicc->uid = i; + if (test_bit(i, cpuinfo->found_cpus)) { + gicc->flags = cpu_to_le32(ACPI_GICC_ENABLED); + } + } + + gic_msi = acpi_data_push(table_data, sizeof *gic_msi); + gic_msi->type = ACPI_APIC_GENERIC_MSI_FRAME; + gic_msi->length = sizeof(*gic_msi); + gic_msi->gic_msi_frame_id = 0; + gic_msi->base_address = cpu_to_le64(memmap[VIRT_GIC_V2M].base); + gic_msi->flags = cpu_to_le32(1); + gic_msi->spi_count = cpu_to_le16(NUM_GICV2M_SPIS); + gic_msi->spi_base = cpu_to_le16(irqmap[VIRT_GIC_V2M] + ARM_SPI_BASE); + } build_header(linker, table_data, (void *)(table_data->data + madt_start), "APIC", diff --git a/hw/arm/virt.c b/hw/arm/virt.c index 6bf0d6d591..d25d6cfce7 100644 --- a/hw/arm/virt.c +++ b/hw/arm/virt.c @@ -51,6 +51,7 @@ #include "hw/intc/arm_gic_common.h" #include "kvm_arm.h" #include "hw/smbios/smbios.h" +#include "qapi/visitor.h" /* Number of external interrupt lines to configure the GIC with */ #define NUM_IRQS 256 @@ -81,6 +82,7 @@ typedef struct { MachineState parent; bool secure; bool highmem; + int32_t gic_version; } VirtMachineState; #define TYPE_VIRT_MACHINE MACHINE_TYPE_NAME("virt") @@ -111,6 +113,10 @@ static const MemMapEntry a15memmap[] = { [VIRT_GIC_DIST] = { 0x08000000, 0x00010000 }, [VIRT_GIC_CPU] = { 0x08010000, 0x00010000 }, [VIRT_GIC_V2M] = { 0x08020000, 0x00001000 }, + /* The space in between here is reserved for GICv3 CPU/vCPU/HYP */ + [VIRT_GIC_ITS] = { 0x08080000, 0x00020000 }, + /* This redistributor space allows up to 2*64kB*123 CPUs */ + [VIRT_GIC_REDIST] = { 0x080A0000, 0x00F60000 }, [VIRT_UART] = { 0x09000000, 0x00001000 }, [VIRT_RTC] = { 0x09010000, 0x00001000 }, [VIRT_FW_CFG] = { 0x09020000, 0x0000000a }, @@ -255,7 +261,7 @@ static void fdt_add_psci_node(const VirtBoardInfo *vbi) qemu_fdt_setprop_cell(fdt, "/psci", "migrate", migrate_fn); } -static void fdt_add_timer_nodes(const VirtBoardInfo *vbi) +static void fdt_add_timer_nodes(const VirtBoardInfo *vbi, int gictype) { /* Note that on A15 h/w these interrupts are level-triggered, * but for the GIC implementation provided by both QEMU and KVM @@ -264,8 +270,11 @@ static void fdt_add_timer_nodes(const VirtBoardInfo *vbi) ARMCPU *armcpu; uint32_t irqflags = GIC_FDT_IRQ_FLAGS_EDGE_LO_HI; - irqflags = deposit32(irqflags, GIC_FDT_IRQ_PPI_CPU_START, - GIC_FDT_IRQ_PPI_CPU_WIDTH, (1 << vbi->smp_cpus) - 1); + if (gictype == 2) { + irqflags = deposit32(irqflags, GIC_FDT_IRQ_PPI_CPU_START, + GIC_FDT_IRQ_PPI_CPU_WIDTH, + (1 << vbi->smp_cpus) - 1); + } qemu_fdt_add_subnode(vbi->fdt, "/timer"); @@ -355,25 +364,36 @@ static void fdt_add_v2m_gic_node(VirtBoardInfo *vbi) qemu_fdt_setprop_cell(vbi->fdt, "/intc/v2m", "phandle", vbi->v2m_phandle); } -static void fdt_add_gic_node(VirtBoardInfo *vbi) +static void fdt_add_gic_node(VirtBoardInfo *vbi, int type) { vbi->gic_phandle = qemu_fdt_alloc_phandle(vbi->fdt); qemu_fdt_setprop_cell(vbi->fdt, "/", "interrupt-parent", vbi->gic_phandle); qemu_fdt_add_subnode(vbi->fdt, "/intc"); - /* 'cortex-a15-gic' means 'GIC v2' */ - qemu_fdt_setprop_string(vbi->fdt, "/intc", "compatible", - "arm,cortex-a15-gic"); qemu_fdt_setprop_cell(vbi->fdt, "/intc", "#interrupt-cells", 3); qemu_fdt_setprop(vbi->fdt, "/intc", "interrupt-controller", NULL, 0); - qemu_fdt_setprop_sized_cells(vbi->fdt, "/intc", "reg", - 2, vbi->memmap[VIRT_GIC_DIST].base, - 2, vbi->memmap[VIRT_GIC_DIST].size, - 2, vbi->memmap[VIRT_GIC_CPU].base, - 2, vbi->memmap[VIRT_GIC_CPU].size); qemu_fdt_setprop_cell(vbi->fdt, "/intc", "#address-cells", 0x2); qemu_fdt_setprop_cell(vbi->fdt, "/intc", "#size-cells", 0x2); qemu_fdt_setprop(vbi->fdt, "/intc", "ranges", NULL, 0); + if (type == 3) { + qemu_fdt_setprop_string(vbi->fdt, "/intc", "compatible", + "arm,gic-v3"); + qemu_fdt_setprop_sized_cells(vbi->fdt, "/intc", "reg", + 2, vbi->memmap[VIRT_GIC_DIST].base, + 2, vbi->memmap[VIRT_GIC_DIST].size, + 2, vbi->memmap[VIRT_GIC_REDIST].base, + 2, vbi->memmap[VIRT_GIC_REDIST].size); + } else { + /* 'cortex-a15-gic' means 'GIC v2' */ + qemu_fdt_setprop_string(vbi->fdt, "/intc", "compatible", + "arm,cortex-a15-gic"); + qemu_fdt_setprop_sized_cells(vbi->fdt, "/intc", "reg", + 2, vbi->memmap[VIRT_GIC_DIST].base, + 2, vbi->memmap[VIRT_GIC_DIST].size, + 2, vbi->memmap[VIRT_GIC_CPU].base, + 2, vbi->memmap[VIRT_GIC_CPU].size); + } + qemu_fdt_setprop_cell(vbi->fdt, "/intc", "phandle", vbi->gic_phandle); } @@ -396,18 +416,18 @@ static void create_v2m(VirtBoardInfo *vbi, qemu_irq *pic) fdt_add_v2m_gic_node(vbi); } -static void create_gic(VirtBoardInfo *vbi, qemu_irq *pic, bool secure) +static void create_gic(VirtBoardInfo *vbi, qemu_irq *pic, int type, bool secure) { - /* We create a standalone GIC v2 */ + /* We create a standalone GIC */ DeviceState *gicdev; SysBusDevice *gicbusdev; const char *gictype; int i; - gictype = gic_class_name(); + gictype = (type == 3) ? gicv3_class_name() : gic_class_name(); gicdev = qdev_create(NULL, gictype); - qdev_prop_set_uint32(gicdev, "revision", 2); + qdev_prop_set_uint32(gicdev, "revision", type); qdev_prop_set_uint32(gicdev, "num-cpu", smp_cpus); /* Note that the num-irq property counts both internal and external * interrupts; there are always 32 of the former (mandated by GIC spec). @@ -419,7 +439,11 @@ static void create_gic(VirtBoardInfo *vbi, qemu_irq *pic, bool secure) qdev_init_nofail(gicdev); gicbusdev = SYS_BUS_DEVICE(gicdev); sysbus_mmio_map(gicbusdev, 0, vbi->memmap[VIRT_GIC_DIST].base); - sysbus_mmio_map(gicbusdev, 1, vbi->memmap[VIRT_GIC_CPU].base); + if (type == 3) { + sysbus_mmio_map(gicbusdev, 1, vbi->memmap[VIRT_GIC_REDIST].base); + } else { + sysbus_mmio_map(gicbusdev, 1, vbi->memmap[VIRT_GIC_CPU].base); + } /* Wire the outputs from each CPU's generic timer to the * appropriate GIC PPI inputs, and the GIC's IRQ output to @@ -454,9 +478,11 @@ static void create_gic(VirtBoardInfo *vbi, qemu_irq *pic, bool secure) pic[i] = qdev_get_gpio_in(gicdev, i); } - fdt_add_gic_node(vbi); + fdt_add_gic_node(vbi, type); - create_v2m(vbi, pic); + if (type == 2) { + create_v2m(vbi, pic); + } } static void create_uart(const VirtBoardInfo *vbi, qemu_irq *pic) @@ -773,7 +799,10 @@ static void create_pcie(const VirtBoardInfo *vbi, qemu_irq *pic, qemu_fdt_setprop_cells(vbi->fdt, nodename, "bus-range", 0, nr_pcie_buses - 1); - qemu_fdt_setprop_cells(vbi->fdt, nodename, "msi-parent", vbi->v2m_phandle); + if (vbi->v2m_phandle) { + qemu_fdt_setprop_cells(vbi->fdt, nodename, "msi-parent", + vbi->v2m_phandle); + } qemu_fdt_setprop_sized_cells(vbi->fdt, nodename, "reg", 2, base_ecam, 2, size_ecam); @@ -888,6 +917,7 @@ static void machvirt_init(MachineState *machine) VirtMachineState *vms = VIRT_MACHINE(machine); qemu_irq pic[NUM_IRQS]; MemoryRegion *sysmem = get_system_memory(); + int gic_version = vms->gic_version; int n; MemoryRegion *ram = g_new(MemoryRegion, 1); const char *cpu_model = machine->cpu_model; @@ -900,6 +930,18 @@ static void machvirt_init(MachineState *machine) cpu_model = "cortex-a15"; } + /* We can probe only here because during property set + * KVM is not available yet + */ + if (!gic_version) { + gic_version = kvm_arm_vgic_probe(); + if (!gic_version) { + error_report("Unable to determine GIC version supported by host\n" + "Probably KVM acceleration is not supported\n"); + exit(1); + } + } + /* Separate the actual CPU model name from any appended features */ cpustr = g_strsplit(cpu_model, ",", 2); @@ -960,7 +1002,7 @@ static void machvirt_init(MachineState *machine) object_property_set_bool(cpuobj, true, "realized", NULL); } g_strfreev(cpustr); - fdt_add_timer_nodes(vbi); + fdt_add_timer_nodes(vbi, gic_version); fdt_add_cpu_nodes(vbi); fdt_add_psci_node(vbi); @@ -970,7 +1012,7 @@ static void machvirt_init(MachineState *machine) create_flash(vbi); - create_gic(vbi, pic, vms->secure); + create_gic(vbi, pic, gic_version, vms->secure); create_uart(vbi, pic); @@ -992,6 +1034,7 @@ static void machvirt_init(MachineState *machine) guest_info->memmap = vbi->memmap; guest_info->irqmap = vbi->irqmap; guest_info->use_highmem = vms->highmem; + guest_info->gic_version = gic_version; guest_info_state->machine_done.notify = virt_guest_info_machine_done; qemu_add_machine_init_done_notifier(&guest_info_state->machine_done); @@ -1043,6 +1086,31 @@ static void virt_set_highmem(Object *obj, bool value, Error **errp) vms->highmem = value; } +static char *virt_get_gic_version(Object *obj, Error **errp) +{ + VirtMachineState *vms = VIRT_MACHINE(obj); + const char *val = vms->gic_version == 3 ? "3" : "2"; + + return g_strdup(val); +} + +static void virt_set_gic_version(Object *obj, const char *value, Error **errp) +{ + VirtMachineState *vms = VIRT_MACHINE(obj); + + if (!strcmp(value, "3")) { + vms->gic_version = 3; + } else if (!strcmp(value, "2")) { + vms->gic_version = 2; + } else if (!strcmp(value, "host")) { + vms->gic_version = 0; /* Will probe later */ + } else { + error_report("Invalid gic-version option value\n" + "Allowed values are: 3, 2, host\n"); + exit(1); + } +} + static void virt_instance_init(Object *obj) { VirtMachineState *vms = VIRT_MACHINE(obj); @@ -1067,6 +1135,13 @@ static void virt_instance_init(Object *obj) "Set on/off to enable/disable using " "physical address space above 32 bits", NULL); + /* Default GIC type is v2 */ + vms->gic_version = 2; + object_property_add_str(obj, "gic-version", virt_get_gic_version, + virt_set_gic_version, NULL); + object_property_set_description(obj, "gic-version", + "Set GIC version. " + "Valid values are 2, 3 and host", NULL); } static void virt_class_init(ObjectClass *oc, void *data) @@ -1075,7 +1150,10 @@ static void virt_class_init(ObjectClass *oc, void *data) mc->desc = "ARM Virtual Machine", mc->init = machvirt_init; - mc->max_cpus = 8; + /* Our maximum number of CPUs depends on how many redistributors + * we can fit into memory map + */ + mc->max_cpus = a15memmap[VIRT_GIC_REDIST].size / 0x20000; mc->has_dynamic_sysbus = true; mc->block_default_type = IF_VIRTIO; mc->no_cdrom = 1; diff --git a/include/hw/acpi/acpi-defs.h b/include/hw/acpi/acpi-defs.h index 2b431e6242..c7a03d43b9 100644 --- a/include/hw/acpi/acpi-defs.h +++ b/include/hw/acpi/acpi-defs.h @@ -384,6 +384,15 @@ struct AcpiMadtGenericMsiFrame { typedef struct AcpiMadtGenericMsiFrame AcpiMadtGenericMsiFrame; +struct AcpiMadtGenericRedistributor { + ACPI_SUB_HEADER_DEF + uint16_t reserved; + uint64_t base_address; + uint32_t range_length; +} QEMU_PACKED; + +typedef struct AcpiMadtGenericRedistributor AcpiMadtGenericRedistributor; + /* * Generic Timer Description Table (GTDT) */ diff --git a/include/hw/arm/virt-acpi-build.h b/include/hw/arm/virt-acpi-build.h index 19b68a404e..744b666385 100644 --- a/include/hw/arm/virt-acpi-build.h +++ b/include/hw/arm/virt-acpi-build.h @@ -32,6 +32,7 @@ typedef struct VirtGuestInfo { const MemMapEntry *memmap; const int *irqmap; bool use_highmem; + int gic_version; } VirtGuestInfo; diff --git a/include/hw/arm/virt.h b/include/hw/arm/virt.h index 808753f08e..f464586304 100644 --- a/include/hw/arm/virt.h +++ b/include/hw/arm/virt.h @@ -46,6 +46,9 @@ enum { VIRT_CPUPERIPHS, VIRT_GIC_DIST, VIRT_GIC_CPU, + VIRT_GIC_V2M, + VIRT_GIC_ITS, + VIRT_GIC_REDIST, VIRT_UART, VIRT_MMIO, VIRT_RTC, @@ -54,7 +57,6 @@ enum { VIRT_PCIE_MMIO, VIRT_PCIE_PIO, VIRT_PCIE_ECAM, - VIRT_GIC_V2M, VIRT_PLATFORM_BUS, VIRT_PCIE_MMIO_HIGH, }; From cd37aaf876717a75d7af3a7465e8706cc4e13661 Mon Sep 17 00:00:00 2001 From: Shannon Zhao Date: Thu, 24 Sep 2015 01:29:37 +0100 Subject: [PATCH 6/7] hw/arm/virt-acpi-build: Fix wrong size of flash in ACPI table While virt machine creates two flash devices with total size 0x08000000, the ACPI table generation code was wrongly using this total size as the size of each flash device, so it would overlap other MMIO spaces. Make each device entry in the table half the total; this brings the ACPI table into line with the code which generates the device tree and which creates the flash devices themselves. Signed-off-by: Shannon Zhao Reviewed-by: Andrew Jones Reviewed-by: Wei Huang Tested-by: Graeme Gregory Message-id: 1442455041-6596-1-git-send-email-shannon.zhao@linaro.org [PMM: edited commit message] Signed-off-by: Peter Maydell --- hw/arm/virt-acpi-build.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hw/arm/virt-acpi-build.c b/hw/arm/virt-acpi-build.c index 59c84ff27e..1aaff1f662 100644 --- a/hw/arm/virt-acpi-build.c +++ b/hw/arm/virt-acpi-build.c @@ -114,7 +114,7 @@ static void acpi_dsdt_add_flash(Aml *scope, const MemMapEntry *flash_memmap) { Aml *dev, *crs; hwaddr base = flash_memmap->base; - hwaddr size = flash_memmap->size; + hwaddr size = flash_memmap->size / 2; dev = aml_device("FLS0"); aml_append(dev, aml_name_decl("_HID", aml_string("LNRO0015"))); From 85b4d5dae12580ecdd446c0f71afa04a95641c91 Mon Sep 17 00:00:00 2001 From: Beniamino Galvani Date: Thu, 24 Sep 2015 01:29:37 +0100 Subject: [PATCH 7/7] MAINTAINERS: update Allwinner A10 maintainer Change the maintainer for Allwinner A10 to myself as Li Guang's mail address bounces. While at it, extend the file pattern for the entry to include allwinner_emac.[ch]. Signed-off-by: Beniamino Galvani Reviewed-by: Peter Crosthwaite Message-id: 1442865156-5598-1-git-send-email-b.galvani@gmail.com Signed-off-by: Peter Maydell --- MAINTAINERS | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/MAINTAINERS b/MAINTAINERS index 71c652be98..cf02890e8b 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -266,10 +266,10 @@ F: *win32* ARM Machines ------------ Allwinner-a10 -M: Li Guang +M: Beniamino Galvani S: Maintained -F: hw/*/allwinner-a10* -F: include/hw/*/allwinner-a10* +F: hw/*/allwinner* +F: include/hw/*/allwinner* F: hw/arm/cubieboard.c Exynos