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
13Module 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
39UINT8 mPhysMemAddressWidth;
40
41UINT32
42GetSystemMemorySizeBelow4gb (
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
65STATIC
66UINT64
67GetSystemMemorySizeAbove4gb (
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**/
93VOID
94AddressWidthInitialization (
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**/
133STATIC
134UINT32
135GetPeiMemoryCap (
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**/
201EFI_STATUS
202PublishPeiMemory (
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**/
262STATIC
263VOID
264QemuInitializeRam (
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**/
353VOID
354InitializeRamRegions (
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