18c2ecf20Sopenharmony_ci/* SPDX-License-Identifier: GPL-2.0 */
28c2ecf20Sopenharmony_ci#ifndef _ASM_PARISC_FUTEX_H
38c2ecf20Sopenharmony_ci#define _ASM_PARISC_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/atomic.h>
108c2ecf20Sopenharmony_ci#include <asm/errno.h>
118c2ecf20Sopenharmony_ci
128c2ecf20Sopenharmony_ci/* The following has to match the LWS code in syscall.S.  We have
138c2ecf20Sopenharmony_ci   sixteen four-word locks. */
148c2ecf20Sopenharmony_ci
158c2ecf20Sopenharmony_cistatic inline void
168c2ecf20Sopenharmony_ci_futex_spin_lock_irqsave(u32 __user *uaddr, unsigned long int *flags)
178c2ecf20Sopenharmony_ci{
188c2ecf20Sopenharmony_ci	extern u32 lws_lock_start[];
198c2ecf20Sopenharmony_ci	long index = ((long)uaddr & 0x7f8) >> 1;
208c2ecf20Sopenharmony_ci	arch_spinlock_t *s = (arch_spinlock_t *)&lws_lock_start[index];
218c2ecf20Sopenharmony_ci	local_irq_save(*flags);
228c2ecf20Sopenharmony_ci	arch_spin_lock(s);
238c2ecf20Sopenharmony_ci}
248c2ecf20Sopenharmony_ci
258c2ecf20Sopenharmony_cistatic inline void
268c2ecf20Sopenharmony_ci_futex_spin_unlock_irqrestore(u32 __user *uaddr, unsigned long int *flags)
278c2ecf20Sopenharmony_ci{
288c2ecf20Sopenharmony_ci	extern u32 lws_lock_start[];
298c2ecf20Sopenharmony_ci	long index = ((long)uaddr & 0x7f8) >> 1;
308c2ecf20Sopenharmony_ci	arch_spinlock_t *s = (arch_spinlock_t *)&lws_lock_start[index];
318c2ecf20Sopenharmony_ci	arch_spin_unlock(s);
328c2ecf20Sopenharmony_ci	local_irq_restore(*flags);
338c2ecf20Sopenharmony_ci}
348c2ecf20Sopenharmony_ci
358c2ecf20Sopenharmony_cistatic inline int
368c2ecf20Sopenharmony_ciarch_futex_atomic_op_inuser(int op, int oparg, int *oval, u32 __user *uaddr)
378c2ecf20Sopenharmony_ci{
388c2ecf20Sopenharmony_ci	unsigned long int flags;
398c2ecf20Sopenharmony_ci	int oldval, ret;
408c2ecf20Sopenharmony_ci	u32 tmp;
418c2ecf20Sopenharmony_ci
428c2ecf20Sopenharmony_ci	_futex_spin_lock_irqsave(uaddr, &flags);
438c2ecf20Sopenharmony_ci
448c2ecf20Sopenharmony_ci	ret = -EFAULT;
458c2ecf20Sopenharmony_ci	if (unlikely(get_user(oldval, uaddr) != 0))
468c2ecf20Sopenharmony_ci		goto out_pagefault_enable;
478c2ecf20Sopenharmony_ci
488c2ecf20Sopenharmony_ci	ret = 0;
498c2ecf20Sopenharmony_ci	tmp = oldval;
508c2ecf20Sopenharmony_ci
518c2ecf20Sopenharmony_ci	switch (op) {
528c2ecf20Sopenharmony_ci	case FUTEX_OP_SET:
538c2ecf20Sopenharmony_ci		tmp = oparg;
548c2ecf20Sopenharmony_ci		break;
558c2ecf20Sopenharmony_ci	case FUTEX_OP_ADD:
568c2ecf20Sopenharmony_ci		tmp += oparg;
578c2ecf20Sopenharmony_ci		break;
588c2ecf20Sopenharmony_ci	case FUTEX_OP_OR:
598c2ecf20Sopenharmony_ci		tmp |= oparg;
608c2ecf20Sopenharmony_ci		break;
618c2ecf20Sopenharmony_ci	case FUTEX_OP_ANDN:
628c2ecf20Sopenharmony_ci		tmp &= ~oparg;
638c2ecf20Sopenharmony_ci		break;
648c2ecf20Sopenharmony_ci	case FUTEX_OP_XOR:
658c2ecf20Sopenharmony_ci		tmp ^= oparg;
668c2ecf20Sopenharmony_ci		break;
678c2ecf20Sopenharmony_ci	default:
688c2ecf20Sopenharmony_ci		ret = -ENOSYS;
698c2ecf20Sopenharmony_ci	}
708c2ecf20Sopenharmony_ci
718c2ecf20Sopenharmony_ci	if (ret == 0 && unlikely(put_user(tmp, uaddr) != 0))
728c2ecf20Sopenharmony_ci		ret = -EFAULT;
738c2ecf20Sopenharmony_ci
748c2ecf20Sopenharmony_ciout_pagefault_enable:
758c2ecf20Sopenharmony_ci	_futex_spin_unlock_irqrestore(uaddr, &flags);
768c2ecf20Sopenharmony_ci
778c2ecf20Sopenharmony_ci	if (!ret)
788c2ecf20Sopenharmony_ci		*oval = oldval;
798c2ecf20Sopenharmony_ci
808c2ecf20Sopenharmony_ci	return ret;
818c2ecf20Sopenharmony_ci}
828c2ecf20Sopenharmony_ci
838c2ecf20Sopenharmony_cistatic inline int
848c2ecf20Sopenharmony_cifutex_atomic_cmpxchg_inatomic(u32 *uval, u32 __user *uaddr,
858c2ecf20Sopenharmony_ci			      u32 oldval, u32 newval)
868c2ecf20Sopenharmony_ci{
878c2ecf20Sopenharmony_ci	u32 val;
888c2ecf20Sopenharmony_ci	unsigned long flags;
898c2ecf20Sopenharmony_ci
908c2ecf20Sopenharmony_ci	/* futex.c wants to do a cmpxchg_inatomic on kernel NULL, which is
918c2ecf20Sopenharmony_ci	 * our gateway page, and causes no end of trouble...
928c2ecf20Sopenharmony_ci	 */
938c2ecf20Sopenharmony_ci	if (uaccess_kernel() && !uaddr)
948c2ecf20Sopenharmony_ci		return -EFAULT;
958c2ecf20Sopenharmony_ci
968c2ecf20Sopenharmony_ci	if (!access_ok(uaddr, sizeof(u32)))
978c2ecf20Sopenharmony_ci		return -EFAULT;
988c2ecf20Sopenharmony_ci
998c2ecf20Sopenharmony_ci	/* HPPA has no cmpxchg in hardware and therefore the
1008c2ecf20Sopenharmony_ci	 * best we can do here is use an array of locks. The
1018c2ecf20Sopenharmony_ci	 * lock selected is based on a hash of the userspace
1028c2ecf20Sopenharmony_ci	 * address. This should scale to a couple of CPUs.
1038c2ecf20Sopenharmony_ci	 */
1048c2ecf20Sopenharmony_ci
1058c2ecf20Sopenharmony_ci	_futex_spin_lock_irqsave(uaddr, &flags);
1068c2ecf20Sopenharmony_ci	if (unlikely(get_user(val, uaddr) != 0)) {
1078c2ecf20Sopenharmony_ci		_futex_spin_unlock_irqrestore(uaddr, &flags);
1088c2ecf20Sopenharmony_ci		return -EFAULT;
1098c2ecf20Sopenharmony_ci	}
1108c2ecf20Sopenharmony_ci
1118c2ecf20Sopenharmony_ci	if (val == oldval && unlikely(put_user(newval, uaddr) != 0)) {
1128c2ecf20Sopenharmony_ci		_futex_spin_unlock_irqrestore(uaddr, &flags);
1138c2ecf20Sopenharmony_ci		return -EFAULT;
1148c2ecf20Sopenharmony_ci	}
1158c2ecf20Sopenharmony_ci
1168c2ecf20Sopenharmony_ci	*uval = val;
1178c2ecf20Sopenharmony_ci	_futex_spin_unlock_irqrestore(uaddr, &flags);
1188c2ecf20Sopenharmony_ci
1198c2ecf20Sopenharmony_ci	return 0;
1208c2ecf20Sopenharmony_ci}
1218c2ecf20Sopenharmony_ci
1228c2ecf20Sopenharmony_ci#endif /*__KERNEL__*/
1238c2ecf20Sopenharmony_ci#endif /*_ASM_PARISC_FUTEX_H*/
124