]> git.proxmox.com Git - mirror_edk2.git/blobdiff - MdePkg/Library/SecPeiDxeTimerLibCpu/X86TimerLib.c
MdePkg: Apply uncrustify changes
[mirror_edk2.git] / MdePkg / Library / SecPeiDxeTimerLibCpu / X86TimerLib.c
index 2c6a92bf0372a03ca554fbf1f2864277d9effb67..c60589607fde1d5f07fe4199b4d4a39cb1841799 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<BR>\r
-  All rights reserved. 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
 #include <Library/BaseLib.h>\r
 #include <Library/IoLib.h>\r
 #include <Library/PcdLib.h>\r
+#include <Library/DebugLib.h>\r
 \r
-#define APIC_LVTERR     0x370\r
-#define APIC_TMICT      0x380   \r
-#define APIC_TMCCT      0x390\r
-#define APIC_TDCR       0x3e0\r
+#define APIC_SVR     0x0f0\r
+#define APIC_LVTERR  0x370\r
+#define APIC_TMICT   0x380\r
+#define APIC_TMCCT   0x390\r
+#define APIC_TDCR    0x3e0\r
 \r
 //\r
 // The following array is used in calculating the frequency of local APIC\r
 // timer. Refer to IA-32 developers' manual for more details.\r
 //\r
 GLOBAL_REMOVE_IF_UNREFERENCED\r
-CONST UINT8                           mTimerLibLocalApicDivisor[] = {\r
+CONST UINT8  mTimerLibLocalApicDivisor[] = {\r
   0x02, 0x04, 0x08, 0x10,\r
   0x02, 0x04, 0x08, 0x10,\r
   0x20, 0x40, 0x80, 0x01,\r
@@ -38,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
@@ -47,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
@@ -61,11 +87,11 @@ InternalX86GetApicBase (
 UINT32\r
 EFIAPI\r
 InternalX86GetTimerFrequency (\r
-  IN      UINTN                     ApicBase\r
+  IN      UINTN  ApicBase\r
   )\r
 {\r
   return\r
-    PcdGet32(PcdFSBClock) /\r
+    PcdGet32 (PcdFSBClock) /\r
     mTimerLibLocalApicDivisor[MmioBitFieldRead32 (ApicBase + APIC_TDCR, 0, 3)];\r
 }\r
 \r
@@ -80,18 +106,37 @@ InternalX86GetTimerFrequency (
 INT32\r
 EFIAPI\r
 InternalX86GetTimerTick (\r
-  IN      UINTN                     ApicBase\r
+  IN      UINTN  ApicBase\r
   )\r
 {\r
   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
@@ -99,23 +144,55 @@ InternalX86GetTimerTick (
 VOID\r
 EFIAPI\r
 InternalX86Delay (\r
-  IN      UINTN                     ApicBase,\r
-  IN      UINT32                    Delay\r
+  IN      UINTN   ApicBase,\r
+  IN      UINT32  Delay\r
   )\r
 {\r
-  INT32                             Ticks;\r
+  INT32   Ticks;\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
-  while (InternalX86GetTimerTick (ApicBase) - Ticks >= 0);\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
@@ -131,10 +208,10 @@ InternalX86Delay (
 UINTN\r
 EFIAPI\r
 MicroSecondDelay (\r
-  IN      UINTN                     MicroSeconds\r
+  IN      UINTN  MicroSeconds\r
   )\r
 {\r
-  UINTN                             ApicBase;\r
+  UINTN  ApicBase;\r
 \r
   ApicBase = InternalX86GetApicBase ();\r
   InternalX86Delay (\r
@@ -163,10 +240,10 @@ MicroSecondDelay (
 UINTN\r
 EFIAPI\r
 NanoSecondDelay (\r
-  IN      UINTN                     NanoSeconds\r
+  IN      UINTN  NanoSeconds\r
   )\r
 {\r
-  UINTN                             ApicBase;\r
+  UINTN  ApicBase;\r
 \r
   ApicBase = InternalX86GetApicBase ();\r
   InternalX86Delay (\r
@@ -183,8 +260,9 @@ NanoSecondDelay (
 }\r
 \r
 /**\r
-  Retrieves the current value of a 64-bit free running performance counter. The\r
-  counter can either count up by 1 or count down by 1. If the physical\r
+  Retrieves the current value of a 64-bit free running performance counter.\r
+\r
+  The counter can either count up by 1 or count down by 1. If the physical\r
   performance counter counts by a larger increment, then the counter values\r
   must be translated. The properties of the counter can be retrieved from\r
   GetPerformanceCounterProperties().\r
@@ -227,21 +305,65 @@ GetPerformanceCounter (
 UINT64\r
 EFIAPI\r
 GetPerformanceCounterProperties (\r
-  OUT      UINT64                    *StartValue,  OPTIONAL\r
-  OUT      UINT64                    *EndValue     OPTIONAL\r
+  OUT      UINT64  *StartValue   OPTIONAL,\r
+  OUT      UINT64  *EndValue     OPTIONAL\r
   )\r
 {\r
-  UINTN                             ApicBase;\r
+  UINTN  ApicBase;\r
 \r
   ApicBase = InternalX86GetApicBase ();\r
 \r
   if (StartValue != NULL) {\r
-    *StartValue = MmioRead32 (ApicBase + APIC_TMICT);\r
+    *StartValue = (UINT64)InternalX86GetInitTimerCount (ApicBase);\r
   }\r
 \r
   if (EndValue != NULL) {\r
     *EndValue = 0;\r
   }\r
 \r
-  return (UINT64) InternalX86GetTimerFrequency (ApicBase);\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