TianoCore EDK2 master
Loading...
Searching...
No Matches
MemDetect.c
Go to the documentation of this file.
1
13//
14// The package level header files this module uses
15//
20#include <IndustryStandard/Xen/arch-x86/hvm/start_info.h>
21#include <PiPei.h>
23
24//
25// The Library classes this module consumes
26//
27#include <Library/BaseLib.h>
29#include <Library/DebugLib.h>
30#include <Library/HobLib.h>
31#include <Library/IoLib.h>
33#include <Library/PcdLib.h>
34#include <Library/PciLib.h>
37
40#include "Platform.h"
41
42VOID
43Q35TsegMbytesInitialization (
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 __func__,
79 ExtendedTsegMbytes
80 ));
81 PcdStatus = PcdSet16S (PcdQ35TsegMbytes, ExtendedTsegMbytes);
82 ASSERT_RETURN_ERROR (PcdStatus);
83 PlatformInfoHob->Q35TsegMbytes = ExtendedTsegMbytes;
84}
85
86VOID
87Q35SmramAtDefaultSmbaseInitialization (
88 IN OUT EFI_HOB_PLATFORM_INFO *PlatformInfoHob
89 )
90{
91 RETURN_STATUS PcdStatus;
92 UINTN CtlReg;
93 UINT8 CtlRegVal;
94
95 ASSERT (PlatformInfoHob->HostBridgeDevId == INTEL_Q35_MCH_DEVICE_ID);
96
97 CtlReg = DRAMC_REGISTER_Q35 (MCH_DEFAULT_SMBASE_CTL);
98 PciWrite8 (CtlReg, MCH_DEFAULT_SMBASE_QUERY);
99 CtlRegVal = PciRead8 (CtlReg);
100 PlatformInfoHob->Q35SmramAtDefaultSmbase = (BOOLEAN)(CtlRegVal ==
101 MCH_DEFAULT_SMBASE_IN_RAM);
102 DEBUG ((
103 DEBUG_INFO,
104 "%a: SMRAM at default SMBASE %a\n",
105 __func__,
106 PlatformInfoHob->Q35SmramAtDefaultSmbase ? "found" : "not found"
107 ));
108
109 PcdStatus = PcdSetBoolS (
110 PcdQ35SmramAtDefaultSmbase,
111 PlatformInfoHob->Q35SmramAtDefaultSmbase
112 );
113 ASSERT_RETURN_ERROR (PcdStatus);
114}
115
119VOID
121 IN OUT EFI_HOB_PLATFORM_INFO *PlatformInfoHob
122 )
123{
124 RETURN_STATUS PcdStatus;
125
126 PlatformAddressWidthInitialization (PlatformInfoHob);
127
128 //
129 // If DXE is 32-bit, then we're done; PciBusDxe will degrade 64-bit MMIO
130 // resources to 32-bit anyway. See DegradeResource() in
131 // "PciResourceSupport.c".
132 //
133 #ifdef MDE_CPU_IA32
134 if (!FeaturePcdGet (PcdDxeIplSwitchToLongMode)) {
135 return;
136 }
137
138 #endif
139
140 if (PlatformInfoHob->PcdPciMmio64Size == 0) {
141 if (PlatformInfoHob->BootMode != BOOT_ON_S3_RESUME) {
142 DEBUG ((
143 DEBUG_INFO,
144 "%a: disabling 64-bit PCI host aperture\n",
145 __func__
146 ));
147 PcdStatus = PcdSet64S (PcdPciMmio64Size, 0);
148 ASSERT_RETURN_ERROR (PcdStatus);
149 }
150
151 return;
152 }
153
154 if (PlatformInfoHob->BootMode != BOOT_ON_S3_RESUME) {
155 //
156 // The core PciHostBridgeDxe driver will automatically add this range to
157 // the GCD memory space map through our PciHostBridgeLib instance; here we
158 // only need to set the PCDs.
159 //
160 PcdStatus = PcdSet64S (PcdPciMmio64Base, PlatformInfoHob->PcdPciMmio64Base);
161 ASSERT_RETURN_ERROR (PcdStatus);
162 PcdStatus = PcdSet64S (PcdPciMmio64Size, PlatformInfoHob->PcdPciMmio64Size);
163 ASSERT_RETURN_ERROR (PcdStatus);
164
165 DEBUG ((
166 DEBUG_INFO,
167 "%a: Pci64Base=0x%Lx Pci64Size=0x%Lx\n",
168 __func__,
169 PlatformInfoHob->PcdPciMmio64Base,
170 PlatformInfoHob->PcdPciMmio64Size
171 ));
172 }
173}
174
178STATIC
179UINT32
181 IN EFI_HOB_PLATFORM_INFO *PlatformInfoHob
182 )
183{
184 BOOLEAN Page1GSupport;
185 UINT32 RegEax;
186 UINT32 RegEdx;
187 UINT64 MaxAddr;
188 UINT32 Level5Pages;
189 UINT32 Level4Pages;
190 UINT32 Level3Pages;
191 UINT32 Level2Pages;
192 UINT32 TotalPages;
193 UINT64 ApStacks;
194 UINT64 MemoryCap;
195
196 //
197 // If DXE is 32-bit, then just return the traditional 64 MB cap.
198 //
199 #ifdef MDE_CPU_IA32
200 if (!FeaturePcdGet (PcdDxeIplSwitchToLongMode)) {
201 return SIZE_64MB;
202 }
203
204 #endif
205
206 //
207 // Dependent on physical address width, PEI memory allocations can be
208 // dominated by the page tables built for 64-bit DXE. So we key the cap off
209 // of those.
210 //
211 Page1GSupport = FALSE;
212 if (PcdGetBool (PcdUse1GPageTable)) {
213 AsmCpuid (0x80000000, &RegEax, NULL, NULL, NULL);
214 if (RegEax >= 0x80000001) {
215 AsmCpuid (0x80000001, NULL, NULL, NULL, &RegEdx);
216 if ((RegEdx & BIT26) != 0) {
217 Page1GSupport = TRUE;
218 }
219 }
220 }
221
222 //
223 // - A 4KB page accommodates the least significant 12 bits of the
224 // virtual address.
225 // - A page table entry at any level consumes 8 bytes, so a 4KB page
226 // table page (at any level) contains 512 entries, and
227 // accommodates 9 bits of the virtual address.
228 // - we minimally cover the phys address space with 2MB pages, so
229 // level 1 never exists.
230 // - If 1G paging is available, then level 2 doesn't exist either.
231 // - Start with level 2, where a page table page accommodates
232 // 9 + 9 + 12 = 30 bits of the virtual address (and covers 1GB of
233 // physical address space).
234 //
235
236 MaxAddr = LShiftU64 (1, PlatformInfoHob->PhysMemAddressWidth);
237 Level2Pages = (UINT32)RShiftU64 (MaxAddr, 30);
238 Level3Pages = MAX (Level2Pages >> 9, 1u);
239 Level4Pages = MAX (Level3Pages >> 9, 1u);
240 Level5Pages = 1;
241
242 if (Page1GSupport) {
243 Level2Pages = 0;
244 TotalPages = Level5Pages + Level4Pages + Level3Pages;
245 ASSERT (TotalPages <= 0x40201);
246 } else {
247 TotalPages = Level5Pages + Level4Pages + Level3Pages + Level2Pages;
248 // PlatformAddressWidthFromCpuid() caps at 40 phys bits without 1G pages.
249 ASSERT (PlatformInfoHob->PhysMemAddressWidth <= 40);
250 ASSERT (TotalPages <= 0x404);
251 }
252
253 //
254 // With 32k stacks and 4096 vcpus this lands at 128 MB (far away
255 // from MAX_UINT32).
256 //
257 ApStacks = PlatformInfoHob->PcdCpuMaxLogicalProcessorNumber * PcdGet32 (PcdCpuApStackSize);
258
259 //
260 // Add 64 MB for miscellaneous allocations. Note that for
261 // PhysMemAddressWidth values close to 36 and a small number of
262 // CPUs, the cap will actually be dominated by this increment.
263 //
264 MemoryCap = EFI_PAGES_TO_SIZE ((UINTN)TotalPages) + ApStacks + SIZE_64MB;
265
266 DEBUG ((
267 DEBUG_INFO,
268 "%a: page tables: %6lu KB (%u/%u/%u/%u pages for levels 5/4/3/2)\n",
269 __func__,
270 RShiftU64 (EFI_PAGES_TO_SIZE ((UINTN)TotalPages), 10),
271 Level5Pages,
272 Level4Pages,
273 Level3Pages,
274 Level2Pages
275 ));
276 DEBUG ((
277 DEBUG_INFO,
278 "%a: ap stacks: %6lu KB (%u cpus)\n",
279 __func__,
280 RShiftU64 (ApStacks, 10),
281 PlatformInfoHob->PcdCpuMaxLogicalProcessorNumber
282 ));
283 DEBUG ((
284 DEBUG_INFO,
285 "%a: memory cap: %6lu KB\n",
286 __func__,
287 RShiftU64 (MemoryCap, 10)
288 ));
289
290 ASSERT (MemoryCap <= MAX_UINT32);
291 return (UINT32)MemoryCap;
292}
293
302 IN OUT EFI_HOB_PLATFORM_INFO *PlatformInfoHob
303 )
304{
305 EFI_STATUS Status;
306 EFI_PHYSICAL_ADDRESS MemoryBase;
307 UINT64 MemorySize;
308 UINT32 LowerMemorySize;
309 UINT32 PeiMemoryCap;
310 UINT32 S3AcpiReservedMemoryBase;
311 UINT32 S3AcpiReservedMemorySize;
312
313 PlatformGetSystemMemorySizeBelow4gb (PlatformInfoHob);
314 LowerMemorySize = PlatformInfoHob->LowMemory;
315 if (PlatformInfoHob->SmmSmramRequire) {
316 //
317 // TSEG is chipped from the end of low RAM
318 //
319 LowerMemorySize -= PlatformInfoHob->Q35TsegMbytes * SIZE_1MB;
320 }
321
322 S3AcpiReservedMemoryBase = 0;
323 S3AcpiReservedMemorySize = 0;
324
325 //
326 // If S3 is supported, then the S3 permanent PEI memory is placed next,
327 // downwards. Its size is primarily dictated by CpuMpPei. The formula below
328 // is an approximation.
329 //
330 if (PlatformInfoHob->S3Supported) {
331 S3AcpiReservedMemorySize = SIZE_512KB +
332 PlatformInfoHob->PcdCpuMaxLogicalProcessorNumber *
333 PcdGet32 (PcdCpuApStackSize);
334 S3AcpiReservedMemoryBase = LowerMemorySize - S3AcpiReservedMemorySize;
335 LowerMemorySize = S3AcpiReservedMemoryBase;
336 }
337
338 PlatformInfoHob->S3AcpiReservedMemoryBase = S3AcpiReservedMemoryBase;
339 PlatformInfoHob->S3AcpiReservedMemorySize = S3AcpiReservedMemorySize;
340
341 if (PlatformInfoHob->BootMode == BOOT_ON_S3_RESUME) {
342 MemoryBase = S3AcpiReservedMemoryBase;
343 MemorySize = S3AcpiReservedMemorySize;
344 } else {
345 PeiMemoryCap = GetPeiMemoryCap (PlatformInfoHob);
346 DEBUG ((
347 DEBUG_INFO,
348 "%a: PhysMemAddressWidth=%d PeiMemoryCap=%u KB\n",
349 __func__,
350 PlatformInfoHob->PhysMemAddressWidth,
351 PeiMemoryCap >> 10
352 ));
353
354 //
355 // Determine the range of memory to use during PEI
356 //
357 // Technically we could lay the permanent PEI RAM over SEC's temporary
358 // decompression and scratch buffer even if "secure S3" is needed, since
359 // their lifetimes don't overlap. However, PeiFvInitialization() will cover
360 // RAM up to PcdOvmfDecompressionScratchEnd with an EfiACPIMemoryNVS memory
361 // allocation HOB, and other allocations served from the permanent PEI RAM
362 // shouldn't overlap with that HOB.
363 //
364 MemoryBase = PlatformInfoHob->S3Supported && PlatformInfoHob->SmmSmramRequire ?
365 PcdGet32 (PcdOvmfDecompressionScratchEnd) :
366 PcdGet32 (PcdOvmfDxeMemFvBase) + PcdGet32 (PcdOvmfDxeMemFvSize);
367 MemorySize = LowerMemorySize - MemoryBase;
368 if (MemorySize > PeiMemoryCap) {
369 MemoryBase = LowerMemorySize - PeiMemoryCap;
370 MemorySize = PeiMemoryCap;
371 } else {
372 DEBUG ((
373 DEBUG_WARN,
374 "%a: Not enough memory for PEI (have %lu KB, estimated need %u KB)\n",
375 __func__,
376 RShiftU64 (MemorySize, 10),
377 PeiMemoryCap >> 10
378 ));
379 }
380 }
381
382 //
383 // MEMFD_BASE_ADDRESS separates the SMRAM at the default SMBASE from the
384 // normal boot permanent PEI RAM. Regarding the S3 boot path, the S3
385 // permanent PEI RAM is located even higher.
386 //
387 if (PlatformInfoHob->SmmSmramRequire && PlatformInfoHob->Q35SmramAtDefaultSmbase) {
388 ASSERT (SMM_DEFAULT_SMBASE + MCH_DEFAULT_SMBASE_SIZE <= MemoryBase);
389 }
390
391 //
392 // Publish this memory to the PEI Core
393 //
394 Status = PublishSystemMemory (MemoryBase, MemorySize);
395 ASSERT_EFI_ERROR (Status);
396
397 return Status;
398}
399
404VOID
406 IN EFI_HOB_PLATFORM_INFO *PlatformInfoHob
407 )
408{
409 if (TdIsEnabled ()) {
411 return;
412 }
413
414 PlatformQemuInitializeRam (PlatformInfoHob);
415
417
418 PlatformQemuInitializeRamForS3 (PlatformInfoHob);
419}
UINT64 UINTN
UINT64 EFIAPI RShiftU64(IN UINT64 Operand, IN UINTN Count)
Definition: RShiftU64.c:28
UINT64 EFIAPI LShiftU64(IN UINT64 Operand, IN UINTN Count)
Definition: LShiftU64.c:28
VOID AddressWidthInitialization(VOID)
Definition: MemDetect.c:248
EFI_STATUS PublishPeiMemory(VOID)
Definition: MemDetect.c:356
STATIC UINT32 GetPeiMemoryCap(VOID)
Definition: MemDetect.c:289
VOID InitializeRamRegions(VOID)
Definition: MemDetect.c:556
VOID EFIAPI PlatformAddressWidthInitialization(IN OUT EFI_HOB_PLATFORM_INFO *PlatformInfoHob)
Definition: MemDetect.c:1045
VOID EFIAPI PlatformQemuInitializeRam(IN EFI_HOB_PLATFORM_INFO *PlatformInfoHob)
Definition: MemDetect.c:1231
#define NULL
Definition: Base.h:319
#define STATIC
Definition: Base.h:264
#define TRUE
Definition: Base.h:301
#define FALSE
Definition: Base.h:307
#define IN
Definition: Base.h:279
#define OUT
Definition: Base.h:284
#define MAX(a, b)
Definition: Base.h:992
#define ASSERT_EFI_ERROR(StatusParameter)
Definition: DebugLib.h:462
#define ASSERT_RETURN_ERROR(StatusParameter)
Definition: DebugLib.h:493
#define DEBUG(Expression)
Definition: DebugLib.h:434
UINT8 EFIAPI PciRead8(IN UINTN Address)
Definition: PciLib.c:62
UINT8 EFIAPI PciWrite8(IN UINTN Address, IN UINT8 Value)
Definition: PciLib.c:87
UINT16 EFIAPI PciWrite16(IN UINTN Address, IN UINT16 Value)
Definition: PciLib.c:422
UINT16 EFIAPI PciRead16(IN UINTN Address)
Definition: PciLib.c:396
#define SMM_DEFAULT_SMBASE
UINT32 EFIAPI AsmCpuid(IN UINT32 Index, OUT UINT32 *RegisterEax OPTIONAL, OUT UINT32 *RegisterEbx OPTIONAL, OUT UINT32 *RegisterEcx OPTIONAL, OUT UINT32 *RegisterEdx OPTIONAL)
Definition: CpuId.c:36
BOOLEAN EFIAPI TdIsEnabled()
Definition: IntelTdxNull.c:79
VOID SevInitializeRam(VOID)
Definition: AmdSev.c:542
#define PcdGet16(TokenName)
Definition: PcdLib.h:349
#define PcdSetBoolS(TokenName, Value)
Definition: PcdLib.h:549
#define PcdGet32(TokenName)
Definition: PcdLib.h:362
#define PcdGetBool(TokenName)
Definition: PcdLib.h:401
#define PcdSet64S(TokenName, Value)
Definition: PcdLib.h:511
#define PcdSet16S(TokenName, Value)
Definition: PcdLib.h:483
#define FeaturePcdGet(TokenName)
Definition: PcdLib.h:50
VOID EFIAPI PlatformTdxPublishRamRegions(VOID)
Definition: IntelTdx.c:146
RETURN_STATUS EFIAPI PublishSystemMemory(IN PHYSICAL_ADDRESS MemoryBegin, IN UINT64 MemoryLength)
UINT64 EFI_PHYSICAL_ADDRESS
Definition: UefiBaseType.h:50
#define EFI_PAGES_TO_SIZE(Pages)
Definition: UefiBaseType.h:213
RETURN_STATUS EFI_STATUS
Definition: UefiBaseType.h:29