18c2ecf20Sopenharmony_ci/* SPDX-License-Identifier: GPL-2.0 */
28c2ecf20Sopenharmony_ci#ifndef _ASM_POWERPC_FUTEX_H
38c2ecf20Sopenharmony_ci#define _ASM_POWERPC_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#include <asm/synch.h>
118c2ecf20Sopenharmony_ci
128c2ecf20Sopenharmony_ci#define __futex_atomic_op(insn, ret, oldval, uaddr, oparg) \
138c2ecf20Sopenharmony_ci  __asm__ __volatile ( \
148c2ecf20Sopenharmony_ci	PPC_ATOMIC_ENTRY_BARRIER \
158c2ecf20Sopenharmony_ci"1:	lwarx	%0,0,%2\n" \
168c2ecf20Sopenharmony_ci	insn \
178c2ecf20Sopenharmony_ci"2:	stwcx.	%1,0,%2\n" \
188c2ecf20Sopenharmony_ci	"bne-	1b\n" \
198c2ecf20Sopenharmony_ci	PPC_ATOMIC_EXIT_BARRIER \
208c2ecf20Sopenharmony_ci	"li	%1,0\n" \
218c2ecf20Sopenharmony_ci"3:	.section .fixup,\"ax\"\n" \
228c2ecf20Sopenharmony_ci"4:	li	%1,%3\n" \
238c2ecf20Sopenharmony_ci	"b	3b\n" \
248c2ecf20Sopenharmony_ci	".previous\n" \
258c2ecf20Sopenharmony_ci	EX_TABLE(1b, 4b) \
268c2ecf20Sopenharmony_ci	EX_TABLE(2b, 4b) \
278c2ecf20Sopenharmony_ci	: "=&r" (oldval), "=&r" (ret) \
288c2ecf20Sopenharmony_ci	: "b" (uaddr), "i" (-EFAULT), "r" (oparg) \
298c2ecf20Sopenharmony_ci	: "cr0", "memory")
308c2ecf20Sopenharmony_ci
318c2ecf20Sopenharmony_cistatic inline int arch_futex_atomic_op_inuser(int op, int oparg, int *oval,
328c2ecf20Sopenharmony_ci		u32 __user *uaddr)
338c2ecf20Sopenharmony_ci{
348c2ecf20Sopenharmony_ci	int oldval = 0, ret;
358c2ecf20Sopenharmony_ci
368c2ecf20Sopenharmony_ci	if (!access_ok(uaddr, sizeof(u32)))
378c2ecf20Sopenharmony_ci		return -EFAULT;
388c2ecf20Sopenharmony_ci	allow_read_write_user(uaddr, uaddr, sizeof(*uaddr));
398c2ecf20Sopenharmony_ci
408c2ecf20Sopenharmony_ci	switch (op) {
418c2ecf20Sopenharmony_ci	case FUTEX_OP_SET:
428c2ecf20Sopenharmony_ci		__futex_atomic_op("mr %1,%4\n", ret, oldval, uaddr, oparg);
438c2ecf20Sopenharmony_ci		break;
448c2ecf20Sopenharmony_ci	case FUTEX_OP_ADD:
458c2ecf20Sopenharmony_ci		__futex_atomic_op("add %1,%0,%4\n", ret, oldval, uaddr, oparg);
468c2ecf20Sopenharmony_ci		break;
478c2ecf20Sopenharmony_ci	case FUTEX_OP_OR:
488c2ecf20Sopenharmony_ci		__futex_atomic_op("or %1,%0,%4\n", ret, oldval, uaddr, oparg);
498c2ecf20Sopenharmony_ci		break;
508c2ecf20Sopenharmony_ci	case FUTEX_OP_ANDN:
518c2ecf20Sopenharmony_ci		__futex_atomic_op("andc %1,%0,%4\n", ret, oldval, uaddr, oparg);
528c2ecf20Sopenharmony_ci		break;
538c2ecf20Sopenharmony_ci	case FUTEX_OP_XOR:
548c2ecf20Sopenharmony_ci		__futex_atomic_op("xor %1,%0,%4\n", ret, oldval, uaddr, oparg);
558c2ecf20Sopenharmony_ci		break;
568c2ecf20Sopenharmony_ci	default:
578c2ecf20Sopenharmony_ci		ret = -ENOSYS;
588c2ecf20Sopenharmony_ci	}
598c2ecf20Sopenharmony_ci
608c2ecf20Sopenharmony_ci	*oval = oldval;
618c2ecf20Sopenharmony_ci
628c2ecf20Sopenharmony_ci	prevent_read_write_user(uaddr, uaddr, sizeof(*uaddr));
638c2ecf20Sopenharmony_ci	return ret;
648c2ecf20Sopenharmony_ci}
658c2ecf20Sopenharmony_ci
668c2ecf20Sopenharmony_cistatic inline int
678c2ecf20Sopenharmony_cifutex_atomic_cmpxchg_inatomic(u32 *uval, u32 __user *uaddr,
688c2ecf20Sopenharmony_ci			      u32 oldval, u32 newval)
698c2ecf20Sopenharmony_ci{
708c2ecf20Sopenharmony_ci	int ret = 0;
718c2ecf20Sopenharmony_ci	u32 prev;
728c2ecf20Sopenharmony_ci
738c2ecf20Sopenharmony_ci	if (!access_ok(uaddr, sizeof(u32)))
748c2ecf20Sopenharmony_ci		return -EFAULT;
758c2ecf20Sopenharmony_ci
768c2ecf20Sopenharmony_ci	allow_read_write_user(uaddr, uaddr, sizeof(*uaddr));
778c2ecf20Sopenharmony_ci
788c2ecf20Sopenharmony_ci        __asm__ __volatile__ (
798c2ecf20Sopenharmony_ci        PPC_ATOMIC_ENTRY_BARRIER
808c2ecf20Sopenharmony_ci"1:     lwarx   %1,0,%3         # futex_atomic_cmpxchg_inatomic\n\
818c2ecf20Sopenharmony_ci        cmpw    0,%1,%4\n\
828c2ecf20Sopenharmony_ci        bne-    3f\n"
838c2ecf20Sopenharmony_ci"2:     stwcx.  %5,0,%3\n\
848c2ecf20Sopenharmony_ci        bne-    1b\n"
858c2ecf20Sopenharmony_ci        PPC_ATOMIC_EXIT_BARRIER
868c2ecf20Sopenharmony_ci"3:	.section .fixup,\"ax\"\n\
878c2ecf20Sopenharmony_ci4:	li	%0,%6\n\
888c2ecf20Sopenharmony_ci	b	3b\n\
898c2ecf20Sopenharmony_ci	.previous\n"
908c2ecf20Sopenharmony_ci	EX_TABLE(1b, 4b)
918c2ecf20Sopenharmony_ci	EX_TABLE(2b, 4b)
928c2ecf20Sopenharmony_ci        : "+r" (ret), "=&r" (prev), "+m" (*uaddr)
938c2ecf20Sopenharmony_ci        : "r" (uaddr), "r" (oldval), "r" (newval), "i" (-EFAULT)
948c2ecf20Sopenharmony_ci        : "cc", "memory");
958c2ecf20Sopenharmony_ci
968c2ecf20Sopenharmony_ci	*uval = prev;
978c2ecf20Sopenharmony_ci	prevent_read_write_user(uaddr, uaddr, sizeof(*uaddr));
988c2ecf20Sopenharmony_ci
998c2ecf20Sopenharmony_ci        return ret;
1008c2ecf20Sopenharmony_ci}
1018c2ecf20Sopenharmony_ci
1028c2ecf20Sopenharmony_ci#endif /* __KERNEL__ */
1038c2ecf20Sopenharmony_ci#endif /* _ASM_POWERPC_FUTEX_H */
104