Merge tag 'for-linus' of git://git.kernel.org/pub/scm/virt/kvm/kvm
[platform/kernel/linux-rpi.git] / arch / x86 / kernel / fpu / core.c
index dd3777a..8dea01f 100644 (file)
@@ -206,7 +206,27 @@ void fpu_reset_from_exception_fixup(void)
 }
 
 #if IS_ENABLED(CONFIG_KVM)
-static void __fpstate_reset(struct fpstate *fpstate);
+static void __fpstate_reset(struct fpstate *fpstate, u64 xfd);
+
+static void fpu_init_guest_permissions(struct fpu_guest *gfpu)
+{
+       struct fpu_state_perm *fpuperm;
+       u64 perm;
+
+       if (!IS_ENABLED(CONFIG_X86_64))
+               return;
+
+       spin_lock_irq(&current->sighand->siglock);
+       fpuperm = &current->group_leader->thread.fpu.guest_perm;
+       perm = fpuperm->__state_perm;
+
+       /* First fpstate allocation locks down permissions. */
+       WRITE_ONCE(fpuperm->__state_perm, perm | FPU_GUEST_PERM_LOCKED);
+
+       spin_unlock_irq(&current->sighand->siglock);
+
+       gfpu->perm = perm & ~FPU_GUEST_PERM_LOCKED;
+}
 
 bool fpu_alloc_guest_fpstate(struct fpu_guest *gfpu)
 {
@@ -218,12 +238,18 @@ bool fpu_alloc_guest_fpstate(struct fpu_guest *gfpu)
        if (!fpstate)
                return false;
 
-       __fpstate_reset(fpstate);
+       /* Leave xfd to 0 (the reset value defined by spec) */
+       __fpstate_reset(fpstate, 0);
        fpstate_init_user(fpstate);
        fpstate->is_valloc      = true;
        fpstate->is_guest       = true;
 
-       gfpu->fpstate = fpstate;
+       gfpu->fpstate           = fpstate;
+       gfpu->xfeatures         = fpu_user_cfg.default_features;
+       gfpu->perm              = fpu_user_cfg.default_features;
+       gfpu->uabi_size         = fpu_user_cfg.default_size;
+       fpu_init_guest_permissions(gfpu);
+
        return true;
 }
 EXPORT_SYMBOL_GPL(fpu_alloc_guest_fpstate);
@@ -243,6 +269,64 @@ void fpu_free_guest_fpstate(struct fpu_guest *gfpu)
 }
 EXPORT_SYMBOL_GPL(fpu_free_guest_fpstate);
 
+/*
+  * fpu_enable_guest_xfd_features - Check xfeatures against guest perm and enable
+  * @guest_fpu:         Pointer to the guest FPU container
+  * @xfeatures:         Features requested by guest CPUID
+  *
+  * Enable all dynamic xfeatures according to guest perm and requested CPUID.
+  *
+  * Return: 0 on success, error code otherwise
+  */
+int fpu_enable_guest_xfd_features(struct fpu_guest *guest_fpu, u64 xfeatures)
+{
+       lockdep_assert_preemption_enabled();
+
+       /* Nothing to do if all requested features are already enabled. */
+       xfeatures &= ~guest_fpu->xfeatures;
+       if (!xfeatures)
+               return 0;
+
+       return __xfd_enable_feature(xfeatures, guest_fpu);
+}
+EXPORT_SYMBOL_GPL(fpu_enable_guest_xfd_features);
+
+#ifdef CONFIG_X86_64
+void fpu_update_guest_xfd(struct fpu_guest *guest_fpu, u64 xfd)
+{
+       fpregs_lock();
+       guest_fpu->fpstate->xfd = xfd;
+       if (guest_fpu->fpstate->in_use)
+               xfd_update_state(guest_fpu->fpstate);
+       fpregs_unlock();
+}
+EXPORT_SYMBOL_GPL(fpu_update_guest_xfd);
+
+/**
+ * fpu_sync_guest_vmexit_xfd_state - Synchronize XFD MSR and software state
+ *
+ * Must be invoked from KVM after a VMEXIT before enabling interrupts when
+ * XFD write emulation is disabled. This is required because the guest can
+ * freely modify XFD and the state at VMEXIT is not guaranteed to be the
+ * same as the state on VMENTER. So software state has to be udpated before
+ * any operation which depends on it can take place.
+ *
+ * Note: It can be invoked unconditionally even when write emulation is
+ * enabled for the price of a then pointless MSR read.
+ */
+void fpu_sync_guest_vmexit_xfd_state(void)
+{
+       struct fpstate *fps = current->thread.fpu.fpstate;
+
+       lockdep_assert_irqs_disabled();
+       if (fpu_state_size_dynamic()) {
+               rdmsrl(MSR_IA32_XFD, fps->xfd);
+               __this_cpu_write(xfd_state, fps->xfd);
+       }
+}
+EXPORT_SYMBOL_GPL(fpu_sync_guest_vmexit_xfd_state);
+#endif /* CONFIG_X86_64 */
+
 int fpu_swap_kvm_fpstate(struct fpu_guest *guest_fpu, bool enter_guest)
 {
        struct fpstate *guest_fps = guest_fpu->fpstate;
@@ -437,26 +521,28 @@ void fpstate_init_user(struct fpstate *fpstate)
                fpstate_init_fstate(fpstate);
 }
 
-static void __fpstate_reset(struct fpstate *fpstate)
+static void __fpstate_reset(struct fpstate *fpstate, u64 xfd)
 {
        /* Initialize sizes and feature masks */
        fpstate->size           = fpu_kernel_cfg.default_size;
        fpstate->user_size      = fpu_user_cfg.default_size;
        fpstate->xfeatures      = fpu_kernel_cfg.default_features;
        fpstate->user_xfeatures = fpu_user_cfg.default_features;
-       fpstate->xfd            = init_fpstate.xfd;
+       fpstate->xfd            = xfd;
 }
 
 void fpstate_reset(struct fpu *fpu)
 {
        /* Set the fpstate pointer to the default fpstate */
        fpu->fpstate = &fpu->__fpstate;
-       __fpstate_reset(fpu->fpstate);
+       __fpstate_reset(fpu->fpstate, init_fpstate.xfd);
 
        /* Initialize the permission related info in fpu */
        fpu->perm.__state_perm          = fpu_kernel_cfg.default_features;
        fpu->perm.__state_size          = fpu_kernel_cfg.default_size;
        fpu->perm.__user_state_size     = fpu_user_cfg.default_size;
+       /* Same defaults for guests */
+       fpu->guest_perm = fpu->perm;
 }
 
 static inline void fpu_inherit_perms(struct fpu *dst_fpu)
@@ -467,6 +553,7 @@ static inline void fpu_inherit_perms(struct fpu *dst_fpu)
                spin_lock_irq(&current->sighand->siglock);
                /* Fork also inherits the permissions of the parent */
                dst_fpu->perm = src_fpu->perm;
+               dst_fpu->guest_perm = src_fpu->guest_perm;
                spin_unlock_irq(&current->sighand->siglock);
        }
 }