KVM: arm64: Honor guest requested traps in GICv3 emulation
authorMarc Zyngier <maz@kernel.org>
Tue, 27 Aug 2024 15:25:14 +0000 (16:25 +0100)
committerMarc Zyngier <maz@kernel.org>
Tue, 27 Aug 2024 17:32:55 +0000 (18:32 +0100)
On platforms that require emulation of the CPU interface, we still
need to honor the traps requested by the guest (ICH_HCR_EL2 as well
as the FGTs for ICC_IGRPEN{0,1}_EL1.

Check for these bits early and lail out if any trap applies.

Reviewed-by: Oliver Upton <oliver.upton@linux.dev>
Link: https://lore.kernel.org/r/20240827152517.3909653-9-maz@kernel.org
Signed-off-by: Marc Zyngier <maz@kernel.org>
arch/arm64/kvm/hyp/vgic-v3-sr.c

index c9ab76652c32f7ecbbce0c7e8d630d108a25e850..39f6363caa1f3a818ebc9a08d8d506cdabe437dc 100644 (file)
@@ -1042,6 +1042,75 @@ static void __vgic_v3_write_ctlr(struct kvm_vcpu *vcpu, u32 vmcr, int rt)
        write_gicreg(vmcr, ICH_VMCR_EL2);
 }
 
+static bool __vgic_v3_check_trap_forwarding(struct kvm_vcpu *vcpu,
+                                           u32 sysreg, bool is_read)
+{
+       u64 ich_hcr;
+
+       if (!vcpu_has_nv(vcpu) || is_hyp_ctxt(vcpu))
+               return false;
+
+       ich_hcr = __vcpu_sys_reg(vcpu, ICH_HCR_EL2);
+
+       switch (sysreg) {
+       case SYS_ICC_IGRPEN0_EL1:
+               if (is_read &&
+                   (__vcpu_sys_reg(vcpu, HFGRTR_EL2) & HFGxTR_EL2_ICC_IGRPENn_EL1))
+                       return true;
+
+               if (!is_read &&
+                   (__vcpu_sys_reg(vcpu, HFGWTR_EL2) & HFGxTR_EL2_ICC_IGRPENn_EL1))
+                       return true;
+
+               fallthrough;
+
+       case SYS_ICC_AP0Rn_EL1(0):
+       case SYS_ICC_AP0Rn_EL1(1):
+       case SYS_ICC_AP0Rn_EL1(2):
+       case SYS_ICC_AP0Rn_EL1(3):
+       case SYS_ICC_BPR0_EL1:
+       case SYS_ICC_EOIR0_EL1:
+       case SYS_ICC_HPPIR0_EL1:
+       case SYS_ICC_IAR0_EL1:
+               return ich_hcr & ICH_HCR_TALL0;
+
+       case SYS_ICC_IGRPEN1_EL1:
+               if (is_read &&
+                   (__vcpu_sys_reg(vcpu, HFGRTR_EL2) & HFGxTR_EL2_ICC_IGRPENn_EL1))
+                       return true;
+
+               if (!is_read &&
+                   (__vcpu_sys_reg(vcpu, HFGWTR_EL2) & HFGxTR_EL2_ICC_IGRPENn_EL1))
+                       return true;
+
+               fallthrough;
+
+       case SYS_ICC_AP1Rn_EL1(0):
+       case SYS_ICC_AP1Rn_EL1(1):
+       case SYS_ICC_AP1Rn_EL1(2):
+       case SYS_ICC_AP1Rn_EL1(3):
+       case SYS_ICC_BPR1_EL1:
+       case SYS_ICC_EOIR1_EL1:
+       case SYS_ICC_HPPIR1_EL1:
+       case SYS_ICC_IAR1_EL1:
+               return ich_hcr & ICH_HCR_TALL1;
+
+       case SYS_ICC_DIR_EL1:
+               if (ich_hcr & ICH_HCR_TDIR)
+                       return true;
+
+               fallthrough;
+
+       case SYS_ICC_RPR_EL1:
+       case SYS_ICC_CTLR_EL1:
+       case SYS_ICC_PMR_EL1:
+               return ich_hcr & ICH_HCR_TC;
+
+       default:
+               return false;
+       }
+}
+
 int __vgic_v3_perform_cpuif_access(struct kvm_vcpu *vcpu)
 {
        int rt;
@@ -1065,6 +1134,9 @@ int __vgic_v3_perform_cpuif_access(struct kvm_vcpu *vcpu)
 
        is_read = (esr & ESR_ELx_SYS64_ISS_DIR_MASK) == ESR_ELx_SYS64_ISS_DIR_READ;
 
+       if (__vgic_v3_check_trap_forwarding(vcpu, sysreg, is_read))
+               return 0;
+
        switch (sysreg) {
        case SYS_ICC_IAR0_EL1:
        case SYS_ICC_IAR1_EL1: