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/war.h>
  20
  21#define __futex_atomic_op(insn, ret, oldval, uaddr, oparg)              \
  22{                                                                       \
  23        if (cpu_has_llsc && R10000_LLSC_WAR) {                          \
  24                __asm__ __volatile__(                                   \
  25                "       .set    push                            \n"     \
  26                "       .set    noat                            \n"     \
  27                "       .set    arch=r4000                      \n"     \
  28                "1:     ll      %1, %4  # __futex_atomic_op     \n"     \
  29                "       .set    mips0                           \n"     \
  30                "       " insn  "                               \n"     \
  31                "       .set    arch=r4000                      \n"     \
  32                "2:     sc      $1, %2                          \n"     \
  33                "       beqzl   $1, 1b                          \n"     \
  34                __WEAK_LLSC_MB                                          \
  35                "3:                                             \n"     \
  36                "       .insn                                   \n"     \
  37                "       .set    pop                             \n"     \
  38                "       .set    mips0                           \n"     \
  39                "       .section .fixup,\"ax\"                  \n"     \
  40                "4:     li      %0, %6                          \n"     \
  41                "       j       3b                              \n"     \
  42                "       .previous                               \n"     \
  43                "       .section __ex_table,\"a\"               \n"     \
  44                "       "__UA_ADDR "\t1b, 4b                    \n"     \
  45                "       "__UA_ADDR "\t2b, 4b                    \n"     \
  46                "       .previous                               \n"     \
  47                : "=r" (ret), "=&r" (oldval),                           \
  48                  "=" GCC_OFF_SMALL_ASM() (*uaddr)                              \
  49                : "0" (0), GCC_OFF_SMALL_ASM() (*uaddr), "Jr" (oparg),  \
  50                  "i" (-EFAULT)                                         \
  51                : "memory");                                            \
  52        } else if (cpu_has_llsc) {                                      \
  53                __asm__ __volatile__(                                   \
  54                "       .set    push                            \n"     \
  55                "       .set    noat                            \n"     \
  56                "       .set    "MIPS_ISA_ARCH_LEVEL"           \n"     \
  57                "1:     "user_ll("%1", "%4")" # __futex_atomic_op\n"    \
  58                "       .set    mips0                           \n"     \
  59                "       " insn  "                               \n"     \
  60                "       .set    "MIPS_ISA_ARCH_LEVEL"           \n"     \
  61                "2:     "user_sc("$1", "%2")"                   \n"     \
  62                "       beqz    $1, 1b                          \n"     \
  63                __WEAK_LLSC_MB                                          \
  64                "3:                                             \n"     \
  65                "       .insn                                   \n"     \
  66                "       .set    pop                             \n"     \
  67                "       .set    mips0                           \n"     \
  68                "       .section .fixup,\"ax\"                  \n"     \
  69                "4:     li      %0, %6                          \n"     \
  70                "       j       3b                              \n"     \
  71                "       .previous                               \n"     \
  72                "       .section __ex_table,\"a\"               \n"     \
  73                "       "__UA_ADDR "\t1b, 4b                    \n"     \
  74                "       "__UA_ADDR "\t2b, 4b                    \n"     \
  75                "       .previous                               \n"     \
  76                : "=r" (ret), "=&r" (oldval),                           \
  77                  "=" GCC_OFF_SMALL_ASM() (*uaddr)                              \
  78                : "0" (0), GCC_OFF_SMALL_ASM() (*uaddr), "Jr" (oparg),  \
  79                  "i" (-EFAULT)                                         \
  80                : "memory");                                            \
  81        } else                                                          \
  82                ret = -ENOSYS;                                          \
  83}
  84
  85static inline int
  86futex_atomic_op_inuser(int encoded_op, u32 __user *uaddr)
  87{
  88        int op = (encoded_op >> 28) & 7;
  89        int cmp = (encoded_op >> 24) & 15;
  90        int oparg = (encoded_op << 8) >> 20;
  91        int cmparg = (encoded_op << 20) >> 20;
  92        int oldval = 0, ret;
  93        if (encoded_op & (FUTEX_OP_OPARG_SHIFT << 28))
  94                oparg = 1 << oparg;
  95
  96        if (! access_ok (VERIFY_WRITE, uaddr, sizeof(u32)))
  97                return -EFAULT;
  98
  99        pagefault_disable();
 100
 101        switch (op) {
 102        case FUTEX_OP_SET:
 103                __futex_atomic_op("move $1, %z5", ret, oldval, uaddr, oparg);
 104                break;
 105
 106        case FUTEX_OP_ADD:
 107                __futex_atomic_op("addu $1, %1, %z5",
 108                                  ret, oldval, uaddr, oparg);
 109                break;
 110        case FUTEX_OP_OR:
 111                __futex_atomic_op("or   $1, %1, %z5",
 112                                  ret, oldval, uaddr, oparg);
 113                break;
 114        case FUTEX_OP_ANDN:
 115                __futex_atomic_op("and  $1, %1, %z5",
 116                                  ret, oldval, uaddr, ~oparg);
 117                break;
 118        case FUTEX_OP_XOR:
 119                __futex_atomic_op("xor  $1, %1, %z5",
 120                                  ret, oldval, uaddr, oparg);
 121                break;
 122        default:
 123                ret = -ENOSYS;
 124        }
 125
 126        pagefault_enable();
 127
 128        if (!ret) {
 129                switch (cmp) {
 130                case FUTEX_OP_CMP_EQ: ret = (oldval == cmparg); break;
 131                case FUTEX_OP_CMP_NE: ret = (oldval != cmparg); break;
 132                case FUTEX_OP_CMP_LT: ret = (oldval < cmparg); break;
 133                case FUTEX_OP_CMP_GE: ret = (oldval >= cmparg); break;
 134                case FUTEX_OP_CMP_LE: ret = (oldval <= cmparg); break;
 135                case FUTEX_OP_CMP_GT: ret = (oldval > cmparg); break;
 136                default: ret = -ENOSYS;
 137                }
 138        }
 139        return ret;
 140}
 141
 142static inline int
 143futex_atomic_cmpxchg_inatomic(u32 *uval, u32 __user *uaddr,
 144                              u32 oldval, u32 newval)
 145{
 146        int ret = 0;
 147        u32 val;
 148
 149        if (!access_ok(VERIFY_WRITE, uaddr, sizeof(u32)))
 150                return -EFAULT;
 151
 152        if (cpu_has_llsc && R10000_LLSC_WAR) {
 153                __asm__ __volatile__(
 154                "# futex_atomic_cmpxchg_inatomic                        \n"
 155                "       .set    push                                    \n"
 156                "       .set    noat                                    \n"
 157                "       .set    arch=r4000                              \n"
 158                "1:     ll      %1, %3                                  \n"
 159                "       bne     %1, %z4, 3f                             \n"
 160                "       .set    mips0                                   \n"
 161                "       move    $1, %z5                                 \n"
 162                "       .set    arch=r4000                              \n"
 163                "2:     sc      $1, %2                                  \n"
 164                "       beqzl   $1, 1b                                  \n"
 165                __WEAK_LLSC_MB
 166                "3:                                                     \n"
 167                "       .insn                                           \n"
 168                "       .set    pop                                     \n"
 169                "       .section .fixup,\"ax\"                          \n"
 170                "4:     li      %0, %6                                  \n"
 171                "       j       3b                                      \n"
 172                "       .previous                                       \n"
 173                "       .section __ex_table,\"a\"                       \n"
 174                "       "__UA_ADDR "\t1b, 4b                            \n"
 175                "       "__UA_ADDR "\t2b, 4b                            \n"
 176                "       .previous                                       \n"
 177                : "+r" (ret), "=&r" (val), "=" GCC_OFF_SMALL_ASM() (*uaddr)
 178                : GCC_OFF_SMALL_ASM() (*uaddr), "Jr" (oldval), "Jr" (newval),
 179                  "i" (-EFAULT)
 180                : "memory");
 181        } else if (cpu_has_llsc) {
 182                __asm__ __volatile__(
 183                "# futex_atomic_cmpxchg_inatomic                        \n"
 184                "       .set    push                                    \n"
 185                "       .set    noat                                    \n"
 186                "       .set    "MIPS_ISA_ARCH_LEVEL"                   \n"
 187                "1:     "user_ll("%1", "%3")"                           \n"
 188                "       bne     %1, %z4, 3f                             \n"
 189                "       .set    mips0                                   \n"
 190                "       move    $1, %z5                                 \n"
 191                "       .set    "MIPS_ISA_ARCH_LEVEL"                   \n"
 192                "2:     "user_sc("$1", "%2")"                           \n"
 193                "       beqz    $1, 1b                                  \n"
 194                __WEAK_LLSC_MB
 195                "3:                                                     \n"
 196                "       .insn                                           \n"
 197                "       .set    pop                                     \n"
 198                "       .section .fixup,\"ax\"                          \n"
 199                "4:     li      %0, %6                                  \n"
 200                "       j       3b                                      \n"
 201                "       .previous                                       \n"
 202                "       .section __ex_table,\"a\"                       \n"
 203                "       "__UA_ADDR "\t1b, 4b                            \n"
 204                "       "__UA_ADDR "\t2b, 4b                            \n"
 205                "       .previous                                       \n"
 206                : "+r" (ret), "=&r" (val), "=" GCC_OFF_SMALL_ASM() (*uaddr)
 207                : GCC_OFF_SMALL_ASM() (*uaddr), "Jr" (oldval), "Jr" (newval),
 208                  "i" (-EFAULT)
 209                : "memory");
 210        } else
 211                return -ENOSYS;
 212
 213        *uval = val;
 214        return ret;
 215}
 216
 217#endif
 218#endif /* _ASM_FUTEX_H */
 219