]> git.proxmox.com Git - mirror_edk2.git/blobdiff - OvmfPkg/XenPlatformPei/Xen.c
OvmfPkg: Apply uncrustify changes
[mirror_edk2.git] / OvmfPkg / XenPlatformPei / Xen.c
index c67f4c96973603ee445f9f09214f8cff7d91aaad..7f00eef11e8faf6e86407b175ad76395bb488d43 100644 (file)
 //\r
 // The Library classes this module consumes\r
 //\r
+#include <Library/BaseMemoryLib.h>\r
+#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
 #include <Library/MtrrLib.h>\r
+#include <IndustryStandard/PageTable.h>\r
 #include <IndustryStandard/Xen/arch-x86/hvm/start_info.h>\r
 #include <Library/XenHypercallLib.h>\r
+#include <IndustryStandard/Xen/memory.h>\r
 \r
 #include "Platform.h"\r
 #include "Xen.h"\r
 \r
-STATIC UINT32 mXenLeaf = 0;\r
+STATIC UINT32  mXenLeaf = 0;\r
 \r
-EFI_XEN_INFO mXenInfo;\r
+EFI_XEN_INFO  mXenInfo;\r
 \r
 //\r
 // Location of the firmware info struct setup by hvmloader.\r
 // Only the E820 table is used by OVMF.\r
 //\r
-EFI_XEN_OVMF_INFO *mXenHvmloaderInfo;\r
+EFI_XEN_OVMF_INFO        *mXenHvmloaderInfo;\r
+STATIC EFI_E820_ENTRY64  mE820Entries[128];\r
+STATIC UINT32            mE820EntriesCount;\r
 \r
 /**\r
   Returns E820 map provided by Xen\r
@@ -51,61 +59,115 @@ EFI_XEN_OVMF_INFO *mXenHvmloaderInfo;
 **/\r
 EFI_STATUS\r
 XenGetE820Map (\r
-  EFI_E820_ENTRY64 **Entries,\r
-  UINT32 *Count\r
+  EFI_E820_ENTRY64  **Entries,\r
+  UINT32            *Count\r
   )\r
 {\r
+  INTN              ReturnCode;\r
+  xen_memory_map_t  Parameters;\r
+  UINTN             LoopIndex;\r
+  UINTN             Index;\r
+  EFI_E820_ENTRY64  TmpEntry;\r
+\r
   //\r
   // Get E820 produced by hvmloader\r
   //\r
   if (mXenHvmloaderInfo != NULL) {\r
     ASSERT (mXenHvmloaderInfo->E820 < MAX_ADDRESS);\r
-    *Entries = (EFI_E820_ENTRY64 *)(UINTN) mXenHvmloaderInfo->E820;\r
-    *Count = mXenHvmloaderInfo->E820EntriesCount;\r
+    *Entries = (EFI_E820_ENTRY64 *)(UINTN)mXenHvmloaderInfo->E820;\r
+    *Count   = mXenHvmloaderInfo->E820EntriesCount;\r
+\r
+    return EFI_SUCCESS;\r
+  }\r
 \r
+  //\r
+  // Otherwise, get the E820 table from the Xen hypervisor\r
+  //\r
+\r
+  if (mE820EntriesCount > 0) {\r
+    *Entries = mE820Entries;\r
+    *Count   = mE820EntriesCount;\r
     return EFI_SUCCESS;\r
   }\r
 \r
-  return EFI_NOT_FOUND;\r
+  Parameters.nr_entries = 128;\r
+  set_xen_guest_handle (Parameters.buffer, mE820Entries);\r
+\r
+  // Returns a errno\r
+  ReturnCode = XenHypercallMemoryOp (XENMEM_memory_map, &Parameters);\r
+  ASSERT (ReturnCode == 0);\r
+\r
+  mE820EntriesCount = Parameters.nr_entries;\r
+\r
+  //\r
+  // Sort E820 entries\r
+  //\r
+  for (LoopIndex = 1; LoopIndex < mE820EntriesCount; LoopIndex++) {\r
+    for (Index = LoopIndex; Index < mE820EntriesCount; Index++) {\r
+      if (mE820Entries[Index - 1].BaseAddr > mE820Entries[Index].BaseAddr) {\r
+        TmpEntry                = mE820Entries[Index];\r
+        mE820Entries[Index]     = mE820Entries[Index - 1];\r
+        mE820Entries[Index - 1] = TmpEntry;\r
+      }\r
+    }\r
+  }\r
+\r
+  *Count   = mE820EntriesCount;\r
+  *Entries = mE820Entries;\r
+\r
+  return EFI_SUCCESS;\r
 }\r
 \r
 /**\r
   Connects to the Hypervisor.\r
 \r
-  @param  XenLeaf     CPUID index used to connect.\r
-\r
   @return EFI_STATUS\r
 \r
 **/\r
 EFI_STATUS\r
 XenConnect (\r
-  UINT32 XenLeaf\r
   )\r
 {\r
-  UINT32 Index;\r
-  UINT32 TransferReg;\r
-  UINT32 TransferPages;\r
-  UINT32 XenVersion;\r
-  EFI_XEN_OVMF_INFO *Info;\r
-  CHAR8 Sig[sizeof (Info->Signature) + 1];\r
-  UINT32 *PVHResetVectorData;\r
-  RETURN_STATUS Status;\r
-\r
-  AsmCpuid (XenLeaf + 2, &TransferPages, &TransferReg, NULL, NULL);\r
+  UINT32             Index;\r
+  UINT32             TransferReg;\r
+  UINT32             TransferPages;\r
+  UINT32             XenVersion;\r
+  EFI_XEN_OVMF_INFO  *Info;\r
+  CHAR8              Sig[sizeof (Info->Signature) + 1];\r
+  UINT32             *PVHResetVectorData;\r
+  RETURN_STATUS      Status;\r
+\r
+  ASSERT (mXenLeaf != 0);\r
+\r
+  //\r
+  // Prepare HyperPages to be able to make hypercalls\r
+  //\r
+\r
+  AsmCpuid (mXenLeaf + 2, &TransferPages, &TransferReg, NULL, NULL);\r
   mXenInfo.HyperPages = AllocatePages (TransferPages);\r
   if (!mXenInfo.HyperPages) {\r
     return EFI_OUT_OF_RESOURCES;\r
   }\r
 \r
   for (Index = 0; Index < TransferPages; Index++) {\r
-    AsmWriteMsr64 (TransferReg,\r
-                   (UINTN) mXenInfo.HyperPages +\r
-                   (Index << EFI_PAGE_SHIFT) + Index);\r
+    AsmWriteMsr64 (\r
+      TransferReg,\r
+      (UINTN)mXenInfo.HyperPages +\r
+      (Index << EFI_PAGE_SHIFT) + Index\r
+      );\r
   }\r
 \r
-  AsmCpuid (XenLeaf + 1, &XenVersion, NULL, NULL, NULL);\r
-  DEBUG ((DEBUG_ERROR, "Detected Xen version %d.%d\n",\r
-          XenVersion >> 16, XenVersion & 0xFFFF));\r
+  //\r
+  // Find out the Xen version\r
+  //\r
+\r
+  AsmCpuid (mXenLeaf + 1, &XenVersion, NULL, NULL, NULL);\r
+  DEBUG ((\r
+    DEBUG_ERROR,\r
+    "Detected Xen version %d.%d\n",\r
+    XenVersion >> 16,\r
+    XenVersion & 0xFFFF\r
+    ));\r
   mXenInfo.VersionMajor = (UINT16)(XenVersion >> 16);\r
   mXenInfo.VersionMinor = (UINT16)(XenVersion & 0xFFFF);\r
 \r
@@ -113,12 +175,16 @@ XenConnect (
   // Check if there are information left by hvmloader\r
   //\r
 \r
-  Info = (EFI_XEN_OVMF_INFO *)(UINTN) OVMF_INFO_PHYSICAL_ADDRESS;\r
+  Info = (EFI_XEN_OVMF_INFO *)(UINTN)OVMF_INFO_PHYSICAL_ADDRESS;\r
   //\r
   // Copy the signature, and make it null-terminated.\r
   //\r
-  AsciiStrnCpyS (Sig, sizeof (Sig), (CHAR8 *) &Info->Signature,\r
-    sizeof (Info->Signature));\r
+  AsciiStrnCpyS (\r
+    Sig,\r
+    sizeof (Sig),\r
+    (CHAR8 *)&Info->Signature,\r
+    sizeof (Info->Signature)\r
+    );\r
   if (AsciiStrCmp (Sig, "XenHVMOVMF") == 0) {\r
     mXenHvmloaderInfo = Info;\r
   } else {\r
@@ -132,14 +198,14 @@ XenConnect (
   // booted via the PVH entry point.\r
   //\r
 \r
-  PVHResetVectorData = (VOID *)(UINTN) PcdGet32 (PcdXenPvhStartOfDayStructPtr);\r
+  PVHResetVectorData = (VOID *)(UINTN)PcdGet32 (PcdXenPvhStartOfDayStructPtr);\r
   //\r
   // That magic value is written in XenResetVector/Ia32/XenPVHMain.asm\r
   //\r
   if (PVHResetVectorData[1] == SIGNATURE_32 ('X', 'P', 'V', 'H')) {\r
-    struct hvm_start_info *HVMStartInfo;\r
+    struct hvm_start_info  *HVMStartInfo;\r
 \r
-    HVMStartInfo = (VOID *)(UINTN) PVHResetVectorData[0];\r
+    HVMStartInfo = (VOID *)(UINTN)PVHResetVectorData[0];\r
     if (HVMStartInfo->magic == XEN_HVM_START_MAGIC_VALUE) {\r
       ASSERT (HVMStartInfo->rsdp_paddr != 0);\r
       if (HVMStartInfo->rsdp_paddr != 0) {\r
@@ -151,7 +217,7 @@ XenConnect (
   BuildGuidDataHob (\r
     &gEfiXenInfoGuid,\r
     &mXenInfo,\r
-    sizeof(mXenInfo)\r
+    sizeof (mXenInfo)\r
     );\r
 \r
   //\r
@@ -176,7 +242,7 @@ XenDetect (
   VOID\r
   )\r
 {\r
-  UINT8 Signature[13];\r
+  UINT8  Signature[13];\r
 \r
   if (mXenLeaf != 0) {\r
     return TRUE;\r
@@ -184,13 +250,15 @@ XenDetect (
 \r
   Signature[12] = '\0';\r
   for (mXenLeaf = 0x40000000; mXenLeaf < 0x40010000; mXenLeaf += 0x100) {\r
-    AsmCpuid (mXenLeaf,\r
-              NULL,\r
-              (UINT32 *) &Signature[0],\r
-              (UINT32 *) &Signature[4],\r
-              (UINT32 *) &Signature[8]);\r
-\r
-    if (!AsciiStrCmp ((CHAR8 *) Signature, "XenVMMXenVMM")) {\r
+    AsmCpuid (\r
+      mXenLeaf,\r
+      NULL,\r
+      (UINT32 *)&Signature[0],\r
+      (UINT32 *)&Signature[4],\r
+      (UINT32 *)&Signature[8]\r
+      );\r
+\r
+    if (!AsciiStrCmp ((CHAR8 *)Signature, "XenVMMXenVMM")) {\r
       return TRUE;\r
     }\r
   }\r
@@ -199,6 +267,26 @@ XenDetect (
   return FALSE;\r
 }\r
 \r
+BOOLEAN\r
+XenHvmloaderDetected (\r
+  VOID\r
+  )\r
+{\r
+  return (mXenHvmloaderInfo != NULL);\r
+}\r
+\r
+BOOLEAN\r
+XenPvhDetected (\r
+  VOID\r
+  )\r
+{\r
+  //\r
+  // This function should only be used after XenConnect\r
+  //\r
+  ASSERT (mXenInfo.HyperPages != NULL);\r
+\r
+  return mXenHvmloaderInfo == NULL;\r
+}\r
 \r
 VOID\r
 XenPublishRamRegions (\r
@@ -208,6 +296,10 @@ XenPublishRamRegions (
   EFI_E820_ENTRY64  *E820Map;\r
   UINT32            E820EntriesCount;\r
   EFI_STATUS        Status;\r
+  EFI_E820_ENTRY64  *Entry;\r
+  UINTN             Index;\r
+  UINT64            LapicBase;\r
+  UINT64            LapicEnd;\r
 \r
   DEBUG ((DEBUG_INFO, "Using memory map provided by Xen\n"));\r
 \r
@@ -215,60 +307,336 @@ XenPublishRamRegions (
   // Parse RAM in E820 map\r
   //\r
   E820EntriesCount = 0;\r
-  Status = XenGetE820Map (&E820Map, &E820EntriesCount);\r
-\r
+  Status           = XenGetE820Map (&E820Map, &E820EntriesCount);\r
   ASSERT_EFI_ERROR (Status);\r
 \r
-  if (E820EntriesCount > 0) {\r
-    EFI_E820_ENTRY64 *Entry;\r
-    UINT32 Loop;\r
+  AddMemoryBaseSizeHob (0, 0xA0000);\r
+  //\r
+  // Video memory + Legacy BIOS region, to allow Linux to boot.\r
+  //\r
+  AddReservedMemoryBaseSizeHob (0xA0000, BASE_1MB - 0xA0000, TRUE);\r
+\r
+  LapicBase = PcdGet32 (PcdCpuLocalApicBaseAddress);\r
+  LapicEnd  = LapicBase + SIZE_1MB;\r
+  AddIoMemoryRangeHob (LapicBase, LapicEnd);\r
+\r
+  for (Index = 0; Index < E820EntriesCount; Index++) {\r
+    UINT64  Base;\r
+    UINT64  End;\r
+    UINT64  ReservedBase;\r
+    UINT64  ReservedEnd;\r
+\r
+    Entry = &E820Map[Index];\r
+\r
+    //\r
+    // Round up the start address, and round down the end address.\r
+    //\r
+    Base = ALIGN_VALUE (Entry->BaseAddr, (UINT64)EFI_PAGE_SIZE);\r
+    End  = (Entry->BaseAddr + Entry->Length) & ~(UINT64)EFI_PAGE_MASK;\r
+\r
+    //\r
+    // Ignore the first 1MB, this is handled before the loop.\r
+    //\r
+    if (Base < BASE_1MB) {\r
+      Base = BASE_1MB;\r
+    }\r
 \r
-    for (Loop = 0; Loop < E820EntriesCount; Loop++) {\r
-      Entry = E820Map + Loop;\r
+    if (Base >= End) {\r
+      continue;\r
+    }\r
 \r
-      //\r
-      // Only care about RAM\r
-      //\r
-      if (Entry->Type != EfiAcpiAddressRangeMemory) {\r
-        continue;\r
-      }\r
+    switch (Entry->Type) {\r
+      case EfiAcpiAddressRangeMemory:\r
+        AddMemoryRangeHob (Base, End);\r
+        break;\r
+      case EfiAcpiAddressRangeACPI:\r
+        AddReservedMemoryRangeHob (Base, End, FALSE);\r
+        break;\r
+      case EfiAcpiAddressRangeReserved:\r
+        //\r
+        // hvmloader marks a range that overlaps with the local APIC memory\r
+        // mapped region as reserved, but CpuDxe wants it as mapped IO. We\r
+        // have already added it as mapped IO, so skip it here.\r
+        //\r
+\r
+        //\r
+        // add LAPIC predecessor range, if any\r
+        //\r
+        ReservedBase = Base;\r
+        ReservedEnd  = MIN (End, LapicBase);\r
+        if (ReservedBase < ReservedEnd) {\r
+          AddReservedMemoryRangeHob (ReservedBase, ReservedEnd, FALSE);\r
+        }\r
+\r
+        //\r
+        // add LAPIC successor range, if any\r
+        //\r
+        ReservedBase = MAX (Base, LapicEnd);\r
+        ReservedEnd  = End;\r
+        if (ReservedBase < ReservedEnd) {\r
+          AddReservedMemoryRangeHob (ReservedBase, ReservedEnd, FALSE);\r
+        }\r
+\r
+        break;\r
+      default:\r
+        break;\r
+    }\r
+  }\r
+}\r
 \r
-      AddMemoryBaseSizeHob (Entry->BaseAddr, Entry->Length);\r
+EFI_STATUS\r
+PhysicalAddressIdentityMapping (\r
+  IN EFI_PHYSICAL_ADDRESS  AddressToMap\r
+  )\r
+{\r
+  INTN                            Index;\r
+  PAGE_MAP_AND_DIRECTORY_POINTER  *L4, *L3;\r
+  PAGE_TABLE_ENTRY                *PageTable;\r
+\r
+  DEBUG ((DEBUG_INFO, "Mapping 1:1 of address 0x%lx\n", (UINT64)AddressToMap));\r
 \r
-      MtrrSetMemoryAttribute (Entry->BaseAddr, Entry->Length, CacheWriteBack);\r
+  // L4 / Top level Page Directory Pointers\r
+\r
+  L4    = (VOID *)(UINTN)PcdGet32 (PcdOvmfSecPageTablesBase);\r
+  Index = PML4_OFFSET (AddressToMap);\r
+\r
+  if (!L4[Index].Bits.Present) {\r
+    L3 = AllocatePages (1);\r
+    if (L3 == NULL) {\r
+      return EFI_OUT_OF_RESOURCES;\r
     }\r
+\r
+    ZeroMem (L3, EFI_PAGE_SIZE);\r
+\r
+    L4[Index].Bits.ReadWrite            = 1;\r
+    L4[Index].Bits.Accessed             = 1;\r
+    L4[Index].Bits.PageTableBaseAddress = (EFI_PHYSICAL_ADDRESS)L3 >> 12;\r
+    L4[Index].Bits.Present              = 1;\r
   }\r
-}\r
 \r
+  // L3 / Next level Page Directory Pointers\r
 \r
-/**\r
-  Perform Xen PEI initialization.\r
+  L3    = (VOID *)(EFI_PHYSICAL_ADDRESS)(L4[Index].Bits.PageTableBaseAddress << 12);\r
+  Index = PDP_OFFSET (AddressToMap);\r
+\r
+  if (!L3[Index].Bits.Present) {\r
+    PageTable = AllocatePages (1);\r
+    if (PageTable == NULL) {\r
+      return EFI_OUT_OF_RESOURCES;\r
+    }\r
 \r
-  @return EFI_SUCCESS     Xen initialized successfully\r
-  @return EFI_NOT_FOUND   Not running under Xen\r
+    ZeroMem (PageTable, EFI_PAGE_SIZE);\r
 \r
-**/\r
+    L3[Index].Bits.ReadWrite            = 1;\r
+    L3[Index].Bits.Accessed             = 1;\r
+    L3[Index].Bits.PageTableBaseAddress = (EFI_PHYSICAL_ADDRESS)PageTable >> 12;\r
+    L3[Index].Bits.Present              = 1;\r
+  }\r
+\r
+  // L2 / Page Table Entries\r
+\r
+  PageTable = (VOID *)(EFI_PHYSICAL_ADDRESS)(L3[Index].Bits.PageTableBaseAddress << 12);\r
+  Index     = PDE_OFFSET (AddressToMap);\r
+\r
+  if (!PageTable[Index].Bits.Present) {\r
+    PageTable[Index].Bits.ReadWrite            = 1;\r
+    PageTable[Index].Bits.Accessed             = 1;\r
+    PageTable[Index].Bits.Dirty                = 1;\r
+    PageTable[Index].Bits.MustBe1              = 1;\r
+    PageTable[Index].Bits.PageTableBaseAddress = AddressToMap >> 21;\r
+    PageTable[Index].Bits.Present              = 1;\r
+  }\r
+\r
+  CpuFlushTlb ();\r
+\r
+  return EFI_SUCCESS;\r
+}\r
+\r
+STATIC\r
 EFI_STATUS\r
-InitializeXen (\r
-  VOID\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
+\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
+STATIC\r
+UINT64\r
+GetCpuFreq (\r
+  IN XEN_VCPU_TIME_INFO  *VcpuTime\r
   )\r
 {\r
-  RETURN_STATUS PcdStatus;\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
+\r
+  return CpuFreq;\r
+}\r
 \r
-  if (mXenLeaf == 0) {\r
-    return EFI_NOT_FOUND;\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 ((\r
+      DEBUG_ERROR,\r
+      "XenDelay (%lu ns): delay too big in relation to CPU freq %lu Hz\n",\r
+      DelayNs,\r
+      CpuFreq\r
+      ));\r
+    ASSERT_EFI_ERROR (Status);\r
+    CpuDeadLoop ();\r
   }\r
 \r
-  XenConnect (mXenLeaf);\r
+  DelayTick = DivU64x32 (Delay, 1000000000);\r
+\r
+  NewTick = Tick + DelayTick;\r
 \r
   //\r
-  // Reserve away HVMLOADER reserved memory [0xFC000000,0xFD000000).\r
-  // This needs to match HVMLOADER RESERVED_MEMBASE/RESERVED_MEMSIZE.\r
+  // Check for overflow\r
   //\r
-  AddReservedMemoryBaseSizeHob (0xFC000000, 0x1000000, FALSE);\r
+  if (NewTick < Tick) {\r
+    //\r
+    // Overflow, wait for TSC to also overflow\r
+    //\r
+    while (AsmReadTsc () >= Tick) {\r
+      CpuPause ();\r
+    }\r
+  }\r
 \r
-  PcdStatus = PcdSetBoolS (PcdPciDisableBusEnumeration, TRUE);\r
-  ASSERT_RETURN_ERROR (PcdStatus);\r
+  while (AsmReadTsc () <= NewTick) {\r
+    CpuPause ();\r
+  }\r
+}\r
 \r
-  return EFI_SUCCESS;\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
+  SharedInfo = (VOID *)((UINTN)PcdGet32 (PcdCpuLocalApicBaseAddress) + SIZE_1MB);\r
+  Status     = PhysicalAddressIdentityMapping ((EFI_PHYSICAL_ADDRESS)SharedInfo);\r
+  if (EFI_ERROR (Status)) {\r
+    DEBUG ((\r
+      DEBUG_ERROR,\r
+      "Failed to add page table entry for Xen shared info page: %r\n",\r
+      Status\r
+      ));\r
+    ASSERT_EFI_ERROR (Status);\r
+    return;\r
+  }\r
+\r
+  Status = MapSharedInfoPage (SharedInfo);\r
+  if (EFI_ERROR (Status)) {\r
+    DEBUG ((\r
+      DEBUG_ERROR,\r
+      "Failed to map Xen's shared info page: %r\n",\r
+      Status\r
+      ));\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
+  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 ((\r
+      DEBUG_ERROR,\r
+      "CPU freq: %lu Hz; APIC timer tick count for 1 ms: %u\n",\r
+      GetCpuFreq (VcpuTimeInfo),\r
+      DiffTimer\r
+      ));\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
+  ASSERT (Freq <= MAX_UINT32);\r
+  Status = PcdSet32S (PcdFSBClock, (UINT32)Freq);\r
+  ASSERT_EFI_ERROR (Status);\r
+\r
+  UnmapXenPage (SharedInfo);\r
 }\r