Home | History | Annotate | Download | only in BaseSynchronizationLib
      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
     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
     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
    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
    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
    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
    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
    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
    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
    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
    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
    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