1 /** @file
2   Implementation of synchronization functions.
3 
4   Copyright (c) 2006 - 2018, Intel Corporation. All rights reserved.<BR>
5   Portions copyright (c) 2008 - 2009, Apple Inc. All rights reserved.<BR>
6   SPDX-License-Identifier: BSD-2-Clause-Patent
7 
8 **/
9 
10 #include "BaseSynchronizationLibInternals.h"
11 
12 //
13 // GCC inline assembly for Read Write Barrier
14 //
15 #define _ReadWriteBarrier() do { __asm__ __volatile__ ("": : : "memory"); } while(0)
16 
17 #define SPIN_LOCK_RELEASED          ((UINTN) 1)
18 #define SPIN_LOCK_ACQUIRED          ((UINTN) 2)
19 
20 /**
21   Retrieves the architecture specific spin lock alignment requirements for
22   optimal spin lock performance.
23 
24   This function retrieves the spin lock alignment requirements for optimal
25   performance on a given CPU architecture. The spin lock alignment is byte alignment.
26   It must be a power of two and is returned by this function. If there are no alignment
27   requirements, then 1 must be returned. The spin lock synchronization
28   functions must function correctly if the spin lock size and alignment values
29   returned by this function are not used at all. These values are hints to the
30   consumers of the spin lock synchronization functions to obtain optimal spin
31   lock performance.
32 
33   @return The architecture specific spin lock alignment.
34 
35 **/
36 UINTN
37 EFIAPI
GetSpinLockProperties(VOID)38 GetSpinLockProperties (
39   VOID
40   )
41 {
42   return InternalGetSpinLockProperties ();
43 }
44 
45 /**
46   Initializes a spin lock to the released state and returns the spin lock.
47 
48   This function initializes the spin lock specified by SpinLock to the released
49   state, and returns SpinLock. Optimal performance can be achieved by calling
50   GetSpinLockProperties() to determine the size and alignment requirements for
51   SpinLock.
52 
53   If SpinLock is NULL, then ASSERT().
54 
55   @param  SpinLock  A pointer to the spin lock to initialize to the released
56                     state.
57 
58   @return SpinLock is in release state.
59 
60 **/
61 SPIN_LOCK *
62 EFIAPI
InitializeSpinLock(OUT SPIN_LOCK * SpinLock)63 InitializeSpinLock (
64   OUT      SPIN_LOCK                 *SpinLock
65   )
66 {
67   ASSERT (SpinLock != NULL);
68 
69   _ReadWriteBarrier();
70   *SpinLock = SPIN_LOCK_RELEASED;
71   _ReadWriteBarrier();
72 
73   return SpinLock;
74 }
75 
76 /**
77   Waits until a spin lock can be placed in the acquired state.
78 
79   This function checks the state of the spin lock specified by SpinLock. If
80   SpinLock is in the released state, then this function places SpinLock in the
81   acquired state and returns SpinLock. Otherwise, this function waits
82   indefinitely for the spin lock to be released, and then places it in the
83   acquired state and returns SpinLock. All state transitions of SpinLock must
84   be performed using MP safe mechanisms.
85 
86   If SpinLock is NULL, then ASSERT().
87   If SpinLock was not initialized with InitializeSpinLock(), then ASSERT().
88   If PcdSpinLockTimeout is not zero, and SpinLock is can not be acquired in
89   PcdSpinLockTimeout microseconds, then ASSERT().
90 
91   @param  SpinLock  A pointer to the spin lock to place in the acquired state.
92 
93   @return SpinLock acquired the lock.
94 
95 **/
96 SPIN_LOCK *
97 EFIAPI
AcquireSpinLock(IN OUT SPIN_LOCK * SpinLock)98 AcquireSpinLock (
99   IN OUT  SPIN_LOCK                 *SpinLock
100   )
101 {
102   UINT64  Current;
103   UINT64  Previous;
104   UINT64  Total;
105   UINT64  Start;
106   UINT64  End;
107   UINT64  Timeout;
108   INT64   Cycle;
109   INT64   Delta;
110 
111   if (PcdGet32 (PcdSpinLockTimeout) == 0) {
112     while (!AcquireSpinLockOrFail (SpinLock)) {
113       CpuPause ();
114     }
115   } else if (!AcquireSpinLockOrFail (SpinLock)) {
116     //
117     // Get the current timer value
118     //
119     Current = GetPerformanceCounter();
120 
121     //
122     // Initialize local variables
123     //
124     Start = 0;
125     End   = 0;
126     Total = 0;
127 
128     //
129     // Retrieve the performance counter properties and compute the number of performance
130     // counter ticks required to reach the timeout
131     //
132     Timeout = DivU64x32 (
133                 MultU64x32 (
134                   GetPerformanceCounterProperties (&Start, &End),
135                   PcdGet32 (PcdSpinLockTimeout)
136                   ),
137                 1000000
138                 );
139     Cycle = End - Start;
140     if (Cycle < 0) {
141       Cycle = -Cycle;
142     }
143     Cycle++;
144 
145     while (!AcquireSpinLockOrFail (SpinLock)) {
146       CpuPause ();
147       Previous = Current;
148       Current  = GetPerformanceCounter();
149       Delta = (INT64) (Current - Previous);
150       if (Start > End) {
151         Delta = -Delta;
152       }
153       if (Delta < 0) {
154         Delta += Cycle;
155       }
156       Total += Delta;
157       ASSERT (Total < Timeout);
158     }
159   }
160   return SpinLock;
161 }
162 
163 /**
164   Attempts to place a spin lock in the acquired state.
165 
166   This function checks the state of the spin lock specified by SpinLock. If
167   SpinLock is in the released state, then this function places SpinLock in the
168   acquired state and returns TRUE. Otherwise, FALSE is returned. All state
169   transitions of SpinLock must be performed using MP safe mechanisms.
170 
171   If SpinLock is NULL, then ASSERT().
172   If SpinLock was not initialized with InitializeSpinLock(), then ASSERT().
173 
174   @param  SpinLock  A pointer to the spin lock to place in the acquired state.
175 
176   @retval TRUE  SpinLock was placed in the acquired state.
177   @retval FALSE SpinLock could not be acquired.
178 
179 **/
180 BOOLEAN
181 EFIAPI
AcquireSpinLockOrFail(IN OUT SPIN_LOCK * SpinLock)182 AcquireSpinLockOrFail (
183   IN OUT  SPIN_LOCK                 *SpinLock
184   )
185 {
186   SPIN_LOCK   LockValue;
187   VOID        *Result;
188 
189   ASSERT (SpinLock != NULL);
190 
191   LockValue = *SpinLock;
192   ASSERT (LockValue == SPIN_LOCK_ACQUIRED || LockValue == SPIN_LOCK_RELEASED);
193 
194   _ReadWriteBarrier ();
195   Result = InterlockedCompareExchangePointer (
196              (VOID**)SpinLock,
197              (VOID*)SPIN_LOCK_RELEASED,
198              (VOID*)SPIN_LOCK_ACQUIRED
199            );
200 
201   _ReadWriteBarrier ();
202   return (BOOLEAN) (Result == (VOID*) SPIN_LOCK_RELEASED);
203 }
204 
205 /**
206   Releases a spin lock.
207 
208   This function places the spin lock specified by SpinLock in the release state
209   and returns SpinLock.
210 
211   If SpinLock is NULL, then ASSERT().
212   If SpinLock was not initialized with InitializeSpinLock(), then ASSERT().
213 
214   @param  SpinLock  A pointer to the spin lock to release.
215 
216   @return SpinLock released the lock.
217 
218 **/
219 SPIN_LOCK *
220 EFIAPI
ReleaseSpinLock(IN OUT SPIN_LOCK * SpinLock)221 ReleaseSpinLock (
222   IN OUT  SPIN_LOCK                 *SpinLock
223   )
224 {
225   SPIN_LOCK    LockValue;
226 
227   ASSERT (SpinLock != NULL);
228 
229   LockValue = *SpinLock;
230   ASSERT (LockValue == SPIN_LOCK_ACQUIRED || LockValue == SPIN_LOCK_RELEASED);
231 
232   _ReadWriteBarrier ();
233   *SpinLock = SPIN_LOCK_RELEASED;
234   _ReadWriteBarrier ();
235 
236   return SpinLock;
237 }
238 
239 /**
240   Performs an atomic increment of an 32-bit unsigned integer.
241 
242   Performs an atomic increment of the 32-bit unsigned integer specified by
243   Value and returns the incremented value. The increment operation must be
244   performed using MP safe mechanisms.
245 
246   If Value is NULL, then ASSERT().
247 
248   @param  Value A pointer to the 32-bit value to increment.
249 
250   @return The incremented value.
251 
252 **/
253 UINT32
254 EFIAPI
InterlockedIncrement(IN volatile UINT32 * Value)255 InterlockedIncrement (
256   IN      volatile UINT32           *Value
257   )
258 {
259   ASSERT (Value != NULL);
260   return InternalSyncIncrement (Value);
261 }
262 
263 /**
264   Performs an atomic decrement of an 32-bit unsigned integer.
265 
266   Performs an atomic decrement of the 32-bit unsigned integer specified by
267   Value and returns the decremented value. The decrement operation must be
268   performed using MP safe mechanisms.
269 
270   If Value is NULL, then ASSERT().
271 
272   @param  Value A pointer to the 32-bit value to decrement.
273 
274   @return The decremented value.
275 
276 **/
277 UINT32
278 EFIAPI
InterlockedDecrement(IN volatile UINT32 * Value)279 InterlockedDecrement (
280   IN      volatile UINT32           *Value
281   )
282 {
283   ASSERT (Value != NULL);
284   return InternalSyncDecrement (Value);
285 }
286 
287 /**
288   Performs an atomic compare exchange operation on a 16-bit unsigned integer.
289 
290   Performs an atomic compare exchange operation on the 16-bit unsigned integer
291   specified by Value.  If Value is equal to CompareValue, then Value is set to
292   ExchangeValue and CompareValue is returned.  If Value is not equal to CompareValue,
293   then Value is returned.  The compare exchange operation must be performed using
294   MP safe mechanisms.
295 
296   If Value is NULL, then ASSERT().
297 
298   @param  Value         A pointer to the 16-bit value for the compare exchange
299                         operation.
300   @param  CompareValue  A 16-bit value used in compare operation.
301   @param  ExchangeValue A 16-bit value used in exchange operation.
302 
303   @return The original *Value before exchange.
304 
305 **/
306 UINT16
307 EFIAPI
InterlockedCompareExchange16(IN OUT volatile UINT16 * Value,IN UINT16 CompareValue,IN UINT16 ExchangeValue)308 InterlockedCompareExchange16 (
309   IN OUT  volatile UINT16           *Value,
310   IN      UINT16                    CompareValue,
311   IN      UINT16                    ExchangeValue
312   )
313 {
314   ASSERT (Value != NULL);
315   return InternalSyncCompareExchange16 (Value, CompareValue, ExchangeValue);
316 }
317 
318 /**
319   Performs an atomic compare exchange operation on a 32-bit unsigned integer.
320 
321   Performs an atomic compare exchange operation on the 32-bit unsigned integer
322   specified by Value.  If Value is equal to CompareValue, then Value is set to
323   ExchangeValue and CompareValue is returned.  If Value is not equal to CompareValue,
324   then Value is returned.  The compare exchange operation must be performed using
325   MP safe mechanisms.
326 
327   If Value is NULL, then ASSERT().
328 
329   @param  Value         A pointer to the 32-bit value for the compare exchange
330                         operation.
331   @param  CompareValue  A 32-bit value used in compare operation.
332   @param  ExchangeValue A 32-bit value used in exchange operation.
333 
334   @return The original *Value before exchange.
335 
336 **/
337 UINT32
338 EFIAPI
InterlockedCompareExchange32(IN OUT volatile UINT32 * Value,IN UINT32 CompareValue,IN UINT32 ExchangeValue)339 InterlockedCompareExchange32 (
340   IN OUT  volatile UINT32           *Value,
341   IN      UINT32                    CompareValue,
342   IN      UINT32                    ExchangeValue
343   )
344 {
345   ASSERT (Value != NULL);
346   return InternalSyncCompareExchange32 (Value, CompareValue, ExchangeValue);
347 }
348 
349 /**
350   Performs an atomic compare exchange operation on a 64-bit unsigned integer.
351 
352   Performs an atomic compare exchange operation on the 64-bit unsigned integer specified
353   by Value.  If Value is equal to CompareValue, then Value is set to ExchangeValue and
354   CompareValue is returned.  If Value is not equal to CompareValue, then Value is returned.
355   The compare exchange operation must be performed using MP safe mechanisms.
356 
357   If Value is NULL, then ASSERT().
358 
359   @param  Value         A pointer to the 64-bit value for the compare exchange
360                         operation.
361   @param  CompareValue  A 64-bit value used in a compare operation.
362   @param  ExchangeValue A 64-bit value used in an exchange operation.
363 
364   @return The original *Value before exchange.
365 
366 **/
367 UINT64
368 EFIAPI
InterlockedCompareExchange64(IN OUT volatile UINT64 * Value,IN UINT64 CompareValue,IN UINT64 ExchangeValue)369 InterlockedCompareExchange64 (
370   IN OUT  volatile UINT64           *Value,
371   IN      UINT64                    CompareValue,
372   IN      UINT64                    ExchangeValue
373   )
374 {
375   ASSERT (Value != NULL);
376   return InternalSyncCompareExchange64 (Value, CompareValue, ExchangeValue);
377 }
378 
379 /**
380   Performs an atomic compare exchange operation on a pointer value.
381 
382   Performs an atomic compare exchange operation on the pointer value specified
383   by Value. If Value is equal to CompareValue, then Value is set to
384   ExchangeValue and CompareValue is returned. If Value is not equal to
385   CompareValue, then Value is returned. The compare exchange operation must be
386   performed using MP safe mechanisms.
387 
388   If Value is NULL, then ASSERT().
389 
390   @param  Value         A pointer to the pointer value for the compare exchange
391                         operation.
392   @param  CompareValue  A pointer value used in a compare operation.
393   @param  ExchangeValue A pointer value used in an exchange operation.
394 
395   @return The original *Value before exchange.
396 **/
397 VOID *
398 EFIAPI
InterlockedCompareExchangePointer(IN OUT VOID * volatile * Value,IN VOID * CompareValue,IN VOID * ExchangeValue)399 InterlockedCompareExchangePointer (
400   IN OUT  VOID                      * volatile *Value,
401   IN      VOID                      *CompareValue,
402   IN      VOID                      *ExchangeValue
403   )
404 {
405   UINT8  SizeOfValue;
406 
407   SizeOfValue = sizeof (*Value);
408 
409   switch (SizeOfValue) {
410     case sizeof (UINT32):
411       return (VOID*)(UINTN)InterlockedCompareExchange32 (
412                              (volatile UINT32 *)Value,
413                              (UINT32)(UINTN)CompareValue,
414                              (UINT32)(UINTN)ExchangeValue
415                              );
416     case sizeof (UINT64):
417       return (VOID*)(UINTN)InterlockedCompareExchange64 (
418                              (volatile UINT64 *)Value,
419                              (UINT64)(UINTN)CompareValue,
420                              (UINT64)(UINTN)ExchangeValue
421                              );
422     default:
423       ASSERT (FALSE);
424       return NULL;
425   }
426 }
427