]> git.proxmox.com Git - mirror_edk2.git/blobdiff - MdePkg/Library/SecPeiDxeTimerLibCpu/X86TimerLib.c
MdePkg: Replace BSD License with BSD+Patent License
[mirror_edk2.git] / MdePkg / Library / SecPeiDxeTimerLibCpu / X86TimerLib.c
index 01702f3a90f6097f57f9e9c7c787f64067c12aa4..50911fc55eb627671d91ab8e096e32e77cf2f0a0 100644 (file)
@@ -1,14 +1,8 @@
 /** @file\r
   Timer Library functions built upon local APIC on IA32/x64.\r
 \r
-  Copyright (c) 2006 - 2008, Intel Corporation. All rights reserved.<BR>\r
-  This program and the accompanying materials\r
-  are licensed and made available under the terms and conditions of the BSD License\r
-  which accompanies this distribution.  The full text of the license may be found at\r
-  http://opensource.org/licenses/bsd-license.php\r
-\r
-  THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,\r
-  WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.\r
+  Copyright (c) 2006 - 2015, Intel Corporation. All rights reserved.<BR>\r
+  SPDX-License-Identifier: BSD-2-Clause-Patent\r
 \r
 **/\r
 \r
@@ -19,6 +13,7 @@
 #include <Library/PcdLib.h>\r
 #include <Library/DebugLib.h>\r
 \r
+#define APIC_SVR        0x0f0\r
 #define APIC_LVTERR     0x370\r
 #define APIC_TMICT      0x380\r
 #define APIC_TMCCT      0x390\r
@@ -39,6 +34,11 @@ CONST UINT8                           mTimerLibLocalApicDivisor[] = {
 /**\r
   Internal function to retrieve the base address of local APIC.\r
 \r
+  This function will ASSERT if:\r
+  The local APIC is not globally enabled.\r
+  The local APIC is not working under XAPIC mode.\r
+  The local APIC is not software enabled.\r
+\r
   @return The base address of local APIC\r
 \r
 **/\r
@@ -48,7 +48,32 @@ InternalX86GetApicBase (
   VOID\r
   )\r
 {\r
-  return (UINTN)AsmMsrBitFieldRead64 (27, 12, 35) << 12;\r
+  UINTN                             MsrValue;\r
+  UINTN                             ApicBase;\r
+\r
+  MsrValue = (UINTN) AsmReadMsr64 (27);\r
+  ApicBase = MsrValue & 0xffffff000ULL;\r
+\r
+  //\r
+  // Check the APIC Global Enable bit (bit 11) in IA32_APIC_BASE MSR.\r
+  // This bit will be 1, if local APIC is globally enabled.\r
+  //\r
+  ASSERT ((MsrValue & BIT11) != 0);\r
+\r
+  //\r
+  // Check the APIC Extended Mode bit (bit 10) in IA32_APIC_BASE MSR.\r
+  // This bit will be 0, if local APIC is under XAPIC mode.\r
+  //\r
+  ASSERT ((MsrValue & BIT10) == 0);\r
+\r
+  //\r
+  // Check the APIC Software Enable/Disable bit (bit 8) in Spurious-Interrupt\r
+  // Vector Register.\r
+  // This bit will be 1, if local APIC is software enabled.\r
+  //\r
+  ASSERT ((MmioRead32 (ApicBase + APIC_SVR) & BIT8) != 0);\r
+\r
+  return ApicBase;\r
 }\r
 \r
 /**\r
@@ -87,12 +112,31 @@ InternalX86GetTimerTick (
   return MmioRead32 (ApicBase + APIC_TMCCT);\r
 }\r
 \r
+/**\r
+  Internal function to read the initial timer count of local APIC.\r
+\r
+  @param  ApicBase  The base address of memory mapped registers of local APIC.\r
+\r
+  @return The initial timer count read.\r
+\r
+**/\r
+UINT32\r
+InternalX86GetInitTimerCount (\r
+  IN      UINTN                     ApicBase\r
+  )\r
+{\r
+  return MmioRead32 (ApicBase + APIC_TMICT);\r
+}\r
+\r
 /**\r
   Stalls the CPU for at least the given number of ticks.\r
 \r
   Stalls the CPU for at least the given number of ticks. It's invoked by\r
   MicroSecondDelay() and NanoSecondDelay().\r
 \r
+  This function will ASSERT if the APIC timer intial count returned from\r
+  InternalX86GetInitTimerCount() is zero.\r
+\r
   @param  ApicBase  The base address of memory mapped registers of local APIC.\r
   @param  Delay     A period of time to delay in ticks.\r
 \r
@@ -105,22 +149,50 @@ InternalX86Delay (
   )\r
 {\r
   INT32                             Ticks;\r
-  UINT32                            PowerOfTwoCounter;\r
+  UINT32                            Times;\r
+  UINT32                            InitCount;\r
+  UINT32                            StartTick;\r
 \r
   //\r
-  // The target timer count is calculated here\r
+  // In case Delay is too larger, separate it into several small delay slot.\r
+  // Devided Delay by half value of Init Count is to avoid Delay close to\r
+  // the Init Count, timeout maybe missing if the time consuming between 2\r
+  // GetApicTimerCurrentCount() invoking is larger than the time gap between\r
+  // Delay and the Init Count.\r
   //\r
-  Ticks = InternalX86GetTimerTick (ApicBase) - Delay;\r
+  InitCount = InternalX86GetInitTimerCount (ApicBase);\r
+  ASSERT (InitCount != 0);\r
+  Times     = Delay / (InitCount / 2);\r
+  Delay     = Delay % (InitCount / 2);\r
 \r
   //\r
-  // Wait until time out\r
-  // Delay > 2^31 could not be handled by this function\r
-  // Timer wrap-arounds are handled correctly by this function\r
+  // Get Start Tick and do delay\r
   //\r
-  PowerOfTwoCounter = GetPowerOfTwo32 (MmioRead32 (ApicBase + APIC_TMICT));\r
-  while (((UINT32)(InternalX86GetTimerTick (ApicBase) - Ticks) & PowerOfTwoCounter) == 0) {\r
-    CpuPause ();\r
-  }\r
+  StartTick  = InternalX86GetTimerTick (ApicBase);\r
+  do {\r
+    //\r
+    // Wait until time out by Delay value\r
+    //\r
+    do {\r
+      CpuPause ();\r
+      //\r
+      // Get Ticks from Start to Current.\r
+      //\r
+      Ticks = StartTick - InternalX86GetTimerTick (ApicBase);\r
+      //\r
+      // Ticks < 0 means Timer wrap-arounds happens.\r
+      //\r
+      if (Ticks < 0) {\r
+        Ticks += InitCount;\r
+      }\r
+    } while ((UINT32)Ticks < Delay);\r
+\r
+    //\r
+    // Update StartTick and Delay for next delay slot\r
+    //\r
+    StartTick -= (StartTick > Delay) ?  Delay : (Delay - InitCount);\r
+    Delay      = InitCount / 2;\r
+  } while (Times-- > 0);\r
 }\r
 \r
 /**\r
@@ -242,11 +314,7 @@ GetPerformanceCounterProperties (
   ApicBase = InternalX86GetApicBase ();\r
 \r
   if (StartValue != NULL) {\r
-    *StartValue = MmioRead32 (ApicBase + APIC_TMICT);\r
-    //\r
-    // make sure StartValue is all 1s from High Bit\r
-    //\r
-    ASSERT ((*StartValue & (*StartValue + 1)) == 0);\r
+    *StartValue = (UINT64)InternalX86GetInitTimerCount (ApicBase);\r
   }\r
 \r
   if (EndValue != NULL) {\r
@@ -255,3 +323,47 @@ GetPerformanceCounterProperties (
 \r
   return (UINT64) InternalX86GetTimerFrequency (ApicBase);\r
 }\r
+\r
+/**\r
+  Converts elapsed ticks of performance counter to time in nanoseconds.\r
+\r
+  This function converts the elapsed ticks of running performance counter to\r
+  time value in unit of nanoseconds.\r
+\r
+  @param  Ticks     The number of elapsed ticks of running performance counter.\r
+\r
+  @return The elapsed time in nanoseconds.\r
+\r
+**/\r
+UINT64\r
+EFIAPI\r
+GetTimeInNanoSecond (\r
+  IN      UINT64                     Ticks\r
+  )\r
+{\r
+  UINT64  Frequency;\r
+  UINT64  NanoSeconds;\r
+  UINT64  Remainder;\r
+  INTN    Shift;\r
+\r
+  Frequency = GetPerformanceCounterProperties (NULL, NULL);\r
+\r
+  //\r
+  //          Ticks\r
+  // Time = --------- x 1,000,000,000\r
+  //        Frequency\r
+  //\r
+  NanoSeconds = MultU64x32 (DivU64x64Remainder (Ticks, Frequency, &Remainder), 1000000000u);\r
+\r
+  //\r
+  // Ensure (Remainder * 1,000,000,000) will not overflow 64-bit.\r
+  // Since 2^29 < 1,000,000,000 = 0x3B9ACA00 < 2^30, Remainder should < 2^(64-30) = 2^34,\r
+  // i.e. highest bit set in Remainder should <= 33.\r
+  //\r
+  Shift = MAX (0, HighBitSet64 (Remainder) - 33);\r
+  Remainder = RShiftU64 (Remainder, (UINTN) Shift);\r
+  Frequency = RShiftU64 (Frequency, (UINTN) Shift);\r
+  NanoSeconds += DivU64x64Remainder (MultU64x32 (Remainder, 1000000000u), Frequency, NULL);\r
+\r
+  return NanoSeconds;\r
+}\r