]> git.proxmox.com Git - mirror_edk2.git/blob - OvmfPkg/Library/PlatformInitLib/MemDetect.c
38cece9173e8011fd1930d43e163cd3048b24198
[mirror_edk2.git] / OvmfPkg / Library / PlatformInitLib / MemDetect.c
1 /**@file
2 Memory Detection for Virtual Machines.
3
4 Copyright (c) 2006 - 2016, Intel Corporation. All rights reserved.<BR>
5 SPDX-License-Identifier: BSD-2-Clause-Patent
6
7 Module Name:
8
9 MemDetect.c
10
11 **/
12
13 //
14 // The package level header files this module uses
15 //
16 #include <IndustryStandard/E820.h>
17 #include <IndustryStandard/I440FxPiix4.h>
18 #include <IndustryStandard/Q35MchIch9.h>
19 #include <IndustryStandard/CloudHv.h>
20 #include <IndustryStandard/Xen/arch-x86/hvm/start_info.h>
21 #include <PiPei.h>
22 #include <Register/Intel/SmramSaveStateMap.h>
23
24 //
25 // The Library classes this module consumes
26 //
27 #include <Library/BaseLib.h>
28 #include <Library/BaseMemoryLib.h>
29 #include <Library/CcProbeLib.h>
30 #include <Library/DebugLib.h>
31 #include <Library/HardwareInfoLib.h>
32 #include <Library/HobLib.h>
33 #include <Library/IoLib.h>
34 #include <Library/MemEncryptSevLib.h>
35 #include <Library/PcdLib.h>
36 #include <Library/PciLib.h>
37 #include <Library/PeimEntryPoint.h>
38 #include <Library/ResourcePublicationLib.h>
39 #include <Library/MtrrLib.h>
40 #include <Library/QemuFwCfgLib.h>
41 #include <Library/QemuFwCfgSimpleParserLib.h>
42 #include <Library/TdxLib.h>
43
44 #include <Library/PlatformInitLib.h>
45
46 #define MEGABYTE_SHIFT 20
47
48 VOID
49 EFIAPI
50 PlatformQemuUc32BaseInitialization (
51 IN OUT EFI_HOB_PLATFORM_INFO *PlatformInfoHob
52 )
53 {
54 if (PlatformInfoHob->HostBridgeDevId == 0xffff /* microvm */) {
55 return;
56 }
57
58 if (PlatformInfoHob->HostBridgeDevId == CLOUDHV_DEVICE_ID) {
59 PlatformInfoHob->Uc32Size = CLOUDHV_MMIO_HOLE_SIZE;
60 PlatformInfoHob->Uc32Base = CLOUDHV_MMIO_HOLE_ADDRESS;
61 return;
62 }
63
64 PlatformGetSystemMemorySizeBelow4gb (PlatformInfoHob);
65
66 if (PlatformInfoHob->HostBridgeDevId == INTEL_Q35_MCH_DEVICE_ID) {
67 ASSERT (PcdGet64 (PcdPciExpressBaseAddress) <= MAX_UINT32);
68 ASSERT (PcdGet64 (PcdPciExpressBaseAddress) >= PlatformInfoHob->LowMemory);
69
70 if (PlatformInfoHob->LowMemory <= BASE_2GB) {
71 // Newer qemu with gigabyte aligned memory,
72 // 32-bit pci mmio window is 2G -> 4G then.
73 PlatformInfoHob->Uc32Base = BASE_2GB;
74 } else {
75 //
76 // On q35, the 32-bit area that we'll mark as UC, through variable MTRRs,
77 // starts at PcdPciExpressBaseAddress. The platform DSC is responsible for
78 // setting PcdPciExpressBaseAddress such that describing the
79 // [PcdPciExpressBaseAddress, 4GB) range require a very small number of
80 // variable MTRRs (preferably 1 or 2).
81 //
82 PlatformInfoHob->Uc32Base = (UINT32)PcdGet64 (PcdPciExpressBaseAddress);
83 }
84
85 return;
86 }
87
88 ASSERT (PlatformInfoHob->HostBridgeDevId == INTEL_82441_DEVICE_ID);
89 //
90 // On i440fx, start with the [LowerMemorySize, 4GB) range. Make sure one
91 // variable MTRR suffices by truncating the size to a whole power of two,
92 // while keeping the end affixed to 4GB. This will round the base up.
93 //
94 PlatformInfoHob->Uc32Size = GetPowerOfTwo32 ((UINT32)(SIZE_4GB - PlatformInfoHob->LowMemory));
95 PlatformInfoHob->Uc32Base = (UINT32)(SIZE_4GB - PlatformInfoHob->Uc32Size);
96 //
97 // Assuming that LowerMemorySize is at least 1 byte, Uc32Size is at most 2GB.
98 // Therefore Uc32Base is at least 2GB.
99 //
100 ASSERT (PlatformInfoHob->Uc32Base >= BASE_2GB);
101
102 if (PlatformInfoHob->Uc32Base != PlatformInfoHob->LowMemory) {
103 DEBUG ((
104 DEBUG_VERBOSE,
105 "%a: rounded UC32 base from 0x%x up to 0x%x, for "
106 "an UC32 size of 0x%x\n",
107 __FUNCTION__,
108 PlatformInfoHob->LowMemory,
109 PlatformInfoHob->Uc32Base,
110 PlatformInfoHob->Uc32Size
111 ));
112 }
113 }
114
115 typedef VOID (*E820_SCAN_CALLBACK) (
116 EFI_E820_ENTRY64 *E820Entry,
117 EFI_HOB_PLATFORM_INFO *PlatformInfoHob
118 );
119
120 /**
121 Store first address not used by e820 RAM entries in
122 PlatformInfoHob->FirstNonAddress
123 **/
124 STATIC
125 VOID
126 PlatformGetFirstNonAddressCB (
127 IN EFI_E820_ENTRY64 *E820Entry,
128 IN OUT EFI_HOB_PLATFORM_INFO *PlatformInfoHob
129 )
130 {
131 UINT64 Candidate;
132
133 if (E820Entry->Type != EfiAcpiAddressRangeMemory) {
134 return;
135 }
136
137 Candidate = E820Entry->BaseAddr + E820Entry->Length;
138 if (PlatformInfoHob->FirstNonAddress < Candidate) {
139 DEBUG ((DEBUG_INFO, "%a: FirstNonAddress=0x%Lx\n", __FUNCTION__, Candidate));
140 PlatformInfoHob->FirstNonAddress = Candidate;
141 }
142 }
143
144 /**
145 Store the low (below 4G) memory size in
146 PlatformInfoHob->LowMemory
147 **/
148 STATIC
149 VOID
150 PlatformGetLowMemoryCB (
151 IN EFI_E820_ENTRY64 *E820Entry,
152 IN OUT EFI_HOB_PLATFORM_INFO *PlatformInfoHob
153 )
154 {
155 UINT64 Candidate;
156
157 if (E820Entry->Type != EfiAcpiAddressRangeMemory) {
158 return;
159 }
160
161 Candidate = E820Entry->BaseAddr + E820Entry->Length;
162 if (Candidate >= BASE_4GB) {
163 return;
164 }
165
166 if (PlatformInfoHob->LowMemory < Candidate) {
167 DEBUG ((DEBUG_INFO, "%a: LowMemory=0x%Lx\n", __FUNCTION__, Candidate));
168 PlatformInfoHob->LowMemory = (UINT32)Candidate;
169 }
170 }
171
172 /**
173 Create HOBs for reservations and RAM (except low memory).
174 **/
175 STATIC
176 VOID
177 PlatformAddHobCB (
178 IN EFI_E820_ENTRY64 *E820Entry,
179 IN OUT EFI_HOB_PLATFORM_INFO *PlatformInfoHob
180 )
181 {
182 UINT64 Base, End;
183
184 Base = E820Entry->BaseAddr;
185 End = E820Entry->BaseAddr + E820Entry->Length;
186
187 switch (E820Entry->Type) {
188 case EfiAcpiAddressRangeMemory:
189 if (Base >= BASE_4GB) {
190 //
191 // Round up the start address, and round down the end address.
192 //
193 Base = ALIGN_VALUE (Base, (UINT64)EFI_PAGE_SIZE);
194 End = End & ~(UINT64)EFI_PAGE_MASK;
195 if (Base < End) {
196 DEBUG ((DEBUG_INFO, "%a: HighMemory [0x%Lx, 0x%Lx)\n", __FUNCTION__, Base, End));
197 PlatformAddMemoryRangeHob (Base, End);
198 }
199 }
200
201 break;
202 case EfiAcpiAddressRangeReserved:
203 BuildResourceDescriptorHob (EFI_RESOURCE_MEMORY_RESERVED, 0, Base, End - Base);
204 DEBUG ((DEBUG_INFO, "%a: Reserved [0x%Lx, 0x%Lx)\n", __FUNCTION__, Base, End));
205 break;
206 default:
207 DEBUG ((
208 DEBUG_WARN,
209 "%a: Type %u [0x%Lx, 0x%Lx) (NOT HANDLED)\n",
210 __FUNCTION__,
211 E820Entry->Type,
212 Base,
213 End
214 ));
215 break;
216 }
217 }
218
219 /**
220 Check whenever the 64bit PCI MMIO window overlaps with a reservation
221 from qemu. If so move down the MMIO window to resolve the conflict.
222
223 This happens on (virtual) AMD machines with 1TB address space,
224 because the AMD IOMMU uses an address window just below 1TB.
225 **/
226 STATIC
227 VOID
228 PlatformReservationConflictCB (
229 IN EFI_E820_ENTRY64 *E820Entry,
230 IN OUT EFI_HOB_PLATFORM_INFO *PlatformInfoHob
231 )
232 {
233 UINT64 IntersectionBase;
234 UINT64 IntersectionEnd;
235 UINT64 NewBase;
236
237 IntersectionBase = MAX (
238 E820Entry->BaseAddr,
239 PlatformInfoHob->PcdPciMmio64Base
240 );
241 IntersectionEnd = MIN (
242 E820Entry->BaseAddr + E820Entry->Length,
243 PlatformInfoHob->PcdPciMmio64Base +
244 PlatformInfoHob->PcdPciMmio64Size
245 );
246
247 if (IntersectionBase >= IntersectionEnd) {
248 return; // no overlap
249 }
250
251 NewBase = E820Entry->BaseAddr - PlatformInfoHob->PcdPciMmio64Size;
252 NewBase = NewBase & ~(PlatformInfoHob->PcdPciMmio64Size - 1);
253
254 DEBUG ((
255 DEBUG_INFO,
256 "%a: move mmio: 0x%Lx => %Lx\n",
257 __FUNCTION__,
258 PlatformInfoHob->PcdPciMmio64Base,
259 NewBase
260 ));
261 PlatformInfoHob->PcdPciMmio64Base = NewBase;
262 }
263
264 /**
265 Iterate over the entries in QEMU's fw_cfg E820 RAM map, call the
266 passed callback for each entry.
267
268 @param[in] Callback The callback function to be called.
269
270 @param[in out] PlatformInfoHob PlatformInfo struct which is passed
271 through to the callback.
272
273 @retval EFI_SUCCESS The fw_cfg E820 RAM map was found and processed.
274
275 @retval EFI_PROTOCOL_ERROR The RAM map was found, but its size wasn't a
276 whole multiple of sizeof(EFI_E820_ENTRY64). No
277 RAM entry was processed.
278
279 @return Error codes from QemuFwCfgFindFile(). No RAM
280 entry was processed.
281 **/
282 STATIC
283 EFI_STATUS
284 PlatformScanE820 (
285 IN E820_SCAN_CALLBACK Callback,
286 IN OUT EFI_HOB_PLATFORM_INFO *PlatformInfoHob
287 )
288 {
289 EFI_STATUS Status;
290 FIRMWARE_CONFIG_ITEM FwCfgItem;
291 UINTN FwCfgSize;
292 EFI_E820_ENTRY64 E820Entry;
293 UINTN Processed;
294
295 Status = QemuFwCfgFindFile ("etc/e820", &FwCfgItem, &FwCfgSize);
296 if (EFI_ERROR (Status)) {
297 return Status;
298 }
299
300 if (FwCfgSize % sizeof E820Entry != 0) {
301 return EFI_PROTOCOL_ERROR;
302 }
303
304 QemuFwCfgSelectItem (FwCfgItem);
305 for (Processed = 0; Processed < FwCfgSize; Processed += sizeof E820Entry) {
306 QemuFwCfgReadBytes (sizeof E820Entry, &E820Entry);
307 Callback (&E820Entry, PlatformInfoHob);
308 }
309
310 return EFI_SUCCESS;
311 }
312
313 /**
314 Returns PVH memmap
315
316 @param Entries Pointer to PVH memmap
317 @param Count Number of entries
318
319 @return EFI_STATUS
320 **/
321 EFI_STATUS
322 GetPvhMemmapEntries (
323 struct hvm_memmap_table_entry **Entries,
324 UINT32 *Count
325 )
326 {
327 UINT32 *PVHResetVectorData;
328 struct hvm_start_info *pvh_start_info;
329
330 PVHResetVectorData = (VOID *)(UINTN)PcdGet32 (PcdXenPvhStartOfDayStructPtr);
331 if (PVHResetVectorData == 0) {
332 return EFI_NOT_FOUND;
333 }
334
335 pvh_start_info = (struct hvm_start_info *)(UINTN)PVHResetVectorData[0];
336
337 *Entries = (struct hvm_memmap_table_entry *)(UINTN)pvh_start_info->memmap_paddr;
338 *Count = pvh_start_info->memmap_entries;
339
340 return EFI_SUCCESS;
341 }
342
343 STATIC
344 UINT64
345 GetHighestSystemMemoryAddressFromPvhMemmap (
346 BOOLEAN Below4gb
347 )
348 {
349 struct hvm_memmap_table_entry *Memmap;
350 UINT32 MemmapEntriesCount;
351 struct hvm_memmap_table_entry *Entry;
352 EFI_STATUS Status;
353 UINT32 Loop;
354 UINT64 HighestAddress;
355 UINT64 EntryEnd;
356
357 HighestAddress = 0;
358
359 Status = GetPvhMemmapEntries (&Memmap, &MemmapEntriesCount);
360 ASSERT_EFI_ERROR (Status);
361
362 for (Loop = 0; Loop < MemmapEntriesCount; Loop++) {
363 Entry = Memmap + Loop;
364 EntryEnd = Entry->addr + Entry->size;
365
366 if ((Entry->type == XEN_HVM_MEMMAP_TYPE_RAM) &&
367 (EntryEnd > HighestAddress))
368 {
369 if (Below4gb && (EntryEnd <= BASE_4GB)) {
370 HighestAddress = EntryEnd;
371 } else if (!Below4gb && (EntryEnd >= BASE_4GB)) {
372 HighestAddress = EntryEnd;
373 }
374 }
375 }
376
377 return HighestAddress;
378 }
379
380 VOID
381 EFIAPI
382 PlatformGetSystemMemorySizeBelow4gb (
383 IN EFI_HOB_PLATFORM_INFO *PlatformInfoHob
384 )
385 {
386 EFI_STATUS Status;
387 UINT8 Cmos0x34;
388 UINT8 Cmos0x35;
389
390 if ((PlatformInfoHob->HostBridgeDevId == CLOUDHV_DEVICE_ID) &&
391 (CcProbe () != CcGuestTypeIntelTdx))
392 {
393 // Get the information from PVH memmap
394 PlatformInfoHob->LowMemory = (UINT32)GetHighestSystemMemoryAddressFromPvhMemmap (TRUE);
395 return;
396 }
397
398 Status = PlatformScanE820 (PlatformGetLowMemoryCB, PlatformInfoHob);
399 if (!EFI_ERROR (Status) && (PlatformInfoHob->LowMemory > 0)) {
400 return;
401 }
402
403 //
404 // CMOS 0x34/0x35 specifies the system memory above 16 MB.
405 // * CMOS(0x35) is the high byte
406 // * CMOS(0x34) is the low byte
407 // * The size is specified in 64kb chunks
408 // * Since this is memory above 16MB, the 16MB must be added
409 // into the calculation to get the total memory size.
410 //
411
412 Cmos0x34 = (UINT8)PlatformCmosRead8 (0x34);
413 Cmos0x35 = (UINT8)PlatformCmosRead8 (0x35);
414
415 PlatformInfoHob->LowMemory = (UINT32)(((UINTN)((Cmos0x35 << 8) + Cmos0x34) << 16) + SIZE_16MB);
416 }
417
418 STATIC
419 UINT64
420 PlatformGetSystemMemorySizeAbove4gb (
421 )
422 {
423 UINT32 Size;
424 UINTN CmosIndex;
425
426 //
427 // CMOS 0x5b-0x5d specifies the system memory above 4GB MB.
428 // * CMOS(0x5d) is the most significant size byte
429 // * CMOS(0x5c) is the middle size byte
430 // * CMOS(0x5b) is the least significant size byte
431 // * The size is specified in 64kb chunks
432 //
433
434 Size = 0;
435 for (CmosIndex = 0x5d; CmosIndex >= 0x5b; CmosIndex--) {
436 Size = (UINT32)(Size << 8) + (UINT32)PlatformCmosRead8 (CmosIndex);
437 }
438
439 return LShiftU64 (Size, 16);
440 }
441
442 /**
443 Return the highest address that DXE could possibly use, plus one.
444 **/
445 STATIC
446 VOID
447 PlatformGetFirstNonAddress (
448 IN OUT EFI_HOB_PLATFORM_INFO *PlatformInfoHob
449 )
450 {
451 UINT32 FwCfgPciMmio64Mb;
452 EFI_STATUS Status;
453 FIRMWARE_CONFIG_ITEM FwCfgItem;
454 UINTN FwCfgSize;
455 UINT64 HotPlugMemoryEnd;
456
457 //
458 // If QEMU presents an E820 map, then get the highest exclusive >=4GB RAM
459 // address from it. This can express an address >= 4GB+1TB.
460 //
461 // Otherwise, get the flat size of the memory above 4GB from the CMOS (which
462 // can only express a size smaller than 1TB), and add it to 4GB.
463 //
464 PlatformInfoHob->FirstNonAddress = BASE_4GB;
465 Status = PlatformScanE820 (PlatformGetFirstNonAddressCB, PlatformInfoHob);
466 if (EFI_ERROR (Status)) {
467 PlatformInfoHob->FirstNonAddress = BASE_4GB + PlatformGetSystemMemorySizeAbove4gb ();
468 }
469
470 //
471 // If DXE is 32-bit, then we're done; PciBusDxe will degrade 64-bit MMIO
472 // resources to 32-bit anyway. See DegradeResource() in
473 // "PciResourceSupport.c".
474 //
475 #ifdef MDE_CPU_IA32
476 if (!FeaturePcdGet (PcdDxeIplSwitchToLongMode)) {
477 return;
478 }
479
480 #endif
481
482 //
483 // See if the user specified the number of megabytes for the 64-bit PCI host
484 // aperture. Accept an aperture size up to 16TB.
485 //
486 // As signaled by the "X-" prefix, this knob is experimental, and might go
487 // away at any time.
488 //
489 Status = QemuFwCfgParseUint32 (
490 "opt/ovmf/X-PciMmio64Mb",
491 FALSE,
492 &FwCfgPciMmio64Mb
493 );
494 switch (Status) {
495 case EFI_UNSUPPORTED:
496 case EFI_NOT_FOUND:
497 break;
498 case EFI_SUCCESS:
499 if (FwCfgPciMmio64Mb <= 0x1000000) {
500 PlatformInfoHob->PcdPciMmio64Size = LShiftU64 (FwCfgPciMmio64Mb, 20);
501 break;
502 }
503
504 //
505 // fall through
506 //
507 default:
508 DEBUG ((
509 DEBUG_WARN,
510 "%a: ignoring malformed 64-bit PCI host aperture size from fw_cfg\n",
511 __FUNCTION__
512 ));
513 break;
514 }
515
516 if (PlatformInfoHob->PcdPciMmio64Size == 0) {
517 if (PlatformInfoHob->BootMode != BOOT_ON_S3_RESUME) {
518 DEBUG ((
519 DEBUG_INFO,
520 "%a: disabling 64-bit PCI host aperture\n",
521 __FUNCTION__
522 ));
523 }
524
525 //
526 // There's nothing more to do; the amount of memory above 4GB fully
527 // determines the highest address plus one. The memory hotplug area (see
528 // below) plays no role for the firmware in this case.
529 //
530 return;
531 }
532
533 //
534 // The "etc/reserved-memory-end" fw_cfg file, when present, contains an
535 // absolute, exclusive end address for the memory hotplug area. This area
536 // starts right at the end of the memory above 4GB. The 64-bit PCI host
537 // aperture must be placed above it.
538 //
539 Status = QemuFwCfgFindFile (
540 "etc/reserved-memory-end",
541 &FwCfgItem,
542 &FwCfgSize
543 );
544 if (!EFI_ERROR (Status) && (FwCfgSize == sizeof HotPlugMemoryEnd)) {
545 QemuFwCfgSelectItem (FwCfgItem);
546 QemuFwCfgReadBytes (FwCfgSize, &HotPlugMemoryEnd);
547 DEBUG ((
548 DEBUG_VERBOSE,
549 "%a: HotPlugMemoryEnd=0x%Lx\n",
550 __FUNCTION__,
551 HotPlugMemoryEnd
552 ));
553
554 ASSERT (HotPlugMemoryEnd >= PlatformInfoHob->FirstNonAddress);
555 PlatformInfoHob->FirstNonAddress = HotPlugMemoryEnd;
556 }
557
558 //
559 // SeaBIOS aligns both boundaries of the 64-bit PCI host aperture to 1GB, so
560 // that the host can map it with 1GB hugepages. Follow suit.
561 //
562 PlatformInfoHob->PcdPciMmio64Base = ALIGN_VALUE (PlatformInfoHob->FirstNonAddress, (UINT64)SIZE_1GB);
563 PlatformInfoHob->PcdPciMmio64Size = ALIGN_VALUE (PlatformInfoHob->PcdPciMmio64Size, (UINT64)SIZE_1GB);
564
565 //
566 // The 64-bit PCI host aperture should also be "naturally" aligned. The
567 // alignment is determined by rounding the size of the aperture down to the
568 // next smaller or equal power of two. That is, align the aperture by the
569 // largest BAR size that can fit into it.
570 //
571 PlatformInfoHob->PcdPciMmio64Base = ALIGN_VALUE (PlatformInfoHob->PcdPciMmio64Base, GetPowerOfTwo64 (PlatformInfoHob->PcdPciMmio64Size));
572
573 //
574 // The useful address space ends with the 64-bit PCI host aperture.
575 //
576 PlatformInfoHob->FirstNonAddress = PlatformInfoHob->PcdPciMmio64Base + PlatformInfoHob->PcdPciMmio64Size;
577 return;
578 }
579
580 /*
581 * Use CPUID to figure physical address width.
582 *
583 * Does *not* work reliable on qemu. For historical reasons qemu
584 * returns phys-bits=40 by default even in case the host machine
585 * supports less than that.
586 *
587 * So we apply the following rules (which can be enabled/disabled
588 * using the QemuQuirk parameter) to figure whenever we can work with
589 * the returned physical address width or not:
590 *
591 * (1) If it is 41 or higher consider it valid.
592 * (2) If it is 40 or lower consider it valid in case it matches a
593 * known-good value for the CPU vendor, which is:
594 * -> 36 or 39 for Intel
595 * -> 40 for AMD
596 * (3) Otherwise consider it invalid.
597 *
598 * Recommendation: Run qemu with host-phys-bits=on. That will make
599 * sure guest phys-bits is not larger than host phys-bits. Some
600 * distro builds do that by default.
601 */
602 VOID
603 EFIAPI
604 PlatformAddressWidthFromCpuid (
605 IN OUT EFI_HOB_PLATFORM_INFO *PlatformInfoHob,
606 IN BOOLEAN QemuQuirk
607 )
608 {
609 UINT32 RegEax, RegEbx, RegEcx, RegEdx, Max;
610 UINT8 PhysBits;
611 CHAR8 Signature[13] = { 0 };
612 BOOLEAN Valid = FALSE;
613 BOOLEAN Page1GSupport = FALSE;
614
615 AsmCpuid (0x80000000, &RegEax, &RegEbx, &RegEcx, &RegEdx);
616 *(UINT32 *)(Signature + 0) = RegEbx;
617 *(UINT32 *)(Signature + 4) = RegEdx;
618 *(UINT32 *)(Signature + 8) = RegEcx;
619 Max = RegEax;
620
621 if (Max >= 0x80000001) {
622 AsmCpuid (0x80000001, NULL, NULL, NULL, &RegEdx);
623 if ((RegEdx & BIT26) != 0) {
624 Page1GSupport = TRUE;
625 }
626 }
627
628 if (Max >= 0x80000008) {
629 AsmCpuid (0x80000008, &RegEax, NULL, NULL, NULL);
630 PhysBits = (UINT8)RegEax;
631 } else {
632 PhysBits = 36;
633 }
634
635 if (!QemuQuirk) {
636 Valid = TRUE;
637 } else if (PhysBits >= 41) {
638 Valid = TRUE;
639 } else if (AsciiStrCmp (Signature, "GenuineIntel") == 0) {
640 if ((PhysBits == 36) || (PhysBits == 39)) {
641 Valid = TRUE;
642 }
643 } else if (AsciiStrCmp (Signature, "AuthenticAMD") == 0) {
644 if (PhysBits == 40) {
645 Valid = TRUE;
646 }
647 }
648
649 DEBUG ((
650 DEBUG_INFO,
651 "%a: Signature: '%a', PhysBits: %d, QemuQuirk: %a, Valid: %a\n",
652 __FUNCTION__,
653 Signature,
654 PhysBits,
655 QemuQuirk ? "On" : "Off",
656 Valid ? "Yes" : "No"
657 ));
658
659 if (Valid) {
660 if (PhysBits > 47) {
661 /*
662 * Avoid 5-level paging altogether for now, which limits
663 * PhysBits to 48. Also avoid using address bit 48, due to sign
664 * extension we can't identity-map these addresses (and lots of
665 * places in edk2 assume we have everything identity-mapped).
666 * So the actual limit is 47.
667 */
668 DEBUG ((DEBUG_INFO, "%a: limit PhysBits to 47 (avoid 5-level paging)\n", __func__));
669 PhysBits = 47;
670 }
671
672 if (!Page1GSupport && (PhysBits > 40)) {
673 DEBUG ((DEBUG_INFO, "%a: limit PhysBits to 40 (no 1G pages available)\n", __func__));
674 PhysBits = 40;
675 }
676
677 PlatformInfoHob->PhysMemAddressWidth = PhysBits;
678 PlatformInfoHob->FirstNonAddress = LShiftU64 (1, PlatformInfoHob->PhysMemAddressWidth);
679 }
680 }
681
682 VOID
683 EFIAPI
684 PlatformDynamicMmioWindow (
685 IN OUT EFI_HOB_PLATFORM_INFO *PlatformInfoHob
686 )
687 {
688 UINT64 AddrSpace, MmioSpace;
689
690 AddrSpace = LShiftU64 (1, PlatformInfoHob->PhysMemAddressWidth);
691 MmioSpace = LShiftU64 (1, PlatformInfoHob->PhysMemAddressWidth - 3);
692
693 if ((PlatformInfoHob->PcdPciMmio64Size < MmioSpace) &&
694 (PlatformInfoHob->PcdPciMmio64Base + MmioSpace < AddrSpace))
695 {
696 DEBUG ((DEBUG_INFO, "%a: using dynamic mmio window\n", __func__));
697 DEBUG ((DEBUG_INFO, "%a: Addr Space 0x%Lx (%Ld GB)\n", __func__, AddrSpace, RShiftU64 (AddrSpace, 30)));
698 DEBUG ((DEBUG_INFO, "%a: MMIO Space 0x%Lx (%Ld GB)\n", __func__, MmioSpace, RShiftU64 (MmioSpace, 30)));
699 PlatformInfoHob->PcdPciMmio64Size = MmioSpace;
700 PlatformInfoHob->PcdPciMmio64Base = AddrSpace - MmioSpace;
701 PlatformScanE820 (PlatformReservationConflictCB, PlatformInfoHob);
702 } else {
703 DEBUG ((DEBUG_INFO, "%a: using classic mmio window\n", __func__));
704 }
705
706 DEBUG ((DEBUG_INFO, "%a: Pci64 Base 0x%Lx\n", __func__, PlatformInfoHob->PcdPciMmio64Base));
707 DEBUG ((DEBUG_INFO, "%a: Pci64 Size 0x%Lx\n", __func__, PlatformInfoHob->PcdPciMmio64Size));
708 }
709
710 /**
711 Iterate over the PCI host bridges resources information optionally provided
712 in fw-cfg and find the highest address contained in the PCI MMIO windows. If
713 the information is found, return the exclusive end; one past the last usable
714 address.
715
716 @param[out] PciMmioAddressEnd Pointer to one-after End Address updated with
717 information extracted from host-provided data
718 or zero if no information available or an
719 error happened
720
721 @retval EFI_SUCCESS PCI information was read and the output
722 parameter updated with the last valid
723 address in the 64-bit MMIO range.
724 @retval EFI_INVALID_PARAMETER Pointer parameter is invalid
725 @retval EFI_INCOMPATIBLE_VERSION Hardware information found in fw-cfg
726 has an incompatible format
727 @retval EFI_UNSUPPORTED Fw-cfg is not supported, thus host
728 provided information, if any, cannot be
729 read
730 @retval EFI_NOT_FOUND No PCI host bridge information provided
731 by the host.
732 **/
733 STATIC
734 EFI_STATUS
735 PlatformScanHostProvided64BitPciMmioEnd (
736 OUT UINT64 *PciMmioAddressEnd
737 )
738 {
739 EFI_STATUS Status;
740 HOST_BRIDGE_INFO HostBridge;
741 FIRMWARE_CONFIG_ITEM FwCfgItem;
742 UINTN FwCfgSize;
743 UINTN FwCfgReadIndex;
744 UINTN ReadDataSize;
745 UINT64 Above4GMmioEnd;
746
747 if (PciMmioAddressEnd == NULL) {
748 return EFI_INVALID_PARAMETER;
749 }
750
751 *PciMmioAddressEnd = 0;
752 Above4GMmioEnd = 0;
753
754 Status = QemuFwCfgFindFile ("etc/hardware-info", &FwCfgItem, &FwCfgSize);
755 if (EFI_ERROR (Status)) {
756 return Status;
757 }
758
759 QemuFwCfgSelectItem (FwCfgItem);
760
761 FwCfgReadIndex = 0;
762 while (FwCfgReadIndex < FwCfgSize) {
763 Status = QemuFwCfgReadNextHardwareInfoByType (
764 HardwareInfoTypeHostBridge,
765 sizeof (HostBridge),
766 FwCfgSize,
767 &HostBridge,
768 &ReadDataSize,
769 &FwCfgReadIndex
770 );
771
772 if (Status != EFI_SUCCESS) {
773 //
774 // No more data available to read in the file, break
775 // loop and finish process
776 //
777 break;
778 }
779
780 Status = HardwareInfoPciHostBridgeLastMmioAddress (
781 &HostBridge,
782 ReadDataSize,
783 TRUE,
784 &Above4GMmioEnd
785 );
786
787 if (Status != EFI_SUCCESS) {
788 //
789 // Error parsing MMIO apertures and extracting last MMIO
790 // address, reset PciMmioAddressEnd as if no information was
791 // found, to avoid moving forward with incomplete data, and
792 // bail out
793 //
794 DEBUG ((
795 DEBUG_ERROR,
796 "%a: ignoring malformed hardware information from fw_cfg\n",
797 __FUNCTION__
798 ));
799 *PciMmioAddressEnd = 0;
800 return Status;
801 }
802
803 if (Above4GMmioEnd > *PciMmioAddressEnd) {
804 *PciMmioAddressEnd = Above4GMmioEnd;
805 }
806 }
807
808 if (*PciMmioAddressEnd > 0) {
809 //
810 // Host-provided PCI information was found and a MMIO window end
811 // derived from it.
812 // Increase the End address by one to have the output pointing to
813 // one after the address in use (exclusive end).
814 //
815 *PciMmioAddressEnd += 1;
816
817 DEBUG ((
818 DEBUG_INFO,
819 "%a: Pci64End=0x%Lx\n",
820 __FUNCTION__,
821 *PciMmioAddressEnd
822 ));
823
824 return EFI_SUCCESS;
825 }
826
827 return EFI_NOT_FOUND;
828 }
829
830 /**
831 Initialize the PhysMemAddressWidth field in PlatformInfoHob based on guest RAM size.
832 **/
833 VOID
834 EFIAPI
835 PlatformAddressWidthInitialization (
836 IN OUT EFI_HOB_PLATFORM_INFO *PlatformInfoHob
837 )
838 {
839 UINT8 PhysMemAddressWidth;
840 EFI_STATUS Status;
841
842 if (PlatformInfoHob->HostBridgeDevId == 0xffff /* microvm */) {
843 PlatformAddressWidthFromCpuid (PlatformInfoHob, FALSE);
844 return;
845 }
846
847 //
848 // First scan host-provided hardware information to assess if the address
849 // space is already known. If so, guest must use those values.
850 //
851 Status = PlatformScanHostProvided64BitPciMmioEnd (&PlatformInfoHob->FirstNonAddress);
852
853 if (EFI_ERROR (Status)) {
854 //
855 // If the host did not provide valid hardware information leading to a
856 // hard-defined 64-bit MMIO end, fold back to calculating the minimum range
857 // needed.
858 // As guest-physical memory size grows, the permanent PEI RAM requirements
859 // are dominated by the identity-mapping page tables built by the DXE IPL.
860 // The DXL IPL keys off of the physical address bits advertized in the CPU
861 // HOB. To conserve memory, we calculate the minimum address width here.
862 //
863 PlatformGetFirstNonAddress (PlatformInfoHob);
864 }
865
866 PlatformAddressWidthFromCpuid (PlatformInfoHob, TRUE);
867 if (PlatformInfoHob->PhysMemAddressWidth != 0) {
868 // physical address width is known
869 PlatformDynamicMmioWindow (PlatformInfoHob);
870 return;
871 }
872
873 //
874 // physical address width is NOT known
875 // -> do some guess work, mostly based on installed memory
876 // -> try be conservstibe to stay below the guaranteed minimum of
877 // 36 phys bits (aka 64 GB).
878 //
879 PhysMemAddressWidth = (UINT8)HighBitSet64 (PlatformInfoHob->FirstNonAddress);
880
881 //
882 // If FirstNonAddress is not an integral power of two, then we need an
883 // additional bit.
884 //
885 if ((PlatformInfoHob->FirstNonAddress & (PlatformInfoHob->FirstNonAddress - 1)) != 0) {
886 ++PhysMemAddressWidth;
887 }
888
889 //
890 // The minimum address width is 36 (covers up to and excluding 64 GB, which
891 // is the maximum for Ia32 + PAE). The theoretical architecture maximum for
892 // X64 long mode is 52 bits, but the DXE IPL clamps that down to 48 bits. We
893 // can simply assert that here, since 48 bits are good enough for 256 TB.
894 //
895 if (PhysMemAddressWidth <= 36) {
896 PhysMemAddressWidth = 36;
897 }
898
899 #if defined (MDE_CPU_X64)
900 if (TdIsEnabled ()) {
901 if (TdSharedPageMask () == (1ULL << 47)) {
902 PhysMemAddressWidth = 48;
903 } else {
904 PhysMemAddressWidth = 52;
905 }
906 }
907
908 ASSERT (PhysMemAddressWidth <= 52);
909 #else
910 ASSERT (PhysMemAddressWidth <= 48);
911 #endif
912
913 PlatformInfoHob->PhysMemAddressWidth = PhysMemAddressWidth;
914 }
915
916 STATIC
917 VOID
918 QemuInitializeRamBelow1gb (
919 IN EFI_HOB_PLATFORM_INFO *PlatformInfoHob
920 )
921 {
922 if (PlatformInfoHob->SmmSmramRequire && PlatformInfoHob->Q35SmramAtDefaultSmbase) {
923 PlatformAddMemoryRangeHob (0, SMM_DEFAULT_SMBASE);
924 PlatformAddReservedMemoryBaseSizeHob (
925 SMM_DEFAULT_SMBASE,
926 MCH_DEFAULT_SMBASE_SIZE,
927 TRUE /* Cacheable */
928 );
929 STATIC_ASSERT (
930 SMM_DEFAULT_SMBASE + MCH_DEFAULT_SMBASE_SIZE < BASE_512KB + BASE_128KB,
931 "end of SMRAM at default SMBASE ends at, or exceeds, 640KB"
932 );
933 PlatformAddMemoryRangeHob (
934 SMM_DEFAULT_SMBASE + MCH_DEFAULT_SMBASE_SIZE,
935 BASE_512KB + BASE_128KB
936 );
937 } else {
938 PlatformAddMemoryRangeHob (0, BASE_512KB + BASE_128KB);
939 }
940 }
941
942 /**
943 Peform Memory Detection for QEMU / KVM
944
945 **/
946 VOID
947 EFIAPI
948 PlatformQemuInitializeRam (
949 IN EFI_HOB_PLATFORM_INFO *PlatformInfoHob
950 )
951 {
952 UINT64 UpperMemorySize;
953 MTRR_SETTINGS MtrrSettings;
954 EFI_STATUS Status;
955
956 DEBUG ((DEBUG_INFO, "%a called\n", __FUNCTION__));
957
958 //
959 // Determine total memory size available
960 //
961 PlatformGetSystemMemorySizeBelow4gb (PlatformInfoHob);
962
963 if (PlatformInfoHob->BootMode == BOOT_ON_S3_RESUME) {
964 //
965 // Create the following memory HOB as an exception on the S3 boot path.
966 //
967 // Normally we'd create memory HOBs only on the normal boot path. However,
968 // CpuMpPei specifically needs such a low-memory HOB on the S3 path as
969 // well, for "borrowing" a subset of it temporarily, for the AP startup
970 // vector.
971 //
972 // CpuMpPei saves the original contents of the borrowed area in permanent
973 // PEI RAM, in a backup buffer allocated with the normal PEI services.
974 // CpuMpPei restores the original contents ("returns" the borrowed area) at
975 // End-of-PEI. End-of-PEI in turn is emitted by S3Resume2Pei before
976 // transferring control to the OS's wakeup vector in the FACS.
977 //
978 // We expect any other PEIMs that "borrow" memory similarly to CpuMpPei to
979 // restore the original contents. Furthermore, we expect all such PEIMs
980 // (CpuMpPei included) to claim the borrowed areas by producing memory
981 // allocation HOBs, and to honor preexistent memory allocation HOBs when
982 // looking for an area to borrow.
983 //
984 QemuInitializeRamBelow1gb (PlatformInfoHob);
985 } else {
986 //
987 // Create memory HOBs
988 //
989 QemuInitializeRamBelow1gb (PlatformInfoHob);
990
991 if (PlatformInfoHob->SmmSmramRequire) {
992 UINT32 TsegSize;
993
994 TsegSize = PlatformInfoHob->Q35TsegMbytes * SIZE_1MB;
995 PlatformAddMemoryRangeHob (BASE_1MB, PlatformInfoHob->LowMemory - TsegSize);
996 PlatformAddReservedMemoryBaseSizeHob (
997 PlatformInfoHob->LowMemory - TsegSize,
998 TsegSize,
999 TRUE
1000 );
1001 } else {
1002 PlatformAddMemoryRangeHob (BASE_1MB, PlatformInfoHob->LowMemory);
1003 }
1004
1005 //
1006 // If QEMU presents an E820 map, then create memory HOBs for the >=4GB RAM
1007 // entries. Otherwise, create a single memory HOB with the flat >=4GB
1008 // memory size read from the CMOS.
1009 //
1010 Status = PlatformScanE820 (PlatformAddHobCB, PlatformInfoHob);
1011 if (EFI_ERROR (Status)) {
1012 UpperMemorySize = PlatformGetSystemMemorySizeAbove4gb ();
1013 if (UpperMemorySize != 0) {
1014 PlatformAddMemoryBaseSizeHob (BASE_4GB, UpperMemorySize);
1015 }
1016 }
1017 }
1018
1019 //
1020 // We'd like to keep the following ranges uncached:
1021 // - [640 KB, 1 MB)
1022 // - [Uc32Base, 4 GB)
1023 //
1024 // Everything else should be WB. Unfortunately, programming the inverse (ie.
1025 // keeping the default UC, and configuring the complement set of the above as
1026 // WB) is not reliable in general, because the end of the upper RAM can have
1027 // practically any alignment, and we may not have enough variable MTRRs to
1028 // cover it exactly.
1029 //
1030 if (IsMtrrSupported () && (PlatformInfoHob->HostBridgeDevId != CLOUDHV_DEVICE_ID)) {
1031 MtrrGetAllMtrrs (&MtrrSettings);
1032
1033 //
1034 // MTRRs disabled, fixed MTRRs disabled, default type is uncached
1035 //
1036 ASSERT ((MtrrSettings.MtrrDefType & BIT11) == 0);
1037 ASSERT ((MtrrSettings.MtrrDefType & BIT10) == 0);
1038 ASSERT ((MtrrSettings.MtrrDefType & 0xFF) == 0);
1039
1040 //
1041 // flip default type to writeback
1042 //
1043 SetMem (&MtrrSettings.Fixed, sizeof MtrrSettings.Fixed, 0x06);
1044 ZeroMem (&MtrrSettings.Variables, sizeof MtrrSettings.Variables);
1045 MtrrSettings.MtrrDefType |= BIT11 | BIT10 | 6;
1046 MtrrSetAllMtrrs (&MtrrSettings);
1047
1048 //
1049 // Set memory range from 640KB to 1MB to uncacheable
1050 //
1051 Status = MtrrSetMemoryAttribute (
1052 BASE_512KB + BASE_128KB,
1053 BASE_1MB - (BASE_512KB + BASE_128KB),
1054 CacheUncacheable
1055 );
1056 ASSERT_EFI_ERROR (Status);
1057
1058 //
1059 // Set the memory range from the start of the 32-bit MMIO area (32-bit PCI
1060 // MMIO aperture on i440fx, PCIEXBAR on q35) to 4GB as uncacheable.
1061 //
1062 Status = MtrrSetMemoryAttribute (
1063 PlatformInfoHob->Uc32Base,
1064 SIZE_4GB - PlatformInfoHob->Uc32Base,
1065 CacheUncacheable
1066 );
1067 ASSERT_EFI_ERROR (Status);
1068 }
1069 }
1070
1071 VOID
1072 EFIAPI
1073 PlatformQemuInitializeRamForS3 (
1074 IN EFI_HOB_PLATFORM_INFO *PlatformInfoHob
1075 )
1076 {
1077 if (PlatformInfoHob->S3Supported && (PlatformInfoHob->BootMode != BOOT_ON_S3_RESUME)) {
1078 //
1079 // This is the memory range that will be used for PEI on S3 resume
1080 //
1081 BuildMemoryAllocationHob (
1082 PlatformInfoHob->S3AcpiReservedMemoryBase,
1083 PlatformInfoHob->S3AcpiReservedMemorySize,
1084 EfiACPIMemoryNVS
1085 );
1086
1087 //
1088 // Cover the initial RAM area used as stack and temporary PEI heap.
1089 //
1090 // This is reserved as ACPI NVS so it can be used on S3 resume.
1091 //
1092 BuildMemoryAllocationHob (
1093 PcdGet32 (PcdOvmfSecPeiTempRamBase),
1094 PcdGet32 (PcdOvmfSecPeiTempRamSize),
1095 EfiACPIMemoryNVS
1096 );
1097
1098 //
1099 // SEC stores its table of GUIDed section handlers here.
1100 //
1101 BuildMemoryAllocationHob (
1102 PcdGet64 (PcdGuidedExtractHandlerTableAddress),
1103 PcdGet32 (PcdGuidedExtractHandlerTableSize),
1104 EfiACPIMemoryNVS
1105 );
1106
1107 #ifdef MDE_CPU_X64
1108 //
1109 // Reserve the initial page tables built by the reset vector code.
1110 //
1111 // Since this memory range will be used by the Reset Vector on S3
1112 // resume, it must be reserved as ACPI NVS.
1113 //
1114 BuildMemoryAllocationHob (
1115 (EFI_PHYSICAL_ADDRESS)(UINTN)PcdGet32 (PcdOvmfSecPageTablesBase),
1116 (UINT64)(UINTN)PcdGet32 (PcdOvmfSecPageTablesSize),
1117 EfiACPIMemoryNVS
1118 );
1119
1120 if (PlatformInfoHob->SevEsIsEnabled) {
1121 //
1122 // If SEV-ES is enabled, reserve the GHCB-related memory area. This
1123 // includes the extra page table used to break down the 2MB page
1124 // mapping into 4KB page entries where the GHCB resides and the
1125 // GHCB area itself.
1126 //
1127 // Since this memory range will be used by the Reset Vector on S3
1128 // resume, it must be reserved as ACPI NVS.
1129 //
1130 BuildMemoryAllocationHob (
1131 (EFI_PHYSICAL_ADDRESS)(UINTN)PcdGet32 (PcdOvmfSecGhcbPageTableBase),
1132 (UINT64)(UINTN)PcdGet32 (PcdOvmfSecGhcbPageTableSize),
1133 EfiACPIMemoryNVS
1134 );
1135 BuildMemoryAllocationHob (
1136 (EFI_PHYSICAL_ADDRESS)(UINTN)PcdGet32 (PcdOvmfSecGhcbBase),
1137 (UINT64)(UINTN)PcdGet32 (PcdOvmfSecGhcbSize),
1138 EfiACPIMemoryNVS
1139 );
1140 BuildMemoryAllocationHob (
1141 (EFI_PHYSICAL_ADDRESS)(UINTN)PcdGet32 (PcdOvmfSecGhcbBackupBase),
1142 (UINT64)(UINTN)PcdGet32 (PcdOvmfSecGhcbBackupSize),
1143 EfiACPIMemoryNVS
1144 );
1145 }
1146
1147 #endif
1148 }
1149
1150 if (PlatformInfoHob->BootMode != BOOT_ON_S3_RESUME) {
1151 if (!PlatformInfoHob->SmmSmramRequire) {
1152 //
1153 // Reserve the lock box storage area
1154 //
1155 // Since this memory range will be used on S3 resume, it must be
1156 // reserved as ACPI NVS.
1157 //
1158 // If S3 is unsupported, then various drivers might still write to the
1159 // LockBox area. We ought to prevent DXE from serving allocation requests
1160 // such that they would overlap the LockBox storage.
1161 //
1162 ZeroMem (
1163 (VOID *)(UINTN)PcdGet32 (PcdOvmfLockBoxStorageBase),
1164 (UINTN)PcdGet32 (PcdOvmfLockBoxStorageSize)
1165 );
1166 BuildMemoryAllocationHob (
1167 (EFI_PHYSICAL_ADDRESS)(UINTN)PcdGet32 (PcdOvmfLockBoxStorageBase),
1168 (UINT64)(UINTN)PcdGet32 (PcdOvmfLockBoxStorageSize),
1169 PlatformInfoHob->S3Supported ? EfiACPIMemoryNVS : EfiBootServicesData
1170 );
1171 }
1172
1173 if (PlatformInfoHob->SmmSmramRequire) {
1174 UINT32 TsegSize;
1175
1176 //
1177 // Make sure the TSEG area that we reported as a reserved memory resource
1178 // cannot be used for reserved memory allocations.
1179 //
1180 PlatformGetSystemMemorySizeBelow4gb (PlatformInfoHob);
1181 TsegSize = PlatformInfoHob->Q35TsegMbytes * SIZE_1MB;
1182 BuildMemoryAllocationHob (
1183 PlatformInfoHob->LowMemory - TsegSize,
1184 TsegSize,
1185 EfiReservedMemoryType
1186 );
1187 //
1188 // Similarly, allocate away the (already reserved) SMRAM at the default
1189 // SMBASE, if it exists.
1190 //
1191 if (PlatformInfoHob->Q35SmramAtDefaultSmbase) {
1192 BuildMemoryAllocationHob (
1193 SMM_DEFAULT_SMBASE,
1194 MCH_DEFAULT_SMBASE_SIZE,
1195 EfiReservedMemoryType
1196 );
1197 }
1198 }
1199
1200 #ifdef MDE_CPU_X64
1201 if (FixedPcdGet32 (PcdOvmfWorkAreaSize) != 0) {
1202 //
1203 // Reserve the work area.
1204 //
1205 // Since this memory range will be used by the Reset Vector on S3
1206 // resume, it must be reserved as ACPI NVS.
1207 //
1208 // If S3 is unsupported, then various drivers might still write to the
1209 // work area. We ought to prevent DXE from serving allocation requests
1210 // such that they would overlap the work area.
1211 //
1212 BuildMemoryAllocationHob (
1213 (EFI_PHYSICAL_ADDRESS)(UINTN)FixedPcdGet32 (PcdOvmfWorkAreaBase),
1214 (UINT64)(UINTN)FixedPcdGet32 (PcdOvmfWorkAreaSize),
1215 PlatformInfoHob->S3Supported ? EfiACPIMemoryNVS : EfiBootServicesData
1216 );
1217 }
1218
1219 #endif
1220 }
1221 }