]> git.proxmox.com Git - mirror_edk2.git/blobdiff - MdePkg/Library/SecPeiDxeTimerLibCpu/X86TimerLib.c
MdePkg: Implement SCSI commands for Security Protocol In/Out
[mirror_edk2.git] / MdePkg / Library / SecPeiDxeTimerLibCpu / X86TimerLib.c
index 98e86403bdfeda323238d43f578ef18715c44824..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<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/TimerLib.h>\r
 #include <Library/BaseLib.h>\r
 #include <Library/IoLib.h>\r
-#include <Library/DebugLib.h>\r
 #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
+#define APIC_TDCR       0x3e0\r
 \r
 //\r
 // The following array is used in calculating the frequency of local APIC\r
@@ -35,7 +34,10 @@ CONST UINT8                           mTimerLibLocalApicDivisor[] = {
 /**\r
   Internal function to retrieve the base address of local APIC.\r
 \r
-  Internal function to retrieve the base address of local APIC.\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
@@ -46,14 +48,37 @@ 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
   Internal function to return the frequency of the local APIC timer.\r
 \r
-  Internal function to return the frequency of the local APIC timer.\r
-\r
   @param  ApicBase  The base address of memory mapped registers of local APIC.\r
 \r
   @return The frequency of the timer in Hz.\r
@@ -67,14 +92,12 @@ InternalX86GetTimerFrequency (
 {\r
   return\r
     PcdGet32(PcdFSBClock) /\r
-    mTimerLibLocalApicDivisor[MmioBitFieldRead32 (ApicBase + 0x3e0, 0, 3)];\r
+    mTimerLibLocalApicDivisor[MmioBitFieldRead32 (ApicBase + APIC_TDCR, 0, 3)];\r
 }\r
 \r
 /**\r
   Internal function to read the current tick counter of local APIC.\r
 \r
-  Internal function to read the current tick counter of local APIC.\r
-\r
   @param  ApicBase  The base address of memory mapped registers of local APIC.\r
 \r
   @return The tick counter read.\r
@@ -86,7 +109,23 @@ InternalX86GetTimerTick (
   IN      UINTN                     ApicBase\r
   )\r
 {\r
-  return MmioRead32 (ApicBase + 0x390);\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
@@ -95,6 +134,9 @@ InternalX86GetTimerTick (
   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
@@ -107,18 +149,50 @@ InternalX86Delay (
   )\r
 {\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
@@ -128,7 +202,7 @@ InternalX86Delay (
 \r
   @param  MicroSeconds  The minimum number of microseconds to delay.\r
 \r
-  @return MicroSeconds\r
+  @return The value of MicroSeconds inputted.\r
 \r
 **/\r
 UINTN\r
@@ -160,7 +234,7 @@ MicroSecondDelay (
 \r
   @param  NanoSeconds The minimum number of nanoseconds to delay.\r
 \r
-  @return NanoSeconds\r
+  @return The value of NanoSeconds inputted.\r
 \r
 **/\r
 UINTN\r
@@ -188,8 +262,7 @@ NanoSecondDelay (
 /**\r
   Retrieves the current value of a 64-bit free running performance counter.\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
+  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
@@ -241,12 +314,56 @@ GetPerformanceCounterProperties (
   ApicBase = InternalX86GetApicBase ();\r
 \r
   if (StartValue != NULL) {\r
-    *StartValue = MmioRead32 (ApicBase + 0x380);\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