All of lore.kernel.org
 help / color / mirror / Atom feed
* [RFC PATCH v2 0/2] MdeModulePkg, OvmfPkg: fixed PCI BAR placement
@ 2026-08-27  0:45 Tushar Dave
  2026-08-27  0:45 ` [RFC PATCH v2 1/2] MdeModulePkg/PciBusDxe: honor fixed BAR addresses from IncompatiblePci Tushar Dave
  2026-08-27  0:45 ` [RFC PATCH v2 2/2] ArmVirtPkg, OvmfPkg: add QemuFixedBarsDxe for fixed PCI BAR placement Tushar Dave via qemu development
  0 siblings, 2 replies; 3+ messages in thread
From: Tushar Dave @ 2026-08-27  0:45 UTC (permalink / raw)
  To: devel
  Cc: gaoliming, ardb+tianocore, sami.mujawar, jiewen.yao, kraxel,
	qemu-devel, alwilliamson, jgg, skolothumtho

This is the EDK2 companion to the QEMU RFC v2 series [1].

This series implements the firmware side of fixed PCI BAR placement.
QEMU exports validated fixed BAR addresses through the
`etc/fixed-bars` fw_cfg file. This series consumes that information
in two steps.

Patch 1 adds minimal changes to `PciBusDxe` to honor fixed BAR
addresses supplied through `EFI_INCOMPATIBLE_PCI_DEVICE_SUPPORT_PROTOCOL`.
When both `_MIF` and `_MAF` flags are set in an ACPI address
descriptor returned by `CheckDevice()`, the described address is
treated as a fixed requirement. `UpdatePciInfo()` records the fixed
address on the BAR and `ProgramBar()` uses it in place of the
GCD-allocated address.

Patch 2 introduces `QemuFixedBarsDxe`, a new OvmfPkg driver that
installs `EFI_INCOMPATIBLE_PCI_DEVICE_SUPPORT_PROTOCOL` before
`PciBusDxe` starts. It reads the `etc/fixed-bars` fw_cfg blob,
reserves the required address ranges in the GCD, and returns fixed
BAR addresses through `CheckDevice()`. After `PciEnumerationComplete`,
it reprograms bridge memory windows along each fixed root-port
hierarchy to cover the fixed BAR placements.

Testing:

The series was tested together with the QEMU companion patches on
the AArch64 `virt` machine using multiple PCI topologies, including
emulated devices, VFIO passthrough devices, PCIe switches, and PCIe
root ports configured for fixed BAR placement.

A git branch with this series applied is available at:
https://github.com/tdavenvidia/edk2/tree/RFC-v2-fixed-bar-upstream

[1] companion QEMU RFC v2
https://github.com/tdavenvidia/upstream-qemu/tree/RFC-v2-fixed-bar-upstream

Tushar Dave (2):
  MdeModulePkg/PciBusDxe: honor fixed BAR addresses from IncompatiblePci
  ArmVirtPkg,OvmfPkg: add QemuFixedBarsDxe for fixed PCI BAR placement

 ArmVirtPkg/ArmVirtQemu.dsc                    |   1 +
 ArmVirtPkg/ArmVirtQemuFvMain.fdf.inc          |   1 +
 MdeModulePkg/Bus/Pci/PciBusDxe/PciBus.h       |   1 +
 .../Bus/Pci/PciBusDxe/PciEnumeratorSupport.c  |  12 +
 .../Bus/Pci/PciBusDxe/PciResourceSupport.c    |  13 +
 OvmfPkg/QemuFixedBarsDxe/QemuFixedBarsDxe.c   | 772 ++++++++++++++++++
 OvmfPkg/QemuFixedBarsDxe/QemuFixedBarsDxe.inf |  43 +
 7 files changed, 843 insertions(+)
 create mode 100644 OvmfPkg/QemuFixedBarsDxe/QemuFixedBarsDxe.c
 create mode 100644 OvmfPkg/QemuFixedBarsDxe/QemuFixedBarsDxe.inf

-- 
2.34.1



^ permalink raw reply	[flat|nested] 3+ messages in thread

* [RFC PATCH v2 1/2] MdeModulePkg/PciBusDxe: honor fixed BAR addresses from IncompatiblePci
  2026-08-27  0:45 [RFC PATCH v2 0/2] MdeModulePkg, OvmfPkg: fixed PCI BAR placement Tushar Dave
@ 2026-08-27  0:45 ` Tushar Dave
  2026-08-27  0:45 ` [RFC PATCH v2 2/2] ArmVirtPkg, OvmfPkg: add QemuFixedBarsDxe for fixed PCI BAR placement Tushar Dave via qemu development
  1 sibling, 0 replies; 3+ messages in thread
From: Tushar Dave @ 2026-08-27  0:45 UTC (permalink / raw)
  To: devel
  Cc: gaoliming, ardb+tianocore, sami.mujawar, jiewen.yao, kraxel,
	qemu-devel, alwilliamson, jgg, skolothumtho

EFI_INCOMPATIBLE_PCI_DEVICE_SUPPORT_PROTOCOL returns ACPI address
descriptors describing PCI resource requirements. When both _MIF (BIT2)
and _MAF (BIT3) are set in the descriptor's GenFlag, the descriptor
represents a fixed address range.

Extend PCI_BAR with FixedBaseAddress and update the PCI bus driver to
preserve and use this information:

  * UpdatePciInfo() detects _MIF|_MAF, records AddrRangeMin as
    FixedBaseAddress, and marks the BAR as fixed.

  * ProgramBar() programs FixedBaseAddress into the BAR register instead
    of the resource allocator selected address for fixed BARs.

No functional change occurs unless a platform driver supplies fixed BAR
descriptors through EFI_INCOMPATIBLE_PCI_DEVICE_SUPPORT_PROTOCOL.

Signed-off-by: Tushar Dave <tdave@nvidia.com>
---
 MdeModulePkg/Bus/Pci/PciBusDxe/PciBus.h             |  1 +
 .../Bus/Pci/PciBusDxe/PciEnumeratorSupport.c        | 12 ++++++++++++
 MdeModulePkg/Bus/Pci/PciBusDxe/PciResourceSupport.c | 13 +++++++++++++
 3 files changed, 26 insertions(+)

diff --git a/MdeModulePkg/Bus/Pci/PciBusDxe/PciBus.h b/MdeModulePkg/Bus/Pci/PciBusDxe/PciBus.h
index 01bce28b1b..4edf45119f 100644
--- a/MdeModulePkg/Bus/Pci/PciBusDxe/PciBus.h
+++ b/MdeModulePkg/Bus/Pci/PciBusDxe/PciBus.h
@@ -96,6 +96,7 @@ struct _PCI_BAR {
   UINT64          Alignment;
   PCI_BAR_TYPE    BarType;
   BOOLEAN         BarTypeFixed;
+  UINT64          FixedBaseAddress;
   UINT16          Offset;
 };
 
diff --git a/MdeModulePkg/Bus/Pci/PciBusDxe/PciEnumeratorSupport.c b/MdeModulePkg/Bus/Pci/PciBusDxe/PciEnumeratorSupport.c
index ae94462f60..da16355d9f 100644
--- a/MdeModulePkg/Bus/Pci/PciBusDxe/PciEnumeratorSupport.c
+++ b/MdeModulePkg/Bus/Pci/PciBusDxe/PciEnumeratorSupport.c
@@ -1638,6 +1638,18 @@ UpdatePciInfo (
         if (Ptr->AddrLen != 0) {
           PciIoDevice->PciBar[BarIndex].Length = Ptr->AddrLen;
         }
+
+        //
+        // When both _MIF (BIT2) and _MAF (BIT3) are set in GenFlag the
+        // descriptor carries a fixed base address in AddrRangeMin.
+        //
+        if ((Ptr->GenFlag & (BIT2 | BIT3)) == (BIT2 | BIT3)) {
+          PciIoDevice->PciBar[BarIndex].BarTypeFixed     = TRUE;
+          PciIoDevice->PciBar[BarIndex].FixedBaseAddress = Ptr->AddrRangeMin;
+          DEBUG ((DEBUG_INFO,
+                  "PciBus: UpdatePciInfo BAR[%u] fixed address=0x%016Lx\n",
+                  (UINT32)BarIndex, Ptr->AddrRangeMin));
+        }
       }
     }
 
diff --git a/MdeModulePkg/Bus/Pci/PciBusDxe/PciResourceSupport.c b/MdeModulePkg/Bus/Pci/PciBusDxe/PciResourceSupport.c
index 8f3cff3eaa..5cc3ae797d 100644
--- a/MdeModulePkg/Bus/Pci/PciBusDxe/PciResourceSupport.c
+++ b/MdeModulePkg/Bus/Pci/PciBusDxe/PciResourceSupport.c
@@ -1276,6 +1276,19 @@ ProgramBar (
 
   Address = Base + Node->Offset;
 
+  //
+  // If EFI_INCOMPATIBLE_PCI_DEVICE_SUPPORT_PROTOCOL supplied a fixed
+  // base address (_MIF|_MAF in the ACPI descriptor), use it directly.
+  //
+  if (Node->PciDev->PciBar[Node->Bar].BarTypeFixed &&
+      (Node->PciDev->PciBar[Node->Bar].FixedBaseAddress != 0))
+  {
+    Address = Node->PciDev->PciBar[Node->Bar].FixedBaseAddress;
+    DEBUG ((DEBUG_INFO,
+            "PciBus: BAR[%d] fixed address 0x%016Lx (GCD 0x%016Lx)\n",
+            Node->Bar, Address, Base + Node->Offset));
+  }
+
   //
   // Indicate pci bus driver has allocated
   // resource for this device
-- 
2.34.1



^ permalink raw reply related	[flat|nested] 3+ messages in thread

* [RFC PATCH v2 2/2] ArmVirtPkg, OvmfPkg: add QemuFixedBarsDxe for fixed PCI BAR placement
  2026-08-27  0:45 [RFC PATCH v2 0/2] MdeModulePkg, OvmfPkg: fixed PCI BAR placement Tushar Dave
  2026-08-27  0:45 ` [RFC PATCH v2 1/2] MdeModulePkg/PciBusDxe: honor fixed BAR addresses from IncompatiblePci Tushar Dave
@ 2026-08-27  0:45 ` Tushar Dave via qemu development
  1 sibling, 0 replies; 3+ messages in thread
From: Tushar Dave via qemu development @ 2026-08-27  0:45 UTC (permalink / raw)
  To: devel
  Cc: gaoliming, ardb+tianocore, sami.mujawar, jiewen.yao, kraxel,
	qemu-devel, alwilliamson, jgg, skolothumtho

Add QemuFixedBarsDxe, a DXE driver that enables fixed PCI BAR placement
using metadata provided by QEMU through the "etc/fixed-bars" fw_cfg
file.

The driver installs EFI_INCOMPATIBLE_PCI_DEVICE_SUPPORT_PROTOCOL before
PciBusDxe starts so fixed BAR addresses can be returned during
CheckDevice(). PciBusDxe then programs those addresses directly when
assigning BAR resources.

The driver also reserves the fixed BAR address ranges in GCD before PCI
enumeration and, after enumeration completes, updates PCI bridge memory
windows to cover the programmed BAR ranges.

Signed-off-by: Tushar Dave <tdave@nvidia.com>
---
 ArmVirtPkg/ArmVirtQemu.dsc                    |   1 +
 ArmVirtPkg/ArmVirtQemuFvMain.fdf.inc          |   1 +
 OvmfPkg/QemuFixedBarsDxe/QemuFixedBarsDxe.c   | 772 ++++++++++++++++++
 OvmfPkg/QemuFixedBarsDxe/QemuFixedBarsDxe.inf |  43 +
 4 files changed, 817 insertions(+)
 create mode 100644 OvmfPkg/QemuFixedBarsDxe/QemuFixedBarsDxe.c
 create mode 100644 OvmfPkg/QemuFixedBarsDxe/QemuFixedBarsDxe.inf

diff --git a/ArmVirtPkg/ArmVirtQemu.dsc b/ArmVirtPkg/ArmVirtQemu.dsc
index 0212be4a33..58164d5418 100644
--- a/ArmVirtPkg/ArmVirtQemu.dsc
+++ b/ArmVirtPkg/ArmVirtQemu.dsc
@@ -469,6 +469,7 @@
   #
   # PCI support
   #
+  OvmfPkg/QemuFixedBarsDxe/QemuFixedBarsDxe.inf
   MdeModulePkg/Bus/Pci/PciHostBridgeDxe/PciHostBridgeDxe.inf
 
   #
diff --git a/ArmVirtPkg/ArmVirtQemuFvMain.fdf.inc b/ArmVirtPkg/ArmVirtQemuFvMain.fdf.inc
index ad158f57f1..879165de3f 100644
--- a/ArmVirtPkg/ArmVirtQemuFvMain.fdf.inc
+++ b/ArmVirtPkg/ArmVirtQemuFvMain.fdf.inc
@@ -157,6 +157,7 @@ READ_LOCK_STATUS   = TRUE
   # PCI support
   #
   INF UefiCpuPkg/CpuMmio2Dxe/CpuMmio2Dxe.inf
+  INF OvmfPkg/QemuFixedBarsDxe/QemuFixedBarsDxe.inf
   INF MdeModulePkg/Bus/Pci/PciHostBridgeDxe/PciHostBridgeDxe.inf
   INF MdeModulePkg/Bus/Pci/PciBusDxe/PciBusDxe.inf
   INF OvmfPkg/PciHotPlugInitDxe/PciHotPlugInit.inf
diff --git a/OvmfPkg/QemuFixedBarsDxe/QemuFixedBarsDxe.c b/OvmfPkg/QemuFixedBarsDxe/QemuFixedBarsDxe.c
new file mode 100644
index 0000000000..1ae45e1079
--- /dev/null
+++ b/OvmfPkg/QemuFixedBarsDxe/QemuFixedBarsDxe.c
@@ -0,0 +1,772 @@
+/** @file
+  QemuFixedBarsDxe implements EFI_INCOMPATIBLE_PCI_DEVICE_SUPPORT_PROTOCOL
+  to place PCI BAR addresses at fixed addresses using the
+  etc/fixed-bars fw_cfg blob provided by QEMU.
+
+  Copyright (c) 2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved.<BR>
+
+  SPDX-License-Identifier: BSD-2-Clause-Patent
+**/
+
+#include <PiDxe.h>
+#include <IndustryStandard/Acpi.h>
+#include <IndustryStandard/Pci.h>
+#include <Library/BaseMemoryLib.h>
+#include <Library/DebugLib.h>
+#include <Library/DxeServicesTableLib.h>
+#include <Library/MemoryAllocationLib.h>
+#include <Library/QemuFwCfgLib.h>
+#include <Library/UefiBootServicesTableLib.h>
+#include <Library/UefiDriverEntryPoint.h>
+#include <Protocol/IncompatiblePciDeviceSupport.h>
+#include <Protocol/PciEnumerationComplete.h>
+#include <Protocol/PciIo.h>
+
+/* byte-for-byte identical to QEMU's structs in hw/pci/pci-fixed-bar.h */
+#pragma pack (1)
+
+typedef struct {
+  UINT32    Version;
+  UINT32    NumDevices;
+} QEMU_FIXED_BARS_HDR;                       /* 8 bytes */
+
+typedef struct {
+  UINT16    VendorId;
+  UINT16    DeviceId;
+  UINT8     DevFlags;
+  UINT8     RpBus;
+  UINT8     NumBars;
+  UINT8     Reserved;
+} QEMU_FIXED_BARS_DEVICE;                    /* 8 bytes */
+
+typedef struct {
+  UINT8     Bar;
+  UINT8     Reserved[3];
+  UINT32    Flags;
+  UINT64    Address;
+  UINT64    Size;
+} QEMU_FIXED_BARS_BAR;                       /* 24 bytes */
+
+#pragma pack ()
+
+#define QEMU_FIXED_BARS_VERSION      1
+#define QEMU_FIXED_BAR_F_MEM64       BIT0
+#define QEMU_FIXED_BAR_F_PREF        BIT1
+#define QEMU_FIXED_BARS_DEV_F_FIXED  BIT0
+
+/*
+ * ACPI general-flag bits (ACPI 6.x 6.4.3.5.1):
+ *   BIT2 = _MIF (minimum address fixed)
+ *   BIT3 = _MAF (maximum address fixed)
+ * Both set means AddrRangeMin carries the exact required base address.
+ */
+#define ACPI_ADDR_FLAG_FIXED  (BIT2 | BIT3)
+
+/*
+ * P2P bridge memory window register offsets.
+ * PCI Local Bus Specification 2.2, Section 3.2.5.6.
+ */
+#define PPB_MEM32_BASE_OFFSET       OFFSET_OF (PCI_TYPE01, Bridge.MemoryBase)
+#define PPB_MEM32_LIMIT_OFFSET      OFFSET_OF (PCI_TYPE01, Bridge.MemoryLimit)
+#define PPB_PREF_MEM_BASE_OFFSET    OFFSET_OF (PCI_TYPE01, Bridge.PrefetchableMemoryBase)
+#define PPB_PREF_MEM_LIMIT_OFFSET   OFFSET_OF (PCI_TYPE01, Bridge.PrefetchableMemoryLimit)
+#define PPB_PREF_BASE_HI32_OFFSET   OFFSET_OF (PCI_TYPE01, Bridge.PrefetchableBaseUpper32)
+#define PPB_PREF_LIMIT_HI32_OFFSET  OFFSET_OF (PCI_TYPE01, Bridge.PrefetchableLimitUpper32)
+
+/* QFBD = Qemu Fixed Bars Dxe; used as the tag in this file's DEBUG() messages. */
+/* Raw blob kept in memory; device entries point into it. */
+STATIC UINT8              *mBlobData;
+STATIC QEMU_FIXED_BARS_HDR mHdr;
+
+/* Pre-parsed per-device index built from the blob at startup. */
+typedef struct {
+  UINT16                VendorId;
+  UINT16                DeviceId;
+  UINT8                 DevFlags;
+  UINT8                 RpBus;    /* primary bus of the root port */
+  UINT8                 NumBars;
+  QEMU_FIXED_BARS_BAR  *Bars;    /* pointer into mBlobData */
+} QFBD_DEVICE;
+
+/*
+ * Per-(VendorId, DeviceId) class. CheckDevice() is called once per PCI
+ * function and once per scan pass. Different VID:DID classes must advance
+ * their counters independently so that one class cannot disturb the
+ * position of another across scan passes.
+ *
+ * Assumptions:
+ *   - QEMU emits blob entries in the same order PciBusDxe discovers devices.
+ *   - For identical VID:DID devices, discovery order is stable across passes.
+ *   - The counter is maintained per VID:DID class, not globally.
+ */
+typedef struct {
+  UINT16    VendorId;
+  UINT16    DeviceId;
+  UINTN     Count;       /* number of devices with this VID:DID in the blob */
+  UINTN     CurrentIdx;  /* next entry to serve; wraps at Count */
+} QFBD_DEVICE_CLASS;
+
+STATIC QFBD_DEVICE        *mDevices;
+STATIC UINTN               mNumDevices;
+STATIC QFBD_DEVICE_CLASS  *mClasses;   /* heap-allocated; mNumDevices entries max */
+STATIC UINTN               mNumClasses;
+
+STATIC VOID
+ReserveHpaRanges (VOID)
+{
+  UINTN       DevIdx;
+  UINTN       BarIdx;
+  UINT64      Min64;
+  UINT64      Max64;
+  UINT64      Min32;
+  UINT64      Max32;
+  UINT64      Gran;
+  UINT64      Base;
+  UINT64      Size;
+  EFI_STATUS  Status;
+
+  Min64 = MAX_UINT64;
+  Max64 = 0;
+  Min32 = MAX_UINT64;
+  Max32 = 0;
+  Gran  = SIZE_1MB;
+
+  for (DevIdx = 0; DevIdx < mNumDevices; DevIdx++) {
+    QFBD_DEVICE  *Dev = &mDevices[DevIdx];
+
+    if (!(Dev->DevFlags & QEMU_FIXED_BARS_DEV_F_FIXED)) {
+      continue;
+    }
+    for (BarIdx = 0; BarIdx < Dev->NumBars; BarIdx++) {
+      QEMU_FIXED_BARS_BAR  *Bar = &Dev->Bars[BarIdx];
+
+      if (Bar->Flags & QEMU_FIXED_BAR_F_MEM64) {
+        if (Bar->Address < Min64) {
+          Min64 = Bar->Address;
+        }
+        if (Bar->Address + Bar->Size > Max64) {
+          Max64 = Bar->Address + Bar->Size;
+        }
+      } else {
+        if (Bar->Address < Min32) {
+          Min32 = Bar->Address;
+        }
+        if (Bar->Address + Bar->Size > Max32) {
+          Max32 = Bar->Address + Bar->Size;
+        }
+      }
+    }
+  }
+
+  if (Min64 < Max64) {
+    Base   = Min64 & ~(Gran - 1);
+    Size   = ALIGN_VALUE (Max64 - Base, Gran);
+    Status = gDS->AllocateMemorySpace (
+                    EfiGcdAllocateAddress,
+                    EfiGcdMemoryTypeMemoryMappedIo,
+                    0, Size, &Base, gImageHandle, NULL
+                    );
+    DEBUG ((EFI_ERROR (Status) ? DEBUG_ERROR : DEBUG_INFO,
+            "QFBD: reserve 64-bit 0x%016Lx+0x%016Lx: %r\n",
+            Base, Size, Status));
+  }
+
+  if (Min32 < Max32) {
+    Base   = Min32 & ~(Gran - 1);
+    Size   = ALIGN_VALUE (Max32 - Base, Gran);
+    Status = gDS->AllocateMemorySpace (
+                    EfiGcdAllocateAddress,
+                    EfiGcdMemoryTypeMemoryMappedIo,
+                    0, Size, &Base, gImageHandle, NULL
+                    );
+    DEBUG ((EFI_ERROR (Status) ? DEBUG_ERROR : DEBUG_INFO,
+            "QFBD: reserve 32-bit 0x%016Lx+0x%016Lx: %r\n",
+            Base, Size, Status));
+  }
+}
+
+/* Walk the blob once, build the mDevices[] index and the mClasses[] table. */
+STATIC VOID
+InitDevices (
+  IN UINTN  BlobSize
+  )
+{
+  UINT8  *Ptr = mBlobData + sizeof (QEMU_FIXED_BARS_HDR);
+  UINT8  *End = mBlobData + BlobSize;
+  UINTN   DevIdx, ClassIdx;
+
+  for (DevIdx = 0; DevIdx < mHdr.NumDevices; DevIdx++) {
+    QEMU_FIXED_BARS_DEVICE  *Dev;
+    UINT8                   *Next;
+
+    if (Ptr + sizeof (QEMU_FIXED_BARS_DEVICE) > End) {
+      DEBUG ((DEBUG_ERROR, "QFBD: blob truncated at device %u\n", (UINT32)DevIdx));
+      break;
+    }
+
+    Dev  = (QEMU_FIXED_BARS_DEVICE *)Ptr;
+    Next = Ptr
+         + sizeof (QEMU_FIXED_BARS_DEVICE)
+         + Dev->NumBars * sizeof (QEMU_FIXED_BARS_BAR);
+
+    if (Next > End) {
+      DEBUG ((DEBUG_ERROR, "QFBD: blob truncated at device %u\n", (UINT32)DevIdx));
+      break;
+    }
+
+    mDevices[mNumDevices].VendorId  = Dev->VendorId;
+    mDevices[mNumDevices].DeviceId  = Dev->DeviceId;
+    mDevices[mNumDevices].DevFlags  = Dev->DevFlags;
+    mDevices[mNumDevices].RpBus     = Dev->RpBus;
+    mDevices[mNumDevices].NumBars   = Dev->NumBars;
+    mDevices[mNumDevices].Bars      = (QEMU_FIXED_BARS_BAR *)(
+      Ptr + sizeof (QEMU_FIXED_BARS_DEVICE)
+      );
+    mNumDevices++;
+
+    /* Find or create the class entry for this VID:DID. */
+    for (ClassIdx = 0; ClassIdx < mNumClasses; ClassIdx++) {
+      if (mClasses[ClassIdx].VendorId == Dev->VendorId &&
+          mClasses[ClassIdx].DeviceId == Dev->DeviceId)
+      {
+        mClasses[ClassIdx].Count++;
+        break;
+      }
+    }
+    if (ClassIdx == mNumClasses) {
+      mClasses[mNumClasses].VendorId   = Dev->VendorId;
+      mClasses[mNumClasses].DeviceId   = Dev->DeviceId;
+      mClasses[mNumClasses].Count      = 1;
+      mClasses[mNumClasses].CurrentIdx = 0;
+      mNumClasses++;
+    }
+
+    DEBUG ((DEBUG_INFO,
+            "QFBD: device %04x:%04x num_bars=%u\n",
+            Dev->VendorId, Dev->DeviceId, Dev->NumBars));
+
+    Ptr = Next;
+  }
+
+  for (ClassIdx = 0; ClassIdx < mNumClasses; ClassIdx++) {
+    DEBUG ((DEBUG_INFO,
+            "QFBD: class %04x:%04x count=%u\n",
+            mClasses[ClassIdx].VendorId, mClasses[ClassIdx].DeviceId,
+            (UINT32)mClasses[ClassIdx].Count));
+  }
+}
+
+STATIC
+EFI_STATUS
+EFIAPI
+FixedBarCheckDevice (
+  IN  EFI_INCOMPATIBLE_PCI_DEVICE_SUPPORT_PROTOCOL  *This,
+  IN  UINTN                                          VendorId,
+  IN  UINTN                                          DeviceId,
+  IN  UINTN                                          RevisionId,
+  IN  UINTN                                          SubsystemVendorId,
+  IN  UINTN                                          SubsystemDeviceId,
+  OUT VOID                                           **Configuration
+  )
+{
+  QFBD_DEVICE_CLASS                  *Class;
+  QFBD_DEVICE                        *Dev;
+  UINTN                               BarIdx, DevIdx, MatchCount, ClassIdx;
+  UINT8                              *Buffer;
+  EFI_ACPI_ADDRESS_SPACE_DESCRIPTOR  *Desc;
+  EFI_ACPI_END_TAG_DESCRIPTOR        *End;
+
+  (VOID)RevisionId;
+  (VOID)SubsystemVendorId;
+  (VOID)SubsystemDeviceId;
+
+  if (Configuration == NULL) {
+    return EFI_INVALID_PARAMETER;
+  }
+
+  *Configuration = NULL;
+
+  if (mNumClasses == 0) {
+    return EFI_SUCCESS;
+  }
+
+  /* Look up the per-VID:DID class. */
+  Class = NULL;
+  for (ClassIdx = 0; ClassIdx < mNumClasses; ClassIdx++) {
+    if (mClasses[ClassIdx].VendorId == (UINT16)VendorId &&
+        mClasses[ClassIdx].DeviceId == (UINT16)DeviceId)
+    {
+      Class = &mClasses[ClassIdx];
+      break;
+    }
+  }
+  if (Class == NULL) {
+    return EFI_SUCCESS;  /* not a fixed-bar device */
+  }
+
+  /* Find the CurrentIdx-th device in mDevices[] matching this VID:DID. */
+  Dev        = NULL;
+  MatchCount = 0;
+  for (DevIdx = 0; DevIdx < mNumDevices; DevIdx++) {
+    if (mDevices[DevIdx].VendorId == (UINT16)VendorId &&
+        mDevices[DevIdx].DeviceId == (UINT16)DeviceId)
+    {
+      if (MatchCount == Class->CurrentIdx) {
+        Dev = &mDevices[DevIdx];
+        break;
+      }
+      MatchCount++;
+    }
+  }
+  if (Dev == NULL) {
+    return EFI_SUCCESS;
+  }
+
+  /* Always advance the counter so ordering stays in sync with the blob. */
+  Class->CurrentIdx++;
+  if (Class->CurrentIdx >= Class->Count) {
+    Class->CurrentIdx = 0;
+  }
+
+  if (!(Dev->DevFlags & QEMU_FIXED_BARS_DEV_F_FIXED)) {
+    return EFI_SUCCESS;
+  }
+
+  Buffer = AllocateZeroPool (Dev->NumBars * sizeof (*Desc) + sizeof (*End));
+  if (Buffer == NULL) {
+    return EFI_OUT_OF_RESOURCES;
+  }
+
+  Desc = (EFI_ACPI_ADDRESS_SPACE_DESCRIPTOR *)Buffer;
+
+  for (BarIdx = 0; BarIdx < Dev->NumBars; BarIdx++) {
+    QEMU_FIXED_BARS_BAR  *Bar = &Dev->Bars[BarIdx];
+
+    Desc->Desc                  = ACPI_ADDRESS_SPACE_DESCRIPTOR;
+    Desc->Len                   = (UINT16)(sizeof (*Desc) - 3);
+    Desc->ResType               = ACPI_ADDRESS_SPACE_TYPE_MEM;
+    Desc->GenFlag               = ACPI_ADDR_FLAG_FIXED;
+    Desc->SpecificFlag          = 0;
+    Desc->AddrSpaceGranularity  = (Bar->Flags & QEMU_FIXED_BAR_F_MEM64) ? 64 : 32;
+    Desc->AddrRangeMin          = Bar->Address;
+    Desc->AddrRangeMax          = Bar->Size - 1;
+    Desc->AddrTranslationOffset = BarIdx;
+    Desc->AddrLen               = Bar->Size;
+
+    DEBUG ((DEBUG_INFO,
+            "QFBD: %04x:%04x BAR%u -> 0x%016Lx size 0x%016Lx\n",
+            Dev->VendorId, Dev->DeviceId,
+            (UINT32)Bar->Bar, Bar->Address, Bar->Size));
+
+    Desc++;
+  }
+
+  End           = (EFI_ACPI_END_TAG_DESCRIPTOR *)Desc;
+  End->Desc     = ACPI_END_TAG_DESCRIPTOR;
+  End->Checksum = 0;
+
+  *Configuration = Buffer;
+
+  return EFI_SUCCESS;
+}
+
+STATIC EFI_INCOMPATIBLE_PCI_DEVICE_SUPPORT_PROTOCOL  mFixedBarSupport = {
+  FixedBarCheckDevice
+};
+
+/*
+ * Range type: Min inclusive, Max exclusive (base + size).
+ */
+typedef struct {
+  UINT64   Min;
+  UINT64   Max;
+  BOOLEAN  Valid;
+} QFBD_RANGE;
+
+typedef struct {
+  QFBD_RANGE  Pref;
+  QFBD_RANGE  NonPref;
+} QFBD_BRIDGE_RANGES;
+
+STATIC VOID
+RangeUnion (
+  QFBD_RANGE  *R,
+  UINT64       Addr,
+  UINT64       Size
+  )
+{
+  if (Addr     < R->Min) R->Min = Addr;
+  if (Addr + Size > R->Max) R->Max = Addr + Size;
+  R->Valid = TRUE;
+}
+
+/*
+ * Write PMem and Mem32 window registers on one bridge.
+ */
+STATIC VOID
+SetBridgeWindow (
+  IN EFI_PCI_IO_PROTOCOL  *PciIo,
+  IN UINTN                 B,
+  IN UINTN                 D,
+  IN UINTN                 F,
+  IN QFBD_BRIDGE_RANGES    R
+  )
+{
+  UINT16  Cmd;
+
+  if (R.Pref.Valid) {
+    UINT64  Base  = R.Pref.Min;
+    UINT64  End   = R.Pref.Max - 1;
+    UINT16  B16   = (UINT16)(((UINT32)Base) >> 16);
+    UINT16  L16   = (UINT16)(((UINT32)End)  >> 16);
+    UINT32  BHi   = (UINT32)RShiftU64 (Base, 32);
+    UINT32  LHi   = (UINT32)RShiftU64 (End,  32);
+
+    PciIo->Pci.Write (PciIo, EfiPciIoWidthUint16, PPB_PREF_MEM_BASE_OFFSET,  1, &B16);
+    PciIo->Pci.Write (PciIo, EfiPciIoWidthUint16, PPB_PREF_MEM_LIMIT_OFFSET, 1, &L16);
+    PciIo->Pci.Write (PciIo, EfiPciIoWidthUint32, PPB_PREF_BASE_HI32_OFFSET, 1, &BHi);
+    PciIo->Pci.Write (PciIo, EfiPciIoWidthUint32, PPB_PREF_LIMIT_HI32_OFFSET, 1, &LHi);
+
+    DEBUG ((DEBUG_INFO, "QFBD: bridge %02Lx:%02Lx.%Lx PMem [0x%Lx, 0x%Lx]\n",
+            (UINT64)B, (UINT64)D, (UINT64)F, Base, End));
+  }
+
+  if (R.NonPref.Valid) {
+    UINT64  Base = R.NonPref.Min;
+    UINT64  End  = R.NonPref.Max - 1;
+    UINT16  B16  = (UINT16)(Base >> 16);
+    UINT16  L16  = (UINT16)(End  >> 16);
+
+    PciIo->Pci.Write (PciIo, EfiPciIoWidthUint16, PPB_MEM32_BASE_OFFSET,  1, &B16);
+    PciIo->Pci.Write (PciIo, EfiPciIoWidthUint16, PPB_MEM32_LIMIT_OFFSET, 1, &L16);
+
+    DEBUG ((DEBUG_INFO, "QFBD: bridge %02Lx:%02Lx.%Lx Mem32 [0x%Lx, 0x%Lx]\n",
+            (UINT64)B, (UINT64)D, (UINT64)F, Base, End));
+  }
+
+  if (R.Pref.Valid || R.NonPref.Valid) {
+    PciIo->Pci.Read  (PciIo, EfiPciIoWidthUint16, PCI_COMMAND_OFFSET, 1, &Cmd);
+    Cmd |= EFI_PCI_COMMAND_MEMORY_SPACE | EFI_PCI_COMMAND_BUS_MASTER;
+    PciIo->Pci.Write (PciIo, EfiPciIoWidthUint16, PCI_COMMAND_OFFSET, 1, &Cmd);
+  }
+}
+
+/*
+ * Read the BAR ranges (prefetchable and non-prefetchable) from an endpoint
+ * by looking up its programmed BAR addresses in the blob.  Every device
+ * with the FIXED devflag bit set is guaranteed to have blob-provided BAR
+ * entries covering all its BARs, so a match is always found.
+ */
+STATIC VOID
+ClassifyBar (
+  IN OUT QFBD_BRIDGE_RANGES  *R,
+  IN     UINT64              Addr
+  )
+{
+  UINTN  DevIdx, BarIdx;
+
+  for (DevIdx = 0; DevIdx < mNumDevices; DevIdx++) {
+    for (BarIdx = 0; BarIdx < mDevices[DevIdx].NumBars; BarIdx++) {
+      QEMU_FIXED_BARS_BAR  *Bar = &mDevices[DevIdx].Bars[BarIdx];
+
+      if (Bar->Address == Addr && Addr != 0) {
+        if (Bar->Flags & QEMU_FIXED_BAR_F_PREF) {
+          RangeUnion (&R->Pref, Addr, Bar->Size);
+        } else {
+          RangeUnion (&R->NonPref, Addr, Bar->Size);
+        }
+      }
+    }
+  }
+}
+
+STATIC QFBD_BRIDGE_RANGES
+EndpointRanges (
+  IN EFI_PCI_IO_PROTOCOL  *PciIo
+  )
+{
+  QFBD_BRIDGE_RANGES  R = { { MAX_UINT64, 0, FALSE }, { MAX_UINT64, 0, FALSE } };
+  UINTN               Off;
+
+  for (Off = PCI_BASE_ADDRESSREG_OFFSET; Off <= 0x24; ) {
+    UINT32  Lo;
+
+    PciIo->Pci.Read (PciIo, EfiPciIoWidthUint32, Off, 1, &Lo);
+
+    if ((Lo & 0x1) == 1) {          /* IO BAR */
+      Off += 4;
+      continue;
+    }
+    if ((Lo & 0x6) == 0x4) {        /* 64-bit memory BAR */
+      UINT32  Hi;
+      UINT64  Addr;
+
+      PciIo->Pci.Read (PciIo, EfiPciIoWidthUint32, Off + 4, 1, &Hi);
+      Addr = ((UINT64)Hi << 32) | (Lo & ~(UINT32)0xF);
+      ClassifyBar (&R, Addr);
+      Off += 8;
+    } else {                         /* 32-bit memory BAR */
+      ClassifyBar (&R, (UINT64)(Lo & ~(UINT32)0xF));
+      Off += 4;
+    }
+  }
+  return R;
+}
+
+/*
+ * Recursively walk bridges starting at SecBus.  For each bridge, collect
+ * ranges (prefetchable and non-prefetchable) from all endpoints below it
+ * (bottom-up), program the bridge window, and return the union to the
+ * caller.
+ *
+ * Because every device with the FIXED devflag bit set is guaranteed to have
+ * blob-provided BAR entries, EndpointRanges() only returns non-empty
+ * results for fixed devices.  Branches leading to endpoints with no fixed
+ * BARs naturally produce empty ranges and are not programmed.
+ */
+STATIC QFBD_BRIDGE_RANGES
+WalkAndProgram (
+  IN UINTN    Seg,
+  IN UINT8    Bus,
+  IN UINTN    HandleCount,
+  IN EFI_HANDLE  *Handles
+  )
+{
+  QFBD_BRIDGE_RANGES  Total = { { MAX_UINT64, 0, FALSE }, { MAX_UINT64, 0, FALSE } };
+  UINTN               Idx;
+
+  for (Idx = 0; Idx < HandleCount; Idx++) {
+    EFI_PCI_IO_PROTOCOL  *PciIo;
+    UINTN                 S, B, D, F;
+    UINT8                 HeaderType, SecBus;
+    QFBD_BRIDGE_RANGES    Sub;
+
+    if (EFI_ERROR (gBS->HandleProtocol (
+                          Handles[Idx], &gEfiPciIoProtocolGuid,
+                          (VOID **)&PciIo)))
+    {
+      continue;
+    }
+
+    PciIo->GetLocation (PciIo, &S, &B, &D, &F);
+    if (S != Seg || (UINT8)B != Bus) {
+      continue;
+    }
+
+    PciIo->Pci.Read (PciIo, EfiPciIoWidthUint8, PCI_HEADER_TYPE_OFFSET, 1, &HeaderType);
+
+    if ((HeaderType & 0x7F) == HEADER_TYPE_PCI_TO_PCI_BRIDGE) {
+      /* Bridge: recurse first, then program with the subtree's range. */
+      PciIo->Pci.Read (PciIo, EfiPciIoWidthUint8,
+                       PCI_BRIDGE_SECONDARY_BUS_REGISTER_OFFSET, 1, &SecBus);
+      Sub = WalkAndProgram (Seg, SecBus, HandleCount, Handles);
+      if (Sub.Pref.Valid || Sub.NonPref.Valid) {
+        SetBridgeWindow (PciIo, B, D, F, Sub);
+      }
+    } else {
+      /* Endpoint: collect its fixed BAR ranges. */
+      Sub = EndpointRanges (PciIo);
+    }
+
+    if (Sub.Pref.Valid) {
+      RangeUnion (&Total.Pref, Sub.Pref.Min, Sub.Pref.Max - Sub.Pref.Min);
+    }
+    if (Sub.NonPref.Valid) {
+      RangeUnion (&Total.NonPref, Sub.NonPref.Min, Sub.NonPref.Max - Sub.NonPref.Min);
+    }
+  }
+
+  return Total;
+}
+
+STATIC VOID
+EFIAPI
+FixBridgeWindows (
+  IN EFI_EVENT  Event,
+  IN VOID       *Context
+  )
+{
+  EFI_STATUS  Status;
+  UINTN       HandleCount;
+  EFI_HANDLE  *Handles;
+  UINTN       DevIdx, Idx;
+
+  (VOID)Context;
+
+  gBS->CloseEvent (Event);
+
+  Status = gBS->LocateHandleBuffer (
+                  ByProtocol, &gEfiPciIoProtocolGuid,
+                  NULL, &HandleCount, &Handles
+                  );
+  if (EFI_ERROR (Status)) {
+    return;
+  }
+
+  /*
+   * For each unique rp_bus that has at least one FIXED device, find the
+   * bridge on that bus and walk its subtree bottom-up to set windows.
+   */
+  for (DevIdx = 0; DevIdx < mNumDevices; DevIdx++) {
+    UINT8  RpBus;
+
+    if (!(mDevices[DevIdx].DevFlags & QEMU_FIXED_BARS_DEV_F_FIXED)) {
+      continue;
+    }
+    RpBus = mDevices[DevIdx].RpBus;
+
+    /* Skip if we already processed this rp_bus. */
+    {
+      UINTN  Di;
+      BOOLEAN Already = FALSE;
+      for (Di = 0; Di < DevIdx; Di++) {
+        if ((mDevices[Di].DevFlags & QEMU_FIXED_BARS_DEV_F_FIXED) &&
+            mDevices[Di].RpBus == RpBus)
+        {
+          Already = TRUE;
+          break;
+        }
+      }
+      if (Already) continue;
+    }
+
+    /* Find the RP bridge handle: bridge on Bus == RpBus. */
+    for (Idx = 0; Idx < HandleCount; Idx++) {
+      EFI_PCI_IO_PROTOCOL  *PciIo;
+      UINTN                 S, B, D, F;
+      UINT8                 HeaderType, SecBus;
+      QFBD_BRIDGE_RANGES    Sub;
+
+      if (EFI_ERROR (gBS->HandleProtocol (
+                            Handles[Idx], &gEfiPciIoProtocolGuid,
+                            (VOID **)&PciIo)))
+      {
+        continue;
+      }
+
+      PciIo->GetLocation (PciIo, &S, &B, &D, &F);
+      if ((UINT8)B != RpBus) {
+        continue;
+      }
+
+      PciIo->Pci.Read (PciIo, EfiPciIoWidthUint8,
+                       PCI_HEADER_TYPE_OFFSET, 1, &HeaderType);
+      if ((HeaderType & 0x7F) != HEADER_TYPE_PCI_TO_PCI_BRIDGE) {
+        continue;
+      }
+
+      PciIo->Pci.Read (PciIo, EfiPciIoWidthUint8,
+                       PCI_BRIDGE_SECONDARY_BUS_REGISTER_OFFSET, 1, &SecBus);
+
+      Sub = WalkAndProgram (S, SecBus, HandleCount, Handles);
+      if (Sub.Pref.Valid || Sub.NonPref.Valid) {
+        SetBridgeWindow (PciIo, B, D, F, Sub);
+        DEBUG ((DEBUG_INFO, "QFBD: RP %02Lx:%02Lx.%Lx programmed\n",
+                (UINT64)B, (UINT64)D, (UINT64)F));
+      }
+    }
+  }
+
+  FreePool (Handles);
+}
+
+EFI_STATUS
+EFIAPI
+QemuFixedBarsDxeEntryPoint (
+  IN EFI_HANDLE        ImageHandle,
+  IN EFI_SYSTEM_TABLE  *SystemTable
+  )
+{
+  FIRMWARE_CONFIG_ITEM  FwCfgItem;
+  UINTN                 FwCfgSize;
+  EFI_STATUS            Status;
+  EFI_HANDLE            Handle;
+  EFI_EVENT             Event;
+  VOID                  *Registration;
+
+  Status = QemuFwCfgFindFile ("etc/fixed-bars", &FwCfgItem, &FwCfgSize);
+  if (EFI_ERROR (Status)) {
+    DEBUG ((DEBUG_INFO, "QFBD: etc/fixed-bars absent, nothing to do\n"));
+    return EFI_SUCCESS;
+  }
+
+  if (FwCfgSize < sizeof (mHdr)) {
+    DEBUG ((DEBUG_ERROR, "QFBD: blob too small (%Lu bytes)\n",
+            (UINT64)FwCfgSize));
+    return EFI_UNSUPPORTED;
+  }
+
+  QemuFwCfgSelectItem (FwCfgItem);
+  QemuFwCfgReadBytes (sizeof (mHdr), &mHdr);
+
+  if (mHdr.Version != QEMU_FIXED_BARS_VERSION) {
+    DEBUG ((DEBUG_ERROR,
+            "QFBD: unsupported blob version %u (expected %u)\n",
+            mHdr.Version, QEMU_FIXED_BARS_VERSION));
+    return EFI_UNSUPPORTED;
+  }
+
+  if (mHdr.NumDevices == 0) {
+    DEBUG ((DEBUG_INFO, "QFBD: zero devices, nothing to do\n"));
+    return EFI_SUCCESS;
+  }
+
+  /* Read the full blob into a persistent buffer; device entries point into it. */
+  mBlobData = AllocatePool (FwCfgSize);
+  if (mBlobData == NULL) {
+    return EFI_OUT_OF_RESOURCES;
+  }
+
+  QemuFwCfgSelectItem (FwCfgItem);
+  QemuFwCfgReadBytes (FwCfgSize, mBlobData);
+  CopyMem (&mHdr, mBlobData, sizeof (mHdr));
+
+  mDevices = AllocateZeroPool (mHdr.NumDevices * sizeof (QFBD_DEVICE));
+  if (mDevices == NULL) {
+    FreePool (mBlobData);
+    return EFI_OUT_OF_RESOURCES;
+  }
+
+  /* Worst case: every device has a unique VID:DID — allocate mNumDevices slots. */
+  mClasses = AllocateZeroPool (mHdr.NumDevices * sizeof (QFBD_DEVICE_CLASS));
+  if (mClasses == NULL) {
+    FreePool (mDevices);
+    FreePool (mBlobData);
+    return EFI_OUT_OF_RESOURCES;
+  }
+
+  DEBUG ((DEBUG_INFO, "QFBD: %u device(s)\n", mHdr.NumDevices));
+
+  mNumDevices = 0;
+  mNumClasses = 0;
+  InitDevices (FwCfgSize);
+  ReserveHpaRanges ();
+
+  Handle = NULL;
+  Status = gBS->InstallProtocolInterface (
+                  &Handle,
+                  &gEfiIncompatiblePciDeviceSupportProtocolGuid,
+                  EFI_NATIVE_INTERFACE,
+                  &mFixedBarSupport
+                  );
+  if (EFI_ERROR (Status)) {
+    DEBUG ((DEBUG_ERROR, "QFBD: install protocol failed: %r\n", Status));
+    FreePool (mDevices);
+    FreePool (mBlobData);
+    return Status;
+  }
+
+  /* Register bridge window fixup for after PCI enumeration completes. */
+  Status = gBS->CreateEvent (
+                  EVT_NOTIFY_SIGNAL, TPL_CALLBACK,
+                  FixBridgeWindows, NULL, &Event
+                  );
+  if (!EFI_ERROR (Status)) {
+    gBS->RegisterProtocolNotify (
+           &gEfiPciEnumerationCompleteProtocolGuid,
+           Event, &Registration
+           );
+  }
+
+  return EFI_SUCCESS;
+}
diff --git a/OvmfPkg/QemuFixedBarsDxe/QemuFixedBarsDxe.inf b/OvmfPkg/QemuFixedBarsDxe/QemuFixedBarsDxe.inf
new file mode 100644
index 0000000000..8c08a332b3
--- /dev/null
+++ b/OvmfPkg/QemuFixedBarsDxe/QemuFixedBarsDxe.inf
@@ -0,0 +1,43 @@
+## @file
+#  Fixed-BAR support for QEMU passthrough devices.
+#
+#  Reads QEMU's etc/fixed-bars fw_cfg blob and publishes
+#  EFI_INCOMPATIBLE_PCI_DEVICE_SUPPORT_PROTOCOL so that PciBusDxe places
+#  each matching device's BARs at given addresses.
+#
+#  SPDX-License-Identifier: BSD-2-Clause-Patent
+##
+
+[Defines]
+  INF_VERSION    = 0x00010005
+  BASE_NAME      = QemuFixedBarsDxe
+  FILE_GUID      = a1b2c3d4-e5f6-7890-abcd-ef1234567890
+  MODULE_TYPE    = DXE_DRIVER
+  VERSION_STRING = 1.0
+  ENTRY_POINT    = QemuFixedBarsDxeEntryPoint
+
+[Sources]
+  QemuFixedBarsDxe.c
+
+[Packages]
+  MdePkg/MdePkg.dec
+  MdeModulePkg/MdeModulePkg.dec
+  OvmfPkg/OvmfPkg.dec
+
+[LibraryClasses]
+  BaseMemoryLib
+  DebugLib
+  DxeServicesTableLib
+  MemoryAllocationLib
+  QemuFwCfgLib
+  UefiBootServicesTableLib
+  UefiDriverEntryPoint
+
+[Protocols]
+  gEfiIncompatiblePciDeviceSupportProtocolGuid       ## PRODUCES
+  gEfiPciHostBridgeResourceAllocationProtocolGuid    ## CONSUMES
+  gEfiPciEnumerationCompleteProtocolGuid             ## CONSUMES
+  gEfiPciIoProtocolGuid                              ## CONSUMES
+
+[Depex]
+  gEfiPciHostBridgeResourceAllocationProtocolGuid
-- 
2.34.1



^ permalink raw reply related	[flat|nested] 3+ messages in thread

end of thread, other threads:[~2026-08-27  0:46 UTC | newest]

Thread overview: 3+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-08-27  0:45 [RFC PATCH v2 0/2] MdeModulePkg, OvmfPkg: fixed PCI BAR placement Tushar Dave
2026-08-27  0:45 ` [RFC PATCH v2 1/2] MdeModulePkg/PciBusDxe: honor fixed BAR addresses from IncompatiblePci Tushar Dave
2026-08-27  0:45 ` [RFC PATCH v2 2/2] ArmVirtPkg, OvmfPkg: add QemuFixedBarsDxe for fixed PCI BAR placement Tushar Dave via qemu development

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.