Branch data Line data Source code
1 : : /* SPDX-License-Identifier: GPL-2.0 */
2 : : #ifndef _ASM_ARM_FUTEX_H
3 : : #define _ASM_ARM_FUTEX_H
4 : :
5 : : #ifdef __KERNEL__
6 : :
7 : : #include <linux/futex.h>
8 : : #include <linux/uaccess.h>
9 : : #include <asm/errno.h>
10 : :
11 : : #define __futex_atomic_ex_table(err_reg) \
12 : : "3:\n" \
13 : : " .pushsection __ex_table,\"a\"\n" \
14 : : " .align 3\n" \
15 : : " .long 1b, 4f, 2b, 4f\n" \
16 : : " .popsection\n" \
17 : : " .pushsection .text.fixup,\"ax\"\n" \
18 : : " .align 2\n" \
19 : : "4: mov %0, " err_reg "\n" \
20 : : " b 3b\n" \
21 : : " .popsection"
22 : :
23 : : #ifdef CONFIG_SMP
24 : :
25 : : #define __futex_atomic_op(insn, ret, oldval, tmp, uaddr, oparg) \
26 : : ({ \
27 : : unsigned int __ua_flags; \
28 : : smp_mb(); \
29 : : prefetchw(uaddr); \
30 : : __ua_flags = uaccess_save_and_enable(); \
31 : : __asm__ __volatile__( \
32 : : "1: ldrex %1, [%3]\n" \
33 : : " " insn "\n" \
34 : : "2: strex %2, %0, [%3]\n" \
35 : : " teq %2, #0\n" \
36 : : " bne 1b\n" \
37 : : " mov %0, #0\n" \
38 : : __futex_atomic_ex_table("%5") \
39 : : : "=&r" (ret), "=&r" (oldval), "=&r" (tmp) \
40 : : : "r" (uaddr), "r" (oparg), "Ir" (-EFAULT) \
41 : : : "cc", "memory"); \
42 : : uaccess_restore(__ua_flags); \
43 : : })
44 : :
45 : : static inline int
46 : 3 : futex_atomic_cmpxchg_inatomic(u32 *uval, u32 __user *uaddr,
47 : : u32 oldval, u32 newval)
48 : : {
49 : : unsigned int __ua_flags;
50 : : int ret;
51 : : u32 val;
52 : :
53 : 3 : if (!access_ok(uaddr, sizeof(u32)))
54 : : return -EFAULT;
55 : :
56 : 3 : smp_mb();
57 : : /* Prefetching cannot fault */
58 : : prefetchw(uaddr);
59 : : __ua_flags = uaccess_save_and_enable();
60 : 3 : __asm__ __volatile__("@futex_atomic_cmpxchg_inatomic\n"
61 : : "1: ldrex %1, [%4]\n"
62 : : " teq %1, %2\n"
63 : : " ite eq @ explicit IT needed for the 2b label\n"
64 : : "2: strexeq %0, %3, [%4]\n"
65 : : " movne %0, #0\n"
66 : : " teq %0, #0\n"
67 : : " bne 1b\n"
68 : : __futex_atomic_ex_table("%5")
69 : : : "=&r" (ret), "=&r" (val)
70 : : : "r" (oldval), "r" (newval), "r" (uaddr), "Ir" (-EFAULT)
71 : : : "cc", "memory");
72 : : uaccess_restore(__ua_flags);
73 : 3 : smp_mb();
74 : :
75 : 3 : *uval = val;
76 : 3 : return ret;
77 : : }
78 : :
79 : : #else /* !SMP, we can work around lack of atomic ops by disabling preemption */
80 : :
81 : : #include <linux/preempt.h>
82 : : #include <asm/domain.h>
83 : :
84 : : #define __futex_atomic_op(insn, ret, oldval, tmp, uaddr, oparg) \
85 : : ({ \
86 : : unsigned int __ua_flags = uaccess_save_and_enable(); \
87 : : __asm__ __volatile__( \
88 : : "1: " TUSER(ldr) " %1, [%3]\n" \
89 : : " " insn "\n" \
90 : : "2: " TUSER(str) " %0, [%3]\n" \
91 : : " mov %0, #0\n" \
92 : : __futex_atomic_ex_table("%5") \
93 : : : "=&r" (ret), "=&r" (oldval), "=&r" (tmp) \
94 : : : "r" (uaddr), "r" (oparg), "Ir" (-EFAULT) \
95 : : : "cc", "memory"); \
96 : : uaccess_restore(__ua_flags); \
97 : : })
98 : :
99 : : static inline int
100 : : futex_atomic_cmpxchg_inatomic(u32 *uval, u32 __user *uaddr,
101 : : u32 oldval, u32 newval)
102 : : {
103 : : unsigned int __ua_flags;
104 : : int ret = 0;
105 : : u32 val;
106 : :
107 : : if (!access_ok(uaddr, sizeof(u32)))
108 : : return -EFAULT;
109 : :
110 : : preempt_disable();
111 : : __ua_flags = uaccess_save_and_enable();
112 : : __asm__ __volatile__("@futex_atomic_cmpxchg_inatomic\n"
113 : : " .syntax unified\n"
114 : : "1: " TUSER(ldr) " %1, [%4]\n"
115 : : " teq %1, %2\n"
116 : : " it eq @ explicit IT needed for the 2b label\n"
117 : : "2: " TUSERCOND(str, eq) " %3, [%4]\n"
118 : : __futex_atomic_ex_table("%5")
119 : : : "+r" (ret), "=&r" (val)
120 : : : "r" (oldval), "r" (newval), "r" (uaddr), "Ir" (-EFAULT)
121 : : : "cc", "memory");
122 : : uaccess_restore(__ua_flags);
123 : :
124 : : *uval = val;
125 : : preempt_enable();
126 : :
127 : : return ret;
128 : : }
129 : :
130 : : #endif /* !SMP */
131 : :
132 : : static inline int
133 : 0 : arch_futex_atomic_op_inuser(int op, int oparg, int *oval, u32 __user *uaddr)
134 : : {
135 : : int oldval = 0, ret, tmp;
136 : :
137 : : #ifndef CONFIG_SMP
138 : : preempt_disable();
139 : : #endif
140 : : pagefault_disable();
141 : :
142 : 0 : switch (op) {
143 : : case FUTEX_OP_SET:
144 : 0 : __futex_atomic_op("mov %0, %4", ret, oldval, tmp, uaddr, oparg);
145 : : break;
146 : : case FUTEX_OP_ADD:
147 : 0 : __futex_atomic_op("add %0, %1, %4", ret, oldval, tmp, uaddr, oparg);
148 : : break;
149 : : case FUTEX_OP_OR:
150 : 0 : __futex_atomic_op("orr %0, %1, %4", ret, oldval, tmp, uaddr, oparg);
151 : : break;
152 : : case FUTEX_OP_ANDN:
153 : 0 : __futex_atomic_op("and %0, %1, %4", ret, oldval, tmp, uaddr, ~oparg);
154 : : break;
155 : : case FUTEX_OP_XOR:
156 : 0 : __futex_atomic_op("eor %0, %1, %4", ret, oldval, tmp, uaddr, oparg);
157 : : break;
158 : : default:
159 : : ret = -ENOSYS;
160 : : }
161 : :
162 : : pagefault_enable();
163 : : #ifndef CONFIG_SMP
164 : : preempt_enable();
165 : : #endif
166 : :
167 : : /*
168 : : * Store unconditionally. If ret != 0 the extra store is the least
169 : : * of the worries but GCC cannot figure out that __futex_atomic_op()
170 : : * is either setting ret to -EFAULT or storing the old value in
171 : : * oldval which results in a uninitialized warning at the call site.
172 : : */
173 : : *oval = oldval;
174 : :
175 : 0 : return ret;
176 : : }
177 : :
178 : : #endif /* __KERNEL__ */
179 : : #endif /* _ASM_ARM_FUTEX_H */
|