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