]> git.proxmox.com Git - mirror_edk2.git/commitdiff
OvmfPkg/PciHotPlugInitDxe: translate QEMU's resource reservation hints
authorLaszlo Ersek <lersek@redhat.com>
Fri, 22 Sep 2017 09:23:43 +0000 (11:23 +0200)
committerLaszlo Ersek <lersek@redhat.com>
Tue, 3 Oct 2017 14:07:39 +0000 (16:07 +0200)
Parse QEMU_PCI_BRIDGE_CAPABILITY_RESOURCE_RESERVATION from the bridges'
conventional config spaces. Translate the fields as follows:

* BusNumbers:
  * 0 -- no reservation;
  * (-1) -- firmware default, i.e. no reservation;
  * otherwise -- reserve the requested value. (NB, bus number reservation
    is not supposed to work before
    <https://bugzilla.tianocore.org/show_bug.cgi?id=656> is fixed.)

* Io:
  * 0 -- no reservation;
  * (-1) -- keep our current default (512B);
  * otherwise -- round up the requested value and reserve that.

* NonPrefetchable32BitMmio:
  * 0 -- no reservation;
  * (-1) -- keep our current default (2MB);
  * otherwise -- round up the requested value and reserve that.

* Prefetchable32BitMmio:
  * 0 -- no reservation, proceed to Prefetchable64BitMmio;
  * (-1) -- firmware default, i.e. no reservation, proceed to
    Prefetchable64BitMmio;
  * otherwise -- round up the requested value and reserve that. (NB, if
    Prefetchable32BitMmio is reserved in addition to
    NonPrefetchable32BitMmio, then PciBusDxe currently runs into an
    assertion failure. Refer to
    <https://bugzilla.tianocore.org/show_bug.cgi?id=720>.)

* Prefetchable64BitMmio:
  * only reached if Prefetchable32BitMmio was not reserved;
  * 0 -- no reservation;
  * (-1) -- firmware default, i.e. no reservation;
  * otherwise -- round up the requested value and reserve that.

If QEMU_PCI_BRIDGE_CAPABILITY_RESOURCE_RESERVATION is missing, plus any
time the rounding fails, fall back to the current defaults.

Cc: Jordan Justen <jordan.l.justen@intel.com>
Cc: Marcel Apfelbaum <marcel@redhat.com>
Cc: Ruiyu Ni <ruiyu.ni@intel.com>
Contributed-under: TianoCore Contribution Agreement 1.1
Signed-off-by: Laszlo Ersek <lersek@redhat.com>
Reviewed-by: Jordan Justen <jordan.l.justen@intel.com>
OvmfPkg/PciHotPlugInitDxe/PciHotPlugInit.c
OvmfPkg/PciHotPlugInitDxe/PciHotPlugInit.inf

index 39646973794bfd04a4d2b29f1296dff7e34a48af..177e1a62120de3757b0cfef800e32acd47f112d7 100644 (file)
 **/\r
 \r
 #include <IndustryStandard/Acpi10.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/PciLib.h>\r
 #include <Library/UefiBootServicesTableLib.h>\r
 \r
 #include <Protocol/PciHotPlugInit.h>\r
@@ -246,6 +248,237 @@ HighBitSetRoundUp64 (
 }\r
 \r
 \r
+/**\r
+  Read a slice from conventional PCI config space at the given offset, then\r
+  advance the offset.\r
+\r
+  @param[in] PciAddress  The address of the PCI Device -- Bus, Device, Function\r
+                         -- in UEFI (not PciLib) encoding.\r
+\r
+  @param[in,out] Offset  On input, the offset in conventional PCI config space\r
+                         to start reading from. On output, the offset of the\r
+                         first byte that was not read.\r
+\r
+  @param[in] Size        The number of bytes to read.\r
+\r
+  @param[out] Buffer     On output, the bytes read from PCI config space are\r
+                         stored in this object.\r
+**/\r
+STATIC\r
+VOID\r
+ReadConfigSpace (\r
+  IN     CONST EFI_PCI_ROOT_BRIDGE_IO_PROTOCOL_PCI_ADDRESS *PciAddress,\r
+  IN OUT UINT8                                             *Offset,\r
+  IN     UINT8                                             Size,\r
+  OUT    VOID                                              *Buffer\r
+  )\r
+{\r
+  PciReadBuffer (\r
+    PCI_LIB_ADDRESS (\r
+      PciAddress->Bus,\r
+      PciAddress->Device,\r
+      PciAddress->Function,\r
+      *Offset\r
+      ),\r
+    Size,\r
+    Buffer\r
+    );\r
+  *Offset += Size;\r
+}\r
+\r
+\r
+/**\r
+  Convenience wrapper macro for ReadConfigSpace().\r
+\r
+  Given the following conditions:\r
+\r
+  - HeaderField is the first field in the structure pointed-to by Struct,\r
+\r
+  - Struct->HeaderField has been populated from the conventional PCI config\r
+    space of the PCI device identified by PciAddress,\r
+\r
+  - *Offset points one past HeaderField in the conventional PCI config space of\r
+    the PCI device identified by PciAddress,\r
+\r
+  populate the rest of *Struct from conventional PCI config space, starting at\r
+  *Offset. Finally, increment *Offset so that it point one past *Struct.\r
+\r
+  @param[in] PciAddress  The address of the PCI Device -- Bus, Device, Function\r
+                         -- in UEFI (not PciLib) encoding. Type: pointer to\r
+                         CONST EFI_PCI_ROOT_BRIDGE_IO_PROTOCOL_PCI_ADDRESS.\r
+\r
+  @param[in,out] Offset  On input, the offset in conventional PCI config space\r
+                         to start reading from; one past Struct->HeaderField.\r
+                         On output, the offset of the first byte that was not\r
+                         read; one past *Struct. Type: pointer to UINT8.\r
+\r
+  @param[out] Struct     The structure to complete. Type: pointer to structure\r
+                         object.\r
+\r
+  @param[in] HeaderField The name of the first field in *Struct, after which\r
+                         *Struct should be populated. Type: structure member\r
+                         identifier.\r
+**/\r
+#define COMPLETE_CONFIG_SPACE_STRUCT(PciAddress, Offset, Struct, HeaderField) \\r
+          ReadConfigSpace (                                                   \\r
+            (PciAddress),                                                     \\r
+            (Offset),                                                         \\r
+            (UINT8)(sizeof *(Struct) - sizeof ((Struct)->HeaderField)),       \\r
+            &((Struct)->HeaderField) + 1                                      \\r
+            )\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
+  This function performs as few config space reads as possible.\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. The contents of\r
+                         ReservationHint are now indeterminate.\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
+  UINT16 PciStatus;\r
+  UINT8  PciCapPtr;\r
+  UINT8  Offset;\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
+  // Check the Capabilities List bit in the PCI Status Register.\r
+  //\r
+  PciStatus = PciRead16 (\r
+                PCI_LIB_ADDRESS (\r
+                  HpcPciAddress->Bus,\r
+                  HpcPciAddress->Device,\r
+                  HpcPciAddress->Function,\r
+                  PCI_PRIMARY_STATUS_OFFSET\r
+                  )\r
+                );\r
+  if ((PciStatus & EFI_PCI_STATUS_CAPABILITY) == 0) {\r
+    return EFI_NOT_FOUND;\r
+  }\r
+\r
+  //\r
+  // Fetch the start of the Capabilities List.\r
+  //\r
+  PciCapPtr = PciRead8 (\r
+                PCI_LIB_ADDRESS (\r
+                  HpcPciAddress->Bus,\r
+                  HpcPciAddress->Device,\r
+                  HpcPciAddress->Function,\r
+                  PCI_CAPBILITY_POINTER_OFFSET\r
+                  )\r
+                );\r
+\r
+  //\r
+  // Scan the Capabilities List until we find the terminator element, or the\r
+  // Resource Reservation capability.\r
+  //\r
+  for (Offset = PciCapPtr & 0xFC;\r
+       Offset > 0;\r
+       Offset = ReservationHint->BridgeHdr.VendorHdr.Hdr.NextItemPtr & 0xFC) {\r
+    BOOLEAN EnoughRoom;\r
+\r
+    //\r
+    // Check if the Resource Reservation capability would fit into config space\r
+    // at this offset.\r
+    //\r
+    EnoughRoom = (BOOLEAN)(\r
+                   Offset <= PCI_MAX_CONFIG_OFFSET - sizeof *ReservationHint\r
+                   );\r
+\r
+    //\r
+    // Read the standard capability header so we can check the capability ID\r
+    // (if necessary) and advance to the next capability.\r
+    //\r
+    ReadConfigSpace (\r
+      HpcPciAddress,\r
+      &Offset,\r
+      (UINT8)sizeof ReservationHint->BridgeHdr.VendorHdr.Hdr,\r
+      &ReservationHint->BridgeHdr.VendorHdr.Hdr\r
+      );\r
+    if (!EnoughRoom ||\r
+        (ReservationHint->BridgeHdr.VendorHdr.Hdr.CapabilityID !=\r
+         EFI_PCI_CAPABILITY_ID_VENDOR)) {\r
+      continue;\r
+    }\r
+\r
+    //\r
+    // Read the rest of the vendor capability header so we can check the\r
+    // capability length.\r
+    //\r
+    COMPLETE_CONFIG_SPACE_STRUCT (\r
+      HpcPciAddress,\r
+      &Offset,\r
+      &ReservationHint->BridgeHdr.VendorHdr,\r
+      Hdr\r
+      );\r
+    if (ReservationHint->BridgeHdr.VendorHdr.Length !=\r
+        sizeof *ReservationHint) {\r
+      continue;\r
+    }\r
+\r
+    //\r
+    // Read the rest of the QEMU bridge capability header so we can check the\r
+    // capability type.\r
+    //\r
+    COMPLETE_CONFIG_SPACE_STRUCT (\r
+      HpcPciAddress,\r
+      &Offset,\r
+      &ReservationHint->BridgeHdr,\r
+      VendorHdr\r
+      );\r
+    if (ReservationHint->BridgeHdr.Type !=\r
+        QEMU_PCI_BRIDGE_CAPABILITY_TYPE_RESOURCE_RESERVATION) {\r
+      continue;\r
+    }\r
+\r
+    //\r
+    // Read the body of the reservation hint.\r
+    //\r
+    COMPLETE_CONFIG_SPACE_STRUCT (\r
+      HpcPciAddress,\r
+      &Offset,\r
+      ReservationHint,\r
+      BridgeHdr\r
+      );\r
+    return EFI_SUCCESS;\r
+  }\r
+\r
+  return EFI_NOT_FOUND;\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 +635,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 +676,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 +811,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
index e0ec9baae1c274a9cd704cd6ec9147cf57792fba..38043986eb6706af368c93b02fd1063543a1a116 100644 (file)
@@ -27,6 +27,7 @@
 [Packages]\r
   MdeModulePkg/MdeModulePkg.dec\r
   MdePkg/MdePkg.dec\r
+  OvmfPkg/OvmfPkg.dec\r
 \r
 [LibraryClasses]\r
   BaseLib\r
@@ -34,6 +35,7 @@
   DebugLib\r
   DevicePathLib\r
   MemoryAllocationLib\r
+  PciLib\r
   UefiBootServicesTableLib\r
   UefiDriverEntryPoint\r
 \r