fix insufficient synchronization in sh atomic asm
[oweals/musl.git] / arch / sh / src / atomic.c
1 #include "libc.h"
2
3 #define LLSC_CLOBBERS   "r0", "t", "memory"
4 #define LLSC_START(mem) "synco\n"  \
5         "0:     movli.l @" mem ", r0\n"
6 #define LLSC_END(mem)              \
7         "1:     movco.l r0, @" mem "\n"    \
8         "       bf 0b\n"                   \
9         "       synco\n"
10
11 /* gusa is a hack in the kernel which lets you create a sequence of instructions
12  * which will be restarted if the process is preempted in the middle of the
13  * sequence. It will do for implementing atomics on non-smp systems. ABI is:
14  * r0  = address of first instruction after the atomic sequence
15  * r1  = original stack pointer
16  * r15 = -1 * length of atomic sequence in bytes
17  */
18 #define GUSA_CLOBBERS   "r0", "r1", "memory"
19 #define GUSA_START(mem,old,nop)    \
20         "       .align 2\n"                \
21         "       mova 1f, r0\n"             \
22         nop                            \
23         "       mov r15, r1\n"             \
24         "       mov #(0f-1f), r15\n"       \
25         "0:     mov.l @" mem ", " old "\n"
26 /* the target of mova must be 4 byte aligned, so we may need a nop */
27 #define GUSA_START_ODD(mem,old)  GUSA_START(mem,old,"")
28 #define GUSA_START_EVEN(mem,old) GUSA_START(mem,old,"\tnop\n")
29 #define GUSA_END(mem,new)          \
30         "       mov.l " new ", @" mem "\n" \
31         "1:     mov r1, r15\n"
32
33 #define CPU_HAS_LLSC 0x0040
34
35 int __sh_cas(volatile int *p, int t, int s)
36 {
37         int old;
38         if (__hwcap & CPU_HAS_LLSC) {
39                 __asm__ __volatile__(
40                         LLSC_START("%1")
41                         "       mov r0, %0\n"
42                         "       cmp/eq %0, %2\n"
43                         "       bf 1f\n"
44                         "       mov %3, r0\n"
45                         LLSC_END("%1")
46                         : "=&r"(old) : "r"(p), "r"(t), "r"(s) : LLSC_CLOBBERS);
47         } else {
48                 __asm__ __volatile__(
49                         GUSA_START_EVEN("%1", "%0")
50                         "       cmp/eq %0, %2\n"
51                         "       bf 1f\n"
52                         GUSA_END("%1", "%3")
53                         : "=&r"(old) : "r"(p), "r"(t), "r"(s) : GUSA_CLOBBERS, "t");
54         }
55         return old;
56 }
57
58 int __sh_swap(volatile int *x, int v)
59 {
60         int old;
61         if (__hwcap & CPU_HAS_LLSC) {
62                 __asm__ __volatile__(
63                         LLSC_START("%1")
64                         "       mov r0, %0\n"
65                         "       mov %2, r0\n"
66                         LLSC_END("%1")
67                         : "=&r"(old) : "r"(x), "r"(v) : LLSC_CLOBBERS);
68         } else {
69                 __asm__ __volatile__(
70                         GUSA_START_EVEN("%1", "%0")
71                         GUSA_END("%1", "%2")
72                         : "=&r"(old) : "r"(x), "r"(v) : GUSA_CLOBBERS);
73         }
74         return old;
75 }
76
77 int __sh_fetch_add(volatile int *x, int v)
78 {
79         int old, dummy;
80         if (__hwcap & CPU_HAS_LLSC) {
81                 __asm__ __volatile__(
82                         LLSC_START("%1")
83                         "       mov r0, %0\n"
84                         "       add %2, r0\n"
85                         LLSC_END("%1")
86                         : "=&r"(old) : "r"(x), "r"(v) : LLSC_CLOBBERS);
87         } else {
88                 __asm__ __volatile__(
89                         GUSA_START_EVEN("%2", "%0")
90                         "       mov %0, %1\n"
91                         "       add %3, %1\n"
92                         GUSA_END("%2", "%1")
93                         : "=&r"(old), "=&r"(dummy) : "r"(x), "r"(v) : GUSA_CLOBBERS);
94         }
95         return old;
96 }
97
98 void __sh_store(volatile int *p, int x)
99 {
100         if (__hwcap & CPU_HAS_LLSC) {
101                 __asm__ __volatile__(
102                         "       synco\n"
103                         "       mov.l %1, @%0\n"
104                         "       synco\n"
105                         : : "r"(p), "r"(x) : "memory");
106         } else {
107                 __asm__ __volatile__(
108                         "       mov.l %1, @%0\n"
109                         : : "r"(p), "r"(x) : "memory");
110         }
111 }
112
113 void __sh_and(volatile int *x, int v)
114 {
115         int dummy;
116         if (__hwcap & CPU_HAS_LLSC) {
117                 __asm__ __volatile__(
118                         LLSC_START("%0")
119                         "       and %1, r0\n"
120                         LLSC_END("%0")
121                         : : "r"(x), "r"(v) : LLSC_CLOBBERS);
122         } else {
123                 __asm__ __volatile__(
124                         GUSA_START_ODD("%1", "%0")
125                         "       and %2, %0\n"
126                         GUSA_END("%1", "%0")
127                         : "=&r"(dummy) : "r"(x), "r"(v) : GUSA_CLOBBERS);
128         }
129 }
130
131 void __sh_or(volatile int *x, int v)
132 {
133         int dummy;
134         if (__hwcap & CPU_HAS_LLSC) {
135                 __asm__ __volatile__(
136                         LLSC_START("%0")
137                         "       or %1, r0\n"
138                         LLSC_END("%0")
139                         : : "r"(x), "r"(v) : LLSC_CLOBBERS);
140         } else {
141                 __asm__ __volatile__(
142                         GUSA_START_ODD("%1", "%0")
143                         "       or %2, %0\n"
144                         GUSA_END("%1", "%0")
145                         : "=&r"(dummy) : "r"(x), "r"(v) : GUSA_CLOBBERS);
146         }
147 }