Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

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 <timothy.hayes@arm.com>
Signed-off-by: Timothy Hayes <timothy.hayes@arm.com>
Signed-off-by: Sascha Bischoff <sascha.bischoff@arm.com>
Link: https://lore.kernel.org/r/20250627100847.1022515-5-sascha.bischoff@arm.com
Signed-off-by: Oliver Upton <oliver.upton@linux.dev>

authored by

Sascha Bischoff
Timothy Hayes
and committed by
Oliver Upton
c017e49e b62f4b5d

+72 -12
+43 -8
arch/arm64/kvm/hyp/vgic-v3-sr.c
··· 296 296 } 297 297 298 298 /* 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. 302 302 */ 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 + } 305 312 306 313 /* 307 314 * If we need to trap system registers, we must write ··· 329 322 cpu_if->vgic_vmcr = read_gicreg(ICH_VMCR_EL2); 330 323 } 331 324 332 - val = read_gicreg(ICC_SRE_EL2); 333 - write_gicreg(val | ICC_SRE_EL2_ENABLE, ICC_SRE_EL2); 325 + /* 326 + * Can be dropped in the future when GICv5 spec is relaxed. See comment 327 + * above. 328 + */ 329 + if (!cpus_have_final_cap(ARM64_HAS_GICV5_CPUIF)) { 330 + val = read_gicreg(ICC_SRE_EL2); 331 + write_gicreg(val | ICC_SRE_EL2_ENABLE, ICC_SRE_EL2); 332 + } 334 333 335 334 if (!cpu_if->vgic_sre) { 336 335 /* Make sure ENABLE is set at EL2 before setting SRE at EL1 */ ··· 436 423 */ 437 424 u64 __vgic_v3_get_gic_config(void) 438 425 { 439 - u64 val, sre = read_gicreg(ICC_SRE_EL1); 426 + u64 val, sre; 440 427 unsigned long flags = 0; 441 428 429 + /* 430 + * In compat mode, we cannot access ICC_SRE_EL1 at any EL 431 + * other than EL1 itself; just return the 432 + * ICH_VTR_EL2. ICC_IDR0_EL1 is only implemented on a GICv5 433 + * system, so we first check if we have GICv5 support. 434 + */ 435 + if (cpus_have_final_cap(ARM64_HAS_GICV5_CPUIF)) 436 + return read_gicreg(ICH_VTR_EL2); 437 + 438 + sre = read_gicreg(ICC_SRE_EL1); 442 439 /* 443 440 * To check whether we have a MMIO-based (GICv2 compatible) 444 441 * CPU interface, we need to disable the system register ··· 494 471 return val; 495 472 } 496 473 474 + static void __vgic_v3_compat_mode_enable(void) 475 + { 476 + if (!cpus_have_final_cap(ARM64_HAS_GICV5_CPUIF)) 477 + return; 478 + 479 + sysreg_clear_set_s(SYS_ICH_VCTLR_EL2, 0, ICH_VCTLR_EL2_V3); 480 + /* Wait for V3 to become enabled */ 481 + isb(); 482 + } 483 + 497 484 static u64 __vgic_v3_read_vmcr(void) 498 485 { 499 486 return read_gicreg(ICH_VMCR_EL2); ··· 523 490 524 491 void __vgic_v3_restore_vmcr_aprs(struct vgic_v3_cpu_if *cpu_if) 525 492 { 493 + __vgic_v3_compat_mode_enable(); 494 + 526 495 /* 527 496 * If dealing with a GICv2 emulation on GICv3, VMCR_EL2.VFIQen 528 497 * is dependent on ICC_SRE_EL1.SRE, and we have to perform the
+9 -1
arch/arm64/kvm/sys_regs.c
··· 1811 1811 val |= SYS_FIELD_PREP_ENUM(ID_AA64PFR0_EL1, CSV3, IMP); 1812 1812 } 1813 1813 1814 - if (kvm_vgic_global_state.type == VGIC_V3) { 1814 + if (vgic_is_v3(vcpu->kvm)) { 1815 1815 val &= ~ID_AA64PFR0_EL1_GIC_MASK; 1816 1816 val |= SYS_FIELD_PREP_ENUM(ID_AA64PFR0_EL1, GIC, IMP); 1817 1817 } ··· 1951 1951 if (!FIELD_GET(ID_AA64PFR0_EL1_EL0, user_val) || 1952 1952 !FIELD_GET(ID_AA64PFR0_EL1_EL1, user_val) || 1953 1953 (vcpu_has_nv(vcpu) && !FIELD_GET(ID_AA64PFR0_EL1_EL2, user_val))) 1954 + return -EINVAL; 1955 + 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) 1954 1962 return -EINVAL; 1955 1963 1956 1964 return set_id_reg(vcpu, rd, user_val);
+4 -2
arch/arm64/kvm/vgic/vgic-init.c
··· 674 674 * We want to make sure the list registers start out clear so that we 675 675 * only have the program the used registers. 676 676 */ 677 - if (kvm_vgic_global_state.type == VGIC_V2) 677 + if (kvm_vgic_global_state.type == VGIC_V2) { 678 678 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) { 680 681 kvm_call_hyp(__vgic_v3_init_lrs); 682 + } 681 683 } 682 684 683 685 /**
+11
arch/arm64/kvm/vgic/vgic.h
··· 389 389 void vgic_v3_handle_nested_maint_irq(struct kvm_vcpu *vcpu); 390 390 void vgic_v3_nested_update_mi(struct kvm_vcpu *vcpu); 391 391 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 + 392 403 int vgic_its_debug_init(struct kvm_device *dev); 393 404 void vgic_its_debug_destroy(struct kvm_device *dev); 394 405
+5 -1
include/kvm/arm_vgic.h
··· 38 38 enum vgic_type { 39 39 VGIC_V2, /* Good ol' GICv2 */ 40 40 VGIC_V3, /* New fancy GICv3 */ 41 + VGIC_V5, /* Newer, fancier GICv5 */ 41 42 }; 42 43 43 44 /* same for all guests, as depending only on the _host's_ GIC model */ ··· 78 77 /* Pseudo GICv3 from outer space */ 79 78 bool no_hw_deactivation; 80 79 81 - /* GIC system register CPU interface */ 80 + /* GICv3 system register CPU interface */ 82 81 struct static_key_false gicv3_cpuif; 82 + 83 + /* GICv3 compat mode on a GICv5 host */ 84 + bool has_gcie_v3_compat; 83 85 84 86 u32 ich_vtr_el2; 85 87 };