1#ifndef _ASM_HEXAGON_FUTEX_H
2#define _ASM_HEXAGON_FUTEX_H
3
4#ifdef __KERNEL__
5
6#include <linux/futex.h>
7#include <linux/uaccess.h>
8#include <asm/errno.h>
9
10/* XXX TODO-- need to add sync barriers! */
11
12#define __futex_atomic_op(insn, ret, oldval, uaddr, oparg) \
13	__asm__ __volatile( \
14	"1: %0 = memw_locked(%3);\n" \
15	    /* For example: %1 = %4 */ \
16	    insn \
17	"2: memw_locked(%3,p2) = %1;\n" \
18	"   if !p2 jump 1b;\n" \
19	"   %1 = #0;\n" \
20	"3:\n" \
21	".section .fixup,\"ax\"\n" \
22	"4: %1 = #%5;\n" \
23	"   jump 3b\n" \
24	".previous\n" \
25	".section __ex_table,\"a\"\n" \
26	".long 1b,4b,2b,4b\n" \
27	".previous\n" \
28	: "=&r" (oldval), "=&r" (ret), "+m" (*uaddr) \
29	: "r" (uaddr), "r" (oparg), "i" (-EFAULT) \
30	: "p2", "memory")
31
32
33static inline int
34futex_atomic_op_inuser(int encoded_op, int __user *uaddr)
35{
36	int op = (encoded_op >> 28) & 7;
37	int cmp = (encoded_op >> 24) & 15;
38	int oparg = (encoded_op << 8) >> 20;
39	int cmparg = (encoded_op << 20) >> 20;
40	int oldval = 0, ret;
41	if (encoded_op & (FUTEX_OP_OPARG_SHIFT << 28))
42		oparg = 1 << oparg;
43
44	if (!access_ok(VERIFY_WRITE, uaddr, sizeof(int)))
45		return -EFAULT;
46
47	pagefault_disable();
48
49	switch (op) {
50	case FUTEX_OP_SET:
51		__futex_atomic_op("%1 = %4\n", ret, oldval, uaddr, oparg);
52		break;
53	case FUTEX_OP_ADD:
54		__futex_atomic_op("%1 = add(%0,%4)\n", ret, oldval, uaddr,
55				  oparg);
56		break;
57	case FUTEX_OP_OR:
58		__futex_atomic_op("%1 = or(%0,%4)\n", ret, oldval, uaddr,
59				  oparg);
60		break;
61	case FUTEX_OP_ANDN:
62		__futex_atomic_op("%1 = not(%4); %1 = and(%0,%1)\n", ret,
63				  oldval, uaddr, oparg);
64		break;
65	case FUTEX_OP_XOR:
66		__futex_atomic_op("%1 = xor(%0,%4)\n", ret, oldval, uaddr,
67				  oparg);
68		break;
69	default:
70		ret = -ENOSYS;
71	}
72
73	pagefault_enable();
74
75	if (!ret) {
76		switch (cmp) {
77		case FUTEX_OP_CMP_EQ:
78			ret = (oldval == cmparg);
79			break;
80		case FUTEX_OP_CMP_NE:
81			ret = (oldval != cmparg);
82			break;
83		case FUTEX_OP_CMP_LT:
84			ret = (oldval < cmparg);
85			break;
86		case FUTEX_OP_CMP_GE:
87			ret = (oldval >= cmparg);
88			break;
89		case FUTEX_OP_CMP_LE:
90			ret = (oldval <= cmparg);
91			break;
92		case FUTEX_OP_CMP_GT:
93			ret = (oldval > cmparg);
94			break;
95		default:
96			ret = -ENOSYS;
97		}
98	}
99	return ret;
100}
101
102static inline int
103futex_atomic_cmpxchg_inatomic(u32 *uval, u32 __user *uaddr, u32 oldval,
104			      u32 newval)
105{
106	int prev;
107	int ret;
108
109	if (!access_ok(VERIFY_WRITE, uaddr, sizeof(u32)))
110		return -EFAULT;
111
112	__asm__ __volatile__ (
113	"1: %1 = memw_locked(%3)\n"
114	"   {\n"
115	"      p2 = cmp.eq(%1,%4)\n"
116	"      if !p2.new jump:NT 3f\n"
117	"   }\n"
118	"2: memw_locked(%3,p2) = %5\n"
119	"   if !p2 jump 1b\n"
120	"3:\n"
121	".section .fixup,\"ax\"\n"
122	"4: %0 = #%6\n"
123	"   jump 3b\n"
124	".previous\n"
125	".section __ex_table,\"a\"\n"
126	".long 1b,4b,2b,4b\n"
127	".previous\n"
128	: "+r" (ret), "=&r" (prev), "+m" (*uaddr)
129	: "r" (uaddr), "r" (oldval), "r" (newval), "i"(-EFAULT)
130	: "p2", "memory");
131
132	*uval = prev;
133	return ret;
134}
135
136#endif /* __KERNEL__ */
137#endif /* _ASM_HEXAGON_FUTEX_H */
138