18c2ecf20Sopenharmony_ci/* SPDX-License-Identifier: GPL-2.0-only */ 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Atomic futex routines 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Based on the PowerPC implementataion 68c2ecf20Sopenharmony_ci * 78c2ecf20Sopenharmony_ci * Copyright (C) 2013 TangoTec Ltd. 88c2ecf20Sopenharmony_ci * 98c2ecf20Sopenharmony_ci * Baruch Siach <baruch@tkos.co.il> 108c2ecf20Sopenharmony_ci */ 118c2ecf20Sopenharmony_ci 128c2ecf20Sopenharmony_ci#ifndef _ASM_XTENSA_FUTEX_H 138c2ecf20Sopenharmony_ci#define _ASM_XTENSA_FUTEX_H 148c2ecf20Sopenharmony_ci 158c2ecf20Sopenharmony_ci#include <linux/futex.h> 168c2ecf20Sopenharmony_ci#include <linux/uaccess.h> 178c2ecf20Sopenharmony_ci#include <linux/errno.h> 188c2ecf20Sopenharmony_ci 198c2ecf20Sopenharmony_ci#if XCHAL_HAVE_EXCLUSIVE 208c2ecf20Sopenharmony_ci#define __futex_atomic_op(insn, ret, old, uaddr, arg) \ 218c2ecf20Sopenharmony_ci __asm__ __volatile( \ 228c2ecf20Sopenharmony_ci "1: l32ex %[oldval], %[addr]\n" \ 238c2ecf20Sopenharmony_ci insn "\n" \ 248c2ecf20Sopenharmony_ci "2: s32ex %[newval], %[addr]\n" \ 258c2ecf20Sopenharmony_ci " getex %[newval]\n" \ 268c2ecf20Sopenharmony_ci " beqz %[newval], 1b\n" \ 278c2ecf20Sopenharmony_ci " movi %[newval], 0\n" \ 288c2ecf20Sopenharmony_ci "3:\n" \ 298c2ecf20Sopenharmony_ci " .section .fixup,\"ax\"\n" \ 308c2ecf20Sopenharmony_ci " .align 4\n" \ 318c2ecf20Sopenharmony_ci " .literal_position\n" \ 328c2ecf20Sopenharmony_ci "5: movi %[oldval], 3b\n" \ 338c2ecf20Sopenharmony_ci " movi %[newval], %[fault]\n" \ 348c2ecf20Sopenharmony_ci " jx %[oldval]\n" \ 358c2ecf20Sopenharmony_ci " .previous\n" \ 368c2ecf20Sopenharmony_ci " .section __ex_table,\"a\"\n" \ 378c2ecf20Sopenharmony_ci " .long 1b, 5b, 2b, 5b\n" \ 388c2ecf20Sopenharmony_ci " .previous\n" \ 398c2ecf20Sopenharmony_ci : [oldval] "=&r" (old), [newval] "=&r" (ret) \ 408c2ecf20Sopenharmony_ci : [addr] "r" (uaddr), [oparg] "r" (arg), \ 418c2ecf20Sopenharmony_ci [fault] "I" (-EFAULT) \ 428c2ecf20Sopenharmony_ci : "memory") 438c2ecf20Sopenharmony_ci#elif XCHAL_HAVE_S32C1I 448c2ecf20Sopenharmony_ci#define __futex_atomic_op(insn, ret, old, uaddr, arg) \ 458c2ecf20Sopenharmony_ci __asm__ __volatile( \ 468c2ecf20Sopenharmony_ci "1: l32i %[oldval], %[mem]\n" \ 478c2ecf20Sopenharmony_ci insn "\n" \ 488c2ecf20Sopenharmony_ci " wsr %[oldval], scompare1\n" \ 498c2ecf20Sopenharmony_ci "2: s32c1i %[newval], %[mem]\n" \ 508c2ecf20Sopenharmony_ci " bne %[newval], %[oldval], 1b\n" \ 518c2ecf20Sopenharmony_ci " movi %[newval], 0\n" \ 528c2ecf20Sopenharmony_ci "3:\n" \ 538c2ecf20Sopenharmony_ci " .section .fixup,\"ax\"\n" \ 548c2ecf20Sopenharmony_ci " .align 4\n" \ 558c2ecf20Sopenharmony_ci " .literal_position\n" \ 568c2ecf20Sopenharmony_ci "5: movi %[oldval], 3b\n" \ 578c2ecf20Sopenharmony_ci " movi %[newval], %[fault]\n" \ 588c2ecf20Sopenharmony_ci " jx %[oldval]\n" \ 598c2ecf20Sopenharmony_ci " .previous\n" \ 608c2ecf20Sopenharmony_ci " .section __ex_table,\"a\"\n" \ 618c2ecf20Sopenharmony_ci " .long 1b, 5b, 2b, 5b\n" \ 628c2ecf20Sopenharmony_ci " .previous\n" \ 638c2ecf20Sopenharmony_ci : [oldval] "=&r" (old), [newval] "=&r" (ret), \ 648c2ecf20Sopenharmony_ci [mem] "+m" (*(uaddr)) \ 658c2ecf20Sopenharmony_ci : [oparg] "r" (arg), [fault] "I" (-EFAULT) \ 668c2ecf20Sopenharmony_ci : "memory") 678c2ecf20Sopenharmony_ci#endif 688c2ecf20Sopenharmony_ci 698c2ecf20Sopenharmony_cistatic inline int arch_futex_atomic_op_inuser(int op, int oparg, int *oval, 708c2ecf20Sopenharmony_ci u32 __user *uaddr) 718c2ecf20Sopenharmony_ci{ 728c2ecf20Sopenharmony_ci#if XCHAL_HAVE_S32C1I || XCHAL_HAVE_EXCLUSIVE 738c2ecf20Sopenharmony_ci int oldval = 0, ret; 748c2ecf20Sopenharmony_ci 758c2ecf20Sopenharmony_ci if (!access_ok(uaddr, sizeof(u32))) 768c2ecf20Sopenharmony_ci return -EFAULT; 778c2ecf20Sopenharmony_ci 788c2ecf20Sopenharmony_ci switch (op) { 798c2ecf20Sopenharmony_ci case FUTEX_OP_SET: 808c2ecf20Sopenharmony_ci __futex_atomic_op("mov %[newval], %[oparg]", 818c2ecf20Sopenharmony_ci ret, oldval, uaddr, oparg); 828c2ecf20Sopenharmony_ci break; 838c2ecf20Sopenharmony_ci case FUTEX_OP_ADD: 848c2ecf20Sopenharmony_ci __futex_atomic_op("add %[newval], %[oldval], %[oparg]", 858c2ecf20Sopenharmony_ci ret, oldval, uaddr, oparg); 868c2ecf20Sopenharmony_ci break; 878c2ecf20Sopenharmony_ci case FUTEX_OP_OR: 888c2ecf20Sopenharmony_ci __futex_atomic_op("or %[newval], %[oldval], %[oparg]", 898c2ecf20Sopenharmony_ci ret, oldval, uaddr, oparg); 908c2ecf20Sopenharmony_ci break; 918c2ecf20Sopenharmony_ci case FUTEX_OP_ANDN: 928c2ecf20Sopenharmony_ci __futex_atomic_op("and %[newval], %[oldval], %[oparg]", 938c2ecf20Sopenharmony_ci ret, oldval, uaddr, ~oparg); 948c2ecf20Sopenharmony_ci break; 958c2ecf20Sopenharmony_ci case FUTEX_OP_XOR: 968c2ecf20Sopenharmony_ci __futex_atomic_op("xor %[newval], %[oldval], %[oparg]", 978c2ecf20Sopenharmony_ci ret, oldval, uaddr, oparg); 988c2ecf20Sopenharmony_ci break; 998c2ecf20Sopenharmony_ci default: 1008c2ecf20Sopenharmony_ci ret = -ENOSYS; 1018c2ecf20Sopenharmony_ci } 1028c2ecf20Sopenharmony_ci 1038c2ecf20Sopenharmony_ci if (!ret) 1048c2ecf20Sopenharmony_ci *oval = oldval; 1058c2ecf20Sopenharmony_ci 1068c2ecf20Sopenharmony_ci return ret; 1078c2ecf20Sopenharmony_ci#else 1088c2ecf20Sopenharmony_ci return -ENOSYS; 1098c2ecf20Sopenharmony_ci#endif 1108c2ecf20Sopenharmony_ci} 1118c2ecf20Sopenharmony_ci 1128c2ecf20Sopenharmony_cistatic inline int 1138c2ecf20Sopenharmony_cifutex_atomic_cmpxchg_inatomic(u32 *uval, u32 __user *uaddr, 1148c2ecf20Sopenharmony_ci u32 oldval, u32 newval) 1158c2ecf20Sopenharmony_ci{ 1168c2ecf20Sopenharmony_ci#if XCHAL_HAVE_S32C1I || XCHAL_HAVE_EXCLUSIVE 1178c2ecf20Sopenharmony_ci unsigned long tmp; 1188c2ecf20Sopenharmony_ci int ret = 0; 1198c2ecf20Sopenharmony_ci 1208c2ecf20Sopenharmony_ci if (!access_ok(uaddr, sizeof(u32))) 1218c2ecf20Sopenharmony_ci return -EFAULT; 1228c2ecf20Sopenharmony_ci 1238c2ecf20Sopenharmony_ci __asm__ __volatile__ ( 1248c2ecf20Sopenharmony_ci " # futex_atomic_cmpxchg_inatomic\n" 1258c2ecf20Sopenharmony_ci#if XCHAL_HAVE_EXCLUSIVE 1268c2ecf20Sopenharmony_ci "1: l32ex %[tmp], %[addr]\n" 1278c2ecf20Sopenharmony_ci " s32i %[tmp], %[uval], 0\n" 1288c2ecf20Sopenharmony_ci " bne %[tmp], %[oldval], 2f\n" 1298c2ecf20Sopenharmony_ci " mov %[tmp], %[newval]\n" 1308c2ecf20Sopenharmony_ci "3: s32ex %[tmp], %[addr]\n" 1318c2ecf20Sopenharmony_ci " getex %[tmp]\n" 1328c2ecf20Sopenharmony_ci " beqz %[tmp], 1b\n" 1338c2ecf20Sopenharmony_ci#elif XCHAL_HAVE_S32C1I 1348c2ecf20Sopenharmony_ci " wsr %[oldval], scompare1\n" 1358c2ecf20Sopenharmony_ci "1: s32c1i %[newval], %[addr], 0\n" 1368c2ecf20Sopenharmony_ci " s32i %[newval], %[uval], 0\n" 1378c2ecf20Sopenharmony_ci#endif 1388c2ecf20Sopenharmony_ci "2:\n" 1398c2ecf20Sopenharmony_ci " .section .fixup,\"ax\"\n" 1408c2ecf20Sopenharmony_ci " .align 4\n" 1418c2ecf20Sopenharmony_ci " .literal_position\n" 1428c2ecf20Sopenharmony_ci "4: movi %[tmp], 2b\n" 1438c2ecf20Sopenharmony_ci " movi %[ret], %[fault]\n" 1448c2ecf20Sopenharmony_ci " jx %[tmp]\n" 1458c2ecf20Sopenharmony_ci " .previous\n" 1468c2ecf20Sopenharmony_ci " .section __ex_table,\"a\"\n" 1478c2ecf20Sopenharmony_ci " .long 1b, 4b\n" 1488c2ecf20Sopenharmony_ci#if XCHAL_HAVE_EXCLUSIVE 1498c2ecf20Sopenharmony_ci " .long 3b, 4b\n" 1508c2ecf20Sopenharmony_ci#endif 1518c2ecf20Sopenharmony_ci " .previous\n" 1528c2ecf20Sopenharmony_ci : [ret] "+r" (ret), [newval] "+r" (newval), [tmp] "=&r" (tmp) 1538c2ecf20Sopenharmony_ci : [addr] "r" (uaddr), [oldval] "r" (oldval), [uval] "r" (uval), 1548c2ecf20Sopenharmony_ci [fault] "I" (-EFAULT) 1558c2ecf20Sopenharmony_ci : "memory"); 1568c2ecf20Sopenharmony_ci 1578c2ecf20Sopenharmony_ci return ret; 1588c2ecf20Sopenharmony_ci#else 1598c2ecf20Sopenharmony_ci return -ENOSYS; 1608c2ecf20Sopenharmony_ci#endif 1618c2ecf20Sopenharmony_ci} 1628c2ecf20Sopenharmony_ci 1638c2ecf20Sopenharmony_ci#endif /* _ASM_XTENSA_FUTEX_H */ 164