]>
Commit | Line | Data |
---|---|---|
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 |
7 | Module 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 |
9afcd48a | 19 | #include <IndustryStandard/CloudHv.h>\r |
82bfd2e8 | 20 | #include <IndustryStandard/Xen/arch-x86/hvm/start_info.h>\r |
49ba9447 | 21 | #include <PiPei.h>\r |
adec2bd5 | 22 | #include <Register/Intel/SmramSaveStateMap.h>\r |
49ba9447 | 23 | \r |
24 | //\r | |
25 | // The Library classes this module consumes\r | |
26 | //\r | |
d5e06444 | 27 | #include <Library/BaseLib.h>\r |
6a7cba79 | 28 | #include <Library/BaseMemoryLib.h>\r |
49ba9447 | 29 | #include <Library/DebugLib.h>\r |
30 | #include <Library/HobLib.h>\r | |
31 | #include <Library/IoLib.h>\r | |
0bbed066 | 32 | #include <Library/MemEncryptSevLib.h>\r |
c1c2669c | 33 | #include <Library/PcdLib.h>\r |
d5e06444 | 34 | #include <Library/PciLib.h>\r |
49ba9447 | 35 | #include <Library/PeimEntryPoint.h>\r |
36 | #include <Library/ResourcePublicationLib.h>\r | |
10460942 | 37 | \r |
7e5b1b67 | 38 | #include <Library/QemuFwCfgLib.h>\r |
98800cce | 39 | #include <Library/QemuFwCfgSimpleParserLib.h>\r |
49ba9447 | 40 | #include "Platform.h"\r |
49ba9447 | 41 | \r |
23bfb5c0 LE |
42 | VOID\r |
43 | Q35TsegMbytesInitialization (\r | |
00743d14 | 44 | IN OUT EFI_HOB_PLATFORM_INFO *PlatformInfoHob\r |
23bfb5c0 LE |
45 | )\r |
46 | {\r | |
ac0a286f MK |
47 | UINT16 ExtendedTsegMbytes;\r |
48 | RETURN_STATUS PcdStatus;\r | |
d5e06444 | 49 | \r |
00743d14 | 50 | ASSERT (PlatformInfoHob->HostBridgeDevId == INTEL_Q35_MCH_DEVICE_ID);\r |
d5e06444 LE |
51 | \r |
52 | //\r | |
53 | // Check if QEMU offers an extended TSEG.\r | |
54 | //\r | |
55 | // This can be seen from writing MCH_EXT_TSEG_MB_QUERY to the MCH_EXT_TSEG_MB\r | |
56 | // register, and reading back the register.\r | |
57 | //\r | |
58 | // On a QEMU machine type that does not offer an extended TSEG, the initial\r | |
59 | // write overwrites whatever value a malicious guest OS may have placed in\r | |
60 | // the (unimplemented) register, before entering S3 or rebooting.\r | |
61 | // Subsequently, the read returns MCH_EXT_TSEG_MB_QUERY unchanged.\r | |
62 | //\r | |
63 | // On a QEMU machine type that offers an extended TSEG, the initial write\r | |
64 | // triggers an update to the register. Subsequently, the value read back\r | |
65 | // (which is guaranteed to differ from MCH_EXT_TSEG_MB_QUERY) tells us the\r | |
66 | // number of megabytes.\r | |
67 | //\r | |
68 | PciWrite16 (DRAMC_REGISTER_Q35 (MCH_EXT_TSEG_MB), MCH_EXT_TSEG_MB_QUERY);\r | |
69 | ExtendedTsegMbytes = PciRead16 (DRAMC_REGISTER_Q35 (MCH_EXT_TSEG_MB));\r | |
70 | if (ExtendedTsegMbytes == MCH_EXT_TSEG_MB_QUERY) {\r | |
00743d14 | 71 | PlatformInfoHob->Q35TsegMbytes = PcdGet16 (PcdQ35TsegMbytes);\r |
d5e06444 LE |
72 | return;\r |
73 | }\r | |
74 | \r | |
75 | DEBUG ((\r | |
76 | DEBUG_INFO,\r | |
77 | "%a: QEMU offers an extended TSEG (%d MB)\n",\r | |
78 | __FUNCTION__,\r | |
79 | ExtendedTsegMbytes\r | |
80 | ));\r | |
81 | PcdStatus = PcdSet16S (PcdQ35TsegMbytes, ExtendedTsegMbytes);\r | |
82 | ASSERT_RETURN_ERROR (PcdStatus);\r | |
00743d14 | 83 | PlatformInfoHob->Q35TsegMbytes = ExtendedTsegMbytes;\r |
23bfb5c0 LE |
84 | }\r |
85 | \r | |
73974f80 LE |
86 | VOID\r |
87 | Q35SmramAtDefaultSmbaseInitialization (\r | |
00743d14 | 88 | IN OUT EFI_HOB_PLATFORM_INFO *PlatformInfoHob\r |
73974f80 LE |
89 | )\r |
90 | {\r | |
ac0a286f | 91 | RETURN_STATUS PcdStatus;\r |
73974f80 | 92 | \r |
00743d14 | 93 | ASSERT (PlatformInfoHob->HostBridgeDevId == INTEL_Q35_MCH_DEVICE_ID);\r |
73974f80 | 94 | \r |
00743d14 | 95 | PlatformInfoHob->Q35SmramAtDefaultSmbase = FALSE;\r |
75839f97 | 96 | if (FeaturePcdGet (PcdCsmEnable)) {\r |
ac0a286f MK |
97 | DEBUG ((\r |
98 | DEBUG_INFO,\r | |
99 | "%a: SMRAM at default SMBASE not checked due to CSM\n",\r | |
100 | __FUNCTION__\r | |
101 | ));\r | |
75839f97 | 102 | } else {\r |
ac0a286f MK |
103 | UINTN CtlReg;\r |
104 | UINT8 CtlRegVal;\r | |
75839f97 LE |
105 | \r |
106 | CtlReg = DRAMC_REGISTER_Q35 (MCH_DEFAULT_SMBASE_CTL);\r | |
107 | PciWrite8 (CtlReg, MCH_DEFAULT_SMBASE_QUERY);\r | |
9a9b33b3 | 108 | CtlRegVal = PciRead8 (CtlReg);\r |
00743d14 | 109 | PlatformInfoHob->Q35SmramAtDefaultSmbase = (BOOLEAN)(CtlRegVal ==\r |
9a9b33b3 | 110 | MCH_DEFAULT_SMBASE_IN_RAM);\r |
ac0a286f MK |
111 | DEBUG ((\r |
112 | DEBUG_INFO,\r | |
113 | "%a: SMRAM at default SMBASE %a\n",\r | |
114 | __FUNCTION__,\r | |
00743d14 | 115 | PlatformInfoHob->Q35SmramAtDefaultSmbase ? "found" : "not found"\r |
ac0a286f | 116 | ));\r |
75839f97 LE |
117 | }\r |
118 | \r | |
ac0a286f MK |
119 | PcdStatus = PcdSetBoolS (\r |
120 | PcdQ35SmramAtDefaultSmbase,\r | |
00743d14 | 121 | PlatformInfoHob->Q35SmramAtDefaultSmbase\r |
ac0a286f | 122 | );\r |
73974f80 LE |
123 | ASSERT_RETURN_ERROR (PcdStatus);\r |
124 | }\r | |
125 | \r | |
3dd47f95 MX |
126 | /**\r |
127 | Initialize the PhysMemAddressWidth field in PlatformInfoHob based on guest RAM size.\r | |
128 | **/\r | |
129 | VOID\r | |
130 | AddressWidthInitialization (\r | |
131 | IN OUT EFI_HOB_PLATFORM_INFO *PlatformInfoHob\r | |
132 | )\r | |
133 | {\r | |
134 | RETURN_STATUS PcdStatus;\r | |
135 | \r | |
136 | PlatformAddressWidthInitialization (PlatformInfoHob);\r | |
137 | \r | |
138 | //\r | |
139 | // If DXE is 32-bit, then we're done; PciBusDxe will degrade 64-bit MMIO\r | |
140 | // resources to 32-bit anyway. See DegradeResource() in\r | |
141 | // "PciResourceSupport.c".\r | |
142 | //\r | |
143 | #ifdef MDE_CPU_IA32\r | |
144 | if (!FeaturePcdGet (PcdDxeIplSwitchToLongMode)) {\r | |
145 | return;\r | |
146 | }\r | |
147 | \r | |
148 | #endif\r | |
149 | \r | |
150 | if (PlatformInfoHob->PcdPciMmio64Size == 0) {\r | |
151 | if (PlatformInfoHob->BootMode != BOOT_ON_S3_RESUME) {\r | |
152 | DEBUG ((\r | |
153 | DEBUG_INFO,\r | |
154 | "%a: disabling 64-bit PCI host aperture\n",\r | |
155 | __FUNCTION__\r | |
156 | ));\r | |
157 | PcdStatus = PcdSet64S (PcdPciMmio64Size, 0);\r | |
158 | ASSERT_RETURN_ERROR (PcdStatus);\r | |
159 | }\r | |
160 | \r | |
161 | return;\r | |
162 | }\r | |
163 | \r | |
164 | if (PlatformInfoHob->BootMode != BOOT_ON_S3_RESUME) {\r | |
165 | //\r | |
166 | // The core PciHostBridgeDxe driver will automatically add this range to\r | |
167 | // the GCD memory space map through our PciHostBridgeLib instance; here we\r | |
168 | // only need to set the PCDs.\r | |
169 | //\r | |
170 | PcdStatus = PcdSet64S (PcdPciMmio64Base, PlatformInfoHob->PcdPciMmio64Base);\r | |
171 | ASSERT_RETURN_ERROR (PcdStatus);\r | |
172 | PcdStatus = PcdSet64S (PcdPciMmio64Size, PlatformInfoHob->PcdPciMmio64Size);\r | |
173 | ASSERT_RETURN_ERROR (PcdStatus);\r | |
174 | \r | |
175 | DEBUG ((\r | |
176 | DEBUG_INFO,\r | |
177 | "%a: Pci64Base=0x%Lx Pci64Size=0x%Lx\n",\r | |
178 | __FUNCTION__,\r | |
179 | PlatformInfoHob->PcdPciMmio64Base,\r | |
180 | PlatformInfoHob->PcdPciMmio64Size\r | |
181 | ));\r | |
182 | }\r | |
183 | }\r | |
184 | \r | |
bc89fe48 LE |
185 | /**\r |
186 | Calculate the cap for the permanent PEI memory.\r | |
187 | **/\r | |
188 | STATIC\r | |
189 | UINT32\r | |
190 | GetPeiMemoryCap (\r | |
27874a38 | 191 | IN EFI_HOB_PLATFORM_INFO *PlatformInfoHob\r |
bc89fe48 LE |
192 | )\r |
193 | {\r | |
ac0a286f MK |
194 | BOOLEAN Page1GSupport;\r |
195 | UINT32 RegEax;\r | |
196 | UINT32 RegEdx;\r | |
197 | UINT32 Pml4Entries;\r | |
198 | UINT32 PdpEntries;\r | |
199 | UINTN TotalPages;\r | |
bc89fe48 LE |
200 | \r |
201 | //\r | |
202 | // If DXE is 32-bit, then just return the traditional 64 MB cap.\r | |
203 | //\r | |
ac0a286f | 204 | #ifdef MDE_CPU_IA32\r |
bc89fe48 LE |
205 | if (!FeaturePcdGet (PcdDxeIplSwitchToLongMode)) {\r |
206 | return SIZE_64MB;\r | |
207 | }\r | |
ac0a286f MK |
208 | \r |
209 | #endif\r | |
bc89fe48 LE |
210 | \r |
211 | //\r | |
212 | // Dependent on physical address width, PEI memory allocations can be\r | |
213 | // dominated by the page tables built for 64-bit DXE. So we key the cap off\r | |
214 | // of those. The code below is based on CreateIdentityMappingPageTables() in\r | |
215 | // "MdeModulePkg/Core/DxeIplPeim/X64/VirtualMemory.c".\r | |
216 | //\r | |
217 | Page1GSupport = FALSE;\r | |
218 | if (PcdGetBool (PcdUse1GPageTable)) {\r | |
219 | AsmCpuid (0x80000000, &RegEax, NULL, NULL, NULL);\r | |
220 | if (RegEax >= 0x80000001) {\r | |
221 | AsmCpuid (0x80000001, NULL, NULL, NULL, &RegEdx);\r | |
222 | if ((RegEdx & BIT26) != 0) {\r | |
223 | Page1GSupport = TRUE;\r | |
224 | }\r | |
225 | }\r | |
226 | }\r | |
227 | \r | |
27874a38 | 228 | if (PlatformInfoHob->PhysMemAddressWidth <= 39) {\r |
bc89fe48 | 229 | Pml4Entries = 1;\r |
27874a38 | 230 | PdpEntries = 1 << (PlatformInfoHob->PhysMemAddressWidth - 30);\r |
bc89fe48 LE |
231 | ASSERT (PdpEntries <= 0x200);\r |
232 | } else {\r | |
27874a38 | 233 | if (PlatformInfoHob->PhysMemAddressWidth > 48) {\r |
cf17156d MX |
234 | Pml4Entries = 0x200;\r |
235 | } else {\r | |
27874a38 | 236 | Pml4Entries = 1 << (PlatformInfoHob->PhysMemAddressWidth - 39);\r |
cf17156d MX |
237 | }\r |
238 | \r | |
bc89fe48 LE |
239 | ASSERT (Pml4Entries <= 0x200);\r |
240 | PdpEntries = 512;\r | |
241 | }\r | |
242 | \r | |
243 | TotalPages = Page1GSupport ? Pml4Entries + 1 :\r | |
ac0a286f | 244 | (PdpEntries + 1) * Pml4Entries + 1;\r |
bc89fe48 LE |
245 | ASSERT (TotalPages <= 0x40201);\r |
246 | \r | |
247 | //\r | |
248 | // Add 64 MB for miscellaneous allocations. Note that for\r | |
3dd47f95 | 249 | // PhysMemAddressWidth values close to 36, the cap will actually be\r |
bc89fe48 LE |
250 | // dominated by this increment.\r |
251 | //\r | |
252 | return (UINT32)(EFI_PAGES_TO_SIZE (TotalPages) + SIZE_64MB);\r | |
253 | }\r | |
254 | \r | |
36658fff WL |
255 | /**\r |
256 | Publish PEI core memory\r | |
257 | \r | |
258 | @return EFI_SUCCESS The PEIM initialized successfully.\r | |
259 | \r | |
260 | **/\r | |
261 | EFI_STATUS\r | |
262 | PublishPeiMemory (\r | |
27874a38 | 263 | IN OUT EFI_HOB_PLATFORM_INFO *PlatformInfoHob\r |
36658fff WL |
264 | )\r |
265 | {\r | |
ac0a286f MK |
266 | EFI_STATUS Status;\r |
267 | EFI_PHYSICAL_ADDRESS MemoryBase;\r | |
268 | UINT64 MemorySize;\r | |
269 | UINT32 LowerMemorySize;\r | |
270 | UINT32 PeiMemoryCap;\r | |
9a9b33b3 MX |
271 | UINT32 S3AcpiReservedMemoryBase;\r |
272 | UINT32 S3AcpiReservedMemorySize;\r | |
36658fff | 273 | \r |
27874a38 GH |
274 | LowerMemorySize = PlatformGetSystemMemorySizeBelow4gb (PlatformInfoHob);\r |
275 | if (PlatformInfoHob->SmmSmramRequire) {\r | |
45d87081 LE |
276 | //\r |
277 | // TSEG is chipped from the end of low RAM\r | |
278 | //\r | |
27874a38 | 279 | LowerMemorySize -= PlatformInfoHob->Q35TsegMbytes * SIZE_1MB;\r |
45d87081 LE |
280 | }\r |
281 | \r | |
9a9b33b3 MX |
282 | S3AcpiReservedMemoryBase = 0;\r |
283 | S3AcpiReservedMemorySize = 0;\r | |
284 | \r | |
45d87081 LE |
285 | //\r |
286 | // If S3 is supported, then the S3 permanent PEI memory is placed next,\r | |
287 | // downwards. Its size is primarily dictated by CpuMpPei. The formula below\r | |
288 | // is an approximation.\r | |
289 | //\r | |
27874a38 | 290 | if (PlatformInfoHob->S3Supported) {\r |
9a9b33b3 | 291 | S3AcpiReservedMemorySize = SIZE_512KB +\r |
27874a38 | 292 | PlatformInfoHob->PcdCpuMaxLogicalProcessorNumber *\r |
9a9b33b3 MX |
293 | PcdGet32 (PcdCpuApStackSize);\r |
294 | S3AcpiReservedMemoryBase = LowerMemorySize - S3AcpiReservedMemorySize;\r | |
295 | LowerMemorySize = S3AcpiReservedMemoryBase;\r | |
45d87081 LE |
296 | }\r |
297 | \r | |
27874a38 GH |
298 | PlatformInfoHob->S3AcpiReservedMemoryBase = S3AcpiReservedMemoryBase;\r |
299 | PlatformInfoHob->S3AcpiReservedMemorySize = S3AcpiReservedMemorySize;\r | |
9a9b33b3 | 300 | \r |
27874a38 | 301 | if (PlatformInfoHob->BootMode == BOOT_ON_S3_RESUME) {\r |
9a9b33b3 MX |
302 | MemoryBase = S3AcpiReservedMemoryBase;\r |
303 | MemorySize = S3AcpiReservedMemorySize;\r | |
8e54500f | 304 | } else {\r |
27874a38 | 305 | PeiMemoryCap = GetPeiMemoryCap (PlatformInfoHob);\r |
ac0a286f MK |
306 | DEBUG ((\r |
307 | DEBUG_INFO,\r | |
3dd47f95 | 308 | "%a: PhysMemAddressWidth=%d PeiMemoryCap=%u KB\n",\r |
ac0a286f | 309 | __FUNCTION__,\r |
27874a38 | 310 | PlatformInfoHob->PhysMemAddressWidth,\r |
ac0a286f MK |
311 | PeiMemoryCap >> 10\r |
312 | ));\r | |
bc89fe48 | 313 | \r |
8e54500f JJ |
314 | //\r |
315 | // Determine the range of memory to use during PEI\r | |
316 | //\r | |
efb0f16e LE |
317 | // Technically we could lay the permanent PEI RAM over SEC's temporary\r |
318 | // decompression and scratch buffer even if "secure S3" is needed, since\r | |
319 | // their lifetimes don't overlap. However, PeiFvInitialization() will cover\r | |
320 | // RAM up to PcdOvmfDecompressionScratchEnd with an EfiACPIMemoryNVS memory\r | |
321 | // allocation HOB, and other allocations served from the permanent PEI RAM\r | |
322 | // shouldn't overlap with that HOB.\r | |
323 | //\r | |
27874a38 | 324 | MemoryBase = PlatformInfoHob->S3Supported && PlatformInfoHob->SmmSmramRequire ?\r |
ac0a286f MK |
325 | PcdGet32 (PcdOvmfDecompressionScratchEnd) :\r |
326 | PcdGet32 (PcdOvmfDxeMemFvBase) + PcdGet32 (PcdOvmfDxeMemFvSize);\r | |
8e54500f | 327 | MemorySize = LowerMemorySize - MemoryBase;\r |
bc89fe48 LE |
328 | if (MemorySize > PeiMemoryCap) {\r |
329 | MemoryBase = LowerMemorySize - PeiMemoryCap;\r | |
330 | MemorySize = PeiMemoryCap;\r | |
8e54500f | 331 | }\r |
36658fff WL |
332 | }\r |
333 | \r | |
adec2bd5 LE |
334 | //\r |
335 | // MEMFD_BASE_ADDRESS separates the SMRAM at the default SMBASE from the\r | |
336 | // normal boot permanent PEI RAM. Regarding the S3 boot path, the S3\r | |
337 | // permanent PEI RAM is located even higher.\r | |
338 | //\r | |
27874a38 | 339 | if (PlatformInfoHob->SmmSmramRequire && PlatformInfoHob->Q35SmramAtDefaultSmbase) {\r |
adec2bd5 LE |
340 | ASSERT (SMM_DEFAULT_SMBASE + MCH_DEFAULT_SMBASE_SIZE <= MemoryBase);\r |
341 | }\r | |
342 | \r | |
36658fff WL |
343 | //\r |
344 | // Publish this memory to the PEI Core\r | |
345 | //\r | |
ac0a286f | 346 | Status = PublishSystemMemory (MemoryBase, MemorySize);\r |
36658fff WL |
347 | ASSERT_EFI_ERROR (Status);\r |
348 | \r | |
349 | return Status;\r | |
350 | }\r | |
351 | \r | |
e5103262 MX |
352 | /**\r |
353 | Publish system RAM and reserve memory regions\r | |
354 | \r | |
355 | **/\r | |
356 | VOID\r | |
357 | InitializeRamRegions (\r | |
358 | IN EFI_HOB_PLATFORM_INFO *PlatformInfoHob\r | |
359 | )\r | |
360 | {\r | |
cf17156d MX |
361 | if (TdIsEnabled ()) {\r |
362 | PlatformTdxPublishRamRegions ();\r | |
363 | return;\r | |
364 | }\r | |
365 | \r | |
e5103262 MX |
366 | PlatformQemuInitializeRam (PlatformInfoHob);\r |
367 | \r | |
368 | SevInitializeRam ();\r | |
369 | \r | |
370 | PlatformQemuInitializeRamForS3 (PlatformInfoHob);\r | |
371 | }\r |