]> git.proxmox.com Git - mirror_edk2.git/blame - OvmfPkg/PlatformPei/MemDetect.c
OvmfPkg/PlatformPei: parse "X-PciMmio64Mb" with QemuFwCfgSimpleParserLib
[mirror_edk2.git] / OvmfPkg / PlatformPei / MemDetect.c
CommitLineData
49ba9447 1/**@file\r
2 Memory Detection for Virtual Machines.\r
3\r
035ce3b3 4 Copyright (c) 2006 - 2016, Intel Corporation. All rights reserved.<BR>\r
b26f0cf9 5 SPDX-License-Identifier: BSD-2-Clause-Patent\r
49ba9447 6\r
7Module Name:\r
8\r
9 MemDetect.c\r
10\r
11**/\r
12\r
13//\r
14// The package level header files this module uses\r
15//\r
1fceaddb 16#include <IndustryStandard/E820.h>\r
49edde15 17#include <IndustryStandard/I440FxPiix4.h>\r
d5e06444 18#include <IndustryStandard/Q35MchIch9.h>\r
49ba9447 19#include <PiPei.h>\r
adec2bd5 20#include <Register/Intel/SmramSaveStateMap.h>\r
49ba9447 21\r
22//\r
23// The Library classes this module consumes\r
24//\r
d5e06444 25#include <Library/BaseLib.h>\r
6a7cba79 26#include <Library/BaseMemoryLib.h>\r
49ba9447 27#include <Library/DebugLib.h>\r
28#include <Library/HobLib.h>\r
29#include <Library/IoLib.h>\r
c1c2669c 30#include <Library/PcdLib.h>\r
d5e06444 31#include <Library/PciLib.h>\r
49ba9447 32#include <Library/PeimEntryPoint.h>\r
33#include <Library/ResourcePublicationLib.h>\r
e8e5cd4a 34#include <Library/MtrrLib.h>\r
7e5b1b67 35#include <Library/QemuFwCfgLib.h>\r
98800cce 36#include <Library/QemuFwCfgSimpleParserLib.h>\r
49ba9447 37\r
38#include "Platform.h"\r
39#include "Cmos.h"\r
40\r
bc89fe48
LE
41UINT8 mPhysMemAddressWidth;\r
42\r
45d87081
LE
43STATIC UINT32 mS3AcpiReservedMemoryBase;\r
44STATIC UINT32 mS3AcpiReservedMemorySize;\r
45\r
23bfb5c0
LE
46STATIC UINT16 mQ35TsegMbytes;\r
47\r
73974f80
LE
48BOOLEAN mQ35SmramAtDefaultSmbase;\r
49\r
49edde15
LE
50UINT32 mQemuUc32Base;\r
51\r
23bfb5c0
LE
52VOID\r
53Q35TsegMbytesInitialization (\r
54 VOID\r
55 )\r
56{\r
d5e06444
LE
57 UINT16 ExtendedTsegMbytes;\r
58 RETURN_STATUS PcdStatus;\r
59\r
e0ed7a9b 60 ASSERT (mHostBridgeDevId == INTEL_Q35_MCH_DEVICE_ID);\r
d5e06444
LE
61\r
62 //\r
63 // Check if QEMU offers an extended TSEG.\r
64 //\r
65 // This can be seen from writing MCH_EXT_TSEG_MB_QUERY to the MCH_EXT_TSEG_MB\r
66 // register, and reading back the register.\r
67 //\r
68 // On a QEMU machine type that does not offer an extended TSEG, the initial\r
69 // write overwrites whatever value a malicious guest OS may have placed in\r
70 // the (unimplemented) register, before entering S3 or rebooting.\r
71 // Subsequently, the read returns MCH_EXT_TSEG_MB_QUERY unchanged.\r
72 //\r
73 // On a QEMU machine type that offers an extended TSEG, the initial write\r
74 // triggers an update to the register. Subsequently, the value read back\r
75 // (which is guaranteed to differ from MCH_EXT_TSEG_MB_QUERY) tells us the\r
76 // number of megabytes.\r
77 //\r
78 PciWrite16 (DRAMC_REGISTER_Q35 (MCH_EXT_TSEG_MB), MCH_EXT_TSEG_MB_QUERY);\r
79 ExtendedTsegMbytes = PciRead16 (DRAMC_REGISTER_Q35 (MCH_EXT_TSEG_MB));\r
80 if (ExtendedTsegMbytes == MCH_EXT_TSEG_MB_QUERY) {\r
81 mQ35TsegMbytes = PcdGet16 (PcdQ35TsegMbytes);\r
82 return;\r
83 }\r
84\r
85 DEBUG ((\r
86 DEBUG_INFO,\r
87 "%a: QEMU offers an extended TSEG (%d MB)\n",\r
88 __FUNCTION__,\r
89 ExtendedTsegMbytes\r
90 ));\r
91 PcdStatus = PcdSet16S (PcdQ35TsegMbytes, ExtendedTsegMbytes);\r
92 ASSERT_RETURN_ERROR (PcdStatus);\r
93 mQ35TsegMbytes = ExtendedTsegMbytes;\r
23bfb5c0
LE
94}\r
95\r
96\r
73974f80
LE
97VOID\r
98Q35SmramAtDefaultSmbaseInitialization (\r
99 VOID\r
100 )\r
101{\r
102 RETURN_STATUS PcdStatus;\r
103\r
104 ASSERT (mHostBridgeDevId == INTEL_Q35_MCH_DEVICE_ID);\r
105\r
106 mQ35SmramAtDefaultSmbase = FALSE;\r
75839f97
LE
107 if (FeaturePcdGet (PcdCsmEnable)) {\r
108 DEBUG ((DEBUG_INFO, "%a: SMRAM at default SMBASE not checked due to CSM\n",\r
109 __FUNCTION__));\r
110 } else {\r
111 UINTN CtlReg;\r
112 UINT8 CtlRegVal;\r
113\r
114 CtlReg = DRAMC_REGISTER_Q35 (MCH_DEFAULT_SMBASE_CTL);\r
115 PciWrite8 (CtlReg, MCH_DEFAULT_SMBASE_QUERY);\r
116 CtlRegVal = PciRead8 (CtlReg);\r
117 mQ35SmramAtDefaultSmbase = (BOOLEAN)(CtlRegVal ==\r
118 MCH_DEFAULT_SMBASE_IN_RAM);\r
119 DEBUG ((DEBUG_INFO, "%a: SMRAM at default SMBASE %a\n", __FUNCTION__,\r
120 mQ35SmramAtDefaultSmbase ? "found" : "not found"));\r
121 }\r
122\r
73974f80
LE
123 PcdStatus = PcdSetBoolS (PcdQ35SmramAtDefaultSmbase,\r
124 mQ35SmramAtDefaultSmbase);\r
125 ASSERT_RETURN_ERROR (PcdStatus);\r
126}\r
127\r
128\r
49edde15
LE
129VOID\r
130QemuUc32BaseInitialization (\r
131 VOID\r
132 )\r
133{\r
134 UINT32 LowerMemorySize;\r
135 UINT32 Uc32Size;\r
136\r
137 if (mXen) {\r
138 return;\r
139 }\r
140\r
141 if (mHostBridgeDevId == INTEL_Q35_MCH_DEVICE_ID) {\r
142 //\r
143 // On q35, the 32-bit area that we'll mark as UC, through variable MTRRs,\r
144 // starts at PcdPciExpressBaseAddress. The platform DSC is responsible for\r
145 // setting PcdPciExpressBaseAddress such that describing the\r
146 // [PcdPciExpressBaseAddress, 4GB) range require a very small number of\r
147 // variable MTRRs (preferably 1 or 2).\r
148 //\r
149 ASSERT (FixedPcdGet64 (PcdPciExpressBaseAddress) <= MAX_UINT32);\r
150 mQemuUc32Base = (UINT32)FixedPcdGet64 (PcdPciExpressBaseAddress);\r
151 return;\r
152 }\r
153\r
154 ASSERT (mHostBridgeDevId == INTEL_82441_DEVICE_ID);\r
155 //\r
156 // On i440fx, start with the [LowerMemorySize, 4GB) range. Make sure one\r
157 // variable MTRR suffices by truncating the size to a whole power of two,\r
158 // while keeping the end affixed to 4GB. This will round the base up.\r
159 //\r
160 LowerMemorySize = GetSystemMemorySizeBelow4gb ();\r
161 Uc32Size = GetPowerOfTwo32 ((UINT32)(SIZE_4GB - LowerMemorySize));\r
162 mQemuUc32Base = (UINT32)(SIZE_4GB - Uc32Size);\r
163 //\r
164 // Assuming that LowerMemorySize is at least 1 byte, Uc32Size is at most 2GB.\r
165 // Therefore mQemuUc32Base is at least 2GB.\r
166 //\r
167 ASSERT (mQemuUc32Base >= BASE_2GB);\r
168\r
169 if (mQemuUc32Base != LowerMemorySize) {\r
170 DEBUG ((DEBUG_VERBOSE, "%a: rounded UC32 base from 0x%x up to 0x%x, for "\r
171 "an UC32 size of 0x%x\n", __FUNCTION__, LowerMemorySize, mQemuUc32Base,\r
172 Uc32Size));\r
173 }\r
174}\r
175\r
176\r
1fceaddb
LE
177/**\r
178 Iterate over the RAM entries in QEMU's fw_cfg E820 RAM map that start outside\r
179 of the 32-bit address range.\r
180\r
181 Find the highest exclusive >=4GB RAM address, or produce memory resource\r
182 descriptor HOBs for RAM entries that start at or above 4GB.\r
183\r
184 @param[out] MaxAddress If MaxAddress is NULL, then ScanOrAdd64BitE820Ram()\r
185 produces memory resource descriptor HOBs for RAM\r
186 entries that start at or above 4GB.\r
187\r
188 Otherwise, MaxAddress holds the highest exclusive\r
189 >=4GB RAM address on output. If QEMU's fw_cfg E820\r
190 RAM map contains no RAM entry that starts outside of\r
191 the 32-bit address range, then MaxAddress is exactly\r
192 4GB on output.\r
193\r
194 @retval EFI_SUCCESS The fw_cfg E820 RAM map was found and processed.\r
195\r
196 @retval EFI_PROTOCOL_ERROR The RAM map was found, but its size wasn't a\r
197 whole multiple of sizeof(EFI_E820_ENTRY64). No\r
198 RAM entry was processed.\r
199\r
200 @return Error codes from QemuFwCfgFindFile(). No RAM\r
201 entry was processed.\r
202**/\r
203STATIC\r
204EFI_STATUS\r
205ScanOrAdd64BitE820Ram (\r
206 OUT UINT64 *MaxAddress OPTIONAL\r
207 )\r
208{\r
209 EFI_STATUS Status;\r
210 FIRMWARE_CONFIG_ITEM FwCfgItem;\r
211 UINTN FwCfgSize;\r
212 EFI_E820_ENTRY64 E820Entry;\r
213 UINTN Processed;\r
214\r
215 Status = QemuFwCfgFindFile ("etc/e820", &FwCfgItem, &FwCfgSize);\r
216 if (EFI_ERROR (Status)) {\r
217 return Status;\r
218 }\r
219 if (FwCfgSize % sizeof E820Entry != 0) {\r
220 return EFI_PROTOCOL_ERROR;\r
221 }\r
222\r
223 if (MaxAddress != NULL) {\r
224 *MaxAddress = BASE_4GB;\r
225 }\r
226\r
227 QemuFwCfgSelectItem (FwCfgItem);\r
228 for (Processed = 0; Processed < FwCfgSize; Processed += sizeof E820Entry) {\r
229 QemuFwCfgReadBytes (sizeof E820Entry, &E820Entry);\r
230 DEBUG ((\r
231 DEBUG_VERBOSE,\r
232 "%a: Base=0x%Lx Length=0x%Lx Type=%u\n",\r
233 __FUNCTION__,\r
234 E820Entry.BaseAddr,\r
235 E820Entry.Length,\r
236 E820Entry.Type\r
237 ));\r
238 if (E820Entry.Type == EfiAcpiAddressRangeMemory &&\r
239 E820Entry.BaseAddr >= BASE_4GB) {\r
240 if (MaxAddress == NULL) {\r
241 UINT64 Base;\r
242 UINT64 End;\r
243\r
244 //\r
245 // Round up the start address, and round down the end address.\r
246 //\r
247 Base = ALIGN_VALUE (E820Entry.BaseAddr, (UINT64)EFI_PAGE_SIZE);\r
248 End = (E820Entry.BaseAddr + E820Entry.Length) &\r
249 ~(UINT64)EFI_PAGE_MASK;\r
250 if (Base < End) {\r
251 AddMemoryRangeHob (Base, End);\r
252 DEBUG ((\r
253 DEBUG_VERBOSE,\r
254 "%a: AddMemoryRangeHob [0x%Lx, 0x%Lx)\n",\r
255 __FUNCTION__,\r
256 Base,\r
257 End\r
258 ));\r
259 }\r
260 } else {\r
261 UINT64 Candidate;\r
262\r
263 Candidate = E820Entry.BaseAddr + E820Entry.Length;\r
264 if (Candidate > *MaxAddress) {\r
265 *MaxAddress = Candidate;\r
266 DEBUG ((\r
267 DEBUG_VERBOSE,\r
268 "%a: MaxAddress=0x%Lx\n",\r
269 __FUNCTION__,\r
270 *MaxAddress\r
271 ));\r
272 }\r
273 }\r
274 }\r
275 }\r
276 return EFI_SUCCESS;\r
277}\r
278\r
279\r
4b455f7b 280UINT32\r
c0e10976 281GetSystemMemorySizeBelow4gb (\r
4b455f7b 282 VOID\r
49ba9447 283 )\r
284{\r
285 UINT8 Cmos0x34;\r
286 UINT8 Cmos0x35;\r
287\r
288 //\r
289 // CMOS 0x34/0x35 specifies the system memory above 16 MB.\r
290 // * CMOS(0x35) is the high byte\r
291 // * CMOS(0x34) is the low byte\r
292 // * The size is specified in 64kb chunks\r
293 // * Since this is memory above 16MB, the 16MB must be added\r
294 // into the calculation to get the total memory size.\r
295 //\r
296\r
297 Cmos0x34 = (UINT8) CmosRead8 (0x34);\r
298 Cmos0x35 = (UINT8) CmosRead8 (0x35);\r
299\r
c4046161 300 return (UINT32) (((UINTN)((Cmos0x35 << 8) + Cmos0x34) << 16) + SIZE_16MB);\r
49ba9447 301}\r
302\r
303\r
c0e10976 304STATIC\r
305UINT64\r
306GetSystemMemorySizeAbove4gb (\r
307 )\r
308{\r
309 UINT32 Size;\r
310 UINTN CmosIndex;\r
311\r
312 //\r
313 // CMOS 0x5b-0x5d specifies the system memory above 4GB MB.\r
314 // * CMOS(0x5d) is the most significant size byte\r
315 // * CMOS(0x5c) is the middle size byte\r
316 // * CMOS(0x5b) is the least significant size byte\r
317 // * The size is specified in 64kb chunks\r
318 //\r
319\r
320 Size = 0;\r
321 for (CmosIndex = 0x5d; CmosIndex >= 0x5b; CmosIndex--) {\r
322 Size = (UINT32) (Size << 8) + (UINT32) CmosRead8 (CmosIndex);\r
323 }\r
324\r
325 return LShiftU64 (Size, 16);\r
326}\r
327\r
bc89fe48 328\r
d5371680
LE
329/**\r
330 Return the highest address that DXE could possibly use, plus one.\r
331**/\r
332STATIC\r
333UINT64\r
334GetFirstNonAddress (\r
335 VOID\r
336 )\r
337{\r
338 UINT64 FirstNonAddress;\r
7e5b1b67 339 UINT64 Pci64Base, Pci64Size;\r
98800cce 340 UINT32 FwCfgPciMmio64Mb;\r
7e5b1b67
LE
341 EFI_STATUS Status;\r
342 FIRMWARE_CONFIG_ITEM FwCfgItem;\r
343 UINTN FwCfgSize;\r
344 UINT64 HotPlugMemoryEnd;\r
32e083c7 345 RETURN_STATUS PcdStatus;\r
d5371680 346\r
1fceaddb
LE
347 //\r
348 // set FirstNonAddress to suppress incorrect compiler/analyzer warnings\r
349 //\r
350 FirstNonAddress = 0;\r
351\r
352 //\r
353 // If QEMU presents an E820 map, then get the highest exclusive >=4GB RAM\r
354 // address from it. This can express an address >= 4GB+1TB.\r
355 //\r
356 // Otherwise, get the flat size of the memory above 4GB from the CMOS (which\r
357 // can only express a size smaller than 1TB), and add it to 4GB.\r
358 //\r
359 Status = ScanOrAdd64BitE820Ram (&FirstNonAddress);\r
360 if (EFI_ERROR (Status)) {\r
361 FirstNonAddress = BASE_4GB + GetSystemMemorySizeAbove4gb ();\r
362 }\r
7e5b1b67
LE
363\r
364 //\r
365 // If DXE is 32-bit, then we're done; PciBusDxe will degrade 64-bit MMIO\r
366 // resources to 32-bit anyway. See DegradeResource() in\r
367 // "PciResourceSupport.c".\r
368 //\r
369#ifdef MDE_CPU_IA32\r
370 if (!FeaturePcdGet (PcdDxeIplSwitchToLongMode)) {\r
371 return FirstNonAddress;\r
372 }\r
373#endif\r
374\r
375 //\r
376 // Otherwise, in order to calculate the highest address plus one, we must\r
377 // consider the 64-bit PCI host aperture too. Fetch the default size.\r
378 //\r
379 Pci64Size = PcdGet64 (PcdPciMmio64Size);\r
380\r
381 //\r
382 // See if the user specified the number of megabytes for the 64-bit PCI host\r
98800cce 383 // aperture. Accept an aperture size up to 16TB.\r
7e5b1b67
LE
384 //\r
385 // As signaled by the "X-" prefix, this knob is experimental, and might go\r
386 // away at any time.\r
387 //\r
98800cce
LE
388 Status = QemuFwCfgParseUint32 ("opt/ovmf/X-PciMmio64Mb", FALSE,\r
389 &FwCfgPciMmio64Mb);\r
390 switch (Status) {\r
391 case EFI_UNSUPPORTED:\r
392 case EFI_NOT_FOUND:\r
393 break;\r
394 case EFI_SUCCESS:\r
395 if (FwCfgPciMmio64Mb <= 0x1000000) {\r
396 Pci64Size = LShiftU64 (FwCfgPciMmio64Mb, 20);\r
397 break;\r
7e5b1b67 398 }\r
98800cce
LE
399 //\r
400 // fall through\r
401 //\r
402 default:\r
403 DEBUG ((DEBUG_WARN,\r
404 "%a: ignoring malformed 64-bit PCI host aperture size from fw_cfg\n",\r
405 __FUNCTION__));\r
406 break;\r
7e5b1b67
LE
407 }\r
408\r
409 if (Pci64Size == 0) {\r
410 if (mBootMode != BOOT_ON_S3_RESUME) {\r
411 DEBUG ((EFI_D_INFO, "%a: disabling 64-bit PCI host aperture\n",\r
412 __FUNCTION__));\r
32e083c7
LE
413 PcdStatus = PcdSet64S (PcdPciMmio64Size, 0);\r
414 ASSERT_RETURN_ERROR (PcdStatus);\r
7e5b1b67
LE
415 }\r
416\r
417 //\r
418 // There's nothing more to do; the amount of memory above 4GB fully\r
419 // determines the highest address plus one. The memory hotplug area (see\r
420 // below) plays no role for the firmware in this case.\r
421 //\r
422 return FirstNonAddress;\r
423 }\r
424\r
425 //\r
426 // The "etc/reserved-memory-end" fw_cfg file, when present, contains an\r
427 // absolute, exclusive end address for the memory hotplug area. This area\r
428 // starts right at the end of the memory above 4GB. The 64-bit PCI host\r
429 // aperture must be placed above it.\r
430 //\r
431 Status = QemuFwCfgFindFile ("etc/reserved-memory-end", &FwCfgItem,\r
432 &FwCfgSize);\r
433 if (!EFI_ERROR (Status) && FwCfgSize == sizeof HotPlugMemoryEnd) {\r
434 QemuFwCfgSelectItem (FwCfgItem);\r
435 QemuFwCfgReadBytes (FwCfgSize, &HotPlugMemoryEnd);\r
c27c0003
LE
436 DEBUG ((DEBUG_VERBOSE, "%a: HotPlugMemoryEnd=0x%Lx\n", __FUNCTION__,\r
437 HotPlugMemoryEnd));\r
7e5b1b67
LE
438\r
439 ASSERT (HotPlugMemoryEnd >= FirstNonAddress);\r
440 FirstNonAddress = HotPlugMemoryEnd;\r
441 }\r
442\r
443 //\r
444 // SeaBIOS aligns both boundaries of the 64-bit PCI host aperture to 1GB, so\r
445 // that the host can map it with 1GB hugepages. Follow suit.\r
446 //\r
447 Pci64Base = ALIGN_VALUE (FirstNonAddress, (UINT64)SIZE_1GB);\r
448 Pci64Size = ALIGN_VALUE (Pci64Size, (UINT64)SIZE_1GB);\r
449\r
450 //\r
451 // The 64-bit PCI host aperture should also be "naturally" aligned. The\r
452 // alignment is determined by rounding the size of the aperture down to the\r
453 // next smaller or equal power of two. That is, align the aperture by the\r
454 // largest BAR size that can fit into it.\r
455 //\r
456 Pci64Base = ALIGN_VALUE (Pci64Base, GetPowerOfTwo64 (Pci64Size));\r
457\r
458 if (mBootMode != BOOT_ON_S3_RESUME) {\r
459 //\r
460 // The core PciHostBridgeDxe driver will automatically add this range to\r
461 // the GCD memory space map through our PciHostBridgeLib instance; here we\r
462 // only need to set the PCDs.\r
463 //\r
32e083c7
LE
464 PcdStatus = PcdSet64S (PcdPciMmio64Base, Pci64Base);\r
465 ASSERT_RETURN_ERROR (PcdStatus);\r
466 PcdStatus = PcdSet64S (PcdPciMmio64Size, Pci64Size);\r
467 ASSERT_RETURN_ERROR (PcdStatus);\r
468\r
7e5b1b67
LE
469 DEBUG ((EFI_D_INFO, "%a: Pci64Base=0x%Lx Pci64Size=0x%Lx\n",\r
470 __FUNCTION__, Pci64Base, Pci64Size));\r
471 }\r
472\r
473 //\r
474 // The useful address space ends with the 64-bit PCI host aperture.\r
475 //\r
476 FirstNonAddress = Pci64Base + Pci64Size;\r
d5371680
LE
477 return FirstNonAddress;\r
478}\r
479\r
480\r
bc89fe48
LE
481/**\r
482 Initialize the mPhysMemAddressWidth variable, based on guest RAM size.\r
483**/\r
484VOID\r
485AddressWidthInitialization (\r
486 VOID\r
487 )\r
488{\r
489 UINT64 FirstNonAddress;\r
490\r
491 //\r
492 // As guest-physical memory size grows, the permanent PEI RAM requirements\r
493 // are dominated by the identity-mapping page tables built by the DXE IPL.\r
494 // The DXL IPL keys off of the physical address bits advertized in the CPU\r
495 // HOB. To conserve memory, we calculate the minimum address width here.\r
496 //\r
d5371680 497 FirstNonAddress = GetFirstNonAddress ();\r
bc89fe48
LE
498 mPhysMemAddressWidth = (UINT8)HighBitSet64 (FirstNonAddress);\r
499\r
500 //\r
501 // If FirstNonAddress is not an integral power of two, then we need an\r
502 // additional bit.\r
503 //\r
504 if ((FirstNonAddress & (FirstNonAddress - 1)) != 0) {\r
505 ++mPhysMemAddressWidth;\r
506 }\r
507\r
508 //\r
509 // The minimum address width is 36 (covers up to and excluding 64 GB, which\r
510 // is the maximum for Ia32 + PAE). The theoretical architecture maximum for\r
511 // X64 long mode is 52 bits, but the DXE IPL clamps that down to 48 bits. We\r
512 // can simply assert that here, since 48 bits are good enough for 256 TB.\r
513 //\r
514 if (mPhysMemAddressWidth <= 36) {\r
515 mPhysMemAddressWidth = 36;\r
516 }\r
517 ASSERT (mPhysMemAddressWidth <= 48);\r
518}\r
519\r
520\r
521/**\r
522 Calculate the cap for the permanent PEI memory.\r
523**/\r
524STATIC\r
525UINT32\r
526GetPeiMemoryCap (\r
527 VOID\r
528 )\r
529{\r
530 BOOLEAN Page1GSupport;\r
531 UINT32 RegEax;\r
532 UINT32 RegEdx;\r
533 UINT32 Pml4Entries;\r
534 UINT32 PdpEntries;\r
535 UINTN TotalPages;\r
536\r
537 //\r
538 // If DXE is 32-bit, then just return the traditional 64 MB cap.\r
539 //\r
540#ifdef MDE_CPU_IA32\r
541 if (!FeaturePcdGet (PcdDxeIplSwitchToLongMode)) {\r
542 return SIZE_64MB;\r
543 }\r
544#endif\r
545\r
546 //\r
547 // Dependent on physical address width, PEI memory allocations can be\r
548 // dominated by the page tables built for 64-bit DXE. So we key the cap off\r
549 // of those. The code below is based on CreateIdentityMappingPageTables() in\r
550 // "MdeModulePkg/Core/DxeIplPeim/X64/VirtualMemory.c".\r
551 //\r
552 Page1GSupport = FALSE;\r
553 if (PcdGetBool (PcdUse1GPageTable)) {\r
554 AsmCpuid (0x80000000, &RegEax, NULL, NULL, NULL);\r
555 if (RegEax >= 0x80000001) {\r
556 AsmCpuid (0x80000001, NULL, NULL, NULL, &RegEdx);\r
557 if ((RegEdx & BIT26) != 0) {\r
558 Page1GSupport = TRUE;\r
559 }\r
560 }\r
561 }\r
562\r
563 if (mPhysMemAddressWidth <= 39) {\r
564 Pml4Entries = 1;\r
565 PdpEntries = 1 << (mPhysMemAddressWidth - 30);\r
566 ASSERT (PdpEntries <= 0x200);\r
567 } else {\r
568 Pml4Entries = 1 << (mPhysMemAddressWidth - 39);\r
569 ASSERT (Pml4Entries <= 0x200);\r
570 PdpEntries = 512;\r
571 }\r
572\r
573 TotalPages = Page1GSupport ? Pml4Entries + 1 :\r
574 (PdpEntries + 1) * Pml4Entries + 1;\r
575 ASSERT (TotalPages <= 0x40201);\r
576\r
577 //\r
578 // Add 64 MB for miscellaneous allocations. Note that for\r
579 // mPhysMemAddressWidth values close to 36, the cap will actually be\r
580 // dominated by this increment.\r
581 //\r
582 return (UINT32)(EFI_PAGES_TO_SIZE (TotalPages) + SIZE_64MB);\r
583}\r
584\r
585\r
36658fff
WL
586/**\r
587 Publish PEI core memory\r
588\r
589 @return EFI_SUCCESS The PEIM initialized successfully.\r
590\r
591**/\r
592EFI_STATUS\r
593PublishPeiMemory (\r
594 VOID\r
595 )\r
596{\r
597 EFI_STATUS Status;\r
598 EFI_PHYSICAL_ADDRESS MemoryBase;\r
599 UINT64 MemorySize;\r
fc3f005a 600 UINT32 LowerMemorySize;\r
bc89fe48 601 UINT32 PeiMemoryCap;\r
36658fff 602\r
45d87081
LE
603 LowerMemorySize = GetSystemMemorySizeBelow4gb ();\r
604 if (FeaturePcdGet (PcdSmmSmramRequire)) {\r
605 //\r
606 // TSEG is chipped from the end of low RAM\r
607 //\r
23bfb5c0 608 LowerMemorySize -= mQ35TsegMbytes * SIZE_1MB;\r
45d87081
LE
609 }\r
610\r
611 //\r
612 // If S3 is supported, then the S3 permanent PEI memory is placed next,\r
613 // downwards. Its size is primarily dictated by CpuMpPei. The formula below\r
614 // is an approximation.\r
615 //\r
616 if (mS3Supported) {\r
617 mS3AcpiReservedMemorySize = SIZE_512KB +\r
45a70db3 618 mMaxCpuCount *\r
45d87081
LE
619 PcdGet32 (PcdCpuApStackSize);\r
620 mS3AcpiReservedMemoryBase = LowerMemorySize - mS3AcpiReservedMemorySize;\r
621 LowerMemorySize = mS3AcpiReservedMemoryBase;\r
622 }\r
623\r
8e54500f 624 if (mBootMode == BOOT_ON_S3_RESUME) {\r
45d87081
LE
625 MemoryBase = mS3AcpiReservedMemoryBase;\r
626 MemorySize = mS3AcpiReservedMemorySize;\r
8e54500f 627 } else {\r
bc89fe48
LE
628 PeiMemoryCap = GetPeiMemoryCap ();\r
629 DEBUG ((EFI_D_INFO, "%a: mPhysMemAddressWidth=%d PeiMemoryCap=%u KB\n",\r
630 __FUNCTION__, mPhysMemAddressWidth, PeiMemoryCap >> 10));\r
631\r
8e54500f
JJ
632 //\r
633 // Determine the range of memory to use during PEI\r
634 //\r
efb0f16e
LE
635 // Technically we could lay the permanent PEI RAM over SEC's temporary\r
636 // decompression and scratch buffer even if "secure S3" is needed, since\r
637 // their lifetimes don't overlap. However, PeiFvInitialization() will cover\r
638 // RAM up to PcdOvmfDecompressionScratchEnd with an EfiACPIMemoryNVS memory\r
639 // allocation HOB, and other allocations served from the permanent PEI RAM\r
640 // shouldn't overlap with that HOB.\r
641 //\r
642 MemoryBase = mS3Supported && FeaturePcdGet (PcdSmmSmramRequire) ?\r
643 PcdGet32 (PcdOvmfDecompressionScratchEnd) :\r
644 PcdGet32 (PcdOvmfDxeMemFvBase) + PcdGet32 (PcdOvmfDxeMemFvSize);\r
8e54500f 645 MemorySize = LowerMemorySize - MemoryBase;\r
bc89fe48
LE
646 if (MemorySize > PeiMemoryCap) {\r
647 MemoryBase = LowerMemorySize - PeiMemoryCap;\r
648 MemorySize = PeiMemoryCap;\r
8e54500f 649 }\r
36658fff
WL
650 }\r
651\r
adec2bd5
LE
652 //\r
653 // MEMFD_BASE_ADDRESS separates the SMRAM at the default SMBASE from the\r
654 // normal boot permanent PEI RAM. Regarding the S3 boot path, the S3\r
655 // permanent PEI RAM is located even higher.\r
656 //\r
657 if (FeaturePcdGet (PcdSmmSmramRequire) && mQ35SmramAtDefaultSmbase) {\r
658 ASSERT (SMM_DEFAULT_SMBASE + MCH_DEFAULT_SMBASE_SIZE <= MemoryBase);\r
659 }\r
660\r
36658fff
WL
661 //\r
662 // Publish this memory to the PEI Core\r
663 //\r
664 Status = PublishSystemMemory(MemoryBase, MemorySize);\r
665 ASSERT_EFI_ERROR (Status);\r
666\r
667 return Status;\r
668}\r
669\r
c0e10976 670\r
84b223c1
LE
671STATIC\r
672VOID\r
673QemuInitializeRamBelow1gb (\r
674 VOID\r
675 )\r
676{\r
677 if (FeaturePcdGet (PcdSmmSmramRequire) && mQ35SmramAtDefaultSmbase) {\r
678 AddMemoryRangeHob (0, SMM_DEFAULT_SMBASE);\r
679 AddReservedMemoryBaseSizeHob (SMM_DEFAULT_SMBASE, MCH_DEFAULT_SMBASE_SIZE,\r
680 TRUE /* Cacheable */);\r
681 STATIC_ASSERT (\r
682 SMM_DEFAULT_SMBASE + MCH_DEFAULT_SMBASE_SIZE < BASE_512KB + BASE_128KB,\r
683 "end of SMRAM at default SMBASE ends at, or exceeds, 640KB"\r
684 );\r
685 AddMemoryRangeHob (SMM_DEFAULT_SMBASE + MCH_DEFAULT_SMBASE_SIZE,\r
686 BASE_512KB + BASE_128KB);\r
687 } else {\r
688 AddMemoryRangeHob (0, BASE_512KB + BASE_128KB);\r
689 }\r
690}\r
691\r
692\r
49ba9447 693/**\r
c034906e 694 Peform Memory Detection for QEMU / KVM\r
49ba9447 695\r
696**/\r
c034906e
JJ
697STATIC\r
698VOID\r
699QemuInitializeRam (\r
700 VOID\r
49ba9447 701 )\r
702{\r
c0e10976 703 UINT64 LowerMemorySize;\r
704 UINT64 UpperMemorySize;\r
79d274b8
LE
705 MTRR_SETTINGS MtrrSettings;\r
706 EFI_STATUS Status;\r
49ba9447 707\r
c034906e 708 DEBUG ((EFI_D_INFO, "%a called\n", __FUNCTION__));\r
49ba9447 709\r
710 //\r
711 // Determine total memory size available\r
712 //\r
c0e10976 713 LowerMemorySize = GetSystemMemorySizeBelow4gb ();\r
714 UpperMemorySize = GetSystemMemorySizeAbove4gb ();\r
49ba9447 715\r
e3e3090a
LE
716 if (mBootMode == BOOT_ON_S3_RESUME) {\r
717 //\r
718 // Create the following memory HOB as an exception on the S3 boot path.\r
719 //\r
720 // Normally we'd create memory HOBs only on the normal boot path. However,\r
721 // CpuMpPei specifically needs such a low-memory HOB on the S3 path as\r
722 // well, for "borrowing" a subset of it temporarily, for the AP startup\r
723 // vector.\r
724 //\r
725 // CpuMpPei saves the original contents of the borrowed area in permanent\r
726 // PEI RAM, in a backup buffer allocated with the normal PEI services.\r
727 // CpuMpPei restores the original contents ("returns" the borrowed area) at\r
728 // End-of-PEI. End-of-PEI in turn is emitted by S3Resume2Pei before\r
8c0b0b34 729 // transferring control to the OS's wakeup vector in the FACS.\r
e3e3090a
LE
730 //\r
731 // We expect any other PEIMs that "borrow" memory similarly to CpuMpPei to\r
732 // restore the original contents. Furthermore, we expect all such PEIMs\r
733 // (CpuMpPei included) to claim the borrowed areas by producing memory\r
734 // allocation HOBs, and to honor preexistent memory allocation HOBs when\r
735 // looking for an area to borrow.\r
736 //\r
84b223c1 737 QemuInitializeRamBelow1gb ();\r
e3e3090a 738 } else {\r
bd386eaf
JJ
739 //\r
740 // Create memory HOBs\r
741 //\r
84b223c1 742 QemuInitializeRamBelow1gb ();\r
b09c1c6f
LE
743\r
744 if (FeaturePcdGet (PcdSmmSmramRequire)) {\r
745 UINT32 TsegSize;\r
746\r
23bfb5c0 747 TsegSize = mQ35TsegMbytes * SIZE_1MB;\r
b09c1c6f
LE
748 AddMemoryRangeHob (BASE_1MB, LowerMemorySize - TsegSize);\r
749 AddReservedMemoryBaseSizeHob (LowerMemorySize - TsegSize, TsegSize,\r
750 TRUE);\r
751 } else {\r
752 AddMemoryRangeHob (BASE_1MB, LowerMemorySize);\r
753 }\r
754\r
1fceaddb
LE
755 //\r
756 // If QEMU presents an E820 map, then create memory HOBs for the >=4GB RAM\r
757 // entries. Otherwise, create a single memory HOB with the flat >=4GB\r
758 // memory size read from the CMOS.\r
759 //\r
760 Status = ScanOrAdd64BitE820Ram (NULL);\r
761 if (EFI_ERROR (Status) && UpperMemorySize != 0) {\r
035ce3b3 762 AddMemoryBaseSizeHob (BASE_4GB, UpperMemorySize);\r
cfc80e2e 763 }\r
bd386eaf 764 }\r
49ba9447 765\r
79d274b8
LE
766 //\r
767 // We'd like to keep the following ranges uncached:\r
768 // - [640 KB, 1 MB)\r
769 // - [LowerMemorySize, 4 GB)\r
770 //\r
771 // Everything else should be WB. Unfortunately, programming the inverse (ie.\r
772 // keeping the default UC, and configuring the complement set of the above as\r
773 // WB) is not reliable in general, because the end of the upper RAM can have\r
774 // practically any alignment, and we may not have enough variable MTRRs to\r
775 // cover it exactly.\r
776 //\r
777 if (IsMtrrSupported ()) {\r
778 MtrrGetAllMtrrs (&MtrrSettings);\r
779\r
780 //\r
781 // MTRRs disabled, fixed MTRRs disabled, default type is uncached\r
782 //\r
783 ASSERT ((MtrrSettings.MtrrDefType & BIT11) == 0);\r
784 ASSERT ((MtrrSettings.MtrrDefType & BIT10) == 0);\r
785 ASSERT ((MtrrSettings.MtrrDefType & 0xFF) == 0);\r
786\r
787 //\r
788 // flip default type to writeback\r
789 //\r
790 SetMem (&MtrrSettings.Fixed, sizeof MtrrSettings.Fixed, 0x06);\r
791 ZeroMem (&MtrrSettings.Variables, sizeof MtrrSettings.Variables);\r
792 MtrrSettings.MtrrDefType |= BIT11 | BIT10 | 6;\r
793 MtrrSetAllMtrrs (&MtrrSettings);\r
e8e5cd4a 794\r
79d274b8
LE
795 //\r
796 // Set memory range from 640KB to 1MB to uncacheable\r
797 //\r
798 Status = MtrrSetMemoryAttribute (BASE_512KB + BASE_128KB,\r
799 BASE_1MB - (BASE_512KB + BASE_128KB), CacheUncacheable);\r
800 ASSERT_EFI_ERROR (Status);\r
e8e5cd4a 801\r
79d274b8 802 //\r
49edde15
LE
803 // Set the memory range from the start of the 32-bit MMIO area (32-bit PCI\r
804 // MMIO aperture on i440fx, PCIEXBAR on q35) to 4GB as uncacheable.\r
79d274b8 805 //\r
49edde15
LE
806 Status = MtrrSetMemoryAttribute (mQemuUc32Base, SIZE_4GB - mQemuUc32Base,\r
807 CacheUncacheable);\r
79d274b8 808 ASSERT_EFI_ERROR (Status);\r
c0e10976 809 }\r
49ba9447 810}\r
811\r
c034906e
JJ
812/**\r
813 Publish system RAM and reserve memory regions\r
814\r
815**/\r
816VOID\r
817InitializeRamRegions (\r
818 VOID\r
819 )\r
820{\r
2818c158
JJ
821 if (!mXen) {\r
822 QemuInitializeRam ();\r
823 } else {\r
2818c158
JJ
824 XenPublishRamRegions ();\r
825 }\r
8e54500f
JJ
826\r
827 if (mS3Supported && mBootMode != BOOT_ON_S3_RESUME) {\r
828 //\r
829 // This is the memory range that will be used for PEI on S3 resume\r
830 //\r
831 BuildMemoryAllocationHob (\r
45d87081
LE
832 mS3AcpiReservedMemoryBase,\r
833 mS3AcpiReservedMemorySize,\r
8e54500f
JJ
834 EfiACPIMemoryNVS\r
835 );\r
e249f906
LE
836\r
837 //\r
838 // Cover the initial RAM area used as stack and temporary PEI heap.\r
839 //\r
840 // This is reserved as ACPI NVS so it can be used on S3 resume.\r
841 //\r
842 BuildMemoryAllocationHob (\r
843 PcdGet32 (PcdOvmfSecPeiTempRamBase),\r
844 PcdGet32 (PcdOvmfSecPeiTempRamSize),\r
845 EfiACPIMemoryNVS\r
846 );\r
78a38b73 847\r
ad43bc6b
LE
848 //\r
849 // SEC stores its table of GUIDed section handlers here.\r
850 //\r
851 BuildMemoryAllocationHob (\r
852 PcdGet64 (PcdGuidedExtractHandlerTableAddress),\r
853 PcdGet32 (PcdGuidedExtractHandlerTableSize),\r
854 EfiACPIMemoryNVS\r
855 );\r
856\r
78a38b73
LE
857#ifdef MDE_CPU_X64\r
858 //\r
859 // Reserve the initial page tables built by the reset vector code.\r
860 //\r
861 // Since this memory range will be used by the Reset Vector on S3\r
862 // resume, it must be reserved as ACPI NVS.\r
863 //\r
864 BuildMemoryAllocationHob (\r
865 (EFI_PHYSICAL_ADDRESS)(UINTN) PcdGet32 (PcdOvmfSecPageTablesBase),\r
866 (UINT64)(UINTN) PcdGet32 (PcdOvmfSecPageTablesSize),\r
867 EfiACPIMemoryNVS\r
868 );\r
869#endif\r
0e8a31f5 870 }\r
6a7cba79 871\r
0e8a31f5 872 if (mBootMode != BOOT_ON_S3_RESUME) {\r
1a7edbbc
LE
873 if (!FeaturePcdGet (PcdSmmSmramRequire)) {\r
874 //\r
875 // Reserve the lock box storage area\r
876 //\r
877 // Since this memory range will be used on S3 resume, it must be\r
878 // reserved as ACPI NVS.\r
879 //\r
880 // If S3 is unsupported, then various drivers might still write to the\r
881 // LockBox area. We ought to prevent DXE from serving allocation requests\r
882 // such that they would overlap the LockBox storage.\r
883 //\r
884 ZeroMem (\r
885 (VOID*)(UINTN) PcdGet32 (PcdOvmfLockBoxStorageBase),\r
886 (UINTN) PcdGet32 (PcdOvmfLockBoxStorageSize)\r
887 );\r
888 BuildMemoryAllocationHob (\r
889 (EFI_PHYSICAL_ADDRESS)(UINTN) PcdGet32 (PcdOvmfLockBoxStorageBase),\r
890 (UINT64)(UINTN) PcdGet32 (PcdOvmfLockBoxStorageSize),\r
891 mS3Supported ? EfiACPIMemoryNVS : EfiBootServicesData\r
892 );\r
893 }\r
b09c1c6f
LE
894\r
895 if (FeaturePcdGet (PcdSmmSmramRequire)) {\r
896 UINT32 TsegSize;\r
897\r
898 //\r
899 // Make sure the TSEG area that we reported as a reserved memory resource\r
900 // cannot be used for reserved memory allocations.\r
901 //\r
23bfb5c0 902 TsegSize = mQ35TsegMbytes * SIZE_1MB;\r
b09c1c6f
LE
903 BuildMemoryAllocationHob (\r
904 GetSystemMemorySizeBelow4gb() - TsegSize,\r
905 TsegSize,\r
906 EfiReservedMemoryType\r
907 );\r
84b223c1
LE
908 //\r
909 // Similarly, allocate away the (already reserved) SMRAM at the default\r
910 // SMBASE, if it exists.\r
911 //\r
912 if (mQ35SmramAtDefaultSmbase) {\r
913 BuildMemoryAllocationHob (\r
914 SMM_DEFAULT_SMBASE,\r
915 MCH_DEFAULT_SMBASE_SIZE,\r
916 EfiReservedMemoryType\r
917 );\r
918 }\r
b09c1c6f 919 }\r
8e54500f 920 }\r
c034906e 921}\r