[edk2,15/21] ArmVirtPkg/PciHostBridgeDxe: move to FDT client protocol

Message ID 1459959319-19293-16-git-send-email-ard.biesheuvel@linaro.org
State Superseded
Headers show

Commit Message

Ard Biesheuvel April 6, 2016, 4:15 p.m.
Instead of relying on VirtFdtDxe to populate various dynamic PCDs with
information retrieved from the host provided device tree, perform the
PCI ECAM related DT node parsing directly in PciHostBridgeDxe.

Contributed-under: TianoCore Contribution Agreement 1.0
Signed-off-by: Ard Biesheuvel <ard.biesheuvel@linaro.org>

---
 ArmVirtPkg/PciHostBridgeDxe/PciHostBridge.c      | 233 ++++++++++++++++++--
 ArmVirtPkg/PciHostBridgeDxe/PciHostBridge.h      |   2 +
 ArmVirtPkg/PciHostBridgeDxe/PciHostBridgeDxe.inf |  16 +-
 3 files changed, 227 insertions(+), 24 deletions(-)

-- 
2.5.0

_______________________________________________
edk2-devel mailing list
edk2-devel@lists.01.org
https://lists.01.org/mailman/listinfo/edk2-devel

Patch

diff --git a/ArmVirtPkg/PciHostBridgeDxe/PciHostBridge.c b/ArmVirtPkg/PciHostBridgeDxe/PciHostBridge.c
index 735c7f216318..cf9496fb1f15 100644
--- a/ArmVirtPkg/PciHostBridgeDxe/PciHostBridge.c
+++ b/ArmVirtPkg/PciHostBridgeDxe/PciHostBridge.c
@@ -79,6 +79,198 @@  PCI_HOST_BRIDGE_INSTANCE mPciHostBridgeInstanceTemplate = {
 // Implementation
 //
 
+STATIC UINT64     mIoBase, mIoSize, mIoTranslation;
+STATIC UINT64     mMmioBase, mMmioSize, mMmioTranslation;
+STATIC UINT32     mBusMin, mBusMax;
+
+//
+// We expect the "ranges" property of "pci-host-ecam-generic" to consist of
+// records like this.
+//
+#pragma pack (1)
+typedef struct {
+  UINT32 Type;
+  UINT64 ChildBase;
+  UINT64 CpuBase;
+  UINT64 Size;
+} DTB_PCI_HOST_RANGE_RECORD;
+#pragma pack ()
+
+#define DTB_PCI_HOST_RANGE_RELOCATABLE  BIT31
+#define DTB_PCI_HOST_RANGE_PREFETCHABLE BIT30
+#define DTB_PCI_HOST_RANGE_ALIASED      BIT29
+#define DTB_PCI_HOST_RANGE_MMIO32       BIT25
+#define DTB_PCI_HOST_RANGE_MMIO64       (BIT25 | BIT24)
+#define DTB_PCI_HOST_RANGE_IO           BIT24
+#define DTB_PCI_HOST_RANGE_TYPEMASK     (BIT31 | BIT30 | BIT29 | BIT25 | BIT24)
+
+/**
+  Process the device tree node describing the generic PCI host controller.
+
+  param[in] FdtClient       Pointer to the FDT client protocol
+
+  param[in] Node            Offset of the device tree node whose "compatible"
+                            property is "pci-host-ecam-generic".
+
+  @retval EFI_SUCCESS         Parsing successful, properties parsed from Node
+                              have been stored in dynamic PCDs.
+
+  @retval EFI_PROTOCOL_ERROR  Parsing failed. PCDs are left unchanged.
+**/
+STATIC
+EFI_STATUS
+EFIAPI
+ProcessPciHost (
+  FDT_CLIENT_PROTOCOL         *FdtClient,
+  IN INT32                    Node
+  )
+{
+  UINT64     ConfigBase, ConfigSize;
+  CONST VOID *Prop;
+  UINTN      Len;
+  UINT32     RecordIdx;
+  EFI_STATUS Status;
+  UINT32     Tmp;
+
+  Status = FdtClient->GetNodeProperty (FdtClient, Node, "reg", &Prop, &Len);
+  if (EFI_ERROR (Status) || Prop == NULL || Len != 2 * sizeof(UINT64)) {
+    DEBUG ((EFI_D_ERROR, "%a: 'reg' property not found or invalid\n",
+      __FUNCTION__));
+    return EFI_PROTOCOL_ERROR;
+  }
+
+  //
+  // Fetch the ECAM window.
+  //
+  ConfigBase = fdt64_to_cpu (((CONST UINT64 *)Prop)[0]);
+  ConfigSize = fdt64_to_cpu (((CONST UINT64 *)Prop)[1]);
+
+  //
+  // Fetch the bus range (note: inclusive).
+  //
+  Status = FdtClient->GetNodeProperty (FdtClient, Node, "bus-range", &Prop, &Len);
+  if (EFI_ERROR (Status) || Prop == NULL || Len != 2 * sizeof(UINT32)) {
+    DEBUG ((EFI_D_ERROR, "%a: 'bus-range' not found or invalid\n",
+      __FUNCTION__));
+    return EFI_PROTOCOL_ERROR;
+  }
+  mBusMin = fdt32_to_cpu (((CONST UINT32 *)Prop)[0]);
+  mBusMax = fdt32_to_cpu (((CONST UINT32 *)Prop)[1]);
+
+  //
+  // Sanity check: the config space must accommodate all 4K register bytes of
+  // all 8 functions of all 32 devices of all buses.
+  //
+  if (mBusMax < mBusMin || mBusMax - mBusMin == MAX_UINT32 ||
+      DivU64x32 (ConfigSize, SIZE_4KB * 8 * 32) < mBusMax - mBusMin + 1) {
+    DEBUG ((EFI_D_ERROR, "%a: invalid 'bus-range' and/or 'reg'\n",
+      __FUNCTION__));
+    return EFI_PROTOCOL_ERROR;
+  }
+
+  //
+  // Iterate over "ranges".
+  //
+  Status = FdtClient->GetNodeProperty (FdtClient, Node, "ranges", &Prop, &Len);
+  if (EFI_ERROR (Status) || Prop == NULL || Len == 0 ||
+      Len % sizeof (DTB_PCI_HOST_RANGE_RECORD) != 0) {
+    DEBUG ((EFI_D_ERROR, "%a: 'ranges' not found or invalid\n", __FUNCTION__));
+    return EFI_PROTOCOL_ERROR;
+  }
+
+  //
+  // mIoBase, IoTranslation, mMmioBase and MmioTranslation are initialized only
+  // in order to suppress '-Werror=maybe-uninitialized' warnings *incorrectly*
+  // emitted by some gcc versions.
+  //
+  mIoBase = 0;
+  mIoTranslation = 0;
+  mMmioBase = 0;
+  mMmioTranslation = 0;
+
+  //
+  // IoSize and MmioSize are initialized to zero because the logic below
+  // requires it.
+  //
+  mIoSize = 0;
+  mMmioSize = 0;
+  for (RecordIdx = 0; RecordIdx < Len / sizeof (DTB_PCI_HOST_RANGE_RECORD);
+       ++RecordIdx) {
+    CONST DTB_PCI_HOST_RANGE_RECORD *Record;
+
+    Record = (CONST DTB_PCI_HOST_RANGE_RECORD *)Prop + RecordIdx;
+    switch (fdt32_to_cpu (Record->Type) & DTB_PCI_HOST_RANGE_TYPEMASK) {
+    case DTB_PCI_HOST_RANGE_IO:
+      mIoBase = fdt64_to_cpu (Record->ChildBase);
+      mIoSize = fdt64_to_cpu (Record->Size);
+      mIoTranslation = fdt64_to_cpu (Record->CpuBase) - mIoBase;
+      break;
+
+    case DTB_PCI_HOST_RANGE_MMIO32:
+      mMmioBase = fdt64_to_cpu (Record->ChildBase);
+      mMmioSize = fdt64_to_cpu (Record->Size);
+      mMmioTranslation = fdt64_to_cpu (Record->CpuBase) - mMmioBase;
+
+      if (mMmioBase > MAX_UINT32 || mMmioSize > MAX_UINT32 ||
+          mMmioBase + mMmioSize > SIZE_4GB) {
+        DEBUG ((EFI_D_ERROR, "%a: MMIO32 space invalid\n", __FUNCTION__));
+        return EFI_PROTOCOL_ERROR;
+      }
+
+      if (mMmioTranslation != 0) {
+        DEBUG ((EFI_D_ERROR, "%a: unsupported nonzero MMIO32 translation "
+          "0x%Lx\n", __FUNCTION__, mMmioTranslation));
+        return EFI_UNSUPPORTED;
+      }
+
+      break;
+    }
+  }
+  if (mIoSize == 0 || mMmioSize == 0) {
+    DEBUG ((EFI_D_ERROR, "%a: %a space empty\n", __FUNCTION__,
+      (mIoSize == 0) ? "IO" : "MMIO32"));
+    return EFI_PROTOCOL_ERROR;
+  }
+
+  PcdSet64 (PcdPciExpressBaseAddress, ConfigBase);
+
+  PcdSetBool (PcdPciDisableBusEnumeration, FALSE);
+
+  if (!FeaturePcdGet (PcdPureAcpiBoot)) {
+    //
+    // Set the /chosen/linux,pci-probe-only property to 1, so that the PCI
+    // setup we will perform in the firmware is honored by the Linux OS,
+    // rather than torn down and done from scratch. This is generally a more
+    // sensible approach, and aligns with what ACPI based OSes do in general.
+    //
+    // In case we are exposing an emulated VGA PCI device to the guest, which
+    // may subsequently get exposed via the Graphics Output protocol and
+    // driven as an efifb by Linux, we need this setting to prevent the
+    // framebuffer from becoming unresponsive.
+    //
+    Status = FdtClient->GetChosenNode (FdtClient, &Node);
+
+    if (Status == EFI_SUCCESS) {
+      Tmp = cpu_to_fdt32 (1);
+      Status = FdtClient->SetNodeProperty (FdtClient,
+                                           Node,
+                                           "linux,pci-probe-only",
+                                           &Tmp,
+                                           sizeof (Tmp));
+    }
+    if (EFI_ERROR (Status)) {
+      DEBUG ((EFI_D_WARN, "Failed to set /chosen/linux,pci-probe-only property\n"));
+    }
+  }
+
+  DEBUG ((EFI_D_INFO, "%a: Config[0x%Lx+0x%Lx) Bus[0x%x..0x%x] "
+    "Io[0x%Lx+0x%Lx)@0x%Lx Mem[0x%Lx+0x%Lx)@0x%Lx\n", __FUNCTION__, ConfigBase,
+    ConfigSize, mBusMin, mBusMax, mIoBase, mIoSize, mIoTranslation, mMmioBase,
+    mMmioSize, mMmioTranslation));
+  return EFI_SUCCESS;
+}
+
+
 /**
   Entry point of this driver
 
@@ -97,6 +289,8 @@  InitializePciHostBridge (
   IN EFI_SYSTEM_TABLE  *SystemTable
   )
 {
+  FDT_CLIENT_PROTOCOL         *FdtClient;
+  INT32                       PciDtNode;
   UINT64                      MmioAttributes;
   EFI_STATUS                  Status;
   UINTN                       Loop1;
@@ -104,24 +298,31 @@  InitializePciHostBridge (
   PCI_HOST_BRIDGE_INSTANCE    *HostBridge;
   PCI_ROOT_BRIDGE_INSTANCE    *PrivateData;
 
-  if (PcdGet64 (PcdPciExpressBaseAddress) == 0) {
+  Status = gBS->LocateProtocol (&gFdtClientProtocolGuid, NULL, (VOID **)&FdtClient);
+  ASSERT_EFI_ERROR (Status);
+
+  Status = FdtClient->FindCompatibleNode (FdtClient, "pci-host-ecam-generic", &PciDtNode);
+  if (EFI_ERROR (Status)) {
     DEBUG ((EFI_D_INFO, "%a: PCI host bridge not present\n", __FUNCTION__));
     return EFI_ABORTED;
   }
 
+  Status = ProcessPciHost (FdtClient, PciDtNode);
+  if (EFI_ERROR (Status)) {
+    return EFI_ABORTED;
+  }
+
   mDriverImageHandle = ImageHandle;
 
-  mResAperture[0][0].BusBase  = PcdGet32 (PcdPciBusMin);
-  mResAperture[0][0].BusLimit = PcdGet32 (PcdPciBusMax);
+  mResAperture[0][0].BusBase  = mBusMin;
+  mResAperture[0][0].BusLimit = mBusMax;
 
-  mResAperture[0][0].MemBase  = PcdGet32 (PcdPciMmio32Base);
-  mResAperture[0][0].MemLimit = (UINT64)PcdGet32 (PcdPciMmio32Base) +
-                                PcdGet32 (PcdPciMmio32Size) - 1;
+  mResAperture[0][0].MemBase  = mMmioBase;
+  mResAperture[0][0].MemLimit = (UINT64)mMmioBase + mMmioSize - 1;
 
-  mResAperture[0][0].IoBase        = PcdGet64 (PcdPciIoBase);
-  mResAperture[0][0].IoLimit       = PcdGet64 (PcdPciIoBase) +
-                                     PcdGet64 (PcdPciIoSize) - 1;
-  mResAperture[0][0].IoTranslation = PcdGet64 (PcdPciIoTranslation);
+  mResAperture[0][0].IoBase        = mIoBase;
+  mResAperture[0][0].IoLimit       = mIoBase + mIoSize - 1;
+  mResAperture[0][0].IoTranslation = mIoTranslation;
 
   //
   // Add IO and MMIO memory space, so that resources can be allocated in the
@@ -129,8 +330,8 @@  InitializePciHostBridge (
   //
   Status = gDS->AddIoSpace (
                   EfiGcdIoTypeIo,
-                  PcdGet64 (PcdPciIoBase),
-                  PcdGet64 (PcdPciIoSize)
+                  mIoBase,
+                  mIoSize
                   );
   ASSERT_EFI_ERROR (Status);
 
@@ -139,8 +340,8 @@  InitializePciHostBridge (
 
   Status = gDS->AddMemorySpace (
                   EfiGcdMemoryTypeMemoryMappedIo,
-                  PcdGet32 (PcdPciMmio32Base),
-                  PcdGet32 (PcdPciMmio32Size),
+                  mMmioBase,
+                  mMmioSize,
                   MmioAttributes
                   );
   if (EFI_ERROR (Status)) {
@@ -149,8 +350,8 @@  InitializePciHostBridge (
   }
 
   Status = gDS->SetMemorySpaceAttributes (
-                  PcdGet32 (PcdPciMmio32Base),
-                  PcdGet32 (PcdPciMmio32Size),
+                  mMmioBase,
+                  mMmioSize,
                   MmioAttributes
                   );
   if (EFI_ERROR (Status)) {
diff --git a/ArmVirtPkg/PciHostBridgeDxe/PciHostBridge.h b/ArmVirtPkg/PciHostBridgeDxe/PciHostBridge.h
index 8161b546ff87..5675ab6d5bd0 100644
--- a/ArmVirtPkg/PciHostBridgeDxe/PciHostBridge.h
+++ b/ArmVirtPkg/PciHostBridgeDxe/PciHostBridge.h
@@ -24,6 +24,7 @@ 
 #include <Protocol/PciRootBridgeIo.h>
 #include <Protocol/Metronome.h>
 #include <Protocol/DevicePath.h>
+#include <Protocol/FdtClient.h>
 
 
 #include <Library/BaseLib.h>
@@ -37,6 +38,7 @@ 
 #include <Library/IoLib.h>
 #include <Library/PciLib.h>
 #include <Library/PcdLib.h>
+#include <libfdt.h>
 
 //
 // Hard code the host bridge number in the platform.
diff --git a/ArmVirtPkg/PciHostBridgeDxe/PciHostBridgeDxe.inf b/ArmVirtPkg/PciHostBridgeDxe/PciHostBridgeDxe.inf
index 6d782582e02d..fd8b351ed09b 100644
--- a/ArmVirtPkg/PciHostBridgeDxe/PciHostBridgeDxe.inf
+++ b/ArmVirtPkg/PciHostBridgeDxe/PciHostBridgeDxe.inf
@@ -23,8 +23,10 @@  [Defines]
 
 [Packages]
   MdePkg/MdePkg.dec
+  MdeModulePkg/MdeModulePkg.dec
   ArmPlatformPkg/ArmPlatformPkg.dec
   ArmVirtPkg/ArmVirtPkg.dec
+  EmbeddedPkg/EmbeddedPkg.dec
 
 [LibraryClasses]
   UefiDriverEntryPoint
@@ -39,6 +41,7 @@  [LibraryClasses]
   IoLib
   PciLib
   PcdLib
+  FdtLib
 
 [Sources]
   PciHostBridge.c
@@ -50,20 +53,17 @@  [Protocols]
   gEfiPciRootBridgeIoProtocolGuid                       ## PRODUCES
   gEfiMetronomeArchProtocolGuid                         ## CONSUMES
   gEfiDevicePathProtocolGuid                            ## PRODUCES
+  gFdtClientProtocolGuid                                ## CONSUMES
 
 [Pcd]
-  gArmPlatformTokenSpaceGuid.PcdPciBusMin
-  gArmPlatformTokenSpaceGuid.PcdPciBusMax
-  gArmPlatformTokenSpaceGuid.PcdPciIoBase
-  gArmPlatformTokenSpaceGuid.PcdPciIoSize
-  gArmPlatformTokenSpaceGuid.PcdPciIoTranslation
-  gArmPlatformTokenSpaceGuid.PcdPciMmio32Base
-  gArmPlatformTokenSpaceGuid.PcdPciMmio32Size
   gEfiMdePkgTokenSpaceGuid.PcdPciExpressBaseAddress
+  gEfiMdeModulePkgTokenSpaceGuid.PcdPciDisableBusEnumeration
 
 [FeaturePcd]
   gArmVirtTokenSpaceGuid.PcdKludgeMapPciMmioAsCached
+  gArmVirtTokenSpaceGuid.PcdPureAcpiBoot
 
 [depex]
   gEfiMetronomeArchProtocolGuid AND
-  gEfiCpuArchProtocolGuid
+  gEfiCpuArchProtocolGuid AND
+  gFdtClientProtocolGuid