18c2ecf20Sopenharmony_ci/* SPDX-License-Identifier: GPL-2.0-only */ 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Copyright (C) 2012 ARM Ltd. 48c2ecf20Sopenharmony_ci */ 58c2ecf20Sopenharmony_ci#ifndef __ASM_FUTEX_H 68c2ecf20Sopenharmony_ci#define __ASM_FUTEX_H 78c2ecf20Sopenharmony_ci 88c2ecf20Sopenharmony_ci#include <linux/futex.h> 98c2ecf20Sopenharmony_ci#include <linux/uaccess.h> 108c2ecf20Sopenharmony_ci 118c2ecf20Sopenharmony_ci#include <asm/errno.h> 128c2ecf20Sopenharmony_ci 138c2ecf20Sopenharmony_ci#define FUTEX_MAX_LOOPS 128 /* What's the largest number you can think of? */ 148c2ecf20Sopenharmony_ci 158c2ecf20Sopenharmony_ci#define __futex_atomic_op(insn, ret, oldval, uaddr, tmp, oparg) \ 168c2ecf20Sopenharmony_cido { \ 178c2ecf20Sopenharmony_ci unsigned int loops = FUTEX_MAX_LOOPS; \ 188c2ecf20Sopenharmony_ci \ 198c2ecf20Sopenharmony_ci uaccess_enable(); \ 208c2ecf20Sopenharmony_ci asm volatile( \ 218c2ecf20Sopenharmony_ci" prfm pstl1strm, %2\n" \ 228c2ecf20Sopenharmony_ci"1: ldxr %w1, %2\n" \ 238c2ecf20Sopenharmony_ci insn "\n" \ 248c2ecf20Sopenharmony_ci"2: stlxr %w0, %w3, %2\n" \ 258c2ecf20Sopenharmony_ci" cbz %w0, 3f\n" \ 268c2ecf20Sopenharmony_ci" sub %w4, %w4, %w0\n" \ 278c2ecf20Sopenharmony_ci" cbnz %w4, 1b\n" \ 288c2ecf20Sopenharmony_ci" mov %w0, %w7\n" \ 298c2ecf20Sopenharmony_ci"3:\n" \ 308c2ecf20Sopenharmony_ci" dmb ish\n" \ 318c2ecf20Sopenharmony_ci" .pushsection .fixup,\"ax\"\n" \ 328c2ecf20Sopenharmony_ci" .align 2\n" \ 338c2ecf20Sopenharmony_ci"4: mov %w0, %w6\n" \ 348c2ecf20Sopenharmony_ci" b 3b\n" \ 358c2ecf20Sopenharmony_ci" .popsection\n" \ 368c2ecf20Sopenharmony_ci _ASM_EXTABLE(1b, 4b) \ 378c2ecf20Sopenharmony_ci _ASM_EXTABLE(2b, 4b) \ 388c2ecf20Sopenharmony_ci : "=&r" (ret), "=&r" (oldval), "+Q" (*uaddr), "=&r" (tmp), \ 398c2ecf20Sopenharmony_ci "+r" (loops) \ 408c2ecf20Sopenharmony_ci : "r" (oparg), "Ir" (-EFAULT), "Ir" (-EAGAIN) \ 418c2ecf20Sopenharmony_ci : "memory"); \ 428c2ecf20Sopenharmony_ci uaccess_disable(); \ 438c2ecf20Sopenharmony_ci} while (0) 448c2ecf20Sopenharmony_ci 458c2ecf20Sopenharmony_cistatic inline int 468c2ecf20Sopenharmony_ciarch_futex_atomic_op_inuser(int op, int oparg, int *oval, u32 __user *_uaddr) 478c2ecf20Sopenharmony_ci{ 488c2ecf20Sopenharmony_ci int oldval = 0, ret, tmp; 498c2ecf20Sopenharmony_ci u32 __user *uaddr = __uaccess_mask_ptr(_uaddr); 508c2ecf20Sopenharmony_ci 518c2ecf20Sopenharmony_ci if (!access_ok(_uaddr, sizeof(u32))) 528c2ecf20Sopenharmony_ci return -EFAULT; 538c2ecf20Sopenharmony_ci 548c2ecf20Sopenharmony_ci switch (op) { 558c2ecf20Sopenharmony_ci case FUTEX_OP_SET: 568c2ecf20Sopenharmony_ci __futex_atomic_op("mov %w3, %w5", 578c2ecf20Sopenharmony_ci ret, oldval, uaddr, tmp, oparg); 588c2ecf20Sopenharmony_ci break; 598c2ecf20Sopenharmony_ci case FUTEX_OP_ADD: 608c2ecf20Sopenharmony_ci __futex_atomic_op("add %w3, %w1, %w5", 618c2ecf20Sopenharmony_ci ret, oldval, uaddr, tmp, oparg); 628c2ecf20Sopenharmony_ci break; 638c2ecf20Sopenharmony_ci case FUTEX_OP_OR: 648c2ecf20Sopenharmony_ci __futex_atomic_op("orr %w3, %w1, %w5", 658c2ecf20Sopenharmony_ci ret, oldval, uaddr, tmp, oparg); 668c2ecf20Sopenharmony_ci break; 678c2ecf20Sopenharmony_ci case FUTEX_OP_ANDN: 688c2ecf20Sopenharmony_ci __futex_atomic_op("and %w3, %w1, %w5", 698c2ecf20Sopenharmony_ci ret, oldval, uaddr, tmp, ~oparg); 708c2ecf20Sopenharmony_ci break; 718c2ecf20Sopenharmony_ci case FUTEX_OP_XOR: 728c2ecf20Sopenharmony_ci __futex_atomic_op("eor %w3, %w1, %w5", 738c2ecf20Sopenharmony_ci ret, oldval, uaddr, tmp, oparg); 748c2ecf20Sopenharmony_ci break; 758c2ecf20Sopenharmony_ci default: 768c2ecf20Sopenharmony_ci ret = -ENOSYS; 778c2ecf20Sopenharmony_ci } 788c2ecf20Sopenharmony_ci 798c2ecf20Sopenharmony_ci if (!ret) 808c2ecf20Sopenharmony_ci *oval = oldval; 818c2ecf20Sopenharmony_ci 828c2ecf20Sopenharmony_ci return ret; 838c2ecf20Sopenharmony_ci} 848c2ecf20Sopenharmony_ci 858c2ecf20Sopenharmony_cistatic inline int 868c2ecf20Sopenharmony_cifutex_atomic_cmpxchg_inatomic(u32 *uval, u32 __user *_uaddr, 878c2ecf20Sopenharmony_ci u32 oldval, u32 newval) 888c2ecf20Sopenharmony_ci{ 898c2ecf20Sopenharmony_ci int ret = 0; 908c2ecf20Sopenharmony_ci unsigned int loops = FUTEX_MAX_LOOPS; 918c2ecf20Sopenharmony_ci u32 val, tmp; 928c2ecf20Sopenharmony_ci u32 __user *uaddr; 938c2ecf20Sopenharmony_ci 948c2ecf20Sopenharmony_ci if (!access_ok(_uaddr, sizeof(u32))) 958c2ecf20Sopenharmony_ci return -EFAULT; 968c2ecf20Sopenharmony_ci 978c2ecf20Sopenharmony_ci uaddr = __uaccess_mask_ptr(_uaddr); 988c2ecf20Sopenharmony_ci uaccess_enable(); 998c2ecf20Sopenharmony_ci asm volatile("// futex_atomic_cmpxchg_inatomic\n" 1008c2ecf20Sopenharmony_ci" prfm pstl1strm, %2\n" 1018c2ecf20Sopenharmony_ci"1: ldxr %w1, %2\n" 1028c2ecf20Sopenharmony_ci" sub %w3, %w1, %w5\n" 1038c2ecf20Sopenharmony_ci" cbnz %w3, 4f\n" 1048c2ecf20Sopenharmony_ci"2: stlxr %w3, %w6, %2\n" 1058c2ecf20Sopenharmony_ci" cbz %w3, 3f\n" 1068c2ecf20Sopenharmony_ci" sub %w4, %w4, %w3\n" 1078c2ecf20Sopenharmony_ci" cbnz %w4, 1b\n" 1088c2ecf20Sopenharmony_ci" mov %w0, %w8\n" 1098c2ecf20Sopenharmony_ci"3:\n" 1108c2ecf20Sopenharmony_ci" dmb ish\n" 1118c2ecf20Sopenharmony_ci"4:\n" 1128c2ecf20Sopenharmony_ci" .pushsection .fixup,\"ax\"\n" 1138c2ecf20Sopenharmony_ci"5: mov %w0, %w7\n" 1148c2ecf20Sopenharmony_ci" b 4b\n" 1158c2ecf20Sopenharmony_ci" .popsection\n" 1168c2ecf20Sopenharmony_ci _ASM_EXTABLE(1b, 5b) 1178c2ecf20Sopenharmony_ci _ASM_EXTABLE(2b, 5b) 1188c2ecf20Sopenharmony_ci : "+r" (ret), "=&r" (val), "+Q" (*uaddr), "=&r" (tmp), "+r" (loops) 1198c2ecf20Sopenharmony_ci : "r" (oldval), "r" (newval), "Ir" (-EFAULT), "Ir" (-EAGAIN) 1208c2ecf20Sopenharmony_ci : "memory"); 1218c2ecf20Sopenharmony_ci uaccess_disable(); 1228c2ecf20Sopenharmony_ci 1238c2ecf20Sopenharmony_ci if (!ret) 1248c2ecf20Sopenharmony_ci *uval = val; 1258c2ecf20Sopenharmony_ci 1268c2ecf20Sopenharmony_ci return ret; 1278c2ecf20Sopenharmony_ci} 1288c2ecf20Sopenharmony_ci 1298c2ecf20Sopenharmony_ci#endif /* __ASM_FUTEX_H */ 130