Files
system76-edk2/OvmfPkg/XenPlatformPei/Xen.c
Anthony PERARD b37cfdd280 OvmfPkg/XenPlatformPei: Relocate shared_info page mapping
Unfortunately, Xen isn't ready to deal with mapping at the top of the
physical address space, so we relocate the mapping after the LAPIC
location.

See this thread about the issue with the mapping:
- https://lore.kernel.org/xen-devel/f8c4151a-6dac-d87c-ef46-eb35ada07bd9@suse.com/

The PhysicalAddressIdentityMapping() call isn't going to do anything
anymore since everything under 4GB is already mapped, but there is no
need to remove the call.

Cc: Jan Beulich <JBeulich@suse.com>
Cc: Andrew Cooper <andrew.cooper3@citrix.com>
Signed-off-by: Anthony PERARD <anthony.perard@citrix.com>
Message-Id: <20210628132337.46345-1-anthony.perard@citrix.com>
Acked-by: Laszlo Ersek <lersek@redhat.com>
[lersek@redhat.com: replace "CC:" with "Cc:", to pacify PatchCheck.py]
2021-06-29 13:40:43 +00:00

621 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;
}
}
}
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*)((UINTN)PcdGet32 (PcdCpuLocalApicBaseAddress) + SIZE_1MB);
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);
}