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
GetSystemMemorySizeBelow4gb(VOID)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
GetSystemMemorySizeAbove4gb()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 Initialize the mPhysMemAddressWidth variable, based on guest RAM size.
92 **/
93 VOID
AddressWidthInitialization(VOID)94 AddressWidthInitialization (
95 VOID
96 )
97 {
98 UINT64 FirstNonAddress;
99
100 //
101 // As guest-physical memory size grows, the permanent PEI RAM requirements
102 // are dominated by the identity-mapping page tables built by the DXE IPL.
103 // The DXL IPL keys off of the physical address bits advertized in the CPU
104 // HOB. To conserve memory, we calculate the minimum address width here.
105 //
106 FirstNonAddress = BASE_4GB + GetSystemMemorySizeAbove4gb ();
107 mPhysMemAddressWidth = (UINT8)HighBitSet64 (FirstNonAddress);
108
109 //
110 // If FirstNonAddress is not an integral power of two, then we need an
111 // additional bit.
112 //
113 if ((FirstNonAddress & (FirstNonAddress - 1)) != 0) {
114 ++mPhysMemAddressWidth;
115 }
116
117 //
118 // The minimum address width is 36 (covers up to and excluding 64 GB, which
119 // is the maximum for Ia32 + PAE). The theoretical architecture maximum for
120 // X64 long mode is 52 bits, but the DXE IPL clamps that down to 48 bits. We
121 // can simply assert that here, since 48 bits are good enough for 256 TB.
122 //
123 if (mPhysMemAddressWidth <= 36) {
124 mPhysMemAddressWidth = 36;
125 }
126 ASSERT (mPhysMemAddressWidth <= 48);
127 }
128
129
130 /**
131 Calculate the cap for the permanent PEI memory.
132 **/
133 STATIC
134 UINT32
GetPeiMemoryCap(VOID)135 GetPeiMemoryCap (
136 VOID
137 )
138 {
139 BOOLEAN Page1GSupport;
140 UINT32 RegEax;
141 UINT32 RegEdx;
142 UINT32 Pml4Entries;
143 UINT32 PdpEntries;
144 UINTN TotalPages;
145
146 //
147 // If DXE is 32-bit, then just return the traditional 64 MB cap.
148 //
149 #ifdef MDE_CPU_IA32
150 if (!FeaturePcdGet (PcdDxeIplSwitchToLongMode)) {
151 return SIZE_64MB;
152 }
153 #endif
154
155 //
156 // Dependent on physical address width, PEI memory allocations can be
157 // dominated by the page tables built for 64-bit DXE. So we key the cap off
158 // of those. The code below is based on CreateIdentityMappingPageTables() in
159 // "MdeModulePkg/Core/DxeIplPeim/X64/VirtualMemory.c".
160 //
161 Page1GSupport = FALSE;
162 if (PcdGetBool (PcdUse1GPageTable)) {
163 AsmCpuid (0x80000000, &RegEax, NULL, NULL, NULL);
164 if (RegEax >= 0x80000001) {
165 AsmCpuid (0x80000001, NULL, NULL, NULL, &RegEdx);
166 if ((RegEdx & BIT26) != 0) {
167 Page1GSupport = TRUE;
168 }
169 }
170 }
171
172 if (mPhysMemAddressWidth <= 39) {
173 Pml4Entries = 1;
174 PdpEntries = 1 << (mPhysMemAddressWidth - 30);
175 ASSERT (PdpEntries <= 0x200);
176 } else {
177 Pml4Entries = 1 << (mPhysMemAddressWidth - 39);
178 ASSERT (Pml4Entries <= 0x200);
179 PdpEntries = 512;
180 }
181
182 TotalPages = Page1GSupport ? Pml4Entries + 1 :
183 (PdpEntries + 1) * Pml4Entries + 1;
184 ASSERT (TotalPages <= 0x40201);
185
186 //
187 // Add 64 MB for miscellaneous allocations. Note that for
188 // mPhysMemAddressWidth values close to 36, the cap will actually be
189 // dominated by this increment.
190 //
191 return (UINT32)(EFI_PAGES_TO_SIZE (TotalPages) + SIZE_64MB);
192 }
193
194
195 /**
196 Publish PEI core memory
197
198 @return EFI_SUCCESS The PEIM initialized successfully.
199
200 **/
201 EFI_STATUS
PublishPeiMemory(VOID)202 PublishPeiMemory (
203 VOID
204 )
205 {
206 EFI_STATUS Status;
207 EFI_PHYSICAL_ADDRESS MemoryBase;
208 UINT64 MemorySize;
209 UINT64 LowerMemorySize;
210 UINT32 PeiMemoryCap;
211
212 if (mBootMode == BOOT_ON_S3_RESUME) {
213 MemoryBase = PcdGet32 (PcdS3AcpiReservedMemoryBase);
214 MemorySize = PcdGet32 (PcdS3AcpiReservedMemorySize);
215 } else {
216 LowerMemorySize = GetSystemMemorySizeBelow4gb ();
217 if (FeaturePcdGet (PcdSmmSmramRequire)) {
218 //
219 // TSEG is chipped from the end of low RAM
220 //
221 LowerMemorySize -= FixedPcdGet8 (PcdQ35TsegMbytes) * SIZE_1MB;
222 }
223
224 PeiMemoryCap = GetPeiMemoryCap ();
225 DEBUG ((EFI_D_INFO, "%a: mPhysMemAddressWidth=%d PeiMemoryCap=%u KB\n",
226 __FUNCTION__, mPhysMemAddressWidth, PeiMemoryCap >> 10));
227
228 //
229 // Determine the range of memory to use during PEI
230 //
231 // Technically we could lay the permanent PEI RAM over SEC's temporary
232 // decompression and scratch buffer even if "secure S3" is needed, since
233 // their lifetimes don't overlap. However, PeiFvInitialization() will cover
234 // RAM up to PcdOvmfDecompressionScratchEnd with an EfiACPIMemoryNVS memory
235 // allocation HOB, and other allocations served from the permanent PEI RAM
236 // shouldn't overlap with that HOB.
237 //
238 MemoryBase = mS3Supported && FeaturePcdGet (PcdSmmSmramRequire) ?
239 PcdGet32 (PcdOvmfDecompressionScratchEnd) :
240 PcdGet32 (PcdOvmfDxeMemFvBase) + PcdGet32 (PcdOvmfDxeMemFvSize);
241 MemorySize = LowerMemorySize - MemoryBase;
242 if (MemorySize > PeiMemoryCap) {
243 MemoryBase = LowerMemorySize - PeiMemoryCap;
244 MemorySize = PeiMemoryCap;
245 }
246 }
247
248 //
249 // Publish this memory to the PEI Core
250 //
251 Status = PublishSystemMemory(MemoryBase, MemorySize);
252 ASSERT_EFI_ERROR (Status);
253
254 return Status;
255 }
256
257
258 /**
259 Peform Memory Detection for QEMU / KVM
260
261 **/
262 STATIC
263 VOID
QemuInitializeRam(VOID)264 QemuInitializeRam (
265 VOID
266 )
267 {
268 UINT64 LowerMemorySize;
269 UINT64 UpperMemorySize;
270 MTRR_SETTINGS MtrrSettings;
271 EFI_STATUS Status;
272
273 DEBUG ((EFI_D_INFO, "%a called\n", __FUNCTION__));
274
275 //
276 // Determine total memory size available
277 //
278 LowerMemorySize = GetSystemMemorySizeBelow4gb ();
279 UpperMemorySize = GetSystemMemorySizeAbove4gb ();
280
281 if (mBootMode != BOOT_ON_S3_RESUME) {
282 //
283 // Create memory HOBs
284 //
285 AddMemoryRangeHob (0, BASE_512KB + BASE_128KB);
286
287 if (FeaturePcdGet (PcdSmmSmramRequire)) {
288 UINT32 TsegSize;
289
290 TsegSize = FixedPcdGet8 (PcdQ35TsegMbytes) * SIZE_1MB;
291 AddMemoryRangeHob (BASE_1MB, LowerMemorySize - TsegSize);
292 AddReservedMemoryBaseSizeHob (LowerMemorySize - TsegSize, TsegSize,
293 TRUE);
294 } else {
295 AddMemoryRangeHob (BASE_1MB, LowerMemorySize);
296 }
297
298 if (UpperMemorySize != 0) {
299 AddUntestedMemoryBaseSizeHob (BASE_4GB, UpperMemorySize);
300 }
301 }
302
303 //
304 // We'd like to keep the following ranges uncached:
305 // - [640 KB, 1 MB)
306 // - [LowerMemorySize, 4 GB)
307 //
308 // Everything else should be WB. Unfortunately, programming the inverse (ie.
309 // keeping the default UC, and configuring the complement set of the above as
310 // WB) is not reliable in general, because the end of the upper RAM can have
311 // practically any alignment, and we may not have enough variable MTRRs to
312 // cover it exactly.
313 //
314 if (IsMtrrSupported ()) {
315 MtrrGetAllMtrrs (&MtrrSettings);
316
317 //
318 // MTRRs disabled, fixed MTRRs disabled, default type is uncached
319 //
320 ASSERT ((MtrrSettings.MtrrDefType & BIT11) == 0);
321 ASSERT ((MtrrSettings.MtrrDefType & BIT10) == 0);
322 ASSERT ((MtrrSettings.MtrrDefType & 0xFF) == 0);
323
324 //
325 // flip default type to writeback
326 //
327 SetMem (&MtrrSettings.Fixed, sizeof MtrrSettings.Fixed, 0x06);
328 ZeroMem (&MtrrSettings.Variables, sizeof MtrrSettings.Variables);
329 MtrrSettings.MtrrDefType |= BIT11 | BIT10 | 6;
330 MtrrSetAllMtrrs (&MtrrSettings);
331
332 //
333 // Set memory range from 640KB to 1MB to uncacheable
334 //
335 Status = MtrrSetMemoryAttribute (BASE_512KB + BASE_128KB,
336 BASE_1MB - (BASE_512KB + BASE_128KB), CacheUncacheable);
337 ASSERT_EFI_ERROR (Status);
338
339 //
340 // Set memory range from the "top of lower RAM" (RAM below 4GB) to 4GB as
341 // uncacheable
342 //
343 Status = MtrrSetMemoryAttribute (LowerMemorySize,
344 SIZE_4GB - LowerMemorySize, CacheUncacheable);
345 ASSERT_EFI_ERROR (Status);
346 }
347 }
348
349 /**
350 Publish system RAM and reserve memory regions
351
352 **/
353 VOID
InitializeRamRegions(VOID)354 InitializeRamRegions (
355 VOID
356 )
357 {
358 if (!mXen) {
359 QemuInitializeRam ();
360 } else {
361 XenPublishRamRegions ();
362 }
363
364 if (mS3Supported && mBootMode != BOOT_ON_S3_RESUME) {
365 //
366 // This is the memory range that will be used for PEI on S3 resume
367 //
368 BuildMemoryAllocationHob (
369 (EFI_PHYSICAL_ADDRESS)(UINTN) PcdGet32 (PcdS3AcpiReservedMemoryBase),
370 (UINT64)(UINTN) PcdGet32 (PcdS3AcpiReservedMemorySize),
371 EfiACPIMemoryNVS
372 );
373
374 //
375 // Cover the initial RAM area used as stack and temporary PEI heap.
376 //
377 // This is reserved as ACPI NVS so it can be used on S3 resume.
378 //
379 BuildMemoryAllocationHob (
380 PcdGet32 (PcdOvmfSecPeiTempRamBase),
381 PcdGet32 (PcdOvmfSecPeiTempRamSize),
382 EfiACPIMemoryNVS
383 );
384
385 //
386 // SEC stores its table of GUIDed section handlers here.
387 //
388 BuildMemoryAllocationHob (
389 PcdGet64 (PcdGuidedExtractHandlerTableAddress),
390 PcdGet32 (PcdGuidedExtractHandlerTableSize),
391 EfiACPIMemoryNVS
392 );
393
394 #ifdef MDE_CPU_X64
395 //
396 // Reserve the initial page tables built by the reset vector code.
397 //
398 // Since this memory range will be used by the Reset Vector on S3
399 // resume, it must be reserved as ACPI NVS.
400 //
401 BuildMemoryAllocationHob (
402 (EFI_PHYSICAL_ADDRESS)(UINTN) PcdGet32 (PcdOvmfSecPageTablesBase),
403 (UINT64)(UINTN) PcdGet32 (PcdOvmfSecPageTablesSize),
404 EfiACPIMemoryNVS
405 );
406 #endif
407 }
408
409 if (mBootMode != BOOT_ON_S3_RESUME) {
410 if (!FeaturePcdGet (PcdSmmSmramRequire)) {
411 //
412 // Reserve the lock box storage area
413 //
414 // Since this memory range will be used on S3 resume, it must be
415 // reserved as ACPI NVS.
416 //
417 // If S3 is unsupported, then various drivers might still write to the
418 // LockBox area. We ought to prevent DXE from serving allocation requests
419 // such that they would overlap the LockBox storage.
420 //
421 ZeroMem (
422 (VOID*)(UINTN) PcdGet32 (PcdOvmfLockBoxStorageBase),
423 (UINTN) PcdGet32 (PcdOvmfLockBoxStorageSize)
424 );
425 BuildMemoryAllocationHob (
426 (EFI_PHYSICAL_ADDRESS)(UINTN) PcdGet32 (PcdOvmfLockBoxStorageBase),
427 (UINT64)(UINTN) PcdGet32 (PcdOvmfLockBoxStorageSize),
428 mS3Supported ? EfiACPIMemoryNVS : EfiBootServicesData
429 );
430 }
431
432 if (FeaturePcdGet (PcdSmmSmramRequire)) {
433 UINT32 TsegSize;
434
435 //
436 // Make sure the TSEG area that we reported as a reserved memory resource
437 // cannot be used for reserved memory allocations.
438 //
439 TsegSize = FixedPcdGet8 (PcdQ35TsegMbytes) * SIZE_1MB;
440 BuildMemoryAllocationHob (
441 GetSystemMemorySizeBelow4gb() - TsegSize,
442 TsegSize,
443 EfiReservedMemoryType
444 );
445 }
446 }
447 }
448