x86/fpu: Extend fpu_xstate_prctl() with guest permissions
[linux-2.6-microblaze.git] / arch / x86 / kernel / fpu / xstate.c
index d288294..5f01d46 100644 (file)
@@ -1595,7 +1595,7 @@ static int validate_sigaltstack(unsigned int usize)
        return 0;
 }
 
-static int __xstate_request_perm(u64 permitted, u64 requested)
+static int __xstate_request_perm(u64 permitted, u64 requested, bool guest)
 {
        /*
         * This deliberately does not exclude !XSAVES as we still might
@@ -1605,9 +1605,10 @@ static int __xstate_request_perm(u64 permitted, u64 requested)
         */
        bool compacted = cpu_feature_enabled(X86_FEATURE_XSAVES);
        struct fpu *fpu = &current->group_leader->thread.fpu;
+       struct fpu_state_perm *perm;
        unsigned int ksize, usize;
        u64 mask;
-       int ret;
+       int ret = 0;
 
        /* Check whether fully enabled */
        if ((permitted & requested) == requested)
@@ -1621,15 +1622,18 @@ static int __xstate_request_perm(u64 permitted, u64 requested)
        mask &= XFEATURE_MASK_USER_SUPPORTED;
        usize = xstate_calculate_size(mask, false);
 
-       ret = validate_sigaltstack(usize);
-       if (ret)
-               return ret;
+       if (!guest) {
+               ret = validate_sigaltstack(usize);
+               if (ret)
+                       return ret;
+       }
 
+       perm = guest ? &fpu->guest_perm : &fpu->perm;
        /* Pairs with the READ_ONCE() in xstate_get_group_perm() */
-       WRITE_ONCE(fpu->perm.__state_perm, requested);
+       WRITE_ONCE(perm->__state_perm, requested);
        /* Protected by sighand lock */
-       fpu->perm.__state_size = ksize;
-       fpu->perm.__user_state_size = usize;
+       perm->__state_size = ksize;
+       perm->__user_state_size = usize;
        return ret;
 }
 
@@ -1640,7 +1644,7 @@ static const u64 xstate_prctl_req[XFEATURE_MAX] = {
        [XFEATURE_XTILE_DATA] = XFEATURE_MASK_XTILE_DATA,
 };
 
-static int xstate_request_perm(unsigned long idx)
+static int xstate_request_perm(unsigned long idx, bool guest)
 {
        u64 permitted, requested;
        int ret;
@@ -1661,14 +1665,19 @@ static int xstate_request_perm(unsigned long idx)
                return -EOPNOTSUPP;
 
        /* Lockless quick check */
-       permitted = xstate_get_host_group_perm();
+       permitted = xstate_get_group_perm(guest);
        if ((permitted & requested) == requested)
                return 0;
 
        /* Protect against concurrent modifications */
        spin_lock_irq(&current->sighand->siglock);
-       permitted = xstate_get_host_group_perm();
-       ret = __xstate_request_perm(permitted, requested);
+       permitted = xstate_get_group_perm(guest);
+
+       /* First vCPU allocation locks the permissions. */
+       if (guest && (permitted & FPU_GUEST_PERM_LOCKED))
+               ret = -EBUSY;
+       else
+               ret = __xstate_request_perm(permitted, requested, guest);
        spin_unlock_irq(&current->sighand->siglock);
        return ret;
 }
@@ -1713,12 +1722,18 @@ int xfd_enable_feature(u64 xfd_err)
        return 0;
 }
 #else /* CONFIG_X86_64 */
-static inline int xstate_request_perm(unsigned long idx)
+static inline int xstate_request_perm(unsigned long idx, bool guest)
 {
        return -EPERM;
 }
 #endif  /* !CONFIG_X86_64 */
 
+inline u64 xstate_get_guest_group_perm(void)
+{
+       return xstate_get_group_perm(true);
+}
+EXPORT_SYMBOL_GPL(xstate_get_guest_group_perm);
+
 /**
  * fpu_xstate_prctl - xstate permission operations
  * @tsk:       Redundant pointer to current
@@ -1742,6 +1757,7 @@ long fpu_xstate_prctl(struct task_struct *tsk, int option, unsigned long arg2)
        u64 __user *uptr = (u64 __user *)arg2;
        u64 permitted, supported;
        unsigned long idx = arg2;
+       bool guest = false;
 
        if (tsk != current)
                return -EPERM;
@@ -1760,11 +1776,20 @@ long fpu_xstate_prctl(struct task_struct *tsk, int option, unsigned long arg2)
                permitted &= XFEATURE_MASK_USER_SUPPORTED;
                return put_user(permitted, uptr);
 
+       case ARCH_GET_XCOMP_GUEST_PERM:
+               permitted = xstate_get_guest_group_perm();
+               permitted &= XFEATURE_MASK_USER_SUPPORTED;
+               return put_user(permitted, uptr);
+
+       case ARCH_REQ_XCOMP_GUEST_PERM:
+               guest = true;
+               fallthrough;
+
        case ARCH_REQ_XCOMP_PERM:
                if (!IS_ENABLED(CONFIG_X86_64))
                        return -EOPNOTSUPP;
 
-               return xstate_request_perm(idx);
+               return xstate_request_perm(idx, guest);
 
        default:
                return -EINVAL;