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
++) {
155 (UINTN
)mXenInfo
.HyperPages
+
156 (Index
<< EFI_PAGE_SHIFT
) + Index
161 // Find out the Xen version
164 AsmCpuid (mXenLeaf
+ 1, &XenVersion
, NULL
, NULL
, NULL
);
167 "Detected Xen version %d.%d\n",
171 mXenInfo
.VersionMajor
= (UINT16
)(XenVersion
>> 16);
172 mXenInfo
.VersionMinor
= (UINT16
)(XenVersion
& 0xFFFF);
175 // Check if there are information left by hvmloader
178 Info
= (EFI_XEN_OVMF_INFO
*)(UINTN
)OVMF_INFO_PHYSICAL_ADDRESS
;
180 // Copy the signature, and make it null-terminated.
185 (CHAR8
*)&Info
->Signature
,
186 sizeof (Info
->Signature
)
188 if (AsciiStrCmp (Sig
, "XenHVMOVMF") == 0) {
189 mXenHvmloaderInfo
= Info
;
191 mXenHvmloaderInfo
= NULL
;
194 mXenInfo
.RsdpPvh
= NULL
;
197 // Locate and use information from the start of day structure if we have
198 // booted via the PVH entry point.
201 PVHResetVectorData
= (VOID
*)(UINTN
)PcdGet32 (PcdXenPvhStartOfDayStructPtr
);
203 // That magic value is written in XenResetVector/Ia32/XenPVHMain.asm
205 if (PVHResetVectorData
[1] == SIGNATURE_32 ('X', 'P', 'V', 'H')) {
206 struct hvm_start_info
*HVMStartInfo
;
208 HVMStartInfo
= (VOID
*)(UINTN
)PVHResetVectorData
[0];
209 if (HVMStartInfo
->magic
== XEN_HVM_START_MAGIC_VALUE
) {
210 ASSERT (HVMStartInfo
->rsdp_paddr
!= 0);
211 if (HVMStartInfo
->rsdp_paddr
!= 0) {
212 mXenInfo
.RsdpPvh
= (VOID
*)(UINTN
)HVMStartInfo
->rsdp_paddr
;
224 // Initialize the XenHypercall library, now that the XenInfo HOB is
227 Status
= XenHypercallLibInit ();
228 ASSERT_RETURN_ERROR (Status
);
234 Figures out if we are running inside Xen HVM.
236 @retval TRUE Xen was detected
237 @retval FALSE Xen was not detected
251 Signature
[12] = '\0';
252 for (mXenLeaf
= 0x40000000; mXenLeaf
< 0x40010000; mXenLeaf
+= 0x100) {
256 (UINT32
*)&Signature
[0],
257 (UINT32
*)&Signature
[4],
258 (UINT32
*)&Signature
[8]
261 if (!AsciiStrCmp ((CHAR8
*)Signature
, "XenVMMXenVMM")) {
271 XenHvmloaderDetected (
275 return (mXenHvmloaderInfo
!= NULL
);
284 // This function should only be used after XenConnect
286 ASSERT (mXenInfo
.HyperPages
!= NULL
);
288 return mXenHvmloaderInfo
== NULL
;
292 XenPublishRamRegions (
296 EFI_E820_ENTRY64
*E820Map
;
297 UINT32 E820EntriesCount
;
299 EFI_E820_ENTRY64
*Entry
;
304 DEBUG ((DEBUG_INFO
, "Using memory map provided by Xen\n"));
307 // Parse RAM in E820 map
309 E820EntriesCount
= 0;
310 Status
= XenGetE820Map (&E820Map
, &E820EntriesCount
);
311 ASSERT_EFI_ERROR (Status
);
313 AddMemoryBaseSizeHob (0, 0xA0000);
315 // Video memory + Legacy BIOS region, to allow Linux to boot.
317 AddReservedMemoryBaseSizeHob (0xA0000, BASE_1MB
- 0xA0000, TRUE
);
319 LapicBase
= PcdGet32 (PcdCpuLocalApicBaseAddress
);
320 LapicEnd
= LapicBase
+ SIZE_1MB
;
321 AddIoMemoryRangeHob (LapicBase
, LapicEnd
);
323 for (Index
= 0; Index
< E820EntriesCount
; Index
++) {
329 Entry
= &E820Map
[Index
];
332 // Round up the start address, and round down the end address.
334 Base
= ALIGN_VALUE (Entry
->BaseAddr
, (UINT64
)EFI_PAGE_SIZE
);
335 End
= (Entry
->BaseAddr
+ Entry
->Length
) & ~(UINT64
)EFI_PAGE_MASK
;
338 // Ignore the first 1MB, this is handled before the loop.
340 if (Base
< BASE_1MB
) {
348 switch (Entry
->Type
) {
349 case EfiAcpiAddressRangeMemory
:
350 AddMemoryRangeHob (Base
, End
);
352 case EfiAcpiAddressRangeACPI
:
353 AddReservedMemoryRangeHob (Base
, End
, FALSE
);
355 case EfiAcpiAddressRangeReserved
:
357 // hvmloader marks a range that overlaps with the local APIC memory
358 // mapped region as reserved, but CpuDxe wants it as mapped IO. We
359 // have already added it as mapped IO, so skip it here.
363 // add LAPIC predecessor range, if any
366 ReservedEnd
= MIN (End
, LapicBase
);
367 if (ReservedBase
< ReservedEnd
) {
368 AddReservedMemoryRangeHob (ReservedBase
, ReservedEnd
, FALSE
);
372 // add LAPIC successor range, if any
374 ReservedBase
= MAX (Base
, LapicEnd
);
376 if (ReservedBase
< ReservedEnd
) {
377 AddReservedMemoryRangeHob (ReservedBase
, ReservedEnd
, FALSE
);
388 PhysicalAddressIdentityMapping (
389 IN EFI_PHYSICAL_ADDRESS AddressToMap
393 PAGE_MAP_AND_DIRECTORY_POINTER
*L4
, *L3
;
394 PAGE_TABLE_ENTRY
*PageTable
;
396 DEBUG ((DEBUG_INFO
, "Mapping 1:1 of address 0x%lx\n", (UINT64
)AddressToMap
));
398 // L4 / Top level Page Directory Pointers
400 L4
= (VOID
*)(UINTN
)PcdGet32 (PcdOvmfSecPageTablesBase
);
401 Index
= PML4_OFFSET (AddressToMap
);
403 if (!L4
[Index
].Bits
.Present
) {
404 L3
= AllocatePages (1);
406 return EFI_OUT_OF_RESOURCES
;
409 ZeroMem (L3
, EFI_PAGE_SIZE
);
411 L4
[Index
].Bits
.ReadWrite
= 1;
412 L4
[Index
].Bits
.Accessed
= 1;
413 L4
[Index
].Bits
.PageTableBaseAddress
= (EFI_PHYSICAL_ADDRESS
)L3
>> 12;
414 L4
[Index
].Bits
.Present
= 1;
417 // L3 / Next level Page Directory Pointers
419 L3
= (VOID
*)(EFI_PHYSICAL_ADDRESS
)(L4
[Index
].Bits
.PageTableBaseAddress
<< 12);
420 Index
= PDP_OFFSET (AddressToMap
);
422 if (!L3
[Index
].Bits
.Present
) {
423 PageTable
= AllocatePages (1);
424 if (PageTable
== NULL
) {
425 return EFI_OUT_OF_RESOURCES
;
428 ZeroMem (PageTable
, EFI_PAGE_SIZE
);
430 L3
[Index
].Bits
.ReadWrite
= 1;
431 L3
[Index
].Bits
.Accessed
= 1;
432 L3
[Index
].Bits
.PageTableBaseAddress
= (EFI_PHYSICAL_ADDRESS
)PageTable
>> 12;
433 L3
[Index
].Bits
.Present
= 1;
436 // L2 / Page Table Entries
438 PageTable
= (VOID
*)(EFI_PHYSICAL_ADDRESS
)(L3
[Index
].Bits
.PageTableBaseAddress
<< 12);
439 Index
= PDE_OFFSET (AddressToMap
);
441 if (!PageTable
[Index
].Bits
.Present
) {
442 PageTable
[Index
].Bits
.ReadWrite
= 1;
443 PageTable
[Index
].Bits
.Accessed
= 1;
444 PageTable
[Index
].Bits
.Dirty
= 1;
445 PageTable
[Index
].Bits
.MustBe1
= 1;
446 PageTable
[Index
].Bits
.PageTableBaseAddress
= AddressToMap
>> 21;
447 PageTable
[Index
].Bits
.Present
= 1;
461 xen_add_to_physmap_t Parameters
;
464 Parameters
.domid
= DOMID_SELF
;
465 Parameters
.space
= XENMAPSPACE_shared_info
;
467 Parameters
.gpfn
= (UINTN
)PagePtr
>> EFI_PAGE_SHIFT
;
468 ReturnCode
= XenHypercallMemoryOp (XENMEM_add_to_physmap
, &Parameters
);
469 if (ReturnCode
!= 0) {
470 return EFI_NO_MAPPING
;
482 xen_remove_from_physmap_t Parameters
;
485 Parameters
.domid
= DOMID_SELF
;
486 Parameters
.gpfn
= (UINTN
)PagePtr
>> EFI_PAGE_SHIFT
;
487 ReturnCode
= XenHypercallMemoryOp (XENMEM_remove_from_physmap
, &Parameters
);
488 ASSERT (ReturnCode
== 0);
494 IN XEN_VCPU_TIME_INFO
*VcpuTime
498 UINT32 TscToSystemMultiplier
;
503 Version
= VcpuTime
->Version
;
505 TscToSystemMultiplier
= VcpuTime
->TscToSystemMultiplier
;
506 TscShift
= VcpuTime
->TscShift
;
508 } while (((Version
& 1) != 0) && (Version
!= VcpuTime
->Version
));
510 CpuFreq
= DivU64x32 (LShiftU64 (1000000000ULL, 32), TscToSystemMultiplier
);
512 CpuFreq
= RShiftU64 (CpuFreq
, TscShift
);
514 CpuFreq
= LShiftU64 (CpuFreq
, -TscShift
);
523 IN XEN_VCPU_TIME_INFO
*VcpuTimeInfo
,
532 RETURN_STATUS Status
;
534 Tick
= AsmReadTsc ();
536 CpuFreq
= GetCpuFreq (VcpuTimeInfo
);
537 Status
= SafeUint64Mult (DelayNs
, CpuFreq
, &Delay
);
538 if (EFI_ERROR (Status
)) {
541 "XenDelay (%lu ns): delay too big in relation to CPU freq %lu Hz\n",
545 ASSERT_EFI_ERROR (Status
);
549 DelayTick
= DivU64x32 (Delay
, 1000000000);
551 NewTick
= Tick
+ DelayTick
;
554 // Check for overflow
556 if (NewTick
< Tick
) {
558 // Overflow, wait for TSC to also overflow
560 while (AsmReadTsc () >= Tick
) {
565 while (AsmReadTsc () <= NewTick
) {
571 Calculate the frequency of the Local Apic Timer
574 CalibrateLapicTimer (
578 XEN_SHARED_INFO
*SharedInfo
;
579 XEN_VCPU_TIME_INFO
*VcpuTimeInfo
;
580 UINT32 TimerTick
, TimerTick2
, DiffTimer
;
581 UINT64 TscTick
, TscTick2
;
586 SharedInfo
= (VOID
*)((UINTN
)PcdGet32 (PcdCpuLocalApicBaseAddress
) + SIZE_1MB
);
587 Status
= PhysicalAddressIdentityMapping ((EFI_PHYSICAL_ADDRESS
)SharedInfo
);
588 if (EFI_ERROR (Status
)) {
591 "Failed to add page table entry for Xen shared info page: %r\n",
594 ASSERT_EFI_ERROR (Status
);
598 Status
= MapSharedInfoPage (SharedInfo
);
599 if (EFI_ERROR (Status
)) {
602 "Failed to map Xen's shared info page: %r\n",
605 ASSERT_EFI_ERROR (Status
);
609 VcpuTimeInfo
= &SharedInfo
->VcpuInfo
[0].Time
;
611 InitializeApicTimer (1, MAX_UINT32
, TRUE
, 0);
612 DisableApicTimerInterrupt ();
614 TimerTick
= GetApicTimerCurrentCount ();
615 TscTick
= AsmReadTsc ();
616 XenDelay (VcpuTimeInfo
, 1000000ULL);
617 TimerTick2
= GetApicTimerCurrentCount ();
618 TscTick2
= AsmReadTsc ();
620 DiffTimer
= TimerTick
- TimerTick2
;
621 Status
= SafeUint64Mult (GetCpuFreq (VcpuTimeInfo
), DiffTimer
, &Dividend
);
622 if (EFI_ERROR (Status
)) {
623 DEBUG ((DEBUG_ERROR
, "overflow while calculating APIC frequency\n"));
626 "CPU freq: %lu Hz; APIC timer tick count for 1 ms: %u\n",
627 GetCpuFreq (VcpuTimeInfo
),
630 ASSERT_EFI_ERROR (Status
);
634 Freq
= DivU64x64Remainder (Dividend
, TscTick2
- TscTick
, NULL
);
635 DEBUG ((DEBUG_INFO
, "APIC Freq % 8lu Hz\n", Freq
));
637 ASSERT (Freq
<= MAX_UINT32
);
638 Status
= PcdSet32S (PcdFSBClock
, (UINT32
)Freq
);
639 ASSERT_EFI_ERROR (Status
);
641 UnmapXenPage (SharedInfo
);