VirtualBox

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

Last change on this file since 85718 was 85718, checked in by vboxsync, 4 years ago

Devices/EFI: Merge edk-stable202005 and make it build, bugref:4643

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