Home | History | Annotate | Download | only in BaseSynchronizationLib
      1 /** @file
      2   Implementation of synchronization functions.
      3 
      4   Copyright (c) 2006 - 2016, 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 **/
     36 UINTN
     37 EFIAPI
     38 GetSpinLockProperties (
     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 **/
     61 SPIN_LOCK *
     62 EFIAPI
     63 InitializeSpinLock (
     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 **/
     92 SPIN_LOCK *
     93 EFIAPI
     94 AcquireSpinLock (
     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     while (!AcquireSpinLockOrFail (SpinLock)) {
    109       CpuPause ();
    110     }
    111   } else if (!AcquireSpinLockOrFail (SpinLock)) {
    112     //
    113     // Get the current timer value
    114     //
    115     Current = GetPerformanceCounter();
    116 
    117     //
    118     // Initialize local variables
    119     //
    120     Start = 0;
    121     End   = 0;
    122     Total = 0;
    123 
    124     //
    125     // Retrieve the performance counter properties and compute the number of performance
    126     // counter ticks required to reach the timeout
    127     //
    128     Timeout = DivU64x32 (
    129                 MultU64x32 (
    130                   GetPerformanceCounterProperties (&Start, &End),
    131                   PcdGet32 (PcdSpinLockTimeout)
    132                   ),
    133                 1000000
    134                 );
    135     Cycle = End - Start;
    136     if (Cycle < 0) {
    137       Cycle = -Cycle;
    138     }
    139     Cycle++;
    140 
    141     while (!AcquireSpinLockOrFail (SpinLock)) {
    142       CpuPause ();
    143       Previous = Current;
    144       Current  = GetPerformanceCounter();
    145       Delta = (INT64) (Current - Previous);
    146       if (Start > End) {
    147         Delta = -Delta;
    148       }
    149       if (Delta < 0) {
    150         Delta += Cycle;
    151       }
    152       Total += Delta;
    153       ASSERT (Total < Timeout);
    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 **/
    176 BOOLEAN
    177 EFIAPI
    178 AcquireSpinLockOrFail (
    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 **/
    212 SPIN_LOCK *
    213 EFIAPI
    214 ReleaseSpinLock (
    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 **/
    244 UINT32
    245 EFIAPI
    246 InterlockedIncrement (
    247   IN      volatile 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 **/
    269 UINT32
    270 EFIAPI
    271 InterlockedDecrement (
    272   IN      volatile 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 **/
    298 UINT16
    299 EFIAPI
    300 InterlockedCompareExchange16 (
    301   IN OUT  volatile 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 **/
    329 UINT32
    330 EFIAPI
    331 InterlockedCompareExchange32 (
    332   IN OUT  volatile 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 **/
    359 UINT64
    360 EFIAPI
    361 InterlockedCompareExchange64 (
    362   IN OUT  volatile 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 **/
    389 VOID *
    390 EFIAPI
    391 InterlockedCompareExchangePointer (
    392   IN OUT  VOID                      * volatile *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                              (volatile UINT32 *)Value,
    405                              (UINT32)(UINTN)CompareValue,
    406                              (UINT32)(UINTN)ExchangeValue
    407                              );
    408     case sizeof (UINT64):
    409       return (VOID*)(UINTN)InterlockedCompareExchange64 (
    410                              (volatile UINT64 *)Value,
    411                              (UINT64)(UINTN)CompareValue,
    412                              (UINT64)(UINTN)ExchangeValue
    413                              );
    414     default:
    415       ASSERT (FALSE);
    416       return NULL;
    417   }
    418 }
    419