VirtualBox

source: vbox/trunk/src/VBox/Devices/EFI/FirmwareNew/OvmfPkg/PlatformPei/MemDetect.c@ 80721

Last change on this file since 80721 was 80721, checked in by vboxsync, 5 years ago

Devices/EFI/FirmwareNew: Start upgrade process to edk2-stable201908 (compiles on Windows and works to some extent), bugref:4643

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

© 2024 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette