]> git.proxmox.com Git - mirror_edk2.git/blame - OvmfPkg/PlatformPei/MemDetect.c
OvmfPkg: PlatformPei: factor out GetFirstNonAddress()
[mirror_edk2.git] / OvmfPkg / PlatformPei / MemDetect.c
CommitLineData
49ba9447 1/**@file\r
2 Memory Detection for Virtual Machines.\r
3\r
4b455f7b 4 Copyright (c) 2006 - 2014, Intel Corporation. All rights reserved.<BR>\r
56d7640a 5 This program and the accompanying materials\r
49ba9447 6 are licensed and made available under the terms and conditions of the BSD License\r
7 which accompanies this distribution. The full text of the license may be found at\r
8 http://opensource.org/licenses/bsd-license.php\r
9\r
10 THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,\r
11 WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.\r
12\r
13Module Name:\r
14\r
15 MemDetect.c\r
16\r
17**/\r
18\r
19//\r
20// The package level header files this module uses\r
21//\r
22#include <PiPei.h>\r
23\r
24//\r
25// The Library classes this module consumes\r
26//\r
6a7cba79 27#include <Library/BaseMemoryLib.h>\r
49ba9447 28#include <Library/DebugLib.h>\r
29#include <Library/HobLib.h>\r
30#include <Library/IoLib.h>\r
c1c2669c 31#include <Library/PcdLib.h>\r
49ba9447 32#include <Library/PeimEntryPoint.h>\r
33#include <Library/ResourcePublicationLib.h>\r
e8e5cd4a 34#include <Library/MtrrLib.h>\r
49ba9447 35\r
36#include "Platform.h"\r
37#include "Cmos.h"\r
38\r
bc89fe48
LE
39UINT8 mPhysMemAddressWidth;\r
40\r
4b455f7b 41UINT32\r
c0e10976 42GetSystemMemorySizeBelow4gb (\r
4b455f7b 43 VOID\r
49ba9447 44 )\r
45{\r
46 UINT8 Cmos0x34;\r
47 UINT8 Cmos0x35;\r
48\r
49 //\r
50 // CMOS 0x34/0x35 specifies the system memory above 16 MB.\r
51 // * CMOS(0x35) is the high byte\r
52 // * CMOS(0x34) is the low byte\r
53 // * The size is specified in 64kb chunks\r
54 // * Since this is memory above 16MB, the 16MB must be added\r
55 // into the calculation to get the total memory size.\r
56 //\r
57\r
58 Cmos0x34 = (UINT8) CmosRead8 (0x34);\r
59 Cmos0x35 = (UINT8) CmosRead8 (0x35);\r
60\r
c4046161 61 return (UINT32) (((UINTN)((Cmos0x35 << 8) + Cmos0x34) << 16) + SIZE_16MB);\r
49ba9447 62}\r
63\r
64\r
c0e10976 65STATIC\r
66UINT64\r
67GetSystemMemorySizeAbove4gb (\r
68 )\r
69{\r
70 UINT32 Size;\r
71 UINTN CmosIndex;\r
72\r
73 //\r
74 // CMOS 0x5b-0x5d specifies the system memory above 4GB MB.\r
75 // * CMOS(0x5d) is the most significant size byte\r
76 // * CMOS(0x5c) is the middle size byte\r
77 // * CMOS(0x5b) is the least significant size byte\r
78 // * The size is specified in 64kb chunks\r
79 //\r
80\r
81 Size = 0;\r
82 for (CmosIndex = 0x5d; CmosIndex >= 0x5b; CmosIndex--) {\r
83 Size = (UINT32) (Size << 8) + (UINT32) CmosRead8 (CmosIndex);\r
84 }\r
85\r
86 return LShiftU64 (Size, 16);\r
87}\r
88\r
bc89fe48 89\r
d5371680
LE
90/**\r
91 Return the highest address that DXE could possibly use, plus one.\r
92**/\r
93STATIC\r
94UINT64\r
95GetFirstNonAddress (\r
96 VOID\r
97 )\r
98{\r
99 UINT64 FirstNonAddress;\r
100\r
101 FirstNonAddress = BASE_4GB + GetSystemMemorySizeAbove4gb ();\r
102 return FirstNonAddress;\r
103}\r
104\r
105\r
bc89fe48
LE
106/**\r
107 Initialize the mPhysMemAddressWidth variable, based on guest RAM size.\r
108**/\r
109VOID\r
110AddressWidthInitialization (\r
111 VOID\r
112 )\r
113{\r
114 UINT64 FirstNonAddress;\r
115\r
116 //\r
117 // As guest-physical memory size grows, the permanent PEI RAM requirements\r
118 // are dominated by the identity-mapping page tables built by the DXE IPL.\r
119 // The DXL IPL keys off of the physical address bits advertized in the CPU\r
120 // HOB. To conserve memory, we calculate the minimum address width here.\r
121 //\r
d5371680 122 FirstNonAddress = GetFirstNonAddress ();\r
bc89fe48
LE
123 mPhysMemAddressWidth = (UINT8)HighBitSet64 (FirstNonAddress);\r
124\r
125 //\r
126 // If FirstNonAddress is not an integral power of two, then we need an\r
127 // additional bit.\r
128 //\r
129 if ((FirstNonAddress & (FirstNonAddress - 1)) != 0) {\r
130 ++mPhysMemAddressWidth;\r
131 }\r
132\r
133 //\r
134 // The minimum address width is 36 (covers up to and excluding 64 GB, which\r
135 // is the maximum for Ia32 + PAE). The theoretical architecture maximum for\r
136 // X64 long mode is 52 bits, but the DXE IPL clamps that down to 48 bits. We\r
137 // can simply assert that here, since 48 bits are good enough for 256 TB.\r
138 //\r
139 if (mPhysMemAddressWidth <= 36) {\r
140 mPhysMemAddressWidth = 36;\r
141 }\r
142 ASSERT (mPhysMemAddressWidth <= 48);\r
143}\r
144\r
145\r
146/**\r
147 Calculate the cap for the permanent PEI memory.\r
148**/\r
149STATIC\r
150UINT32\r
151GetPeiMemoryCap (\r
152 VOID\r
153 )\r
154{\r
155 BOOLEAN Page1GSupport;\r
156 UINT32 RegEax;\r
157 UINT32 RegEdx;\r
158 UINT32 Pml4Entries;\r
159 UINT32 PdpEntries;\r
160 UINTN TotalPages;\r
161\r
162 //\r
163 // If DXE is 32-bit, then just return the traditional 64 MB cap.\r
164 //\r
165#ifdef MDE_CPU_IA32\r
166 if (!FeaturePcdGet (PcdDxeIplSwitchToLongMode)) {\r
167 return SIZE_64MB;\r
168 }\r
169#endif\r
170\r
171 //\r
172 // Dependent on physical address width, PEI memory allocations can be\r
173 // dominated by the page tables built for 64-bit DXE. So we key the cap off\r
174 // of those. The code below is based on CreateIdentityMappingPageTables() in\r
175 // "MdeModulePkg/Core/DxeIplPeim/X64/VirtualMemory.c".\r
176 //\r
177 Page1GSupport = FALSE;\r
178 if (PcdGetBool (PcdUse1GPageTable)) {\r
179 AsmCpuid (0x80000000, &RegEax, NULL, NULL, NULL);\r
180 if (RegEax >= 0x80000001) {\r
181 AsmCpuid (0x80000001, NULL, NULL, NULL, &RegEdx);\r
182 if ((RegEdx & BIT26) != 0) {\r
183 Page1GSupport = TRUE;\r
184 }\r
185 }\r
186 }\r
187\r
188 if (mPhysMemAddressWidth <= 39) {\r
189 Pml4Entries = 1;\r
190 PdpEntries = 1 << (mPhysMemAddressWidth - 30);\r
191 ASSERT (PdpEntries <= 0x200);\r
192 } else {\r
193 Pml4Entries = 1 << (mPhysMemAddressWidth - 39);\r
194 ASSERT (Pml4Entries <= 0x200);\r
195 PdpEntries = 512;\r
196 }\r
197\r
198 TotalPages = Page1GSupport ? Pml4Entries + 1 :\r
199 (PdpEntries + 1) * Pml4Entries + 1;\r
200 ASSERT (TotalPages <= 0x40201);\r
201\r
202 //\r
203 // Add 64 MB for miscellaneous allocations. Note that for\r
204 // mPhysMemAddressWidth values close to 36, the cap will actually be\r
205 // dominated by this increment.\r
206 //\r
207 return (UINT32)(EFI_PAGES_TO_SIZE (TotalPages) + SIZE_64MB);\r
208}\r
209\r
210\r
36658fff
WL
211/**\r
212 Publish PEI core memory\r
213\r
214 @return EFI_SUCCESS The PEIM initialized successfully.\r
215\r
216**/\r
217EFI_STATUS\r
218PublishPeiMemory (\r
219 VOID\r
220 )\r
221{\r
222 EFI_STATUS Status;\r
223 EFI_PHYSICAL_ADDRESS MemoryBase;\r
224 UINT64 MemorySize;\r
225 UINT64 LowerMemorySize;\r
bc89fe48 226 UINT32 PeiMemoryCap;\r
36658fff 227\r
8e54500f
JJ
228 if (mBootMode == BOOT_ON_S3_RESUME) {\r
229 MemoryBase = PcdGet32 (PcdS3AcpiReservedMemoryBase);\r
230 MemorySize = PcdGet32 (PcdS3AcpiReservedMemorySize);\r
231 } else {\r
232 LowerMemorySize = GetSystemMemorySizeBelow4gb ();\r
b09c1c6f
LE
233 if (FeaturePcdGet (PcdSmmSmramRequire)) {\r
234 //\r
235 // TSEG is chipped from the end of low RAM\r
236 //\r
237 LowerMemorySize -= FixedPcdGet8 (PcdQ35TsegMbytes) * SIZE_1MB;\r
238 }\r
8e54500f 239\r
bc89fe48
LE
240 PeiMemoryCap = GetPeiMemoryCap ();\r
241 DEBUG ((EFI_D_INFO, "%a: mPhysMemAddressWidth=%d PeiMemoryCap=%u KB\n",\r
242 __FUNCTION__, mPhysMemAddressWidth, PeiMemoryCap >> 10));\r
243\r
8e54500f
JJ
244 //\r
245 // Determine the range of memory to use during PEI\r
246 //\r
efb0f16e
LE
247 // Technically we could lay the permanent PEI RAM over SEC's temporary\r
248 // decompression and scratch buffer even if "secure S3" is needed, since\r
249 // their lifetimes don't overlap. However, PeiFvInitialization() will cover\r
250 // RAM up to PcdOvmfDecompressionScratchEnd with an EfiACPIMemoryNVS memory\r
251 // allocation HOB, and other allocations served from the permanent PEI RAM\r
252 // shouldn't overlap with that HOB.\r
253 //\r
254 MemoryBase = mS3Supported && FeaturePcdGet (PcdSmmSmramRequire) ?\r
255 PcdGet32 (PcdOvmfDecompressionScratchEnd) :\r
256 PcdGet32 (PcdOvmfDxeMemFvBase) + PcdGet32 (PcdOvmfDxeMemFvSize);\r
8e54500f 257 MemorySize = LowerMemorySize - MemoryBase;\r
bc89fe48
LE
258 if (MemorySize > PeiMemoryCap) {\r
259 MemoryBase = LowerMemorySize - PeiMemoryCap;\r
260 MemorySize = PeiMemoryCap;\r
8e54500f 261 }\r
36658fff
WL
262 }\r
263\r
264 //\r
265 // Publish this memory to the PEI Core\r
266 //\r
267 Status = PublishSystemMemory(MemoryBase, MemorySize);\r
268 ASSERT_EFI_ERROR (Status);\r
269\r
270 return Status;\r
271}\r
272\r
c0e10976 273\r
49ba9447 274/**\r
c034906e 275 Peform Memory Detection for QEMU / KVM\r
49ba9447 276\r
277**/\r
c034906e
JJ
278STATIC\r
279VOID\r
280QemuInitializeRam (\r
281 VOID\r
49ba9447 282 )\r
283{\r
c0e10976 284 UINT64 LowerMemorySize;\r
285 UINT64 UpperMemorySize;\r
79d274b8
LE
286 MTRR_SETTINGS MtrrSettings;\r
287 EFI_STATUS Status;\r
49ba9447 288\r
c034906e 289 DEBUG ((EFI_D_INFO, "%a called\n", __FUNCTION__));\r
49ba9447 290\r
291 //\r
292 // Determine total memory size available\r
293 //\r
c0e10976 294 LowerMemorySize = GetSystemMemorySizeBelow4gb ();\r
295 UpperMemorySize = GetSystemMemorySizeAbove4gb ();\r
49ba9447 296\r
bd386eaf
JJ
297 if (mBootMode != BOOT_ON_S3_RESUME) {\r
298 //\r
299 // Create memory HOBs\r
300 //\r
bd386eaf 301 AddMemoryRangeHob (0, BASE_512KB + BASE_128KB);\r
b09c1c6f
LE
302\r
303 if (FeaturePcdGet (PcdSmmSmramRequire)) {\r
304 UINT32 TsegSize;\r
305\r
306 TsegSize = FixedPcdGet8 (PcdQ35TsegMbytes) * SIZE_1MB;\r
307 AddMemoryRangeHob (BASE_1MB, LowerMemorySize - TsegSize);\r
308 AddReservedMemoryBaseSizeHob (LowerMemorySize - TsegSize, TsegSize,\r
309 TRUE);\r
310 } else {\r
311 AddMemoryRangeHob (BASE_1MB, LowerMemorySize);\r
312 }\r
313\r
cfc80e2e
LE
314 if (UpperMemorySize != 0) {\r
315 AddUntestedMemoryBaseSizeHob (BASE_4GB, UpperMemorySize);\r
316 }\r
bd386eaf 317 }\r
49ba9447 318\r
79d274b8
LE
319 //\r
320 // We'd like to keep the following ranges uncached:\r
321 // - [640 KB, 1 MB)\r
322 // - [LowerMemorySize, 4 GB)\r
323 //\r
324 // Everything else should be WB. Unfortunately, programming the inverse (ie.\r
325 // keeping the default UC, and configuring the complement set of the above as\r
326 // WB) is not reliable in general, because the end of the upper RAM can have\r
327 // practically any alignment, and we may not have enough variable MTRRs to\r
328 // cover it exactly.\r
329 //\r
330 if (IsMtrrSupported ()) {\r
331 MtrrGetAllMtrrs (&MtrrSettings);\r
332\r
333 //\r
334 // MTRRs disabled, fixed MTRRs disabled, default type is uncached\r
335 //\r
336 ASSERT ((MtrrSettings.MtrrDefType & BIT11) == 0);\r
337 ASSERT ((MtrrSettings.MtrrDefType & BIT10) == 0);\r
338 ASSERT ((MtrrSettings.MtrrDefType & 0xFF) == 0);\r
339\r
340 //\r
341 // flip default type to writeback\r
342 //\r
343 SetMem (&MtrrSettings.Fixed, sizeof MtrrSettings.Fixed, 0x06);\r
344 ZeroMem (&MtrrSettings.Variables, sizeof MtrrSettings.Variables);\r
345 MtrrSettings.MtrrDefType |= BIT11 | BIT10 | 6;\r
346 MtrrSetAllMtrrs (&MtrrSettings);\r
e8e5cd4a 347\r
79d274b8
LE
348 //\r
349 // Set memory range from 640KB to 1MB to uncacheable\r
350 //\r
351 Status = MtrrSetMemoryAttribute (BASE_512KB + BASE_128KB,\r
352 BASE_1MB - (BASE_512KB + BASE_128KB), CacheUncacheable);\r
353 ASSERT_EFI_ERROR (Status);\r
e8e5cd4a 354\r
79d274b8
LE
355 //\r
356 // Set memory range from the "top of lower RAM" (RAM below 4GB) to 4GB as\r
357 // uncacheable\r
358 //\r
359 Status = MtrrSetMemoryAttribute (LowerMemorySize,\r
360 SIZE_4GB - LowerMemorySize, CacheUncacheable);\r
361 ASSERT_EFI_ERROR (Status);\r
c0e10976 362 }\r
49ba9447 363}\r
364\r
c034906e
JJ
365/**\r
366 Publish system RAM and reserve memory regions\r
367\r
368**/\r
369VOID\r
370InitializeRamRegions (\r
371 VOID\r
372 )\r
373{\r
2818c158
JJ
374 if (!mXen) {\r
375 QemuInitializeRam ();\r
376 } else {\r
2818c158
JJ
377 XenPublishRamRegions ();\r
378 }\r
8e54500f
JJ
379\r
380 if (mS3Supported && mBootMode != BOOT_ON_S3_RESUME) {\r
381 //\r
382 // This is the memory range that will be used for PEI on S3 resume\r
383 //\r
384 BuildMemoryAllocationHob (\r
385 (EFI_PHYSICAL_ADDRESS)(UINTN) PcdGet32 (PcdS3AcpiReservedMemoryBase),\r
386 (UINT64)(UINTN) PcdGet32 (PcdS3AcpiReservedMemorySize),\r
387 EfiACPIMemoryNVS\r
388 );\r
e249f906
LE
389\r
390 //\r
391 // Cover the initial RAM area used as stack and temporary PEI heap.\r
392 //\r
393 // This is reserved as ACPI NVS so it can be used on S3 resume.\r
394 //\r
395 BuildMemoryAllocationHob (\r
396 PcdGet32 (PcdOvmfSecPeiTempRamBase),\r
397 PcdGet32 (PcdOvmfSecPeiTempRamSize),\r
398 EfiACPIMemoryNVS\r
399 );\r
78a38b73 400\r
ad43bc6b
LE
401 //\r
402 // SEC stores its table of GUIDed section handlers here.\r
403 //\r
404 BuildMemoryAllocationHob (\r
405 PcdGet64 (PcdGuidedExtractHandlerTableAddress),\r
406 PcdGet32 (PcdGuidedExtractHandlerTableSize),\r
407 EfiACPIMemoryNVS\r
408 );\r
409\r
78a38b73
LE
410#ifdef MDE_CPU_X64\r
411 //\r
412 // Reserve the initial page tables built by the reset vector code.\r
413 //\r
414 // Since this memory range will be used by the Reset Vector on S3\r
415 // resume, it must be reserved as ACPI NVS.\r
416 //\r
417 BuildMemoryAllocationHob (\r
418 (EFI_PHYSICAL_ADDRESS)(UINTN) PcdGet32 (PcdOvmfSecPageTablesBase),\r
419 (UINT64)(UINTN) PcdGet32 (PcdOvmfSecPageTablesSize),\r
420 EfiACPIMemoryNVS\r
421 );\r
422#endif\r
0e8a31f5 423 }\r
6a7cba79 424\r
0e8a31f5 425 if (mBootMode != BOOT_ON_S3_RESUME) {\r
1a7edbbc
LE
426 if (!FeaturePcdGet (PcdSmmSmramRequire)) {\r
427 //\r
428 // Reserve the lock box storage area\r
429 //\r
430 // Since this memory range will be used on S3 resume, it must be\r
431 // reserved as ACPI NVS.\r
432 //\r
433 // If S3 is unsupported, then various drivers might still write to the\r
434 // LockBox area. We ought to prevent DXE from serving allocation requests\r
435 // such that they would overlap the LockBox storage.\r
436 //\r
437 ZeroMem (\r
438 (VOID*)(UINTN) PcdGet32 (PcdOvmfLockBoxStorageBase),\r
439 (UINTN) PcdGet32 (PcdOvmfLockBoxStorageSize)\r
440 );\r
441 BuildMemoryAllocationHob (\r
442 (EFI_PHYSICAL_ADDRESS)(UINTN) PcdGet32 (PcdOvmfLockBoxStorageBase),\r
443 (UINT64)(UINTN) PcdGet32 (PcdOvmfLockBoxStorageSize),\r
444 mS3Supported ? EfiACPIMemoryNVS : EfiBootServicesData\r
445 );\r
446 }\r
b09c1c6f
LE
447\r
448 if (FeaturePcdGet (PcdSmmSmramRequire)) {\r
449 UINT32 TsegSize;\r
450\r
451 //\r
452 // Make sure the TSEG area that we reported as a reserved memory resource\r
453 // cannot be used for reserved memory allocations.\r
454 //\r
455 TsegSize = FixedPcdGet8 (PcdQ35TsegMbytes) * SIZE_1MB;\r
456 BuildMemoryAllocationHob (\r
457 GetSystemMemorySizeBelow4gb() - TsegSize,\r
458 TsegSize,\r
459 EfiReservedMemoryType\r
460 );\r
461 }\r
8e54500f 462 }\r
c034906e 463}\r