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