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