]> git.proxmox.com Git - mirror_edk2.git/blob - OvmfPkg/XenPlatformPei/Xen.c
OvmfPkg/OvmfXen: Set PcdFSBClock
[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 /**
376 Perform Xen PEI initialization.
377
378 @return EFI_SUCCESS Xen initialized successfully
379 @return EFI_NOT_FOUND Not running under Xen
380
381 **/
382 EFI_STATUS
383 InitializeXen (
384 VOID
385 )
386 {
387 RETURN_STATUS PcdStatus;
388
389 PcdStatus = PcdSetBoolS (PcdPciDisableBusEnumeration, TRUE);
390 ASSERT_RETURN_ERROR (PcdStatus);
391
392 return EFI_SUCCESS;
393 }
394
395 EFI_STATUS
396 PhysicalAddressIdentityMapping (
397 IN EFI_PHYSICAL_ADDRESS AddressToMap
398 )
399 {
400 INTN Index;
401 PAGE_MAP_AND_DIRECTORY_POINTER *L4, *L3;
402 PAGE_TABLE_ENTRY *PageTable;
403
404 DEBUG ((DEBUG_INFO, "Mapping 1:1 of address 0x%lx\n", (UINT64)AddressToMap));
405
406 // L4 / Top level Page Directory Pointers
407
408 L4 = (VOID*)(UINTN)PcdGet32 (PcdOvmfSecPageTablesBase);
409 Index = PML4_OFFSET (AddressToMap);
410
411 if (!L4[Index].Bits.Present) {
412 L3 = AllocatePages (1);
413 if (L3 == NULL) {
414 return EFI_OUT_OF_RESOURCES;
415 }
416
417 ZeroMem (L3, EFI_PAGE_SIZE);
418
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;
423 }
424
425 // L3 / Next level Page Directory Pointers
426
427 L3 = (VOID*)(EFI_PHYSICAL_ADDRESS)(L4[Index].Bits.PageTableBaseAddress << 12);
428 Index = PDP_OFFSET (AddressToMap);
429
430 if (!L3[Index].Bits.Present) {
431 PageTable = AllocatePages (1);
432 if (PageTable == NULL) {
433 return EFI_OUT_OF_RESOURCES;
434 }
435
436 ZeroMem (PageTable, EFI_PAGE_SIZE);
437
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;
442 }
443
444 // L2 / Page Table Entries
445
446 PageTable = (VOID*)(EFI_PHYSICAL_ADDRESS)(L3[Index].Bits.PageTableBaseAddress << 12);
447 Index = PDE_OFFSET (AddressToMap);
448
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;
456 }
457
458 CpuFlushTlb ();
459
460 return EFI_SUCCESS;
461 }
462
463 STATIC
464 EFI_STATUS
465 MapSharedInfoPage (
466 IN VOID *PagePtr
467 )
468 {
469 xen_add_to_physmap_t Parameters;
470 INTN ReturnCode;
471
472 Parameters.domid = DOMID_SELF;
473 Parameters.space = XENMAPSPACE_shared_info;
474 Parameters.idx = 0;
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;
479 }
480 return EFI_SUCCESS;
481 }
482
483 STATIC
484 VOID
485 UnmapXenPage (
486 IN VOID *PagePtr
487 )
488 {
489 xen_remove_from_physmap_t Parameters;
490 INTN ReturnCode;
491
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);
496 }
497
498
499 STATIC
500 UINT64
501 GetCpuFreq (
502 IN XEN_VCPU_TIME_INFO *VcpuTime
503 )
504 {
505 UINT32 Version;
506 UINT32 TscToSystemMultiplier;
507 INT8 TscShift;
508 UINT64 CpuFreq;
509
510 do {
511 Version = VcpuTime->Version;
512 MemoryFence ();
513 TscToSystemMultiplier = VcpuTime->TscToSystemMultiplier;
514 TscShift = VcpuTime->TscShift;
515 MemoryFence ();
516 } while (((Version & 1) != 0) && (Version != VcpuTime->Version));
517
518 CpuFreq = DivU64x32 (LShiftU64 (1000000000ULL, 32), TscToSystemMultiplier);
519 if (TscShift >= 0) {
520 CpuFreq = RShiftU64 (CpuFreq, TscShift);
521 } else {
522 CpuFreq = LShiftU64 (CpuFreq, -TscShift);
523 }
524 return CpuFreq;
525 }
526
527 STATIC
528 VOID
529 XenDelay (
530 IN XEN_VCPU_TIME_INFO *VcpuTimeInfo,
531 IN UINT64 DelayNs
532 )
533 {
534 UINT64 Tick;
535 UINT64 CpuFreq;
536 UINT64 Delay;
537 UINT64 DelayTick;
538 UINT64 NewTick;
539 RETURN_STATUS Status;
540
541 Tick = AsmReadTsc ();
542
543 CpuFreq = GetCpuFreq (VcpuTimeInfo);
544 Status = SafeUint64Mult (DelayNs, CpuFreq, &Delay);
545 if (EFI_ERROR (Status)) {
546 DEBUG ((DEBUG_ERROR,
547 "XenDelay (%lu ns): delay too big in relation to CPU freq %lu Hz\n",
548 DelayNs, CpuFreq));
549 ASSERT_EFI_ERROR (Status);
550 CpuDeadLoop ();
551 }
552
553 DelayTick = DivU64x32 (Delay, 1000000000);
554
555 NewTick = Tick + DelayTick;
556
557 //
558 // Check for overflow
559 //
560 if (NewTick < Tick) {
561 //
562 // Overflow, wait for TSC to also overflow
563 //
564 while (AsmReadTsc () >= Tick) {
565 CpuPause ();
566 }
567 }
568
569 while (AsmReadTsc () <= NewTick) {
570 CpuPause ();
571 }
572 }
573
574
575 /**
576 Calculate the frequency of the Local Apic Timer
577 **/
578 VOID
579 CalibrateLapicTimer (
580 VOID
581 )
582 {
583 XEN_SHARED_INFO *SharedInfo;
584 XEN_VCPU_TIME_INFO *VcpuTimeInfo;
585 UINT32 TimerTick, TimerTick2, DiffTimer;
586 UINT64 TscTick, TscTick2;
587 UINT64 Freq;
588 UINT64 Dividend;
589 EFI_STATUS Status;
590
591
592 SharedInfo = (VOID*)((1ULL << mPhysMemAddressWidth) - EFI_PAGE_SIZE);
593 Status = PhysicalAddressIdentityMapping ((EFI_PHYSICAL_ADDRESS)SharedInfo);
594 if (EFI_ERROR (Status)) {
595 DEBUG ((DEBUG_ERROR,
596 "Failed to add page table entry for Xen shared info page: %r\n",
597 Status));
598 ASSERT_EFI_ERROR (Status);
599 return;
600 }
601
602 Status = MapSharedInfoPage (SharedInfo);
603 if (EFI_ERROR (Status)) {
604 DEBUG ((DEBUG_ERROR, "Failed to map Xen's shared info page: %r\n",
605 Status));
606 ASSERT_EFI_ERROR (Status);
607 return;
608 }
609
610 VcpuTimeInfo = &SharedInfo->VcpuInfo[0].Time;
611
612 InitializeApicTimer (1, MAX_UINT32, TRUE, 0);
613 DisableApicTimerInterrupt ();
614
615 TimerTick = GetApicTimerCurrentCount ();
616 TscTick = AsmReadTsc ();
617 XenDelay (VcpuTimeInfo, 1000000ULL);
618 TimerTick2 = GetApicTimerCurrentCount ();
619 TscTick2 = AsmReadTsc ();
620
621
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);
629 CpuDeadLoop ();
630 }
631
632 Freq = DivU64x64Remainder (Dividend, TscTick2 - TscTick, NULL);
633 DEBUG ((DEBUG_INFO, "APIC Freq % 8lu Hz\n", Freq));
634
635 ASSERT (Freq <= MAX_UINT32);
636 Status = PcdSet32S (PcdFSBClock, (UINT32)Freq);
637 ASSERT_EFI_ERROR (Status);
638
639 UnmapXenPage (SharedInfo);
640 }