18c2ecf20Sopenharmony_ci/* SPDX-License-Identifier: GPL-2.0-only */
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * Copyright (C) 2012 ARM Ltd.
48c2ecf20Sopenharmony_ci */
58c2ecf20Sopenharmony_ci#ifndef __ASM_FUTEX_H
68c2ecf20Sopenharmony_ci#define __ASM_FUTEX_H
78c2ecf20Sopenharmony_ci
88c2ecf20Sopenharmony_ci#include <linux/futex.h>
98c2ecf20Sopenharmony_ci#include <linux/uaccess.h>
108c2ecf20Sopenharmony_ci
118c2ecf20Sopenharmony_ci#include <asm/errno.h>
128c2ecf20Sopenharmony_ci
138c2ecf20Sopenharmony_ci#define FUTEX_MAX_LOOPS	128 /* What's the largest number you can think of? */
148c2ecf20Sopenharmony_ci
158c2ecf20Sopenharmony_ci#define __futex_atomic_op(insn, ret, oldval, uaddr, tmp, oparg)		\
168c2ecf20Sopenharmony_cido {									\
178c2ecf20Sopenharmony_ci	unsigned int loops = FUTEX_MAX_LOOPS;				\
188c2ecf20Sopenharmony_ci									\
198c2ecf20Sopenharmony_ci	uaccess_enable();						\
208c2ecf20Sopenharmony_ci	asm volatile(							\
218c2ecf20Sopenharmony_ci"	prfm	pstl1strm, %2\n"					\
228c2ecf20Sopenharmony_ci"1:	ldxr	%w1, %2\n"						\
238c2ecf20Sopenharmony_ci	insn "\n"							\
248c2ecf20Sopenharmony_ci"2:	stlxr	%w0, %w3, %2\n"						\
258c2ecf20Sopenharmony_ci"	cbz	%w0, 3f\n"						\
268c2ecf20Sopenharmony_ci"	sub	%w4, %w4, %w0\n"					\
278c2ecf20Sopenharmony_ci"	cbnz	%w4, 1b\n"						\
288c2ecf20Sopenharmony_ci"	mov	%w0, %w7\n"						\
298c2ecf20Sopenharmony_ci"3:\n"									\
308c2ecf20Sopenharmony_ci"	dmb	ish\n"							\
318c2ecf20Sopenharmony_ci"	.pushsection .fixup,\"ax\"\n"					\
328c2ecf20Sopenharmony_ci"	.align	2\n"							\
338c2ecf20Sopenharmony_ci"4:	mov	%w0, %w6\n"						\
348c2ecf20Sopenharmony_ci"	b	3b\n"							\
358c2ecf20Sopenharmony_ci"	.popsection\n"							\
368c2ecf20Sopenharmony_ci	_ASM_EXTABLE(1b, 4b)						\
378c2ecf20Sopenharmony_ci	_ASM_EXTABLE(2b, 4b)						\
388c2ecf20Sopenharmony_ci	: "=&r" (ret), "=&r" (oldval), "+Q" (*uaddr), "=&r" (tmp),	\
398c2ecf20Sopenharmony_ci	  "+r" (loops)							\
408c2ecf20Sopenharmony_ci	: "r" (oparg), "Ir" (-EFAULT), "Ir" (-EAGAIN)			\
418c2ecf20Sopenharmony_ci	: "memory");							\
428c2ecf20Sopenharmony_ci	uaccess_disable();						\
438c2ecf20Sopenharmony_ci} while (0)
448c2ecf20Sopenharmony_ci
458c2ecf20Sopenharmony_cistatic inline int
468c2ecf20Sopenharmony_ciarch_futex_atomic_op_inuser(int op, int oparg, int *oval, u32 __user *_uaddr)
478c2ecf20Sopenharmony_ci{
488c2ecf20Sopenharmony_ci	int oldval = 0, ret, tmp;
498c2ecf20Sopenharmony_ci	u32 __user *uaddr = __uaccess_mask_ptr(_uaddr);
508c2ecf20Sopenharmony_ci
518c2ecf20Sopenharmony_ci	if (!access_ok(_uaddr, sizeof(u32)))
528c2ecf20Sopenharmony_ci		return -EFAULT;
538c2ecf20Sopenharmony_ci
548c2ecf20Sopenharmony_ci	switch (op) {
558c2ecf20Sopenharmony_ci	case FUTEX_OP_SET:
568c2ecf20Sopenharmony_ci		__futex_atomic_op("mov	%w3, %w5",
578c2ecf20Sopenharmony_ci				  ret, oldval, uaddr, tmp, oparg);
588c2ecf20Sopenharmony_ci		break;
598c2ecf20Sopenharmony_ci	case FUTEX_OP_ADD:
608c2ecf20Sopenharmony_ci		__futex_atomic_op("add	%w3, %w1, %w5",
618c2ecf20Sopenharmony_ci				  ret, oldval, uaddr, tmp, oparg);
628c2ecf20Sopenharmony_ci		break;
638c2ecf20Sopenharmony_ci	case FUTEX_OP_OR:
648c2ecf20Sopenharmony_ci		__futex_atomic_op("orr	%w3, %w1, %w5",
658c2ecf20Sopenharmony_ci				  ret, oldval, uaddr, tmp, oparg);
668c2ecf20Sopenharmony_ci		break;
678c2ecf20Sopenharmony_ci	case FUTEX_OP_ANDN:
688c2ecf20Sopenharmony_ci		__futex_atomic_op("and	%w3, %w1, %w5",
698c2ecf20Sopenharmony_ci				  ret, oldval, uaddr, tmp, ~oparg);
708c2ecf20Sopenharmony_ci		break;
718c2ecf20Sopenharmony_ci	case FUTEX_OP_XOR:
728c2ecf20Sopenharmony_ci		__futex_atomic_op("eor	%w3, %w1, %w5",
738c2ecf20Sopenharmony_ci				  ret, oldval, uaddr, tmp, oparg);
748c2ecf20Sopenharmony_ci		break;
758c2ecf20Sopenharmony_ci	default:
768c2ecf20Sopenharmony_ci		ret = -ENOSYS;
778c2ecf20Sopenharmony_ci	}
788c2ecf20Sopenharmony_ci
798c2ecf20Sopenharmony_ci	if (!ret)
808c2ecf20Sopenharmony_ci		*oval = oldval;
818c2ecf20Sopenharmony_ci
828c2ecf20Sopenharmony_ci	return ret;
838c2ecf20Sopenharmony_ci}
848c2ecf20Sopenharmony_ci
858c2ecf20Sopenharmony_cistatic inline int
868c2ecf20Sopenharmony_cifutex_atomic_cmpxchg_inatomic(u32 *uval, u32 __user *_uaddr,
878c2ecf20Sopenharmony_ci			      u32 oldval, u32 newval)
888c2ecf20Sopenharmony_ci{
898c2ecf20Sopenharmony_ci	int ret = 0;
908c2ecf20Sopenharmony_ci	unsigned int loops = FUTEX_MAX_LOOPS;
918c2ecf20Sopenharmony_ci	u32 val, tmp;
928c2ecf20Sopenharmony_ci	u32 __user *uaddr;
938c2ecf20Sopenharmony_ci
948c2ecf20Sopenharmony_ci	if (!access_ok(_uaddr, sizeof(u32)))
958c2ecf20Sopenharmony_ci		return -EFAULT;
968c2ecf20Sopenharmony_ci
978c2ecf20Sopenharmony_ci	uaddr = __uaccess_mask_ptr(_uaddr);
988c2ecf20Sopenharmony_ci	uaccess_enable();
998c2ecf20Sopenharmony_ci	asm volatile("// futex_atomic_cmpxchg_inatomic\n"
1008c2ecf20Sopenharmony_ci"	prfm	pstl1strm, %2\n"
1018c2ecf20Sopenharmony_ci"1:	ldxr	%w1, %2\n"
1028c2ecf20Sopenharmony_ci"	sub	%w3, %w1, %w5\n"
1038c2ecf20Sopenharmony_ci"	cbnz	%w3, 4f\n"
1048c2ecf20Sopenharmony_ci"2:	stlxr	%w3, %w6, %2\n"
1058c2ecf20Sopenharmony_ci"	cbz	%w3, 3f\n"
1068c2ecf20Sopenharmony_ci"	sub	%w4, %w4, %w3\n"
1078c2ecf20Sopenharmony_ci"	cbnz	%w4, 1b\n"
1088c2ecf20Sopenharmony_ci"	mov	%w0, %w8\n"
1098c2ecf20Sopenharmony_ci"3:\n"
1108c2ecf20Sopenharmony_ci"	dmb	ish\n"
1118c2ecf20Sopenharmony_ci"4:\n"
1128c2ecf20Sopenharmony_ci"	.pushsection .fixup,\"ax\"\n"
1138c2ecf20Sopenharmony_ci"5:	mov	%w0, %w7\n"
1148c2ecf20Sopenharmony_ci"	b	4b\n"
1158c2ecf20Sopenharmony_ci"	.popsection\n"
1168c2ecf20Sopenharmony_ci	_ASM_EXTABLE(1b, 5b)
1178c2ecf20Sopenharmony_ci	_ASM_EXTABLE(2b, 5b)
1188c2ecf20Sopenharmony_ci	: "+r" (ret), "=&r" (val), "+Q" (*uaddr), "=&r" (tmp), "+r" (loops)
1198c2ecf20Sopenharmony_ci	: "r" (oldval), "r" (newval), "Ir" (-EFAULT), "Ir" (-EAGAIN)
1208c2ecf20Sopenharmony_ci	: "memory");
1218c2ecf20Sopenharmony_ci	uaccess_disable();
1228c2ecf20Sopenharmony_ci
1238c2ecf20Sopenharmony_ci	if (!ret)
1248c2ecf20Sopenharmony_ci		*uval = val;
1258c2ecf20Sopenharmony_ci
1268c2ecf20Sopenharmony_ci	return ret;
1278c2ecf20Sopenharmony_ci}
1288c2ecf20Sopenharmony_ci
1298c2ecf20Sopenharmony_ci#endif /* __ASM_FUTEX_H */
130