]> git.proxmox.com Git - mirror_edk2.git/blob - OvmfPkg/PlatformPei/MemDetect.c
UefiCpuPkg: Move AsmRelocateApLoopStart from Mpfuncs.nasm to AmdSev.nasm
[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 <IndustryStandard/CloudHv.h>
20 #include <IndustryStandard/Xen/arch-x86/hvm/start_info.h>
21 #include <PiPei.h>
22 #include <Register/Intel/SmramSaveStateMap.h>
23
24 //
25 // The Library classes this module consumes
26 //
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>
37
38 #include <Library/QemuFwCfgLib.h>
39 #include <Library/QemuFwCfgSimpleParserLib.h>
40 #include "Platform.h"
41
42 VOID
43 Q35TsegMbytesInitialization (
44 IN OUT EFI_HOB_PLATFORM_INFO *PlatformInfoHob
45 )
46 {
47 UINT16 ExtendedTsegMbytes;
48 RETURN_STATUS PcdStatus;
49
50 ASSERT (PlatformInfoHob->HostBridgeDevId == INTEL_Q35_MCH_DEVICE_ID);
51
52 //
53 // Check if QEMU offers an extended TSEG.
54 //
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.
57 //
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.
62 //
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.
67 //
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);
72 return;
73 }
74
75 DEBUG ((
76 DEBUG_INFO,
77 "%a: QEMU offers an extended TSEG (%d MB)\n",
78 __FUNCTION__,
79 ExtendedTsegMbytes
80 ));
81 PcdStatus = PcdSet16S (PcdQ35TsegMbytes, ExtendedTsegMbytes);
82 ASSERT_RETURN_ERROR (PcdStatus);
83 PlatformInfoHob->Q35TsegMbytes = ExtendedTsegMbytes;
84 }
85
86 VOID
87 Q35SmramAtDefaultSmbaseInitialization (
88 IN OUT EFI_HOB_PLATFORM_INFO *PlatformInfoHob
89 )
90 {
91 RETURN_STATUS PcdStatus;
92
93 ASSERT (PlatformInfoHob->HostBridgeDevId == INTEL_Q35_MCH_DEVICE_ID);
94
95 PlatformInfoHob->Q35SmramAtDefaultSmbase = FALSE;
96 if (FeaturePcdGet (PcdCsmEnable)) {
97 DEBUG ((
98 DEBUG_INFO,
99 "%a: SMRAM at default SMBASE not checked due to CSM\n",
100 __FUNCTION__
101 ));
102 } else {
103 UINTN CtlReg;
104 UINT8 CtlRegVal;
105
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);
111 DEBUG ((
112 DEBUG_INFO,
113 "%a: SMRAM at default SMBASE %a\n",
114 __FUNCTION__,
115 PlatformInfoHob->Q35SmramAtDefaultSmbase ? "found" : "not found"
116 ));
117 }
118
119 PcdStatus = PcdSetBoolS (
120 PcdQ35SmramAtDefaultSmbase,
121 PlatformInfoHob->Q35SmramAtDefaultSmbase
122 );
123 ASSERT_RETURN_ERROR (PcdStatus);
124 }
125
126 /**
127 Initialize the PhysMemAddressWidth field in PlatformInfoHob based on guest RAM size.
128 **/
129 VOID
130 AddressWidthInitialization (
131 IN OUT EFI_HOB_PLATFORM_INFO *PlatformInfoHob
132 )
133 {
134 RETURN_STATUS PcdStatus;
135
136 PlatformAddressWidthInitialization (PlatformInfoHob);
137
138 //
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".
142 //
143 #ifdef MDE_CPU_IA32
144 if (!FeaturePcdGet (PcdDxeIplSwitchToLongMode)) {
145 return;
146 }
147
148 #endif
149
150 if (PlatformInfoHob->PcdPciMmio64Size == 0) {
151 if (PlatformInfoHob->BootMode != BOOT_ON_S3_RESUME) {
152 DEBUG ((
153 DEBUG_INFO,
154 "%a: disabling 64-bit PCI host aperture\n",
155 __FUNCTION__
156 ));
157 PcdStatus = PcdSet64S (PcdPciMmio64Size, 0);
158 ASSERT_RETURN_ERROR (PcdStatus);
159 }
160
161 return;
162 }
163
164 if (PlatformInfoHob->BootMode != BOOT_ON_S3_RESUME) {
165 //
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.
169 //
170 PcdStatus = PcdSet64S (PcdPciMmio64Base, PlatformInfoHob->PcdPciMmio64Base);
171 ASSERT_RETURN_ERROR (PcdStatus);
172 PcdStatus = PcdSet64S (PcdPciMmio64Size, PlatformInfoHob->PcdPciMmio64Size);
173 ASSERT_RETURN_ERROR (PcdStatus);
174
175 DEBUG ((
176 DEBUG_INFO,
177 "%a: Pci64Base=0x%Lx Pci64Size=0x%Lx\n",
178 __FUNCTION__,
179 PlatformInfoHob->PcdPciMmio64Base,
180 PlatformInfoHob->PcdPciMmio64Size
181 ));
182 }
183 }
184
185 /**
186 Calculate the cap for the permanent PEI memory.
187 **/
188 STATIC
189 UINT32
190 GetPeiMemoryCap (
191 IN EFI_HOB_PLATFORM_INFO *PlatformInfoHob
192 )
193 {
194 BOOLEAN Page1GSupport;
195 UINT32 RegEax;
196 UINT32 RegEdx;
197 UINT32 Pml4Entries;
198 UINT32 PdpEntries;
199 UINTN TotalPages;
200
201 //
202 // If DXE is 32-bit, then just return the traditional 64 MB cap.
203 //
204 #ifdef MDE_CPU_IA32
205 if (!FeaturePcdGet (PcdDxeIplSwitchToLongMode)) {
206 return SIZE_64MB;
207 }
208
209 #endif
210
211 //
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".
216 //
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;
224 }
225 }
226 }
227
228 if (PlatformInfoHob->PhysMemAddressWidth <= 39) {
229 Pml4Entries = 1;
230 PdpEntries = 1 << (PlatformInfoHob->PhysMemAddressWidth - 30);
231 ASSERT (PdpEntries <= 0x200);
232 } else {
233 if (PlatformInfoHob->PhysMemAddressWidth > 48) {
234 Pml4Entries = 0x200;
235 } else {
236 Pml4Entries = 1 << (PlatformInfoHob->PhysMemAddressWidth - 39);
237 }
238
239 ASSERT (Pml4Entries <= 0x200);
240 PdpEntries = 512;
241 }
242
243 TotalPages = Page1GSupport ? Pml4Entries + 1 :
244 (PdpEntries + 1) * Pml4Entries + 1;
245 ASSERT (TotalPages <= 0x40201);
246
247 //
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.
251 //
252 return (UINT32)(EFI_PAGES_TO_SIZE (TotalPages) + SIZE_64MB);
253 }
254
255 /**
256 Publish PEI core memory
257
258 @return EFI_SUCCESS The PEIM initialized successfully.
259
260 **/
261 EFI_STATUS
262 PublishPeiMemory (
263 IN OUT EFI_HOB_PLATFORM_INFO *PlatformInfoHob
264 )
265 {
266 EFI_STATUS Status;
267 EFI_PHYSICAL_ADDRESS MemoryBase;
268 UINT64 MemorySize;
269 UINT32 LowerMemorySize;
270 UINT32 PeiMemoryCap;
271 UINT32 S3AcpiReservedMemoryBase;
272 UINT32 S3AcpiReservedMemorySize;
273
274 PlatformGetSystemMemorySizeBelow4gb (PlatformInfoHob);
275 LowerMemorySize = PlatformInfoHob->LowMemory;
276 if (PlatformInfoHob->SmmSmramRequire) {
277 //
278 // TSEG is chipped from the end of low RAM
279 //
280 LowerMemorySize -= PlatformInfoHob->Q35TsegMbytes * SIZE_1MB;
281 }
282
283 S3AcpiReservedMemoryBase = 0;
284 S3AcpiReservedMemorySize = 0;
285
286 //
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.
290 //
291 if (PlatformInfoHob->S3Supported) {
292 S3AcpiReservedMemorySize = SIZE_512KB +
293 PlatformInfoHob->PcdCpuMaxLogicalProcessorNumber *
294 PcdGet32 (PcdCpuApStackSize);
295 S3AcpiReservedMemoryBase = LowerMemorySize - S3AcpiReservedMemorySize;
296 LowerMemorySize = S3AcpiReservedMemoryBase;
297 }
298
299 PlatformInfoHob->S3AcpiReservedMemoryBase = S3AcpiReservedMemoryBase;
300 PlatformInfoHob->S3AcpiReservedMemorySize = S3AcpiReservedMemorySize;
301
302 if (PlatformInfoHob->BootMode == BOOT_ON_S3_RESUME) {
303 MemoryBase = S3AcpiReservedMemoryBase;
304 MemorySize = S3AcpiReservedMemorySize;
305 } else {
306 PeiMemoryCap = GetPeiMemoryCap (PlatformInfoHob);
307 DEBUG ((
308 DEBUG_INFO,
309 "%a: PhysMemAddressWidth=%d PeiMemoryCap=%u KB\n",
310 __FUNCTION__,
311 PlatformInfoHob->PhysMemAddressWidth,
312 PeiMemoryCap >> 10
313 ));
314
315 //
316 // Determine the range of memory to use during PEI
317 //
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.
324 //
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;
332 }
333 }
334
335 //
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.
339 //
340 if (PlatformInfoHob->SmmSmramRequire && PlatformInfoHob->Q35SmramAtDefaultSmbase) {
341 ASSERT (SMM_DEFAULT_SMBASE + MCH_DEFAULT_SMBASE_SIZE <= MemoryBase);
342 }
343
344 //
345 // Publish this memory to the PEI Core
346 //
347 Status = PublishSystemMemory (MemoryBase, MemorySize);
348 ASSERT_EFI_ERROR (Status);
349
350 return Status;
351 }
352
353 /**
354 Publish system RAM and reserve memory regions
355
356 **/
357 VOID
358 InitializeRamRegions (
359 IN EFI_HOB_PLATFORM_INFO *PlatformInfoHob
360 )
361 {
362 if (TdIsEnabled ()) {
363 PlatformTdxPublishRamRegions ();
364 return;
365 }
366
367 PlatformQemuInitializeRam (PlatformInfoHob);
368
369 SevInitializeRam ();
370
371 PlatformQemuInitializeRamForS3 (PlatformInfoHob);
372 }