123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642 |
- /**@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);
- }
|