/** @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
#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
/**\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
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
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
)\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
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
\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