]> git.proxmox.com Git - mirror_edk2.git/blame - OvmfPkg/PlatformPei/MemDetect.c
OvmfPkg: PlatformPei: create the CPU HOB with dynamic memory space width
[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
LE
89\r
90/**\r
91 Initialize the mPhysMemAddressWidth variable, based on guest RAM size.\r
92**/\r
93VOID\r
94AddressWidthInitialization (\r
95 VOID\r
96 )\r
97{\r
98 UINT64 FirstNonAddress;\r
99\r
100 //\r
101 // As guest-physical memory size grows, the permanent PEI RAM requirements\r
102 // are dominated by the identity-mapping page tables built by the DXE IPL.\r
103 // The DXL IPL keys off of the physical address bits advertized in the CPU\r
104 // HOB. To conserve memory, we calculate the minimum address width here.\r
105 //\r
106 FirstNonAddress = BASE_4GB + GetSystemMemorySizeAbove4gb ();\r
107 mPhysMemAddressWidth = (UINT8)HighBitSet64 (FirstNonAddress);\r
108\r
109 //\r
110 // If FirstNonAddress is not an integral power of two, then we need an\r
111 // additional bit.\r
112 //\r
113 if ((FirstNonAddress & (FirstNonAddress - 1)) != 0) {\r
114 ++mPhysMemAddressWidth;\r
115 }\r
116\r
117 //\r
118 // The minimum address width is 36 (covers up to and excluding 64 GB, which\r
119 // is the maximum for Ia32 + PAE). The theoretical architecture maximum for\r
120 // X64 long mode is 52 bits, but the DXE IPL clamps that down to 48 bits. We\r
121 // can simply assert that here, since 48 bits are good enough for 256 TB.\r
122 //\r
123 if (mPhysMemAddressWidth <= 36) {\r
124 mPhysMemAddressWidth = 36;\r
125 }\r
126 ASSERT (mPhysMemAddressWidth <= 48);\r
127}\r
128\r
129\r
130/**\r
131 Calculate the cap for the permanent PEI memory.\r
132**/\r
133STATIC\r
134UINT32\r
135GetPeiMemoryCap (\r
136 VOID\r
137 )\r
138{\r
139 BOOLEAN Page1GSupport;\r
140 UINT32 RegEax;\r
141 UINT32 RegEdx;\r
142 UINT32 Pml4Entries;\r
143 UINT32 PdpEntries;\r
144 UINTN TotalPages;\r
145\r
146 //\r
147 // If DXE is 32-bit, then just return the traditional 64 MB cap.\r
148 //\r
149#ifdef MDE_CPU_IA32\r
150 if (!FeaturePcdGet (PcdDxeIplSwitchToLongMode)) {\r
151 return SIZE_64MB;\r
152 }\r
153#endif\r
154\r
155 //\r
156 // Dependent on physical address width, PEI memory allocations can be\r
157 // dominated by the page tables built for 64-bit DXE. So we key the cap off\r
158 // of those. The code below is based on CreateIdentityMappingPageTables() in\r
159 // "MdeModulePkg/Core/DxeIplPeim/X64/VirtualMemory.c".\r
160 //\r
161 Page1GSupport = FALSE;\r
162 if (PcdGetBool (PcdUse1GPageTable)) {\r
163 AsmCpuid (0x80000000, &RegEax, NULL, NULL, NULL);\r
164 if (RegEax >= 0x80000001) {\r
165 AsmCpuid (0x80000001, NULL, NULL, NULL, &RegEdx);\r
166 if ((RegEdx & BIT26) != 0) {\r
167 Page1GSupport = TRUE;\r
168 }\r
169 }\r
170 }\r
171\r
172 if (mPhysMemAddressWidth <= 39) {\r
173 Pml4Entries = 1;\r
174 PdpEntries = 1 << (mPhysMemAddressWidth - 30);\r
175 ASSERT (PdpEntries <= 0x200);\r
176 } else {\r
177 Pml4Entries = 1 << (mPhysMemAddressWidth - 39);\r
178 ASSERT (Pml4Entries <= 0x200);\r
179 PdpEntries = 512;\r
180 }\r
181\r
182 TotalPages = Page1GSupport ? Pml4Entries + 1 :\r
183 (PdpEntries + 1) * Pml4Entries + 1;\r
184 ASSERT (TotalPages <= 0x40201);\r
185\r
186 //\r
187 // Add 64 MB for miscellaneous allocations. Note that for\r
188 // mPhysMemAddressWidth values close to 36, the cap will actually be\r
189 // dominated by this increment.\r
190 //\r
191 return (UINT32)(EFI_PAGES_TO_SIZE (TotalPages) + SIZE_64MB);\r
192}\r
193\r
194\r
36658fff
WL
195/**\r
196 Publish PEI core memory\r
197\r
198 @return EFI_SUCCESS The PEIM initialized successfully.\r
199\r
200**/\r
201EFI_STATUS\r
202PublishPeiMemory (\r
203 VOID\r
204 )\r
205{\r
206 EFI_STATUS Status;\r
207 EFI_PHYSICAL_ADDRESS MemoryBase;\r
208 UINT64 MemorySize;\r
209 UINT64 LowerMemorySize;\r
bc89fe48 210 UINT32 PeiMemoryCap;\r
36658fff 211\r
8e54500f
JJ
212 if (mBootMode == BOOT_ON_S3_RESUME) {\r
213 MemoryBase = PcdGet32 (PcdS3AcpiReservedMemoryBase);\r
214 MemorySize = PcdGet32 (PcdS3AcpiReservedMemorySize);\r
215 } else {\r
216 LowerMemorySize = GetSystemMemorySizeBelow4gb ();\r
217\r
bc89fe48
LE
218 PeiMemoryCap = GetPeiMemoryCap ();\r
219 DEBUG ((EFI_D_INFO, "%a: mPhysMemAddressWidth=%d PeiMemoryCap=%u KB\n",\r
220 __FUNCTION__, mPhysMemAddressWidth, PeiMemoryCap >> 10));\r
221\r
8e54500f
JJ
222 //\r
223 // Determine the range of memory to use during PEI\r
224 //\r
225 MemoryBase = PcdGet32 (PcdOvmfDxeMemFvBase) + PcdGet32 (PcdOvmfDxeMemFvSize);\r
226 MemorySize = LowerMemorySize - MemoryBase;\r
bc89fe48
LE
227 if (MemorySize > PeiMemoryCap) {\r
228 MemoryBase = LowerMemorySize - PeiMemoryCap;\r
229 MemorySize = PeiMemoryCap;\r
8e54500f 230 }\r
36658fff
WL
231 }\r
232\r
233 //\r
234 // Publish this memory to the PEI Core\r
235 //\r
236 Status = PublishSystemMemory(MemoryBase, MemorySize);\r
237 ASSERT_EFI_ERROR (Status);\r
238\r
239 return Status;\r
240}\r
241\r
c0e10976 242\r
49ba9447 243/**\r
c034906e 244 Peform Memory Detection for QEMU / KVM\r
49ba9447 245\r
246**/\r
c034906e
JJ
247STATIC\r
248VOID\r
249QemuInitializeRam (\r
250 VOID\r
49ba9447 251 )\r
252{\r
c0e10976 253 UINT64 LowerMemorySize;\r
254 UINT64 UpperMemorySize;\r
49ba9447 255\r
c034906e 256 DEBUG ((EFI_D_INFO, "%a called\n", __FUNCTION__));\r
49ba9447 257\r
258 //\r
259 // Determine total memory size available\r
260 //\r
c0e10976 261 LowerMemorySize = GetSystemMemorySizeBelow4gb ();\r
262 UpperMemorySize = GetSystemMemorySizeAbove4gb ();\r
49ba9447 263\r
bd386eaf
JJ
264 if (mBootMode != BOOT_ON_S3_RESUME) {\r
265 //\r
266 // Create memory HOBs\r
267 //\r
268 AddMemoryRangeHob (BASE_1MB, LowerMemorySize);\r
269 AddMemoryRangeHob (0, BASE_512KB + BASE_128KB);\r
270 }\r
49ba9447 271\r
9ab36385 272 MtrrSetMemoryAttribute (BASE_1MB, LowerMemorySize - BASE_1MB, CacheWriteBack);\r
e8e5cd4a 273\r
27d7e63f 274 MtrrSetMemoryAttribute (0, BASE_512KB + BASE_128KB, CacheWriteBack);\r
e8e5cd4a 275\r
c0e10976 276 if (UpperMemorySize != 0) {\r
bd386eaf
JJ
277 if (mBootMode != BOOT_ON_S3_RESUME) {\r
278 AddUntestedMemoryBaseSizeHob (BASE_4GB, UpperMemorySize);\r
279 }\r
e8e5cd4a 280\r
27d7e63f 281 MtrrSetMemoryAttribute (BASE_4GB, UpperMemorySize, CacheWriteBack);\r
c0e10976 282 }\r
49ba9447 283}\r
284\r
c034906e
JJ
285/**\r
286 Publish system RAM and reserve memory regions\r
287\r
288**/\r
289VOID\r
290InitializeRamRegions (\r
291 VOID\r
292 )\r
293{\r
2818c158
JJ
294 if (!mXen) {\r
295 QemuInitializeRam ();\r
296 } else {\r
2818c158
JJ
297 XenPublishRamRegions ();\r
298 }\r
8e54500f
JJ
299\r
300 if (mS3Supported && mBootMode != BOOT_ON_S3_RESUME) {\r
301 //\r
302 // This is the memory range that will be used for PEI on S3 resume\r
303 //\r
304 BuildMemoryAllocationHob (\r
305 (EFI_PHYSICAL_ADDRESS)(UINTN) PcdGet32 (PcdS3AcpiReservedMemoryBase),\r
306 (UINT64)(UINTN) PcdGet32 (PcdS3AcpiReservedMemorySize),\r
307 EfiACPIMemoryNVS\r
308 );\r
e249f906
LE
309\r
310 //\r
311 // Cover the initial RAM area used as stack and temporary PEI heap.\r
312 //\r
313 // This is reserved as ACPI NVS so it can be used on S3 resume.\r
314 //\r
315 BuildMemoryAllocationHob (\r
316 PcdGet32 (PcdOvmfSecPeiTempRamBase),\r
317 PcdGet32 (PcdOvmfSecPeiTempRamSize),\r
318 EfiACPIMemoryNVS\r
319 );\r
78a38b73 320\r
ad43bc6b
LE
321 //\r
322 // SEC stores its table of GUIDed section handlers here.\r
323 //\r
324 BuildMemoryAllocationHob (\r
325 PcdGet64 (PcdGuidedExtractHandlerTableAddress),\r
326 PcdGet32 (PcdGuidedExtractHandlerTableSize),\r
327 EfiACPIMemoryNVS\r
328 );\r
329\r
78a38b73
LE
330#ifdef MDE_CPU_X64\r
331 //\r
332 // Reserve the initial page tables built by the reset vector code.\r
333 //\r
334 // Since this memory range will be used by the Reset Vector on S3\r
335 // resume, it must be reserved as ACPI NVS.\r
336 //\r
337 BuildMemoryAllocationHob (\r
338 (EFI_PHYSICAL_ADDRESS)(UINTN) PcdGet32 (PcdOvmfSecPageTablesBase),\r
339 (UINT64)(UINTN) PcdGet32 (PcdOvmfSecPageTablesSize),\r
340 EfiACPIMemoryNVS\r
341 );\r
342#endif\r
0e8a31f5 343 }\r
6a7cba79 344\r
0e8a31f5 345 if (mBootMode != BOOT_ON_S3_RESUME) {\r
6a7cba79
LE
346 //\r
347 // Reserve the lock box storage area\r
348 //\r
349 // Since this memory range will be used on S3 resume, it must be\r
350 // reserved as ACPI NVS.\r
351 //\r
0e8a31f5
LE
352 // If S3 is unsupported, then various drivers might still write to the\r
353 // LockBox area. We ought to prevent DXE from serving allocation requests\r
354 // such that they would overlap the LockBox storage.\r
355 //\r
6a7cba79
LE
356 ZeroMem (\r
357 (VOID*)(UINTN) PcdGet32 (PcdOvmfLockBoxStorageBase),\r
358 (UINTN) PcdGet32 (PcdOvmfLockBoxStorageSize)\r
359 );\r
360 BuildMemoryAllocationHob (\r
361 (EFI_PHYSICAL_ADDRESS)(UINTN) PcdGet32 (PcdOvmfLockBoxStorageBase),\r
362 (UINT64)(UINTN) PcdGet32 (PcdOvmfLockBoxStorageSize),\r
0e8a31f5 363 mS3Supported ? EfiACPIMemoryNVS : EfiBootServicesData\r
6a7cba79 364 );\r
8e54500f 365 }\r
c034906e 366}\r