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