2 Memory Detection for Virtual Machines.
4 Copyright (c) 2006 - 2016, Intel Corporation. All rights reserved.<BR>
5 SPDX-License-Identifier: BSD-2-Clause-Patent
14 // The package level header files this module uses
16 #include <IndustryStandard/E820.h>
17 #include <IndustryStandard/I440FxPiix4.h>
18 #include <IndustryStandard/Q35MchIch9.h>
19 #include <IndustryStandard/CloudHv.h>
20 #include <IndustryStandard/Xen/arch-x86/hvm/start_info.h>
22 #include <Register/Intel/SmramSaveStateMap.h>
25 // The Library classes this module consumes
27 #include <Library/BaseLib.h>
28 #include <Library/BaseMemoryLib.h>
29 #include <Library/DebugLib.h>
30 #include <Library/HobLib.h>
31 #include <Library/IoLib.h>
32 #include <Library/MemEncryptSevLib.h>
33 #include <Library/PcdLib.h>
34 #include <Library/PciLib.h>
35 #include <Library/PeimEntryPoint.h>
36 #include <Library/ResourcePublicationLib.h>
38 #include <Library/QemuFwCfgLib.h>
39 #include <Library/QemuFwCfgSimpleParserLib.h>
43 Q35TsegMbytesInitialization (
44 IN OUT EFI_HOB_PLATFORM_INFO
*PlatformInfoHob
47 UINT16 ExtendedTsegMbytes
;
48 RETURN_STATUS PcdStatus
;
50 ASSERT (PlatformInfoHob
->HostBridgeDevId
== INTEL_Q35_MCH_DEVICE_ID
);
53 // Check if QEMU offers an extended TSEG.
55 // This can be seen from writing MCH_EXT_TSEG_MB_QUERY to the MCH_EXT_TSEG_MB
56 // register, and reading back the register.
58 // On a QEMU machine type that does not offer an extended TSEG, the initial
59 // write overwrites whatever value a malicious guest OS may have placed in
60 // the (unimplemented) register, before entering S3 or rebooting.
61 // Subsequently, the read returns MCH_EXT_TSEG_MB_QUERY unchanged.
63 // On a QEMU machine type that offers an extended TSEG, the initial write
64 // triggers an update to the register. Subsequently, the value read back
65 // (which is guaranteed to differ from MCH_EXT_TSEG_MB_QUERY) tells us the
66 // number of megabytes.
68 PciWrite16 (DRAMC_REGISTER_Q35 (MCH_EXT_TSEG_MB
), MCH_EXT_TSEG_MB_QUERY
);
69 ExtendedTsegMbytes
= PciRead16 (DRAMC_REGISTER_Q35 (MCH_EXT_TSEG_MB
));
70 if (ExtendedTsegMbytes
== MCH_EXT_TSEG_MB_QUERY
) {
71 PlatformInfoHob
->Q35TsegMbytes
= PcdGet16 (PcdQ35TsegMbytes
);
77 "%a: QEMU offers an extended TSEG (%d MB)\n",
81 PcdStatus
= PcdSet16S (PcdQ35TsegMbytes
, ExtendedTsegMbytes
);
82 ASSERT_RETURN_ERROR (PcdStatus
);
83 PlatformInfoHob
->Q35TsegMbytes
= ExtendedTsegMbytes
;
87 Q35SmramAtDefaultSmbaseInitialization (
88 IN OUT EFI_HOB_PLATFORM_INFO
*PlatformInfoHob
91 RETURN_STATUS PcdStatus
;
93 ASSERT (PlatformInfoHob
->HostBridgeDevId
== INTEL_Q35_MCH_DEVICE_ID
);
95 PlatformInfoHob
->Q35SmramAtDefaultSmbase
= FALSE
;
96 if (FeaturePcdGet (PcdCsmEnable
)) {
99 "%a: SMRAM at default SMBASE not checked due to CSM\n",
106 CtlReg
= DRAMC_REGISTER_Q35 (MCH_DEFAULT_SMBASE_CTL
);
107 PciWrite8 (CtlReg
, MCH_DEFAULT_SMBASE_QUERY
);
108 CtlRegVal
= PciRead8 (CtlReg
);
109 PlatformInfoHob
->Q35SmramAtDefaultSmbase
= (BOOLEAN
)(CtlRegVal
==
110 MCH_DEFAULT_SMBASE_IN_RAM
);
113 "%a: SMRAM at default SMBASE %a\n",
115 PlatformInfoHob
->Q35SmramAtDefaultSmbase
? "found" : "not found"
119 PcdStatus
= PcdSetBoolS (
120 PcdQ35SmramAtDefaultSmbase
,
121 PlatformInfoHob
->Q35SmramAtDefaultSmbase
123 ASSERT_RETURN_ERROR (PcdStatus
);
127 Initialize the PhysMemAddressWidth field in PlatformInfoHob based on guest RAM size.
130 AddressWidthInitialization (
131 IN OUT EFI_HOB_PLATFORM_INFO
*PlatformInfoHob
134 RETURN_STATUS PcdStatus
;
136 PlatformAddressWidthInitialization (PlatformInfoHob
);
139 // If DXE is 32-bit, then we're done; PciBusDxe will degrade 64-bit MMIO
140 // resources to 32-bit anyway. See DegradeResource() in
141 // "PciResourceSupport.c".
144 if (!FeaturePcdGet (PcdDxeIplSwitchToLongMode
)) {
150 if (PlatformInfoHob
->PcdPciMmio64Size
== 0) {
151 if (PlatformInfoHob
->BootMode
!= BOOT_ON_S3_RESUME
) {
154 "%a: disabling 64-bit PCI host aperture\n",
157 PcdStatus
= PcdSet64S (PcdPciMmio64Size
, 0);
158 ASSERT_RETURN_ERROR (PcdStatus
);
164 if (PlatformInfoHob
->BootMode
!= BOOT_ON_S3_RESUME
) {
166 // The core PciHostBridgeDxe driver will automatically add this range to
167 // the GCD memory space map through our PciHostBridgeLib instance; here we
168 // only need to set the PCDs.
170 PcdStatus
= PcdSet64S (PcdPciMmio64Base
, PlatformInfoHob
->PcdPciMmio64Base
);
171 ASSERT_RETURN_ERROR (PcdStatus
);
172 PcdStatus
= PcdSet64S (PcdPciMmio64Size
, PlatformInfoHob
->PcdPciMmio64Size
);
173 ASSERT_RETURN_ERROR (PcdStatus
);
177 "%a: Pci64Base=0x%Lx Pci64Size=0x%Lx\n",
179 PlatformInfoHob
->PcdPciMmio64Base
,
180 PlatformInfoHob
->PcdPciMmio64Size
186 Calculate the cap for the permanent PEI memory.
191 IN EFI_HOB_PLATFORM_INFO
*PlatformInfoHob
194 BOOLEAN Page1GSupport
;
202 // If DXE is 32-bit, then just return the traditional 64 MB cap.
205 if (!FeaturePcdGet (PcdDxeIplSwitchToLongMode
)) {
212 // Dependent on physical address width, PEI memory allocations can be
213 // dominated by the page tables built for 64-bit DXE. So we key the cap off
214 // of those. The code below is based on CreateIdentityMappingPageTables() in
215 // "MdeModulePkg/Core/DxeIplPeim/X64/VirtualMemory.c".
217 Page1GSupport
= FALSE
;
218 if (PcdGetBool (PcdUse1GPageTable
)) {
219 AsmCpuid (0x80000000, &RegEax
, NULL
, NULL
, NULL
);
220 if (RegEax
>= 0x80000001) {
221 AsmCpuid (0x80000001, NULL
, NULL
, NULL
, &RegEdx
);
222 if ((RegEdx
& BIT26
) != 0) {
223 Page1GSupport
= TRUE
;
228 if (PlatformInfoHob
->PhysMemAddressWidth
<= 39) {
230 PdpEntries
= 1 << (PlatformInfoHob
->PhysMemAddressWidth
- 30);
231 ASSERT (PdpEntries
<= 0x200);
233 if (PlatformInfoHob
->PhysMemAddressWidth
> 48) {
236 Pml4Entries
= 1 << (PlatformInfoHob
->PhysMemAddressWidth
- 39);
239 ASSERT (Pml4Entries
<= 0x200);
243 TotalPages
= Page1GSupport
? Pml4Entries
+ 1 :
244 (PdpEntries
+ 1) * Pml4Entries
+ 1;
245 ASSERT (TotalPages
<= 0x40201);
248 // Add 64 MB for miscellaneous allocations. Note that for
249 // PhysMemAddressWidth values close to 36, the cap will actually be
250 // dominated by this increment.
252 return (UINT32
)(EFI_PAGES_TO_SIZE (TotalPages
) + SIZE_64MB
);
256 Publish PEI core memory
258 @return EFI_SUCCESS The PEIM initialized successfully.
263 IN OUT EFI_HOB_PLATFORM_INFO
*PlatformInfoHob
267 EFI_PHYSICAL_ADDRESS MemoryBase
;
269 UINT32 LowerMemorySize
;
271 UINT32 S3AcpiReservedMemoryBase
;
272 UINT32 S3AcpiReservedMemorySize
;
274 PlatformGetSystemMemorySizeBelow4gb (PlatformInfoHob
);
275 LowerMemorySize
= PlatformInfoHob
->LowMemory
;
276 if (PlatformInfoHob
->SmmSmramRequire
) {
278 // TSEG is chipped from the end of low RAM
280 LowerMemorySize
-= PlatformInfoHob
->Q35TsegMbytes
* SIZE_1MB
;
283 S3AcpiReservedMemoryBase
= 0;
284 S3AcpiReservedMemorySize
= 0;
287 // If S3 is supported, then the S3 permanent PEI memory is placed next,
288 // downwards. Its size is primarily dictated by CpuMpPei. The formula below
289 // is an approximation.
291 if (PlatformInfoHob
->S3Supported
) {
292 S3AcpiReservedMemorySize
= SIZE_512KB
+
293 PlatformInfoHob
->PcdCpuMaxLogicalProcessorNumber
*
294 PcdGet32 (PcdCpuApStackSize
);
295 S3AcpiReservedMemoryBase
= LowerMemorySize
- S3AcpiReservedMemorySize
;
296 LowerMemorySize
= S3AcpiReservedMemoryBase
;
299 PlatformInfoHob
->S3AcpiReservedMemoryBase
= S3AcpiReservedMemoryBase
;
300 PlatformInfoHob
->S3AcpiReservedMemorySize
= S3AcpiReservedMemorySize
;
302 if (PlatformInfoHob
->BootMode
== BOOT_ON_S3_RESUME
) {
303 MemoryBase
= S3AcpiReservedMemoryBase
;
304 MemorySize
= S3AcpiReservedMemorySize
;
306 PeiMemoryCap
= GetPeiMemoryCap (PlatformInfoHob
);
309 "%a: PhysMemAddressWidth=%d PeiMemoryCap=%u KB\n",
311 PlatformInfoHob
->PhysMemAddressWidth
,
316 // Determine the range of memory to use during PEI
318 // Technically we could lay the permanent PEI RAM over SEC's temporary
319 // decompression and scratch buffer even if "secure S3" is needed, since
320 // their lifetimes don't overlap. However, PeiFvInitialization() will cover
321 // RAM up to PcdOvmfDecompressionScratchEnd with an EfiACPIMemoryNVS memory
322 // allocation HOB, and other allocations served from the permanent PEI RAM
323 // shouldn't overlap with that HOB.
325 MemoryBase
= PlatformInfoHob
->S3Supported
&& PlatformInfoHob
->SmmSmramRequire
?
326 PcdGet32 (PcdOvmfDecompressionScratchEnd
) :
327 PcdGet32 (PcdOvmfDxeMemFvBase
) + PcdGet32 (PcdOvmfDxeMemFvSize
);
328 MemorySize
= LowerMemorySize
- MemoryBase
;
329 if (MemorySize
> PeiMemoryCap
) {
330 MemoryBase
= LowerMemorySize
- PeiMemoryCap
;
331 MemorySize
= PeiMemoryCap
;
336 // MEMFD_BASE_ADDRESS separates the SMRAM at the default SMBASE from the
337 // normal boot permanent PEI RAM. Regarding the S3 boot path, the S3
338 // permanent PEI RAM is located even higher.
340 if (PlatformInfoHob
->SmmSmramRequire
&& PlatformInfoHob
->Q35SmramAtDefaultSmbase
) {
341 ASSERT (SMM_DEFAULT_SMBASE
+ MCH_DEFAULT_SMBASE_SIZE
<= MemoryBase
);
345 // Publish this memory to the PEI Core
347 Status
= PublishSystemMemory (MemoryBase
, MemorySize
);
348 ASSERT_EFI_ERROR (Status
);
354 Publish system RAM and reserve memory regions
358 InitializeRamRegions (
359 IN EFI_HOB_PLATFORM_INFO
*PlatformInfoHob
362 if (TdIsEnabled ()) {
363 PlatformTdxPublishRamRegions ();
367 PlatformQemuInitializeRam (PlatformInfoHob
);
371 PlatformQemuInitializeRamForS3 (PlatformInfoHob
);