]> git.ipfire.org Git - thirdparty/linux.git/commitdiff
KVM: arm64: nv: Fold GICv3 host trapping requirements into guest setup
authorMarc Zyngier <maz@kernel.org>
Tue, 25 Feb 2025 17:29:28 +0000 (17:29 +0000)
committerOliver Upton <oliver.upton@linux.dev>
Mon, 3 Mar 2025 22:57:10 +0000 (14:57 -0800)
Popular HW that is able to use NV also has a broken vgic implementation
that requires trapping.

On such HW, propagate the host trap bits into the guest's shadow
ICH_HCR_EL2 register, making sure we don't allow an L2 guest to bring
the system down.

This involves a bit of tweaking so that the emulation code correctly
poicks up the shadow state as needed, and to only partially sync
ICH_HCR_EL2 back with the guest state to capture EOIcount.

Signed-off-by: Marc Zyngier <maz@kernel.org>
Link: https://lore.kernel.org/r/20250225172930.1850838-15-maz@kernel.org
Signed-off-by: Oliver Upton <oliver.upton@linux.dev>
arch/arm64/kvm/vgic/vgic-v3-nested.c

index 5464ff24474008a8158f792b4531ff9e49e3ea88..bfa5bde1f106799044dd2c2a697e5c5f15e12957 100644 (file)
@@ -296,9 +296,19 @@ static void vgic_v3_create_shadow_state(struct kvm_vcpu *vcpu,
                                        struct vgic_v3_cpu_if *s_cpu_if)
 {
        struct vgic_v3_cpu_if *host_if = &vcpu->arch.vgic_cpu.vgic_v3;
+       u64 val = 0;
        int i;
 
-       s_cpu_if->vgic_hcr = __vcpu_sys_reg(vcpu, ICH_HCR_EL2);
+       /*
+        * If we're on a system with a broken vgic that requires
+        * trapping, propagate the trapping requirements.
+        *
+        * Ah, the smell of rotten fruits...
+        */
+       if (static_branch_unlikely(&vgic_v3_cpuif_trap))
+               val = host_if->vgic_hcr & (ICH_HCR_EL2_TALL0 | ICH_HCR_EL2_TALL1 |
+                                          ICH_HCR_EL2_TC | ICH_HCR_EL2_TDIR);
+       s_cpu_if->vgic_hcr = __vcpu_sys_reg(vcpu, ICH_HCR_EL2) | val;
        s_cpu_if->vgic_vmcr = __vcpu_sys_reg(vcpu, ICH_VMCR_EL2);
        s_cpu_if->vgic_sre = host_if->vgic_sre;
 
@@ -335,6 +345,7 @@ void vgic_v3_put_nested(struct kvm_vcpu *vcpu)
 {
        struct shadow_if *shadow_if = get_shadow_if();
        struct vgic_v3_cpu_if *s_cpu_if = &shadow_if->cpuif;
+       u64 val;
        int i;
 
        __vgic_v3_save_vmcr_aprs(s_cpu_if);
@@ -345,7 +356,10 @@ void vgic_v3_put_nested(struct kvm_vcpu *vcpu)
         * Translate the shadow state HW fields back to the virtual ones
         * before copying the shadow struct back to the nested one.
         */
-       __vcpu_sys_reg(vcpu, ICH_HCR_EL2) = s_cpu_if->vgic_hcr;
+       val = __vcpu_sys_reg(vcpu, ICH_HCR_EL2);
+       val &= ~ICH_HCR_EL2_EOIcount_MASK;
+       val |= (s_cpu_if->vgic_hcr & ICH_HCR_EL2_EOIcount_MASK);
+       __vcpu_sys_reg(vcpu, ICH_HCR_EL2) = val;
        __vcpu_sys_reg(vcpu, ICH_VMCR_EL2) = s_cpu_if->vgic_vmcr;
 
        for (i = 0; i < 4; i++) {
@@ -354,7 +368,7 @@ void vgic_v3_put_nested(struct kvm_vcpu *vcpu)
        }
 
        for_each_set_bit(i, &shadow_if->lr_map, kvm_vgic_global_state.nr_lr) {
-               u64 val = __vcpu_sys_reg(vcpu, ICH_LRN(i));
+               val = __vcpu_sys_reg(vcpu, ICH_LRN(i));
 
                val &= ~ICH_LR_STATE;
                val |= s_cpu_if->vgic_lr[i] & ICH_LR_STATE;