1/* SPDX-License-Identifier: GPL-2.0 */
2#ifndef _ASM_MICROBLAZE_FUTEX_H
3#define _ASM_MICROBLAZE_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_op(insn, ret, oldval, uaddr, oparg) \
12({									\
13	__asm__ __volatile__ (						\
14			"1:	lwx	%0, %2, r0; "			\
15				insn					\
16			"2:	swx	%1, %2, r0;			\
17				addic	%1, r0, 0;			\
18				bnei	%1, 1b;				\
19			3:						\
20			.section .fixup,\"ax\";				\
21			4:	brid	3b;				\
22				addik	%1, r0, %3;			\
23			.previous;					\
24			.section __ex_table,\"a\";			\
25			.word	1b,4b,2b,4b;				\
26			.previous;"					\
27	: "=&r" (oldval), "=&r" (ret)					\
28	: "r" (uaddr), "i" (-EFAULT), "r" (oparg)			\
29	);								\
30})
31
32static inline int
33arch_futex_atomic_op_inuser(int op, int oparg, int *oval, u32 __user *uaddr)
34{
35	int oldval = 0, ret;
36
37	if (!access_ok(uaddr, sizeof(u32)))
38		return -EFAULT;
39
40	switch (op) {
41	case FUTEX_OP_SET:
42		__futex_atomic_op("or %1,%4,%4;", ret, oldval, uaddr, oparg);
43		break;
44	case FUTEX_OP_ADD:
45		__futex_atomic_op("add %1,%0,%4;", ret, oldval, uaddr, oparg);
46		break;
47	case FUTEX_OP_OR:
48		__futex_atomic_op("or %1,%0,%4;", ret, oldval, uaddr, oparg);
49		break;
50	case FUTEX_OP_ANDN:
51		__futex_atomic_op("andn %1,%0,%4;", ret, oldval, uaddr, oparg);
52		break;
53	case FUTEX_OP_XOR:
54		__futex_atomic_op("xor %1,%0,%4;", ret, oldval, uaddr, oparg);
55		break;
56	default:
57		ret = -ENOSYS;
58	}
59
60	if (!ret)
61		*oval = oldval;
62
63	return ret;
64}
65
66static inline int
67futex_atomic_cmpxchg_inatomic(u32 *uval, u32 __user *uaddr,
68			      u32 oldval, u32 newval)
69{
70	int ret = 0, cmp;
71	u32 prev;
72
73	if (!access_ok(uaddr, sizeof(u32)))
74		return -EFAULT;
75
76	__asm__ __volatile__ ("1:	lwx	%1, %3, r0;		\
77					cmp	%2, %1, %4;		\
78					bnei	%2, 3f;			\
79				2:	swx	%5, %3, r0;		\
80					addic	%2, r0, 0;		\
81					bnei	%2, 1b;			\
82				3:					\
83				.section .fixup,\"ax\";			\
84				4:	brid	3b;			\
85					addik	%0, r0, %6;		\
86				.previous;				\
87				.section __ex_table,\"a\";		\
88				.word	1b,4b,2b,4b;			\
89				.previous;"				\
90		: "+r" (ret), "=&r" (prev), "=&r"(cmp)	\
91		: "r" (uaddr), "r" (oldval), "r" (newval), "i" (-EFAULT));
92
93	*uval = prev;
94	return ret;
95}
96
97#endif /* __KERNEL__ */
98
99#endif
100