1 /* SPDX-License-Identifier: GPL-2.0 */
2 #ifndef _ASM_GENERIC_FUTEX_H
3 #define _ASM_GENERIC_FUTEX_H
4 
5 #include <linux/futex.h>
6 #include <linux/uaccess.h>
7 #include <asm/errno.h>
8 
9 #ifndef CONFIG_SMP
10 /*
11  * The following implementation only for uniprocessor machines.
12  * It relies on preempt_disable() ensuring mutual exclusion.
13  *
14  */
15 
16 /**
17  * arch_futex_atomic_op_inuser() - Atomic arithmetic operation with constant
18  *			  argument and comparison of the previous
19  *			  futex value with another constant.
20  *
21  * @encoded_op:	encoded operation to execute
22  * @uaddr:	pointer to user space address
23  *
24  * Return:
25  * 0 - On success
26  * <0 - On error
27  */
28 static inline int
arch_futex_atomic_op_inuser(int op,u32 oparg,int * oval,u32 __user * uaddr)29 arch_futex_atomic_op_inuser(int op, u32 oparg, int *oval, u32 __user *uaddr)
30 {
31 	int oldval, ret;
32 	u32 tmp;
33 
34 	preempt_disable();
35 	pagefault_disable();
36 
37 	ret = -EFAULT;
38 	if (unlikely(get_user(oldval, uaddr) != 0))
39 		goto out_pagefault_enable;
40 
41 	ret = 0;
42 	tmp = oldval;
43 
44 	switch (op) {
45 	case FUTEX_OP_SET:
46 		tmp = oparg;
47 		break;
48 	case FUTEX_OP_ADD:
49 		tmp += oparg;
50 		break;
51 	case FUTEX_OP_OR:
52 		tmp |= oparg;
53 		break;
54 	case FUTEX_OP_ANDN:
55 		tmp &= ~oparg;
56 		break;
57 	case FUTEX_OP_XOR:
58 		tmp ^= oparg;
59 		break;
60 	default:
61 		ret = -ENOSYS;
62 	}
63 
64 	if (ret == 0 && unlikely(put_user(tmp, uaddr) != 0))
65 		ret = -EFAULT;
66 
67 out_pagefault_enable:
68 	pagefault_enable();
69 	preempt_enable();
70 
71 	if (ret == 0)
72 		*oval = oldval;
73 
74 	return ret;
75 }
76 
77 /**
78  * futex_atomic_cmpxchg_inatomic() - Compare and exchange the content of the
79  *				uaddr with newval if the current value is
80  *				oldval.
81  * @uval:	pointer to store content of @uaddr
82  * @uaddr:	pointer to user space address
83  * @oldval:	old value
84  * @newval:	new value to store to @uaddr
85  *
86  * Return:
87  * 0 - On success
88  * <0 - On error
89  */
90 static inline int
futex_atomic_cmpxchg_inatomic(u32 * uval,u32 __user * uaddr,u32 oldval,u32 newval)91 futex_atomic_cmpxchg_inatomic(u32 *uval, u32 __user *uaddr,
92 			      u32 oldval, u32 newval)
93 {
94 	u32 val;
95 
96 	preempt_disable();
97 	if (unlikely(get_user(val, uaddr) != 0)) {
98 		preempt_enable();
99 		return -EFAULT;
100 	}
101 
102 	if (val == oldval && unlikely(put_user(newval, uaddr) != 0)) {
103 		preempt_enable();
104 		return -EFAULT;
105 	}
106 
107 	*uval = val;
108 	preempt_enable();
109 
110 	return 0;
111 }
112 
113 #else
114 static inline int
arch_futex_atomic_op_inuser(int op,u32 oparg,int * oval,u32 __user * uaddr)115 arch_futex_atomic_op_inuser(int op, u32 oparg, int *oval, u32 __user *uaddr)
116 {
117 	int oldval = 0, ret;
118 
119 	pagefault_disable();
120 
121 	switch (op) {
122 	case FUTEX_OP_SET:
123 	case FUTEX_OP_ADD:
124 	case FUTEX_OP_OR:
125 	case FUTEX_OP_ANDN:
126 	case FUTEX_OP_XOR:
127 	default:
128 		ret = -ENOSYS;
129 	}
130 
131 	pagefault_enable();
132 
133 	if (!ret)
134 		*oval = oldval;
135 
136 	return ret;
137 }
138 
139 static inline int
futex_atomic_cmpxchg_inatomic(u32 * uval,u32 __user * uaddr,u32 oldval,u32 newval)140 futex_atomic_cmpxchg_inatomic(u32 *uval, u32 __user *uaddr,
141 			      u32 oldval, u32 newval)
142 {
143 	return -ENOSYS;
144 }
145 
146 #endif /* CONFIG_SMP */
147 #endif
148