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