]> git.baikalelectronics.ru Git - kernel.git/commitdiff
kvm: x86: Add support for getting/setting expanded xstate buffer
authorGuang Zeng <guang.zeng@intel.com>
Wed, 5 Jan 2022 12:35:29 +0000 (04:35 -0800)
committerPaolo Bonzini <pbonzini@redhat.com>
Fri, 14 Jan 2022 18:44:41 +0000 (13:44 -0500)
With KVM_CAP_XSAVE, userspace uses a hardcoded 4KB buffer to get/set
xstate data from/to KVM. This doesn't work when dynamic xfeatures
(e.g. AMX) are exposed to the guest as they require a larger buffer
size.

Introduce a new capability (KVM_CAP_XSAVE2). Userspace VMM gets the
required xstate buffer size via KVM_CHECK_EXTENSION(KVM_CAP_XSAVE2).
KVM_SET_XSAVE is extended to work with both legacy and new capabilities
by doing properly-sized memdup_user() based on the guest fpu container.
KVM_GET_XSAVE is kept for backward-compatible reason. Instead,
KVM_GET_XSAVE2 is introduced under KVM_CAP_XSAVE2 as the preferred
interface for getting xstate buffer (4KB or larger size) from KVM
Link: https://lkml.org/lkml/2021/12/15/510)
Also, update the api doc with the new KVM_GET_XSAVE2 ioctl.

Signed-off-by: Guang Zeng <guang.zeng@intel.com>
Signed-off-by: Wei Wang <wei.w.wang@intel.com>
Signed-off-by: Jing Liu <jing2.liu@intel.com>
Signed-off-by: Kevin Tian <kevin.tian@intel.com>
Signed-off-by: Yang Zhong <yang.zhong@intel.com>
Message-Id: <20220105123532.12586-19-yang.zhong@intel.com>
Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
Documentation/virt/kvm/api.rst
arch/x86/include/uapi/asm/kvm.h
arch/x86/kvm/cpuid.c
arch/x86/kvm/cpuid.h
arch/x86/kvm/x86.c
include/uapi/linux/kvm.h

index f4ea5e41a4d0ca5b4b9681fa7352550a3f64d942..d3791a14eb9ad02bccaec50902e4c5cb6ab5256c 100644 (file)
@@ -1569,6 +1569,7 @@ otherwise it will return EBUSY error.
 
   struct kvm_xsave {
        __u32 region[1024];
+       __u32 extra[0];
   };
 
 This ioctl would copy current vcpu's xsave struct to the userspace.
@@ -1577,7 +1578,7 @@ This ioctl would copy current vcpu's xsave struct to the userspace.
 4.43 KVM_SET_XSAVE
 ------------------
 
-:Capability: KVM_CAP_XSAVE
+:Capability: KVM_CAP_XSAVE and KVM_CAP_XSAVE2
 :Architectures: x86
 :Type: vcpu ioctl
 :Parameters: struct kvm_xsave (in)
@@ -1588,9 +1589,18 @@ This ioctl would copy current vcpu's xsave struct to the userspace.
 
   struct kvm_xsave {
        __u32 region[1024];
+       __u32 extra[0];
   };
 
-This ioctl would copy userspace's xsave struct to the kernel.
+This ioctl would copy userspace's xsave struct to the kernel. It copies
+as many bytes as are returned by KVM_CHECK_EXTENSION(KVM_CAP_XSAVE2),
+when invoked on the vm file descriptor. The size value returned by
+KVM_CHECK_EXTENSION(KVM_CAP_XSAVE2) will always be at least 4096.
+Currently, it is only greater than 4096 if a dynamic feature has been
+enabled with ``arch_prctl()``, but this may change in the future.
+
+The offsets of the state save areas in struct kvm_xsave follow the
+contents of CPUID leaf 0xD on the host.
 
 
 4.44 KVM_GET_XCRS
@@ -5535,6 +5545,34 @@ the trailing ``'\0'``, is indicated by ``name_size`` in the header.
 The Stats Data block contains an array of 64-bit values in the same order
 as the descriptors in Descriptors block.
 
+4.42 KVM_GET_XSAVE2
+------------------
+
+:Capability: KVM_CAP_XSAVE2
+:Architectures: x86
+:Type: vcpu ioctl
+:Parameters: struct kvm_xsave (out)
+:Returns: 0 on success, -1 on error
+
+
+::
+
+  struct kvm_xsave {
+       __u32 region[1024];
+       __u32 extra[0];
+  };
+
+This ioctl would copy current vcpu's xsave struct to the userspace. It
+copies as many bytes as are returned by KVM_CHECK_EXTENSION(KVM_CAP_XSAVE2)
+when invoked on the vm file descriptor. The size value returned by
+KVM_CHECK_EXTENSION(KVM_CAP_XSAVE2) will always be at least 4096.
+Currently, it is only greater than 4096 if a dynamic feature has been
+enabled with ``arch_prctl()``, but this may change in the future.
+
+The offsets of the state save areas in struct kvm_xsave follow the contents
+of CPUID leaf 0xD on the host.
+
+
 5. The kvm_run structure
 ========================
 
index 5a776a08f78cb3b5c9e75e8b018abab051d08262..2da3316bb55959dad6b400e70b06628c4de0201b 100644 (file)
@@ -373,9 +373,23 @@ struct kvm_debugregs {
        __u64 reserved[9];
 };
 
-/* for KVM_CAP_XSAVE */
+/* for KVM_CAP_XSAVE and KVM_CAP_XSAVE2 */
 struct kvm_xsave {
+       /*
+        * KVM_GET_XSAVE2 and KVM_SET_XSAVE write and read as many bytes
+        * as are returned by KVM_CHECK_EXTENSION(KVM_CAP_XSAVE2)
+        * respectively, when invoked on the vm file descriptor.
+        *
+        * The size value returned by KVM_CHECK_EXTENSION(KVM_CAP_XSAVE2)
+        * will always be at least 4096. Currently, it is only greater
+        * than 4096 if a dynamic feature has been enabled with
+        * ``arch_prctl()``, but this may change in the future.
+        *
+        * The offsets of the state save areas in struct kvm_xsave follow
+        * the contents of CPUID leaf 0xD on the host.
+        */
        __u32 region[1024];
+       __u32 extra[0];
 };
 
 #define KVM_MAX_XCRS   16
index ba4c3d5d2386f510a18bb9c274d55d61a3a3bcdd..c55e57b30e81585fd78d4180f51bdb3f2ce0b67c 100644 (file)
@@ -32,7 +32,7 @@
 u32 kvm_cpu_caps[NR_KVM_CPU_CAPS] __read_mostly;
 EXPORT_SYMBOL_GPL(kvm_cpu_caps);
 
-static u32 xstate_required_size(u64 xstate_bv, bool compacted)
+u32 xstate_required_size(u64 xstate_bv, bool compacted)
 {
        int feature_bit = 0;
        u32 ret = XSAVE_HDR_SIZE + XSAVE_HDR_OFFSET;
index c99edfff7f824369d063533adb7b64ef5d32331f..8a770b481d9de0c7853f4baf86993e527afb9439 100644 (file)
@@ -30,6 +30,8 @@ int kvm_vcpu_ioctl_get_cpuid2(struct kvm_vcpu *vcpu,
 bool kvm_cpuid(struct kvm_vcpu *vcpu, u32 *eax, u32 *ebx,
               u32 *ecx, u32 *edx, bool exact_only);
 
+u32 xstate_required_size(u64 xstate_bv, bool compacted);
+
 int cpuid_query_maxphyaddr(struct kvm_vcpu *vcpu);
 u64 kvm_vcpu_reserved_gpa_bits_raw(struct kvm_vcpu *vcpu);
 
index 993eee6451ea3533177321b7a753d5cb7c241fd3..bde18ca657db6318b1dadb5e1bc6b2399d627120 100644 (file)
@@ -4314,6 +4314,14 @@ int kvm_vm_ioctl_check_extension(struct kvm *kvm, long ext)
                else
                        r = 0;
                break;
+       case KVM_CAP_XSAVE2: {
+               u64 guest_perm = xstate_get_guest_group_perm();
+
+               r = xstate_required_size(supported_xcr0 & guest_perm, false);
+               if (r < sizeof(struct kvm_xsave))
+                       r = sizeof(struct kvm_xsave);
+               break;
+       }
        default:
                break;
        }
@@ -4917,6 +4925,16 @@ static void kvm_vcpu_ioctl_x86_get_xsave(struct kvm_vcpu *vcpu,
                                       vcpu->arch.pkru);
 }
 
+static void kvm_vcpu_ioctl_x86_get_xsave2(struct kvm_vcpu *vcpu,
+                                         u8 *state, unsigned int size)
+{
+       if (fpstate_is_confidential(&vcpu->arch.guest_fpu))
+               return;
+
+       fpu_copy_guest_fpstate_to_uabi(&vcpu->arch.guest_fpu,
+                                      state, size, vcpu->arch.pkru);
+}
+
 static int kvm_vcpu_ioctl_x86_set_xsave(struct kvm_vcpu *vcpu,
                                        struct kvm_xsave *guest_xsave)
 {
@@ -5370,6 +5388,10 @@ long kvm_arch_vcpu_ioctl(struct file *filp,
                break;
        }
        case KVM_GET_XSAVE: {
+               r = -EINVAL;
+               if (vcpu->arch.guest_fpu.uabi_size > sizeof(struct kvm_xsave))
+                       break;
+
                u.xsave = kzalloc(sizeof(struct kvm_xsave), GFP_KERNEL_ACCOUNT);
                r = -ENOMEM;
                if (!u.xsave)
@@ -5384,7 +5406,9 @@ long kvm_arch_vcpu_ioctl(struct file *filp,
                break;
        }
        case KVM_SET_XSAVE: {
-               u.xsave = memdup_user(argp, sizeof(*u.xsave));
+               int size = vcpu->arch.guest_fpu.uabi_size;
+
+               u.xsave = memdup_user(argp, size);
                if (IS_ERR(u.xsave)) {
                        r = PTR_ERR(u.xsave);
                        goto out_nofree;
@@ -5393,6 +5417,25 @@ long kvm_arch_vcpu_ioctl(struct file *filp,
                r = kvm_vcpu_ioctl_x86_set_xsave(vcpu, u.xsave);
                break;
        }
+
+       case KVM_GET_XSAVE2: {
+               int size = vcpu->arch.guest_fpu.uabi_size;
+
+               u.xsave = kzalloc(size, GFP_KERNEL_ACCOUNT);
+               r = -ENOMEM;
+               if (!u.xsave)
+                       break;
+
+               kvm_vcpu_ioctl_x86_get_xsave2(vcpu, u.buffer, size);
+
+               r = -EFAULT;
+               if (copy_to_user(argp, u.xsave, size))
+                       break;
+
+               r = 0;
+               break;
+       }
+
        case KVM_GET_XCRS: {
                u.xcrs = kzalloc(sizeof(struct kvm_xcrs), GFP_KERNEL_ACCOUNT);
                r = -ENOMEM;
index fbfd70d965c682d24d96489fc3c06b7df5c05ed9..9563d294f181974a1032ae5588f754af1b9c6f53 100644 (file)
@@ -1132,6 +1132,7 @@ struct kvm_ppc_resize_hpt {
 #define KVM_CAP_ARM_MTE 205
 #define KVM_CAP_VM_MOVE_ENC_CONTEXT_FROM 206
 #define KVM_CAP_VM_GPA_BITS 207
+#define KVM_CAP_XSAVE2 208
 
 #ifdef KVM_CAP_IRQ_ROUTING
 
@@ -1622,6 +1623,9 @@ struct kvm_enc_region {
 #define KVM_S390_NORMAL_RESET  _IO(KVMIO,   0xc3)
 #define KVM_S390_CLEAR_RESET   _IO(KVMIO,   0xc4)
 
+/* Available with KVM_CAP_XSAVE2 */
+#define KVM_GET_XSAVE2           _IOR(KVMIO,  0xcf, struct kvm_xsave)
+
 struct kvm_s390_pv_sec_parm {
        __u64 origin;
        __u64 length;