]> www.infradead.org Git - users/hch/misc.git/commitdiff
KVM: arm64: nv: Don't erroneously claim FEAT_DoubleLock for NV VMs
authorOliver Upton <oliver.upton@linux.dev>
Fri, 12 Sep 2025 21:22:49 +0000 (14:22 -0700)
committerMarc Zyngier <maz@kernel.org>
Fri, 19 Sep 2025 13:01:35 +0000 (14:01 +0100)
ID_AA64DFR0_EL1.DoubleLock is one of those annoying signed feature
fields where a non-negative value implies that a feature is implemented
and a negative value implies that it is not. While the intention of
masking this field was likely to hide the feature, KVM actually
advertises it, even on unsupporting hardware.

Remove FEAT_DoubleLock from the mask, making the NI value visible to the
VM. Take care to accept the old, incorrect values for this field as
we've lied to userspace.

Signed-off-by: Oliver Upton <oliver.upton@linux.dev>
Signed-off-by: Marc Zyngier <maz@kernel.org>
arch/arm64/kvm/nested.c
arch/arm64/kvm/sys_regs.c

index 53c57d105c935023c0b82e4762d25a853380d145..4044dc66fa3920df672e2fd90cc2af8bb9221391 100644 (file)
@@ -1582,7 +1582,6 @@ u64 limit_nv_id_reg(struct kvm *kvm, u32 reg, u64 val)
                         ID_AA64DFR0_EL1_MTPMU          |
                         ID_AA64DFR0_EL1_TraceBuffer    |
                         ID_AA64DFR0_EL1_TraceFilt      |
-                        ID_AA64DFR0_EL1_DoubleLock     |
                         ID_AA64DFR0_EL1_PMSVer         |
                         ID_AA64DFR0_EL1_CTX_CMPs       |
                         ID_AA64DFR0_EL1_SEBEP          |
index e6ec971eb9d12c37a5c4dd5aeee16310d3a45803..caf80c324b356af70f9f964ce0ad61a5b2d2a73e 100644 (file)
@@ -1997,6 +1997,26 @@ static u64 sanitise_id_aa64dfr0_el1(const struct kvm_vcpu *vcpu, u64 val)
        return val;
 }
 
+/*
+ * Older versions of KVM erroneously claim support for FEAT_DoubleLock with
+ * NV-enabled VMs on unsupporting hardware. Silently ignore the incorrect
+ * value if it is consistent with the bug.
+ */
+static bool ignore_feat_doublelock(struct kvm_vcpu *vcpu, u64 val)
+{
+       u8 host, user;
+
+       if (!vcpu_has_nv(vcpu))
+               return false;
+
+       host = SYS_FIELD_GET(ID_AA64DFR0_EL1, DoubleLock,
+                            read_sanitised_ftr_reg(SYS_ID_AA64DFR0_EL1));
+       user = SYS_FIELD_GET(ID_AA64DFR0_EL1, DoubleLock, val);
+
+       return host == ID_AA64DFR0_EL1_DoubleLock_NI &&
+              user == ID_AA64DFR0_EL1_DoubleLock_IMP;
+}
+
 static int set_id_aa64dfr0_el1(struct kvm_vcpu *vcpu,
                               const struct sys_reg_desc *rd,
                               u64 val)
@@ -2028,6 +2048,11 @@ static int set_id_aa64dfr0_el1(struct kvm_vcpu *vcpu,
        if (debugver < ID_AA64DFR0_EL1_DebugVer_IMP)
                return -EINVAL;
 
+       if (ignore_feat_doublelock(vcpu, val)) {
+               val &= ~ID_AA64DFR0_EL1_DoubleLock;
+               val |= SYS_FIELD_PREP_ENUM(ID_AA64DFR0_EL1, DoubleLock, NI);
+       }
+
        return set_id_reg(vcpu, rd, val);
 }