Home | History | Annotate | Download | only in PlatformPei
      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   Initialize the mPhysMemAddressWidth variable, based on guest RAM size.
     92 **/
     93 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
    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
    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
    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
    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