]> git.proxmox.com Git - mirror_edk2.git/blobdiff - OvmfPkg/PlatformPei/Platform.c
OvmfPkg: PlatformPei: enable PCIEXBAR (aka MMCONFIG / ECAM) on Q35
[mirror_edk2.git] / OvmfPkg / PlatformPei / Platform.c
index 8e4da41001e1248d0e255c88ee3e653a4214b19c..0fc227803a84366e5f339402173ad0c04b7b0a77 100644 (file)
@@ -212,17 +212,20 @@ MemMapInitialization (
 \r
   if (!mXen) {\r
     UINT32  TopOfLowRam;\r
 \r
   if (!mXen) {\r
     UINT32  TopOfLowRam;\r
+    UINT64  PciExBarBase;\r
     UINT32  PciBase;\r
     UINT32  PciSize;\r
 \r
     TopOfLowRam = GetSystemMemorySizeBelow4gb ();\r
     if (mHostBridgeDevId == INTEL_Q35_MCH_DEVICE_ID) {\r
       //\r
     UINT32  PciBase;\r
     UINT32  PciSize;\r
 \r
     TopOfLowRam = GetSystemMemorySizeBelow4gb ();\r
     if (mHostBridgeDevId == INTEL_Q35_MCH_DEVICE_ID) {\r
       //\r
-      // On Q35 machine types that QEMU intends to support in the long term,\r
-      // QEMU never lets the RAM below 4 GB exceed 2 GB.\r
+      // The MMCONFIG area is expected to fall between the top of low RAM and\r
+      // the base of the 32-bit PCI host aperture.\r
       //\r
       //\r
-      PciBase = BASE_2GB;\r
-      ASSERT (TopOfLowRam <= PciBase);\r
+      PciExBarBase = FixedPcdGet64 (PcdPciExpressBaseAddress);\r
+      ASSERT (TopOfLowRam <= PciExBarBase);\r
+      ASSERT (PciExBarBase <= MAX_UINT32 - SIZE_256MB);\r
+      PciBase = (UINT32)(PciExBarBase + SIZE_256MB);\r
     } else {\r
       PciBase = (TopOfLowRam < BASE_2GB) ? BASE_2GB : TopOfLowRam;\r
     }\r
     } else {\r
       PciBase = (TopOfLowRam < BASE_2GB) ? BASE_2GB : TopOfLowRam;\r
     }\r
@@ -248,6 +251,30 @@ MemMapInitialization (
     AddIoMemoryBaseSizeHob (0xFED00000, SIZE_1KB);\r
     if (mHostBridgeDevId == INTEL_Q35_MCH_DEVICE_ID) {\r
       AddIoMemoryBaseSizeHob (ICH9_ROOT_COMPLEX_BASE, SIZE_16KB);\r
     AddIoMemoryBaseSizeHob (0xFED00000, SIZE_1KB);\r
     if (mHostBridgeDevId == INTEL_Q35_MCH_DEVICE_ID) {\r
       AddIoMemoryBaseSizeHob (ICH9_ROOT_COMPLEX_BASE, SIZE_16KB);\r
+      //\r
+      // Note: there should be an\r
+      //\r
+      //   AddIoMemoryBaseSizeHob (PciExBarBase, SIZE_256MB);\r
+      //\r
+      // call below, just like the one above for RCBA. However, Linux insists\r
+      // that the MMCONFIG area be marked in the E820 or UEFI memory map as\r
+      // "reserved memory" -- Linux does not content itself with a simple gap\r
+      // in the memory map wherever the MCFG ACPI table points to.\r
+      //\r
+      // This appears to be a safety measure. The PCI Firmware Specification\r
+      // (rev 3.1) says in 4.1.2. "MCFG Table Description": "The resources can\r
+      // *optionally* be returned in [...] EFIGetMemoryMap as reserved memory\r
+      // [...]". (Emphasis added here.)\r
+      //\r
+      // Normally we add memory resource descriptor HOBs in\r
+      // QemuInitializeRam(), and pre-allocate from those with memory\r
+      // allocation HOBs in InitializeRamRegions(). However, the MMCONFIG area\r
+      // is most definitely not RAM; so, as an exception, cover it with\r
+      // uncacheable reserved memory right here.\r
+      //\r
+      AddReservedMemoryBaseSizeHob (PciExBarBase, SIZE_256MB, FALSE);\r
+      BuildMemoryAllocationHob (PciExBarBase, SIZE_256MB,\r
+        EfiReservedMemoryType);\r
     }\r
     AddIoMemoryBaseSizeHob (PcdGet32(PcdCpuLocalApicBaseAddress), SIZE_1MB);\r
   }\r
     }\r
     AddIoMemoryBaseSizeHob (PcdGet32(PcdCpuLocalApicBaseAddress), SIZE_1MB);\r
   }\r
@@ -316,6 +343,47 @@ NoexecDxeInitialization (
   UPDATE_BOOLEAN_PCD_FROM_FW_CFG (PcdSetNxForStack);\r
 }\r
 \r
   UPDATE_BOOLEAN_PCD_FROM_FW_CFG (PcdSetNxForStack);\r
 }\r
 \r
+VOID\r
+PciExBarInitialization (\r
+  VOID\r
+  )\r
+{\r
+  union {\r
+    UINT64 Uint64;\r
+    UINT32 Uint32[2];\r
+  } PciExBarBase;\r
+\r
+  //\r
+  // We only support the 256MB size for the MMCONFIG area:\r
+  // 256 buses * 32 devices * 8 functions * 4096 bytes config space.\r
+  //\r
+  // The masks used below enforce the Q35 requirements that the MMCONFIG area\r
+  // be (a) correctly aligned -- here at 256 MB --, (b) located under 64 GB.\r
+  //\r
+  // Note that (b) also ensures that the minimum address width we have\r
+  // determined in AddressWidthInitialization(), i.e., 36 bits, will suffice\r
+  // for DXE's page tables to cover the MMCONFIG area.\r
+  //\r
+  PciExBarBase.Uint64 = FixedPcdGet64 (PcdPciExpressBaseAddress);\r
+  ASSERT ((PciExBarBase.Uint32[1] & MCH_PCIEXBAR_HIGHMASK) == 0);\r
+  ASSERT ((PciExBarBase.Uint32[0] & MCH_PCIEXBAR_LOWMASK) == 0);\r
+\r
+  //\r
+  // Clear the PCIEXBAREN bit first, before programming the high register.\r
+  //\r
+  PciWrite32 (DRAMC_REGISTER_Q35 (MCH_PCIEXBAR_LOW), 0);\r
+\r
+  //\r
+  // Program the high register. Then program the low register, setting the\r
+  // MMCONFIG area size and enabling decoding at once.\r
+  //\r
+  PciWrite32 (DRAMC_REGISTER_Q35 (MCH_PCIEXBAR_HIGH), PciExBarBase.Uint32[1]);\r
+  PciWrite32 (\r
+    DRAMC_REGISTER_Q35 (MCH_PCIEXBAR_LOW),\r
+    PciExBarBase.Uint32[0] | MCH_PCIEXBAR_BUS_FF | MCH_PCIEXBAR_EN\r
+    );\r
+}\r
+\r
 VOID\r
 MiscInitialization (\r
   VOID\r
 VOID\r
 MiscInitialization (\r
   VOID\r
@@ -393,6 +461,11 @@ MiscInitialization (
       POWER_MGMT_REGISTER_Q35 (ICH9_RCBA),\r
       ICH9_ROOT_COMPLEX_BASE | ICH9_RCBA_EN\r
       );\r
       POWER_MGMT_REGISTER_Q35 (ICH9_RCBA),\r
       ICH9_ROOT_COMPLEX_BASE | ICH9_RCBA_EN\r
       );\r
+\r
+    //\r
+    // Set PCI Express Register Range Base Address\r
+    //\r
+    PciExBarInitialization ();\r
   }\r
 }\r
 \r
   }\r
 }\r
 \r