From: David Woodhouse Plane twins share CPUID, so a twin's guest can set the XCR0 bits for a dynamic xfeature such as AMX's XTILEDATA with its own XSETBV. But kvm_check_cpuid() calls fpu_enable_guest_xfd_features() only for the vCPU that KVM_SET_CPUID was issued on, and a twin created afterwards starts with the default fpstate. A twin that then runs AMX code loses its tile data on every fpstate save, because the fpstate it saves into has no room for it. On a host that exposes AMX to the guest, any twin using AMX hits this. Whether the XRSTORS on the next entry can also leave a previous context's tile registers in place for that twin to read is an open question; it has not been verified. Enlarge the fpstate of every twin in kvm_check_cpuid(), and give a twin created after KVM_SET_CPUID the dynamic xfeatures of its plane-0 sibling's fpstate. Only a twin that has not run may have its fpstate enlarged: fpstate_realloc() hands a guest fpstate that is not loaded a fresh image and copies nothing, so a twin that has run would lose its XSAVE state. kvm_set_cpuid() already accepts only identical CPUID once the vCPU has run, but it checked only the vCPU the ioctl was issued on. With plane 0 held while a higher plane runs, KVM_SET_CPUID2 on plane 0 would pass that check and resize the plane that ran. Since the twins share CPUID, take the identical-CPUID path once any twin has run. Signed-off-by: David Woodhouse --- arch/x86/kvm/cpuid.c | 21 +++++++++++++++++++-- arch/x86/kvm/x86.c | 17 +++++++++++++++++ 2 files changed, 36 insertions(+), 2 deletions(-) diff --git a/arch/x86/kvm/cpuid.c b/arch/x86/kvm/cpuid.c index c5fa0551ddd5..971ebd781c6e 100644 --- a/arch/x86/kvm/cpuid.c +++ b/arch/x86/kvm/cpuid.c @@ -141,7 +141,9 @@ EXPORT_SYMBOL_FOR_KVM_INTERNAL(kvm_find_cpuid_entry2); static int kvm_check_cpuid(struct kvm_vcpu *vcpu) { struct kvm_cpuid_entry2 *best; + struct kvm_vcpu *v; u64 xfeatures; + int i, r; /* * The existing code assumes virtual address is 48-bit or 57-bit in the @@ -168,7 +170,18 @@ static int kvm_check_cpuid(struct kvm_vcpu *vcpu) if (!xfeatures) return 0; - return fpu_enable_guest_xfd_features(&vcpu->arch.guest_fpu, xfeatures); + /* + * The planes share CPUID, so any twin's guest can enable these + * xfeatures in its XCR0: enlarge every twin's fpstate, not just + * this vCPU's. + */ + vcpu_for_each_plane(vcpu->common, i, v) { + r = fpu_enable_guest_xfd_features(&v->arch.guest_fpu, xfeatures); + if (r) + return r; + } + + return 0; } static u32 kvm_apply_cpuid_pv_features_quirk(struct kvm_vcpu *vcpu); @@ -545,8 +558,12 @@ static int kvm_set_cpuid(struct kvm_vcpu *vcpu, struct kvm_cpuid_entry2 *e2, * VMMs (e.g. QEMU) reuse vCPU fds for CPU hotplug/unplug and do * KVM_SET_CPUID{,2} again. To support this legacy behavior, check * whether the supplied CPUID data is equal to what's already set. + * Plane twins share CPUID, so this applies once any twin has run. */ - if (!kvm_can_set_cpuid_and_feature_msrs(vcpu)) { + vcpu_for_each_plane(common, i, v) { + if (kvm_can_set_cpuid_and_feature_msrs(v)) + continue; + r = kvm_cpuid_check_equal(vcpu, e2, nent); if (r) goto err; diff --git a/arch/x86/kvm/x86.c b/arch/x86/kvm/x86.c index 6d3d93e85a65..af28e73e4b9b 100644 --- a/arch/x86/kvm/x86.c +++ b/arch/x86/kvm/x86.c @@ -9771,6 +9771,23 @@ int kvm_arch_vcpu_create(struct kvm_vcpu *vcpu) vcpu->arch.hv_root_tdp = INVALID_PAGE; #endif + /* + * A plane twin created after KVM_SET_CPUID shares CPUID that may + * already allow dynamic xfeatures, which KVM_SET_CPUID enabled only + * in the twins that existed at the time. Give it the dynamic + * xfeatures of its plane-0 sibling's fpstate. + */ + if (vcpu->plane_level && vcpu->common->vcpus[0]) { + u64 dyn = vcpu->common->vcpus[0]->arch.guest_fpu.fpstate->xfeatures & + XFEATURE_MASK_USER_DYNAMIC; + + if (dyn) { + r = fpu_enable_guest_xfd_features(&vcpu->arch.guest_fpu, dyn); + if (r) + goto free_guest_fpu; + } + } + /* * A plane twin created after KVM_SET_CPUID shares CPUID that may * already advertise Hyper-V. Allocate the Hyper-V context now so -- 2.50.1