Skip to content

Commit c017e49

Browse files
Sticklyman1936timothy-hayes
authored andcommitted
KVM: arm64: gic-v5: Support GICv3 compat
Add support for GICv3 compat mode (FEAT_GCIE_LEGACY) which allows a GICv5 host to run GICv3-based VMs. This change enables the VHE/nVHE/hVHE/protected modes, but does not support nested virtualization. A lazy-disable approach is taken for compat mode; it is enabled on the vgic_v3_load path but not disabled on the vgic_v3_put path. A non-GICv3 VM, i.e., one based on GICv5, is responsible for disabling compat mode on the corresponding vgic_v5_load path. Currently, GICv5 is not supported, and hence compat mode is not disabled again once it is enabled, and this function is intentionally omitted from the code. Co-authored-by: Timothy Hayes <[email protected]> Signed-off-by: Timothy Hayes <[email protected]> Signed-off-by: Sascha Bischoff <[email protected]> Link: https://lore.kernel.org/r/[email protected] Signed-off-by: Oliver Upton <[email protected]>
1 parent b62f4b5 commit c017e49

File tree

5 files changed

+72
-12
lines changed

5 files changed

+72
-12
lines changed

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
@@ -1811,7 +1811,7 @@ static u64 sanitise_id_aa64pfr0_el1(const struct kvm_vcpu *vcpu, u64 val)
18111811
val |= SYS_FIELD_PREP_ENUM(ID_AA64PFR0_EL1, CSV3, IMP);
18121812
}
18131813

1814-
if (kvm_vgic_global_state.type == VGIC_V3) {
1814+
if (vgic_is_v3(vcpu->kvm)) {
18151815
val &= ~ID_AA64PFR0_EL1_GIC_MASK;
18161816
val |= SYS_FIELD_PREP_ENUM(ID_AA64PFR0_EL1, GIC, IMP);
18171817
}
@@ -1953,6 +1953,14 @@ static int set_id_aa64pfr0_el1(struct kvm_vcpu *vcpu,
19531953
(vcpu_has_nv(vcpu) && !FIELD_GET(ID_AA64PFR0_EL1_EL2, user_val)))
19541954
return -EINVAL;
19551955

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

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

Lines changed: 4 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
/**

arch/arm64/kvm/vgic/vgic.h

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -389,6 +389,17 @@ void vgic_v3_put_nested(struct kvm_vcpu *vcpu);
389389
void vgic_v3_handle_nested_maint_irq(struct kvm_vcpu *vcpu);
390390
void vgic_v3_nested_update_mi(struct kvm_vcpu *vcpu);
391391

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

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

0 commit comments

Comments
 (0)