| /* SPDX-License-Identifier: GPL-2.0 */ | |
| #ifndef _ASM_S390_FUTEX_H | |
| #define _ASM_S390_FUTEX_H | |
| #include <linux/instrumented.h> | |
| #include <linux/uaccess.h> | |
| #include <linux/futex.h> | |
| #include <asm/asm-extable.h> | |
| #include <asm/mmu_context.h> | |
| #include <asm/errno.h> | |
| #define FUTEX_OP_FUNC(name, insn) \ | |
| static uaccess_kmsan_or_inline int \ | |
| __futex_atomic_##name(int oparg, int *old, u32 __user *uaddr) \ | |
| { \ | |
| bool sacf_flag; \ | |
| int rc, new; \ | |
| \ | |
| instrument_copy_from_user_before(old, uaddr, sizeof(*old)); \ | |
| sacf_flag = enable_sacf_uaccess(); \ | |
| asm_inline volatile( \ | |
| " sacf 256\n" \ | |
| "0: l %[old],%[uaddr]\n" \ | |
| "1:"insn \ | |
| "2: cs %[old],%[new],%[uaddr]\n" \ | |
| "3: jl 1b\n" \ | |
| " lhi %[rc],0\n" \ | |
| "4: sacf 768\n" \ | |
| EX_TABLE_UA_FAULT(0b, 4b, %[rc]) \ | |
| EX_TABLE_UA_FAULT(1b, 4b, %[rc]) \ | |
| EX_TABLE_UA_FAULT(2b, 4b, %[rc]) \ | |
| EX_TABLE_UA_FAULT(3b, 4b, %[rc]) \ | |
| : [rc] "=d" (rc), [old] "=&d" (*old), \ | |
| [new] "=&d" (new), [uaddr] "+Q" (*uaddr) \ | |
| : [oparg] "d" (oparg) \ | |
| : "cc"); \ | |
| disable_sacf_uaccess(sacf_flag); \ | |
| if (!rc) \ | |
| instrument_copy_from_user_after(old, uaddr, sizeof(*old), 0); \ | |
| return rc; \ | |
| } | |
| FUTEX_OP_FUNC(set, "lr %[new],%[oparg]\n") | |
| FUTEX_OP_FUNC(add, "lr %[new],%[old]\n ar %[new],%[oparg]\n") | |
| FUTEX_OP_FUNC(or, "lr %[new],%[old]\n or %[new],%[oparg]\n") | |
| FUTEX_OP_FUNC(and, "lr %[new],%[old]\n nr %[new],%[oparg]\n") | |
| FUTEX_OP_FUNC(xor, "lr %[new],%[old]\n xr %[new],%[oparg]\n") | |
| static inline | |
| int arch_futex_atomic_op_inuser(int op, int oparg, int *oval, u32 __user *uaddr) | |
| { | |
| int old, rc; | |
| switch (op) { | |
| case FUTEX_OP_SET: | |
| rc = __futex_atomic_set(oparg, &old, uaddr); | |
| break; | |
| case FUTEX_OP_ADD: | |
| rc = __futex_atomic_add(oparg, &old, uaddr); | |
| break; | |
| case FUTEX_OP_OR: | |
| rc = __futex_atomic_or(oparg, &old, uaddr); | |
| break; | |
| case FUTEX_OP_ANDN: | |
| rc = __futex_atomic_and(~oparg, &old, uaddr); | |
| break; | |
| case FUTEX_OP_XOR: | |
| rc = __futex_atomic_xor(oparg, &old, uaddr); | |
| break; | |
| default: | |
| rc = -ENOSYS; | |
| } | |
| if (!rc) | |
| *oval = old; | |
| return rc; | |
| } | |
| static uaccess_kmsan_or_inline | |
| int futex_atomic_cmpxchg_inatomic(u32 *uval, u32 __user *uaddr, u32 oldval, u32 newval) | |
| { | |
| bool sacf_flag; | |
| int rc; | |
| instrument_copy_from_user_before(uval, uaddr, sizeof(*uval)); | |
| sacf_flag = enable_sacf_uaccess(); | |
| asm_inline volatile( | |
| " sacf 256\n" | |
| "0: cs %[old],%[new],%[uaddr]\n" | |
| "1: lhi %[rc],0\n" | |
| "2: sacf 768\n" | |
| EX_TABLE_UA_FAULT(0b, 2b, %[rc]) | |
| EX_TABLE_UA_FAULT(1b, 2b, %[rc]) | |
| : [rc] "=d" (rc), [old] "+d" (oldval), [uaddr] "+Q" (*uaddr) | |
| : [new] "d" (newval) | |
| : "cc", "memory"); | |
| disable_sacf_uaccess(sacf_flag); | |
| *uval = oldval; | |
| instrument_copy_from_user_after(uval, uaddr, sizeof(*uval), 0); | |
| return rc; | |
| } | |
| #endif /* _ASM_S390_FUTEX_H */ |