Skip to content

Commit

Permalink
OvmfPkg/XenPlatformPei: Calibrate APIC timer frequency
Browse files Browse the repository at this point in the history
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 <[email protected]>
Acked-by: Laszlo Ersek <[email protected]>
Message-Id: <[email protected]>
  • Loading branch information
anthonyper-ctx authored and mergify[bot] committed Apr 13, 2021
1 parent 51e0bd2 commit c75c640
Show file tree
Hide file tree
Showing 4 changed files with 185 additions and 0 deletions.
1 change: 1 addition & 0 deletions OvmfPkg/XenPlatformPei/Platform.c
Original file line number Diff line number Diff line change
Expand Up @@ -448,6 +448,7 @@ InitializeXenPlatform (
InitializeRamRegions ();

InitializeXen ();
CalibrateLapicTimer ();

if (mBootMode != BOOT_ON_S3_RESUME) {
ReserveEmuVariableNvStore ();
Expand Down
5 changes: 5 additions & 0 deletions OvmfPkg/XenPlatformPei/Platform.h
Original file line number Diff line number Diff line change
Expand Up @@ -132,6 +132,11 @@ PhysicalAddressIdentityMapping (
IN EFI_PHYSICAL_ADDRESS AddressToMap
);

VOID
CalibrateLapicTimer (
VOID
);

extern EFI_BOOT_MODE mBootMode;

extern UINT8 mPhysMemAddressWidth;
Expand Down
177 changes: 177 additions & 0 deletions OvmfPkg/XenPlatformPei/Xen.c
Original file line number Diff line number Diff line change
Expand Up @@ -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>
Expand Down Expand Up @@ -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);
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));

UnmapXenPage (SharedInfo);
}
2 changes: 2 additions & 0 deletions OvmfPkg/XenPlatformPei/XenPlatformPei.inf
Original file line number Diff line number Diff line change
Expand Up @@ -52,13 +52,15 @@
DebugLib
HobLib
IoLib
LocalApicLib
PciLib
ResourcePublicationLib
PeiServicesLib
PeimEntryPoint
MtrrLib
MemEncryptSevLib
PcdLib
SafeIntLib
XenHypercallLib

[Pcd]
Expand Down

0 comments on commit c75c640

Please sign in to comment.