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