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>
44 Q35TsegMbytesInitialization (
48 UINT16 ExtendedTsegMbytes
;
49 RETURN_STATUS PcdStatus
;
51 ASSERT (mPlatformInfoHob
.HostBridgeDevId
== INTEL_Q35_MCH_DEVICE_ID
);
54 // Check if QEMU offers an extended TSEG.
56 // This can be seen from writing MCH_EXT_TSEG_MB_QUERY to the MCH_EXT_TSEG_MB
57 // register, and reading back the register.
59 // On a QEMU machine type that does not offer an extended TSEG, the initial
60 // write overwrites whatever value a malicious guest OS may have placed in
61 // the (unimplemented) register, before entering S3 or rebooting.
62 // Subsequently, the read returns MCH_EXT_TSEG_MB_QUERY unchanged.
64 // On a QEMU machine type that offers an extended TSEG, the initial write
65 // triggers an update to the register. Subsequently, the value read back
66 // (which is guaranteed to differ from MCH_EXT_TSEG_MB_QUERY) tells us the
67 // number of megabytes.
69 PciWrite16 (DRAMC_REGISTER_Q35 (MCH_EXT_TSEG_MB
), MCH_EXT_TSEG_MB_QUERY
);
70 ExtendedTsegMbytes
= PciRead16 (DRAMC_REGISTER_Q35 (MCH_EXT_TSEG_MB
));
71 if (ExtendedTsegMbytes
== MCH_EXT_TSEG_MB_QUERY
) {
72 mPlatformInfoHob
.Q35TsegMbytes
= PcdGet16 (PcdQ35TsegMbytes
);
78 "%a: QEMU offers an extended TSEG (%d MB)\n",
82 PcdStatus
= PcdSet16S (PcdQ35TsegMbytes
, ExtendedTsegMbytes
);
83 ASSERT_RETURN_ERROR (PcdStatus
);
84 mPlatformInfoHob
.Q35TsegMbytes
= ExtendedTsegMbytes
;
88 Q35SmramAtDefaultSmbaseInitialization (
92 RETURN_STATUS PcdStatus
;
94 ASSERT (mPlatformInfoHob
.HostBridgeDevId
== INTEL_Q35_MCH_DEVICE_ID
);
96 mPlatformInfoHob
.Q35SmramAtDefaultSmbase
= FALSE
;
97 if (FeaturePcdGet (PcdCsmEnable
)) {
100 "%a: SMRAM at default SMBASE not checked due to CSM\n",
107 CtlReg
= DRAMC_REGISTER_Q35 (MCH_DEFAULT_SMBASE_CTL
);
108 PciWrite8 (CtlReg
, MCH_DEFAULT_SMBASE_QUERY
);
109 CtlRegVal
= PciRead8 (CtlReg
);
110 mPlatformInfoHob
.Q35SmramAtDefaultSmbase
= (BOOLEAN
)(CtlRegVal
==
111 MCH_DEFAULT_SMBASE_IN_RAM
);
114 "%a: SMRAM at default SMBASE %a\n",
116 mPlatformInfoHob
.Q35SmramAtDefaultSmbase
? "found" : "not found"
120 PcdStatus
= PcdSetBoolS (
121 PcdQ35SmramAtDefaultSmbase
,
122 mPlatformInfoHob
.Q35SmramAtDefaultSmbase
124 ASSERT_RETURN_ERROR (PcdStatus
);
128 Initialize the PhysMemAddressWidth field in PlatformInfoHob based on guest RAM size.
131 AddressWidthInitialization (
132 IN OUT EFI_HOB_PLATFORM_INFO
*PlatformInfoHob
135 RETURN_STATUS PcdStatus
;
137 PlatformAddressWidthInitialization (PlatformInfoHob
);
140 // If DXE is 32-bit, then we're done; PciBusDxe will degrade 64-bit MMIO
141 // resources to 32-bit anyway. See DegradeResource() in
142 // "PciResourceSupport.c".
145 if (!FeaturePcdGet (PcdDxeIplSwitchToLongMode
)) {
151 if (PlatformInfoHob
->PcdPciMmio64Size
== 0) {
152 if (PlatformInfoHob
->BootMode
!= BOOT_ON_S3_RESUME
) {
155 "%a: disabling 64-bit PCI host aperture\n",
158 PcdStatus
= PcdSet64S (PcdPciMmio64Size
, 0);
159 ASSERT_RETURN_ERROR (PcdStatus
);
165 if (PlatformInfoHob
->BootMode
!= BOOT_ON_S3_RESUME
) {
167 // The core PciHostBridgeDxe driver will automatically add this range to
168 // the GCD memory space map through our PciHostBridgeLib instance; here we
169 // only need to set the PCDs.
171 PcdStatus
= PcdSet64S (PcdPciMmio64Base
, PlatformInfoHob
->PcdPciMmio64Base
);
172 ASSERT_RETURN_ERROR (PcdStatus
);
173 PcdStatus
= PcdSet64S (PcdPciMmio64Size
, PlatformInfoHob
->PcdPciMmio64Size
);
174 ASSERT_RETURN_ERROR (PcdStatus
);
178 "%a: Pci64Base=0x%Lx Pci64Size=0x%Lx\n",
180 PlatformInfoHob
->PcdPciMmio64Base
,
181 PlatformInfoHob
->PcdPciMmio64Size
187 Calculate the cap for the permanent PEI memory.
195 BOOLEAN Page1GSupport
;
203 // If DXE is 32-bit, then just return the traditional 64 MB cap.
206 if (!FeaturePcdGet (PcdDxeIplSwitchToLongMode
)) {
213 // Dependent on physical address width, PEI memory allocations can be
214 // dominated by the page tables built for 64-bit DXE. So we key the cap off
215 // of those. The code below is based on CreateIdentityMappingPageTables() in
216 // "MdeModulePkg/Core/DxeIplPeim/X64/VirtualMemory.c".
218 Page1GSupport
= FALSE
;
219 if (PcdGetBool (PcdUse1GPageTable
)) {
220 AsmCpuid (0x80000000, &RegEax
, NULL
, NULL
, NULL
);
221 if (RegEax
>= 0x80000001) {
222 AsmCpuid (0x80000001, NULL
, NULL
, NULL
, &RegEdx
);
223 if ((RegEdx
& BIT26
) != 0) {
224 Page1GSupport
= TRUE
;
229 if (mPlatformInfoHob
.PhysMemAddressWidth
<= 39) {
231 PdpEntries
= 1 << (mPlatformInfoHob
.PhysMemAddressWidth
- 30);
232 ASSERT (PdpEntries
<= 0x200);
234 Pml4Entries
= 1 << (mPlatformInfoHob
.PhysMemAddressWidth
- 39);
235 ASSERT (Pml4Entries
<= 0x200);
239 TotalPages
= Page1GSupport
? Pml4Entries
+ 1 :
240 (PdpEntries
+ 1) * Pml4Entries
+ 1;
241 ASSERT (TotalPages
<= 0x40201);
244 // Add 64 MB for miscellaneous allocations. Note that for
245 // PhysMemAddressWidth values close to 36, the cap will actually be
246 // dominated by this increment.
248 return (UINT32
)(EFI_PAGES_TO_SIZE (TotalPages
) + SIZE_64MB
);
252 Publish PEI core memory
254 @return EFI_SUCCESS The PEIM initialized successfully.
263 EFI_PHYSICAL_ADDRESS MemoryBase
;
265 UINT32 LowerMemorySize
;
267 UINT32 S3AcpiReservedMemoryBase
;
268 UINT32 S3AcpiReservedMemorySize
;
270 LowerMemorySize
= PlatformGetSystemMemorySizeBelow4gb (&mPlatformInfoHob
);
271 if (mPlatformInfoHob
.SmmSmramRequire
) {
273 // TSEG is chipped from the end of low RAM
275 LowerMemorySize
-= mPlatformInfoHob
.Q35TsegMbytes
* SIZE_1MB
;
278 S3AcpiReservedMemoryBase
= 0;
279 S3AcpiReservedMemorySize
= 0;
282 // If S3 is supported, then the S3 permanent PEI memory is placed next,
283 // downwards. Its size is primarily dictated by CpuMpPei. The formula below
284 // is an approximation.
286 if (mPlatformInfoHob
.S3Supported
) {
287 S3AcpiReservedMemorySize
= SIZE_512KB
+
288 mPlatformInfoHob
.PcdCpuMaxLogicalProcessorNumber
*
289 PcdGet32 (PcdCpuApStackSize
);
290 S3AcpiReservedMemoryBase
= LowerMemorySize
- S3AcpiReservedMemorySize
;
291 LowerMemorySize
= S3AcpiReservedMemoryBase
;
294 mPlatformInfoHob
.S3AcpiReservedMemoryBase
= S3AcpiReservedMemoryBase
;
295 mPlatformInfoHob
.S3AcpiReservedMemorySize
= S3AcpiReservedMemorySize
;
297 if (mPlatformInfoHob
.BootMode
== BOOT_ON_S3_RESUME
) {
298 MemoryBase
= S3AcpiReservedMemoryBase
;
299 MemorySize
= S3AcpiReservedMemorySize
;
301 PeiMemoryCap
= GetPeiMemoryCap ();
304 "%a: PhysMemAddressWidth=%d PeiMemoryCap=%u KB\n",
306 mPlatformInfoHob
.PhysMemAddressWidth
,
311 // Determine the range of memory to use during PEI
313 // Technically we could lay the permanent PEI RAM over SEC's temporary
314 // decompression and scratch buffer even if "secure S3" is needed, since
315 // their lifetimes don't overlap. However, PeiFvInitialization() will cover
316 // RAM up to PcdOvmfDecompressionScratchEnd with an EfiACPIMemoryNVS memory
317 // allocation HOB, and other allocations served from the permanent PEI RAM
318 // shouldn't overlap with that HOB.
320 MemoryBase
= mPlatformInfoHob
.S3Supported
&& mPlatformInfoHob
.SmmSmramRequire
?
321 PcdGet32 (PcdOvmfDecompressionScratchEnd
) :
322 PcdGet32 (PcdOvmfDxeMemFvBase
) + PcdGet32 (PcdOvmfDxeMemFvSize
);
323 MemorySize
= LowerMemorySize
- MemoryBase
;
324 if (MemorySize
> PeiMemoryCap
) {
325 MemoryBase
= LowerMemorySize
- PeiMemoryCap
;
326 MemorySize
= PeiMemoryCap
;
331 // MEMFD_BASE_ADDRESS separates the SMRAM at the default SMBASE from the
332 // normal boot permanent PEI RAM. Regarding the S3 boot path, the S3
333 // permanent PEI RAM is located even higher.
335 if (mPlatformInfoHob
.SmmSmramRequire
&& mPlatformInfoHob
.Q35SmramAtDefaultSmbase
) {
336 ASSERT (SMM_DEFAULT_SMBASE
+ MCH_DEFAULT_SMBASE_SIZE
<= MemoryBase
);
340 // Publish this memory to the PEI Core
342 Status
= PublishSystemMemory (MemoryBase
, MemorySize
);
343 ASSERT_EFI_ERROR (Status
);
349 Publish system RAM and reserve memory regions
353 InitializeRamRegions (
354 IN EFI_HOB_PLATFORM_INFO
*PlatformInfoHob
357 PlatformQemuInitializeRam (PlatformInfoHob
);
361 PlatformQemuInitializeRamForS3 (PlatformInfoHob
);