1/*
2 * Atomic futex routines
3 *
4 * Based on the PowerPC implementataion
5 *
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License version 2 as
8 * published by the Free Software Foundation.
9 *
10 * Copyright (C) 2013 TangoTec Ltd.
11 *
12 * Baruch Siach <baruch@tkos.co.il>
13 */
14
15#ifndef _ASM_XTENSA_FUTEX_H
16#define _ASM_XTENSA_FUTEX_H
17
18#ifdef __KERNEL__
19
20#include <linux/futex.h>
21#include <linux/uaccess.h>
22#include <linux/errno.h>
23
24#define __futex_atomic_op(insn, ret, oldval, uaddr, oparg) \
25	__asm__ __volatile(				\
26	"1:	l32i	%0, %2, 0\n"			\
27		insn "\n"				\
28	"	wsr	%0, scompare1\n"		\
29	"2:	s32c1i	%1, %2, 0\n"			\
30	"	bne	%1, %0, 1b\n"			\
31	"	movi	%1, 0\n"			\
32	"3:\n"						\
33	"	.section .fixup,\"ax\"\n"		\
34	"	.align 4\n"				\
35	"4:	.long	3b\n"				\
36	"5:	l32r	%0, 4b\n"			\
37	"	movi	%1, %3\n"			\
38	"	jx	%0\n"				\
39	"	.previous\n"				\
40	"	.section __ex_table,\"a\"\n"		\
41	"	.long 1b,5b,2b,5b\n"			\
42	"	.previous\n"				\
43	: "=&r" (oldval), "=&r" (ret)			\
44	: "r" (uaddr), "I" (-EFAULT), "r" (oparg)	\
45	: "memory")
46
47static inline int futex_atomic_op_inuser(int encoded_op, u32 __user *uaddr)
48{
49	int op = (encoded_op >> 28) & 7;
50	int cmp = (encoded_op >> 24) & 15;
51	int oparg = (encoded_op << 8) >> 20;
52	int cmparg = (encoded_op << 20) >> 20;
53	int oldval = 0, ret;
54	if (encoded_op & (FUTEX_OP_OPARG_SHIFT << 28))
55		oparg = 1 << oparg;
56
57	if (!access_ok(VERIFY_WRITE, uaddr, sizeof(u32)))
58		return -EFAULT;
59
60#if !XCHAL_HAVE_S32C1I
61	return -ENOSYS;
62#endif
63
64	pagefault_disable();
65
66	switch (op) {
67	case FUTEX_OP_SET:
68		__futex_atomic_op("mov %1, %4", ret, oldval, uaddr, oparg);
69		break;
70	case FUTEX_OP_ADD:
71		__futex_atomic_op("add %1, %0, %4", ret, oldval, uaddr,
72				oparg);
73		break;
74	case FUTEX_OP_OR:
75		__futex_atomic_op("or %1, %0, %4", ret, oldval, uaddr,
76				oparg);
77		break;
78	case FUTEX_OP_ANDN:
79		__futex_atomic_op("and %1, %0, %4", ret, oldval, uaddr,
80				~oparg);
81		break;
82	case FUTEX_OP_XOR:
83		__futex_atomic_op("xor %1, %0, %4", ret, oldval, uaddr,
84				oparg);
85		break;
86	default:
87		ret = -ENOSYS;
88	}
89
90	pagefault_enable();
91
92	if (ret)
93		return ret;
94
95	switch (cmp) {
96	case FUTEX_OP_CMP_EQ: return (oldval == cmparg);
97	case FUTEX_OP_CMP_NE: return (oldval != cmparg);
98	case FUTEX_OP_CMP_LT: return (oldval < cmparg);
99	case FUTEX_OP_CMP_GE: return (oldval >= cmparg);
100	case FUTEX_OP_CMP_LE: return (oldval <= cmparg);
101	case FUTEX_OP_CMP_GT: return (oldval > cmparg);
102	}
103
104	return -ENOSYS;
105}
106
107static inline int
108futex_atomic_cmpxchg_inatomic(u32 *uval, u32 __user *uaddr,
109			      u32 oldval, u32 newval)
110{
111	int ret = 0;
112	u32 prev;
113
114	if (!access_ok(VERIFY_WRITE, uaddr, sizeof(u32)))
115		return -EFAULT;
116
117#if !XCHAL_HAVE_S32C1I
118	return -ENOSYS;
119#endif
120
121	__asm__ __volatile__ (
122	"	# futex_atomic_cmpxchg_inatomic\n"
123	"1:	l32i	%1, %3, 0\n"
124	"	mov	%0, %5\n"
125	"	wsr	%1, scompare1\n"
126	"2:	s32c1i	%0, %3, 0\n"
127	"3:\n"
128	"	.section .fixup,\"ax\"\n"
129	"	.align 4\n"
130	"4:	.long	3b\n"
131	"5:	l32r	%1, 4b\n"
132	"	movi	%0, %6\n"
133	"	jx	%1\n"
134	"	.previous\n"
135	"	.section __ex_table,\"a\"\n"
136	"	.long 1b,5b,2b,5b\n"
137	"	.previous\n"
138	: "+r" (ret), "=&r" (prev), "+m" (*uaddr)
139	: "r" (uaddr), "r" (oldval), "r" (newval), "I" (-EFAULT)
140	: "memory");
141
142	*uval = prev;
143	return ret;
144}
145
146#endif /* __KERNEL__ */
147#endif /* _ASM_XTENSA_FUTEX_H */
148