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