1/* SPDX-License-Identifier: GPL-2.0 */
2// Copyright (C) 2005-2017 Andes Technology Corporation
3
4#ifndef __NDS32_FUTEX_H__
5#define __NDS32_FUTEX_H__
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	"	.pushsection __ex_table,\"a\"\n"		\
13	"	.align	3\n"					\
14	"	.long	1b, 4f\n"				\
15	"	.long	2b, 4f\n"				\
16	"	.popsection\n"					\
17	"	.pushsection .fixup,\"ax\"\n"			\
18	"4:	move	%0, " err_reg "\n"			\
19	"	b	3b\n"					\
20	"	.popsection"
21
22#define __futex_atomic_op(insn, ret, oldval, tmp, uaddr, oparg)	\
23	smp_mb();						\
24	asm volatile(					\
25	"	movi	$ta, #0\n"				\
26	"1:	llw	%1, [%2+$ta]\n"				\
27	"	" insn "\n"					\
28	"2:	scw	%0, [%2+$ta]\n"				\
29	"	beqz	%0, 1b\n"				\
30	"	movi	%0, #0\n"				\
31	"3:\n"							\
32	__futex_atomic_ex_table("%4")				\
33	: "=&r" (ret), "=&r" (oldval)				\
34	: "r" (uaddr), "r" (oparg), "i" (-EFAULT)		\
35	: "cc", "memory")
36static inline int
37futex_atomic_cmpxchg_inatomic(u32 * uval, u32 __user * uaddr,
38			      u32 oldval, u32 newval)
39{
40	int ret = 0;
41	u32 val, tmp, flags;
42
43	if (!access_ok(uaddr, sizeof(u32)))
44		return -EFAULT;
45
46	smp_mb();
47	asm volatile ("       movi    $ta, #0\n"
48		      "1:     llw     %1, [%6 + $ta]\n"
49		      "       sub     %3, %1, %4\n"
50		      "       cmovz   %2, %5, %3\n"
51		      "       cmovn   %2, %1, %3\n"
52		      "2:     scw     %2, [%6 + $ta]\n"
53		      "       beqz    %2, 1b\n"
54		      "3:\n                   " __futex_atomic_ex_table("%7")
55		      :"+&r"(ret), "=&r"(val), "=&r"(tmp), "=&r"(flags)
56		      :"r"(oldval), "r"(newval), "r"(uaddr), "i"(-EFAULT)
57		      :"$ta", "memory");
58	smp_mb();
59
60	*uval = val;
61	return ret;
62}
63
64static inline int
65arch_futex_atomic_op_inuser(int op, int oparg, int *oval, u32 __user *uaddr)
66{
67	int oldval = 0, ret;
68
69	if (!access_ok(uaddr, sizeof(u32)))
70		return -EFAULT;
71	switch (op) {
72	case FUTEX_OP_SET:
73		__futex_atomic_op("move	%0, %3", ret, oldval, tmp, uaddr,
74				  oparg);
75		break;
76	case FUTEX_OP_ADD:
77		__futex_atomic_op("add	%0, %1, %3", ret, oldval, tmp, uaddr,
78				  oparg);
79		break;
80	case FUTEX_OP_OR:
81		__futex_atomic_op("or	%0, %1, %3", ret, oldval, tmp, uaddr,
82				  oparg);
83		break;
84	case FUTEX_OP_ANDN:
85		__futex_atomic_op("and	%0, %1, %3", ret, oldval, tmp, uaddr,
86				  ~oparg);
87		break;
88	case FUTEX_OP_XOR:
89		__futex_atomic_op("xor	%0, %1, %3", ret, oldval, tmp, uaddr,
90				  oparg);
91		break;
92	default:
93		ret = -ENOSYS;
94	}
95
96	if (!ret)
97		*oval = oldval;
98
99	return ret;
100}
101#endif /* __NDS32_FUTEX_H__ */
102