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