From patchwork Thu Aug 9 16:33:09 2012 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Peter Maydell X-Patchwork-Id: 1302351 Return-Path: X-Original-To: patchwork-kvm@patchwork.kernel.org Delivered-To: patchwork-process-083081@patchwork2.kernel.org Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by patchwork2.kernel.org (Postfix) with ESMTP id 9F2CFDFF7B for ; Thu, 9 Aug 2012 16:56:59 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1752064Ab2HIQ4j (ORCPT ); Thu, 9 Aug 2012 12:56:39 -0400 Received: from mnementh.archaic.org.uk ([81.2.115.146]:33752 "EHLO mnementh.archaic.org.uk" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1750979Ab2HIQ4i (ORCPT ); Thu, 9 Aug 2012 12:56:38 -0400 Received: from pm215 by mnementh.archaic.org.uk with local (Exim 4.72) (envelope-from ) id 1SzVfa-00063c-Q5; Thu, 09 Aug 2012 17:33:10 +0100 From: Peter Maydell To: qemu-devel@nongnu.org Cc: patches@linaro.org, kvmarm@lists.cs.columbia.edu, kvm@vger.kernel.org Subject: [RFC 4/5] hw/kvm/arm_gic: Implement support for KVM in-kernel ARM GIC Date: Thu, 9 Aug 2012 17:33:09 +0100 Message-Id: <1344529990-23251-5-git-send-email-peter.maydell@linaro.org> X-Mailer: git-send-email 1.7.2.5 In-Reply-To: <1344529990-23251-1-git-send-email-peter.maydell@linaro.org> References: <1344529990-23251-1-git-send-email-peter.maydell@linaro.org> Sender: kvm-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: kvm@vger.kernel.org Implement support for using the KVM in-kernel GIC for ARM. Signed-off-by: Peter Maydell --- hw/a15mpcore.c | 11 +++- hw/arm/Makefile.objs | 1 + hw/kvm/arm_gic.c | 153 ++++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 164 insertions(+), 1 deletion(-) create mode 100644 hw/kvm/arm_gic.c diff --git a/hw/a15mpcore.c b/hw/a15mpcore.c index fc0a02a..e075849 100644 --- a/hw/a15mpcore.c +++ b/hw/a15mpcore.c @@ -19,6 +19,7 @@ */ #include "sysbus.h" +#include "kvm.h" /* A15MP private memory region. */ @@ -41,7 +42,15 @@ static int a15mp_priv_init(SysBusDevice *dev) A15MPPrivState *s = FROM_SYSBUS(A15MPPrivState, dev); SysBusDevice *busdev; - s->gic = qdev_create(NULL, "arm_gic"); + /* TODO when the VGIC patches make it to Christoffer's kernel + * tree we can make !kvm_irqchip_in_kernel() a fatal error. + */ + if (kvm_irqchip_in_kernel()) { + s->gic = qdev_create(NULL, "kvm-arm_gic"); + } else { + s->gic = qdev_create(NULL, "arm_gic"); + } + 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 c413780..881fffd 100644 --- a/hw/arm/Makefile.objs +++ b/hw/arm/Makefile.objs @@ -39,5 +39,6 @@ obj-y += imx_serial.o imx_ccm.o imx_timer.o imx_avic.o obj-y += kzm.o obj-y += pl041.o lm4549.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..4535f90 --- /dev/null +++ b/hw/kvm/arm_gic.c @@ -0,0 +1,153 @@ +/* + * 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 . + */ + +#include "hw/sysbus.h" +#include "kvm.h" +#include "hw/arm_gic_internal.h" + +#define TYPE_KVM_ARM_GIC "kvm-arm_gic" +#define KVM_ARM_GIC(obj) \ + OBJECT_CHECK(gic_state, (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 + * ... + */ + gic_state *s = (gic_state *)opaque; + + + if (irq < (s->num_irq - GIC_INTERNAL)) { + /* External interrupt number 'irq' */ + kvm_set_irq(kvm_state, irq + GIC_INTERNAL, !!level); + } else { + struct kvm_irq_level irq_level; + int cpu; + irq -= (s->num_irq - GIC_INTERNAL); + cpu = irq / GIC_INTERNAL; + irq %= GIC_INTERNAL; + /* Internal interrupt 'irq' for CPU 'cpu' */ + irq_level.irq = irq; + irq_level.level = !!level; + kvm_vcpu_ioctl(qemu_get_cpu(cpu), KVM_IRQ_LINE, &irq_level); + } +} + +static void kvm_arm_gic_put(gic_state *s) +{ + /* TODO: there isn't currently a kernel interface to set the GIC state */ +} + +static void kvm_arm_gic_get(gic_state *s) +{ + /* TODO: there isn't currently a kernel interface to get the GIC state */ +} + +static void kvm_arm_gic_reset(DeviceState *dev) +{ + gic_state *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; + gic_state *s = FROM_SYSBUS(gic_state, 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); + /* 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]); + /* 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 TypeInfo arm_gic_info = { + .name = TYPE_KVM_ARM_GIC, + .parent = TYPE_ARM_GIC_COMMON, + .instance_size = sizeof(gic_state), + .class_init = kvm_arm_gic_class_init, +}; + +static void arm_gic_register_types(void) +{ + type_register_static(&arm_gic_info); +} + +type_init(arm_gic_register_types)