]> git.proxmox.com Git - mirror_edk2.git/blobdiff - OvmfPkg/PlatformPei/MemDetect.c
OvmfPkg: PlatformPei: determine the 64-bit PCI host aperture for X64 DXE
[mirror_edk2.git] / OvmfPkg / PlatformPei / MemDetect.c
index c198c3846fdcf68a4757c9e029b87e27dfb26099..ed13c57beb2fc68c271a1c375e9f95e0d5e7d92a 100644 (file)
@@ -24,6 +24,7 @@ Module Name:
 //\r
 // The Library classes this module consumes\r
 //\r
+#include <Library/BaseMemoryLib.h>\r
 #include <Library/DebugLib.h>\r
 #include <Library/HobLib.h>\r
 #include <Library/IoLib.h>\r
@@ -31,10 +32,13 @@ Module Name:
 #include <Library/PeimEntryPoint.h>\r
 #include <Library/ResourcePublicationLib.h>\r
 #include <Library/MtrrLib.h>\r
+#include <Library/QemuFwCfgLib.h>\r
 \r
 #include "Platform.h"\r
 #include "Cmos.h"\r
 \r
+UINT8 mPhysMemAddressWidth;\r
+\r
 UINT32\r
 GetSystemMemorySizeBelow4gb (\r
   VOID\r
@@ -55,7 +59,7 @@ GetSystemMemorySizeBelow4gb (
   Cmos0x34 = (UINT8) CmosRead8 (0x34);\r
   Cmos0x35 = (UINT8) CmosRead8 (0x35);\r
 \r
-  return (((UINTN)((Cmos0x35 << 8) + Cmos0x34) << 16) + SIZE_16MB);\r
+  return (UINT32) (((UINTN)((Cmos0x35 << 8) + Cmos0x34) << 16) + SIZE_16MB);\r
 }\r
 \r
 \r
@@ -83,6 +87,237 @@ GetSystemMemorySizeAbove4gb (
   return LShiftU64 (Size, 16);\r
 }\r
 \r
+\r
+/**\r
+  Return the highest address that DXE could possibly use, plus one.\r
+**/\r
+STATIC\r
+UINT64\r
+GetFirstNonAddress (\r
+  VOID\r
+  )\r
+{\r
+  UINT64               FirstNonAddress;\r
+  UINT64               Pci64Base, Pci64Size;\r
+  CHAR8                MbString[7 + 1];\r
+  EFI_STATUS           Status;\r
+  FIRMWARE_CONFIG_ITEM FwCfgItem;\r
+  UINTN                FwCfgSize;\r
+  UINT64               HotPlugMemoryEnd;\r
+\r
+  FirstNonAddress = BASE_4GB + GetSystemMemorySizeAbove4gb ();\r
+\r
+  //\r
+  // If DXE is 32-bit, then we're done; PciBusDxe will degrade 64-bit MMIO\r
+  // resources to 32-bit anyway. See DegradeResource() in\r
+  // "PciResourceSupport.c".\r
+  //\r
+#ifdef MDE_CPU_IA32\r
+  if (!FeaturePcdGet (PcdDxeIplSwitchToLongMode)) {\r
+    return FirstNonAddress;\r
+  }\r
+#endif\r
+\r
+  //\r
+  // Otherwise, in order to calculate the highest address plus one, we must\r
+  // consider the 64-bit PCI host aperture too. Fetch the default size.\r
+  //\r
+  Pci64Size = PcdGet64 (PcdPciMmio64Size);\r
+\r
+  //\r
+  // See if the user specified the number of megabytes for the 64-bit PCI host\r
+  // aperture. The number of non-NUL characters in MbString allows for\r
+  // 9,999,999 MB, which is approximately 10 TB.\r
+  //\r
+  // As signaled by the "X-" prefix, this knob is experimental, and might go\r
+  // away at any time.\r
+  //\r
+  Status = QemuFwCfgFindFile ("opt/ovmf/X-PciMmio64Mb", &FwCfgItem,\r
+             &FwCfgSize);\r
+  if (!EFI_ERROR (Status)) {\r
+    if (FwCfgSize >= sizeof MbString) {\r
+      DEBUG ((EFI_D_WARN,\r
+        "%a: ignoring malformed 64-bit PCI host aperture size from fw_cfg\n",\r
+        __FUNCTION__));\r
+    } else {\r
+      QemuFwCfgSelectItem (FwCfgItem);\r
+      QemuFwCfgReadBytes (FwCfgSize, MbString);\r
+      MbString[FwCfgSize] = '\0';\r
+      Pci64Size = LShiftU64 (AsciiStrDecimalToUint64 (MbString), 20);\r
+    }\r
+  }\r
+\r
+  if (Pci64Size == 0) {\r
+    if (mBootMode != BOOT_ON_S3_RESUME) {\r
+      DEBUG ((EFI_D_INFO, "%a: disabling 64-bit PCI host aperture\n",\r
+        __FUNCTION__));\r
+      PcdSet64 (PcdPciMmio64Size, 0);\r
+    }\r
+\r
+    //\r
+    // There's nothing more to do; the amount of memory above 4GB fully\r
+    // determines the highest address plus one. The memory hotplug area (see\r
+    // below) plays no role for the firmware in this case.\r
+    //\r
+    return FirstNonAddress;\r
+  }\r
+\r
+  //\r
+  // The "etc/reserved-memory-end" fw_cfg file, when present, contains an\r
+  // absolute, exclusive end address for the memory hotplug area. This area\r
+  // starts right at the end of the memory above 4GB. The 64-bit PCI host\r
+  // aperture must be placed above it.\r
+  //\r
+  Status = QemuFwCfgFindFile ("etc/reserved-memory-end", &FwCfgItem,\r
+             &FwCfgSize);\r
+  if (!EFI_ERROR (Status) && FwCfgSize == sizeof HotPlugMemoryEnd) {\r
+    QemuFwCfgSelectItem (FwCfgItem);\r
+    QemuFwCfgReadBytes (FwCfgSize, &HotPlugMemoryEnd);\r
+\r
+    ASSERT (HotPlugMemoryEnd >= FirstNonAddress);\r
+    FirstNonAddress = HotPlugMemoryEnd;\r
+  }\r
+\r
+  //\r
+  // SeaBIOS aligns both boundaries of the 64-bit PCI host aperture to 1GB, so\r
+  // that the host can map it with 1GB hugepages. Follow suit.\r
+  //\r
+  Pci64Base = ALIGN_VALUE (FirstNonAddress, (UINT64)SIZE_1GB);\r
+  Pci64Size = ALIGN_VALUE (Pci64Size, (UINT64)SIZE_1GB);\r
+\r
+  //\r
+  // The 64-bit PCI host aperture should also be "naturally" aligned. The\r
+  // alignment is determined by rounding the size of the aperture down to the\r
+  // next smaller or equal power of two. That is, align the aperture by the\r
+  // largest BAR size that can fit into it.\r
+  //\r
+  Pci64Base = ALIGN_VALUE (Pci64Base, GetPowerOfTwo64 (Pci64Size));\r
+\r
+  if (mBootMode != BOOT_ON_S3_RESUME) {\r
+    //\r
+    // The core PciHostBridgeDxe driver will automatically add this range to\r
+    // the GCD memory space map through our PciHostBridgeLib instance; here we\r
+    // only need to set the PCDs.\r
+    //\r
+    PcdSet64 (PcdPciMmio64Base, Pci64Base);\r
+    PcdSet64 (PcdPciMmio64Size, Pci64Size);\r
+    DEBUG ((EFI_D_INFO, "%a: Pci64Base=0x%Lx Pci64Size=0x%Lx\n",\r
+      __FUNCTION__, Pci64Base, Pci64Size));\r
+  }\r
+\r
+  //\r
+  // The useful address space ends with the 64-bit PCI host aperture.\r
+  //\r
+  FirstNonAddress = Pci64Base + Pci64Size;\r
+  return FirstNonAddress;\r
+}\r
+\r
+\r
+/**\r
+  Initialize the mPhysMemAddressWidth variable, based on guest RAM size.\r
+**/\r
+VOID\r
+AddressWidthInitialization (\r
+  VOID\r
+  )\r
+{\r
+  UINT64 FirstNonAddress;\r
+\r
+  //\r
+  // As guest-physical memory size grows, the permanent PEI RAM requirements\r
+  // are dominated by the identity-mapping page tables built by the DXE IPL.\r
+  // The DXL IPL keys off of the physical address bits advertized in the CPU\r
+  // HOB. To conserve memory, we calculate the minimum address width here.\r
+  //\r
+  FirstNonAddress      = GetFirstNonAddress ();\r
+  mPhysMemAddressWidth = (UINT8)HighBitSet64 (FirstNonAddress);\r
+\r
+  //\r
+  // If FirstNonAddress is not an integral power of two, then we need an\r
+  // additional bit.\r
+  //\r
+  if ((FirstNonAddress & (FirstNonAddress - 1)) != 0) {\r
+    ++mPhysMemAddressWidth;\r
+  }\r
+\r
+  //\r
+  // The minimum address width is 36 (covers up to and excluding 64 GB, which\r
+  // is the maximum for Ia32 + PAE). The theoretical architecture maximum for\r
+  // X64 long mode is 52 bits, but the DXE IPL clamps that down to 48 bits. We\r
+  // can simply assert that here, since 48 bits are good enough for 256 TB.\r
+  //\r
+  if (mPhysMemAddressWidth <= 36) {\r
+    mPhysMemAddressWidth = 36;\r
+  }\r
+  ASSERT (mPhysMemAddressWidth <= 48);\r
+}\r
+\r
+\r
+/**\r
+  Calculate the cap for the permanent PEI memory.\r
+**/\r
+STATIC\r
+UINT32\r
+GetPeiMemoryCap (\r
+  VOID\r
+  )\r
+{\r
+  BOOLEAN Page1GSupport;\r
+  UINT32  RegEax;\r
+  UINT32  RegEdx;\r
+  UINT32  Pml4Entries;\r
+  UINT32  PdpEntries;\r
+  UINTN   TotalPages;\r
+\r
+  //\r
+  // If DXE is 32-bit, then just return the traditional 64 MB cap.\r
+  //\r
+#ifdef MDE_CPU_IA32\r
+  if (!FeaturePcdGet (PcdDxeIplSwitchToLongMode)) {\r
+    return SIZE_64MB;\r
+  }\r
+#endif\r
+\r
+  //\r
+  // Dependent on physical address width, PEI memory allocations can be\r
+  // dominated by the page tables built for 64-bit DXE. So we key the cap off\r
+  // of those. The code below is based on CreateIdentityMappingPageTables() in\r
+  // "MdeModulePkg/Core/DxeIplPeim/X64/VirtualMemory.c".\r
+  //\r
+  Page1GSupport = FALSE;\r
+  if (PcdGetBool (PcdUse1GPageTable)) {\r
+    AsmCpuid (0x80000000, &RegEax, NULL, NULL, NULL);\r
+    if (RegEax >= 0x80000001) {\r
+      AsmCpuid (0x80000001, NULL, NULL, NULL, &RegEdx);\r
+      if ((RegEdx & BIT26) != 0) {\r
+        Page1GSupport = TRUE;\r
+      }\r
+    }\r
+  }\r
+\r
+  if (mPhysMemAddressWidth <= 39) {\r
+    Pml4Entries = 1;\r
+    PdpEntries = 1 << (mPhysMemAddressWidth - 30);\r
+    ASSERT (PdpEntries <= 0x200);\r
+  } else {\r
+    Pml4Entries = 1 << (mPhysMemAddressWidth - 39);\r
+    ASSERT (Pml4Entries <= 0x200);\r
+    PdpEntries = 512;\r
+  }\r
+\r
+  TotalPages = Page1GSupport ? Pml4Entries + 1 :\r
+                               (PdpEntries + 1) * Pml4Entries + 1;\r
+  ASSERT (TotalPages <= 0x40201);\r
+\r
+  //\r
+  // Add 64 MB for miscellaneous allocations. Note that for\r
+  // mPhysMemAddressWidth values close to 36, the cap will actually be\r
+  // dominated by this increment.\r
+  //\r
+  return (UINT32)(EFI_PAGES_TO_SIZE (TotalPages) + SIZE_64MB);\r
+}\r
+\r
+\r
 /**\r
   Publish PEI core memory\r
 \r
@@ -98,21 +333,41 @@ PublishPeiMemory (
   EFI_PHYSICAL_ADDRESS        MemoryBase;\r
   UINT64                      MemorySize;\r
   UINT64                      LowerMemorySize;\r
+  UINT32                      PeiMemoryCap;\r
 \r
   if (mBootMode == BOOT_ON_S3_RESUME) {\r
     MemoryBase = PcdGet32 (PcdS3AcpiReservedMemoryBase);\r
     MemorySize = PcdGet32 (PcdS3AcpiReservedMemorySize);\r
   } else {\r
     LowerMemorySize = GetSystemMemorySizeBelow4gb ();\r
+    if (FeaturePcdGet (PcdSmmSmramRequire)) {\r
+      //\r
+      // TSEG is chipped from the end of low RAM\r
+      //\r
+      LowerMemorySize -= FixedPcdGet8 (PcdQ35TsegMbytes) * SIZE_1MB;\r
+    }\r
+\r
+    PeiMemoryCap = GetPeiMemoryCap ();\r
+    DEBUG ((EFI_D_INFO, "%a: mPhysMemAddressWidth=%d PeiMemoryCap=%u KB\n",\r
+      __FUNCTION__, mPhysMemAddressWidth, PeiMemoryCap >> 10));\r
 \r
     //\r
     // Determine the range of memory to use during PEI\r
     //\r
-    MemoryBase = PcdGet32 (PcdOvmfDxeMemFvBase) + PcdGet32 (PcdOvmfDxeMemFvSize);\r
+    // Technically we could lay the permanent PEI RAM over SEC's temporary\r
+    // decompression and scratch buffer even if "secure S3" is needed, since\r
+    // their lifetimes don't overlap. However, PeiFvInitialization() will cover\r
+    // RAM up to PcdOvmfDecompressionScratchEnd with an EfiACPIMemoryNVS memory\r
+    // allocation HOB, and other allocations served from the permanent PEI RAM\r
+    // shouldn't overlap with that HOB.\r
+    //\r
+    MemoryBase = mS3Supported && FeaturePcdGet (PcdSmmSmramRequire) ?\r
+      PcdGet32 (PcdOvmfDecompressionScratchEnd) :\r
+      PcdGet32 (PcdOvmfDxeMemFvBase) + PcdGet32 (PcdOvmfDxeMemFvSize);\r
     MemorySize = LowerMemorySize - MemoryBase;\r
-    if (MemorySize > SIZE_64MB) {\r
-      MemoryBase = LowerMemorySize - SIZE_64MB;\r
-      MemorySize = SIZE_64MB;\r
+    if (MemorySize > PeiMemoryCap) {\r
+      MemoryBase = LowerMemorySize - PeiMemoryCap;\r
+      MemorySize = PeiMemoryCap;\r
     }\r
   }\r
 \r
@@ -138,6 +393,8 @@ QemuInitializeRam (
 {\r
   UINT64                      LowerMemorySize;\r
   UINT64                      UpperMemorySize;\r
+  MTRR_SETTINGS               MtrrSettings;\r
+  EFI_STATUS                  Status;\r
 \r
   DEBUG ((EFI_D_INFO, "%a called\n", __FUNCTION__));\r
 \r
@@ -151,20 +408,67 @@ QemuInitializeRam (
     //\r
     // Create memory HOBs\r
     //\r
-    AddMemoryRangeHob (BASE_1MB, LowerMemorySize);\r
     AddMemoryRangeHob (0, BASE_512KB + BASE_128KB);\r
-  }\r
 \r
-  MtrrSetMemoryAttribute (BASE_1MB, LowerMemorySize - BASE_1MB, CacheWriteBack);\r
+    if (FeaturePcdGet (PcdSmmSmramRequire)) {\r
+      UINT32 TsegSize;\r
 \r
-  MtrrSetMemoryAttribute (0, BASE_512KB + BASE_128KB, CacheWriteBack);\r
+      TsegSize = FixedPcdGet8 (PcdQ35TsegMbytes) * SIZE_1MB;\r
+      AddMemoryRangeHob (BASE_1MB, LowerMemorySize - TsegSize);\r
+      AddReservedMemoryBaseSizeHob (LowerMemorySize - TsegSize, TsegSize,\r
+        TRUE);\r
+    } else {\r
+      AddMemoryRangeHob (BASE_1MB, LowerMemorySize);\r
+    }\r
 \r
-  if (UpperMemorySize != 0) {\r
-    if (mBootMode != BOOT_ON_S3_RESUME) {\r
+    if (UpperMemorySize != 0) {\r
       AddUntestedMemoryBaseSizeHob (BASE_4GB, UpperMemorySize);\r
     }\r
+  }\r
 \r
-    MtrrSetMemoryAttribute (BASE_4GB, UpperMemorySize, CacheWriteBack);\r
+  //\r
+  // We'd like to keep the following ranges uncached:\r
+  // - [640 KB, 1 MB)\r
+  // - [LowerMemorySize, 4 GB)\r
+  //\r
+  // Everything else should be WB. Unfortunately, programming the inverse (ie.\r
+  // keeping the default UC, and configuring the complement set of the above as\r
+  // WB) is not reliable in general, because the end of the upper RAM can have\r
+  // practically any alignment, and we may not have enough variable MTRRs to\r
+  // cover it exactly.\r
+  //\r
+  if (IsMtrrSupported ()) {\r
+    MtrrGetAllMtrrs (&MtrrSettings);\r
+\r
+    //\r
+    // MTRRs disabled, fixed MTRRs disabled, default type is uncached\r
+    //\r
+    ASSERT ((MtrrSettings.MtrrDefType & BIT11) == 0);\r
+    ASSERT ((MtrrSettings.MtrrDefType & BIT10) == 0);\r
+    ASSERT ((MtrrSettings.MtrrDefType & 0xFF) == 0);\r
+\r
+    //\r
+    // flip default type to writeback\r
+    //\r
+    SetMem (&MtrrSettings.Fixed, sizeof MtrrSettings.Fixed, 0x06);\r
+    ZeroMem (&MtrrSettings.Variables, sizeof MtrrSettings.Variables);\r
+    MtrrSettings.MtrrDefType |= BIT11 | BIT10 | 6;\r
+    MtrrSetAllMtrrs (&MtrrSettings);\r
+\r
+    //\r
+    // Set memory range from 640KB to 1MB to uncacheable\r
+    //\r
+    Status = MtrrSetMemoryAttribute (BASE_512KB + BASE_128KB,\r
+               BASE_1MB - (BASE_512KB + BASE_128KB), CacheUncacheable);\r
+    ASSERT_EFI_ERROR (Status);\r
+\r
+    //\r
+    // Set memory range from the "top of lower RAM" (RAM below 4GB) to 4GB as\r
+    // uncacheable\r
+    //\r
+    Status = MtrrSetMemoryAttribute (LowerMemorySize,\r
+               SIZE_4GB - LowerMemorySize, CacheUncacheable);\r
+    ASSERT_EFI_ERROR (Status);\r
   }\r
 }\r
 \r
@@ -192,5 +496,78 @@ InitializeRamRegions (
       (UINT64)(UINTN) PcdGet32 (PcdS3AcpiReservedMemorySize),\r
       EfiACPIMemoryNVS\r
       );\r
+\r
+    //\r
+    // Cover the initial RAM area used as stack and temporary PEI heap.\r
+    //\r
+    // This is reserved as ACPI NVS so it can be used on S3 resume.\r
+    //\r
+    BuildMemoryAllocationHob (\r
+      PcdGet32 (PcdOvmfSecPeiTempRamBase),\r
+      PcdGet32 (PcdOvmfSecPeiTempRamSize),\r
+      EfiACPIMemoryNVS\r
+      );\r
+\r
+    //\r
+    // SEC stores its table of GUIDed section handlers here.\r
+    //\r
+    BuildMemoryAllocationHob (\r
+      PcdGet64 (PcdGuidedExtractHandlerTableAddress),\r
+      PcdGet32 (PcdGuidedExtractHandlerTableSize),\r
+      EfiACPIMemoryNVS\r
+      );\r
+\r
+#ifdef MDE_CPU_X64\r
+    //\r
+    // Reserve the initial page tables built by the reset vector code.\r
+    //\r
+    // Since this memory range will be used by the Reset Vector on S3\r
+    // resume, it must be reserved as ACPI NVS.\r
+    //\r
+    BuildMemoryAllocationHob (\r
+      (EFI_PHYSICAL_ADDRESS)(UINTN) PcdGet32 (PcdOvmfSecPageTablesBase),\r
+      (UINT64)(UINTN) PcdGet32 (PcdOvmfSecPageTablesSize),\r
+      EfiACPIMemoryNVS\r
+      );\r
+#endif\r
+  }\r
+\r
+  if (mBootMode != BOOT_ON_S3_RESUME) {\r
+    if (!FeaturePcdGet (PcdSmmSmramRequire)) {\r
+      //\r
+      // Reserve the lock box storage area\r
+      //\r
+      // Since this memory range will be used on S3 resume, it must be\r
+      // reserved as ACPI NVS.\r
+      //\r
+      // If S3 is unsupported, then various drivers might still write to the\r
+      // LockBox area. We ought to prevent DXE from serving allocation requests\r
+      // such that they would overlap the LockBox storage.\r
+      //\r
+      ZeroMem (\r
+        (VOID*)(UINTN) PcdGet32 (PcdOvmfLockBoxStorageBase),\r
+        (UINTN) PcdGet32 (PcdOvmfLockBoxStorageSize)\r
+        );\r
+      BuildMemoryAllocationHob (\r
+        (EFI_PHYSICAL_ADDRESS)(UINTN) PcdGet32 (PcdOvmfLockBoxStorageBase),\r
+        (UINT64)(UINTN) PcdGet32 (PcdOvmfLockBoxStorageSize),\r
+        mS3Supported ? EfiACPIMemoryNVS : EfiBootServicesData\r
+        );\r
+    }\r
+\r
+    if (FeaturePcdGet (PcdSmmSmramRequire)) {\r
+      UINT32 TsegSize;\r
+\r
+      //\r
+      // Make sure the TSEG area that we reported as a reserved memory resource\r
+      // cannot be used for reserved memory allocations.\r
+      //\r
+      TsegSize = FixedPcdGet8 (PcdQ35TsegMbytes) * SIZE_1MB;\r
+      BuildMemoryAllocationHob (\r
+        GetSystemMemorySizeBelow4gb() - TsegSize,\r
+        TsegSize,\r
+        EfiReservedMemoryType\r
+        );\r
+    }\r
   }\r
 }\r