]> git.proxmox.com Git - mirror_edk2.git/blobdiff - OvmfPkg/PlatformPei/MemDetect.c
OvmfPkg/PlatformPei: debug log "etc/reserved-memory-end" from fw_cfg
[mirror_edk2.git] / OvmfPkg / PlatformPei / MemDetect.c
index ed13c57beb2fc68c271a1c375e9f95e0d5e7d92a..2f9e835513649457e2746015a7042cb161d34728 100644 (file)
@@ -1,7 +1,7 @@
 /**@file\r
   Memory Detection for Virtual Machines.\r
 \r
-  Copyright (c) 2006 - 2014, Intel Corporation. All rights reserved.<BR>\r
+  Copyright (c) 2006 - 2016, Intel Corporation. All rights reserved.<BR>\r
   This program and the accompanying materials\r
   are licensed and made available under the terms and conditions of the BSD License\r
   which accompanies this distribution.  The full text of the license may be found at\r
@@ -19,16 +19,20 @@ Module Name:
 //\r
 // The package level header files this module uses\r
 //\r
+#include <IndustryStandard/E820.h>\r
+#include <IndustryStandard/Q35MchIch9.h>\r
 #include <PiPei.h>\r
 \r
 //\r
 // The Library classes this module consumes\r
 //\r
+#include <Library/BaseLib.h>\r
 #include <Library/BaseMemoryLib.h>\r
 #include <Library/DebugLib.h>\r
 #include <Library/HobLib.h>\r
 #include <Library/IoLib.h>\r
 #include <Library/PcdLib.h>\r
+#include <Library/PciLib.h>\r
 #include <Library/PeimEntryPoint.h>\r
 #include <Library/ResourcePublicationLib.h>\r
 #include <Library/MtrrLib.h>\r
@@ -39,6 +43,170 @@ Module Name:
 \r
 UINT8 mPhysMemAddressWidth;\r
 \r
+STATIC UINT32 mS3AcpiReservedMemoryBase;\r
+STATIC UINT32 mS3AcpiReservedMemorySize;\r
+\r
+STATIC UINT16 mQ35TsegMbytes;\r
+\r
+VOID\r
+Q35TsegMbytesInitialization (\r
+  VOID\r
+  )\r
+{\r
+  UINT16        ExtendedTsegMbytes;\r
+  RETURN_STATUS PcdStatus;\r
+\r
+  if (mHostBridgeDevId != INTEL_Q35_MCH_DEVICE_ID) {\r
+    DEBUG ((\r
+      DEBUG_ERROR,\r
+      "%a: no TSEG (SMRAM) on host bridge DID=0x%04x; "\r
+      "only DID=0x%04x (Q35) is supported\n",\r
+      __FUNCTION__,\r
+      mHostBridgeDevId,\r
+      INTEL_Q35_MCH_DEVICE_ID\r
+      ));\r
+    ASSERT (FALSE);\r
+    CpuDeadLoop ();\r
+  }\r
+\r
+  //\r
+  // Check if QEMU offers an extended TSEG.\r
+  //\r
+  // This can be seen from writing MCH_EXT_TSEG_MB_QUERY to the MCH_EXT_TSEG_MB\r
+  // register, and reading back the register.\r
+  //\r
+  // On a QEMU machine type that does not offer an extended TSEG, the initial\r
+  // write overwrites whatever value a malicious guest OS may have placed in\r
+  // the (unimplemented) register, before entering S3 or rebooting.\r
+  // Subsequently, the read returns MCH_EXT_TSEG_MB_QUERY unchanged.\r
+  //\r
+  // On a QEMU machine type that offers an extended TSEG, the initial write\r
+  // triggers an update to the register. Subsequently, the value read back\r
+  // (which is guaranteed to differ from MCH_EXT_TSEG_MB_QUERY) tells us the\r
+  // number of megabytes.\r
+  //\r
+  PciWrite16 (DRAMC_REGISTER_Q35 (MCH_EXT_TSEG_MB), MCH_EXT_TSEG_MB_QUERY);\r
+  ExtendedTsegMbytes = PciRead16 (DRAMC_REGISTER_Q35 (MCH_EXT_TSEG_MB));\r
+  if (ExtendedTsegMbytes == MCH_EXT_TSEG_MB_QUERY) {\r
+    mQ35TsegMbytes = PcdGet16 (PcdQ35TsegMbytes);\r
+    return;\r
+  }\r
+\r
+  DEBUG ((\r
+    DEBUG_INFO,\r
+    "%a: QEMU offers an extended TSEG (%d MB)\n",\r
+    __FUNCTION__,\r
+    ExtendedTsegMbytes\r
+    ));\r
+  PcdStatus = PcdSet16S (PcdQ35TsegMbytes, ExtendedTsegMbytes);\r
+  ASSERT_RETURN_ERROR (PcdStatus);\r
+  mQ35TsegMbytes = ExtendedTsegMbytes;\r
+}\r
+\r
+\r
+/**\r
+  Iterate over the RAM entries in QEMU's fw_cfg E820 RAM map that start outside\r
+  of the 32-bit address range.\r
+\r
+  Find the highest exclusive >=4GB RAM address, or produce memory resource\r
+  descriptor HOBs for RAM entries that start at or above 4GB.\r
+\r
+  @param[out] MaxAddress  If MaxAddress is NULL, then ScanOrAdd64BitE820Ram()\r
+                          produces memory resource descriptor HOBs for RAM\r
+                          entries that start at or above 4GB.\r
+\r
+                          Otherwise, MaxAddress holds the highest exclusive\r
+                          >=4GB RAM address on output. If QEMU's fw_cfg E820\r
+                          RAM map contains no RAM entry that starts outside of\r
+                          the 32-bit address range, then MaxAddress is exactly\r
+                          4GB on output.\r
+\r
+  @retval EFI_SUCCESS         The fw_cfg E820 RAM map was found and processed.\r
+\r
+  @retval EFI_PROTOCOL_ERROR  The RAM map was found, but its size wasn't a\r
+                              whole multiple of sizeof(EFI_E820_ENTRY64). No\r
+                              RAM entry was processed.\r
+\r
+  @return                     Error codes from QemuFwCfgFindFile(). No RAM\r
+                              entry was processed.\r
+**/\r
+STATIC\r
+EFI_STATUS\r
+ScanOrAdd64BitE820Ram (\r
+  OUT UINT64 *MaxAddress OPTIONAL\r
+  )\r
+{\r
+  EFI_STATUS           Status;\r
+  FIRMWARE_CONFIG_ITEM FwCfgItem;\r
+  UINTN                FwCfgSize;\r
+  EFI_E820_ENTRY64     E820Entry;\r
+  UINTN                Processed;\r
+\r
+  Status = QemuFwCfgFindFile ("etc/e820", &FwCfgItem, &FwCfgSize);\r
+  if (EFI_ERROR (Status)) {\r
+    return Status;\r
+  }\r
+  if (FwCfgSize % sizeof E820Entry != 0) {\r
+    return EFI_PROTOCOL_ERROR;\r
+  }\r
+\r
+  if (MaxAddress != NULL) {\r
+    *MaxAddress = BASE_4GB;\r
+  }\r
+\r
+  QemuFwCfgSelectItem (FwCfgItem);\r
+  for (Processed = 0; Processed < FwCfgSize; Processed += sizeof E820Entry) {\r
+    QemuFwCfgReadBytes (sizeof E820Entry, &E820Entry);\r
+    DEBUG ((\r
+      DEBUG_VERBOSE,\r
+      "%a: Base=0x%Lx Length=0x%Lx Type=%u\n",\r
+      __FUNCTION__,\r
+      E820Entry.BaseAddr,\r
+      E820Entry.Length,\r
+      E820Entry.Type\r
+      ));\r
+    if (E820Entry.Type == EfiAcpiAddressRangeMemory &&\r
+        E820Entry.BaseAddr >= BASE_4GB) {\r
+      if (MaxAddress == NULL) {\r
+        UINT64 Base;\r
+        UINT64 End;\r
+\r
+        //\r
+        // Round up the start address, and round down the end address.\r
+        //\r
+        Base = ALIGN_VALUE (E820Entry.BaseAddr, (UINT64)EFI_PAGE_SIZE);\r
+        End = (E820Entry.BaseAddr + E820Entry.Length) &\r
+              ~(UINT64)EFI_PAGE_MASK;\r
+        if (Base < End) {\r
+          AddMemoryRangeHob (Base, End);\r
+          DEBUG ((\r
+            DEBUG_VERBOSE,\r
+            "%a: AddMemoryRangeHob [0x%Lx, 0x%Lx)\n",\r
+            __FUNCTION__,\r
+            Base,\r
+            End\r
+            ));\r
+        }\r
+      } else {\r
+        UINT64 Candidate;\r
+\r
+        Candidate = E820Entry.BaseAddr + E820Entry.Length;\r
+        if (Candidate > *MaxAddress) {\r
+          *MaxAddress = Candidate;\r
+          DEBUG ((\r
+            DEBUG_VERBOSE,\r
+            "%a: MaxAddress=0x%Lx\n",\r
+            __FUNCTION__,\r
+            *MaxAddress\r
+            ));\r
+        }\r
+      }\r
+    }\r
+  }\r
+  return EFI_SUCCESS;\r
+}\r
+\r
+\r
 UINT32\r
 GetSystemMemorySizeBelow4gb (\r
   VOID\r
@@ -104,8 +272,24 @@ GetFirstNonAddress (
   FIRMWARE_CONFIG_ITEM FwCfgItem;\r
   UINTN                FwCfgSize;\r
   UINT64               HotPlugMemoryEnd;\r
+  RETURN_STATUS        PcdStatus;\r
 \r
-  FirstNonAddress = BASE_4GB + GetSystemMemorySizeAbove4gb ();\r
+  //\r
+  // set FirstNonAddress to suppress incorrect compiler/analyzer warnings\r
+  //\r
+  FirstNonAddress = 0;\r
+\r
+  //\r
+  // If QEMU presents an E820 map, then get the highest exclusive >=4GB RAM\r
+  // address from it. This can express an address >= 4GB+1TB.\r
+  //\r
+  // Otherwise, get the flat size of the memory above 4GB from the CMOS (which\r
+  // can only express a size smaller than 1TB), and add it to 4GB.\r
+  //\r
+  Status = ScanOrAdd64BitE820Ram (&FirstNonAddress);\r
+  if (EFI_ERROR (Status)) {\r
+    FirstNonAddress = BASE_4GB + GetSystemMemorySizeAbove4gb ();\r
+  }\r
 \r
   //\r
   // If DXE is 32-bit, then we're done; PciBusDxe will degrade 64-bit MMIO\r
@@ -151,7 +335,8 @@ GetFirstNonAddress (
     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
+      PcdStatus = PcdSet64S (PcdPciMmio64Size, 0);\r
+      ASSERT_RETURN_ERROR (PcdStatus);\r
     }\r
 \r
     //\r
@@ -173,6 +358,8 @@ GetFirstNonAddress (
   if (!EFI_ERROR (Status) && FwCfgSize == sizeof HotPlugMemoryEnd) {\r
     QemuFwCfgSelectItem (FwCfgItem);\r
     QemuFwCfgReadBytes (FwCfgSize, &HotPlugMemoryEnd);\r
+    DEBUG ((DEBUG_VERBOSE, "%a: HotPlugMemoryEnd=0x%Lx\n", __FUNCTION__,\r
+      HotPlugMemoryEnd));\r
 \r
     ASSERT (HotPlugMemoryEnd >= FirstNonAddress);\r
     FirstNonAddress = HotPlugMemoryEnd;\r
@@ -199,8 +386,11 @@ GetFirstNonAddress (
     // 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
+    PcdStatus = PcdSet64S (PcdPciMmio64Base, Pci64Base);\r
+    ASSERT_RETURN_ERROR (PcdStatus);\r
+    PcdStatus = PcdSet64S (PcdPciMmio64Size, Pci64Size);\r
+    ASSERT_RETURN_ERROR (PcdStatus);\r
+\r
     DEBUG ((EFI_D_INFO, "%a: Pci64Base=0x%Lx Pci64Size=0x%Lx\n",\r
       __FUNCTION__, Pci64Base, Pci64Size));\r
   }\r
@@ -332,21 +522,34 @@ PublishPeiMemory (
   EFI_STATUS                  Status;\r
   EFI_PHYSICAL_ADDRESS        MemoryBase;\r
   UINT64                      MemorySize;\r
-  UINT64                      LowerMemorySize;\r
+  UINT32                      LowerMemorySize;\r
   UINT32                      PeiMemoryCap;\r
 \r
+  LowerMemorySize = GetSystemMemorySizeBelow4gb ();\r
+  if (FeaturePcdGet (PcdSmmSmramRequire)) {\r
+    //\r
+    // TSEG is chipped from the end of low RAM\r
+    //\r
+    LowerMemorySize -= mQ35TsegMbytes * SIZE_1MB;\r
+  }\r
+\r
+  //\r
+  // If S3 is supported, then the S3 permanent PEI memory is placed next,\r
+  // downwards. Its size is primarily dictated by CpuMpPei. The formula below\r
+  // is an approximation.\r
+  //\r
+  if (mS3Supported) {\r
+    mS3AcpiReservedMemorySize = SIZE_512KB +\r
+      mMaxCpuCount *\r
+      PcdGet32 (PcdCpuApStackSize);\r
+    mS3AcpiReservedMemoryBase = LowerMemorySize - mS3AcpiReservedMemorySize;\r
+    LowerMemorySize = mS3AcpiReservedMemoryBase;\r
+  }\r
+\r
   if (mBootMode == BOOT_ON_S3_RESUME) {\r
-    MemoryBase = PcdGet32 (PcdS3AcpiReservedMemoryBase);\r
-    MemorySize = PcdGet32 (PcdS3AcpiReservedMemorySize);\r
+    MemoryBase = mS3AcpiReservedMemoryBase;\r
+    MemorySize = mS3AcpiReservedMemorySize;\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
@@ -404,7 +607,29 @@ QemuInitializeRam (
   LowerMemorySize = GetSystemMemorySizeBelow4gb ();\r
   UpperMemorySize = GetSystemMemorySizeAbove4gb ();\r
 \r
-  if (mBootMode != BOOT_ON_S3_RESUME) {\r
+  if (mBootMode == BOOT_ON_S3_RESUME) {\r
+    //\r
+    // Create the following memory HOB as an exception on the S3 boot path.\r
+    //\r
+    // Normally we'd create memory HOBs only on the normal boot path. However,\r
+    // CpuMpPei specifically needs such a low-memory HOB on the S3 path as\r
+    // well, for "borrowing" a subset of it temporarily, for the AP startup\r
+    // vector.\r
+    //\r
+    // CpuMpPei saves the original contents of the borrowed area in permanent\r
+    // PEI RAM, in a backup buffer allocated with the normal PEI services.\r
+    // CpuMpPei restores the original contents ("returns" the borrowed area) at\r
+    // End-of-PEI. End-of-PEI in turn is emitted by S3Resume2Pei before\r
+    // transferring control to the OS's wakeup vector in the FACS.\r
+    //\r
+    // We expect any other PEIMs that "borrow" memory similarly to CpuMpPei to\r
+    // restore the original contents. Furthermore, we expect all such PEIMs\r
+    // (CpuMpPei included) to claim the borrowed areas by producing memory\r
+    // allocation HOBs, and to honor preexistent memory allocation HOBs when\r
+    // looking for an area to borrow.\r
+    //\r
+    AddMemoryRangeHob (0, BASE_512KB + BASE_128KB);\r
+  } else {\r
     //\r
     // Create memory HOBs\r
     //\r
@@ -413,7 +638,7 @@ QemuInitializeRam (
     if (FeaturePcdGet (PcdSmmSmramRequire)) {\r
       UINT32 TsegSize;\r
 \r
-      TsegSize = FixedPcdGet8 (PcdQ35TsegMbytes) * SIZE_1MB;\r
+      TsegSize = mQ35TsegMbytes * SIZE_1MB;\r
       AddMemoryRangeHob (BASE_1MB, LowerMemorySize - TsegSize);\r
       AddReservedMemoryBaseSizeHob (LowerMemorySize - TsegSize, TsegSize,\r
         TRUE);\r
@@ -421,8 +646,14 @@ QemuInitializeRam (
       AddMemoryRangeHob (BASE_1MB, LowerMemorySize);\r
     }\r
 \r
-    if (UpperMemorySize != 0) {\r
-      AddUntestedMemoryBaseSizeHob (BASE_4GB, UpperMemorySize);\r
+    //\r
+    // If QEMU presents an E820 map, then create memory HOBs for the >=4GB RAM\r
+    // entries. Otherwise, create a single memory HOB with the flat >=4GB\r
+    // memory size read from the CMOS.\r
+    //\r
+    Status = ScanOrAdd64BitE820Ram (NULL);\r
+    if (EFI_ERROR (Status) && UpperMemorySize != 0) {\r
+      AddMemoryBaseSizeHob (BASE_4GB, UpperMemorySize);\r
     }\r
   }\r
 \r
@@ -492,8 +723,8 @@ InitializeRamRegions (
     // This is the memory range that will be used for PEI on S3 resume\r
     //\r
     BuildMemoryAllocationHob (\r
-      (EFI_PHYSICAL_ADDRESS)(UINTN) PcdGet32 (PcdS3AcpiReservedMemoryBase),\r
-      (UINT64)(UINTN) PcdGet32 (PcdS3AcpiReservedMemorySize),\r
+      mS3AcpiReservedMemoryBase,\r
+      mS3AcpiReservedMemorySize,\r
       EfiACPIMemoryNVS\r
       );\r
 \r
@@ -562,7 +793,7 @@ InitializeRamRegions (
       // 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
+      TsegSize = mQ35TsegMbytes * SIZE_1MB;\r
       BuildMemoryAllocationHob (\r
         GetSystemMemorySizeBelow4gb() - TsegSize,\r
         TsegSize,\r