]> git.proxmox.com Git - mirror_edk2.git/commitdiff
OvmfPkg/XenPlatformPei: Calibrate APIC timer frequency
authorAnthony PERARD <anthony.perard@citrix.com>
Mon, 12 Apr 2021 13:30:02 +0000 (14:30 +0100)
committermergify[bot] <37929162+mergify[bot]@users.noreply.github.com>
Tue, 13 Apr 2021 11:54:58 +0000 (11:54 +0000)
Calculate the frequency of the APIC timer that Xen provides.

Even though the frequency is currently hard-coded, it isn't part of
the public ABI that Xen provides and thus may change at any time. OVMF
needs to determine the frequency by an other mean.

Fortunately, Xen provides a way to determines the frequency of the
TSC, so we can use TSC to calibrate the frequency of the APIC timer.
That information is found in the shared_info page which we map and
unmap once done (XenBusDxe is going to map the page somewhere else).

The shared_info page is mapped at the highest physical address allowed
as it doesn't need to be in the RAM, thus there's a call to update the
page table.

The calculated frequency is only logged in this patch, it will be used
in a following patch.

Ref: https://bugzilla.tianocore.org/show_bug.cgi?id=2490
Signed-off-by: Anthony PERARD <anthony.perard@citrix.com>
Acked-by: Laszlo Ersek <lersek@redhat.com>
Message-Id: <20210412133003.146438-7-anthony.perard@citrix.com>

OvmfPkg/XenPlatformPei/Platform.c
OvmfPkg/XenPlatformPei/Platform.h
OvmfPkg/XenPlatformPei/Xen.c
OvmfPkg/XenPlatformPei/XenPlatformPei.inf

index 717fd0ab1a455168b9b1d82938fc245e3ad66117..e9511eb40c62cc9161f9ea6f71413df2d46b7a5e 100644 (file)
@@ -448,6 +448,7 @@ InitializeXenPlatform (
   InitializeRamRegions ();\r
 \r
   InitializeXen ();\r
+  CalibrateLapicTimer ();\r
 \r
   if (mBootMode != BOOT_ON_S3_RESUME) {\r
     ReserveEmuVariableNvStore ();\r
index e70ca58078ebe82344fe79d31263c709f90f9845..77d88fc159d7f0824f09b169838853b4ac529c7b 100644 (file)
@@ -132,6 +132,11 @@ PhysicalAddressIdentityMapping (
   IN EFI_PHYSICAL_ADDRESS AddressToMap\r
   );\r
 \r
+VOID\r
+CalibrateLapicTimer (\r
+  VOID\r
+  );\r
+\r
 extern EFI_BOOT_MODE mBootMode;\r
 \r
 extern UINT8 mPhysMemAddressWidth;\r
index b2a7d1c21dacc305a036808515acbd1d79849e6f..8b06bebd7731800bdda1ce0248758fdccb0a0940 100644 (file)
 #include <Library/CpuLib.h>\r
 #include <Library/DebugLib.h>\r
 #include <Library/HobLib.h>\r
+#include <Library/LocalApicLib.h>\r
 #include <Library/MemoryAllocationLib.h>\r
 #include <Library/PcdLib.h>\r
+#include <Library/SafeIntLib.h>\r
 #include <Guid/XenInfo.h>\r
 #include <IndustryStandard/E820.h>\r
 #include <Library/ResourcePublicationLib.h>\r
@@ -457,3 +459,178 @@ PhysicalAddressIdentityMapping (
 \r
   return EFI_SUCCESS;\r
 }\r
+\r
+STATIC\r
+EFI_STATUS\r
+MapSharedInfoPage (\r
+  IN VOID *PagePtr\r
+  )\r
+{\r
+  xen_add_to_physmap_t  Parameters;\r
+  INTN                  ReturnCode;\r
+\r
+  Parameters.domid = DOMID_SELF;\r
+  Parameters.space = XENMAPSPACE_shared_info;\r
+  Parameters.idx = 0;\r
+  Parameters.gpfn = (UINTN)PagePtr >> EFI_PAGE_SHIFT;\r
+  ReturnCode = XenHypercallMemoryOp (XENMEM_add_to_physmap, &Parameters);\r
+  if (ReturnCode != 0) {\r
+    return EFI_NO_MAPPING;\r
+  }\r
+  return EFI_SUCCESS;\r
+}\r
+\r
+STATIC\r
+VOID\r
+UnmapXenPage (\r
+  IN VOID *PagePtr\r
+  )\r
+{\r
+  xen_remove_from_physmap_t Parameters;\r
+  INTN                      ReturnCode;\r
+\r
+  Parameters.domid = DOMID_SELF;\r
+  Parameters.gpfn = (UINTN)PagePtr >> EFI_PAGE_SHIFT;\r
+  ReturnCode = XenHypercallMemoryOp (XENMEM_remove_from_physmap, &Parameters);\r
+  ASSERT (ReturnCode == 0);\r
+}\r
+\r
+\r
+STATIC\r
+UINT64\r
+GetCpuFreq (\r
+  IN XEN_VCPU_TIME_INFO *VcpuTime\r
+  )\r
+{\r
+  UINT32 Version;\r
+  UINT32 TscToSystemMultiplier;\r
+  INT8   TscShift;\r
+  UINT64 CpuFreq;\r
+\r
+  do {\r
+    Version = VcpuTime->Version;\r
+    MemoryFence ();\r
+    TscToSystemMultiplier = VcpuTime->TscToSystemMultiplier;\r
+    TscShift = VcpuTime->TscShift;\r
+    MemoryFence ();\r
+  } while (((Version & 1) != 0) && (Version != VcpuTime->Version));\r
+\r
+  CpuFreq = DivU64x32 (LShiftU64 (1000000000ULL, 32), TscToSystemMultiplier);\r
+  if (TscShift >= 0) {\r
+      CpuFreq = RShiftU64 (CpuFreq, TscShift);\r
+  } else {\r
+      CpuFreq = LShiftU64 (CpuFreq, -TscShift);\r
+  }\r
+  return CpuFreq;\r
+}\r
+\r
+STATIC\r
+VOID\r
+XenDelay (\r
+  IN XEN_VCPU_TIME_INFO *VcpuTimeInfo,\r
+  IN UINT64             DelayNs\r
+  )\r
+{\r
+  UINT64        Tick;\r
+  UINT64        CpuFreq;\r
+  UINT64        Delay;\r
+  UINT64        DelayTick;\r
+  UINT64        NewTick;\r
+  RETURN_STATUS Status;\r
+\r
+  Tick = AsmReadTsc ();\r
+\r
+  CpuFreq = GetCpuFreq (VcpuTimeInfo);\r
+  Status = SafeUint64Mult (DelayNs, CpuFreq, &Delay);\r
+  if (EFI_ERROR (Status)) {\r
+    DEBUG ((DEBUG_ERROR,\r
+      "XenDelay (%lu ns): delay too big in relation to CPU freq %lu Hz\n",\r
+      DelayNs, CpuFreq));\r
+    ASSERT_EFI_ERROR (Status);\r
+    CpuDeadLoop ();\r
+  }\r
+\r
+  DelayTick = DivU64x32 (Delay, 1000000000);\r
+\r
+  NewTick = Tick + DelayTick;\r
+\r
+  //\r
+  // Check for overflow\r
+  //\r
+  if (NewTick < Tick) {\r
+    //\r
+    // Overflow, wait for TSC to also overflow\r
+    //\r
+    while (AsmReadTsc () >= Tick) {\r
+      CpuPause ();\r
+    }\r
+  }\r
+\r
+  while (AsmReadTsc () <= NewTick) {\r
+    CpuPause ();\r
+  }\r
+}\r
+\r
+\r
+/**\r
+  Calculate the frequency of the Local Apic Timer\r
+**/\r
+VOID\r
+CalibrateLapicTimer (\r
+  VOID\r
+  )\r
+{\r
+  XEN_SHARED_INFO       *SharedInfo;\r
+  XEN_VCPU_TIME_INFO    *VcpuTimeInfo;\r
+  UINT32                TimerTick, TimerTick2, DiffTimer;\r
+  UINT64                TscTick, TscTick2;\r
+  UINT64                Freq;\r
+  UINT64                Dividend;\r
+  EFI_STATUS            Status;\r
+\r
+\r
+  SharedInfo = (VOID*)((1ULL << mPhysMemAddressWidth) - EFI_PAGE_SIZE);\r
+  Status = PhysicalAddressIdentityMapping ((EFI_PHYSICAL_ADDRESS)SharedInfo);\r
+  if (EFI_ERROR (Status)) {\r
+    DEBUG ((DEBUG_ERROR,\r
+      "Failed to add page table entry for Xen shared info page: %r\n",\r
+      Status));\r
+    ASSERT_EFI_ERROR (Status);\r
+    return;\r
+  }\r
+\r
+  Status = MapSharedInfoPage (SharedInfo);\r
+  if (EFI_ERROR (Status)) {\r
+    DEBUG ((DEBUG_ERROR, "Failed to map Xen's shared info page: %r\n",\r
+      Status));\r
+    ASSERT_EFI_ERROR (Status);\r
+    return;\r
+  }\r
+\r
+  VcpuTimeInfo = &SharedInfo->VcpuInfo[0].Time;\r
+\r
+  InitializeApicTimer (1, MAX_UINT32, TRUE, 0);\r
+  DisableApicTimerInterrupt ();\r
+\r
+  TimerTick = GetApicTimerCurrentCount ();\r
+  TscTick = AsmReadTsc ();\r
+  XenDelay (VcpuTimeInfo, 1000000ULL);\r
+  TimerTick2 = GetApicTimerCurrentCount ();\r
+  TscTick2 = AsmReadTsc ();\r
+\r
+\r
+  DiffTimer = TimerTick - TimerTick2;\r
+  Status = SafeUint64Mult (GetCpuFreq (VcpuTimeInfo), DiffTimer, &Dividend);\r
+  if (EFI_ERROR (Status)) {\r
+    DEBUG ((DEBUG_ERROR, "overflow while calculating APIC frequency\n"));\r
+    DEBUG ((DEBUG_ERROR, "CPU freq: %lu Hz; APIC timer tick count for 1 ms: %u\n",\r
+      GetCpuFreq (VcpuTimeInfo), DiffTimer));\r
+    ASSERT_EFI_ERROR (Status);\r
+    CpuDeadLoop ();\r
+  }\r
+\r
+  Freq = DivU64x64Remainder (Dividend, TscTick2 - TscTick, NULL);\r
+  DEBUG ((DEBUG_INFO, "APIC Freq % 8lu Hz\n", Freq));\r
+\r
+  UnmapXenPage (SharedInfo);\r
+}\r
index 8790d907d3ec6640227c39b854bc334503d15834..5732d2188871834ebbd9d58f9ce57a548a72a549 100644 (file)
@@ -52,6 +52,7 @@
   DebugLib\r
   HobLib\r
   IoLib\r
+  LocalApicLib\r
   PciLib\r
   ResourcePublicationLib\r
   PeiServicesLib\r
@@ -59,6 +60,7 @@
   MtrrLib\r
   MemEncryptSevLib\r
   PcdLib\r
+  SafeIntLib\r
   XenHypercallLib\r
 \r
 [Pcd]\r