aboutsummaryrefslogtreecommitdiffstats
path: root/virt
diff options
context:
space:
mode:
authorEric Auger <eric.auger@linaro.org>2015-12-01 09:02:35 -0500
committerChristoffer Dall <christoffer.dall@linaro.org>2016-05-20 09:40:05 -0400
commit909777324588b40d431e6e3af0911ee62e0d00e3 (patch)
tree90c53aef39980fca19e84de6f070f65e41cf626d /virt
parent878c569e45066a76a2a841dab965e6d22c4e187e (diff)
KVM: arm/arm64: vgic-new: vgic_init: implement kvm_vgic_hyp_init
Implements kvm_vgic_hyp_init and vgic_probe function. This uses the new firmware independent VGIC probing to support both ACPI and DT based systems (code from Marc Zyngier). The vgic_global struct is enriched with new fields populated by those functions. Signed-off-by: Eric Auger <eric.auger@linaro.org> Signed-off-by: Andre Przywara <andre.przywara@arm.com> Reviewed-by: Christoffer Dall <christoffer.dall@linaro.org>
Diffstat (limited to 'virt')
-rw-r--r--virt/kvm/arm/vgic/vgic-init.c123
-rw-r--r--virt/kvm/arm/vgic/vgic-v2.c64
-rw-r--r--virt/kvm/arm/vgic/vgic-v3.c49
-rw-r--r--virt/kvm/arm/vgic/vgic.h9
4 files changed, 245 insertions, 0 deletions
diff --git a/virt/kvm/arm/vgic/vgic-init.c b/virt/kvm/arm/vgic/vgic-init.c
new file mode 100644
index 000000000000..4523beb029f3
--- /dev/null
+++ b/virt/kvm/arm/vgic/vgic-init.c
@@ -0,0 +1,123 @@
1/*
2 * Copyright (C) 2015, 2016 ARM Ltd.
3 *
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License version 2 as
6 * published by the Free Software Foundation.
7 *
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU General Public License for more details.
12 *
13 * You should have received a copy of the GNU General Public License
14 * along with this program. If not, see <http://www.gnu.org/licenses/>.
15 */
16
17#include <linux/uaccess.h>
18#include <linux/interrupt.h>
19#include <linux/cpu.h>
20#include <linux/kvm_host.h>
21#include <kvm/arm_vgic.h>
22#include <asm/kvm_mmu.h>
23#include "vgic.h"
24
25/* GENERIC PROBE */
26
27static void vgic_init_maintenance_interrupt(void *info)
28{
29 enable_percpu_irq(kvm_vgic_global_state.maint_irq, 0);
30}
31
32static int vgic_cpu_notify(struct notifier_block *self,
33 unsigned long action, void *cpu)
34{
35 switch (action) {
36 case CPU_STARTING:
37 case CPU_STARTING_FROZEN:
38 vgic_init_maintenance_interrupt(NULL);
39 break;
40 case CPU_DYING:
41 case CPU_DYING_FROZEN:
42 disable_percpu_irq(kvm_vgic_global_state.maint_irq);
43 break;
44 }
45
46 return NOTIFY_OK;
47}
48
49static struct notifier_block vgic_cpu_nb = {
50 .notifier_call = vgic_cpu_notify,
51};
52
53static irqreturn_t vgic_maintenance_handler(int irq, void *data)
54{
55 /*
56 * We cannot rely on the vgic maintenance interrupt to be
57 * delivered synchronously. This means we can only use it to
58 * exit the VM, and we perform the handling of EOIed
59 * interrupts on the exit path (see vgic_process_maintenance).
60 */
61 return IRQ_HANDLED;
62}
63
64/**
65 * kvm_vgic_hyp_init: populates the kvm_vgic_global_state variable
66 * according to the host GIC model. Accordingly calls either
67 * vgic_v2/v3_probe which registers the KVM_DEVICE that can be
68 * instantiated by a guest later on .
69 */
70int kvm_vgic_hyp_init(void)
71{
72 const struct gic_kvm_info *gic_kvm_info;
73 int ret;
74
75 gic_kvm_info = gic_get_kvm_info();
76 if (!gic_kvm_info)
77 return -ENODEV;
78
79 if (!gic_kvm_info->maint_irq) {
80 kvm_err("No vgic maintenance irq\n");
81 return -ENXIO;
82 }
83
84 switch (gic_kvm_info->type) {
85 case GIC_V2:
86 ret = vgic_v2_probe(gic_kvm_info);
87 break;
88 case GIC_V3:
89 ret = vgic_v3_probe(gic_kvm_info);
90 break;
91 default:
92 ret = -ENODEV;
93 };
94
95 if (ret)
96 return ret;
97
98 kvm_vgic_global_state.maint_irq = gic_kvm_info->maint_irq;
99 ret = request_percpu_irq(kvm_vgic_global_state.maint_irq,
100 vgic_maintenance_handler,
101 "vgic", kvm_get_running_vcpus());
102 if (ret) {
103 kvm_err("Cannot register interrupt %d\n",
104 kvm_vgic_global_state.maint_irq);
105 return ret;
106 }
107
108 ret = __register_cpu_notifier(&vgic_cpu_nb);
109 if (ret) {
110 kvm_err("Cannot register vgic CPU notifier\n");
111 goto out_free_irq;
112 }
113
114 on_each_cpu(vgic_init_maintenance_interrupt, NULL, 1);
115
116 kvm_info("vgic interrupt IRQ%d\n", kvm_vgic_global_state.maint_irq);
117 return 0;
118
119out_free_irq:
120 free_percpu_irq(kvm_vgic_global_state.maint_irq,
121 kvm_get_running_vcpus());
122 return ret;
123}
diff --git a/virt/kvm/arm/vgic/vgic-v2.c b/virt/kvm/arm/vgic/vgic-v2.c
index d943059ee6c5..09777c852b16 100644
--- a/virt/kvm/arm/vgic/vgic-v2.c
+++ b/virt/kvm/arm/vgic/vgic-v2.c
@@ -17,6 +17,8 @@
17#include <linux/irqchip/arm-gic.h> 17#include <linux/irqchip/arm-gic.h>
18#include <linux/kvm.h> 18#include <linux/kvm.h>
19#include <linux/kvm_host.h> 19#include <linux/kvm_host.h>
20#include <kvm/arm_vgic.h>
21#include <asm/kvm_mmu.h>
20 22
21#include "vgic.h" 23#include "vgic.h"
22 24
@@ -203,3 +205,65 @@ void vgic_v2_get_vmcr(struct kvm_vcpu *vcpu, struct vgic_vmcr *vmcrp)
203 vmcrp->pmr = (vmcr & GICH_VMCR_PRIMASK_MASK) >> 205 vmcrp->pmr = (vmcr & GICH_VMCR_PRIMASK_MASK) >>
204 GICH_VMCR_PRIMASK_SHIFT; 206 GICH_VMCR_PRIMASK_SHIFT;
205} 207}
208
209/**
210 * vgic_v2_probe - probe for a GICv2 compatible interrupt controller in DT
211 * @node: pointer to the DT node
212 *
213 * Returns 0 if a GICv2 has been found, returns an error code otherwise
214 */
215int vgic_v2_probe(const struct gic_kvm_info *info)
216{
217 int ret;
218 u32 vtr;
219
220 if (!info->vctrl.start) {
221 kvm_err("GICH not present in the firmware table\n");
222 return -ENXIO;
223 }
224
225 if (!PAGE_ALIGNED(info->vcpu.start)) {
226 kvm_err("GICV physical address 0x%llx not page aligned\n",
227 (unsigned long long)info->vcpu.start);
228 return -ENXIO;
229 }
230
231 if (!PAGE_ALIGNED(resource_size(&info->vcpu))) {
232 kvm_err("GICV size 0x%llx not a multiple of page size 0x%lx\n",
233 (unsigned long long)resource_size(&info->vcpu),
234 PAGE_SIZE);
235 return -ENXIO;
236 }
237
238 kvm_vgic_global_state.vctrl_base = ioremap(info->vctrl.start,
239 resource_size(&info->vctrl));
240 if (!kvm_vgic_global_state.vctrl_base) {
241 kvm_err("Cannot ioremap GICH\n");
242 return -ENOMEM;
243 }
244
245 vtr = readl_relaxed(kvm_vgic_global_state.vctrl_base + GICH_VTR);
246 kvm_vgic_global_state.nr_lr = (vtr & 0x3f) + 1;
247
248 ret = create_hyp_io_mappings(kvm_vgic_global_state.vctrl_base,
249 kvm_vgic_global_state.vctrl_base +
250 resource_size(&info->vctrl),
251 info->vctrl.start);
252
253 if (ret) {
254 kvm_err("Cannot map VCTRL into hyp\n");
255 iounmap(kvm_vgic_global_state.vctrl_base);
256 return ret;
257 }
258
259 kvm_vgic_global_state.can_emulate_gicv2 = true;
260 kvm_register_vgic_device(KVM_DEV_TYPE_ARM_VGIC_V2);
261
262 kvm_vgic_global_state.vcpu_base = info->vcpu.start;
263 kvm_vgic_global_state.type = VGIC_V2;
264 kvm_vgic_global_state.max_gic_vcpus = VGIC_V2_MAX_CPUS;
265
266 kvm_info("vgic-v2@%llx\n", info->vctrl.start);
267
268 return 0;
269}
diff --git a/virt/kvm/arm/vgic/vgic-v3.c b/virt/kvm/arm/vgic/vgic-v3.c
index 8548297c6f76..de0e8e0b2625 100644
--- a/virt/kvm/arm/vgic/vgic-v3.c
+++ b/virt/kvm/arm/vgic/vgic-v3.c
@@ -15,6 +15,9 @@
15#include <linux/irqchip/arm-gic-v3.h> 15#include <linux/irqchip/arm-gic-v3.h>
16#include <linux/kvm.h> 16#include <linux/kvm.h>
17#include <linux/kvm_host.h> 17#include <linux/kvm_host.h>
18#include <kvm/arm_vgic.h>
19#include <asm/kvm_mmu.h>
20#include <asm/kvm_asm.h>
18 21
19#include "vgic.h" 22#include "vgic.h"
20 23
@@ -182,3 +185,49 @@ void vgic_v3_get_vmcr(struct kvm_vcpu *vcpu, struct vgic_vmcr *vmcrp)
182 vmcrp->bpr = (vmcr & ICH_VMCR_BPR0_MASK) >> ICH_VMCR_BPR0_SHIFT; 185 vmcrp->bpr = (vmcr & ICH_VMCR_BPR0_MASK) >> ICH_VMCR_BPR0_SHIFT;
183 vmcrp->pmr = (vmcr & ICH_VMCR_PMR_MASK) >> ICH_VMCR_PMR_SHIFT; 186 vmcrp->pmr = (vmcr & ICH_VMCR_PMR_MASK) >> ICH_VMCR_PMR_SHIFT;
184} 187}
188
189/**
190 * vgic_v3_probe - probe for a GICv3 compatible interrupt controller in DT
191 * @node: pointer to the DT node
192 *
193 * Returns 0 if a GICv3 has been found, returns an error code otherwise
194 */
195int vgic_v3_probe(const struct gic_kvm_info *info)
196{
197 u32 ich_vtr_el2 = kvm_call_hyp(__vgic_v3_get_ich_vtr_el2);
198
199 /*
200 * The ListRegs field is 5 bits, but there is a architectural
201 * maximum of 16 list registers. Just ignore bit 4...
202 */
203 kvm_vgic_global_state.nr_lr = (ich_vtr_el2 & 0xf) + 1;
204 kvm_vgic_global_state.can_emulate_gicv2 = false;
205
206 if (!info->vcpu.start) {
207 kvm_info("GICv3: no GICV resource entry\n");
208 kvm_vgic_global_state.vcpu_base = 0;
209 } else if (!PAGE_ALIGNED(info->vcpu.start)) {
210 pr_warn("GICV physical address 0x%llx not page aligned\n",
211 (unsigned long long)info->vcpu.start);
212 kvm_vgic_global_state.vcpu_base = 0;
213 } else if (!PAGE_ALIGNED(resource_size(&info->vcpu))) {
214 pr_warn("GICV size 0x%llx not a multiple of page size 0x%lx\n",
215 (unsigned long long)resource_size(&info->vcpu),
216 PAGE_SIZE);
217 kvm_vgic_global_state.vcpu_base = 0;
218 } else {
219 kvm_vgic_global_state.vcpu_base = info->vcpu.start;
220 kvm_vgic_global_state.can_emulate_gicv2 = true;
221 kvm_register_vgic_device(KVM_DEV_TYPE_ARM_VGIC_V2);
222 kvm_info("vgic-v2@%llx\n", info->vcpu.start);
223 }
224 if (kvm_vgic_global_state.vcpu_base == 0)
225 kvm_info("disabling GICv2 emulation\n");
226 kvm_register_vgic_device(KVM_DEV_TYPE_ARM_VGIC_V3);
227
228 kvm_vgic_global_state.vctrl_base = NULL;
229 kvm_vgic_global_state.type = VGIC_V3;
230 kvm_vgic_global_state.max_gic_vcpus = VGIC_V3_MAX_CPUS;
231
232 return 0;
233}
diff --git a/virt/kvm/arm/vgic/vgic.h b/virt/kvm/arm/vgic/vgic.h
index de9dc7170c1b..f4244b6eb4b8 100644
--- a/virt/kvm/arm/vgic/vgic.h
+++ b/virt/kvm/arm/vgic/vgic.h
@@ -16,6 +16,8 @@
16#ifndef __KVM_ARM_VGIC_NEW_H__ 16#ifndef __KVM_ARM_VGIC_NEW_H__
17#define __KVM_ARM_VGIC_NEW_H__ 17#define __KVM_ARM_VGIC_NEW_H__
18 18
19#include <linux/irqchip/arm-gic-common.h>
20
19#define PRODUCT_ID_KVM 0x4b /* ASCII code K */ 21#define PRODUCT_ID_KVM 0x4b /* ASCII code K */
20#define IMPLEMENTER_ARM 0x43b 22#define IMPLEMENTER_ARM 0x43b
21 23
@@ -51,6 +53,7 @@ int vgic_v2_cpuif_uaccess(struct kvm_vcpu *vcpu, bool is_write,
51 int offset, u32 *val); 53 int offset, u32 *val);
52void vgic_v2_set_vmcr(struct kvm_vcpu *vcpu, struct vgic_vmcr *vmcr); 54void vgic_v2_set_vmcr(struct kvm_vcpu *vcpu, struct vgic_vmcr *vmcr);
53void vgic_v2_get_vmcr(struct kvm_vcpu *vcpu, struct vgic_vmcr *vmcr); 55void vgic_v2_get_vmcr(struct kvm_vcpu *vcpu, struct vgic_vmcr *vmcr);
56int vgic_v2_probe(const struct gic_kvm_info *info);
54int vgic_register_dist_iodev(struct kvm *kvm, gpa_t dist_base_address, 57int vgic_register_dist_iodev(struct kvm *kvm, gpa_t dist_base_address,
55 enum vgic_type); 58 enum vgic_type);
56 59
@@ -62,6 +65,7 @@ void vgic_v3_clear_lr(struct kvm_vcpu *vcpu, int lr);
62void vgic_v3_set_underflow(struct kvm_vcpu *vcpu); 65void vgic_v3_set_underflow(struct kvm_vcpu *vcpu);
63void vgic_v3_set_vmcr(struct kvm_vcpu *vcpu, struct vgic_vmcr *vmcr); 66void vgic_v3_set_vmcr(struct kvm_vcpu *vcpu, struct vgic_vmcr *vmcr);
64void vgic_v3_get_vmcr(struct kvm_vcpu *vcpu, struct vgic_vmcr *vmcr); 67void vgic_v3_get_vmcr(struct kvm_vcpu *vcpu, struct vgic_vmcr *vmcr);
68int vgic_v3_probe(const struct gic_kvm_info *info);
65int vgic_register_redist_iodevs(struct kvm *kvm, gpa_t dist_base_address); 69int vgic_register_redist_iodevs(struct kvm *kvm, gpa_t dist_base_address);
66#else 70#else
67static inline void vgic_v3_process_maintenance(struct kvm_vcpu *vcpu) 71static inline void vgic_v3_process_maintenance(struct kvm_vcpu *vcpu)
@@ -95,6 +99,11 @@ void vgic_v3_get_vmcr(struct kvm_vcpu *vcpu, struct vgic_vmcr *vmcr)
95{ 99{
96} 100}
97 101
102static inline int vgic_v3_probe(const struct gic_kvm_info *info)
103{
104 return -ENODEV;
105}
106
98static inline int vgic_register_redist_iodevs(struct kvm *kvm, 107static inline int vgic_register_redist_iodevs(struct kvm *kvm,
99 gpa_t dist_base_address) 108 gpa_t dist_base_address)
100{ 109{