1 /* SPDX-License-Identifier: GPL-2.0 */ 2 #ifndef _ASM_PARISC_FUTEX_H 3 #define _ASM_PARISC_FUTEX_H 4 5 #include <linux/futex.h> 6 #include <linux/uaccess.h> 7 #include <asm/atomic.h> 8 #include <asm/errno.h> 9 10 /* The following has to match the LWS code in syscall.S. We have 11 sixteen four-word locks. */ 12 13 static inline void 14 _futex_spin_lock_irqsave(u32 __user *uaddr, unsigned long int *flags) 15 { 16 extern u32 lws_lock_start[]; 17 long index = ((long)uaddr & 0x3f8) >> 1; 18 arch_spinlock_t *s = (arch_spinlock_t *)&lws_lock_start[index]; 19 local_irq_save(*flags); 20 arch_spin_lock(s); 21 } 22 23 static inline void 24 _futex_spin_unlock_irqrestore(u32 __user *uaddr, unsigned long int *flags) 25 { 26 extern u32 lws_lock_start[]; 27 long index = ((long)uaddr & 0x3f8) >> 1; 28 arch_spinlock_t *s = (arch_spinlock_t *)&lws_lock_start[index]; 29 arch_spin_unlock(s); 30 local_irq_restore(*flags); 31 } 32 33 static inline int 34 arch_futex_atomic_op_inuser(int op, int oparg, int *oval, u32 __user *uaddr) 35 { 36 unsigned long int flags; 37 int oldval, ret; 38 u32 tmp; 39 40 _futex_spin_lock_irqsave(uaddr, &flags); 41 42 ret = -EFAULT; 43 if (unlikely(get_user(oldval, uaddr) != 0)) 44 goto out_pagefault_enable; 45 46 ret = 0; 47 tmp = oldval; 48 49 switch (op) { 50 case FUTEX_OP_SET: 51 tmp = oparg; 52 break; 53 case FUTEX_OP_ADD: 54 tmp += oparg; 55 break; 56 case FUTEX_OP_OR: 57 tmp |= oparg; 58 break; 59 case FUTEX_OP_ANDN: 60 tmp &= ~oparg; 61 break; 62 case FUTEX_OP_XOR: 63 tmp ^= oparg; 64 break; 65 default: 66 ret = -ENOSYS; 67 } 68 69 if (ret == 0 && unlikely(put_user(tmp, uaddr) != 0)) 70 ret = -EFAULT; 71 72 out_pagefault_enable: 73 _futex_spin_unlock_irqrestore(uaddr, &flags); 74 75 if (!ret) 76 *oval = oldval; 77 78 return ret; 79 } 80 81 static inline int 82 futex_atomic_cmpxchg_inatomic(u32 *uval, u32 __user *uaddr, 83 u32 oldval, u32 newval) 84 { 85 u32 val; 86 unsigned long flags; 87 88 /* futex.c wants to do a cmpxchg_inatomic on kernel NULL, which is 89 * our gateway page, and causes no end of trouble... 90 */ 91 if (uaccess_kernel() && !uaddr) 92 return -EFAULT; 93 94 if (!access_ok(uaddr, sizeof(u32))) 95 return -EFAULT; 96 97 /* HPPA has no cmpxchg in hardware and therefore the 98 * best we can do here is use an array of locks. The 99 * lock selected is based on a hash of the userspace 100 * address. This should scale to a couple of CPUs. 101 */ 102 103 _futex_spin_lock_irqsave(uaddr, &flags); 104 if (unlikely(get_user(val, uaddr) != 0)) { 105 _futex_spin_unlock_irqrestore(uaddr, &flags); 106 return -EFAULT; 107 } 108 109 if (val == oldval && unlikely(put_user(newval, uaddr) != 0)) { 110 _futex_spin_unlock_irqrestore(uaddr, &flags); 111 return -EFAULT; 112 } 113 114 *uval = val; 115 _futex_spin_unlock_irqrestore(uaddr, &flags); 116 117 return 0; 118 } 119 120 #endif /*_ASM_PARISC_FUTEX_H*/ 121