VirtualBox

source: vbox/trunk/src/VBox/Devices/EFI/Firmware/OvmfPkg/PlatformPei/MemDetect.c

Last change on this file was 105670, checked in by vboxsync, 4 months ago

Devices/EFI/FirmwareNew: Merge edk2-stable-202405 and make it build on aarch64, bugref:4643

  • Property svn:eol-style set to native
File size: 12.1 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 <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
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
116/**
117 Initialize the PhysMemAddressWidth field in PlatformInfoHob based on guest RAM size.
118**/
119VOID
120AddressWidthInitialization (
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
175/**
176 Calculate the cap for the permanent PEI memory.
177**/
178STATIC
179UINT32
180GetPeiMemoryCap (
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
294/**
295 Publish PEI core memory
296
297 @return EFI_SUCCESS The PEIM initialized successfully.
298
299**/
300EFI_STATUS
301PublishPeiMemory (
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#ifdef VBOX
382 MemorySize -= BASE_64KB; /* Reserves 64KB for ACPI tables. */
383#endif
384
385 //
386 // MEMFD_BASE_ADDRESS separates the SMRAM at the default SMBASE from the
387 // normal boot permanent PEI RAM. Regarding the S3 boot path, the S3
388 // permanent PEI RAM is located even higher.
389 //
390 if (PlatformInfoHob->SmmSmramRequire && PlatformInfoHob->Q35SmramAtDefaultSmbase) {
391 ASSERT (SMM_DEFAULT_SMBASE + MCH_DEFAULT_SMBASE_SIZE <= MemoryBase);
392 }
393
394 //
395 // Publish this memory to the PEI Core
396 //
397 Status = PublishSystemMemory (MemoryBase, MemorySize);
398 ASSERT_EFI_ERROR (Status);
399
400 return Status;
401}
402
403/**
404 Publish system RAM and reserve memory regions
405
406**/
407VOID
408InitializeRamRegions (
409 IN EFI_HOB_PLATFORM_INFO *PlatformInfoHob
410 )
411{
412 if (TdIsEnabled ()) {
413 PlatformTdxPublishRamRegions ();
414 return;
415 }
416
417 PlatformQemuInitializeRam (PlatformInfoHob);
418
419 SevInitializeRam ();
420
421 PlatformQemuInitializeRamForS3 (PlatformInfoHob);
422}
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