1 /*
2 * Copyright (C) 2022 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17 #ifndef CHRE_PLATFORM_ARM_ATOMIC_BASE_H_
18 #define CHRE_PLATFORM_ARM_ATOMIC_BASE_H_
19
20 namespace chre {
21
22 namespace atomic {
23
24 /**
25 * Atomically swap the value of a byte with a new value.
26 *
27 * @param byte Pointer to a byte whose value is to be swapped out.
28 * @param newValue The value replacing the old value in the byte.
29 * @return The bytes pre-swap value.
30 */
swapByte(volatile uint8_t * byte,uint32_t newValue)31 inline bool swapByte(volatile uint8_t *byte, uint32_t newValue) {
32 uint32_t prevValue;
33 uint32_t storeFailed;
34
35 do {
36 asm volatile(
37 "ldrexb %0, [%3] \n"
38 "strexb %1, %2, [%3] \n"
39 : "=r"(prevValue), "=r"(storeFailed), "=r"(newValue), "=r"(byte)
40 : "2"(newValue), "3"(byte)
41 : "memory");
42 } while (storeFailed);
43
44 return prevValue;
45 }
46
47 /**
48 * Atomically swap the value of a 32-bit word with a new value.
49 *
50 * @param word Pointer to a 32-bit word whose value is to be swapped out.
51 * @param newValue The value replacing the old value in the 32-bit word.
52 * @return The 32-bit word's pre-swap value.
53 */
swapWord(volatile uint32_t * word,uint32_t newValue)54 inline uint32_t swapWord(volatile uint32_t *word, uint32_t newValue) {
55 uint32_t prevValue;
56 uint32_t storeFailed;
57
58 do {
59 asm volatile(
60 "ldrex %0, [%3] \n"
61 "strex %1, %2, [%3] \n"
62 : "=r"(prevValue), "=r"(storeFailed), "=r"(newValue), "=r"(word)
63 : "2"(newValue), "3"(word)
64 : "memory");
65 } while (storeFailed);
66
67 return prevValue;
68 }
69
70 /**
71 * Atomically add a value to a 32-bit word.
72 *
73 * @param word Pointer to a 32-bit word whose value is to be incremented.
74 * @param addend Value that is to be added to the 32-bit word.
75 * @return The 32-bit word's value before the addition was performed.
76 */
addToWord(volatile uint32_t * word,uint32_t addend)77 inline uint32_t addToWord(volatile uint32_t *word, uint32_t addend) {
78 uint32_t prevValue;
79 uint32_t storeFailed;
80 uint32_t tmp;
81
82 do {
83 asm volatile(
84 "ldrex %0, [%4] \n"
85 "add %2, %0, %3 \n"
86 "strex %1, %2, [%4] \n"
87 : "=r"(prevValue), "=r"(storeFailed), "=r"(tmp), "=r"(addend),
88 "=r"(word)
89 : "3"(addend), "4"(word)
90 : "memory");
91 } while (storeFailed);
92
93 return prevValue;
94 }
95
96 /**
97 * Atomically subtract a value from a 32-bit word.
98 *
99 * @param word Pointer to a 32-bit word which is to be decremented.
100 * @param arg Value to subtract from the 32-bit word.
101 * @return Value of the 32-bit word before the subtraction was performed.
102 */
subFromWord(volatile uint32_t * word,uint32_t arg)103 inline uint32_t subFromWord(volatile uint32_t *word, uint32_t arg) {
104 uint32_t prevValue;
105 uint32_t storeFailed;
106 uint32_t tmp;
107
108 do {
109 asm volatile(
110 "ldrex %0, [%4] \n"
111 "sub %2, %0, %3 \n"
112 "strex %1, %2, [%4] \n"
113 : "=r"(prevValue), "=r"(storeFailed), "=r"(tmp), "=r"(arg), "=r"(word)
114 : "3"(arg), "4"(word)
115 : "memory");
116 } while (storeFailed);
117
118 return prevValue;
119 }
120
121 } // namespace atomic
122
123 /**
124 * Base class implementation for the Atomic Bool and Uint32 types.
125 */
126 template <typename T>
127 class AtomicBase {
128 public:
129 /**
130 * Generic atomic load of a value implemented via a compiler level memory
131 * barrier.
132 *
133 * @return The current value of the data stored.
134 */
get()135 inline bool get() const {
136 barrier();
137 return mValue;
138 }
139
140 /**
141 * Generic atomic store of a value implemented via a compiler level memory
142 * barrier.
143 *
144 * @param value The new value that the data stored is to change to.
145 */
set(T value)146 inline void set(T value) {
147 mValue = value;
148 barrier();
149 }
150
151 protected:
152 volatile T mValue;
153
154 /**
155 * Forces the compiler to not optimize/re-order memory accesses around the
156 * barrier.
157 */
barrier()158 inline void barrier() const {
159 asm volatile("" ::: "memory");
160 }
161 };
162
163 /**
164 * Base class implementation for the Atomic Bool type.
165 */
166 class AtomicBoolBase : public AtomicBase<bool> {
167 public:
168 /**
169 * Atomically swap the stored boolean with a new value.
170 *
171 * @param desired New value to be assigned to the stored boolean.
172 * @return Previous value of the stored boolean.
173 */
swap(bool desired)174 bool swap(bool desired) {
175 return atomic::swapByte(reinterpret_cast<volatile uint8_t *>(&mValue),
176 desired);
177 }
178 };
179
180 /**
181 * Base class implementation for the Atomic Uint32 type.
182 */
183 class AtomicUint32Base : public AtomicBase<uint32_t> {
184 public:
185 /**
186 * Atomically swap the stored 32-bit word with a new value.
187 *
188 * @param desired New value to be assigned to the stored 32-bit word.
189 * @return Previous value of the stored 32-bit word.
190 */
swap(uint32_t desired)191 uint32_t swap(uint32_t desired) {
192 return atomic::swapWord(&mValue, desired);
193 }
194
195 /**
196 * Atomically add a new value to the stored 32-bit word.
197 *
198 * @param arg Value to be added to the stored word.
199 * @return Pre-addition value of the stored word.
200 */
add(uint32_t arg)201 uint32_t add(uint32_t arg) {
202 return atomic::addToWord(&mValue, arg);
203 }
204
205 /**
206 * Atomically subtract a value from the stored 32-bit word.
207 *
208 * @param arg Value to be subtracted from the stored word.
209 * @return Pre-subtraction value of the stored word.
210 */
sub(uint32_t arg)211 uint32_t sub(uint32_t arg) {
212 return atomic::subFromWord(&mValue, arg);
213 }
214 };
215
216 } // namespace chre
217
218 #endif // CHRE_PLATFORM_ARM_ATOMIC_BASE_H_
219