Update gEfiMdePkgTokenSpaceGuid.PcdFSBClock so it can have the correct value when SecPeiDxeTimerLibCpu start to use it for the APIC timer. Currently, nothing appear to use the value in PcdFSBClock before XenPlatformPei had a chance to set it even though TimerLib is included in modules run before XenPlatformPei. XenPlatformPei doesn't use any of the functions that would use that value. No other modules in the PEI phase seems to use the TimerLib before PcdFSBClock is set. There are currently two other modules in the PEI phase that needs the TimerLib: - S3Resume2Pei, but only because LocalApicLib needs it, but nothing is using the value from PcdFSBClock. - CpuMpPei, but I believe it only runs after XenPlatformPei Before the PEI phase, there's the SEC phase, and SecMain needs TimerLib because of LocalApicLib. And it initialise the APIC timers for the debug agent. But I don't think any of the DebugLib that OvmfXen could use are actually using the *Delay functions in TimerLib, and so would not use the value from PcdFSBClock which would be uninitialised. A simple runtime test showed that TimerLib doesn't use PcdFSBClock value before it is set. Ref: https://bugzilla.tianocore.org/show_bug.cgi?id=2490 Signed-off-by: Anthony PERARD <anthony.perard@citrix.com> Reviewed-by: Laszlo Ersek <lersek@redhat.com> Message-Id: <20210412133003.146438-8-anthony.perard@citrix.com> [lersek@redhat.com: cast Freq to UINT32 for PcdSet32S(), not for ASSERT()]
641 lines
15 KiB
C
641 lines
15 KiB
C
/**@file
|
|
Xen Platform PEI support
|
|
|
|
Copyright (c) 2006 - 2016, Intel Corporation. All rights reserved.<BR>
|
|
Copyright (c) 2011, Andrei Warkentin <andreiw@motorola.com>
|
|
Copyright (c) 2019, Citrix Systems, Inc.
|
|
|
|
SPDX-License-Identifier: BSD-2-Clause-Patent
|
|
|
|
**/
|
|
|
|
//
|
|
// The package level header files this module uses
|
|
//
|
|
#include <PiPei.h>
|
|
|
|
//
|
|
// The Library classes this module consumes
|
|
//
|
|
#include <Library/BaseMemoryLib.h>
|
|
#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>
|
|
#include <Library/MtrrLib.h>
|
|
#include <IndustryStandard/PageTable.h>
|
|
#include <IndustryStandard/Xen/arch-x86/hvm/start_info.h>
|
|
#include <Library/XenHypercallLib.h>
|
|
#include <IndustryStandard/Xen/memory.h>
|
|
|
|
#include "Platform.h"
|
|
#include "Xen.h"
|
|
|
|
STATIC UINT32 mXenLeaf = 0;
|
|
|
|
EFI_XEN_INFO mXenInfo;
|
|
|
|
//
|
|
// Location of the firmware info struct setup by hvmloader.
|
|
// Only the E820 table is used by OVMF.
|
|
//
|
|
EFI_XEN_OVMF_INFO *mXenHvmloaderInfo;
|
|
STATIC EFI_E820_ENTRY64 mE820Entries[128];
|
|
STATIC UINT32 mE820EntriesCount;
|
|
|
|
/**
|
|
Returns E820 map provided by Xen
|
|
|
|
@param Entries Pointer to E820 map
|
|
@param Count Number of entries
|
|
|
|
@return EFI_STATUS
|
|
**/
|
|
EFI_STATUS
|
|
XenGetE820Map (
|
|
EFI_E820_ENTRY64 **Entries,
|
|
UINT32 *Count
|
|
)
|
|
{
|
|
INTN ReturnCode;
|
|
xen_memory_map_t Parameters;
|
|
UINTN LoopIndex;
|
|
UINTN Index;
|
|
EFI_E820_ENTRY64 TmpEntry;
|
|
|
|
//
|
|
// Get E820 produced by hvmloader
|
|
//
|
|
if (mXenHvmloaderInfo != NULL) {
|
|
ASSERT (mXenHvmloaderInfo->E820 < MAX_ADDRESS);
|
|
*Entries = (EFI_E820_ENTRY64 *)(UINTN) mXenHvmloaderInfo->E820;
|
|
*Count = mXenHvmloaderInfo->E820EntriesCount;
|
|
|
|
return EFI_SUCCESS;
|
|
}
|
|
|
|
//
|
|
// Otherwise, get the E820 table from the Xen hypervisor
|
|
//
|
|
|
|
if (mE820EntriesCount > 0) {
|
|
*Entries = mE820Entries;
|
|
*Count = mE820EntriesCount;
|
|
return EFI_SUCCESS;
|
|
}
|
|
|
|
Parameters.nr_entries = 128;
|
|
set_xen_guest_handle (Parameters.buffer, mE820Entries);
|
|
|
|
// Returns a errno
|
|
ReturnCode = XenHypercallMemoryOp (XENMEM_memory_map, &Parameters);
|
|
ASSERT (ReturnCode == 0);
|
|
|
|
mE820EntriesCount = Parameters.nr_entries;
|
|
|
|
//
|
|
// Sort E820 entries
|
|
//
|
|
for (LoopIndex = 1; LoopIndex < mE820EntriesCount; LoopIndex++) {
|
|
for (Index = LoopIndex; Index < mE820EntriesCount; Index++) {
|
|
if (mE820Entries[Index - 1].BaseAddr > mE820Entries[Index].BaseAddr) {
|
|
TmpEntry = mE820Entries[Index];
|
|
mE820Entries[Index] = mE820Entries[Index - 1];
|
|
mE820Entries[Index - 1] = TmpEntry;
|
|
}
|
|
}
|
|
}
|
|
|
|
*Count = mE820EntriesCount;
|
|
*Entries = mE820Entries;
|
|
|
|
return EFI_SUCCESS;
|
|
}
|
|
|
|
/**
|
|
Connects to the Hypervisor.
|
|
|
|
@return EFI_STATUS
|
|
|
|
**/
|
|
EFI_STATUS
|
|
XenConnect (
|
|
)
|
|
{
|
|
UINT32 Index;
|
|
UINT32 TransferReg;
|
|
UINT32 TransferPages;
|
|
UINT32 XenVersion;
|
|
EFI_XEN_OVMF_INFO *Info;
|
|
CHAR8 Sig[sizeof (Info->Signature) + 1];
|
|
UINT32 *PVHResetVectorData;
|
|
RETURN_STATUS Status;
|
|
|
|
ASSERT (mXenLeaf != 0);
|
|
|
|
//
|
|
// Prepare HyperPages to be able to make hypercalls
|
|
//
|
|
|
|
AsmCpuid (mXenLeaf + 2, &TransferPages, &TransferReg, NULL, NULL);
|
|
mXenInfo.HyperPages = AllocatePages (TransferPages);
|
|
if (!mXenInfo.HyperPages) {
|
|
return EFI_OUT_OF_RESOURCES;
|
|
}
|
|
|
|
for (Index = 0; Index < TransferPages; Index++) {
|
|
AsmWriteMsr64 (TransferReg,
|
|
(UINTN) mXenInfo.HyperPages +
|
|
(Index << EFI_PAGE_SHIFT) + Index);
|
|
}
|
|
|
|
//
|
|
// Find out the Xen version
|
|
//
|
|
|
|
AsmCpuid (mXenLeaf + 1, &XenVersion, NULL, NULL, NULL);
|
|
DEBUG ((DEBUG_ERROR, "Detected Xen version %d.%d\n",
|
|
XenVersion >> 16, XenVersion & 0xFFFF));
|
|
mXenInfo.VersionMajor = (UINT16)(XenVersion >> 16);
|
|
mXenInfo.VersionMinor = (UINT16)(XenVersion & 0xFFFF);
|
|
|
|
//
|
|
// Check if there are information left by hvmloader
|
|
//
|
|
|
|
Info = (EFI_XEN_OVMF_INFO *)(UINTN) OVMF_INFO_PHYSICAL_ADDRESS;
|
|
//
|
|
// Copy the signature, and make it null-terminated.
|
|
//
|
|
AsciiStrnCpyS (Sig, sizeof (Sig), (CHAR8 *) &Info->Signature,
|
|
sizeof (Info->Signature));
|
|
if (AsciiStrCmp (Sig, "XenHVMOVMF") == 0) {
|
|
mXenHvmloaderInfo = Info;
|
|
} else {
|
|
mXenHvmloaderInfo = NULL;
|
|
}
|
|
|
|
mXenInfo.RsdpPvh = NULL;
|
|
|
|
//
|
|
// Locate and use information from the start of day structure if we have
|
|
// booted via the PVH entry point.
|
|
//
|
|
|
|
PVHResetVectorData = (VOID *)(UINTN) PcdGet32 (PcdXenPvhStartOfDayStructPtr);
|
|
//
|
|
// That magic value is written in XenResetVector/Ia32/XenPVHMain.asm
|
|
//
|
|
if (PVHResetVectorData[1] == SIGNATURE_32 ('X', 'P', 'V', 'H')) {
|
|
struct hvm_start_info *HVMStartInfo;
|
|
|
|
HVMStartInfo = (VOID *)(UINTN) PVHResetVectorData[0];
|
|
if (HVMStartInfo->magic == XEN_HVM_START_MAGIC_VALUE) {
|
|
ASSERT (HVMStartInfo->rsdp_paddr != 0);
|
|
if (HVMStartInfo->rsdp_paddr != 0) {
|
|
mXenInfo.RsdpPvh = (VOID *)(UINTN)HVMStartInfo->rsdp_paddr;
|
|
}
|
|
}
|
|
}
|
|
|
|
BuildGuidDataHob (
|
|
&gEfiXenInfoGuid,
|
|
&mXenInfo,
|
|
sizeof(mXenInfo)
|
|
);
|
|
|
|
//
|
|
// Initialize the XenHypercall library, now that the XenInfo HOB is
|
|
// available
|
|
//
|
|
Status = XenHypercallLibInit ();
|
|
ASSERT_RETURN_ERROR (Status);
|
|
|
|
return EFI_SUCCESS;
|
|
}
|
|
|
|
/**
|
|
Figures out if we are running inside Xen HVM.
|
|
|
|
@retval TRUE Xen was detected
|
|
@retval FALSE Xen was not detected
|
|
|
|
**/
|
|
BOOLEAN
|
|
XenDetect (
|
|
VOID
|
|
)
|
|
{
|
|
UINT8 Signature[13];
|
|
|
|
if (mXenLeaf != 0) {
|
|
return TRUE;
|
|
}
|
|
|
|
Signature[12] = '\0';
|
|
for (mXenLeaf = 0x40000000; mXenLeaf < 0x40010000; mXenLeaf += 0x100) {
|
|
AsmCpuid (mXenLeaf,
|
|
NULL,
|
|
(UINT32 *) &Signature[0],
|
|
(UINT32 *) &Signature[4],
|
|
(UINT32 *) &Signature[8]);
|
|
|
|
if (!AsciiStrCmp ((CHAR8 *) Signature, "XenVMMXenVMM")) {
|
|
return TRUE;
|
|
}
|
|
}
|
|
|
|
mXenLeaf = 0;
|
|
return FALSE;
|
|
}
|
|
|
|
BOOLEAN
|
|
XenHvmloaderDetected (
|
|
VOID
|
|
)
|
|
{
|
|
return (mXenHvmloaderInfo != NULL);
|
|
}
|
|
|
|
BOOLEAN
|
|
XenPvhDetected (
|
|
VOID
|
|
)
|
|
{
|
|
//
|
|
// This function should only be used after XenConnect
|
|
//
|
|
ASSERT (mXenInfo.HyperPages != NULL);
|
|
|
|
return mXenHvmloaderInfo == NULL;
|
|
}
|
|
|
|
VOID
|
|
XenPublishRamRegions (
|
|
VOID
|
|
)
|
|
{
|
|
EFI_E820_ENTRY64 *E820Map;
|
|
UINT32 E820EntriesCount;
|
|
EFI_STATUS Status;
|
|
EFI_E820_ENTRY64 *Entry;
|
|
UINTN Index;
|
|
UINT64 LapicBase;
|
|
UINT64 LapicEnd;
|
|
|
|
|
|
DEBUG ((DEBUG_INFO, "Using memory map provided by Xen\n"));
|
|
|
|
//
|
|
// Parse RAM in E820 map
|
|
//
|
|
E820EntriesCount = 0;
|
|
Status = XenGetE820Map (&E820Map, &E820EntriesCount);
|
|
ASSERT_EFI_ERROR (Status);
|
|
|
|
AddMemoryBaseSizeHob (0, 0xA0000);
|
|
//
|
|
// Video memory + Legacy BIOS region, to allow Linux to boot.
|
|
//
|
|
AddReservedMemoryBaseSizeHob (0xA0000, BASE_1MB - 0xA0000, TRUE);
|
|
|
|
LapicBase = PcdGet32 (PcdCpuLocalApicBaseAddress);
|
|
LapicEnd = LapicBase + SIZE_1MB;
|
|
AddIoMemoryRangeHob (LapicBase, LapicEnd);
|
|
|
|
for (Index = 0; Index < E820EntriesCount; Index++) {
|
|
UINT64 Base;
|
|
UINT64 End;
|
|
UINT64 ReservedBase;
|
|
UINT64 ReservedEnd;
|
|
|
|
Entry = &E820Map[Index];
|
|
|
|
//
|
|
// Round up the start address, and round down the end address.
|
|
//
|
|
Base = ALIGN_VALUE (Entry->BaseAddr, (UINT64)EFI_PAGE_SIZE);
|
|
End = (Entry->BaseAddr + Entry->Length) & ~(UINT64)EFI_PAGE_MASK;
|
|
|
|
//
|
|
// Ignore the first 1MB, this is handled before the loop.
|
|
//
|
|
if (Base < BASE_1MB) {
|
|
Base = BASE_1MB;
|
|
}
|
|
if (Base >= End) {
|
|
continue;
|
|
}
|
|
|
|
switch (Entry->Type) {
|
|
case EfiAcpiAddressRangeMemory:
|
|
AddMemoryRangeHob (Base, End);
|
|
break;
|
|
case EfiAcpiAddressRangeACPI:
|
|
AddReservedMemoryRangeHob (Base, End, FALSE);
|
|
break;
|
|
case EfiAcpiAddressRangeReserved:
|
|
//
|
|
// hvmloader marks a range that overlaps with the local APIC memory
|
|
// mapped region as reserved, but CpuDxe wants it as mapped IO. We
|
|
// have already added it as mapped IO, so skip it here.
|
|
//
|
|
|
|
//
|
|
// add LAPIC predecessor range, if any
|
|
//
|
|
ReservedBase = Base;
|
|
ReservedEnd = MIN (End, LapicBase);
|
|
if (ReservedBase < ReservedEnd) {
|
|
AddReservedMemoryRangeHob (ReservedBase, ReservedEnd, FALSE);
|
|
}
|
|
|
|
//
|
|
// add LAPIC successor range, if any
|
|
//
|
|
ReservedBase = MAX (Base, LapicEnd);
|
|
ReservedEnd = End;
|
|
if (ReservedBase < ReservedEnd) {
|
|
AddReservedMemoryRangeHob (ReservedBase, ReservedEnd, FALSE);
|
|
}
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
/**
|
|
Perform Xen PEI initialization.
|
|
|
|
@return EFI_SUCCESS Xen initialized successfully
|
|
@return EFI_NOT_FOUND Not running under Xen
|
|
|
|
**/
|
|
EFI_STATUS
|
|
InitializeXen (
|
|
VOID
|
|
)
|
|
{
|
|
RETURN_STATUS PcdStatus;
|
|
|
|
PcdStatus = PcdSetBoolS (PcdPciDisableBusEnumeration, TRUE);
|
|
ASSERT_RETURN_ERROR (PcdStatus);
|
|
|
|
return EFI_SUCCESS;
|
|
}
|
|
|
|
EFI_STATUS
|
|
PhysicalAddressIdentityMapping (
|
|
IN EFI_PHYSICAL_ADDRESS AddressToMap
|
|
)
|
|
{
|
|
INTN Index;
|
|
PAGE_MAP_AND_DIRECTORY_POINTER *L4, *L3;
|
|
PAGE_TABLE_ENTRY *PageTable;
|
|
|
|
DEBUG ((DEBUG_INFO, "Mapping 1:1 of address 0x%lx\n", (UINT64)AddressToMap));
|
|
|
|
// L4 / Top level Page Directory Pointers
|
|
|
|
L4 = (VOID*)(UINTN)PcdGet32 (PcdOvmfSecPageTablesBase);
|
|
Index = PML4_OFFSET (AddressToMap);
|
|
|
|
if (!L4[Index].Bits.Present) {
|
|
L3 = AllocatePages (1);
|
|
if (L3 == NULL) {
|
|
return EFI_OUT_OF_RESOURCES;
|
|
}
|
|
|
|
ZeroMem (L3, EFI_PAGE_SIZE);
|
|
|
|
L4[Index].Bits.ReadWrite = 1;
|
|
L4[Index].Bits.Accessed = 1;
|
|
L4[Index].Bits.PageTableBaseAddress = (EFI_PHYSICAL_ADDRESS)L3 >> 12;
|
|
L4[Index].Bits.Present = 1;
|
|
}
|
|
|
|
// L3 / Next level Page Directory Pointers
|
|
|
|
L3 = (VOID*)(EFI_PHYSICAL_ADDRESS)(L4[Index].Bits.PageTableBaseAddress << 12);
|
|
Index = PDP_OFFSET (AddressToMap);
|
|
|
|
if (!L3[Index].Bits.Present) {
|
|
PageTable = AllocatePages (1);
|
|
if (PageTable == NULL) {
|
|
return EFI_OUT_OF_RESOURCES;
|
|
}
|
|
|
|
ZeroMem (PageTable, EFI_PAGE_SIZE);
|
|
|
|
L3[Index].Bits.ReadWrite = 1;
|
|
L3[Index].Bits.Accessed = 1;
|
|
L3[Index].Bits.PageTableBaseAddress = (EFI_PHYSICAL_ADDRESS)PageTable >> 12;
|
|
L3[Index].Bits.Present = 1;
|
|
}
|
|
|
|
// L2 / Page Table Entries
|
|
|
|
PageTable = (VOID*)(EFI_PHYSICAL_ADDRESS)(L3[Index].Bits.PageTableBaseAddress << 12);
|
|
Index = PDE_OFFSET (AddressToMap);
|
|
|
|
if (!PageTable[Index].Bits.Present) {
|
|
PageTable[Index].Bits.ReadWrite = 1;
|
|
PageTable[Index].Bits.Accessed = 1;
|
|
PageTable[Index].Bits.Dirty = 1;
|
|
PageTable[Index].Bits.MustBe1 = 1;
|
|
PageTable[Index].Bits.PageTableBaseAddress = AddressToMap >> 21;
|
|
PageTable[Index].Bits.Present = 1;
|
|
}
|
|
|
|
CpuFlushTlb ();
|
|
|
|
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);
|
|
if (EFI_ERROR (Status)) {
|
|
DEBUG ((DEBUG_ERROR,
|
|
"XenDelay (%lu ns): delay too big in relation to CPU freq %lu Hz\n",
|
|
DelayNs, CpuFreq));
|
|
ASSERT_EFI_ERROR (Status);
|
|
CpuDeadLoop ();
|
|
}
|
|
|
|
DelayTick = DivU64x32 (Delay, 1000000000);
|
|
|
|
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);
|
|
if (EFI_ERROR (Status)) {
|
|
DEBUG ((DEBUG_ERROR,
|
|
"Failed to add page table entry for Xen shared info page: %r\n",
|
|
Status));
|
|
ASSERT_EFI_ERROR (Status);
|
|
return;
|
|
}
|
|
|
|
Status = MapSharedInfoPage (SharedInfo);
|
|
if (EFI_ERROR (Status)) {
|
|
DEBUG ((DEBUG_ERROR, "Failed to map Xen's shared info page: %r\n",
|
|
Status));
|
|
ASSERT_EFI_ERROR (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);
|
|
if (EFI_ERROR (Status)) {
|
|
DEBUG ((DEBUG_ERROR, "overflow while calculating APIC frequency\n"));
|
|
DEBUG ((DEBUG_ERROR, "CPU freq: %lu Hz; APIC timer tick count for 1 ms: %u\n",
|
|
GetCpuFreq (VcpuTimeInfo), DiffTimer));
|
|
ASSERT_EFI_ERROR (Status);
|
|
CpuDeadLoop ();
|
|
}
|
|
|
|
Freq = DivU64x64Remainder (Dividend, TscTick2 - TscTick, NULL);
|
|
DEBUG ((DEBUG_INFO, "APIC Freq % 8lu Hz\n", Freq));
|
|
|
|
ASSERT (Freq <= MAX_UINT32);
|
|
Status = PcdSet32S (PcdFSBClock, (UINT32)Freq);
|
|
ASSERT_EFI_ERROR (Status);
|
|
|
|
UnmapXenPage (SharedInfo);
|
|
}
|