TianoCore EDK2 master
Loading...
Searching...
No Matches
Xen.c
Go to the documentation of this file.
1
12//
13// The package level header files this module uses
14//
15#include <PiPei.h>
16
17//
18// The Library classes this module consumes
19//
21#include <Library/CpuLib.h>
22#include <Library/DebugLib.h>
23#include <Library/HobLib.h>
26#include <Library/PcdLib.h>
27#include <Library/SafeIntLib.h>
28#include <Guid/XenInfo.h>
31#include <Library/MtrrLib.h>
33#include <IndustryStandard/Xen/arch-x86/hvm/start_info.h>
35#include <IndustryStandard/Xen/memory.h>
36
37#include "Platform.h"
38#include "Xen.h"
39
40STATIC UINT32 mXenLeaf = 0;
41
42EFI_XEN_INFO mXenInfo;
43
44//
45// Location of the firmware info struct setup by hvmloader.
46// Only the E820 table is used by OVMF.
47//
48EFI_XEN_OVMF_INFO *mXenHvmloaderInfo;
49STATIC EFI_E820_ENTRY64 mE820Entries[128];
50STATIC UINT32 mE820EntriesCount;
51
62 EFI_E820_ENTRY64 **Entries,
63 UINT32 *Count
64 )
65{
66 INTN ReturnCode;
67 xen_memory_map_t Parameters;
68 UINTN LoopIndex;
69 UINTN Index;
70 EFI_E820_ENTRY64 TmpEntry;
71
72 //
73 // Get E820 produced by hvmloader
74 //
75 if (mXenHvmloaderInfo != NULL) {
76 ASSERT (mXenHvmloaderInfo->E820 < MAX_ADDRESS);
77 *Entries = (EFI_E820_ENTRY64 *)(UINTN)mXenHvmloaderInfo->E820;
78 *Count = mXenHvmloaderInfo->E820EntriesCount;
79
80 return EFI_SUCCESS;
81 }
82
83 //
84 // Otherwise, get the E820 table from the Xen hypervisor
85 //
86
87 if (mE820EntriesCount > 0) {
88 *Entries = mE820Entries;
89 *Count = mE820EntriesCount;
90 return EFI_SUCCESS;
91 }
92
93 Parameters.nr_entries = 128;
94 set_xen_guest_handle (Parameters.buffer, mE820Entries);
95
96 // Returns a errno
97 ReturnCode = XenHypercallMemoryOp (XENMEM_memory_map, &Parameters);
98 ASSERT (ReturnCode == 0);
99
100 mE820EntriesCount = Parameters.nr_entries;
101
102 //
103 // Sort E820 entries
104 //
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;
111 }
112 }
113 }
114
115 *Count = mE820EntriesCount;
116 *Entries = mE820Entries;
117
118 return EFI_SUCCESS;
119}
120
129 )
130{
131 UINT32 XenVersion;
132 EFI_XEN_OVMF_INFO *Info;
133 CHAR8 Sig[sizeof (Info->Signature) + 1];
134 UINT32 *PVHResetVectorData;
135 RETURN_STATUS Status;
136
137 ASSERT (mXenLeaf != 0);
138
139 //
140 // Find out the Xen version
141 //
142
143 AsmCpuid (mXenLeaf + 1, &XenVersion, NULL, NULL, NULL);
144 DEBUG ((
145 DEBUG_ERROR,
146 "Detected Xen version %d.%d\n",
147 XenVersion >> 16,
148 XenVersion & 0xFFFF
149 ));
150 mXenInfo.VersionMajor = (UINT16)(XenVersion >> 16);
151 mXenInfo.VersionMinor = (UINT16)(XenVersion & 0xFFFF);
152
153 //
154 // Check if there are information left by hvmloader
155 //
156
157 Info = (EFI_XEN_OVMF_INFO *)(UINTN)OVMF_INFO_PHYSICAL_ADDRESS;
158 //
159 // Copy the signature, and make it null-terminated.
160 //
162 Sig,
163 sizeof (Sig),
164 (CHAR8 *)&Info->Signature,
165 sizeof (Info->Signature)
166 );
167 if (AsciiStrCmp (Sig, "XenHVMOVMF") == 0) {
168 mXenHvmloaderInfo = Info;
169 } else {
170 mXenHvmloaderInfo = NULL;
171 }
172
173 mXenInfo.RsdpPvh = NULL;
174
175 //
176 // Locate and use information from the start of day structure if we have
177 // booted via the PVH entry point.
178 //
179
180 PVHResetVectorData = (VOID *)(UINTN)PcdGet32 (PcdXenPvhStartOfDayStructPtr);
181 //
182 // That magic value is written in XenResetVector/Ia32/XenPVHMain.asm
183 //
184 if (PVHResetVectorData[1] == SIGNATURE_32 ('X', 'P', 'V', 'H')) {
185 struct hvm_start_info *HVMStartInfo;
186
187 HVMStartInfo = (VOID *)(UINTN)PVHResetVectorData[0];
188 if (HVMStartInfo->magic == XEN_HVM_START_MAGIC_VALUE) {
189 ASSERT (HVMStartInfo->rsdp_paddr != 0);
190 if (HVMStartInfo->rsdp_paddr != 0) {
191 mXenInfo.RsdpPvh = (VOID *)(UINTN)HVMStartInfo->rsdp_paddr;
192 }
193 }
194 }
195
197 &gEfiXenInfoGuid,
198 &mXenInfo,
199 sizeof (mXenInfo)
200 );
201
202 //
203 // Initialize the XenHypercall library, now that the XenInfo HOB is
204 // available
205 //
206 Status = XenHypercallLibInit ();
207 ASSERT_RETURN_ERROR (Status);
208
209 return EFI_SUCCESS;
210}
211
219BOOLEAN
221 VOID
222 )
223{
224 UINT8 Signature[13];
225
226 if (mXenLeaf != 0) {
227 return TRUE;
228 }
229
230 Signature[12] = '\0';
231 for (mXenLeaf = 0x40000000; mXenLeaf < 0x40010000; mXenLeaf += 0x100) {
232 AsmCpuid (
233 mXenLeaf,
234 NULL,
235 (UINT32 *)&Signature[0],
236 (UINT32 *)&Signature[4],
237 (UINT32 *)&Signature[8]
238 );
239
240 if (!AsciiStrCmp ((CHAR8 *)Signature, "XenVMMXenVMM")) {
241 return TRUE;
242 }
243 }
244
245 mXenLeaf = 0;
246 return FALSE;
247}
248
249BOOLEAN
250XenHvmloaderDetected (
251 VOID
252 )
253{
254 return (mXenHvmloaderInfo != NULL);
255}
256
257BOOLEAN
259 VOID
260 )
261{
262 //
263 // This function should only be used after XenConnect
264 //
265 ASSERT (mXenInfo.VersionMajor);
266
267 return mXenHvmloaderInfo == NULL;
268}
269
270VOID
271XenPublishRamRegions (
272 VOID
273 )
274{
275 EFI_E820_ENTRY64 *E820Map;
276 UINT32 E820EntriesCount;
277 EFI_STATUS Status;
278 EFI_E820_ENTRY64 *Entry;
279 UINTN Index;
280 UINT64 LapicBase;
281 UINT64 LapicEnd;
282
283 DEBUG ((DEBUG_INFO, "Using memory map provided by Xen\n"));
284
285 //
286 // Parse RAM in E820 map
287 //
288 E820EntriesCount = 0;
289 Status = XenGetE820Map (&E820Map, &E820EntriesCount);
290 ASSERT_EFI_ERROR (Status);
291
292 AddMemoryBaseSizeHob (0, 0xA0000);
293 //
294 // Video memory + Legacy BIOS region, to allow Linux to boot.
295 //
296 AddReservedMemoryBaseSizeHob (0xA0000, BASE_1MB - 0xA0000, TRUE);
297
298 LapicBase = PcdGet32 (PcdCpuLocalApicBaseAddress);
299 LapicEnd = LapicBase + SIZE_1MB;
300 AddIoMemoryRangeHob (LapicBase, LapicEnd);
301
302 for (Index = 0; Index < E820EntriesCount; Index++) {
303 UINT64 Base;
304 UINT64 End;
305 UINT64 ReservedBase;
306 UINT64 ReservedEnd;
307
308 Entry = &E820Map[Index];
309
310 //
311 // Round up the start address, and round down the end address.
312 //
313 Base = ALIGN_VALUE (Entry->BaseAddr, (UINT64)EFI_PAGE_SIZE);
314 End = (Entry->BaseAddr + Entry->Length) & ~(UINT64)EFI_PAGE_MASK;
315
316 //
317 // Ignore the first 1MB, this is handled before the loop.
318 //
319 if (Base < BASE_1MB) {
320 Base = BASE_1MB;
321 }
322
323 if (Base >= End) {
324 continue;
325 }
326
327 switch (Entry->Type) {
328 case EfiAcpiAddressRangeMemory:
329 AddMemoryRangeHob (Base, End);
330 break;
331 case EfiAcpiAddressRangeACPI:
332 AddReservedMemoryRangeHob (Base, End, FALSE);
333 break;
334 case EfiAcpiAddressRangeReserved:
335 //
336 // hvmloader marks a range that overlaps with the local APIC memory
337 // mapped region as reserved, but CpuDxe wants it as mapped IO. We
338 // have already added it as mapped IO, so skip it here.
339 //
340
341 //
342 // add LAPIC predecessor range, if any
343 //
344 ReservedBase = Base;
345 ReservedEnd = MIN (End, LapicBase);
346 if (ReservedBase < ReservedEnd) {
347 AddReservedMemoryRangeHob (ReservedBase, ReservedEnd, FALSE);
348 }
349
350 //
351 // add LAPIC successor range, if any
352 //
353 ReservedBase = MAX (Base, LapicEnd);
354 ReservedEnd = End;
355 if (ReservedBase < ReservedEnd) {
356 AddReservedMemoryRangeHob (ReservedBase, ReservedEnd, FALSE);
357 }
358
359 break;
360 default:
361 break;
362 }
363 }
364}
365
367PhysicalAddressIdentityMapping (
368 IN EFI_PHYSICAL_ADDRESS AddressToMap
369 )
370{
371 INTN Index;
373 PAGE_TABLE_ENTRY *PageTable;
374
375 DEBUG ((DEBUG_INFO, "Mapping 1:1 of address 0x%lx\n", (UINT64)AddressToMap));
376
377 // L4 / Top level Page Directory Pointers
378
379 L4 = (VOID *)(UINTN)PcdGet32 (PcdOvmfSecPageTablesBase);
380 Index = PML4_OFFSET (AddressToMap);
381
382 if (!L4[Index].Bits.Present) {
383 L3 = AllocatePages (1);
384 if (L3 == NULL) {
385 return EFI_OUT_OF_RESOURCES;
386 }
387
388 ZeroMem (L3, EFI_PAGE_SIZE);
389
390 L4[Index].Bits.ReadWrite = 1;
391 L4[Index].Bits.Accessed = 1;
392 L4[Index].Bits.PageTableBaseAddress = (EFI_PHYSICAL_ADDRESS)L3 >> 12;
393 L4[Index].Bits.Present = 1;
394 }
395
396 // L3 / Next level Page Directory Pointers
397
398 L3 = (VOID *)(EFI_PHYSICAL_ADDRESS)(L4[Index].Bits.PageTableBaseAddress << 12);
399 Index = PDP_OFFSET (AddressToMap);
400
401 if (!L3[Index].Bits.Present) {
402 PageTable = AllocatePages (1);
403 if (PageTable == NULL) {
404 return EFI_OUT_OF_RESOURCES;
405 }
406
407 ZeroMem (PageTable, EFI_PAGE_SIZE);
408
409 L3[Index].Bits.ReadWrite = 1;
410 L3[Index].Bits.Accessed = 1;
411 L3[Index].Bits.PageTableBaseAddress = (EFI_PHYSICAL_ADDRESS)PageTable >> 12;
412 L3[Index].Bits.Present = 1;
413 }
414
415 // L2 / Page Table Entries
416
417 PageTable = (VOID *)(EFI_PHYSICAL_ADDRESS)(L3[Index].Bits.PageTableBaseAddress << 12);
418 Index = PDE_OFFSET (AddressToMap);
419
420 if (!PageTable[Index].Bits.Present) {
421 PageTable[Index].Bits.ReadWrite = 1;
422 PageTable[Index].Bits.Accessed = 1;
423 PageTable[Index].Bits.Dirty = 1;
424 PageTable[Index].Bits.MustBe1 = 1;
425 PageTable[Index].Bits.PageTableBaseAddress = AddressToMap >> 21;
426 PageTable[Index].Bits.Present = 1;
427 }
428
429 CpuFlushTlb ();
430
431 return EFI_SUCCESS;
432}
433
434STATIC
436MapSharedInfoPage (
437 IN VOID *PagePtr
438 )
439{
440 xen_add_to_physmap_t Parameters;
441 INTN ReturnCode;
442
443 Parameters.domid = DOMID_SELF;
444 Parameters.space = XENMAPSPACE_shared_info;
445 Parameters.idx = 0;
446 Parameters.gpfn = (UINTN)PagePtr >> EFI_PAGE_SHIFT;
447 ReturnCode = XenHypercallMemoryOp (XENMEM_add_to_physmap, &Parameters);
448 if (ReturnCode != 0) {
449 return EFI_NO_MAPPING;
450 }
451
452 return EFI_SUCCESS;
453}
454
455STATIC
456VOID
457UnmapXenPage (
458 IN VOID *PagePtr
459 )
460{
461 xen_remove_from_physmap_t Parameters;
462 INTN ReturnCode;
463
464 Parameters.domid = DOMID_SELF;
465 Parameters.gpfn = (UINTN)PagePtr >> EFI_PAGE_SHIFT;
466 ReturnCode = XenHypercallMemoryOp (XENMEM_remove_from_physmap, &Parameters);
467 ASSERT (ReturnCode == 0);
468}
469
470STATIC
471UINT64
472GetCpuFreq (
473 IN XEN_VCPU_TIME_INFO *VcpuTime
474 )
475{
476 UINT32 Version;
477 UINT32 TscToSystemMultiplier;
478 INT8 TscShift;
479 UINT64 CpuFreq;
480
481 do {
482 Version = VcpuTime->Version;
483 MemoryFence ();
484 TscToSystemMultiplier = VcpuTime->TscToSystemMultiplier;
485 TscShift = VcpuTime->TscShift;
486 MemoryFence ();
487 } while (((Version & 1) != 0) && (Version != VcpuTime->Version));
488
489 CpuFreq = DivU64x32 (LShiftU64 (1000000000ULL, 32), TscToSystemMultiplier);
490 if (TscShift >= 0) {
491 CpuFreq = RShiftU64 (CpuFreq, TscShift);
492 } else {
493 CpuFreq = LShiftU64 (CpuFreq, -TscShift);
494 }
495
496 return CpuFreq;
497}
498
499STATIC
500VOID
501XenDelay (
502 IN XEN_VCPU_TIME_INFO *VcpuTimeInfo,
503 IN UINT64 DelayNs
504 )
505{
506 UINT64 Tick;
507 UINT64 CpuFreq;
508 UINT64 Delay;
509 UINT64 DelayTick;
510 UINT64 NewTick;
511 RETURN_STATUS Status;
512
513 Tick = AsmReadTsc ();
514
515 CpuFreq = GetCpuFreq (VcpuTimeInfo);
516 Status = SafeUint64Mult (DelayNs, CpuFreq, &Delay);
517 if (EFI_ERROR (Status)) {
518 DEBUG ((
519 DEBUG_ERROR,
520 "XenDelay (%lu ns): delay too big in relation to CPU freq %lu Hz\n",
521 DelayNs,
522 CpuFreq
523 ));
524 ASSERT_EFI_ERROR (Status);
525 CpuDeadLoop ();
526 }
527
528 DelayTick = DivU64x32 (Delay, 1000000000);
529
530 NewTick = Tick + DelayTick;
531
532 //
533 // Check for overflow
534 //
535 if (NewTick < Tick) {
536 //
537 // Overflow, wait for TSC to also overflow
538 //
539 while (AsmReadTsc () >= Tick) {
540 CpuPause ();
541 }
542 }
543
544 while (AsmReadTsc () <= NewTick) {
545 CpuPause ();
546 }
547}
548
552VOID
554 VOID
555 )
556{
557 XEN_SHARED_INFO *SharedInfo;
558 XEN_VCPU_TIME_INFO *VcpuTimeInfo;
559 UINT32 TimerTick, TimerTick2, DiffTimer;
560 UINT64 TscTick, TscTick2;
561 UINT64 Freq;
562 UINT64 Dividend;
563 EFI_STATUS Status;
564
565 SharedInfo = (VOID *)((UINTN)PcdGet32 (PcdCpuLocalApicBaseAddress) + SIZE_1MB);
566 Status = PhysicalAddressIdentityMapping ((EFI_PHYSICAL_ADDRESS)SharedInfo);
567 if (EFI_ERROR (Status)) {
568 DEBUG ((
569 DEBUG_ERROR,
570 "Failed to add page table entry for Xen shared info page: %r\n",
571 Status
572 ));
573 ASSERT_EFI_ERROR (Status);
574 return;
575 }
576
577 Status = MapSharedInfoPage (SharedInfo);
578 if (EFI_ERROR (Status)) {
579 DEBUG ((
580 DEBUG_ERROR,
581 "Failed to map Xen's shared info page: %r\n",
582 Status
583 ));
584 ASSERT_EFI_ERROR (Status);
585 return;
586 }
587
588 VcpuTimeInfo = &SharedInfo->VcpuInfo[0].Time;
589
590 InitializeApicTimer (1, MAX_UINT32, TRUE, 0);
592
593 TimerTick = GetApicTimerCurrentCount ();
594 TscTick = AsmReadTsc ();
595 XenDelay (VcpuTimeInfo, 1000000ULL);
596 TimerTick2 = GetApicTimerCurrentCount ();
597 TscTick2 = AsmReadTsc ();
598
599 DiffTimer = TimerTick - TimerTick2;
600 Status = SafeUint64Mult (GetCpuFreq (VcpuTimeInfo), DiffTimer, &Dividend);
601 if (EFI_ERROR (Status)) {
602 DEBUG ((DEBUG_ERROR, "overflow while calculating APIC frequency\n"));
603 DEBUG ((
604 DEBUG_ERROR,
605 "CPU freq: %lu Hz; APIC timer tick count for 1 ms: %u\n",
606 GetCpuFreq (VcpuTimeInfo),
607 DiffTimer
608 ));
609 ASSERT_EFI_ERROR (Status);
610 CpuDeadLoop ();
611 }
612
613 Freq = DivU64x64Remainder (Dividend, TscTick2 - TscTick, NULL);
614 DEBUG ((DEBUG_INFO, "APIC Freq % 8lu Hz\n", Freq));
615
616 ASSERT (Freq <= MAX_UINT32);
617 Status = PcdSet32S (PcdFSBClock, (UINT32)Freq);
618 ASSERT_EFI_ERROR (Status);
619
620 UnmapXenPage (SharedInfo);
621}
UINT64 UINTN
INT64 INTN
#define MAX_ADDRESS
VOID *EFIAPI BuildGuidDataHob(IN CONST EFI_GUID *Guid, IN VOID *Data, IN UINTN DataLength)
Definition: HobLib.c:375
UINT64 EFIAPI AsmReadTsc(VOID)
Definition: GccInline.c:555
RETURN_STATUS EFIAPI AsciiStrnCpyS(OUT CHAR8 *Destination, IN UINTN DestMax, IN CONST CHAR8 *Source, IN UINTN Length)
Definition: SafeString.c:1875
UINT64 EFIAPI DivU64x32(IN UINT64 Dividend, IN UINT32 Divisor)
Definition: DivU64x32.c:29
INTN EFIAPI AsciiStrCmp(IN CONST CHAR8 *FirstString, IN CONST CHAR8 *SecondString)
Definition: String.c:716
VOID EFIAPI CpuDeadLoop(VOID)
Definition: CpuDeadLoop.c:25
VOID EFIAPI MemoryFence(VOID)
Definition: CpuBreakpoint.c:42
VOID EFIAPI CpuPause(VOID)
UINT64 EFIAPI RShiftU64(IN UINT64 Operand, IN UINTN Count)
Definition: RShiftU64.c:28
UINT64 EFIAPI DivU64x64Remainder(IN UINT64 Dividend, IN UINT64 Divisor, OUT UINT64 *Remainder OPTIONAL)
UINT64 EFIAPI LShiftU64(IN UINT64 Operand, IN UINTN Count)
Definition: LShiftU64.c:28
VOID *EFIAPI ZeroMem(OUT VOID *Buffer, IN UINTN Length)
VOID EFIAPI CpuFlushTlb(VOID)
VOID EFIAPI InitializeApicTimer(IN UINTN DivideValue, IN UINT32 InitCount, IN BOOLEAN PeriodicMode, IN UINT8 Vector)
Definition: BaseXApicLib.c:732
UINT32 EFIAPI GetApicTimerCurrentCount(VOID)
Definition: BaseXApicLib.c:712
VOID EFIAPI DisableApicTimerInterrupt(VOID)
Definition: BaseXApicLib.c:851
#define NULL
Definition: Base.h:319
#define STATIC
Definition: Base.h:264
#define MIN(a, b)
Definition: Base.h:1007
#define ALIGN_VALUE(Value, Alignment)
Definition: Base.h:948
#define TRUE
Definition: Base.h:301
#define FALSE
Definition: Base.h:307
#define IN
Definition: Base.h:279
#define SIGNATURE_32(A, B, C, D)
Definition: Base.h:1310
#define MAX(a, b)
Definition: Base.h:992
#define ASSERT_EFI_ERROR(StatusParameter)
Definition: DebugLib.h:462
#define ASSERT_RETURN_ERROR(StatusParameter)
Definition: DebugLib.h:493
#define DEBUG(Expression)
Definition: DebugLib.h:434
UINT32 EFIAPI AsmCpuid(IN UINT32 Index, OUT UINT32 *RegisterEax OPTIONAL, OUT UINT32 *RegisterEbx OPTIONAL, OUT UINT32 *RegisterEcx OPTIONAL, OUT UINT32 *RegisterEdx OPTIONAL)
Definition: CpuId.c:36
VOID AddMemoryRangeHob(EFI_PHYSICAL_ADDRESS MemoryBase, EFI_PHYSICAL_ADDRESS MemoryLimit)
Definition: Platform.c:139
#define PcdGet32(TokenName)
Definition: PcdLib.h:362
#define PcdSet32S(TokenName, Value)
Definition: PcdLib.h:497
VOID *EFIAPI AllocatePages(IN UINTN Pages)
RETURN_STATUS EFIAPI SafeUint64Mult(IN UINT64 Multiplicand, IN UINT64 Multiplier, OUT UINT64 *Result)
Definition: SafeIntLib.c:3319
UINT64 EFI_PHYSICAL_ADDRESS
Definition: UefiBaseType.h:50
RETURN_STATUS EFI_STATUS
Definition: UefiBaseType.h:29
#define EFI_SUCCESS
Definition: UefiBaseType.h:112
INTN EFIAPI XenHypercallMemoryOp(IN UINTN Operation, IN OUT VOID *Arguments)
Definition: XenHypercall.c:65
RETURN_STATUS EFIAPI XenHypercallLibInit(VOID)
BOOLEAN XenPvhDetected(VOID)
Definition: Xen.c:258
EFI_STATUS XenConnect()
Definition: Xen.c:128
VOID CalibrateLapicTimer(VOID)
Definition: Xen.c:553
BOOLEAN XenDetect(VOID)
Definition: Xen.c:220
EFI_STATUS XenGetE820Map(EFI_E820_ENTRY64 **Entries, UINT32 *Count)
Definition: Xen.c:61
UINT16 VersionMajor
Definition: XenInfo.h:19
VOID * RsdpPvh
Definition: XenInfo.h:27
UINT16 VersionMinor
Definition: XenInfo.h:23