Skip to content

Commit 1f315e9

Browse files
committed
Merge branch 'kvm-arm64/gcie-legacy' into kvmarm/next
* kvm-arm64/gcie-legacy: : Support for GICv3 emulation on GICv5, courtesy of Sascha Bischoff : : FEAT_GCIE_LEGACY adds the necessary hardware for GICv5 systems to : support the legacy GICv3 for VMs, including a backwards-compatible VGIC : implementation that we all know and love. : : As a starting point for GICv5 enablement in KVM, enable + use the : GICv3-compatible feature when running VMs on GICv5 hardware. KVM: arm64: gic-v5: Probe for GICv5 KVM: arm64: gic-v5: Support GICv3 compat arm64/sysreg: Add ICH_VCTLR_EL2 irqchip/gic-v5: Populate struct gic_kvm_info irqchip/gic-v5: Skip deactivate for forwarded PPI interrupts Signed-off-by: Oliver Upton <[email protected]>
2 parents ccd73c5 + ff2aa64 commit 1f315e9

File tree

10 files changed

+191
-13
lines changed

10 files changed

+191
-13
lines changed

arch/arm64/kvm/Makefile

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,8 @@ kvm-y += arm.o mmu.o mmio.o psci.o hypercalls.o pvtime.o \
2323
vgic/vgic-v3.o vgic/vgic-v4.o \
2424
vgic/vgic-mmio.o vgic/vgic-mmio-v2.o \
2525
vgic/vgic-mmio-v3.o vgic/vgic-kvm-device.o \
26-
vgic/vgic-its.o vgic/vgic-debug.o vgic/vgic-v3-nested.o
26+
vgic/vgic-its.o vgic/vgic-debug.o vgic/vgic-v3-nested.o \
27+
vgic/vgic-v5.o
2728

2829
kvm-$(CONFIG_HW_PERF_EVENTS) += pmu-emul.o pmu.o
2930
kvm-$(CONFIG_ARM64_PTR_AUTH) += pauth.o

arch/arm64/kvm/hyp/vgic-v3-sr.c

Lines changed: 43 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -296,12 +296,19 @@ void __vgic_v3_activate_traps(struct vgic_v3_cpu_if *cpu_if)
296296
}
297297

298298
/*
299-
* Prevent the guest from touching the ICC_SRE_EL1 system
300-
* register. Note that this may not have any effect, as
301-
* ICC_SRE_EL2.Enable being RAO/WI is a valid implementation.
299+
* GICv5 BET0 FEAT_GCIE_LEGACY doesn't include ICC_SRE_EL2. This is due
300+
* to be relaxed in a future spec release, at which point this in
301+
* condition can be dropped.
302302
*/
303-
write_gicreg(read_gicreg(ICC_SRE_EL2) & ~ICC_SRE_EL2_ENABLE,
304-
ICC_SRE_EL2);
303+
if (!cpus_have_final_cap(ARM64_HAS_GICV5_CPUIF)) {
304+
/*
305+
* Prevent the guest from touching the ICC_SRE_EL1 system
306+
* register. Note that this may not have any effect, as
307+
* ICC_SRE_EL2.Enable being RAO/WI is a valid implementation.
308+
*/
309+
write_gicreg(read_gicreg(ICC_SRE_EL2) & ~ICC_SRE_EL2_ENABLE,
310+
ICC_SRE_EL2);
311+
}
305312

306313
/*
307314
* If we need to trap system registers, we must write
@@ -322,8 +329,14 @@ void __vgic_v3_deactivate_traps(struct vgic_v3_cpu_if *cpu_if)
322329
cpu_if->vgic_vmcr = read_gicreg(ICH_VMCR_EL2);
323330
}
324331

325-
val = read_gicreg(ICC_SRE_EL2);
326-
write_gicreg(val | ICC_SRE_EL2_ENABLE, ICC_SRE_EL2);
332+
/*
333+
* Can be dropped in the future when GICv5 spec is relaxed. See comment
334+
* above.
335+
*/
336+
if (!cpus_have_final_cap(ARM64_HAS_GICV5_CPUIF)) {
337+
val = read_gicreg(ICC_SRE_EL2);
338+
write_gicreg(val | ICC_SRE_EL2_ENABLE, ICC_SRE_EL2);
339+
}
327340

328341
if (!cpu_if->vgic_sre) {
329342
/* Make sure ENABLE is set at EL2 before setting SRE at EL1 */
@@ -423,9 +436,19 @@ void __vgic_v3_init_lrs(void)
423436
*/
424437
u64 __vgic_v3_get_gic_config(void)
425438
{
426-
u64 val, sre = read_gicreg(ICC_SRE_EL1);
439+
u64 val, sre;
427440
unsigned long flags = 0;
428441

442+
/*
443+
* In compat mode, we cannot access ICC_SRE_EL1 at any EL
444+
* other than EL1 itself; just return the
445+
* ICH_VTR_EL2. ICC_IDR0_EL1 is only implemented on a GICv5
446+
* system, so we first check if we have GICv5 support.
447+
*/
448+
if (cpus_have_final_cap(ARM64_HAS_GICV5_CPUIF))
449+
return read_gicreg(ICH_VTR_EL2);
450+
451+
sre = read_gicreg(ICC_SRE_EL1);
429452
/*
430453
* To check whether we have a MMIO-based (GICv2 compatible)
431454
* CPU interface, we need to disable the system register
@@ -471,6 +494,16 @@ u64 __vgic_v3_get_gic_config(void)
471494
return val;
472495
}
473496

497+
static void __vgic_v3_compat_mode_enable(void)
498+
{
499+
if (!cpus_have_final_cap(ARM64_HAS_GICV5_CPUIF))
500+
return;
501+
502+
sysreg_clear_set_s(SYS_ICH_VCTLR_EL2, 0, ICH_VCTLR_EL2_V3);
503+
/* Wait for V3 to become enabled */
504+
isb();
505+
}
506+
474507
static u64 __vgic_v3_read_vmcr(void)
475508
{
476509
return read_gicreg(ICH_VMCR_EL2);
@@ -490,6 +523,8 @@ void __vgic_v3_save_vmcr_aprs(struct vgic_v3_cpu_if *cpu_if)
490523

491524
void __vgic_v3_restore_vmcr_aprs(struct vgic_v3_cpu_if *cpu_if)
492525
{
526+
__vgic_v3_compat_mode_enable();
527+
493528
/*
494529
* If dealing with a GICv2 emulation on GICv3, VMCR_EL2.VFIQen
495530
* is dependent on ICC_SRE_EL1.SRE, and we have to perform the

arch/arm64/kvm/sys_regs.c

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1813,7 +1813,7 @@ static u64 sanitise_id_aa64pfr0_el1(const struct kvm_vcpu *vcpu, u64 val)
18131813
val |= SYS_FIELD_PREP_ENUM(ID_AA64PFR0_EL1, CSV3, IMP);
18141814
}
18151815

1816-
if (kvm_vgic_global_state.type == VGIC_V3) {
1816+
if (vgic_is_v3(vcpu->kvm)) {
18171817
val &= ~ID_AA64PFR0_EL1_GIC_MASK;
18181818
val |= SYS_FIELD_PREP_ENUM(ID_AA64PFR0_EL1, GIC, IMP);
18191819
}
@@ -1955,6 +1955,14 @@ static int set_id_aa64pfr0_el1(struct kvm_vcpu *vcpu,
19551955
(vcpu_has_nv(vcpu) && !FIELD_GET(ID_AA64PFR0_EL1_EL2, user_val)))
19561956
return -EINVAL;
19571957

1958+
/*
1959+
* If we are running on a GICv5 host and support FEAT_GCIE_LEGACY, then
1960+
* we support GICv3. Fail attempts to do anything but set that to IMP.
1961+
*/
1962+
if (vgic_is_v3_compat(vcpu->kvm) &&
1963+
FIELD_GET(ID_AA64PFR0_EL1_GIC_MASK, user_val) != ID_AA64PFR0_EL1_GIC_IMP)
1964+
return -EINVAL;
1965+
19581966
return set_id_reg(vcpu, rd, user_val);
19591967
}
19601968

arch/arm64/kvm/vgic/vgic-init.c

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -674,10 +674,12 @@ void kvm_vgic_init_cpu_hardware(void)
674674
* We want to make sure the list registers start out clear so that we
675675
* only have the program the used registers.
676676
*/
677-
if (kvm_vgic_global_state.type == VGIC_V2)
677+
if (kvm_vgic_global_state.type == VGIC_V2) {
678678
vgic_v2_init_lrs();
679-
else
679+
} else if (kvm_vgic_global_state.type == VGIC_V3 ||
680+
kvm_vgic_global_state.has_gcie_v3_compat) {
680681
kvm_call_hyp(__vgic_v3_init_lrs);
682+
}
681683
}
682684

683685
/**
@@ -722,6 +724,9 @@ int kvm_vgic_hyp_init(void)
722724
kvm_info("GIC system register CPU interface enabled\n");
723725
}
724726
break;
727+
case GIC_V5:
728+
ret = vgic_v5_probe(gic_kvm_info);
729+
break;
725730
default:
726731
ret = -ENODEV;
727732
}

arch/arm64/kvm/vgic/vgic-v5.c

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
// SPDX-License-Identifier: GPL-2.0-only
2+
3+
#include <kvm/arm_vgic.h>
4+
#include <linux/irqchip/arm-vgic-info.h>
5+
6+
#include "vgic.h"
7+
8+
/*
9+
* Probe for a vGICv5 compatible interrupt controller, returning 0 on success.
10+
* Currently only supports GICv3-based VMs on a GICv5 host, and hence only
11+
* registers a VGIC_V3 device.
12+
*/
13+
int vgic_v5_probe(const struct gic_kvm_info *info)
14+
{
15+
u64 ich_vtr_el2;
16+
int ret;
17+
18+
if (!info->has_gcie_v3_compat)
19+
return -ENODEV;
20+
21+
kvm_vgic_global_state.type = VGIC_V5;
22+
kvm_vgic_global_state.has_gcie_v3_compat = true;
23+
24+
/* We only support v3 compat mode - use vGICv3 limits */
25+
kvm_vgic_global_state.max_gic_vcpus = VGIC_V3_MAX_CPUS;
26+
27+
kvm_vgic_global_state.vcpu_base = 0;
28+
kvm_vgic_global_state.vctrl_base = NULL;
29+
kvm_vgic_global_state.can_emulate_gicv2 = false;
30+
kvm_vgic_global_state.has_gicv4 = false;
31+
kvm_vgic_global_state.has_gicv4_1 = false;
32+
33+
ich_vtr_el2 = kvm_call_hyp_ret(__vgic_v3_get_gic_config);
34+
kvm_vgic_global_state.ich_vtr_el2 = (u32)ich_vtr_el2;
35+
36+
/*
37+
* The ListRegs field is 5 bits, but there is an architectural
38+
* maximum of 16 list registers. Just ignore bit 4...
39+
*/
40+
kvm_vgic_global_state.nr_lr = (ich_vtr_el2 & 0xf) + 1;
41+
42+
ret = kvm_register_vgic_device(KVM_DEV_TYPE_ARM_VGIC_V3);
43+
if (ret) {
44+
kvm_err("Cannot register GICv3-legacy KVM device.\n");
45+
return ret;
46+
}
47+
48+
static_branch_enable(&kvm_vgic_global_state.gicv3_cpuif);
49+
kvm_info("GCIE legacy system register CPU interface\n");
50+
51+
return 0;
52+
}

arch/arm64/kvm/vgic/vgic.h

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -308,6 +308,8 @@ int vgic_init(struct kvm *kvm);
308308
void vgic_debug_init(struct kvm *kvm);
309309
void vgic_debug_destroy(struct kvm *kvm);
310310

311+
int vgic_v5_probe(const struct gic_kvm_info *info);
312+
311313
static inline int vgic_v3_max_apr_idx(struct kvm_vcpu *vcpu)
312314
{
313315
struct vgic_cpu *cpu_if = &vcpu->arch.vgic_cpu;
@@ -389,6 +391,17 @@ void vgic_v3_put_nested(struct kvm_vcpu *vcpu);
389391
void vgic_v3_handle_nested_maint_irq(struct kvm_vcpu *vcpu);
390392
void vgic_v3_nested_update_mi(struct kvm_vcpu *vcpu);
391393

394+
static inline bool vgic_is_v3_compat(struct kvm *kvm)
395+
{
396+
return cpus_have_final_cap(ARM64_HAS_GICV5_CPUIF) &&
397+
kvm_vgic_global_state.has_gcie_v3_compat;
398+
}
399+
400+
static inline bool vgic_is_v3(struct kvm *kvm)
401+
{
402+
return kvm_vgic_global_state.type == VGIC_V3 || vgic_is_v3_compat(kvm);
403+
}
404+
392405
int vgic_its_debug_init(struct kvm_device *dev);
393406
void vgic_its_debug_destroy(struct kvm_device *dev);
394407

arch/arm64/tools/sysreg

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4530,6 +4530,12 @@ Field 1 U
45304530
Field 0 EOI
45314531
EndSysreg
45324532

4533+
Sysreg ICH_VCTLR_EL2 3 4 12 11 4
4534+
Res0 63:2
4535+
Field 1 V3
4536+
Field 0 En
4537+
EndSysreg
4538+
45334539
Sysreg CONTEXTIDR_EL2 3 4 13 0 1
45344540
Fields CONTEXTIDR_ELx
45354541
EndSysreg

drivers/irqchip/irq-gic-v5.c

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313

1414
#include <linux/irqchip.h>
1515
#include <linux/irqchip/arm-gic-v5.h>
16+
#include <linux/irqchip/arm-vgic-info.h>
1617

1718
#include <asm/cpufeature.h>
1819
#include <asm/exception.h>
@@ -222,6 +223,12 @@ static void gicv5_hwirq_eoi(u32 hwirq_id, u8 hwirq_type)
222223

223224
static void gicv5_ppi_irq_eoi(struct irq_data *d)
224225
{
226+
/* Skip deactivate for forwarded PPI interrupts */
227+
if (irqd_is_forwarded_to_vcpu(d)) {
228+
gic_insn(0, CDEOI);
229+
return;
230+
}
231+
225232
gicv5_hwirq_eoi(d->hwirq, GICV5_HWIRQ_TYPE_PPI);
226233
}
227234

@@ -503,13 +510,24 @@ static bool gicv5_ppi_irq_is_level(irq_hw_number_t hwirq)
503510
return !!(read_ppi_sysreg_s(hwirq, PPI_HM) & bit);
504511
}
505512

513+
static int gicv5_ppi_irq_set_vcpu_affinity(struct irq_data *d, void *vcpu)
514+
{
515+
if (vcpu)
516+
irqd_set_forwarded_to_vcpu(d);
517+
else
518+
irqd_clr_forwarded_to_vcpu(d);
519+
520+
return 0;
521+
}
522+
506523
static const struct irq_chip gicv5_ppi_irq_chip = {
507524
.name = "GICv5-PPI",
508525
.irq_mask = gicv5_ppi_irq_mask,
509526
.irq_unmask = gicv5_ppi_irq_unmask,
510527
.irq_eoi = gicv5_ppi_irq_eoi,
511528
.irq_get_irqchip_state = gicv5_ppi_irq_get_irqchip_state,
512529
.irq_set_irqchip_state = gicv5_ppi_irq_set_irqchip_state,
530+
.irq_set_vcpu_affinity = gicv5_ppi_irq_set_vcpu_affinity,
513531
.flags = IRQCHIP_SKIP_SET_WAKE |
514532
IRQCHIP_MASK_ON_SUSPEND,
515533
};
@@ -1041,6 +1059,36 @@ static void gicv5_set_cpuif_idbits(void)
10411059
}
10421060
}
10431061

1062+
#ifdef CONFIG_KVM
1063+
static struct gic_kvm_info gic_v5_kvm_info __initdata;
1064+
1065+
static bool __init gicv5_cpuif_has_gcie_legacy(void)
1066+
{
1067+
u64 idr0 = read_sysreg_s(SYS_ICC_IDR0_EL1);
1068+
return !!FIELD_GET(ICC_IDR0_EL1_GCIE_LEGACY, idr0);
1069+
}
1070+
1071+
static void __init gic_of_setup_kvm_info(struct device_node *node)
1072+
{
1073+
gic_v5_kvm_info.type = GIC_V5;
1074+
gic_v5_kvm_info.has_gcie_v3_compat = gicv5_cpuif_has_gcie_legacy();
1075+
1076+
/* GIC Virtual CPU interface maintenance interrupt */
1077+
gic_v5_kvm_info.no_maint_irq_mask = false;
1078+
gic_v5_kvm_info.maint_irq = irq_of_parse_and_map(node, 0);
1079+
if (!gic_v5_kvm_info.maint_irq) {
1080+
pr_warn("cannot find GICv5 virtual CPU interface maintenance interrupt\n");
1081+
return;
1082+
}
1083+
1084+
vgic_set_kvm_info(&gic_v5_kvm_info);
1085+
}
1086+
#else
1087+
static inline void __init gic_of_setup_kvm_info(struct device_node *node)
1088+
{
1089+
}
1090+
#endif // CONFIG_KVM
1091+
10441092
static int __init gicv5_of_init(struct device_node *node, struct device_node *parent)
10451093
{
10461094
int ret = gicv5_irs_of_probe(node);
@@ -1073,6 +1121,8 @@ static int __init gicv5_of_init(struct device_node *node, struct device_node *pa
10731121

10741122
gicv5_irs_its_probe();
10751123

1124+
gic_of_setup_kvm_info(node);
1125+
10761126
return 0;
10771127

10781128
out_int:

include/kvm/arm_vgic.h

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@
3838
enum vgic_type {
3939
VGIC_V2, /* Good ol' GICv2 */
4040
VGIC_V3, /* New fancy GICv3 */
41+
VGIC_V5, /* Newer, fancier GICv5 */
4142
};
4243

4344
/* same for all guests, as depending only on the _host's_ GIC model */
@@ -77,9 +78,12 @@ struct vgic_global {
7778
/* Pseudo GICv3 from outer space */
7879
bool no_hw_deactivation;
7980

80-
/* GIC system register CPU interface */
81+
/* GICv3 system register CPU interface */
8182
struct static_key_false gicv3_cpuif;
8283

84+
/* GICv3 compat mode on a GICv5 host */
85+
bool has_gcie_v3_compat;
86+
8387
u32 ich_vtr_el2;
8488
};
8589

include/linux/irqchip/arm-vgic-info.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,8 @@ enum gic_type {
1515
GIC_V2,
1616
/* Full GICv3, optionally with v2 compat */
1717
GIC_V3,
18+
/* Full GICv5, optionally with v3 compat */
19+
GIC_V5,
1820
};
1921

2022
struct gic_kvm_info {
@@ -34,6 +36,8 @@ struct gic_kvm_info {
3436
bool has_v4_1;
3537
/* Deactivation impared, subpar stuff */
3638
bool no_hw_deactivation;
39+
/* v3 compat support (GICv5 hosts, only) */
40+
bool has_gcie_v3_compat;
3741
};
3842

3943
#ifdef CONFIG_KVM

0 commit comments

Comments
 (0)