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