mirror of https://github.com/acidanthera/audk.git
OvmfPkg/XenPlatformPei: Calibrate APIC timer frequency
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>
This commit is contained in:
parent
51e0bd28bb
commit
c75c640512
|
@ -448,6 +448,7 @@ InitializeXenPlatform (
|
|||
InitializeRamRegions ();
|
||||
|
||||
InitializeXen ();
|
||||
CalibrateLapicTimer ();
|
||||
|
||||
if (mBootMode != BOOT_ON_S3_RESUME) {
|
||||
ReserveEmuVariableNvStore ();
|
||||
|
|
|
@ -132,6 +132,11 @@ PhysicalAddressIdentityMapping (
|
|||
IN EFI_PHYSICAL_ADDRESS AddressToMap
|
||||
);
|
||||
|
||||
VOID
|
||||
CalibrateLapicTimer (
|
||||
VOID
|
||||
);
|
||||
|
||||
extern EFI_BOOT_MODE mBootMode;
|
||||
|
||||
extern UINT8 mPhysMemAddressWidth;
|
||||
|
|
|
@ -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);
|
||||
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);
|
||||
}
|
||||
|
|
|
@ -52,6 +52,7 @@
|
|||
DebugLib
|
||||
HobLib
|
||||
IoLib
|
||||
LocalApicLib
|
||||
PciLib
|
||||
ResourcePublicationLib
|
||||
PeiServicesLib
|
||||
|
@ -59,6 +60,7 @@
|
|||
MtrrLib
|
||||
MemEncryptSevLib
|
||||
PcdLib
|
||||
SafeIntLib
|
||||
XenHypercallLib
|
||||
|
||||
[Pcd]
|
||||
|
|
Loading…
Reference in New Issue