]> git.ipfire.org Git - thirdparty/linux.git/commitdiff
KVM: arm64: nv: Handle ZCR_EL2 traps
authorOliver Upton <oliver.upton@linux.dev>
Thu, 20 Jun 2024 16:46:40 +0000 (16:46 +0000)
committerOliver Upton <oliver.upton@linux.dev>
Thu, 20 Jun 2024 19:01:20 +0000 (19:01 +0000)
Unlike other SVE-related registers, ZCR_EL2 takes a sysreg trap to EL2
when HCR_EL2.NV = 1. KVM still needs to honor the guest hypervisor's
trap configuration, which expects an SVE trap (i.e. ESR_EL2.EC = 0x19)
when CPTR traps are enabled for the vCPU's current context.

Otherwise, if the guest hypervisor has traps disabled, emulate the
access by mapping the requested VL into ZCR_EL1.

Reviewed-by: Marc Zyngier <maz@kernel.org>
Link: https://lore.kernel.org/r/20240620164653.1130714-4-oliver.upton@linux.dev
Signed-off-by: Oliver Upton <oliver.upton@linux.dev>
arch/arm64/include/asm/kvm_emulate.h
arch/arm64/include/asm/kvm_host.h
arch/arm64/kvm/sys_regs.c

index befef5d0daae37eb6f48d8d7eb5c4e226e0538a4..c5fa66f5fd823f8944b20d389dd571e9ab02de48 100644 (file)
@@ -56,6 +56,14 @@ void kvm_emulate_nested_eret(struct kvm_vcpu *vcpu);
 int kvm_inject_nested_sync(struct kvm_vcpu *vcpu, u64 esr_el2);
 int kvm_inject_nested_irq(struct kvm_vcpu *vcpu);
 
+static inline void kvm_inject_nested_sve_trap(struct kvm_vcpu *vcpu)
+{
+       u64 esr = FIELD_PREP(ESR_ELx_EC_MASK, ESR_ELx_EC_SVE) |
+                 ESR_ELx_IL;
+
+       kvm_inject_nested_sync(vcpu, esr);
+}
+
 #if defined(__KVM_VHE_HYPERVISOR__) || defined(__KVM_NVHE_HYPERVISOR__)
 static __always_inline bool vcpu_el1_is_32bit(struct kvm_vcpu *vcpu)
 {
index 36b8e97bf49ec4e742f58f1f8cccccf2ed015666..0ff1ed1341fc11473cbd10fcef3856f72c98740f 100644 (file)
@@ -423,6 +423,7 @@ enum vcpu_sysreg {
        MDCR_EL2,       /* Monitor Debug Configuration Register (EL2) */
        CPTR_EL2,       /* Architectural Feature Trap Register (EL2) */
        HACR_EL2,       /* Hypervisor Auxiliary Control Register */
+       ZCR_EL2,        /* SVE Control Register (EL2) */
        TTBR0_EL2,      /* Translation Table Base Register 0 (EL2) */
        TTBR1_EL2,      /* Translation Table Base Register 1 (EL2) */
        TCR_EL2,        /* Translation Control Register (EL2) */
@@ -991,6 +992,7 @@ static inline bool __vcpu_read_sys_reg_from_cpu(int reg, u64 *val)
        case DACR32_EL2:        *val = read_sysreg_s(SYS_DACR32_EL2);   break;
        case IFSR32_EL2:        *val = read_sysreg_s(SYS_IFSR32_EL2);   break;
        case DBGVCR32_EL2:      *val = read_sysreg_s(SYS_DBGVCR32_EL2); break;
+       case ZCR_EL1:           *val = read_sysreg_s(SYS_ZCR_EL12);     break;
        default:                return false;
        }
 
@@ -1036,6 +1038,7 @@ static inline bool __vcpu_write_sys_reg_to_cpu(u64 val, int reg)
        case DACR32_EL2:        write_sysreg_s(val, SYS_DACR32_EL2);    break;
        case IFSR32_EL2:        write_sysreg_s(val, SYS_IFSR32_EL2);    break;
        case DBGVCR32_EL2:      write_sysreg_s(val, SYS_DBGVCR32_EL2);  break;
+       case ZCR_EL1:           write_sysreg_s(val, SYS_ZCR_EL12);      break;
        default:                return false;
        }
 
index 22b45a15d06881ce25ee124f8b69a20491b5df3d..80aa99a012ae1117dabf3beb7bde7e8a42b59566 100644 (file)
@@ -121,6 +121,7 @@ static bool get_el2_to_el1_mapping(unsigned int reg,
                MAPPED_EL2_SYSREG(AMAIR_EL2,   AMAIR_EL1,   NULL             );
                MAPPED_EL2_SYSREG(ELR_EL2,     ELR_EL1,     NULL             );
                MAPPED_EL2_SYSREG(SPSR_EL2,    SPSR_EL1,    NULL             );
+               MAPPED_EL2_SYSREG(ZCR_EL2,     ZCR_EL1,     NULL             );
        default:
                return false;
        }
@@ -2199,6 +2200,40 @@ static u64 reset_hcr(struct kvm_vcpu *vcpu, const struct sys_reg_desc *r)
        return __vcpu_sys_reg(vcpu, r->reg) = val;
 }
 
+static unsigned int sve_el2_visibility(const struct kvm_vcpu *vcpu,
+                                      const struct sys_reg_desc *rd)
+{
+       unsigned int r;
+
+       r = el2_visibility(vcpu, rd);
+       if (r)
+               return r;
+
+       return sve_visibility(vcpu, rd);
+}
+
+static bool access_zcr_el2(struct kvm_vcpu *vcpu,
+                          struct sys_reg_params *p,
+                          const struct sys_reg_desc *r)
+{
+       unsigned int vq;
+
+       if (guest_hyp_sve_traps_enabled(vcpu)) {
+               kvm_inject_nested_sve_trap(vcpu);
+               return true;
+       }
+
+       if (!p->is_write) {
+               p->regval = vcpu_read_sys_reg(vcpu, ZCR_EL2);
+               return true;
+       }
+
+       vq = SYS_FIELD_GET(ZCR_ELx, LEN, p->regval) + 1;
+       vq = min(vq, vcpu_sve_max_vq(vcpu));
+       vcpu_write_sys_reg(vcpu, vq - 1, ZCR_EL2);
+       return true;
+}
+
 /*
  * Architected system registers.
  * Important: Must be sorted ascending by Op0, Op1, CRn, CRm, Op2
@@ -2688,6 +2723,9 @@ static const struct sys_reg_desc sys_reg_descs[] = {
        EL2_REG_VNCR(HFGITR_EL2, reset_val, 0),
        EL2_REG_VNCR(HACR_EL2, reset_val, 0),
 
+       { SYS_DESC(SYS_ZCR_EL2), .access = access_zcr_el2, .reset = reset_val,
+         .visibility = sve_el2_visibility, .reg = ZCR_EL2 },
+
        EL2_REG_VNCR(HCRX_EL2, reset_val, 0),
 
        EL2_REG(TTBR0_EL2, access_rw, reset_val, 0),