From: David Woodhouse <[email protected]>

XCR0/XFEM is shared between VTLs: the TLFS banks no vector state and
the reference host keeps a single XCR0 (its store_vtl/load_vtl carry
no XSAVE state). The planes series shares the FPU payload between
twins but keeps arch.xcr0 per-vCPU, so an XSETBV in one VTL was
invisible in the other: VTL1 read a stale XFEM, and state enabled by
VTL0 (AVX, for the Windows secure kernel) silently vanished across a
VTL switch.

Propagate the validated value to every plane twin in __kvm_set_xcr(),
covering both guest XSETBV and the KVM_SET_XCRS ioctl path. The CPUID
dynamic-bits dirty marker lives in the shared common state, so the
existing single set covers the twins.

Signed-off-by: David Woodhouse <[email protected]>
Assisted-by: LLM
---
 arch/x86/kvm/x86.c | 30 ++++++++++++++++++++++++++++++
 1 file changed, 30 insertions(+)

diff --git a/arch/x86/kvm/x86.c b/arch/x86/kvm/x86.c
index 491a0360c403..0f8a149ba0eb 100644
--- a/arch/x86/kvm/x86.c
+++ b/arch/x86/kvm/x86.c
@@ -1361,6 +1361,28 @@ int __kvm_set_xcr(struct kvm_vcpu *vcpu, u32 index, u64 
xcr)
 
        vcpu->arch.xcr0 = xcr0;
 
+       /*
+        * XCR0/XFEM is shared across VTLs (TLFS 15.4; the reference host
+        * banks no vector state): propagate the new value to every plane
+        * twin so the other VTLs observe it.  This covers both guest
+        * XSETBV and the KVM_SET_XCRS ioctl path.  The CPUID dirty marker
+        * below lives in the shared common state, so one set covers the
+        * twins too.
+        *
+        * Skip the fan-out until this vCPU is published in
+        * common->vcpus[]: the architectural reset of a twin still being
+        * created must not clobber a live sibling's XCR0 (the new twin
+        * inherits the shared value instead, in kvm_arch_vcpu_create()).
+        */
+       if (vcpu->common &&
+           vcpu->common->vcpus[vcpu->plane_level] == vcpu) {
+               struct kvm_vcpu *twin;
+               int i;
+
+               vcpu_for_each_plane(vcpu->common, i, twin)
+                       twin->arch.xcr0 = xcr0;
+       }
+
        if ((xcr0 ^ old_xcr0) & XFEATURE_MASK_EXTEND)
                cpuid_set_dirty(vcpu);
        return 0;
@@ -13549,6 +13571,14 @@ int kvm_arch_vcpu_create(struct kvm_vcpu *vcpu)
        kvm_vcpu_after_set_cpuid(vcpu);
        kvm_set_tsc_khz(vcpu, vcpu->kvm->arch.default_tsc_khz);
        kvm_vcpu_reset(vcpu, false);
+       /*
+        * XCR0 is shared across planes.  A twin created after its plane-0
+        * sibling has run (Hyper-V VSM enables VTL1 at runtime) inherits
+        * the current shared value; the architectural reset value only
+        * stands when no sibling exists yet.
+        */
+       if (vcpu->plane_level && vcpu->common->vcpus[0])
+               vcpu->arch.xcr0 = vcpu->common->vcpus[0]->arch.xcr0;
        kvm_init_mmu(vcpu);
        vcpu_put(vcpu);
        return 0;
-- 
2.50.1


Reply via email to