From patchwork Thu Aug 21 13:06:46 2014 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Andre Przywara X-Patchwork-Id: 4757541 Return-Path: X-Original-To: patchwork-kvm@patchwork.kernel.org Delivered-To: patchwork-parsemail@patchwork1.web.kernel.org Received: from mail.kernel.org (mail.kernel.org [198.145.19.201]) by patchwork1.web.kernel.org (Postfix) with ESMTP id 62A709F2E9 for ; Thu, 21 Aug 2014 13:08:11 +0000 (UTC) Received: from mail.kernel.org (localhost [127.0.0.1]) by mail.kernel.org (Postfix) with ESMTP id E1F7D2012F for ; Thu, 21 Aug 2014 13:08:09 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by mail.kernel.org (Postfix) with ESMTP id 2E87B20172 for ; Thu, 21 Aug 2014 13:08:08 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1755201AbaHUNH5 (ORCPT ); Thu, 21 Aug 2014 09:07:57 -0400 Received: from cam-admin0.cambridge.arm.com ([217.140.96.50]:63606 "EHLO cam-admin0.cambridge.arm.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1755143AbaHUNHd (ORCPT ); Thu, 21 Aug 2014 09:07:33 -0400 Received: from e106785-lin.cambridge.arm.com (e106785-lin.cambridge.arm.com [10.1.209.161]) by cam-admin0.cambridge.arm.com (8.12.6/8.12.6) with ESMTP id s7LD6uwt029486; Thu, 21 Aug 2014 14:06:57 +0100 (BST) From: Andre Przywara To: kvmarm@lists.cs.columbia.edu, linux-arm-kernel@lists.infradead.org, kvm@vger.kernel.org Cc: christoffer.dall@linaro.org, marc.zyngier@arm.com Subject: [PATCH v2 05/15] arm/arm64: KVM: introduce per-VM ops Date: Thu, 21 Aug 2014 14:06:46 +0100 Message-Id: <1408626416-11326-6-git-send-email-andre.przywara@arm.com> X-Mailer: git-send-email 1.7.9.5 In-Reply-To: <1408626416-11326-1-git-send-email-andre.przywara@arm.com> References: <1408626416-11326-1-git-send-email-andre.przywara@arm.com> Sender: kvm-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: kvm@vger.kernel.org X-Spam-Status: No, score=-7.6 required=5.0 tests=BAYES_00, RCVD_IN_DNSWL_HI, RP_MATCHES_RCVD, UNPARSEABLE_RELAY autolearn=unavailable version=3.3.1 X-Spam-Checker-Version: SpamAssassin 3.3.1 (2010-03-16) on mail.kernel.org X-Virus-Scanned: ClamAV using ClamSMTP Currently we only have one virtual GIC model supported, so all guests use the same emulation code. With the addition of another model we end up with different guests using potentially different vGIC models, so we have to split up some functions to be per VM. Introduce a vgic_vm_ops struct to hold function pointers for those functions that are different and provide the necessary code to initialize them. This includes functions that depend on the emulated GIC model only and functions that depend on the combination of host and guest GIC. Signed-off-by: Andre Przywara --- include/kvm/arm_vgic.h | 18 ++++++++-- virt/kvm/arm/vgic-v2.c | 17 +++++++-- virt/kvm/arm/vgic-v3.c | 16 +++++++-- virt/kvm/arm/vgic.c | 92 +++++++++++++++++++++++++++++++++++------------- 4 files changed, 113 insertions(+), 30 deletions(-) diff --git a/include/kvm/arm_vgic.h b/include/kvm/arm_vgic.h index 4feac9a..7e7c99e 100644 --- a/include/kvm/arm_vgic.h +++ b/include/kvm/arm_vgic.h @@ -99,8 +99,6 @@ struct vgic_vmcr { }; struct vgic_ops { - struct vgic_lr (*get_lr)(const struct kvm_vcpu *, int); - void (*set_lr)(struct kvm_vcpu *, int, struct vgic_lr); void (*sync_lr_elrsr)(struct kvm_vcpu *, int, struct vgic_lr); u64 (*get_elrsr)(const struct kvm_vcpu *vcpu); u64 (*get_eisr)(const struct kvm_vcpu *vcpu); @@ -123,6 +121,17 @@ struct vgic_params { unsigned int maint_irq; /* Virtual control interface base address */ void __iomem *vctrl_base; + bool (*init_emul)(struct kvm *kvm, int type); +}; + +struct vgic_vm_ops { + struct vgic_lr (*get_lr)(const struct kvm_vcpu *, int); + void (*set_lr)(struct kvm_vcpu *, int, struct vgic_lr); + bool (*handle_mmio)(struct kvm_vcpu *, struct kvm_run *, + struct kvm_exit_mmio *); + bool (*queue_sgi)(struct kvm_vcpu *vcpu, int irq); + void (*unqueue_sgi)(struct kvm_vcpu *vcpu, int irq, int source); + int (*vgic_init)(struct kvm *kvm, const struct vgic_params *params); }; struct vgic_dist { @@ -131,6 +140,9 @@ struct vgic_dist { bool in_kernel; bool ready; + /* vGIC model the kernel emulates for the guest (GICv2 or GICv3) */ + u32 vgic_model; + int nr_cpus; int nr_irqs; @@ -168,6 +180,8 @@ struct vgic_dist { /* Bitmap indicating which CPU has something pending */ unsigned long irq_pending_on_cpu; + + struct vgic_vm_ops vm_ops; #endif }; diff --git a/virt/kvm/arm/vgic-v2.c b/virt/kvm/arm/vgic-v2.c index 01124ef..90947c6 100644 --- a/virt/kvm/arm/vgic-v2.c +++ b/virt/kvm/arm/vgic-v2.c @@ -161,8 +161,6 @@ static void vgic_v2_enable(struct kvm_vcpu *vcpu) } static const struct vgic_ops vgic_v2_ops = { - .get_lr = vgic_v2_get_lr, - .set_lr = vgic_v2_set_lr, .sync_lr_elrsr = vgic_v2_sync_lr_elrsr, .get_elrsr = vgic_v2_get_elrsr, .get_eisr = vgic_v2_get_eisr, @@ -176,6 +174,20 @@ static const struct vgic_ops vgic_v2_ops = { static struct vgic_params vgic_v2_params; +static bool vgic_v2_init_emul(struct kvm *kvm, int type) +{ + struct vgic_vm_ops *vm_ops = &kvm->arch.vgic.vm_ops; + + switch (type) { + case KVM_DEV_TYPE_ARM_VGIC_V2: + vm_ops->get_lr = vgic_v2_get_lr; + vm_ops->set_lr = vgic_v2_set_lr; + return true; + } + + return false; +} + /** * vgic_v2_probe - probe for a GICv2 compatible interrupt controller in DT * @node: pointer to the DT node @@ -214,6 +226,7 @@ int vgic_v2_probe(struct device_node *vgic_node, ret = -ENOMEM; goto out; } + vgic->init_emul = vgic_v2_init_emul; vgic->nr_lr = readl_relaxed(vgic->vctrl_base + GICH_VTR); vgic->nr_lr = (vgic->nr_lr & 0x3f) + 1; diff --git a/virt/kvm/arm/vgic-v3.c b/virt/kvm/arm/vgic-v3.c index 1c2c8ee..a38339e 100644 --- a/virt/kvm/arm/vgic-v3.c +++ b/virt/kvm/arm/vgic-v3.c @@ -157,8 +157,6 @@ static void vgic_v3_enable(struct kvm_vcpu *vcpu) } static const struct vgic_ops vgic_v3_ops = { - .get_lr = vgic_v3_get_lr, - .set_lr = vgic_v3_set_lr, .sync_lr_elrsr = vgic_v3_sync_lr_elrsr, .get_elrsr = vgic_v3_get_elrsr, .get_eisr = vgic_v3_get_eisr, @@ -170,6 +168,19 @@ static const struct vgic_ops vgic_v3_ops = { .enable = vgic_v3_enable, }; +static bool vgic_v3_init_emul_compat(struct kvm *kvm, int type) +{ + struct vgic_vm_ops *vm_ops = &kvm->arch.vgic.vm_ops; + + switch (type) { + case KVM_DEV_TYPE_ARM_VGIC_V2: + vm_ops->get_lr = vgic_v3_get_lr; + vm_ops->set_lr = vgic_v3_set_lr; + return true; + } + return false; +} + static struct vgic_params vgic_v3_params; /** @@ -231,6 +242,7 @@ int vgic_v3_probe(struct device_node *vgic_node, goto out; } + vgic->init_emul = vgic_v3_init_emul_compat; vgic->vcpu_base = vcpu_res.start; vgic->vctrl_base = NULL; vgic->type = VGIC_V3; diff --git a/virt/kvm/arm/vgic.c b/virt/kvm/arm/vgic.c index bef9aa0..5e0bc24 100644 --- a/virt/kvm/arm/vgic.c +++ b/virt/kvm/arm/vgic.c @@ -99,6 +99,8 @@ static void vgic_set_vmcr(struct kvm_vcpu *vcpu, struct vgic_vmcr *vmcr); static const struct vgic_ops *vgic_ops; static const struct vgic_params *vgic; +#define vgic_vm_op(kvm, fn) ((kvm)->arch.vgic.vm_ops.fn) + /* * struct vgic_bitmap contains a bitmap of unsigned longs, but * extracts u32s out of them. @@ -668,11 +670,16 @@ static bool handle_mmio_sgi_reg(struct kvm_vcpu *vcpu, * to the distributor but the active state stays in the LRs, because we don't * track the active state on the distributor side. */ -static void vgic_unqueue_irqs(struct kvm_vcpu *vcpu) + +static void vgic_v2_unqueue_sgi(struct kvm_vcpu *vcpu, int irq, int source) { struct vgic_dist *dist = &vcpu->kvm->arch.vgic; + + *vgic_get_sgi_sources(dist, vcpu->vcpu_id, irq) |= 1 << source; +} +static void vgic_unqueue_irqs(struct kvm_vcpu *vcpu) +{ struct vgic_cpu *vgic_cpu = &vcpu->arch.vgic_cpu; - int vcpu_id = vcpu->vcpu_id; int i; for_each_set_bit(i, vgic_cpu->lr_used, vgic_cpu->nr_lr) { @@ -699,7 +706,8 @@ static void vgic_unqueue_irqs(struct kvm_vcpu *vcpu) */ vgic_dist_irq_set(vcpu, lr.irq); if (lr.irq < VGIC_NR_SGIS) - *vgic_get_sgi_sources(dist, vcpu_id, lr.irq) |= 1 << lr.source; + vgic_vm_op(vcpu->kvm, unqueue_sgi)(vcpu, lr.irq, + lr.source); lr.state &= ~LR_STATE_PENDING; vgic_set_lr(vcpu, i, lr); @@ -1050,7 +1058,7 @@ bool vgic_handle_mmio(struct kvm_vcpu *vcpu, struct kvm_run *run, if (!irqchip_in_kernel(vcpu->kvm)) return false; - return vgic_v2_handle_mmio(vcpu, run, mmio); + return vgic_vm_op(vcpu->kvm, handle_mmio)(vcpu, run, mmio); } static u8 *vgic_get_sgi_sources(struct vgic_dist *dist, int vcpu_id, int sgi) @@ -1158,13 +1166,13 @@ static void vgic_update_state(struct kvm *kvm) static struct vgic_lr vgic_get_lr(const struct kvm_vcpu *vcpu, int lr) { - return vgic_ops->get_lr(vcpu, lr); + return vgic_vm_op(vcpu->kvm, get_lr)(vcpu, lr); } static void vgic_set_lr(struct kvm_vcpu *vcpu, int lr, struct vgic_lr vlr) { - vgic_ops->set_lr(vcpu, lr, vlr); + return vgic_vm_op(vcpu->kvm, set_lr)(vcpu, lr, vlr); } static void vgic_sync_lr_elrsr(struct kvm_vcpu *vcpu, int lr, @@ -1302,7 +1310,7 @@ static bool vgic_queue_irq(struct kvm_vcpu *vcpu, u8 sgi_source_id, int irq) return true; } -static bool vgic_queue_sgi(struct kvm_vcpu *vcpu, int irq) +static bool vgic_v2_queue_sgi(struct kvm_vcpu *vcpu, int irq) { struct vgic_dist *dist = &vcpu->kvm->arch.vgic; unsigned long sources; @@ -1377,7 +1385,7 @@ static void __kvm_vgic_flush_hwstate(struct kvm_vcpu *vcpu) /* SGIs */ for_each_set_bit(i, vgic_cpu->pending_percpu, VGIC_NR_SGIS) { - if (!vgic_queue_sgi(vcpu, i)) + if (!vgic_vm_op(vcpu->kvm, queue_sgi)(vcpu, i)) overflow = 1; } @@ -1873,9 +1881,6 @@ static int vgic_init_maps(struct kvm *kvm) } } - for (i = VGIC_NR_PRIVATE_IRQS; i < dist->nr_irqs; i += 4) - vgic_set_target_reg(kvm, 0, i); - out: if (ret) kvm_vgic_destroy(kvm); @@ -1883,6 +1888,31 @@ out: return ret; } +static int vgic_v2_init(struct kvm *kvm, const struct vgic_params *params) +{ + struct vgic_dist *dist = &kvm->arch.vgic; + int ret, i; + + if (IS_VGIC_ADDR_UNDEF(dist->vgic_dist_base) || + IS_VGIC_ADDR_UNDEF(dist->vgic_cpu_base)) { + kvm_err("Need to set vgic distributor addresses first\n"); + return -ENXIO; + } + + ret = kvm_phys_addr_ioremap(kvm, dist->vgic_cpu_base, + params->vcpu_base, + KVM_VGIC_V2_CPU_SIZE); + if (ret) { + kvm_err("Unable to remap VGIC CPU to VCPU\n"); + return ret; + } + + for (i = VGIC_NR_PRIVATE_IRQS; i < dist->nr_irqs; i += 4) + vgic_set_target_reg(kvm, 0, i); + + return 0; +} + /** * kvm_vgic_init - Initialize global VGIC state before running any VCPUs * @kvm: pointer to the kvm struct @@ -1905,25 +1935,15 @@ int kvm_vgic_init(struct kvm *kvm) if (vgic_initialized(kvm)) goto out; - if (IS_VGIC_ADDR_UNDEF(kvm->arch.vgic.vgic_dist_base) || - IS_VGIC_ADDR_UNDEF(kvm->arch.vgic.vgic_cpu_base)) { - kvm_err("Need to set vgic cpu and dist addresses first\n"); - ret = -ENXIO; - goto out; - } - ret = vgic_init_maps(kvm); if (ret) { kvm_err("Unable to allocate maps\n"); goto out; } - ret = kvm_phys_addr_ioremap(kvm, kvm->arch.vgic.vgic_cpu_base, - vgic->vcpu_base, KVM_VGIC_V2_CPU_SIZE); - if (ret) { - kvm_err("Unable to remap VGIC CPU to VCPU\n"); + ret = vgic_vm_op(kvm, vgic_init)(kvm, vgic); + if (ret) goto out; - } kvm_for_each_vcpu(i, vcpu, kvm) kvm_vgic_vcpu_init(vcpu); @@ -1936,6 +1956,21 @@ out: return ret; } +static bool init_emulation_ops(struct kvm *kvm, int type) +{ + struct vgic_dist *dist = &kvm->arch.vgic; + + switch (type) { + case KVM_DEV_TYPE_ARM_VGIC_V2: + dist->vm_ops.handle_mmio = vgic_v2_handle_mmio; + dist->vm_ops.queue_sgi = vgic_v2_queue_sgi; + dist->vm_ops.unqueue_sgi = vgic_v2_unqueue_sgi; + dist->vm_ops.vgic_init = vgic_v2_init; + return true; + } + return false; +} + int kvm_vgic_create(struct kvm *kvm, u32 type) { int i, vcpu_lock_idx = -1, ret = 0; @@ -1943,7 +1978,7 @@ int kvm_vgic_create(struct kvm *kvm, u32 type) mutex_lock(&kvm->lock); - if (kvm->arch.vgic.vctrl_base) { + if (kvm->arch.vgic.in_kernel) { ret = -EEXIST; goto out; } @@ -1966,12 +2001,21 @@ int kvm_vgic_create(struct kvm *kvm, u32 type) } } + if (!vgic->init_emul(kvm, type)) { + ret = -ENODEV; + goto out_unlock; + } + spin_lock_init(&kvm->arch.vgic.lock); kvm->arch.vgic.in_kernel = true; + kvm->arch.vgic.vgic_model = type; kvm->arch.vgic.vctrl_base = vgic->vctrl_base; kvm->arch.vgic.vgic_dist_base = VGIC_ADDR_UNDEF; kvm->arch.vgic.vgic_cpu_base = VGIC_ADDR_UNDEF; + if (!init_emulation_ops(kvm, type)) + ret = -ENODEV; + out_unlock: for (; vcpu_lock_idx >= 0; vcpu_lock_idx--) { vcpu = kvm_get_vcpu(kvm, vcpu_lock_idx);