linux/arch/mips/include/asm/futex.h
<<
>>
Prefs
   1/*
   2 * This file is subject to the terms and conditions of the GNU General Public
   3 * License.  See the file "COPYING" in the main directory of this archive
   4 * for more details.
   5 *
   6 * Copyright (c) 2006  Ralf Baechle (ralf@linux-mips.org)
   7 */
   8#ifndef _ASM_FUTEX_H
   9#define _ASM_FUTEX_H
  10
  11#ifdef __KERNEL__
  12
  13#include <linux/futex.h>
  14#include <linux/uaccess.h>
  15#include <asm/asm-eva.h>
  16#include <asm/barrier.h>
  17#include <asm/compiler.h>
  18#include <asm/errno.h>
  19#include <asm/sync.h>
  20#include <asm/war.h>
  21
  22#define __futex_atomic_op(insn, ret, oldval, uaddr, oparg)              \
  23{                                                                       \
  24        if (cpu_has_llsc && IS_ENABLED(CONFIG_WAR_R10000_LLSC)) {       \
  25                __asm__ __volatile__(                                   \
  26                "       .set    push                            \n"     \
  27                "       .set    noat                            \n"     \
  28                "       .set    push                            \n"     \
  29                "       .set    arch=r4000                      \n"     \
  30                "1:     ll      %1, %4  # __futex_atomic_op     \n"     \
  31                "       .set    pop                             \n"     \
  32                "       " insn  "                               \n"     \
  33                "       .set    arch=r4000                      \n"     \
  34                "2:     sc      $1, %2                          \n"     \
  35                "       beqzl   $1, 1b                          \n"     \
  36                __stringify(__WEAK_LLSC_MB) "                   \n"     \
  37                "3:                                             \n"     \
  38                "       .insn                                   \n"     \
  39                "       .set    pop                             \n"     \
  40                "       .section .fixup,\"ax\"                  \n"     \
  41                "4:     li      %0, %6                          \n"     \
  42                "       j       3b                              \n"     \
  43                "       .previous                               \n"     \
  44                "       .section __ex_table,\"a\"               \n"     \
  45                "       "__UA_ADDR "\t1b, 4b                    \n"     \
  46                "       "__UA_ADDR "\t2b, 4b                    \n"     \
  47                "       .previous                               \n"     \
  48                : "=r" (ret), "=&r" (oldval),                           \
  49                  "=" GCC_OFF_SMALL_ASM() (*uaddr)                              \
  50                : "0" (0), GCC_OFF_SMALL_ASM() (*uaddr), "Jr" (oparg),  \
  51                  "i" (-EFAULT)                                         \
  52                : "memory");                                            \
  53        } else if (cpu_has_llsc) {                                      \
  54                __asm__ __volatile__(                                   \
  55                "       .set    push                            \n"     \
  56                "       .set    noat                            \n"     \
  57                "       .set    push                            \n"     \
  58                "       .set    "MIPS_ISA_ARCH_LEVEL"           \n"     \
  59                "       " __SYNC(full, loongson3_war) "         \n"     \
  60                "1:     "user_ll("%1", "%4")" # __futex_atomic_op\n"    \
  61                "       .set    pop                             \n"     \
  62                "       " insn  "                               \n"     \
  63                "       .set    "MIPS_ISA_ARCH_LEVEL"           \n"     \
  64                "2:     "user_sc("$1", "%2")"                   \n"     \
  65                "       beqz    $1, 1b                          \n"     \
  66                __stringify(__WEAK_LLSC_MB) "                   \n"     \
  67                "3:                                             \n"     \
  68                "       .insn                                   \n"     \
  69                "       .set    pop                             \n"     \
  70                "       .section .fixup,\"ax\"                  \n"     \
  71                "4:     li      %0, %6                          \n"     \
  72                "       j       3b                              \n"     \
  73                "       .previous                               \n"     \
  74                "       .section __ex_table,\"a\"               \n"     \
  75                "       "__UA_ADDR "\t1b, 4b                    \n"     \
  76                "       "__UA_ADDR "\t2b, 4b                    \n"     \
  77                "       .previous                               \n"     \
  78                : "=r" (ret), "=&r" (oldval),                           \
  79                  "=" GCC_OFF_SMALL_ASM() (*uaddr)                              \
  80                : "0" (0), GCC_OFF_SMALL_ASM() (*uaddr), "Jr" (oparg),  \
  81                  "i" (-EFAULT)                                         \
  82                : "memory");                                            \
  83        } else                                                          \
  84                ret = -ENOSYS;                                          \
  85}
  86
  87static inline int
  88arch_futex_atomic_op_inuser(int op, int oparg, int *oval, u32 __user *uaddr)
  89{
  90        int oldval = 0, ret;
  91
  92        if (!access_ok(uaddr, sizeof(u32)))
  93                return -EFAULT;
  94
  95        switch (op) {
  96        case FUTEX_OP_SET:
  97                __futex_atomic_op("move $1, %z5", ret, oldval, uaddr, oparg);
  98                break;
  99
 100        case FUTEX_OP_ADD:
 101                __futex_atomic_op("addu $1, %1, %z5",
 102                                  ret, oldval, uaddr, oparg);
 103                break;
 104        case FUTEX_OP_OR:
 105                __futex_atomic_op("or   $1, %1, %z5",
 106                                  ret, oldval, uaddr, oparg);
 107                break;
 108        case FUTEX_OP_ANDN:
 109                __futex_atomic_op("and  $1, %1, %z5",
 110                                  ret, oldval, uaddr, ~oparg);
 111                break;
 112        case FUTEX_OP_XOR:
 113                __futex_atomic_op("xor  $1, %1, %z5",
 114                                  ret, oldval, uaddr, oparg);
 115                break;
 116        default:
 117                ret = -ENOSYS;
 118        }
 119
 120        if (!ret)
 121                *oval = oldval;
 122
 123        return ret;
 124}
 125
 126static inline int
 127futex_atomic_cmpxchg_inatomic(u32 *uval, u32 __user *uaddr,
 128                              u32 oldval, u32 newval)
 129{
 130        int ret = 0;
 131        u32 val;
 132
 133        if (!access_ok(uaddr, sizeof(u32)))
 134                return -EFAULT;
 135
 136        if (cpu_has_llsc && IS_ENABLED(CONFIG_WAR_R10000_LLSC)) {
 137                __asm__ __volatile__(
 138                "# futex_atomic_cmpxchg_inatomic                        \n"
 139                "       .set    push                                    \n"
 140                "       .set    noat                                    \n"
 141                "       .set    push                                    \n"
 142                "       .set    arch=r4000                              \n"
 143                "1:     ll      %1, %3                                  \n"
 144                "       bne     %1, %z4, 3f                             \n"
 145                "       .set    pop                                     \n"
 146                "       move    $1, %z5                                 \n"
 147                "       .set    arch=r4000                              \n"
 148                "2:     sc      $1, %2                                  \n"
 149                "       beqzl   $1, 1b                                  \n"
 150                __stringify(__WEAK_LLSC_MB) "                           \n"
 151                "3:                                                     \n"
 152                "       .insn                                           \n"
 153                "       .set    pop                                     \n"
 154                "       .section .fixup,\"ax\"                          \n"
 155                "4:     li      %0, %6                                  \n"
 156                "       j       3b                                      \n"
 157                "       .previous                                       \n"
 158                "       .section __ex_table,\"a\"                       \n"
 159                "       "__UA_ADDR "\t1b, 4b                            \n"
 160                "       "__UA_ADDR "\t2b, 4b                            \n"
 161                "       .previous                                       \n"
 162                : "+r" (ret), "=&r" (val), "=" GCC_OFF_SMALL_ASM() (*uaddr)
 163                : GCC_OFF_SMALL_ASM() (*uaddr), "Jr" (oldval), "Jr" (newval),
 164                  "i" (-EFAULT)
 165                : "memory");
 166        } else if (cpu_has_llsc) {
 167                __asm__ __volatile__(
 168                "# futex_atomic_cmpxchg_inatomic                        \n"
 169                "       .set    push                                    \n"
 170                "       .set    noat                                    \n"
 171                "       .set    push                                    \n"
 172                "       .set    "MIPS_ISA_ARCH_LEVEL"                   \n"
 173                "       " __SYNC(full, loongson3_war) "                 \n"
 174                "1:     "user_ll("%1", "%3")"                           \n"
 175                "       bne     %1, %z4, 3f                             \n"
 176                "       .set    pop                                     \n"
 177                "       move    $1, %z5                                 \n"
 178                "       .set    "MIPS_ISA_ARCH_LEVEL"                   \n"
 179                "2:     "user_sc("$1", "%2")"                           \n"
 180                "       beqz    $1, 1b                                  \n"
 181                "3:     " __SYNC_ELSE(full, loongson3_war, __WEAK_LLSC_MB) "\n"
 182                "       .insn                                           \n"
 183                "       .set    pop                                     \n"
 184                "       .section .fixup,\"ax\"                          \n"
 185                "4:     li      %0, %6                                  \n"
 186                "       j       3b                                      \n"
 187                "       .previous                                       \n"
 188                "       .section __ex_table,\"a\"                       \n"
 189                "       "__UA_ADDR "\t1b, 4b                            \n"
 190                "       "__UA_ADDR "\t2b, 4b                            \n"
 191                "       .previous                                       \n"
 192                : "+r" (ret), "=&r" (val), "=" GCC_OFF_SMALL_ASM() (*uaddr)
 193                : GCC_OFF_SMALL_ASM() (*uaddr), "Jr" (oldval), "Jr" (newval),
 194                  "i" (-EFAULT)
 195                : "memory");
 196        } else
 197                return -ENOSYS;
 198
 199        *uval = val;
 200        return ret;
 201}
 202
 203#endif
 204#endif /* _ASM_FUTEX_H */
 205