From 76cb841cb886eef6b3bee341a2266c76578724ad Mon Sep 17 00:00:00 2001 From: Daniel Baumann Date: Mon, 6 May 2024 03:02:30 +0200 Subject: Adding upstream version 4.19.249. Signed-off-by: Daniel Baumann --- arch/arm/include/asm/futex.h | 178 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 178 insertions(+) create mode 100644 arch/arm/include/asm/futex.h (limited to 'arch/arm/include/asm/futex.h') diff --git a/arch/arm/include/asm/futex.h b/arch/arm/include/asm/futex.h new file mode 100644 index 000000000..91ca80035 --- /dev/null +++ b/arch/arm/include/asm/futex.h @@ -0,0 +1,178 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +#ifndef _ASM_ARM_FUTEX_H +#define _ASM_ARM_FUTEX_H + +#ifdef __KERNEL__ + +#include +#include +#include + +#define __futex_atomic_ex_table(err_reg) \ + "3:\n" \ + " .pushsection __ex_table,\"a\"\n" \ + " .align 3\n" \ + " .long 1b, 4f, 2b, 4f\n" \ + " .popsection\n" \ + " .pushsection .text.fixup,\"ax\"\n" \ + " .align 2\n" \ + "4: mov %0, " err_reg "\n" \ + " b 3b\n" \ + " .popsection" + +#ifdef CONFIG_SMP + +#define __futex_atomic_op(insn, ret, oldval, tmp, uaddr, oparg) \ +({ \ + unsigned int __ua_flags; \ + smp_mb(); \ + prefetchw(uaddr); \ + __ua_flags = uaccess_save_and_enable(); \ + __asm__ __volatile__( \ + "1: ldrex %1, [%3]\n" \ + " " insn "\n" \ + "2: strex %2, %0, [%3]\n" \ + " teq %2, #0\n" \ + " bne 1b\n" \ + " mov %0, #0\n" \ + __futex_atomic_ex_table("%5") \ + : "=&r" (ret), "=&r" (oldval), "=&r" (tmp) \ + : "r" (uaddr), "r" (oparg), "Ir" (-EFAULT) \ + : "cc", "memory"); \ + uaccess_restore(__ua_flags); \ +}) + +static inline int +futex_atomic_cmpxchg_inatomic(u32 *uval, u32 __user *uaddr, + u32 oldval, u32 newval) +{ + unsigned int __ua_flags; + int ret; + u32 val; + + if (!access_ok(VERIFY_WRITE, uaddr, sizeof(u32))) + return -EFAULT; + + smp_mb(); + /* Prefetching cannot fault */ + prefetchw(uaddr); + __ua_flags = uaccess_save_and_enable(); + __asm__ __volatile__("@futex_atomic_cmpxchg_inatomic\n" + "1: ldrex %1, [%4]\n" + " teq %1, %2\n" + " ite eq @ explicit IT needed for the 2b label\n" + "2: strexeq %0, %3, [%4]\n" + " movne %0, #0\n" + " teq %0, #0\n" + " bne 1b\n" + __futex_atomic_ex_table("%5") + : "=&r" (ret), "=&r" (val) + : "r" (oldval), "r" (newval), "r" (uaddr), "Ir" (-EFAULT) + : "cc", "memory"); + uaccess_restore(__ua_flags); + smp_mb(); + + *uval = val; + return ret; +} + +#else /* !SMP, we can work around lack of atomic ops by disabling preemption */ + +#include +#include + +#define __futex_atomic_op(insn, ret, oldval, tmp, uaddr, oparg) \ +({ \ + unsigned int __ua_flags = uaccess_save_and_enable(); \ + __asm__ __volatile__( \ + "1: " TUSER(ldr) " %1, [%3]\n" \ + " " insn "\n" \ + "2: " TUSER(str) " %0, [%3]\n" \ + " mov %0, #0\n" \ + __futex_atomic_ex_table("%5") \ + : "=&r" (ret), "=&r" (oldval), "=&r" (tmp) \ + : "r" (uaddr), "r" (oparg), "Ir" (-EFAULT) \ + : "cc", "memory"); \ + uaccess_restore(__ua_flags); \ +}) + +static inline int +futex_atomic_cmpxchg_inatomic(u32 *uval, u32 __user *uaddr, + u32 oldval, u32 newval) +{ + unsigned int __ua_flags; + int ret = 0; + u32 val; + + if (!access_ok(VERIFY_WRITE, uaddr, sizeof(u32))) + return -EFAULT; + + preempt_disable(); + __ua_flags = uaccess_save_and_enable(); + __asm__ __volatile__("@futex_atomic_cmpxchg_inatomic\n" + "1: " TUSER(ldr) " %1, [%4]\n" + " teq %1, %2\n" + " it eq @ explicit IT needed for the 2b label\n" + "2: " TUSER(streq) " %3, [%4]\n" + __futex_atomic_ex_table("%5") + : "+r" (ret), "=&r" (val) + : "r" (oldval), "r" (newval), "r" (uaddr), "Ir" (-EFAULT) + : "cc", "memory"); + uaccess_restore(__ua_flags); + + *uval = val; + preempt_enable(); + + return ret; +} + +#endif /* !SMP */ + +static inline int +arch_futex_atomic_op_inuser(int op, int oparg, int *oval, u32 __user *uaddr) +{ + int oldval = 0, ret, tmp; + +#ifndef CONFIG_SMP + preempt_disable(); +#endif + pagefault_disable(); + + switch (op) { + case FUTEX_OP_SET: + __futex_atomic_op("mov %0, %4", ret, oldval, tmp, uaddr, oparg); + break; + case FUTEX_OP_ADD: + __futex_atomic_op("add %0, %1, %4", ret, oldval, tmp, uaddr, oparg); + break; + case FUTEX_OP_OR: + __futex_atomic_op("orr %0, %1, %4", ret, oldval, tmp, uaddr, oparg); + break; + case FUTEX_OP_ANDN: + __futex_atomic_op("and %0, %1, %4", ret, oldval, tmp, uaddr, ~oparg); + break; + case FUTEX_OP_XOR: + __futex_atomic_op("eor %0, %1, %4", ret, oldval, tmp, uaddr, oparg); + break; + default: + ret = -ENOSYS; + } + + pagefault_enable(); +#ifndef CONFIG_SMP + preempt_enable(); +#endif + + /* + * Store unconditionally. If ret != 0 the extra store is the least + * of the worries but GCC cannot figure out that __futex_atomic_op() + * is either setting ret to -EFAULT or storing the old value in + * oldval which results in a uninitialized warning at the call site. + */ + *oval = oldval; + + return ret; +} + +#endif /* __KERNEL__ */ +#endif /* _ASM_ARM_FUTEX_H */ -- cgit v1.2.3