]> git.proxmox.com Git - mirror_edk2.git/blob - OvmfPkg/XenPlatformPei/Xen.c
OvmfPkg/OvmfXen: make "PcdPciDisableBusEnumeration" Fixed-at-Build
[mirror_edk2.git] / OvmfPkg / XenPlatformPei / Xen.c
1 /**@file
2 Xen Platform PEI support
3
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.
7
8 SPDX-License-Identifier: BSD-2-Clause-Patent
9
10 **/
11
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 //
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>
36
37 #include "Platform.h"
38 #include "Xen.h"
39
40 STATIC UINT32 mXenLeaf = 0;
41
42 EFI_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 //
48 EFI_XEN_OVMF_INFO *mXenHvmloaderInfo;
49 STATIC EFI_E820_ENTRY64 mE820Entries[128];
50 STATIC UINT32 mE820EntriesCount;
51
52 /**
53 Returns E820 map provided by Xen
54
55 @param Entries Pointer to E820 map
56 @param Count Number of entries
57
58 @return EFI_STATUS
59 **/
60 EFI_STATUS
61 XenGetE820Map (
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
121 /**
122 Connects to the Hypervisor.
123
124 @return EFI_STATUS
125
126 **/
127 EFI_STATUS
128 XenConnect (
129 )
130 {
131 UINT32 Index;
132 UINT32 TransferReg;
133 UINT32 TransferPages;
134 UINT32 XenVersion;
135 EFI_XEN_OVMF_INFO *Info;
136 CHAR8 Sig[sizeof (Info->Signature) + 1];
137 UINT32 *PVHResetVectorData;
138 RETURN_STATUS Status;
139
140 ASSERT (mXenLeaf != 0);
141
142 //
143 // Prepare HyperPages to be able to make hypercalls
144 //
145
146 AsmCpuid (mXenLeaf + 2, &TransferPages, &TransferReg, NULL, NULL);
147 mXenInfo.HyperPages = AllocatePages (TransferPages);
148 if (!mXenInfo.HyperPages) {
149 return EFI_OUT_OF_RESOURCES;
150 }
151
152 for (Index = 0; Index < TransferPages; Index++) {
153 AsmWriteMsr64 (TransferReg,
154 (UINTN) mXenInfo.HyperPages +
155 (Index << EFI_PAGE_SHIFT) + Index);
156 }
157
158 //
159 // Find out the Xen version
160 //
161
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);
167
168 //
169 // Check if there are information left by hvmloader
170 //
171
172 Info = (EFI_XEN_OVMF_INFO *)(UINTN) OVMF_INFO_PHYSICAL_ADDRESS;
173 //
174 // Copy the signature, and make it null-terminated.
175 //
176 AsciiStrnCpyS (Sig, sizeof (Sig), (CHAR8 *) &Info->Signature,
177 sizeof (Info->Signature));
178 if (AsciiStrCmp (Sig, "XenHVMOVMF") == 0) {
179 mXenHvmloaderInfo = Info;
180 } else {
181 mXenHvmloaderInfo = NULL;
182 }
183
184 mXenInfo.RsdpPvh = NULL;
185
186 //
187 // Locate and use information from the start of day structure if we have
188 // booted via the PVH entry point.
189 //
190
191 PVHResetVectorData = (VOID *)(UINTN) PcdGet32 (PcdXenPvhStartOfDayStructPtr);
192 //
193 // That magic value is written in XenResetVector/Ia32/XenPVHMain.asm
194 //
195 if (PVHResetVectorData[1] == SIGNATURE_32 ('X', 'P', 'V', 'H')) {
196 struct hvm_start_info *HVMStartInfo;
197
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;
203 }
204 }
205 }
206
207 BuildGuidDataHob (
208 &gEfiXenInfoGuid,
209 &mXenInfo,
210 sizeof(mXenInfo)
211 );
212
213 //
214 // Initialize the XenHypercall library, now that the XenInfo HOB is
215 // available
216 //
217 Status = XenHypercallLibInit ();
218 ASSERT_RETURN_ERROR (Status);
219
220 return EFI_SUCCESS;
221 }
222
223 /**
224 Figures out if we are running inside Xen HVM.
225
226 @retval TRUE Xen was detected
227 @retval FALSE Xen was not detected
228
229 **/
230 BOOLEAN
231 XenDetect (
232 VOID
233 )
234 {
235 UINT8 Signature[13];
236
237 if (mXenLeaf != 0) {
238 return TRUE;
239 }
240
241 Signature[12] = '\0';
242 for (mXenLeaf = 0x40000000; mXenLeaf < 0x40010000; mXenLeaf += 0x100) {
243 AsmCpuid (mXenLeaf,
244 NULL,
245 (UINT32 *) &Signature[0],
246 (UINT32 *) &Signature[4],
247 (UINT32 *) &Signature[8]);
248
249 if (!AsciiStrCmp ((CHAR8 *) Signature, "XenVMMXenVMM")) {
250 return TRUE;
251 }
252 }
253
254 mXenLeaf = 0;
255 return FALSE;
256 }
257
258 BOOLEAN
259 XenHvmloaderDetected (
260 VOID
261 )
262 {
263 return (mXenHvmloaderInfo != NULL);
264 }
265
266 BOOLEAN
267 XenPvhDetected (
268 VOID
269 )
270 {
271 //
272 // This function should only be used after XenConnect
273 //
274 ASSERT (mXenInfo.HyperPages != NULL);
275
276 return mXenHvmloaderInfo == NULL;
277 }
278
279 VOID
280 XenPublishRamRegions (
281 VOID
282 )
283 {
284 EFI_E820_ENTRY64 *E820Map;
285 UINT32 E820EntriesCount;
286 EFI_STATUS Status;
287 EFI_E820_ENTRY64 *Entry;
288 UINTN Index;
289 UINT64 LapicBase;
290 UINT64 LapicEnd;
291
292
293 DEBUG ((DEBUG_INFO, "Using memory map provided by Xen\n"));
294
295 //
296 // Parse RAM in E820 map
297 //
298 E820EntriesCount = 0;
299 Status = XenGetE820Map (&E820Map, &E820EntriesCount);
300 ASSERT_EFI_ERROR (Status);
301
302 AddMemoryBaseSizeHob (0, 0xA0000);
303 //
304 // Video memory + Legacy BIOS region, to allow Linux to boot.
305 //
306 AddReservedMemoryBaseSizeHob (0xA0000, BASE_1MB - 0xA0000, TRUE);
307
308 LapicBase = PcdGet32 (PcdCpuLocalApicBaseAddress);
309 LapicEnd = LapicBase + SIZE_1MB;
310 AddIoMemoryRangeHob (LapicBase, LapicEnd);
311
312 for (Index = 0; Index < E820EntriesCount; Index++) {
313 UINT64 Base;
314 UINT64 End;
315 UINT64 ReservedBase;
316 UINT64 ReservedEnd;
317
318 Entry = &E820Map[Index];
319
320 //
321 // Round up the start address, and round down the end address.
322 //
323 Base = ALIGN_VALUE (Entry->BaseAddr, (UINT64)EFI_PAGE_SIZE);
324 End = (Entry->BaseAddr + Entry->Length) & ~(UINT64)EFI_PAGE_MASK;
325
326 //
327 // Ignore the first 1MB, this is handled before the loop.
328 //
329 if (Base < BASE_1MB) {
330 Base = BASE_1MB;
331 }
332 if (Base >= End) {
333 continue;
334 }
335
336 switch (Entry->Type) {
337 case EfiAcpiAddressRangeMemory:
338 AddMemoryRangeHob (Base, End);
339 break;
340 case EfiAcpiAddressRangeACPI:
341 AddReservedMemoryRangeHob (Base, End, FALSE);
342 break;
343 case EfiAcpiAddressRangeReserved:
344 //
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.
348 //
349
350 //
351 // add LAPIC predecessor range, if any
352 //
353 ReservedBase = Base;
354 ReservedEnd = MIN (End, LapicBase);
355 if (ReservedBase < ReservedEnd) {
356 AddReservedMemoryRangeHob (ReservedBase, ReservedEnd, FALSE);
357 }
358
359 //
360 // add LAPIC successor range, if any
361 //
362 ReservedBase = MAX (Base, LapicEnd);
363 ReservedEnd = End;
364 if (ReservedBase < ReservedEnd) {
365 AddReservedMemoryRangeHob (ReservedBase, ReservedEnd, FALSE);
366 }
367 break;
368 default:
369 break;
370 }
371 }
372 }
373
374
375 EFI_STATUS
376 PhysicalAddressIdentityMapping (
377 IN EFI_PHYSICAL_ADDRESS AddressToMap
378 )
379 {
380 INTN Index;
381 PAGE_MAP_AND_DIRECTORY_POINTER *L4, *L3;
382 PAGE_TABLE_ENTRY *PageTable;
383
384 DEBUG ((DEBUG_INFO, "Mapping 1:1 of address 0x%lx\n", (UINT64)AddressToMap));
385
386 // L4 / Top level Page Directory Pointers
387
388 L4 = (VOID*)(UINTN)PcdGet32 (PcdOvmfSecPageTablesBase);
389 Index = PML4_OFFSET (AddressToMap);
390
391 if (!L4[Index].Bits.Present) {
392 L3 = AllocatePages (1);
393 if (L3 == NULL) {
394 return EFI_OUT_OF_RESOURCES;
395 }
396
397 ZeroMem (L3, EFI_PAGE_SIZE);
398
399 L4[Index].Bits.ReadWrite = 1;
400 L4[Index].Bits.Accessed = 1;
401 L4[Index].Bits.PageTableBaseAddress = (EFI_PHYSICAL_ADDRESS)L3 >> 12;
402 L4[Index].Bits.Present = 1;
403 }
404
405 // L3 / Next level Page Directory Pointers
406
407 L3 = (VOID*)(EFI_PHYSICAL_ADDRESS)(L4[Index].Bits.PageTableBaseAddress << 12);
408 Index = PDP_OFFSET (AddressToMap);
409
410 if (!L3[Index].Bits.Present) {
411 PageTable = AllocatePages (1);
412 if (PageTable == NULL) {
413 return EFI_OUT_OF_RESOURCES;
414 }
415
416 ZeroMem (PageTable, EFI_PAGE_SIZE);
417
418 L3[Index].Bits.ReadWrite = 1;
419 L3[Index].Bits.Accessed = 1;
420 L3[Index].Bits.PageTableBaseAddress = (EFI_PHYSICAL_ADDRESS)PageTable >> 12;
421 L3[Index].Bits.Present = 1;
422 }
423
424 // L2 / Page Table Entries
425
426 PageTable = (VOID*)(EFI_PHYSICAL_ADDRESS)(L3[Index].Bits.PageTableBaseAddress << 12);
427 Index = PDE_OFFSET (AddressToMap);
428
429 if (!PageTable[Index].Bits.Present) {
430 PageTable[Index].Bits.ReadWrite = 1;
431 PageTable[Index].Bits.Accessed = 1;
432 PageTable[Index].Bits.Dirty = 1;
433 PageTable[Index].Bits.MustBe1 = 1;
434 PageTable[Index].Bits.PageTableBaseAddress = AddressToMap >> 21;
435 PageTable[Index].Bits.Present = 1;
436 }
437
438 CpuFlushTlb ();
439
440 return EFI_SUCCESS;
441 }
442
443 STATIC
444 EFI_STATUS
445 MapSharedInfoPage (
446 IN VOID *PagePtr
447 )
448 {
449 xen_add_to_physmap_t Parameters;
450 INTN ReturnCode;
451
452 Parameters.domid = DOMID_SELF;
453 Parameters.space = XENMAPSPACE_shared_info;
454 Parameters.idx = 0;
455 Parameters.gpfn = (UINTN)PagePtr >> EFI_PAGE_SHIFT;
456 ReturnCode = XenHypercallMemoryOp (XENMEM_add_to_physmap, &Parameters);
457 if (ReturnCode != 0) {
458 return EFI_NO_MAPPING;
459 }
460 return EFI_SUCCESS;
461 }
462
463 STATIC
464 VOID
465 UnmapXenPage (
466 IN VOID *PagePtr
467 )
468 {
469 xen_remove_from_physmap_t Parameters;
470 INTN ReturnCode;
471
472 Parameters.domid = DOMID_SELF;
473 Parameters.gpfn = (UINTN)PagePtr >> EFI_PAGE_SHIFT;
474 ReturnCode = XenHypercallMemoryOp (XENMEM_remove_from_physmap, &Parameters);
475 ASSERT (ReturnCode == 0);
476 }
477
478
479 STATIC
480 UINT64
481 GetCpuFreq (
482 IN XEN_VCPU_TIME_INFO *VcpuTime
483 )
484 {
485 UINT32 Version;
486 UINT32 TscToSystemMultiplier;
487 INT8 TscShift;
488 UINT64 CpuFreq;
489
490 do {
491 Version = VcpuTime->Version;
492 MemoryFence ();
493 TscToSystemMultiplier = VcpuTime->TscToSystemMultiplier;
494 TscShift = VcpuTime->TscShift;
495 MemoryFence ();
496 } while (((Version & 1) != 0) && (Version != VcpuTime->Version));
497
498 CpuFreq = DivU64x32 (LShiftU64 (1000000000ULL, 32), TscToSystemMultiplier);
499 if (TscShift >= 0) {
500 CpuFreq = RShiftU64 (CpuFreq, TscShift);
501 } else {
502 CpuFreq = LShiftU64 (CpuFreq, -TscShift);
503 }
504 return CpuFreq;
505 }
506
507 STATIC
508 VOID
509 XenDelay (
510 IN XEN_VCPU_TIME_INFO *VcpuTimeInfo,
511 IN UINT64 DelayNs
512 )
513 {
514 UINT64 Tick;
515 UINT64 CpuFreq;
516 UINT64 Delay;
517 UINT64 DelayTick;
518 UINT64 NewTick;
519 RETURN_STATUS Status;
520
521 Tick = AsmReadTsc ();
522
523 CpuFreq = GetCpuFreq (VcpuTimeInfo);
524 Status = SafeUint64Mult (DelayNs, CpuFreq, &Delay);
525 if (EFI_ERROR (Status)) {
526 DEBUG ((DEBUG_ERROR,
527 "XenDelay (%lu ns): delay too big in relation to CPU freq %lu Hz\n",
528 DelayNs, CpuFreq));
529 ASSERT_EFI_ERROR (Status);
530 CpuDeadLoop ();
531 }
532
533 DelayTick = DivU64x32 (Delay, 1000000000);
534
535 NewTick = Tick + DelayTick;
536
537 //
538 // Check for overflow
539 //
540 if (NewTick < Tick) {
541 //
542 // Overflow, wait for TSC to also overflow
543 //
544 while (AsmReadTsc () >= Tick) {
545 CpuPause ();
546 }
547 }
548
549 while (AsmReadTsc () <= NewTick) {
550 CpuPause ();
551 }
552 }
553
554
555 /**
556 Calculate the frequency of the Local Apic Timer
557 **/
558 VOID
559 CalibrateLapicTimer (
560 VOID
561 )
562 {
563 XEN_SHARED_INFO *SharedInfo;
564 XEN_VCPU_TIME_INFO *VcpuTimeInfo;
565 UINT32 TimerTick, TimerTick2, DiffTimer;
566 UINT64 TscTick, TscTick2;
567 UINT64 Freq;
568 UINT64 Dividend;
569 EFI_STATUS Status;
570
571
572 SharedInfo = (VOID*)((1ULL << mPhysMemAddressWidth) - EFI_PAGE_SIZE);
573 Status = PhysicalAddressIdentityMapping ((EFI_PHYSICAL_ADDRESS)SharedInfo);
574 if (EFI_ERROR (Status)) {
575 DEBUG ((DEBUG_ERROR,
576 "Failed to add page table entry for Xen shared info page: %r\n",
577 Status));
578 ASSERT_EFI_ERROR (Status);
579 return;
580 }
581
582 Status = MapSharedInfoPage (SharedInfo);
583 if (EFI_ERROR (Status)) {
584 DEBUG ((DEBUG_ERROR, "Failed to map Xen's shared info page: %r\n",
585 Status));
586 ASSERT_EFI_ERROR (Status);
587 return;
588 }
589
590 VcpuTimeInfo = &SharedInfo->VcpuInfo[0].Time;
591
592 InitializeApicTimer (1, MAX_UINT32, TRUE, 0);
593 DisableApicTimerInterrupt ();
594
595 TimerTick = GetApicTimerCurrentCount ();
596 TscTick = AsmReadTsc ();
597 XenDelay (VcpuTimeInfo, 1000000ULL);
598 TimerTick2 = GetApicTimerCurrentCount ();
599 TscTick2 = AsmReadTsc ();
600
601
602 DiffTimer = TimerTick - TimerTick2;
603 Status = SafeUint64Mult (GetCpuFreq (VcpuTimeInfo), DiffTimer, &Dividend);
604 if (EFI_ERROR (Status)) {
605 DEBUG ((DEBUG_ERROR, "overflow while calculating APIC frequency\n"));
606 DEBUG ((DEBUG_ERROR, "CPU freq: %lu Hz; APIC timer tick count for 1 ms: %u\n",
607 GetCpuFreq (VcpuTimeInfo), DiffTimer));
608 ASSERT_EFI_ERROR (Status);
609 CpuDeadLoop ();
610 }
611
612 Freq = DivU64x64Remainder (Dividend, TscTick2 - TscTick, NULL);
613 DEBUG ((DEBUG_INFO, "APIC Freq % 8lu Hz\n", Freq));
614
615 ASSERT (Freq <= MAX_UINT32);
616 Status = PcdSet32S (PcdFSBClock, (UINT32)Freq);
617 ASSERT_EFI_ERROR (Status);
618
619 UnmapXenPage (SharedInfo);
620 }