162306a36Sopenharmony_ci/* SPDX-License-Identifier: GPL-2.0 */
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * Copyright (C) 2020-2022 Loongson Technology Corporation Limited
462306a36Sopenharmony_ci */
562306a36Sopenharmony_ci#ifndef _ASM_FUTEX_H
662306a36Sopenharmony_ci#define _ASM_FUTEX_H
762306a36Sopenharmony_ci
862306a36Sopenharmony_ci#include <linux/futex.h>
962306a36Sopenharmony_ci#include <linux/uaccess.h>
1062306a36Sopenharmony_ci#include <asm/asm-extable.h>
1162306a36Sopenharmony_ci#include <asm/barrier.h>
1262306a36Sopenharmony_ci#include <asm/errno.h>
1362306a36Sopenharmony_ci
1462306a36Sopenharmony_ci#define __futex_atomic_op(insn, ret, oldval, uaddr, oparg)		\
1562306a36Sopenharmony_ci{									\
1662306a36Sopenharmony_ci	__asm__ __volatile__(						\
1762306a36Sopenharmony_ci	"1:	ll.w	%1, %4 # __futex_atomic_op\n"		\
1862306a36Sopenharmony_ci	"	" insn	"				\n"	\
1962306a36Sopenharmony_ci	"2:	sc.w	$t0, %2				\n"	\
2062306a36Sopenharmony_ci	"	beqz	$t0, 1b				\n"	\
2162306a36Sopenharmony_ci	"3:						\n"	\
2262306a36Sopenharmony_ci	_ASM_EXTABLE_UACCESS_ERR(1b, 3b, %0)			\
2362306a36Sopenharmony_ci	_ASM_EXTABLE_UACCESS_ERR(2b, 3b, %0)			\
2462306a36Sopenharmony_ci	: "=r" (ret), "=&r" (oldval),				\
2562306a36Sopenharmony_ci	  "=ZC" (*uaddr)					\
2662306a36Sopenharmony_ci	: "0" (0), "ZC" (*uaddr), "Jr" (oparg)			\
2762306a36Sopenharmony_ci	: "memory", "t0");					\
2862306a36Sopenharmony_ci}
2962306a36Sopenharmony_ci
3062306a36Sopenharmony_cistatic inline int
3162306a36Sopenharmony_ciarch_futex_atomic_op_inuser(int op, int oparg, int *oval, u32 __user *uaddr)
3262306a36Sopenharmony_ci{
3362306a36Sopenharmony_ci	int oldval = 0, ret = 0;
3462306a36Sopenharmony_ci
3562306a36Sopenharmony_ci	pagefault_disable();
3662306a36Sopenharmony_ci
3762306a36Sopenharmony_ci	switch (op) {
3862306a36Sopenharmony_ci	case FUTEX_OP_SET:
3962306a36Sopenharmony_ci		__futex_atomic_op("move $t0, %z5", ret, oldval, uaddr, oparg);
4062306a36Sopenharmony_ci		break;
4162306a36Sopenharmony_ci	case FUTEX_OP_ADD:
4262306a36Sopenharmony_ci		__futex_atomic_op("add.w $t0, %1, %z5", ret, oldval, uaddr, oparg);
4362306a36Sopenharmony_ci		break;
4462306a36Sopenharmony_ci	case FUTEX_OP_OR:
4562306a36Sopenharmony_ci		__futex_atomic_op("or	$t0, %1, %z5", ret, oldval, uaddr, oparg);
4662306a36Sopenharmony_ci		break;
4762306a36Sopenharmony_ci	case FUTEX_OP_ANDN:
4862306a36Sopenharmony_ci		__futex_atomic_op("and	$t0, %1, %z5", ret, oldval, uaddr, ~oparg);
4962306a36Sopenharmony_ci		break;
5062306a36Sopenharmony_ci	case FUTEX_OP_XOR:
5162306a36Sopenharmony_ci		__futex_atomic_op("xor	$t0, %1, %z5", ret, oldval, uaddr, oparg);
5262306a36Sopenharmony_ci		break;
5362306a36Sopenharmony_ci	default:
5462306a36Sopenharmony_ci		ret = -ENOSYS;
5562306a36Sopenharmony_ci	}
5662306a36Sopenharmony_ci
5762306a36Sopenharmony_ci	pagefault_enable();
5862306a36Sopenharmony_ci
5962306a36Sopenharmony_ci	if (!ret)
6062306a36Sopenharmony_ci		*oval = oldval;
6162306a36Sopenharmony_ci
6262306a36Sopenharmony_ci	return ret;
6362306a36Sopenharmony_ci}
6462306a36Sopenharmony_ci
6562306a36Sopenharmony_cistatic inline int
6662306a36Sopenharmony_cifutex_atomic_cmpxchg_inatomic(u32 *uval, u32 __user *uaddr, u32 oldval, u32 newval)
6762306a36Sopenharmony_ci{
6862306a36Sopenharmony_ci	int ret = 0;
6962306a36Sopenharmony_ci	u32 val = 0;
7062306a36Sopenharmony_ci
7162306a36Sopenharmony_ci	if (!access_ok(uaddr, sizeof(u32)))
7262306a36Sopenharmony_ci		return -EFAULT;
7362306a36Sopenharmony_ci
7462306a36Sopenharmony_ci	__asm__ __volatile__(
7562306a36Sopenharmony_ci	"# futex_atomic_cmpxchg_inatomic			\n"
7662306a36Sopenharmony_ci	"1:	ll.w	%1, %3					\n"
7762306a36Sopenharmony_ci	"	bne	%1, %z4, 3f				\n"
7862306a36Sopenharmony_ci	"	move	$t0, %z5				\n"
7962306a36Sopenharmony_ci	"2:	sc.w	$t0, %2					\n"
8062306a36Sopenharmony_ci	"	beqz	$t0, 1b					\n"
8162306a36Sopenharmony_ci	"3:							\n"
8262306a36Sopenharmony_ci	__WEAK_LLSC_MB
8362306a36Sopenharmony_ci	_ASM_EXTABLE_UACCESS_ERR(1b, 3b, %0)
8462306a36Sopenharmony_ci	_ASM_EXTABLE_UACCESS_ERR(2b, 3b, %0)
8562306a36Sopenharmony_ci	: "+r" (ret), "=&r" (val), "=ZC" (*uaddr)
8662306a36Sopenharmony_ci	: "ZC" (*uaddr), "Jr" (oldval), "Jr" (newval)
8762306a36Sopenharmony_ci	: "memory", "t0");
8862306a36Sopenharmony_ci
8962306a36Sopenharmony_ci	*uval = val;
9062306a36Sopenharmony_ci
9162306a36Sopenharmony_ci	return ret;
9262306a36Sopenharmony_ci}
9362306a36Sopenharmony_ci
9462306a36Sopenharmony_ci#endif /* _ASM_FUTEX_H */
95