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