18c2ecf20Sopenharmony_ci/*
28c2ecf20Sopenharmony_ci * This file is subject to the terms and conditions of the GNU General Public
38c2ecf20Sopenharmony_ci * License.  See the file "COPYING" in the main directory of this archive
48c2ecf20Sopenharmony_ci * for more details.
58c2ecf20Sopenharmony_ci *
68c2ecf20Sopenharmony_ci * Copyright (c) 2006  Ralf Baechle (ralf@linux-mips.org)
78c2ecf20Sopenharmony_ci */
88c2ecf20Sopenharmony_ci#ifndef _ASM_FUTEX_H
98c2ecf20Sopenharmony_ci#define _ASM_FUTEX_H
108c2ecf20Sopenharmony_ci
118c2ecf20Sopenharmony_ci#ifdef __KERNEL__
128c2ecf20Sopenharmony_ci
138c2ecf20Sopenharmony_ci#include <linux/futex.h>
148c2ecf20Sopenharmony_ci#include <linux/uaccess.h>
158c2ecf20Sopenharmony_ci#include <asm/asm-eva.h>
168c2ecf20Sopenharmony_ci#include <asm/barrier.h>
178c2ecf20Sopenharmony_ci#include <asm/compiler.h>
188c2ecf20Sopenharmony_ci#include <asm/errno.h>
198c2ecf20Sopenharmony_ci#include <asm/sync.h>
208c2ecf20Sopenharmony_ci#include <asm/war.h>
218c2ecf20Sopenharmony_ci
228c2ecf20Sopenharmony_ci#define __futex_atomic_op(insn, ret, oldval, uaddr, oparg)		\
238c2ecf20Sopenharmony_ci{									\
248c2ecf20Sopenharmony_ci	if (cpu_has_llsc && IS_ENABLED(CONFIG_WAR_R10000_LLSC)) {	\
258c2ecf20Sopenharmony_ci		__asm__ __volatile__(					\
268c2ecf20Sopenharmony_ci		"	.set	push				\n"	\
278c2ecf20Sopenharmony_ci		"	.set	noat				\n"	\
288c2ecf20Sopenharmony_ci		"	.set	push				\n"	\
298c2ecf20Sopenharmony_ci		"	.set	arch=r4000			\n"	\
308c2ecf20Sopenharmony_ci		"1:	ll	%1, %4	# __futex_atomic_op	\n"	\
318c2ecf20Sopenharmony_ci		"	.set	pop				\n"	\
328c2ecf20Sopenharmony_ci		"	" insn	"				\n"	\
338c2ecf20Sopenharmony_ci		"	.set	arch=r4000			\n"	\
348c2ecf20Sopenharmony_ci		"2:	sc	$1, %2				\n"	\
358c2ecf20Sopenharmony_ci		"	beqzl	$1, 1b				\n"	\
368c2ecf20Sopenharmony_ci		__stringify(__WEAK_LLSC_MB) "			\n"	\
378c2ecf20Sopenharmony_ci		"3:						\n"	\
388c2ecf20Sopenharmony_ci		"	.insn					\n"	\
398c2ecf20Sopenharmony_ci		"	.set	pop				\n"	\
408c2ecf20Sopenharmony_ci		"	.section .fixup,\"ax\"			\n"	\
418c2ecf20Sopenharmony_ci		"4:	li	%0, %6				\n"	\
428c2ecf20Sopenharmony_ci		"	j	3b				\n"	\
438c2ecf20Sopenharmony_ci		"	.previous				\n"	\
448c2ecf20Sopenharmony_ci		"	.section __ex_table,\"a\"		\n"	\
458c2ecf20Sopenharmony_ci		"	"__UA_ADDR "\t1b, 4b			\n"	\
468c2ecf20Sopenharmony_ci		"	"__UA_ADDR "\t2b, 4b			\n"	\
478c2ecf20Sopenharmony_ci		"	.previous				\n"	\
488c2ecf20Sopenharmony_ci		: "=r" (ret), "=&r" (oldval),				\
498c2ecf20Sopenharmony_ci		  "=" GCC_OFF_SMALL_ASM() (*uaddr)				\
508c2ecf20Sopenharmony_ci		: "0" (0), GCC_OFF_SMALL_ASM() (*uaddr), "Jr" (oparg),	\
518c2ecf20Sopenharmony_ci		  "i" (-EFAULT)						\
528c2ecf20Sopenharmony_ci		: "memory");						\
538c2ecf20Sopenharmony_ci	} else if (cpu_has_llsc) {					\
548c2ecf20Sopenharmony_ci		__asm__ __volatile__(					\
558c2ecf20Sopenharmony_ci		"	.set	push				\n"	\
568c2ecf20Sopenharmony_ci		"	.set	noat				\n"	\
578c2ecf20Sopenharmony_ci		"	.set	push				\n"	\
588c2ecf20Sopenharmony_ci		"	.set	"MIPS_ISA_ARCH_LEVEL"		\n"	\
598c2ecf20Sopenharmony_ci		"	" __SYNC(full, loongson3_war) "		\n"	\
608c2ecf20Sopenharmony_ci		"1:	"user_ll("%1", "%4")" # __futex_atomic_op\n"	\
618c2ecf20Sopenharmony_ci		"	.set	pop				\n"	\
628c2ecf20Sopenharmony_ci		"	" insn	"				\n"	\
638c2ecf20Sopenharmony_ci		"	.set	"MIPS_ISA_ARCH_LEVEL"		\n"	\
648c2ecf20Sopenharmony_ci		"2:	"user_sc("$1", "%2")"			\n"	\
658c2ecf20Sopenharmony_ci		"	beqz	$1, 1b				\n"	\
668c2ecf20Sopenharmony_ci		__stringify(__WEAK_LLSC_MB) "			\n"	\
678c2ecf20Sopenharmony_ci		"3:						\n"	\
688c2ecf20Sopenharmony_ci		"	.insn					\n"	\
698c2ecf20Sopenharmony_ci		"	.set	pop				\n"	\
708c2ecf20Sopenharmony_ci		"	.section .fixup,\"ax\"			\n"	\
718c2ecf20Sopenharmony_ci		"4:	li	%0, %6				\n"	\
728c2ecf20Sopenharmony_ci		"	j	3b				\n"	\
738c2ecf20Sopenharmony_ci		"	.previous				\n"	\
748c2ecf20Sopenharmony_ci		"	.section __ex_table,\"a\"		\n"	\
758c2ecf20Sopenharmony_ci		"	"__UA_ADDR "\t1b, 4b			\n"	\
768c2ecf20Sopenharmony_ci		"	"__UA_ADDR "\t2b, 4b			\n"	\
778c2ecf20Sopenharmony_ci		"	.previous				\n"	\
788c2ecf20Sopenharmony_ci		: "=r" (ret), "=&r" (oldval),				\
798c2ecf20Sopenharmony_ci		  "=" GCC_OFF_SMALL_ASM() (*uaddr)				\
808c2ecf20Sopenharmony_ci		: "0" (0), GCC_OFF_SMALL_ASM() (*uaddr), "Jr" (oparg),	\
818c2ecf20Sopenharmony_ci		  "i" (-EFAULT)						\
828c2ecf20Sopenharmony_ci		: "memory");						\
838c2ecf20Sopenharmony_ci	} else								\
848c2ecf20Sopenharmony_ci		ret = -ENOSYS;						\
858c2ecf20Sopenharmony_ci}
868c2ecf20Sopenharmony_ci
878c2ecf20Sopenharmony_cistatic inline int
888c2ecf20Sopenharmony_ciarch_futex_atomic_op_inuser(int op, int oparg, int *oval, u32 __user *uaddr)
898c2ecf20Sopenharmony_ci{
908c2ecf20Sopenharmony_ci	int oldval = 0, ret;
918c2ecf20Sopenharmony_ci
928c2ecf20Sopenharmony_ci	if (!access_ok(uaddr, sizeof(u32)))
938c2ecf20Sopenharmony_ci		return -EFAULT;
948c2ecf20Sopenharmony_ci
958c2ecf20Sopenharmony_ci	switch (op) {
968c2ecf20Sopenharmony_ci	case FUTEX_OP_SET:
978c2ecf20Sopenharmony_ci		__futex_atomic_op("move $1, %z5", ret, oldval, uaddr, oparg);
988c2ecf20Sopenharmony_ci		break;
998c2ecf20Sopenharmony_ci
1008c2ecf20Sopenharmony_ci	case FUTEX_OP_ADD:
1018c2ecf20Sopenharmony_ci		__futex_atomic_op("addu $1, %1, %z5",
1028c2ecf20Sopenharmony_ci				  ret, oldval, uaddr, oparg);
1038c2ecf20Sopenharmony_ci		break;
1048c2ecf20Sopenharmony_ci	case FUTEX_OP_OR:
1058c2ecf20Sopenharmony_ci		__futex_atomic_op("or	$1, %1, %z5",
1068c2ecf20Sopenharmony_ci				  ret, oldval, uaddr, oparg);
1078c2ecf20Sopenharmony_ci		break;
1088c2ecf20Sopenharmony_ci	case FUTEX_OP_ANDN:
1098c2ecf20Sopenharmony_ci		__futex_atomic_op("and	$1, %1, %z5",
1108c2ecf20Sopenharmony_ci				  ret, oldval, uaddr, ~oparg);
1118c2ecf20Sopenharmony_ci		break;
1128c2ecf20Sopenharmony_ci	case FUTEX_OP_XOR:
1138c2ecf20Sopenharmony_ci		__futex_atomic_op("xor	$1, %1, %z5",
1148c2ecf20Sopenharmony_ci				  ret, oldval, uaddr, oparg);
1158c2ecf20Sopenharmony_ci		break;
1168c2ecf20Sopenharmony_ci	default:
1178c2ecf20Sopenharmony_ci		ret = -ENOSYS;
1188c2ecf20Sopenharmony_ci	}
1198c2ecf20Sopenharmony_ci
1208c2ecf20Sopenharmony_ci	if (!ret)
1218c2ecf20Sopenharmony_ci		*oval = oldval;
1228c2ecf20Sopenharmony_ci
1238c2ecf20Sopenharmony_ci	return ret;
1248c2ecf20Sopenharmony_ci}
1258c2ecf20Sopenharmony_ci
1268c2ecf20Sopenharmony_cistatic inline int
1278c2ecf20Sopenharmony_cifutex_atomic_cmpxchg_inatomic(u32 *uval, u32 __user *uaddr,
1288c2ecf20Sopenharmony_ci			      u32 oldval, u32 newval)
1298c2ecf20Sopenharmony_ci{
1308c2ecf20Sopenharmony_ci	int ret = 0;
1318c2ecf20Sopenharmony_ci	u32 val;
1328c2ecf20Sopenharmony_ci
1338c2ecf20Sopenharmony_ci	if (!access_ok(uaddr, sizeof(u32)))
1348c2ecf20Sopenharmony_ci		return -EFAULT;
1358c2ecf20Sopenharmony_ci
1368c2ecf20Sopenharmony_ci	if (cpu_has_llsc && IS_ENABLED(CONFIG_WAR_R10000_LLSC)) {
1378c2ecf20Sopenharmony_ci		__asm__ __volatile__(
1388c2ecf20Sopenharmony_ci		"# futex_atomic_cmpxchg_inatomic			\n"
1398c2ecf20Sopenharmony_ci		"	.set	push					\n"
1408c2ecf20Sopenharmony_ci		"	.set	noat					\n"
1418c2ecf20Sopenharmony_ci		"	.set	push					\n"
1428c2ecf20Sopenharmony_ci		"	.set	arch=r4000				\n"
1438c2ecf20Sopenharmony_ci		"1:	ll	%1, %3					\n"
1448c2ecf20Sopenharmony_ci		"	bne	%1, %z4, 3f				\n"
1458c2ecf20Sopenharmony_ci		"	.set	pop					\n"
1468c2ecf20Sopenharmony_ci		"	move	$1, %z5					\n"
1478c2ecf20Sopenharmony_ci		"	.set	arch=r4000				\n"
1488c2ecf20Sopenharmony_ci		"2:	sc	$1, %2					\n"
1498c2ecf20Sopenharmony_ci		"	beqzl	$1, 1b					\n"
1508c2ecf20Sopenharmony_ci		__stringify(__WEAK_LLSC_MB) "				\n"
1518c2ecf20Sopenharmony_ci		"3:							\n"
1528c2ecf20Sopenharmony_ci		"	.insn						\n"
1538c2ecf20Sopenharmony_ci		"	.set	pop					\n"
1548c2ecf20Sopenharmony_ci		"	.section .fixup,\"ax\"				\n"
1558c2ecf20Sopenharmony_ci		"4:	li	%0, %6					\n"
1568c2ecf20Sopenharmony_ci		"	j	3b					\n"
1578c2ecf20Sopenharmony_ci		"	.previous					\n"
1588c2ecf20Sopenharmony_ci		"	.section __ex_table,\"a\"			\n"
1598c2ecf20Sopenharmony_ci		"	"__UA_ADDR "\t1b, 4b				\n"
1608c2ecf20Sopenharmony_ci		"	"__UA_ADDR "\t2b, 4b				\n"
1618c2ecf20Sopenharmony_ci		"	.previous					\n"
1628c2ecf20Sopenharmony_ci		: "+r" (ret), "=&r" (val), "=" GCC_OFF_SMALL_ASM() (*uaddr)
1638c2ecf20Sopenharmony_ci		: GCC_OFF_SMALL_ASM() (*uaddr), "Jr" (oldval), "Jr" (newval),
1648c2ecf20Sopenharmony_ci		  "i" (-EFAULT)
1658c2ecf20Sopenharmony_ci		: "memory");
1668c2ecf20Sopenharmony_ci	} else if (cpu_has_llsc) {
1678c2ecf20Sopenharmony_ci		__asm__ __volatile__(
1688c2ecf20Sopenharmony_ci		"# futex_atomic_cmpxchg_inatomic			\n"
1698c2ecf20Sopenharmony_ci		"	.set	push					\n"
1708c2ecf20Sopenharmony_ci		"	.set	noat					\n"
1718c2ecf20Sopenharmony_ci		"	.set	push					\n"
1728c2ecf20Sopenharmony_ci		"	.set	"MIPS_ISA_ARCH_LEVEL"			\n"
1738c2ecf20Sopenharmony_ci		"	" __SYNC(full, loongson3_war) "			\n"
1748c2ecf20Sopenharmony_ci		"1:	"user_ll("%1", "%3")"				\n"
1758c2ecf20Sopenharmony_ci		"	bne	%1, %z4, 3f				\n"
1768c2ecf20Sopenharmony_ci		"	.set	pop					\n"
1778c2ecf20Sopenharmony_ci		"	move	$1, %z5					\n"
1788c2ecf20Sopenharmony_ci		"	.set	"MIPS_ISA_ARCH_LEVEL"			\n"
1798c2ecf20Sopenharmony_ci		"2:	"user_sc("$1", "%2")"				\n"
1808c2ecf20Sopenharmony_ci		"	beqz	$1, 1b					\n"
1818c2ecf20Sopenharmony_ci		"3:	" __SYNC_ELSE(full, loongson3_war, __WEAK_LLSC_MB) "\n"
1828c2ecf20Sopenharmony_ci		"	.insn						\n"
1838c2ecf20Sopenharmony_ci		"	.set	pop					\n"
1848c2ecf20Sopenharmony_ci		"	.section .fixup,\"ax\"				\n"
1858c2ecf20Sopenharmony_ci		"4:	li	%0, %6					\n"
1868c2ecf20Sopenharmony_ci		"	j	3b					\n"
1878c2ecf20Sopenharmony_ci		"	.previous					\n"
1888c2ecf20Sopenharmony_ci		"	.section __ex_table,\"a\"			\n"
1898c2ecf20Sopenharmony_ci		"	"__UA_ADDR "\t1b, 4b				\n"
1908c2ecf20Sopenharmony_ci		"	"__UA_ADDR "\t2b, 4b				\n"
1918c2ecf20Sopenharmony_ci		"	.previous					\n"
1928c2ecf20Sopenharmony_ci		: "+r" (ret), "=&r" (val), "=" GCC_OFF_SMALL_ASM() (*uaddr)
1938c2ecf20Sopenharmony_ci		: GCC_OFF_SMALL_ASM() (*uaddr), "Jr" (oldval), "Jr" (newval),
1948c2ecf20Sopenharmony_ci		  "i" (-EFAULT)
1958c2ecf20Sopenharmony_ci		: "memory");
1968c2ecf20Sopenharmony_ci	} else
1978c2ecf20Sopenharmony_ci		return -ENOSYS;
1988c2ecf20Sopenharmony_ci
1998c2ecf20Sopenharmony_ci	*uval = val;
2008c2ecf20Sopenharmony_ci	return ret;
2018c2ecf20Sopenharmony_ci}
2028c2ecf20Sopenharmony_ci
2038c2ecf20Sopenharmony_ci#endif
2048c2ecf20Sopenharmony_ci#endif /* _ASM_FUTEX_H */
205