All of lore.kernel.org
 help / color / mirror / Atom feed
From: Anthony PERARD <anthony.perard@citrix.com>
To: <devel@edk2.groups.io>
Cc: xen-devel@lists.xenproject.org,
	"Jordan Justen" <jordan.l.justen@intel.com>,
	"Anthony PERARD" <anthony.perard@citrix.com>,
	"Ard Biesheuvel" <ard.biesheuvel@linaro.org>,
	"Laszlo Ersek" <lersek@redhat.com>,
	"Julien Grall" <julien@xen.org>,
	"Roger Pau Monné" <roger.pau@citrix.com>
Subject: [PATCH v2 6/7] OvmfPkg/XenPlatformPei: Calibrate APIC timer frequency
Date: Thu, 25 Mar 2021 15:47:12 +0000	[thread overview]
Message-ID: <20210325154713.670104-7-anthony.perard@citrix.com> (raw)
In-Reply-To: <20210325154713.670104-1-anthony.perard@citrix.com>

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 map 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>
---
CC: Roger Pau Monné <roger.pau@citrix.com>
---

Notes:
    v2:
    - fix CamelCases
    - Use U64 multiplication and division helpers
    - don't read TscShift from the SharedInfo page again
    - change the location of the shared info page to be outside of the ram
    - check for overflow in XenDelay
    - check for overflow when calculating the calculating APIC frequency

 OvmfPkg/XenPlatformPei/XenPlatformPei.inf |   2 +
 OvmfPkg/XenPlatformPei/Platform.h         |   5 +
 OvmfPkg/XenPlatformPei/Platform.c         |   1 +
 OvmfPkg/XenPlatformPei/Xen.c              | 177 ++++++++++++++++++++++
 4 files changed, 185 insertions(+)

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


  parent reply	other threads:[~2021-03-25 15:47 UTC|newest]

Thread overview: 17+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2021-03-25 15:47 [PATCH v2 0/7] OvmfXen: Set PcdFSBClock at runtime Anthony PERARD
2021-03-25 15:47 ` [PATCH v2 1/7] OvmfPkg/XenResetVector: Silent a warning from nasm Anthony PERARD
2021-03-25 15:47 ` [PATCH v2 2/7] MdePkg: Allow PcdFSBClock to by Dynamic Anthony PERARD
2021-03-25 15:47 ` [PATCH v2 3/7] OvmfPkg/IndustryStandard/Xen: Apply EDK2 coding style to XEN_VCPU_TIME_INFO Anthony PERARD
2021-03-25 15:47 ` [PATCH v2 4/7] OvmfPkg/IndustryStandard: Introduce PageTable.h Anthony PERARD
2021-03-26 14:16   ` Tom Lendacky
2021-04-07  8:01     ` [edk2-devel] " Laszlo Ersek
2021-04-07  8:02   ` Laszlo Ersek
2021-04-07  8:04   ` Laszlo Ersek
2021-03-25 15:47 ` [PATCH v2 5/7] OvmfPkg/XenPlatformPei: Map extra physical address Anthony PERARD
2021-04-07  8:06   ` [edk2-devel] " Laszlo Ersek
2021-03-25 15:47 ` Anthony PERARD [this message]
2021-04-07  8:28   ` [edk2-devel] [PATCH v2 6/7] OvmfPkg/XenPlatformPei: Calibrate APIC timer frequency Laszlo Ersek
2021-03-25 15:47 ` [PATCH v2 7/7] OvmfPkg/OvmfXen: Set PcdFSBClock Anthony PERARD
2021-04-07  9:25   ` [edk2-devel] " Laszlo Ersek
2021-03-25 18:22 ` [PATCH v2 0/7] OvmfXen: Set PcdFSBClock at runtime Laszlo Ersek
2021-04-07  9:32 ` [edk2-devel] " Laszlo Ersek

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20210325154713.670104-7-anthony.perard@citrix.com \
    --to=anthony.perard@citrix.com \
    --cc=ard.biesheuvel@linaro.org \
    --cc=devel@edk2.groups.io \
    --cc=jordan.l.justen@intel.com \
    --cc=julien@xen.org \
    --cc=lersek@redhat.com \
    --cc=roger.pau@citrix.com \
    --cc=xen-devel@lists.xenproject.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.