2 Xen Platform PEI support
4 Copyright (c) 2006 - 2016, Intel Corporation. All rights reserved.<BR>
5 Copyright (c) 2011, Andrei Warkentin <andreiw@motorola.com>
6 Copyright (c) 2019, Citrix Systems, Inc.
8 SPDX-License-Identifier: BSD-2-Clause-Patent
13 // The package level header files this module uses
18 // The Library classes this module consumes
20 #include <Library/BaseMemoryLib.h>
21 #include <Library/CpuLib.h>
22 #include <Library/DebugLib.h>
23 #include <Library/HobLib.h>
24 #include <Library/LocalApicLib.h>
25 #include <Library/MemoryAllocationLib.h>
26 #include <Library/PcdLib.h>
27 #include <Library/SafeIntLib.h>
28 #include <Guid/XenInfo.h>
29 #include <IndustryStandard/E820.h>
30 #include <Library/ResourcePublicationLib.h>
31 #include <Library/MtrrLib.h>
32 #include <IndustryStandard/PageTable.h>
33 #include <IndustryStandard/Xen/arch-x86/hvm/start_info.h>
34 #include <Library/XenHypercallLib.h>
35 #include <IndustryStandard/Xen/memory.h>
40 STATIC UINT32 mXenLeaf
= 0;
42 EFI_XEN_INFO mXenInfo
;
45 // Location of the firmware info struct setup by hvmloader.
46 // Only the E820 table is used by OVMF.
48 EFI_XEN_OVMF_INFO
*mXenHvmloaderInfo
;
49 STATIC EFI_E820_ENTRY64 mE820Entries
[128];
50 STATIC UINT32 mE820EntriesCount
;
53 Returns E820 map provided by Xen
55 @param Entries Pointer to E820 map
56 @param Count Number of entries
62 EFI_E820_ENTRY64
**Entries
,
67 xen_memory_map_t Parameters
;
70 EFI_E820_ENTRY64 TmpEntry
;
73 // Get E820 produced by hvmloader
75 if (mXenHvmloaderInfo
!= NULL
) {
76 ASSERT (mXenHvmloaderInfo
->E820
< MAX_ADDRESS
);
77 *Entries
= (EFI_E820_ENTRY64
*)(UINTN
) mXenHvmloaderInfo
->E820
;
78 *Count
= mXenHvmloaderInfo
->E820EntriesCount
;
84 // Otherwise, get the E820 table from the Xen hypervisor
87 if (mE820EntriesCount
> 0) {
88 *Entries
= mE820Entries
;
89 *Count
= mE820EntriesCount
;
93 Parameters
.nr_entries
= 128;
94 set_xen_guest_handle (Parameters
.buffer
, mE820Entries
);
97 ReturnCode
= XenHypercallMemoryOp (XENMEM_memory_map
, &Parameters
);
98 ASSERT (ReturnCode
== 0);
100 mE820EntriesCount
= Parameters
.nr_entries
;
105 for (LoopIndex
= 1; LoopIndex
< mE820EntriesCount
; LoopIndex
++) {
106 for (Index
= LoopIndex
; Index
< mE820EntriesCount
; Index
++) {
107 if (mE820Entries
[Index
- 1].BaseAddr
> mE820Entries
[Index
].BaseAddr
) {
108 TmpEntry
= mE820Entries
[Index
];
109 mE820Entries
[Index
] = mE820Entries
[Index
- 1];
110 mE820Entries
[Index
- 1] = TmpEntry
;
115 *Count
= mE820EntriesCount
;
116 *Entries
= mE820Entries
;
122 Connects to the Hypervisor.
133 UINT32 TransferPages
;
135 EFI_XEN_OVMF_INFO
*Info
;
136 CHAR8 Sig
[sizeof (Info
->Signature
) + 1];
137 UINT32
*PVHResetVectorData
;
138 RETURN_STATUS Status
;
140 ASSERT (mXenLeaf
!= 0);
143 // Prepare HyperPages to be able to make hypercalls
146 AsmCpuid (mXenLeaf
+ 2, &TransferPages
, &TransferReg
, NULL
, NULL
);
147 mXenInfo
.HyperPages
= AllocatePages (TransferPages
);
148 if (!mXenInfo
.HyperPages
) {
149 return EFI_OUT_OF_RESOURCES
;
152 for (Index
= 0; Index
< TransferPages
; Index
++) {
153 AsmWriteMsr64 (TransferReg
,
154 (UINTN
) mXenInfo
.HyperPages
+
155 (Index
<< EFI_PAGE_SHIFT
) + Index
);
159 // Find out the Xen version
162 AsmCpuid (mXenLeaf
+ 1, &XenVersion
, NULL
, NULL
, NULL
);
163 DEBUG ((DEBUG_ERROR
, "Detected Xen version %d.%d\n",
164 XenVersion
>> 16, XenVersion
& 0xFFFF));
165 mXenInfo
.VersionMajor
= (UINT16
)(XenVersion
>> 16);
166 mXenInfo
.VersionMinor
= (UINT16
)(XenVersion
& 0xFFFF);
169 // Check if there are information left by hvmloader
172 Info
= (EFI_XEN_OVMF_INFO
*)(UINTN
) OVMF_INFO_PHYSICAL_ADDRESS
;
174 // Copy the signature, and make it null-terminated.
176 AsciiStrnCpyS (Sig
, sizeof (Sig
), (CHAR8
*) &Info
->Signature
,
177 sizeof (Info
->Signature
));
178 if (AsciiStrCmp (Sig
, "XenHVMOVMF") == 0) {
179 mXenHvmloaderInfo
= Info
;
181 mXenHvmloaderInfo
= NULL
;
184 mXenInfo
.RsdpPvh
= NULL
;
187 // Locate and use information from the start of day structure if we have
188 // booted via the PVH entry point.
191 PVHResetVectorData
= (VOID
*)(UINTN
) PcdGet32 (PcdXenPvhStartOfDayStructPtr
);
193 // That magic value is written in XenResetVector/Ia32/XenPVHMain.asm
195 if (PVHResetVectorData
[1] == SIGNATURE_32 ('X', 'P', 'V', 'H')) {
196 struct hvm_start_info
*HVMStartInfo
;
198 HVMStartInfo
= (VOID
*)(UINTN
) PVHResetVectorData
[0];
199 if (HVMStartInfo
->magic
== XEN_HVM_START_MAGIC_VALUE
) {
200 ASSERT (HVMStartInfo
->rsdp_paddr
!= 0);
201 if (HVMStartInfo
->rsdp_paddr
!= 0) {
202 mXenInfo
.RsdpPvh
= (VOID
*)(UINTN
)HVMStartInfo
->rsdp_paddr
;
214 // Initialize the XenHypercall library, now that the XenInfo HOB is
217 Status
= XenHypercallLibInit ();
218 ASSERT_RETURN_ERROR (Status
);
224 Figures out if we are running inside Xen HVM.
226 @retval TRUE Xen was detected
227 @retval FALSE Xen was not detected
241 Signature
[12] = '\0';
242 for (mXenLeaf
= 0x40000000; mXenLeaf
< 0x40010000; mXenLeaf
+= 0x100) {
245 (UINT32
*) &Signature
[0],
246 (UINT32
*) &Signature
[4],
247 (UINT32
*) &Signature
[8]);
249 if (!AsciiStrCmp ((CHAR8
*) Signature
, "XenVMMXenVMM")) {
259 XenHvmloaderDetected (
263 return (mXenHvmloaderInfo
!= NULL
);
272 // This function should only be used after XenConnect
274 ASSERT (mXenInfo
.HyperPages
!= NULL
);
276 return mXenHvmloaderInfo
== NULL
;
280 XenPublishRamRegions (
284 EFI_E820_ENTRY64
*E820Map
;
285 UINT32 E820EntriesCount
;
287 EFI_E820_ENTRY64
*Entry
;
293 DEBUG ((DEBUG_INFO
, "Using memory map provided by Xen\n"));
296 // Parse RAM in E820 map
298 E820EntriesCount
= 0;
299 Status
= XenGetE820Map (&E820Map
, &E820EntriesCount
);
300 ASSERT_EFI_ERROR (Status
);
302 AddMemoryBaseSizeHob (0, 0xA0000);
304 // Video memory + Legacy BIOS region, to allow Linux to boot.
306 AddReservedMemoryBaseSizeHob (0xA0000, BASE_1MB
- 0xA0000, TRUE
);
308 LapicBase
= PcdGet32 (PcdCpuLocalApicBaseAddress
);
309 LapicEnd
= LapicBase
+ SIZE_1MB
;
310 AddIoMemoryRangeHob (LapicBase
, LapicEnd
);
312 for (Index
= 0; Index
< E820EntriesCount
; Index
++) {
318 Entry
= &E820Map
[Index
];
321 // Round up the start address, and round down the end address.
323 Base
= ALIGN_VALUE (Entry
->BaseAddr
, (UINT64
)EFI_PAGE_SIZE
);
324 End
= (Entry
->BaseAddr
+ Entry
->Length
) & ~(UINT64
)EFI_PAGE_MASK
;
327 // Ignore the first 1MB, this is handled before the loop.
329 if (Base
< BASE_1MB
) {
336 switch (Entry
->Type
) {
337 case EfiAcpiAddressRangeMemory
:
338 AddMemoryRangeHob (Base
, End
);
340 case EfiAcpiAddressRangeACPI
:
341 AddReservedMemoryRangeHob (Base
, End
, FALSE
);
343 case EfiAcpiAddressRangeReserved
:
345 // hvmloader marks a range that overlaps with the local APIC memory
346 // mapped region as reserved, but CpuDxe wants it as mapped IO. We
347 // have already added it as mapped IO, so skip it here.
351 // add LAPIC predecessor range, if any
354 ReservedEnd
= MIN (End
, LapicBase
);
355 if (ReservedBase
< ReservedEnd
) {
356 AddReservedMemoryRangeHob (ReservedBase
, ReservedEnd
, FALSE
);
360 // add LAPIC successor range, if any
362 ReservedBase
= MAX (Base
, LapicEnd
);
364 if (ReservedBase
< ReservedEnd
) {
365 AddReservedMemoryRangeHob (ReservedBase
, ReservedEnd
, FALSE
);
376 Perform Xen PEI initialization.
378 @return EFI_SUCCESS Xen initialized successfully
379 @return EFI_NOT_FOUND Not running under Xen
387 RETURN_STATUS PcdStatus
;
389 PcdStatus
= PcdSetBoolS (PcdPciDisableBusEnumeration
, TRUE
);
390 ASSERT_RETURN_ERROR (PcdStatus
);
396 PhysicalAddressIdentityMapping (
397 IN EFI_PHYSICAL_ADDRESS AddressToMap
401 PAGE_MAP_AND_DIRECTORY_POINTER
*L4
, *L3
;
402 PAGE_TABLE_ENTRY
*PageTable
;
404 DEBUG ((DEBUG_INFO
, "Mapping 1:1 of address 0x%lx\n", (UINT64
)AddressToMap
));
406 // L4 / Top level Page Directory Pointers
408 L4
= (VOID
*)(UINTN
)PcdGet32 (PcdOvmfSecPageTablesBase
);
409 Index
= PML4_OFFSET (AddressToMap
);
411 if (!L4
[Index
].Bits
.Present
) {
412 L3
= AllocatePages (1);
414 return EFI_OUT_OF_RESOURCES
;
417 ZeroMem (L3
, EFI_PAGE_SIZE
);
419 L4
[Index
].Bits
.ReadWrite
= 1;
420 L4
[Index
].Bits
.Accessed
= 1;
421 L4
[Index
].Bits
.PageTableBaseAddress
= (EFI_PHYSICAL_ADDRESS
)L3
>> 12;
422 L4
[Index
].Bits
.Present
= 1;
425 // L3 / Next level Page Directory Pointers
427 L3
= (VOID
*)(EFI_PHYSICAL_ADDRESS
)(L4
[Index
].Bits
.PageTableBaseAddress
<< 12);
428 Index
= PDP_OFFSET (AddressToMap
);
430 if (!L3
[Index
].Bits
.Present
) {
431 PageTable
= AllocatePages (1);
432 if (PageTable
== NULL
) {
433 return EFI_OUT_OF_RESOURCES
;
436 ZeroMem (PageTable
, EFI_PAGE_SIZE
);
438 L3
[Index
].Bits
.ReadWrite
= 1;
439 L3
[Index
].Bits
.Accessed
= 1;
440 L3
[Index
].Bits
.PageTableBaseAddress
= (EFI_PHYSICAL_ADDRESS
)PageTable
>> 12;
441 L3
[Index
].Bits
.Present
= 1;
444 // L2 / Page Table Entries
446 PageTable
= (VOID
*)(EFI_PHYSICAL_ADDRESS
)(L3
[Index
].Bits
.PageTableBaseAddress
<< 12);
447 Index
= PDE_OFFSET (AddressToMap
);
449 if (!PageTable
[Index
].Bits
.Present
) {
450 PageTable
[Index
].Bits
.ReadWrite
= 1;
451 PageTable
[Index
].Bits
.Accessed
= 1;
452 PageTable
[Index
].Bits
.Dirty
= 1;
453 PageTable
[Index
].Bits
.MustBe1
= 1;
454 PageTable
[Index
].Bits
.PageTableBaseAddress
= AddressToMap
>> 21;
455 PageTable
[Index
].Bits
.Present
= 1;
469 xen_add_to_physmap_t Parameters
;
472 Parameters
.domid
= DOMID_SELF
;
473 Parameters
.space
= XENMAPSPACE_shared_info
;
475 Parameters
.gpfn
= (UINTN
)PagePtr
>> EFI_PAGE_SHIFT
;
476 ReturnCode
= XenHypercallMemoryOp (XENMEM_add_to_physmap
, &Parameters
);
477 if (ReturnCode
!= 0) {
478 return EFI_NO_MAPPING
;
489 xen_remove_from_physmap_t Parameters
;
492 Parameters
.domid
= DOMID_SELF
;
493 Parameters
.gpfn
= (UINTN
)PagePtr
>> EFI_PAGE_SHIFT
;
494 ReturnCode
= XenHypercallMemoryOp (XENMEM_remove_from_physmap
, &Parameters
);
495 ASSERT (ReturnCode
== 0);
502 IN XEN_VCPU_TIME_INFO
*VcpuTime
506 UINT32 TscToSystemMultiplier
;
511 Version
= VcpuTime
->Version
;
513 TscToSystemMultiplier
= VcpuTime
->TscToSystemMultiplier
;
514 TscShift
= VcpuTime
->TscShift
;
516 } while (((Version
& 1) != 0) && (Version
!= VcpuTime
->Version
));
518 CpuFreq
= DivU64x32 (LShiftU64 (1000000000ULL, 32), TscToSystemMultiplier
);
520 CpuFreq
= RShiftU64 (CpuFreq
, TscShift
);
522 CpuFreq
= LShiftU64 (CpuFreq
, -TscShift
);
530 IN XEN_VCPU_TIME_INFO
*VcpuTimeInfo
,
539 RETURN_STATUS Status
;
541 Tick
= AsmReadTsc ();
543 CpuFreq
= GetCpuFreq (VcpuTimeInfo
);
544 Status
= SafeUint64Mult (DelayNs
, CpuFreq
, &Delay
);
545 if (EFI_ERROR (Status
)) {
547 "XenDelay (%lu ns): delay too big in relation to CPU freq %lu Hz\n",
549 ASSERT_EFI_ERROR (Status
);
553 DelayTick
= DivU64x32 (Delay
, 1000000000);
555 NewTick
= Tick
+ DelayTick
;
558 // Check for overflow
560 if (NewTick
< Tick
) {
562 // Overflow, wait for TSC to also overflow
564 while (AsmReadTsc () >= Tick
) {
569 while (AsmReadTsc () <= NewTick
) {
576 Calculate the frequency of the Local Apic Timer
579 CalibrateLapicTimer (
583 XEN_SHARED_INFO
*SharedInfo
;
584 XEN_VCPU_TIME_INFO
*VcpuTimeInfo
;
585 UINT32 TimerTick
, TimerTick2
, DiffTimer
;
586 UINT64 TscTick
, TscTick2
;
592 SharedInfo
= (VOID
*)((1ULL << mPhysMemAddressWidth
) - EFI_PAGE_SIZE
);
593 Status
= PhysicalAddressIdentityMapping ((EFI_PHYSICAL_ADDRESS
)SharedInfo
);
594 if (EFI_ERROR (Status
)) {
596 "Failed to add page table entry for Xen shared info page: %r\n",
598 ASSERT_EFI_ERROR (Status
);
602 Status
= MapSharedInfoPage (SharedInfo
);
603 if (EFI_ERROR (Status
)) {
604 DEBUG ((DEBUG_ERROR
, "Failed to map Xen's shared info page: %r\n",
606 ASSERT_EFI_ERROR (Status
);
610 VcpuTimeInfo
= &SharedInfo
->VcpuInfo
[0].Time
;
612 InitializeApicTimer (1, MAX_UINT32
, TRUE
, 0);
613 DisableApicTimerInterrupt ();
615 TimerTick
= GetApicTimerCurrentCount ();
616 TscTick
= AsmReadTsc ();
617 XenDelay (VcpuTimeInfo
, 1000000ULL);
618 TimerTick2
= GetApicTimerCurrentCount ();
619 TscTick2
= AsmReadTsc ();
622 DiffTimer
= TimerTick
- TimerTick2
;
623 Status
= SafeUint64Mult (GetCpuFreq (VcpuTimeInfo
), DiffTimer
, &Dividend
);
624 if (EFI_ERROR (Status
)) {
625 DEBUG ((DEBUG_ERROR
, "overflow while calculating APIC frequency\n"));
626 DEBUG ((DEBUG_ERROR
, "CPU freq: %lu Hz; APIC timer tick count for 1 ms: %u\n",
627 GetCpuFreq (VcpuTimeInfo
), DiffTimer
));
628 ASSERT_EFI_ERROR (Status
);
632 Freq
= DivU64x64Remainder (Dividend
, TscTick2
- TscTick
, NULL
);
633 DEBUG ((DEBUG_INFO
, "APIC Freq % 8lu Hz\n", Freq
));
635 UnmapXenPage (SharedInfo
);