Message ID | 1361559865-22168-8-git-send-email-peter.maydell@linaro.org |
---|---|
State | Superseded |
Headers | show |
Am 22.02.2013 20:04, schrieb Peter Maydell: > Implement support for using the KVM in-kernel GIC for ARM. > > Signed-off-by: Peter Maydell <peter.maydell@linaro.org> > Reviewed-by: Paolo Bonzini <pbonzini@redhat.com> > --- > hw/a15mpcore.c | 8 ++- > hw/arm/Makefile.objs | 1 + > hw/kvm/arm_gic.c | 169 ++++++++++++++++++++++++++++++++++++++++++++++++++ > 3 files changed, 177 insertions(+), 1 deletion(-) > create mode 100644 hw/kvm/arm_gic.c > > diff --git a/hw/a15mpcore.c b/hw/a15mpcore.c > index fe6c34c..97abe41 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 6d049e7..38b10a8 100644 > --- a/hw/arm/Makefile.objs > +++ b/hw/arm/Makefile.objs > @@ -31,5 +31,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/kvm/arm_gic.c b/hw/kvm/arm_gic.c > new file mode 100644 > index 0000000..c3b73c4 > --- /dev/null > +++ b/hw/kvm/arm_gic.c > @@ -0,0 +1,169 @@ > +/* > + * 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; > + int (*parent_init)(SysBusDevice *dev); > + 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 int kvm_arm_gic_init(SysBusDevice *dev) Please make this a realize function ... > +{ > + /* Device instance init function for the GIC sysbus device */ > + int i; > + GICState *s = FROM_SYSBUS(GICState, dev); ARM_GIC_COMMON(dev) > + KVMARMGICClass *kgc = KVM_ARM_GIC_GET_CLASS(s); > + > + kgc->parent_init(dev); ... and call sbc->init(dev) here as long as the base class is not updated. > + > + 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(&s->busdev.qdev, kvm_arm_gic_set_irq, i); DEVICE() and DeviceState *dev variable > + /* 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(&s->busdev, &s->parent_irq[i]); SYS_BUS_DEVICE() and SysBusDevice *d variable > + } > + /* Distributor */ > + memory_region_init_reservation(&s->iomem, "kvm-gic_dist", 0x1000); > + sysbus_init_mmio(dev, &s->iomem); -> instance_init? > + 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(dev, &s->cpuiomem[0]); -> instance_init? > + kvm_arm_register_device(&s->cpuiomem[0], > + (KVM_ARM_DEVICE_VGIC_V2 << KVM_ARM_DEVICE_ID_SHIFT) > + | KVM_VGIC_V2_ADDR_TYPE_CPU); > + /* TODO: we should tell the kernel at some point the address > + * of the private peripheral base. However we don't currently have > + * any convenient infrastructure to do that, and in any case the > + * kernel doesn't yet implement an ioctl to let us tell it. > + */ > + return 0; > +} > + > +static void kvm_arm_gic_class_init(ObjectClass *klass, void *data) > +{ > + DeviceClass *dc = DEVICE_CLASS(klass); > + SysBusDeviceClass *sbc = SYS_BUS_DEVICE_CLASS(klass); > + ARMGICCommonClass *agcc = ARM_GIC_COMMON_CLASS(klass); > + KVMARMGICClass *kgc = KVM_ARM_GIC_CLASS(klass); Please separate variable definitions and rest of block by a white line, here and elsewhere. > + agcc->pre_save = kvm_arm_gic_get; > + agcc->post_load = kvm_arm_gic_put; > + kgc->parent_init = sbc->init; > + kgc->parent_reset = dc->reset; > + sbc->init = kvm_arm_gic_init; > + 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) Andreas
On 23 February 2013 15:29, Andreas Färber <afaerber@suse.de> wrote: >> +static int kvm_arm_gic_init(SysBusDevice *dev) > > Please make this a realize function ... Will do. As you've probably guessed, this was written some time ago and I never updated it to match our current recommendations. >> + KVMARMGICClass *kgc = KVM_ARM_GIC_GET_CLASS(s); >> + >> + kgc->parent_init(dev); > > ... and call sbc->init(dev) here as long as the base class is not updated. Maybe I should just update the base class? >> +static void kvm_arm_gic_class_init(ObjectClass *klass, void *data) >> +{ >> + DeviceClass *dc = DEVICE_CLASS(klass); >> + SysBusDeviceClass *sbc = SYS_BUS_DEVICE_CLASS(klass); >> + ARMGICCommonClass *agcc = ARM_GIC_COMMON_CLASS(klass); >> + KVMARMGICClass *kgc = KVM_ARM_GIC_CLASS(klass); > > Please separate variable definitions and rest of block by a white line, > here and elsewhere. OK. -- PMM
diff --git a/hw/a15mpcore.c b/hw/a15mpcore.c index fe6c34c..97abe41 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 6d049e7..38b10a8 100644 --- a/hw/arm/Makefile.objs +++ b/hw/arm/Makefile.objs @@ -31,5 +31,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/kvm/arm_gic.c b/hw/kvm/arm_gic.c new file mode 100644 index 0000000..c3b73c4 --- /dev/null +++ b/hw/kvm/arm_gic.c @@ -0,0 +1,169 @@ +/* + * 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; + int (*parent_init)(SysBusDevice *dev); + 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 int kvm_arm_gic_init(SysBusDevice *dev) +{ + /* Device instance init function for the GIC sysbus device */ + int i; + GICState *s = FROM_SYSBUS(GICState, dev); + KVMARMGICClass *kgc = KVM_ARM_GIC_GET_CLASS(s); + + kgc->parent_init(dev); + + 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(&s->busdev.qdev, 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(&s->busdev, &s->parent_irq[i]); + } + /* Distributor */ + memory_region_init_reservation(&s->iomem, "kvm-gic_dist", 0x1000); + sysbus_init_mmio(dev, &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(dev, &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); + /* TODO: we should tell the kernel at some point the address + * of the private peripheral base. However we don't currently have + * any convenient infrastructure to do that, and in any case the + * kernel doesn't yet implement an ioctl to let us tell it. + */ + return 0; +} + +static void kvm_arm_gic_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + SysBusDeviceClass *sbc = SYS_BUS_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_init = sbc->init; + kgc->parent_reset = dc->reset; + sbc->init = kvm_arm_gic_init; + 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)