Commit | Line | Data |
---|---|---|
b90edb33 JW |
1 | /* SPDX-License-Identifier: GPL-2.0 */ |
2 | /* | |
3 | * Copyright (c) 2006 Ralf Baechle (ralf@linux-mips.org) | |
4 | * Copyright (c) 2018 Jim Wilson (jimw@sifive.com) | |
5 | */ | |
6 | ||
7 | #ifndef _ASM_FUTEX_H | |
8 | #define _ASM_FUTEX_H | |
9 | ||
10 | #ifndef CONFIG_RISCV_ISA_A | |
11 | /* | |
12 | * Use the generic interrupt disabling versions if the A extension | |
13 | * is not supported. | |
14 | */ | |
15 | #ifdef CONFIG_SMP | |
16 | #error "Can't support generic futex calls without A extension on SMP" | |
17 | #endif | |
18 | #include <asm-generic/futex.h> | |
19 | ||
20 | #else /* CONFIG_RISCV_ISA_A */ | |
21 | ||
22 | #include <linux/futex.h> | |
23 | #include <linux/uaccess.h> | |
24 | #include <linux/errno.h> | |
25 | #include <asm/asm.h> | |
26 | ||
27 | #define __futex_atomic_op(insn, ret, oldval, uaddr, oparg) \ | |
28 | { \ | |
29 | uintptr_t tmp; \ | |
30 | __enable_user_access(); \ | |
31 | __asm__ __volatile__ ( \ | |
32 | "1: " insn " \n" \ | |
33 | "2: \n" \ | |
34 | " .section .fixup,\"ax\" \n" \ | |
35 | " .balign 4 \n" \ | |
36 | "3: li %[r],%[e] \n" \ | |
37 | " jump 2b,%[t] \n" \ | |
38 | " .previous \n" \ | |
39 | " .section __ex_table,\"a\" \n" \ | |
40 | " .balign " RISCV_SZPTR " \n" \ | |
41 | " " RISCV_PTR " 1b, 3b \n" \ | |
42 | " .previous \n" \ | |
43 | : [r] "+r" (ret), [ov] "=&r" (oldval), \ | |
44 | [u] "+m" (*uaddr), [t] "=&r" (tmp) \ | |
45 | : [op] "Jr" (oparg), [e] "i" (-EFAULT) \ | |
46 | : "memory"); \ | |
47 | __disable_user_access(); \ | |
48 | } | |
49 | ||
50 | static inline int | |
51 | arch_futex_atomic_op_inuser(int op, int oparg, int *oval, u32 __user *uaddr) | |
52 | { | |
53 | int oldval = 0, ret = 0; | |
54 | ||
55 | pagefault_disable(); | |
56 | ||
57 | switch (op) { | |
58 | case FUTEX_OP_SET: | |
59 | __futex_atomic_op("amoswap.w.aqrl %[ov],%z[op],%[u]", | |
60 | ret, oldval, uaddr, oparg); | |
61 | break; | |
62 | case FUTEX_OP_ADD: | |
63 | __futex_atomic_op("amoadd.w.aqrl %[ov],%z[op],%[u]", | |
64 | ret, oldval, uaddr, oparg); | |
65 | break; | |
66 | case FUTEX_OP_OR: | |
67 | __futex_atomic_op("amoor.w.aqrl %[ov],%z[op],%[u]", | |
68 | ret, oldval, uaddr, oparg); | |
69 | break; | |
70 | case FUTEX_OP_ANDN: | |
71 | __futex_atomic_op("amoand.w.aqrl %[ov],%z[op],%[u]", | |
72 | ret, oldval, uaddr, ~oparg); | |
73 | break; | |
74 | case FUTEX_OP_XOR: | |
75 | __futex_atomic_op("amoxor.w.aqrl %[ov],%z[op],%[u]", | |
76 | ret, oldval, uaddr, oparg); | |
77 | break; | |
78 | default: | |
79 | ret = -ENOSYS; | |
80 | } | |
81 | ||
82 | pagefault_enable(); | |
83 | ||
84 | if (!ret) | |
85 | *oval = oldval; | |
86 | ||
87 | return ret; | |
88 | } | |
89 | ||
90 | static inline int | |
91 | futex_atomic_cmpxchg_inatomic(u32 *uval, u32 __user *uaddr, | |
92 | u32 oldval, u32 newval) | |
93 | { | |
94 | int ret = 0; | |
95 | u32 val; | |
96 | uintptr_t tmp; | |
97 | ||
96d4f267 | 98 | if (!access_ok(uaddr, sizeof(u32))) |
b90edb33 JW |
99 | return -EFAULT; |
100 | ||
101 | __enable_user_access(); | |
102 | __asm__ __volatile__ ( | |
103 | "1: lr.w.aqrl %[v],%[u] \n" | |
104 | " bne %[v],%z[ov],3f \n" | |
105 | "2: sc.w.aqrl %[t],%z[nv],%[u] \n" | |
106 | " bnez %[t],1b \n" | |
107 | "3: \n" | |
108 | " .section .fixup,\"ax\" \n" | |
109 | " .balign 4 \n" | |
110 | "4: li %[r],%[e] \n" | |
111 | " jump 3b,%[t] \n" | |
112 | " .previous \n" | |
113 | " .section __ex_table,\"a\" \n" | |
114 | " .balign " RISCV_SZPTR " \n" | |
115 | " " RISCV_PTR " 1b, 4b \n" | |
116 | " " RISCV_PTR " 2b, 4b \n" | |
117 | " .previous \n" | |
118 | : [r] "+r" (ret), [v] "=&r" (val), [u] "+m" (*uaddr), [t] "=&r" (tmp) | |
119 | : [ov] "Jr" (oldval), [nv] "Jr" (newval), [e] "i" (-EFAULT) | |
120 | : "memory"); | |
121 | __disable_user_access(); | |
122 | ||
123 | *uval = val; | |
124 | return ret; | |
125 | } | |
126 | ||
127 | #endif /* CONFIG_RISCV_ISA_A */ | |
128 | #endif /* _ASM_FUTEX_H */ |