162306a36Sopenharmony_ci/*
262306a36Sopenharmony_ci * This file is subject to the terms and conditions of the GNU General Public
362306a36Sopenharmony_ci * License.  See the file "COPYING" in the main directory of this archive
462306a36Sopenharmony_ci * for more details.
562306a36Sopenharmony_ci *
662306a36Sopenharmony_ci * Copyright (c) 2006  Ralf Baechle (ralf@linux-mips.org)
762306a36Sopenharmony_ci */
862306a36Sopenharmony_ci#ifndef _ASM_FUTEX_H
962306a36Sopenharmony_ci#define _ASM_FUTEX_H
1062306a36Sopenharmony_ci
1162306a36Sopenharmony_ci#ifdef __KERNEL__
1262306a36Sopenharmony_ci
1362306a36Sopenharmony_ci#include <linux/futex.h>
1462306a36Sopenharmony_ci#include <linux/uaccess.h>
1562306a36Sopenharmony_ci#include <asm/asm-eva.h>
1662306a36Sopenharmony_ci#include <asm/barrier.h>
1762306a36Sopenharmony_ci#include <asm/compiler.h>
1862306a36Sopenharmony_ci#include <asm/errno.h>
1962306a36Sopenharmony_ci#include <asm/sync.h>
2062306a36Sopenharmony_ci
2162306a36Sopenharmony_ci#define arch_futex_atomic_op_inuser arch_futex_atomic_op_inuser
2262306a36Sopenharmony_ci#define futex_atomic_cmpxchg_inatomic futex_atomic_cmpxchg_inatomic
2362306a36Sopenharmony_ci#include <asm-generic/futex.h>
2462306a36Sopenharmony_ci
2562306a36Sopenharmony_ci#define __futex_atomic_op(op, insn, ret, oldval, uaddr, oparg)		\
2662306a36Sopenharmony_ci{									\
2762306a36Sopenharmony_ci	if (cpu_has_llsc && IS_ENABLED(CONFIG_WAR_R10000_LLSC)) {	\
2862306a36Sopenharmony_ci		__asm__ __volatile__(					\
2962306a36Sopenharmony_ci		"	.set	push				\n"	\
3062306a36Sopenharmony_ci		"	.set	noat				\n"	\
3162306a36Sopenharmony_ci		"	.set	push				\n"	\
3262306a36Sopenharmony_ci		"	.set	arch=r4000			\n"	\
3362306a36Sopenharmony_ci		"1:	ll	%1, %4	# __futex_atomic_op	\n"	\
3462306a36Sopenharmony_ci		"	.set	pop				\n"	\
3562306a36Sopenharmony_ci		"	" insn	"				\n"	\
3662306a36Sopenharmony_ci		"	.set	arch=r4000			\n"	\
3762306a36Sopenharmony_ci		"2:	sc	$1, %2				\n"	\
3862306a36Sopenharmony_ci		"	beqzl	$1, 1b				\n"	\
3962306a36Sopenharmony_ci		__stringify(__WEAK_LLSC_MB) "			\n"	\
4062306a36Sopenharmony_ci		"3:						\n"	\
4162306a36Sopenharmony_ci		"	.insn					\n"	\
4262306a36Sopenharmony_ci		"	.set	pop				\n"	\
4362306a36Sopenharmony_ci		"	.section .fixup,\"ax\"			\n"	\
4462306a36Sopenharmony_ci		"4:	li	%0, %6				\n"	\
4562306a36Sopenharmony_ci		"	j	3b				\n"	\
4662306a36Sopenharmony_ci		"	.previous				\n"	\
4762306a36Sopenharmony_ci		"	.section __ex_table,\"a\"		\n"	\
4862306a36Sopenharmony_ci		"	"__UA_ADDR "\t1b, 4b			\n"	\
4962306a36Sopenharmony_ci		"	"__UA_ADDR "\t2b, 4b			\n"	\
5062306a36Sopenharmony_ci		"	.previous				\n"	\
5162306a36Sopenharmony_ci		: "=r" (ret), "=&r" (oldval),				\
5262306a36Sopenharmony_ci		  "=" GCC_OFF_SMALL_ASM() (*uaddr)				\
5362306a36Sopenharmony_ci		: "0" (0), GCC_OFF_SMALL_ASM() (*uaddr), "Jr" (oparg),	\
5462306a36Sopenharmony_ci		  "i" (-EFAULT)						\
5562306a36Sopenharmony_ci		: "memory");						\
5662306a36Sopenharmony_ci	} else if (cpu_has_llsc) {					\
5762306a36Sopenharmony_ci		__asm__ __volatile__(					\
5862306a36Sopenharmony_ci		"	.set	push				\n"	\
5962306a36Sopenharmony_ci		"	.set	noat				\n"	\
6062306a36Sopenharmony_ci		"	.set	push				\n"	\
6162306a36Sopenharmony_ci		"	.set	"MIPS_ISA_ARCH_LEVEL"		\n"	\
6262306a36Sopenharmony_ci		"	" __SYNC(full, loongson3_war) "		\n"	\
6362306a36Sopenharmony_ci		"1:	"user_ll("%1", "%4")" # __futex_atomic_op\n"	\
6462306a36Sopenharmony_ci		"	.set	pop				\n"	\
6562306a36Sopenharmony_ci		"	" insn	"				\n"	\
6662306a36Sopenharmony_ci		"	.set	"MIPS_ISA_ARCH_LEVEL"		\n"	\
6762306a36Sopenharmony_ci		"2:	"user_sc("$1", "%2")"			\n"	\
6862306a36Sopenharmony_ci		"	beqz	$1, 1b				\n"	\
6962306a36Sopenharmony_ci		__stringify(__WEAK_LLSC_MB) "			\n"	\
7062306a36Sopenharmony_ci		"3:						\n"	\
7162306a36Sopenharmony_ci		"	.insn					\n"	\
7262306a36Sopenharmony_ci		"	.set	pop				\n"	\
7362306a36Sopenharmony_ci		"	.section .fixup,\"ax\"			\n"	\
7462306a36Sopenharmony_ci		"4:	li	%0, %6				\n"	\
7562306a36Sopenharmony_ci		"	j	3b				\n"	\
7662306a36Sopenharmony_ci		"	.previous				\n"	\
7762306a36Sopenharmony_ci		"	.section __ex_table,\"a\"		\n"	\
7862306a36Sopenharmony_ci		"	"__UA_ADDR "\t1b, 4b			\n"	\
7962306a36Sopenharmony_ci		"	"__UA_ADDR "\t2b, 4b			\n"	\
8062306a36Sopenharmony_ci		"	.previous				\n"	\
8162306a36Sopenharmony_ci		: "=r" (ret), "=&r" (oldval),				\
8262306a36Sopenharmony_ci		  "=" GCC_OFF_SMALL_ASM() (*uaddr)				\
8362306a36Sopenharmony_ci		: "0" (0), GCC_OFF_SMALL_ASM() (*uaddr), "Jr" (oparg),	\
8462306a36Sopenharmony_ci		  "i" (-EFAULT)						\
8562306a36Sopenharmony_ci		: "memory");						\
8662306a36Sopenharmony_ci	} else {							\
8762306a36Sopenharmony_ci		/* fallback for non-SMP */				\
8862306a36Sopenharmony_ci		ret = futex_atomic_op_inuser_local(op, oparg, oval, uaddr);	\
8962306a36Sopenharmony_ci	}								\
9062306a36Sopenharmony_ci}
9162306a36Sopenharmony_ci
9262306a36Sopenharmony_cistatic inline int
9362306a36Sopenharmony_ciarch_futex_atomic_op_inuser(int op, int oparg, int *oval, u32 __user *uaddr)
9462306a36Sopenharmony_ci{
9562306a36Sopenharmony_ci	int oldval = 0, ret;
9662306a36Sopenharmony_ci
9762306a36Sopenharmony_ci	if (!access_ok(uaddr, sizeof(u32)))
9862306a36Sopenharmony_ci		return -EFAULT;
9962306a36Sopenharmony_ci
10062306a36Sopenharmony_ci	switch (op) {
10162306a36Sopenharmony_ci	case FUTEX_OP_SET:
10262306a36Sopenharmony_ci		__futex_atomic_op(op, "move $1, %z5", ret, oldval, uaddr, oparg);
10362306a36Sopenharmony_ci		break;
10462306a36Sopenharmony_ci
10562306a36Sopenharmony_ci	case FUTEX_OP_ADD:
10662306a36Sopenharmony_ci		__futex_atomic_op(op, "addu $1, %1, %z5",
10762306a36Sopenharmony_ci				  ret, oldval, uaddr, oparg);
10862306a36Sopenharmony_ci		break;
10962306a36Sopenharmony_ci	case FUTEX_OP_OR:
11062306a36Sopenharmony_ci		__futex_atomic_op(op, "or	$1, %1, %z5",
11162306a36Sopenharmony_ci				  ret, oldval, uaddr, oparg);
11262306a36Sopenharmony_ci		break;
11362306a36Sopenharmony_ci	case FUTEX_OP_ANDN:
11462306a36Sopenharmony_ci		__futex_atomic_op(op, "and	$1, %1, %z5",
11562306a36Sopenharmony_ci				  ret, oldval, uaddr, ~oparg);
11662306a36Sopenharmony_ci		break;
11762306a36Sopenharmony_ci	case FUTEX_OP_XOR:
11862306a36Sopenharmony_ci		__futex_atomic_op(op, "xor	$1, %1, %z5",
11962306a36Sopenharmony_ci				  ret, oldval, uaddr, oparg);
12062306a36Sopenharmony_ci		break;
12162306a36Sopenharmony_ci	default:
12262306a36Sopenharmony_ci		ret = -ENOSYS;
12362306a36Sopenharmony_ci	}
12462306a36Sopenharmony_ci
12562306a36Sopenharmony_ci	if (!ret)
12662306a36Sopenharmony_ci		*oval = oldval;
12762306a36Sopenharmony_ci
12862306a36Sopenharmony_ci	return ret;
12962306a36Sopenharmony_ci}
13062306a36Sopenharmony_ci
13162306a36Sopenharmony_cistatic inline int
13262306a36Sopenharmony_cifutex_atomic_cmpxchg_inatomic(u32 *uval, u32 __user *uaddr,
13362306a36Sopenharmony_ci			      u32 oldval, u32 newval)
13462306a36Sopenharmony_ci{
13562306a36Sopenharmony_ci	int ret = 0;
13662306a36Sopenharmony_ci	u32 val;
13762306a36Sopenharmony_ci
13862306a36Sopenharmony_ci	if (!access_ok(uaddr, sizeof(u32)))
13962306a36Sopenharmony_ci		return -EFAULT;
14062306a36Sopenharmony_ci
14162306a36Sopenharmony_ci	if (cpu_has_llsc && IS_ENABLED(CONFIG_WAR_R10000_LLSC)) {
14262306a36Sopenharmony_ci		__asm__ __volatile__(
14362306a36Sopenharmony_ci		"# futex_atomic_cmpxchg_inatomic			\n"
14462306a36Sopenharmony_ci		"	.set	push					\n"
14562306a36Sopenharmony_ci		"	.set	noat					\n"
14662306a36Sopenharmony_ci		"	.set	push					\n"
14762306a36Sopenharmony_ci		"	.set	arch=r4000				\n"
14862306a36Sopenharmony_ci		"1:	ll	%1, %3					\n"
14962306a36Sopenharmony_ci		"	bne	%1, %z4, 3f				\n"
15062306a36Sopenharmony_ci		"	.set	pop					\n"
15162306a36Sopenharmony_ci		"	move	$1, %z5					\n"
15262306a36Sopenharmony_ci		"	.set	arch=r4000				\n"
15362306a36Sopenharmony_ci		"2:	sc	$1, %2					\n"
15462306a36Sopenharmony_ci		"	beqzl	$1, 1b					\n"
15562306a36Sopenharmony_ci		__stringify(__WEAK_LLSC_MB) "				\n"
15662306a36Sopenharmony_ci		"3:							\n"
15762306a36Sopenharmony_ci		"	.insn						\n"
15862306a36Sopenharmony_ci		"	.set	pop					\n"
15962306a36Sopenharmony_ci		"	.section .fixup,\"ax\"				\n"
16062306a36Sopenharmony_ci		"4:	li	%0, %6					\n"
16162306a36Sopenharmony_ci		"	j	3b					\n"
16262306a36Sopenharmony_ci		"	.previous					\n"
16362306a36Sopenharmony_ci		"	.section __ex_table,\"a\"			\n"
16462306a36Sopenharmony_ci		"	"__UA_ADDR "\t1b, 4b				\n"
16562306a36Sopenharmony_ci		"	"__UA_ADDR "\t2b, 4b				\n"
16662306a36Sopenharmony_ci		"	.previous					\n"
16762306a36Sopenharmony_ci		: "+r" (ret), "=&r" (val), "=" GCC_OFF_SMALL_ASM() (*uaddr)
16862306a36Sopenharmony_ci		: GCC_OFF_SMALL_ASM() (*uaddr), "Jr" (oldval), "Jr" (newval),
16962306a36Sopenharmony_ci		  "i" (-EFAULT)
17062306a36Sopenharmony_ci		: "memory");
17162306a36Sopenharmony_ci	} else if (cpu_has_llsc) {
17262306a36Sopenharmony_ci		__asm__ __volatile__(
17362306a36Sopenharmony_ci		"# futex_atomic_cmpxchg_inatomic			\n"
17462306a36Sopenharmony_ci		"	.set	push					\n"
17562306a36Sopenharmony_ci		"	.set	noat					\n"
17662306a36Sopenharmony_ci		"	.set	push					\n"
17762306a36Sopenharmony_ci		"	.set	"MIPS_ISA_ARCH_LEVEL"			\n"
17862306a36Sopenharmony_ci		"	" __SYNC(full, loongson3_war) "			\n"
17962306a36Sopenharmony_ci		"1:	"user_ll("%1", "%3")"				\n"
18062306a36Sopenharmony_ci		"	bne	%1, %z4, 3f				\n"
18162306a36Sopenharmony_ci		"	.set	pop					\n"
18262306a36Sopenharmony_ci		"	move	$1, %z5					\n"
18362306a36Sopenharmony_ci		"	.set	"MIPS_ISA_ARCH_LEVEL"			\n"
18462306a36Sopenharmony_ci		"2:	"user_sc("$1", "%2")"				\n"
18562306a36Sopenharmony_ci		"	beqz	$1, 1b					\n"
18662306a36Sopenharmony_ci		"3:	" __SYNC_ELSE(full, loongson3_war, __WEAK_LLSC_MB) "\n"
18762306a36Sopenharmony_ci		"	.insn						\n"
18862306a36Sopenharmony_ci		"	.set	pop					\n"
18962306a36Sopenharmony_ci		"	.section .fixup,\"ax\"				\n"
19062306a36Sopenharmony_ci		"4:	li	%0, %6					\n"
19162306a36Sopenharmony_ci		"	j	3b					\n"
19262306a36Sopenharmony_ci		"	.previous					\n"
19362306a36Sopenharmony_ci		"	.section __ex_table,\"a\"			\n"
19462306a36Sopenharmony_ci		"	"__UA_ADDR "\t1b, 4b				\n"
19562306a36Sopenharmony_ci		"	"__UA_ADDR "\t2b, 4b				\n"
19662306a36Sopenharmony_ci		"	.previous					\n"
19762306a36Sopenharmony_ci		: "+r" (ret), "=&r" (val), "=" GCC_OFF_SMALL_ASM() (*uaddr)
19862306a36Sopenharmony_ci		: GCC_OFF_SMALL_ASM() (*uaddr), "Jr" (oldval), "Jr" (newval),
19962306a36Sopenharmony_ci		  "i" (-EFAULT)
20062306a36Sopenharmony_ci		: "memory");
20162306a36Sopenharmony_ci	} else {
20262306a36Sopenharmony_ci		return futex_atomic_cmpxchg_inatomic_local(uval, uaddr, oldval, newval);
20362306a36Sopenharmony_ci	}
20462306a36Sopenharmony_ci
20562306a36Sopenharmony_ci	*uval = val;
20662306a36Sopenharmony_ci	return ret;
20762306a36Sopenharmony_ci}
20862306a36Sopenharmony_ci
20962306a36Sopenharmony_ci#endif
21062306a36Sopenharmony_ci#endif /* _ASM_FUTEX_H */
211