]> git.proxmox.com Git - mirror_edk2.git/blob - OvmfPkg/PlatformPei/MemDetect.c
OvmfPkg: Apply uncrustify changes
[mirror_edk2.git] / OvmfPkg / PlatformPei / 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 <PiPei.h>
20 #include <Register/Intel/SmramSaveStateMap.h>
21
22 //
23 // The Library classes this module consumes
24 //
25 #include <Library/BaseLib.h>
26 #include <Library/BaseMemoryLib.h>
27 #include <Library/DebugLib.h>
28 #include <Library/HobLib.h>
29 #include <Library/IoLib.h>
30 #include <Library/MemEncryptSevLib.h>
31 #include <Library/PcdLib.h>
32 #include <Library/PciLib.h>
33 #include <Library/PeimEntryPoint.h>
34 #include <Library/ResourcePublicationLib.h>
35 #include <Library/MtrrLib.h>
36 #include <Library/QemuFwCfgLib.h>
37 #include <Library/QemuFwCfgSimpleParserLib.h>
38
39 #include "Platform.h"
40 #include "Cmos.h"
41
42 UINT8 mPhysMemAddressWidth;
43
44 STATIC UINT32 mS3AcpiReservedMemoryBase;
45 STATIC UINT32 mS3AcpiReservedMemorySize;
46
47 STATIC UINT16 mQ35TsegMbytes;
48
49 BOOLEAN mQ35SmramAtDefaultSmbase;
50
51 UINT32 mQemuUc32Base;
52
53 VOID
54 Q35TsegMbytesInitialization (
55 VOID
56 )
57 {
58 UINT16 ExtendedTsegMbytes;
59 RETURN_STATUS PcdStatus;
60
61 ASSERT (mHostBridgeDevId == INTEL_Q35_MCH_DEVICE_ID);
62
63 //
64 // Check if QEMU offers an extended TSEG.
65 //
66 // This can be seen from writing MCH_EXT_TSEG_MB_QUERY to the MCH_EXT_TSEG_MB
67 // register, and reading back the register.
68 //
69 // On a QEMU machine type that does not offer an extended TSEG, the initial
70 // write overwrites whatever value a malicious guest OS may have placed in
71 // the (unimplemented) register, before entering S3 or rebooting.
72 // Subsequently, the read returns MCH_EXT_TSEG_MB_QUERY unchanged.
73 //
74 // On a QEMU machine type that offers an extended TSEG, the initial write
75 // triggers an update to the register. Subsequently, the value read back
76 // (which is guaranteed to differ from MCH_EXT_TSEG_MB_QUERY) tells us the
77 // number of megabytes.
78 //
79 PciWrite16 (DRAMC_REGISTER_Q35 (MCH_EXT_TSEG_MB), MCH_EXT_TSEG_MB_QUERY);
80 ExtendedTsegMbytes = PciRead16 (DRAMC_REGISTER_Q35 (MCH_EXT_TSEG_MB));
81 if (ExtendedTsegMbytes == MCH_EXT_TSEG_MB_QUERY) {
82 mQ35TsegMbytes = PcdGet16 (PcdQ35TsegMbytes);
83 return;
84 }
85
86 DEBUG ((
87 DEBUG_INFO,
88 "%a: QEMU offers an extended TSEG (%d MB)\n",
89 __FUNCTION__,
90 ExtendedTsegMbytes
91 ));
92 PcdStatus = PcdSet16S (PcdQ35TsegMbytes, ExtendedTsegMbytes);
93 ASSERT_RETURN_ERROR (PcdStatus);
94 mQ35TsegMbytes = ExtendedTsegMbytes;
95 }
96
97 VOID
98 Q35SmramAtDefaultSmbaseInitialization (
99 VOID
100 )
101 {
102 RETURN_STATUS PcdStatus;
103
104 ASSERT (mHostBridgeDevId == INTEL_Q35_MCH_DEVICE_ID);
105
106 mQ35SmramAtDefaultSmbase = FALSE;
107 if (FeaturePcdGet (PcdCsmEnable)) {
108 DEBUG ((
109 DEBUG_INFO,
110 "%a: SMRAM at default SMBASE not checked due to CSM\n",
111 __FUNCTION__
112 ));
113 } else {
114 UINTN CtlReg;
115 UINT8 CtlRegVal;
116
117 CtlReg = DRAMC_REGISTER_Q35 (MCH_DEFAULT_SMBASE_CTL);
118 PciWrite8 (CtlReg, MCH_DEFAULT_SMBASE_QUERY);
119 CtlRegVal = PciRead8 (CtlReg);
120 mQ35SmramAtDefaultSmbase = (BOOLEAN)(CtlRegVal ==
121 MCH_DEFAULT_SMBASE_IN_RAM);
122 DEBUG ((
123 DEBUG_INFO,
124 "%a: SMRAM at default SMBASE %a\n",
125 __FUNCTION__,
126 mQ35SmramAtDefaultSmbase ? "found" : "not found"
127 ));
128 }
129
130 PcdStatus = PcdSetBoolS (
131 PcdQ35SmramAtDefaultSmbase,
132 mQ35SmramAtDefaultSmbase
133 );
134 ASSERT_RETURN_ERROR (PcdStatus);
135 }
136
137 VOID
138 QemuUc32BaseInitialization (
139 VOID
140 )
141 {
142 UINT32 LowerMemorySize;
143 UINT32 Uc32Size;
144
145 if (mHostBridgeDevId == 0xffff /* microvm */) {
146 return;
147 }
148
149 if (mHostBridgeDevId == INTEL_Q35_MCH_DEVICE_ID) {
150 //
151 // On q35, the 32-bit area that we'll mark as UC, through variable MTRRs,
152 // starts at PcdPciExpressBaseAddress. The platform DSC is responsible for
153 // setting PcdPciExpressBaseAddress such that describing the
154 // [PcdPciExpressBaseAddress, 4GB) range require a very small number of
155 // variable MTRRs (preferably 1 or 2).
156 //
157 ASSERT (FixedPcdGet64 (PcdPciExpressBaseAddress) <= MAX_UINT32);
158 mQemuUc32Base = (UINT32)FixedPcdGet64 (PcdPciExpressBaseAddress);
159 return;
160 }
161
162 ASSERT (mHostBridgeDevId == INTEL_82441_DEVICE_ID);
163 //
164 // On i440fx, start with the [LowerMemorySize, 4GB) range. Make sure one
165 // variable MTRR suffices by truncating the size to a whole power of two,
166 // while keeping the end affixed to 4GB. This will round the base up.
167 //
168 LowerMemorySize = GetSystemMemorySizeBelow4gb ();
169 Uc32Size = GetPowerOfTwo32 ((UINT32)(SIZE_4GB - LowerMemorySize));
170 mQemuUc32Base = (UINT32)(SIZE_4GB - Uc32Size);
171 //
172 // Assuming that LowerMemorySize is at least 1 byte, Uc32Size is at most 2GB.
173 // Therefore mQemuUc32Base is at least 2GB.
174 //
175 ASSERT (mQemuUc32Base >= BASE_2GB);
176
177 if (mQemuUc32Base != LowerMemorySize) {
178 DEBUG ((
179 DEBUG_VERBOSE,
180 "%a: rounded UC32 base from 0x%x up to 0x%x, for "
181 "an UC32 size of 0x%x\n",
182 __FUNCTION__,
183 LowerMemorySize,
184 mQemuUc32Base,
185 Uc32Size
186 ));
187 }
188 }
189
190 /**
191 Iterate over the RAM entries in QEMU's fw_cfg E820 RAM map that start outside
192 of the 32-bit address range.
193
194 Find the highest exclusive >=4GB RAM address, or produce memory resource
195 descriptor HOBs for RAM entries that start at or above 4GB.
196
197 @param[out] MaxAddress If MaxAddress is NULL, then ScanOrAdd64BitE820Ram()
198 produces memory resource descriptor HOBs for RAM
199 entries that start at or above 4GB.
200
201 Otherwise, MaxAddress holds the highest exclusive
202 >=4GB RAM address on output. If QEMU's fw_cfg E820
203 RAM map contains no RAM entry that starts outside of
204 the 32-bit address range, then MaxAddress is exactly
205 4GB on output.
206
207 @retval EFI_SUCCESS The fw_cfg E820 RAM map was found and processed.
208
209 @retval EFI_PROTOCOL_ERROR The RAM map was found, but its size wasn't a
210 whole multiple of sizeof(EFI_E820_ENTRY64). No
211 RAM entry was processed.
212
213 @return Error codes from QemuFwCfgFindFile(). No RAM
214 entry was processed.
215 **/
216 STATIC
217 EFI_STATUS
218 ScanOrAdd64BitE820Ram (
219 OUT UINT64 *MaxAddress OPTIONAL
220 )
221 {
222 EFI_STATUS Status;
223 FIRMWARE_CONFIG_ITEM FwCfgItem;
224 UINTN FwCfgSize;
225 EFI_E820_ENTRY64 E820Entry;
226 UINTN Processed;
227
228 Status = QemuFwCfgFindFile ("etc/e820", &FwCfgItem, &FwCfgSize);
229 if (EFI_ERROR (Status)) {
230 return Status;
231 }
232
233 if (FwCfgSize % sizeof E820Entry != 0) {
234 return EFI_PROTOCOL_ERROR;
235 }
236
237 if (MaxAddress != NULL) {
238 *MaxAddress = BASE_4GB;
239 }
240
241 QemuFwCfgSelectItem (FwCfgItem);
242 for (Processed = 0; Processed < FwCfgSize; Processed += sizeof E820Entry) {
243 QemuFwCfgReadBytes (sizeof E820Entry, &E820Entry);
244 DEBUG ((
245 DEBUG_VERBOSE,
246 "%a: Base=0x%Lx Length=0x%Lx Type=%u\n",
247 __FUNCTION__,
248 E820Entry.BaseAddr,
249 E820Entry.Length,
250 E820Entry.Type
251 ));
252 if ((E820Entry.Type == EfiAcpiAddressRangeMemory) &&
253 (E820Entry.BaseAddr >= BASE_4GB))
254 {
255 if (MaxAddress == NULL) {
256 UINT64 Base;
257 UINT64 End;
258
259 //
260 // Round up the start address, and round down the end address.
261 //
262 Base = ALIGN_VALUE (E820Entry.BaseAddr, (UINT64)EFI_PAGE_SIZE);
263 End = (E820Entry.BaseAddr + E820Entry.Length) &
264 ~(UINT64)EFI_PAGE_MASK;
265 if (Base < End) {
266 AddMemoryRangeHob (Base, End);
267 DEBUG ((
268 DEBUG_VERBOSE,
269 "%a: AddMemoryRangeHob [0x%Lx, 0x%Lx)\n",
270 __FUNCTION__,
271 Base,
272 End
273 ));
274 }
275 } else {
276 UINT64 Candidate;
277
278 Candidate = E820Entry.BaseAddr + E820Entry.Length;
279 if (Candidate > *MaxAddress) {
280 *MaxAddress = Candidate;
281 DEBUG ((
282 DEBUG_VERBOSE,
283 "%a: MaxAddress=0x%Lx\n",
284 __FUNCTION__,
285 *MaxAddress
286 ));
287 }
288 }
289 }
290 }
291
292 return EFI_SUCCESS;
293 }
294
295 UINT32
296 GetSystemMemorySizeBelow4gb (
297 VOID
298 )
299 {
300 UINT8 Cmos0x34;
301 UINT8 Cmos0x35;
302
303 //
304 // CMOS 0x34/0x35 specifies the system memory above 16 MB.
305 // * CMOS(0x35) is the high byte
306 // * CMOS(0x34) is the low byte
307 // * The size is specified in 64kb chunks
308 // * Since this is memory above 16MB, the 16MB must be added
309 // into the calculation to get the total memory size.
310 //
311
312 Cmos0x34 = (UINT8)CmosRead8 (0x34);
313 Cmos0x35 = (UINT8)CmosRead8 (0x35);
314
315 return (UINT32)(((UINTN)((Cmos0x35 << 8) + Cmos0x34) << 16) + SIZE_16MB);
316 }
317
318 STATIC
319 UINT64
320 GetSystemMemorySizeAbove4gb (
321 )
322 {
323 UINT32 Size;
324 UINTN CmosIndex;
325
326 //
327 // CMOS 0x5b-0x5d specifies the system memory above 4GB MB.
328 // * CMOS(0x5d) is the most significant size byte
329 // * CMOS(0x5c) is the middle size byte
330 // * CMOS(0x5b) is the least significant size byte
331 // * The size is specified in 64kb chunks
332 //
333
334 Size = 0;
335 for (CmosIndex = 0x5d; CmosIndex >= 0x5b; CmosIndex--) {
336 Size = (UINT32)(Size << 8) + (UINT32)CmosRead8 (CmosIndex);
337 }
338
339 return LShiftU64 (Size, 16);
340 }
341
342 /**
343 Return the highest address that DXE could possibly use, plus one.
344 **/
345 STATIC
346 UINT64
347 GetFirstNonAddress (
348 VOID
349 )
350 {
351 UINT64 FirstNonAddress;
352 UINT64 Pci64Base, Pci64Size;
353 UINT32 FwCfgPciMmio64Mb;
354 EFI_STATUS Status;
355 FIRMWARE_CONFIG_ITEM FwCfgItem;
356 UINTN FwCfgSize;
357 UINT64 HotPlugMemoryEnd;
358 RETURN_STATUS PcdStatus;
359
360 //
361 // set FirstNonAddress to suppress incorrect compiler/analyzer warnings
362 //
363 FirstNonAddress = 0;
364
365 //
366 // If QEMU presents an E820 map, then get the highest exclusive >=4GB RAM
367 // address from it. This can express an address >= 4GB+1TB.
368 //
369 // Otherwise, get the flat size of the memory above 4GB from the CMOS (which
370 // can only express a size smaller than 1TB), and add it to 4GB.
371 //
372 Status = ScanOrAdd64BitE820Ram (&FirstNonAddress);
373 if (EFI_ERROR (Status)) {
374 FirstNonAddress = BASE_4GB + GetSystemMemorySizeAbove4gb ();
375 }
376
377 //
378 // If DXE is 32-bit, then we're done; PciBusDxe will degrade 64-bit MMIO
379 // resources to 32-bit anyway. See DegradeResource() in
380 // "PciResourceSupport.c".
381 //
382 #ifdef MDE_CPU_IA32
383 if (!FeaturePcdGet (PcdDxeIplSwitchToLongMode)) {
384 return FirstNonAddress;
385 }
386
387 #endif
388
389 //
390 // Otherwise, in order to calculate the highest address plus one, we must
391 // consider the 64-bit PCI host aperture too. Fetch the default size.
392 //
393 Pci64Size = PcdGet64 (PcdPciMmio64Size);
394
395 //
396 // See if the user specified the number of megabytes for the 64-bit PCI host
397 // aperture. Accept an aperture size up to 16TB.
398 //
399 // As signaled by the "X-" prefix, this knob is experimental, and might go
400 // away at any time.
401 //
402 Status = QemuFwCfgParseUint32 (
403 "opt/ovmf/X-PciMmio64Mb",
404 FALSE,
405 &FwCfgPciMmio64Mb
406 );
407 switch (Status) {
408 case EFI_UNSUPPORTED:
409 case EFI_NOT_FOUND:
410 break;
411 case EFI_SUCCESS:
412 if (FwCfgPciMmio64Mb <= 0x1000000) {
413 Pci64Size = LShiftU64 (FwCfgPciMmio64Mb, 20);
414 break;
415 }
416
417 //
418 // fall through
419 //
420 default:
421 DEBUG ((
422 DEBUG_WARN,
423 "%a: ignoring malformed 64-bit PCI host aperture size from fw_cfg\n",
424 __FUNCTION__
425 ));
426 break;
427 }
428
429 if (Pci64Size == 0) {
430 if (mBootMode != BOOT_ON_S3_RESUME) {
431 DEBUG ((
432 DEBUG_INFO,
433 "%a: disabling 64-bit PCI host aperture\n",
434 __FUNCTION__
435 ));
436 PcdStatus = PcdSet64S (PcdPciMmio64Size, 0);
437 ASSERT_RETURN_ERROR (PcdStatus);
438 }
439
440 //
441 // There's nothing more to do; the amount of memory above 4GB fully
442 // determines the highest address plus one. The memory hotplug area (see
443 // below) plays no role for the firmware in this case.
444 //
445 return FirstNonAddress;
446 }
447
448 //
449 // The "etc/reserved-memory-end" fw_cfg file, when present, contains an
450 // absolute, exclusive end address for the memory hotplug area. This area
451 // starts right at the end of the memory above 4GB. The 64-bit PCI host
452 // aperture must be placed above it.
453 //
454 Status = QemuFwCfgFindFile (
455 "etc/reserved-memory-end",
456 &FwCfgItem,
457 &FwCfgSize
458 );
459 if (!EFI_ERROR (Status) && (FwCfgSize == sizeof HotPlugMemoryEnd)) {
460 QemuFwCfgSelectItem (FwCfgItem);
461 QemuFwCfgReadBytes (FwCfgSize, &HotPlugMemoryEnd);
462 DEBUG ((
463 DEBUG_VERBOSE,
464 "%a: HotPlugMemoryEnd=0x%Lx\n",
465 __FUNCTION__,
466 HotPlugMemoryEnd
467 ));
468
469 ASSERT (HotPlugMemoryEnd >= FirstNonAddress);
470 FirstNonAddress = HotPlugMemoryEnd;
471 }
472
473 //
474 // SeaBIOS aligns both boundaries of the 64-bit PCI host aperture to 1GB, so
475 // that the host can map it with 1GB hugepages. Follow suit.
476 //
477 Pci64Base = ALIGN_VALUE (FirstNonAddress, (UINT64)SIZE_1GB);
478 Pci64Size = ALIGN_VALUE (Pci64Size, (UINT64)SIZE_1GB);
479
480 //
481 // The 64-bit PCI host aperture should also be "naturally" aligned. The
482 // alignment is determined by rounding the size of the aperture down to the
483 // next smaller or equal power of two. That is, align the aperture by the
484 // largest BAR size that can fit into it.
485 //
486 Pci64Base = ALIGN_VALUE (Pci64Base, GetPowerOfTwo64 (Pci64Size));
487
488 if (mBootMode != BOOT_ON_S3_RESUME) {
489 //
490 // The core PciHostBridgeDxe driver will automatically add this range to
491 // the GCD memory space map through our PciHostBridgeLib instance; here we
492 // only need to set the PCDs.
493 //
494 PcdStatus = PcdSet64S (PcdPciMmio64Base, Pci64Base);
495 ASSERT_RETURN_ERROR (PcdStatus);
496 PcdStatus = PcdSet64S (PcdPciMmio64Size, Pci64Size);
497 ASSERT_RETURN_ERROR (PcdStatus);
498
499 DEBUG ((
500 DEBUG_INFO,
501 "%a: Pci64Base=0x%Lx Pci64Size=0x%Lx\n",
502 __FUNCTION__,
503 Pci64Base,
504 Pci64Size
505 ));
506 }
507
508 //
509 // The useful address space ends with the 64-bit PCI host aperture.
510 //
511 FirstNonAddress = Pci64Base + Pci64Size;
512 return FirstNonAddress;
513 }
514
515 /**
516 Initialize the mPhysMemAddressWidth variable, based on guest RAM size.
517 **/
518 VOID
519 AddressWidthInitialization (
520 VOID
521 )
522 {
523 UINT64 FirstNonAddress;
524
525 //
526 // As guest-physical memory size grows, the permanent PEI RAM requirements
527 // are dominated by the identity-mapping page tables built by the DXE IPL.
528 // The DXL IPL keys off of the physical address bits advertized in the CPU
529 // HOB. To conserve memory, we calculate the minimum address width here.
530 //
531 FirstNonAddress = GetFirstNonAddress ();
532 mPhysMemAddressWidth = (UINT8)HighBitSet64 (FirstNonAddress);
533
534 //
535 // If FirstNonAddress is not an integral power of two, then we need an
536 // additional bit.
537 //
538 if ((FirstNonAddress & (FirstNonAddress - 1)) != 0) {
539 ++mPhysMemAddressWidth;
540 }
541
542 //
543 // The minimum address width is 36 (covers up to and excluding 64 GB, which
544 // is the maximum for Ia32 + PAE). The theoretical architecture maximum for
545 // X64 long mode is 52 bits, but the DXE IPL clamps that down to 48 bits. We
546 // can simply assert that here, since 48 bits are good enough for 256 TB.
547 //
548 if (mPhysMemAddressWidth <= 36) {
549 mPhysMemAddressWidth = 36;
550 }
551
552 ASSERT (mPhysMemAddressWidth <= 48);
553 }
554
555 /**
556 Calculate the cap for the permanent PEI memory.
557 **/
558 STATIC
559 UINT32
560 GetPeiMemoryCap (
561 VOID
562 )
563 {
564 BOOLEAN Page1GSupport;
565 UINT32 RegEax;
566 UINT32 RegEdx;
567 UINT32 Pml4Entries;
568 UINT32 PdpEntries;
569 UINTN TotalPages;
570
571 //
572 // If DXE is 32-bit, then just return the traditional 64 MB cap.
573 //
574 #ifdef MDE_CPU_IA32
575 if (!FeaturePcdGet (PcdDxeIplSwitchToLongMode)) {
576 return SIZE_64MB;
577 }
578
579 #endif
580
581 //
582 // Dependent on physical address width, PEI memory allocations can be
583 // dominated by the page tables built for 64-bit DXE. So we key the cap off
584 // of those. The code below is based on CreateIdentityMappingPageTables() in
585 // "MdeModulePkg/Core/DxeIplPeim/X64/VirtualMemory.c".
586 //
587 Page1GSupport = FALSE;
588 if (PcdGetBool (PcdUse1GPageTable)) {
589 AsmCpuid (0x80000000, &RegEax, NULL, NULL, NULL);
590 if (RegEax >= 0x80000001) {
591 AsmCpuid (0x80000001, NULL, NULL, NULL, &RegEdx);
592 if ((RegEdx & BIT26) != 0) {
593 Page1GSupport = TRUE;
594 }
595 }
596 }
597
598 if (mPhysMemAddressWidth <= 39) {
599 Pml4Entries = 1;
600 PdpEntries = 1 << (mPhysMemAddressWidth - 30);
601 ASSERT (PdpEntries <= 0x200);
602 } else {
603 Pml4Entries = 1 << (mPhysMemAddressWidth - 39);
604 ASSERT (Pml4Entries <= 0x200);
605 PdpEntries = 512;
606 }
607
608 TotalPages = Page1GSupport ? Pml4Entries + 1 :
609 (PdpEntries + 1) * Pml4Entries + 1;
610 ASSERT (TotalPages <= 0x40201);
611
612 //
613 // Add 64 MB for miscellaneous allocations. Note that for
614 // mPhysMemAddressWidth values close to 36, the cap will actually be
615 // dominated by this increment.
616 //
617 return (UINT32)(EFI_PAGES_TO_SIZE (TotalPages) + SIZE_64MB);
618 }
619
620 /**
621 Publish PEI core memory
622
623 @return EFI_SUCCESS The PEIM initialized successfully.
624
625 **/
626 EFI_STATUS
627 PublishPeiMemory (
628 VOID
629 )
630 {
631 EFI_STATUS Status;
632 EFI_PHYSICAL_ADDRESS MemoryBase;
633 UINT64 MemorySize;
634 UINT32 LowerMemorySize;
635 UINT32 PeiMemoryCap;
636
637 LowerMemorySize = GetSystemMemorySizeBelow4gb ();
638 if (FeaturePcdGet (PcdSmmSmramRequire)) {
639 //
640 // TSEG is chipped from the end of low RAM
641 //
642 LowerMemorySize -= mQ35TsegMbytes * SIZE_1MB;
643 }
644
645 //
646 // If S3 is supported, then the S3 permanent PEI memory is placed next,
647 // downwards. Its size is primarily dictated by CpuMpPei. The formula below
648 // is an approximation.
649 //
650 if (mS3Supported) {
651 mS3AcpiReservedMemorySize = SIZE_512KB +
652 mMaxCpuCount *
653 PcdGet32 (PcdCpuApStackSize);
654 mS3AcpiReservedMemoryBase = LowerMemorySize - mS3AcpiReservedMemorySize;
655 LowerMemorySize = mS3AcpiReservedMemoryBase;
656 }
657
658 if (mBootMode == BOOT_ON_S3_RESUME) {
659 MemoryBase = mS3AcpiReservedMemoryBase;
660 MemorySize = mS3AcpiReservedMemorySize;
661 } else {
662 PeiMemoryCap = GetPeiMemoryCap ();
663 DEBUG ((
664 DEBUG_INFO,
665 "%a: mPhysMemAddressWidth=%d PeiMemoryCap=%u KB\n",
666 __FUNCTION__,
667 mPhysMemAddressWidth,
668 PeiMemoryCap >> 10
669 ));
670
671 //
672 // Determine the range of memory to use during PEI
673 //
674 // Technically we could lay the permanent PEI RAM over SEC's temporary
675 // decompression and scratch buffer even if "secure S3" is needed, since
676 // their lifetimes don't overlap. However, PeiFvInitialization() will cover
677 // RAM up to PcdOvmfDecompressionScratchEnd with an EfiACPIMemoryNVS memory
678 // allocation HOB, and other allocations served from the permanent PEI RAM
679 // shouldn't overlap with that HOB.
680 //
681 MemoryBase = mS3Supported && FeaturePcdGet (PcdSmmSmramRequire) ?
682 PcdGet32 (PcdOvmfDecompressionScratchEnd) :
683 PcdGet32 (PcdOvmfDxeMemFvBase) + PcdGet32 (PcdOvmfDxeMemFvSize);
684 MemorySize = LowerMemorySize - MemoryBase;
685 if (MemorySize > PeiMemoryCap) {
686 MemoryBase = LowerMemorySize - PeiMemoryCap;
687 MemorySize = PeiMemoryCap;
688 }
689 }
690
691 //
692 // MEMFD_BASE_ADDRESS separates the SMRAM at the default SMBASE from the
693 // normal boot permanent PEI RAM. Regarding the S3 boot path, the S3
694 // permanent PEI RAM is located even higher.
695 //
696 if (FeaturePcdGet (PcdSmmSmramRequire) && mQ35SmramAtDefaultSmbase) {
697 ASSERT (SMM_DEFAULT_SMBASE + MCH_DEFAULT_SMBASE_SIZE <= MemoryBase);
698 }
699
700 //
701 // Publish this memory to the PEI Core
702 //
703 Status = PublishSystemMemory (MemoryBase, MemorySize);
704 ASSERT_EFI_ERROR (Status);
705
706 return Status;
707 }
708
709 STATIC
710 VOID
711 QemuInitializeRamBelow1gb (
712 VOID
713 )
714 {
715 if (FeaturePcdGet (PcdSmmSmramRequire) && mQ35SmramAtDefaultSmbase) {
716 AddMemoryRangeHob (0, SMM_DEFAULT_SMBASE);
717 AddReservedMemoryBaseSizeHob (
718 SMM_DEFAULT_SMBASE,
719 MCH_DEFAULT_SMBASE_SIZE,
720 TRUE /* Cacheable */
721 );
722 STATIC_ASSERT (
723 SMM_DEFAULT_SMBASE + MCH_DEFAULT_SMBASE_SIZE < BASE_512KB + BASE_128KB,
724 "end of SMRAM at default SMBASE ends at, or exceeds, 640KB"
725 );
726 AddMemoryRangeHob (
727 SMM_DEFAULT_SMBASE + MCH_DEFAULT_SMBASE_SIZE,
728 BASE_512KB + BASE_128KB
729 );
730 } else {
731 AddMemoryRangeHob (0, BASE_512KB + BASE_128KB);
732 }
733 }
734
735 /**
736 Peform Memory Detection for QEMU / KVM
737
738 **/
739 STATIC
740 VOID
741 QemuInitializeRam (
742 VOID
743 )
744 {
745 UINT64 LowerMemorySize;
746 UINT64 UpperMemorySize;
747 MTRR_SETTINGS MtrrSettings;
748 EFI_STATUS Status;
749
750 DEBUG ((DEBUG_INFO, "%a called\n", __FUNCTION__));
751
752 //
753 // Determine total memory size available
754 //
755 LowerMemorySize = GetSystemMemorySizeBelow4gb ();
756 UpperMemorySize = GetSystemMemorySizeAbove4gb ();
757
758 if (mBootMode == BOOT_ON_S3_RESUME) {
759 //
760 // Create the following memory HOB as an exception on the S3 boot path.
761 //
762 // Normally we'd create memory HOBs only on the normal boot path. However,
763 // CpuMpPei specifically needs such a low-memory HOB on the S3 path as
764 // well, for "borrowing" a subset of it temporarily, for the AP startup
765 // vector.
766 //
767 // CpuMpPei saves the original contents of the borrowed area in permanent
768 // PEI RAM, in a backup buffer allocated with the normal PEI services.
769 // CpuMpPei restores the original contents ("returns" the borrowed area) at
770 // End-of-PEI. End-of-PEI in turn is emitted by S3Resume2Pei before
771 // transferring control to the OS's wakeup vector in the FACS.
772 //
773 // We expect any other PEIMs that "borrow" memory similarly to CpuMpPei to
774 // restore the original contents. Furthermore, we expect all such PEIMs
775 // (CpuMpPei included) to claim the borrowed areas by producing memory
776 // allocation HOBs, and to honor preexistent memory allocation HOBs when
777 // looking for an area to borrow.
778 //
779 QemuInitializeRamBelow1gb ();
780 } else {
781 //
782 // Create memory HOBs
783 //
784 QemuInitializeRamBelow1gb ();
785
786 if (FeaturePcdGet (PcdSmmSmramRequire)) {
787 UINT32 TsegSize;
788
789 TsegSize = mQ35TsegMbytes * SIZE_1MB;
790 AddMemoryRangeHob (BASE_1MB, LowerMemorySize - TsegSize);
791 AddReservedMemoryBaseSizeHob (
792 LowerMemorySize - TsegSize,
793 TsegSize,
794 TRUE
795 );
796 } else {
797 AddMemoryRangeHob (BASE_1MB, LowerMemorySize);
798 }
799
800 //
801 // If QEMU presents an E820 map, then create memory HOBs for the >=4GB RAM
802 // entries. Otherwise, create a single memory HOB with the flat >=4GB
803 // memory size read from the CMOS.
804 //
805 Status = ScanOrAdd64BitE820Ram (NULL);
806 if (EFI_ERROR (Status) && (UpperMemorySize != 0)) {
807 AddMemoryBaseSizeHob (BASE_4GB, UpperMemorySize);
808 }
809 }
810
811 //
812 // We'd like to keep the following ranges uncached:
813 // - [640 KB, 1 MB)
814 // - [LowerMemorySize, 4 GB)
815 //
816 // Everything else should be WB. Unfortunately, programming the inverse (ie.
817 // keeping the default UC, and configuring the complement set of the above as
818 // WB) is not reliable in general, because the end of the upper RAM can have
819 // practically any alignment, and we may not have enough variable MTRRs to
820 // cover it exactly.
821 //
822 if (IsMtrrSupported ()) {
823 MtrrGetAllMtrrs (&MtrrSettings);
824
825 //
826 // MTRRs disabled, fixed MTRRs disabled, default type is uncached
827 //
828 ASSERT ((MtrrSettings.MtrrDefType & BIT11) == 0);
829 ASSERT ((MtrrSettings.MtrrDefType & BIT10) == 0);
830 ASSERT ((MtrrSettings.MtrrDefType & 0xFF) == 0);
831
832 //
833 // flip default type to writeback
834 //
835 SetMem (&MtrrSettings.Fixed, sizeof MtrrSettings.Fixed, 0x06);
836 ZeroMem (&MtrrSettings.Variables, sizeof MtrrSettings.Variables);
837 MtrrSettings.MtrrDefType |= BIT11 | BIT10 | 6;
838 MtrrSetAllMtrrs (&MtrrSettings);
839
840 //
841 // Set memory range from 640KB to 1MB to uncacheable
842 //
843 Status = MtrrSetMemoryAttribute (
844 BASE_512KB + BASE_128KB,
845 BASE_1MB - (BASE_512KB + BASE_128KB),
846 CacheUncacheable
847 );
848 ASSERT_EFI_ERROR (Status);
849
850 //
851 // Set the memory range from the start of the 32-bit MMIO area (32-bit PCI
852 // MMIO aperture on i440fx, PCIEXBAR on q35) to 4GB as uncacheable.
853 //
854 Status = MtrrSetMemoryAttribute (
855 mQemuUc32Base,
856 SIZE_4GB - mQemuUc32Base,
857 CacheUncacheable
858 );
859 ASSERT_EFI_ERROR (Status);
860 }
861 }
862
863 /**
864 Publish system RAM and reserve memory regions
865
866 **/
867 VOID
868 InitializeRamRegions (
869 VOID
870 )
871 {
872 QemuInitializeRam ();
873
874 if (mS3Supported && (mBootMode != BOOT_ON_S3_RESUME)) {
875 //
876 // This is the memory range that will be used for PEI on S3 resume
877 //
878 BuildMemoryAllocationHob (
879 mS3AcpiReservedMemoryBase,
880 mS3AcpiReservedMemorySize,
881 EfiACPIMemoryNVS
882 );
883
884 //
885 // Cover the initial RAM area used as stack and temporary PEI heap.
886 //
887 // This is reserved as ACPI NVS so it can be used on S3 resume.
888 //
889 BuildMemoryAllocationHob (
890 PcdGet32 (PcdOvmfSecPeiTempRamBase),
891 PcdGet32 (PcdOvmfSecPeiTempRamSize),
892 EfiACPIMemoryNVS
893 );
894
895 //
896 // SEC stores its table of GUIDed section handlers here.
897 //
898 BuildMemoryAllocationHob (
899 PcdGet64 (PcdGuidedExtractHandlerTableAddress),
900 PcdGet32 (PcdGuidedExtractHandlerTableSize),
901 EfiACPIMemoryNVS
902 );
903
904 #ifdef MDE_CPU_X64
905 //
906 // Reserve the initial page tables built by the reset vector code.
907 //
908 // Since this memory range will be used by the Reset Vector on S3
909 // resume, it must be reserved as ACPI NVS.
910 //
911 BuildMemoryAllocationHob (
912 (EFI_PHYSICAL_ADDRESS)(UINTN)PcdGet32 (PcdOvmfSecPageTablesBase),
913 (UINT64)(UINTN)PcdGet32 (PcdOvmfSecPageTablesSize),
914 EfiACPIMemoryNVS
915 );
916
917 if (MemEncryptSevEsIsEnabled ()) {
918 //
919 // If SEV-ES is enabled, reserve the GHCB-related memory area. This
920 // includes the extra page table used to break down the 2MB page
921 // mapping into 4KB page entries where the GHCB resides and the
922 // GHCB area itself.
923 //
924 // Since this memory range will be used by the Reset Vector on S3
925 // resume, it must be reserved as ACPI NVS.
926 //
927 BuildMemoryAllocationHob (
928 (EFI_PHYSICAL_ADDRESS)(UINTN)PcdGet32 (PcdOvmfSecGhcbPageTableBase),
929 (UINT64)(UINTN)PcdGet32 (PcdOvmfSecGhcbPageTableSize),
930 EfiACPIMemoryNVS
931 );
932 BuildMemoryAllocationHob (
933 (EFI_PHYSICAL_ADDRESS)(UINTN)PcdGet32 (PcdOvmfSecGhcbBase),
934 (UINT64)(UINTN)PcdGet32 (PcdOvmfSecGhcbSize),
935 EfiACPIMemoryNVS
936 );
937 BuildMemoryAllocationHob (
938 (EFI_PHYSICAL_ADDRESS)(UINTN)PcdGet32 (PcdOvmfSecGhcbBackupBase),
939 (UINT64)(UINTN)PcdGet32 (PcdOvmfSecGhcbBackupSize),
940 EfiACPIMemoryNVS
941 );
942 }
943
944 #endif
945 }
946
947 if (mBootMode != BOOT_ON_S3_RESUME) {
948 if (!FeaturePcdGet (PcdSmmSmramRequire)) {
949 //
950 // Reserve the lock box storage area
951 //
952 // Since this memory range will be used on S3 resume, it must be
953 // reserved as ACPI NVS.
954 //
955 // If S3 is unsupported, then various drivers might still write to the
956 // LockBox area. We ought to prevent DXE from serving allocation requests
957 // such that they would overlap the LockBox storage.
958 //
959 ZeroMem (
960 (VOID *)(UINTN)PcdGet32 (PcdOvmfLockBoxStorageBase),
961 (UINTN)PcdGet32 (PcdOvmfLockBoxStorageSize)
962 );
963 BuildMemoryAllocationHob (
964 (EFI_PHYSICAL_ADDRESS)(UINTN)PcdGet32 (PcdOvmfLockBoxStorageBase),
965 (UINT64)(UINTN)PcdGet32 (PcdOvmfLockBoxStorageSize),
966 mS3Supported ? EfiACPIMemoryNVS : EfiBootServicesData
967 );
968 }
969
970 if (FeaturePcdGet (PcdSmmSmramRequire)) {
971 UINT32 TsegSize;
972
973 //
974 // Make sure the TSEG area that we reported as a reserved memory resource
975 // cannot be used for reserved memory allocations.
976 //
977 TsegSize = mQ35TsegMbytes * SIZE_1MB;
978 BuildMemoryAllocationHob (
979 GetSystemMemorySizeBelow4gb () - TsegSize,
980 TsegSize,
981 EfiReservedMemoryType
982 );
983 //
984 // Similarly, allocate away the (already reserved) SMRAM at the default
985 // SMBASE, if it exists.
986 //
987 if (mQ35SmramAtDefaultSmbase) {
988 BuildMemoryAllocationHob (
989 SMM_DEFAULT_SMBASE,
990 MCH_DEFAULT_SMBASE_SIZE,
991 EfiReservedMemoryType
992 );
993 }
994 }
995
996 #ifdef MDE_CPU_X64
997 if (FixedPcdGet32 (PcdOvmfWorkAreaSize) != 0) {
998 //
999 // Reserve the work area.
1000 //
1001 // Since this memory range will be used by the Reset Vector on S3
1002 // resume, it must be reserved as ACPI NVS.
1003 //
1004 // If S3 is unsupported, then various drivers might still write to the
1005 // work area. We ought to prevent DXE from serving allocation requests
1006 // such that they would overlap the work area.
1007 //
1008 BuildMemoryAllocationHob (
1009 (EFI_PHYSICAL_ADDRESS)(UINTN)FixedPcdGet32 (PcdOvmfWorkAreaBase),
1010 (UINT64)(UINTN)FixedPcdGet32 (PcdOvmfWorkAreaSize),
1011 mS3Supported ? EfiACPIMemoryNVS : EfiBootServicesData
1012 );
1013 }
1014
1015 #endif
1016 }
1017 }