]> git.proxmox.com Git - mirror_edk2.git/blobdiff - OvmfPkg/PciHotPlugInitDxe/PciHotPlugInit.c
UefiCpuPkg CpuCommFeaturesLib: Fix GP fault issue about ProcTrace
[mirror_edk2.git] / OvmfPkg / PciHotPlugInitDxe / PciHotPlugInit.c
index 39646973794bfd04a4d2b29f1296dff7e34a48af..b64dba1cc64f48e95611e84fa0671651afbaa28b 100644 (file)
@@ -4,27 +4,32 @@
 \r
   Copyright (C) 2016, Red Hat, Inc.\r
 \r
-  This program and the accompanying materials are licensed and made available\r
-  under the terms and conditions of the BSD License which accompanies this\r
-  distribution. The full text of the license may be found at\r
-  http://opensource.org/licenses/bsd-license.php\r
-\r
-  THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, WITHOUT\r
-  WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.\r
+  SPDX-License-Identifier: BSD-2-Clause-Patent\r
 **/\r
 \r
 #include <IndustryStandard/Acpi10.h>\r
+#include <IndustryStandard/Q35MchIch9.h>\r
+#include <IndustryStandard/QemuPciBridgeCapabilities.h>\r
 \r
 #include <Library/BaseLib.h>\r
 #include <Library/BaseMemoryLib.h>\r
 #include <Library/DebugLib.h>\r
 #include <Library/DevicePathLib.h>\r
 #include <Library/MemoryAllocationLib.h>\r
+#include <Library/PciCapLib.h>\r
+#include <Library/PciCapPciSegmentLib.h>\r
+#include <Library/PciLib.h>\r
 #include <Library/UefiBootServicesTableLib.h>\r
 \r
 #include <Protocol/PciHotPlugInit.h>\r
 #include <Protocol/PciRootBridgeIo.h>\r
 \r
+//\r
+// TRUE if the PCI platform supports extended config space, FALSE otherwise.\r
+//\r
+STATIC BOOLEAN mPciExtConfSpaceSupported;\r
+\r
+\r
 //\r
 // The protocol interface this driver produces.\r
 //\r
@@ -246,6 +251,153 @@ HighBitSetRoundUp64 (
 }\r
 \r
 \r
+/**\r
+  Look up the QEMU-specific Resource Reservation capability in the conventional\r
+  config space of a Hotplug Controller (that is, PCI Bridge).\r
+\r
+  On error, the contents of ReservationHint are indeterminate.\r
+\r
+  @param[in] HpcPciAddress     The address of the PCI Bridge -- Bus, Device,\r
+                               Function -- in UEFI (not PciLib) encoding.\r
+\r
+  @param[out] ReservationHint  The caller-allocated capability structure to\r
+                               populate from the PCI Bridge's config space.\r
+\r
+  @retval EFI_SUCCESS    The capability has been found, ReservationHint has\r
+                         been populated.\r
+\r
+  @retval EFI_NOT_FOUND  The capability is missing.\r
+\r
+  @return                Error codes from PciCapPciSegmentLib and PciCapLib.\r
+**/\r
+STATIC\r
+EFI_STATUS\r
+QueryReservationHint (\r
+  IN  CONST EFI_PCI_ROOT_BRIDGE_IO_PROTOCOL_PCI_ADDRESS *HpcPciAddress,\r
+  OUT QEMU_PCI_BRIDGE_CAPABILITY_RESOURCE_RESERVATION   *ReservationHint\r
+)\r
+{\r
+  UINT16       PciVendorId;\r
+  EFI_STATUS   Status;\r
+  PCI_CAP_DEV  *PciDevice;\r
+  PCI_CAP_LIST *CapList;\r
+  UINT16       VendorInstance;\r
+  PCI_CAP      *VendorCap;\r
+\r
+  //\r
+  // Check the vendor identifier.\r
+  //\r
+  PciVendorId = PciRead16 (\r
+                  PCI_LIB_ADDRESS (\r
+                    HpcPciAddress->Bus,\r
+                    HpcPciAddress->Device,\r
+                    HpcPciAddress->Function,\r
+                    PCI_VENDOR_ID_OFFSET\r
+                    )\r
+                  );\r
+  if (PciVendorId != QEMU_PCI_BRIDGE_VENDOR_ID_REDHAT) {\r
+    return EFI_NOT_FOUND;\r
+  }\r
+\r
+  //\r
+  // Parse the capabilities lists.\r
+  //\r
+  Status = PciCapPciSegmentDeviceInit (\r
+             mPciExtConfSpaceSupported ? PciCapExtended : PciCapNormal,\r
+             0, // Segment\r
+             HpcPciAddress->Bus,\r
+             HpcPciAddress->Device,\r
+             HpcPciAddress->Function,\r
+             &PciDevice\r
+             );\r
+  if (EFI_ERROR (Status)) {\r
+    return Status;\r
+  }\r
+  Status = PciCapListInit (PciDevice, &CapList);\r
+  if (EFI_ERROR (Status)) {\r
+    goto UninitPciDevice;\r
+  }\r
+\r
+  //\r
+  // Scan the vendor capability instances for the Resource Reservation\r
+  // capability.\r
+  //\r
+  VendorInstance = 0;\r
+  for (;;) {\r
+    UINT8 VendorLength;\r
+    UINT8 BridgeCapType;\r
+\r
+    Status = PciCapListFindCap (\r
+               CapList,\r
+               PciCapNormal,\r
+               EFI_PCI_CAPABILITY_ID_VENDOR,\r
+               VendorInstance++,\r
+               &VendorCap\r
+               );\r
+    if (EFI_ERROR (Status)) {\r
+      goto UninitCapList;\r
+    }\r
+\r
+    //\r
+    // Check the vendor capability length.\r
+    //\r
+    Status = PciCapRead (\r
+               PciDevice,\r
+               VendorCap,\r
+               OFFSET_OF (EFI_PCI_CAPABILITY_VENDOR_HDR, Length),\r
+               &VendorLength,\r
+               sizeof VendorLength\r
+               );\r
+    if (EFI_ERROR (Status)) {\r
+      goto UninitCapList;\r
+    }\r
+    if (VendorLength != sizeof *ReservationHint) {\r
+      continue;\r
+    }\r
+\r
+    //\r
+    // Check the vendor bridge capability type.\r
+    //\r
+    Status = PciCapRead (\r
+               PciDevice,\r
+               VendorCap,\r
+               OFFSET_OF (QEMU_PCI_BRIDGE_CAPABILITY_HDR, Type),\r
+               &BridgeCapType,\r
+               sizeof BridgeCapType\r
+               );\r
+    if (EFI_ERROR (Status)) {\r
+      goto UninitCapList;\r
+    }\r
+    if (BridgeCapType ==\r
+        QEMU_PCI_BRIDGE_CAPABILITY_TYPE_RESOURCE_RESERVATION) {\r
+      //\r
+      // We have a match.\r
+      //\r
+      break;\r
+    }\r
+  }\r
+\r
+  //\r
+  // Populate ReservationHint.\r
+  //\r
+  Status = PciCapRead (\r
+             PciDevice,\r
+             VendorCap,\r
+             0, // SourceOffsetInCap\r
+             ReservationHint,\r
+             sizeof *ReservationHint\r
+             );\r
+\r
+UninitCapList:\r
+  PciCapListUninit (CapList);\r
+\r
+UninitPciDevice:\r
+  PciCapPciSegmentDeviceUninit (PciDevice);\r
+\r
+  return Status;\r
+}\r
+\r
+\r
 /**\r
   Returns a list of root Hot Plug Controllers (HPCs) that require\r
   initialization during the boot process.\r
@@ -402,16 +554,19 @@ GetResourcePadding (
   OUT EFI_HPC_PADDING_ATTRIBUTES     *Attributes\r
   )\r
 {\r
+  EFI_PCI_ROOT_BRIDGE_IO_PROTOCOL_PCI_ADDRESS     *Address;\r
   BOOLEAN                                         DefaultIo;\r
   BOOLEAN                                         DefaultMmio;\r
   RESOURCE_PADDING                                ReservationRequest;\r
   EFI_ACPI_ADDRESS_SPACE_DESCRIPTOR               *FirstResource;\r
+  EFI_STATUS                                      ReservationHintStatus;\r
+  QEMU_PCI_BRIDGE_CAPABILITY_RESOURCE_RESERVATION ReservationHint;\r
+\r
+  Address = (EFI_PCI_ROOT_BRIDGE_IO_PROTOCOL_PCI_ADDRESS *)&HpcPciAddress;\r
 \r
   DEBUG_CODE (\r
-    EFI_PCI_ROOT_BRIDGE_IO_PROTOCOL_PCI_ADDRESS *Address;\r
     CHAR16                                      *DevicePathString;\r
 \r
-    Address = (EFI_PCI_ROOT_BRIDGE_IO_PROTOCOL_PCI_ADDRESS *)&HpcPciAddress;\r
     DevicePathString = ConvertDevicePathToText (HpcDevicePath, FALSE, FALSE);\r
 \r
     DEBUG ((EFI_D_VERBOSE, "%a: Address=%02x:%02x.%x DevicePath=%s\n",\r
@@ -440,8 +595,134 @@ GetResourcePadding (
                   ARRAY_SIZE (ReservationRequest.Padding);\r
 \r
   //\r
-  // (b) Reserve IO space.\r
+  // Try to get the QEMU-specific Resource Reservation capability.\r
   //\r
+  ReservationHintStatus = QueryReservationHint (Address, &ReservationHint);\r
+  if (!EFI_ERROR (ReservationHintStatus)) {\r
+    INTN HighBit;\r
+\r
+    DEBUG ((\r
+      DEBUG_VERBOSE,\r
+      "%a: BusNumbers=0x%x Io=0x%Lx NonPrefetchable32BitMmio=0x%x\n"\r
+      "%a: Prefetchable32BitMmio=0x%x Prefetchable64BitMmio=0x%Lx\n",\r
+      __FUNCTION__,\r
+      ReservationHint.BusNumbers,\r
+      ReservationHint.Io,\r
+      ReservationHint.NonPrefetchable32BitMmio,\r
+      __FUNCTION__,\r
+      ReservationHint.Prefetchable32BitMmio,\r
+      ReservationHint.Prefetchable64BitMmio\r
+      ));\r
+\r
+    //\r
+    // (a) Reserve bus numbers.\r
+    //\r
+    switch (ReservationHint.BusNumbers) {\r
+    case 0:\r
+      //\r
+      // No reservation needed.\r
+      //\r
+      break;\r
+    case MAX_UINT32:\r
+      //\r
+      // Firmware default (unspecified). Treat it as "no reservation needed".\r
+      //\r
+      break;\r
+    default:\r
+      //\r
+      // Request the specified amount.\r
+      //\r
+      --FirstResource;\r
+      FirstResource->ResType = ACPI_ADDRESS_SPACE_TYPE_BUS;\r
+      FirstResource->AddrLen = ReservationHint.BusNumbers;\r
+      break;\r
+    }\r
+\r
+    //\r
+    // (b) Reserve IO space.\r
+    //\r
+    switch (ReservationHint.Io) {\r
+    case 0:\r
+      //\r
+      // No reservation needed, disable our built-in.\r
+      //\r
+      DefaultIo = FALSE;\r
+      break;\r
+    case MAX_UINT64:\r
+      //\r
+      // Firmware default (unspecified). Stick with our built-in.\r
+      //\r
+      break;\r
+    default:\r
+      //\r
+      // Round the specified amount up to the next power of two. If rounding is\r
+      // successful, reserve the rounded value. Fall back to the default\r
+      // otherwise.\r
+      //\r
+      HighBit = HighBitSetRoundUp64 (ReservationHint.Io);\r
+      if (HighBit != -1) {\r
+        SetIoPadding (--FirstResource, (UINTN)HighBit);\r
+        DefaultIo = FALSE;\r
+      }\r
+      break;\r
+    }\r
+\r
+    //\r
+    // (c) Reserve non-prefetchable MMIO space (32-bit only).\r
+    //\r
+    switch (ReservationHint.NonPrefetchable32BitMmio) {\r
+    case 0:\r
+      //\r
+      // No reservation needed, disable our built-in.\r
+      //\r
+      DefaultMmio = FALSE;\r
+      break;\r
+    case MAX_UINT32:\r
+      //\r
+      // Firmware default (unspecified). Stick with our built-in.\r
+      //\r
+      break;\r
+    default:\r
+      //\r
+      // Round the specified amount up to the next power of two. If rounding is\r
+      // successful, reserve the rounded value. Fall back to the default\r
+      // otherwise.\r
+      //\r
+      HighBit = HighBitSetRoundUp32 (ReservationHint.NonPrefetchable32BitMmio);\r
+      if (HighBit != -1) {\r
+        SetMmioPadding (--FirstResource, FALSE, TRUE, (UINTN)HighBit);\r
+        DefaultMmio = FALSE;\r
+      }\r
+      break;\r
+    }\r
+\r
+    //\r
+    // (d) Reserve prefetchable MMIO space (either 32-bit or 64-bit, never\r
+    // both).\r
+    //\r
+    // For either space, we treat 0 as "no reservation needed", and the maximum\r
+    // value as "firmware default". The latter is unspecified, and we interpret\r
+    // it as the former.\r
+    //\r
+    // Otherwise, round the specified amount up to the next power of two. If\r
+    // rounding is successful, reserve the rounded value. Do not reserve\r
+    // prefetchable MMIO space otherwise.\r
+    //\r
+    if (ReservationHint.Prefetchable32BitMmio > 0 &&\r
+        ReservationHint.Prefetchable32BitMmio < MAX_UINT32) {\r
+      HighBit = HighBitSetRoundUp32 (ReservationHint.Prefetchable32BitMmio);\r
+      if (HighBit != -1) {\r
+        SetMmioPadding (--FirstResource, TRUE, TRUE, (UINTN)HighBit);\r
+      }\r
+    } else if (ReservationHint.Prefetchable64BitMmio > 0 &&\r
+               ReservationHint.Prefetchable64BitMmio < MAX_UINT64) {\r
+      HighBit = HighBitSetRoundUp64 (ReservationHint.Prefetchable64BitMmio);\r
+      if (HighBit != -1) {\r
+        SetMmioPadding (--FirstResource, TRUE, FALSE, (UINTN)HighBit);\r
+      }\r
+    }\r
+  }\r
+\r
   if (DefaultIo) {\r
     //\r
     // Request defaults.\r
@@ -449,9 +730,6 @@ GetResourcePadding (
     SetIoPadding (--FirstResource, (UINTN)HighBitSetRoundUp64 (512));\r
   }\r
 \r
-  //\r
-  // (c) Reserve non-prefetchable MMIO space (32-bit only).\r
-  //\r
   if (DefaultMmio) {\r
     //\r
     // Request defaults.\r
@@ -511,6 +789,8 @@ DriverInitialize (
 {\r
   EFI_STATUS Status;\r
 \r
+  mPciExtConfSpaceSupported = (PcdGet16 (PcdOvmfHostBridgePciDevId) ==\r
+                               INTEL_Q35_MCH_DEVICE_ID);\r
   mPciHotPlugInit.GetRootHpcList = GetRootHpcList;\r
   mPciHotPlugInit.InitializeRootHpc = InitializeRootHpc;\r
   mPciHotPlugInit.GetResourcePadding = GetResourcePadding;\r