18c2ecf20Sopenharmony_ci/* SPDX-License-Identifier: GPL-2.0 */
28c2ecf20Sopenharmony_ci#ifndef _ASM_ARM_FUTEX_H
38c2ecf20Sopenharmony_ci#define _ASM_ARM_FUTEX_H
48c2ecf20Sopenharmony_ci
58c2ecf20Sopenharmony_ci#ifdef __KERNEL__
68c2ecf20Sopenharmony_ci
78c2ecf20Sopenharmony_ci#include <linux/futex.h>
88c2ecf20Sopenharmony_ci#include <linux/uaccess.h>
98c2ecf20Sopenharmony_ci#include <asm/errno.h>
108c2ecf20Sopenharmony_ci
118c2ecf20Sopenharmony_ci#define __futex_atomic_ex_table(err_reg)			\
128c2ecf20Sopenharmony_ci	"3:\n"							\
138c2ecf20Sopenharmony_ci	"	ex_entry	1b, 4f\n"			\
148c2ecf20Sopenharmony_ci	"	ex_entry	2b, 4f\n"			\
158c2ecf20Sopenharmony_ci	"	.pushsection .text.fixup,\"ax\"\n"		\
168c2ecf20Sopenharmony_ci	"	.align	2\n"					\
178c2ecf20Sopenharmony_ci	"4:	mov	%0, " err_reg "\n"			\
188c2ecf20Sopenharmony_ci	"	b	3b\n"					\
198c2ecf20Sopenharmony_ci	"	.popsection"
208c2ecf20Sopenharmony_ci
218c2ecf20Sopenharmony_ci#ifdef CONFIG_SMP
228c2ecf20Sopenharmony_ci
238c2ecf20Sopenharmony_ci#define __futex_atomic_op(insn, ret, oldval, tmp, uaddr, oparg)	\
248c2ecf20Sopenharmony_ci({								\
258c2ecf20Sopenharmony_ci	unsigned int __ua_flags;				\
268c2ecf20Sopenharmony_ci	smp_mb();						\
278c2ecf20Sopenharmony_ci	prefetchw(uaddr);					\
288c2ecf20Sopenharmony_ci	__ua_flags = uaccess_save_and_enable();			\
298c2ecf20Sopenharmony_ci	__asm__ __volatile__(					\
308c2ecf20Sopenharmony_ci	"1:	ldrex	%1, [%3]\n"				\
318c2ecf20Sopenharmony_ci	"	" insn "\n"					\
328c2ecf20Sopenharmony_ci	"2:	strex	%2, %0, [%3]\n"				\
338c2ecf20Sopenharmony_ci	"	teq	%2, #0\n"				\
348c2ecf20Sopenharmony_ci	"	bne	1b\n"					\
358c2ecf20Sopenharmony_ci	"	mov	%0, #0\n"				\
368c2ecf20Sopenharmony_ci	__futex_atomic_ex_table("%5")				\
378c2ecf20Sopenharmony_ci	: "=&r" (ret), "=&r" (oldval), "=&r" (tmp)		\
388c2ecf20Sopenharmony_ci	: "r" (uaddr), "r" (oparg), "Ir" (-EFAULT)		\
398c2ecf20Sopenharmony_ci	: "cc", "memory");					\
408c2ecf20Sopenharmony_ci	uaccess_restore(__ua_flags);				\
418c2ecf20Sopenharmony_ci})
428c2ecf20Sopenharmony_ci
438c2ecf20Sopenharmony_cistatic inline int
448c2ecf20Sopenharmony_cifutex_atomic_cmpxchg_inatomic(u32 *uval, u32 __user *uaddr,
458c2ecf20Sopenharmony_ci			      u32 oldval, u32 newval)
468c2ecf20Sopenharmony_ci{
478c2ecf20Sopenharmony_ci	unsigned int __ua_flags;
488c2ecf20Sopenharmony_ci	int ret;
498c2ecf20Sopenharmony_ci	u32 val;
508c2ecf20Sopenharmony_ci
518c2ecf20Sopenharmony_ci	if (!access_ok(uaddr, sizeof(u32)))
528c2ecf20Sopenharmony_ci		return -EFAULT;
538c2ecf20Sopenharmony_ci
548c2ecf20Sopenharmony_ci	smp_mb();
558c2ecf20Sopenharmony_ci	/* Prefetching cannot fault */
568c2ecf20Sopenharmony_ci	prefetchw(uaddr);
578c2ecf20Sopenharmony_ci	__ua_flags = uaccess_save_and_enable();
588c2ecf20Sopenharmony_ci	__asm__ __volatile__("@futex_atomic_cmpxchg_inatomic\n"
598c2ecf20Sopenharmony_ci	"1:	ldrex	%1, [%4]\n"
608c2ecf20Sopenharmony_ci	"	teq	%1, %2\n"
618c2ecf20Sopenharmony_ci	"	ite	eq	@ explicit IT needed for the 2b label\n"
628c2ecf20Sopenharmony_ci	"2:	strexeq	%0, %3, [%4]\n"
638c2ecf20Sopenharmony_ci	"	movne	%0, #0\n"
648c2ecf20Sopenharmony_ci	"	teq	%0, #0\n"
658c2ecf20Sopenharmony_ci	"	bne	1b\n"
668c2ecf20Sopenharmony_ci	__futex_atomic_ex_table("%5")
678c2ecf20Sopenharmony_ci	: "=&r" (ret), "=&r" (val)
688c2ecf20Sopenharmony_ci	: "r" (oldval), "r" (newval), "r" (uaddr), "Ir" (-EFAULT)
698c2ecf20Sopenharmony_ci	: "cc", "memory");
708c2ecf20Sopenharmony_ci	uaccess_restore(__ua_flags);
718c2ecf20Sopenharmony_ci	smp_mb();
728c2ecf20Sopenharmony_ci
738c2ecf20Sopenharmony_ci	*uval = val;
748c2ecf20Sopenharmony_ci	return ret;
758c2ecf20Sopenharmony_ci}
768c2ecf20Sopenharmony_ci
778c2ecf20Sopenharmony_ci#else /* !SMP, we can work around lack of atomic ops by disabling preemption */
788c2ecf20Sopenharmony_ci
798c2ecf20Sopenharmony_ci#include <linux/preempt.h>
808c2ecf20Sopenharmony_ci#include <asm/domain.h>
818c2ecf20Sopenharmony_ci
828c2ecf20Sopenharmony_ci#define __futex_atomic_op(insn, ret, oldval, tmp, uaddr, oparg)	\
838c2ecf20Sopenharmony_ci({								\
848c2ecf20Sopenharmony_ci	unsigned int __ua_flags = uaccess_save_and_enable();	\
858c2ecf20Sopenharmony_ci	__asm__ __volatile__(					\
868c2ecf20Sopenharmony_ci	"1:	" TUSER(ldr) "	%1, [%3]\n"			\
878c2ecf20Sopenharmony_ci	"	" insn "\n"					\
888c2ecf20Sopenharmony_ci	"2:	" TUSER(str) "	%0, [%3]\n"			\
898c2ecf20Sopenharmony_ci	"	mov	%0, #0\n"				\
908c2ecf20Sopenharmony_ci	__futex_atomic_ex_table("%5")				\
918c2ecf20Sopenharmony_ci	: "=&r" (ret), "=&r" (oldval), "=&r" (tmp)		\
928c2ecf20Sopenharmony_ci	: "r" (uaddr), "r" (oparg), "Ir" (-EFAULT)		\
938c2ecf20Sopenharmony_ci	: "cc", "memory");					\
948c2ecf20Sopenharmony_ci	uaccess_restore(__ua_flags);				\
958c2ecf20Sopenharmony_ci})
968c2ecf20Sopenharmony_ci
978c2ecf20Sopenharmony_cistatic inline int
988c2ecf20Sopenharmony_cifutex_atomic_cmpxchg_inatomic(u32 *uval, u32 __user *uaddr,
998c2ecf20Sopenharmony_ci			      u32 oldval, u32 newval)
1008c2ecf20Sopenharmony_ci{
1018c2ecf20Sopenharmony_ci	unsigned int __ua_flags;
1028c2ecf20Sopenharmony_ci	int ret = 0;
1038c2ecf20Sopenharmony_ci	u32 val;
1048c2ecf20Sopenharmony_ci
1058c2ecf20Sopenharmony_ci	if (!access_ok(uaddr, sizeof(u32)))
1068c2ecf20Sopenharmony_ci		return -EFAULT;
1078c2ecf20Sopenharmony_ci
1088c2ecf20Sopenharmony_ci	preempt_disable();
1098c2ecf20Sopenharmony_ci	__ua_flags = uaccess_save_and_enable();
1108c2ecf20Sopenharmony_ci	__asm__ __volatile__("@futex_atomic_cmpxchg_inatomic\n"
1118c2ecf20Sopenharmony_ci	"	.syntax unified\n"
1128c2ecf20Sopenharmony_ci	"1:	" TUSER(ldr) "	%1, [%4]\n"
1138c2ecf20Sopenharmony_ci	"	teq	%1, %2\n"
1148c2ecf20Sopenharmony_ci	"	it	eq	@ explicit IT needed for the 2b label\n"
1158c2ecf20Sopenharmony_ci	"2:	" TUSERCOND(str, eq) "	%3, [%4]\n"
1168c2ecf20Sopenharmony_ci	__futex_atomic_ex_table("%5")
1178c2ecf20Sopenharmony_ci	: "+r" (ret), "=&r" (val)
1188c2ecf20Sopenharmony_ci	: "r" (oldval), "r" (newval), "r" (uaddr), "Ir" (-EFAULT)
1198c2ecf20Sopenharmony_ci	: "cc", "memory");
1208c2ecf20Sopenharmony_ci	uaccess_restore(__ua_flags);
1218c2ecf20Sopenharmony_ci
1228c2ecf20Sopenharmony_ci	*uval = val;
1238c2ecf20Sopenharmony_ci	preempt_enable();
1248c2ecf20Sopenharmony_ci
1258c2ecf20Sopenharmony_ci	return ret;
1268c2ecf20Sopenharmony_ci}
1278c2ecf20Sopenharmony_ci
1288c2ecf20Sopenharmony_ci#endif /* !SMP */
1298c2ecf20Sopenharmony_ci
1308c2ecf20Sopenharmony_cistatic inline int
1318c2ecf20Sopenharmony_ciarch_futex_atomic_op_inuser(int op, int oparg, int *oval, u32 __user *uaddr)
1328c2ecf20Sopenharmony_ci{
1338c2ecf20Sopenharmony_ci	int oldval = 0, ret, tmp;
1348c2ecf20Sopenharmony_ci
1358c2ecf20Sopenharmony_ci	if (!access_ok(uaddr, sizeof(u32)))
1368c2ecf20Sopenharmony_ci		return -EFAULT;
1378c2ecf20Sopenharmony_ci
1388c2ecf20Sopenharmony_ci#ifndef CONFIG_SMP
1398c2ecf20Sopenharmony_ci	preempt_disable();
1408c2ecf20Sopenharmony_ci#endif
1418c2ecf20Sopenharmony_ci
1428c2ecf20Sopenharmony_ci	switch (op) {
1438c2ecf20Sopenharmony_ci	case FUTEX_OP_SET:
1448c2ecf20Sopenharmony_ci		__futex_atomic_op("mov	%0, %4", ret, oldval, tmp, uaddr, oparg);
1458c2ecf20Sopenharmony_ci		break;
1468c2ecf20Sopenharmony_ci	case FUTEX_OP_ADD:
1478c2ecf20Sopenharmony_ci		__futex_atomic_op("add	%0, %1, %4", ret, oldval, tmp, uaddr, oparg);
1488c2ecf20Sopenharmony_ci		break;
1498c2ecf20Sopenharmony_ci	case FUTEX_OP_OR:
1508c2ecf20Sopenharmony_ci		__futex_atomic_op("orr	%0, %1, %4", ret, oldval, tmp, uaddr, oparg);
1518c2ecf20Sopenharmony_ci		break;
1528c2ecf20Sopenharmony_ci	case FUTEX_OP_ANDN:
1538c2ecf20Sopenharmony_ci		__futex_atomic_op("and	%0, %1, %4", ret, oldval, tmp, uaddr, ~oparg);
1548c2ecf20Sopenharmony_ci		break;
1558c2ecf20Sopenharmony_ci	case FUTEX_OP_XOR:
1568c2ecf20Sopenharmony_ci		__futex_atomic_op("eor	%0, %1, %4", ret, oldval, tmp, uaddr, oparg);
1578c2ecf20Sopenharmony_ci		break;
1588c2ecf20Sopenharmony_ci	default:
1598c2ecf20Sopenharmony_ci		ret = -ENOSYS;
1608c2ecf20Sopenharmony_ci	}
1618c2ecf20Sopenharmony_ci
1628c2ecf20Sopenharmony_ci#ifndef CONFIG_SMP
1638c2ecf20Sopenharmony_ci	preempt_enable();
1648c2ecf20Sopenharmony_ci#endif
1658c2ecf20Sopenharmony_ci
1668c2ecf20Sopenharmony_ci	/*
1678c2ecf20Sopenharmony_ci	 * Store unconditionally. If ret != 0 the extra store is the least
1688c2ecf20Sopenharmony_ci	 * of the worries but GCC cannot figure out that __futex_atomic_op()
1698c2ecf20Sopenharmony_ci	 * is either setting ret to -EFAULT or storing the old value in
1708c2ecf20Sopenharmony_ci	 * oldval which results in a uninitialized warning at the call site.
1718c2ecf20Sopenharmony_ci	 */
1728c2ecf20Sopenharmony_ci	*oval = oldval;
1738c2ecf20Sopenharmony_ci
1748c2ecf20Sopenharmony_ci	return ret;
1758c2ecf20Sopenharmony_ci}
1768c2ecf20Sopenharmony_ci
1778c2ecf20Sopenharmony_ci#endif /* __KERNEL__ */
1788c2ecf20Sopenharmony_ci#endif /* _ASM_ARM_FUTEX_H */
179