]> git.proxmox.com Git - mirror_edk2.git/commitdiff
ArmVirtPkg/PciHostBridgeDxe: move to FDT client protocol
authorArd Biesheuvel <ard.biesheuvel@linaro.org>
Fri, 8 Apr 2016 09:45:01 +0000 (11:45 +0200)
committerArd Biesheuvel <ard.biesheuvel@linaro.org>
Tue, 12 Apr 2016 15:59:05 +0000 (17:59 +0200)
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>
Reviewed-by: Laszlo Ersek <lersek@redhat.com>
ArmVirtPkg/PciHostBridgeDxe/PciHostBridge.c
ArmVirtPkg/PciHostBridgeDxe/PciHostBridge.h
ArmVirtPkg/PciHostBridgeDxe/PciHostBridgeDxe.inf

index 735c7f216318b7b1bb9b57a19c78e86055c61a1e..4b2b6a562a3f4f2f7069cf4241bd85049e850418 100644 (file)
@@ -79,6 +79,230 @@ PCI_HOST_BRIDGE_INSTANCE mPciHostBridgeInstanceTemplate = {
 // Implementation\r
 //\r
 \r
+STATIC\r
+VOID\r
+SetLinuxPciProbeOnlyProperty (\r
+  IN FDT_CLIENT_PROTOCOL         *FdtClient\r
+  )\r
+{\r
+  INT32         Node;\r
+  UINT32        Tmp;\r
+  EFI_STATUS    Status;\r
+\r
+  if (!FeaturePcdGet (PcdPureAcpiBoot)) {\r
+    //\r
+    // Set the /chosen/linux,pci-probe-only property to 1, so that the PCI\r
+    // setup we will perform in the firmware is honored by the Linux OS,\r
+    // rather than torn down and done from scratch. This is generally a more\r
+    // sensible approach, and aligns with what ACPI based OSes do typically.\r
+    //\r
+    // In case we are exposing an emulated VGA PCI device to the guest, which\r
+    // may subsequently get exposed via the Graphics Output protocol and\r
+    // driven as an efifb by Linux, we need this setting to prevent the\r
+    // framebuffer from becoming unresponsive.\r
+    //\r
+    Status = FdtClient->GetOrInsertChosenNode (FdtClient, &Node);\r
+\r
+    if (!EFI_ERROR (Status)) {\r
+      Tmp = SwapBytes32 (1);\r
+      Status = FdtClient->SetNodeProperty (FdtClient, Node,\r
+                            "linux,pci-probe-only", &Tmp, sizeof (Tmp));\r
+    }\r
+    if (EFI_ERROR (Status)) {\r
+      DEBUG ((EFI_D_WARN,\r
+        "Failed to set /chosen/linux,pci-probe-only property\n"));\r
+    }\r
+  }\r
+}\r
+\r
+//\r
+// We expect the "ranges" property of "pci-host-ecam-generic" to consist of\r
+// records like this.\r
+//\r
+#pragma pack (1)\r
+typedef struct {\r
+  UINT32 Type;\r
+  UINT64 ChildBase;\r
+  UINT64 CpuBase;\r
+  UINT64 Size;\r
+} DTB_PCI_HOST_RANGE_RECORD;\r
+#pragma pack ()\r
+\r
+#define DTB_PCI_HOST_RANGE_RELOCATABLE  BIT31\r
+#define DTB_PCI_HOST_RANGE_PREFETCHABLE BIT30\r
+#define DTB_PCI_HOST_RANGE_ALIASED      BIT29\r
+#define DTB_PCI_HOST_RANGE_MMIO32       BIT25\r
+#define DTB_PCI_HOST_RANGE_MMIO64       (BIT25 | BIT24)\r
+#define DTB_PCI_HOST_RANGE_IO           BIT24\r
+#define DTB_PCI_HOST_RANGE_TYPEMASK     (BIT31 | BIT30 | BIT29 | BIT25 | BIT24)\r
+\r
+STATIC\r
+EFI_STATUS\r
+ProcessPciHost (\r
+  OUT  UINT64    *IoBase,\r
+  OUT  UINT64    *IoSize,\r
+  OUT  UINT64    *IoTranslation,\r
+  OUT  UINT64    *MmioBase,\r
+  OUT  UINT64    *MmioSize,\r
+  OUT  UINT64    *MmioTranslation,\r
+  OUT  UINT32    *BusMin,\r
+  OUT  UINT32    *BusMax\r
+  )\r
+{\r
+  FDT_CLIENT_PROTOCOL         *FdtClient;\r
+  INT32                       Node;\r
+  UINT64                      ConfigBase, ConfigSize;\r
+  CONST VOID                  *Prop;\r
+  UINT32                      Len;\r
+  UINT32                      RecordIdx;\r
+  EFI_STATUS                  Status;\r
+\r
+  //\r
+  // The following output arguments are initialized only in\r
+  // order to suppress '-Werror=maybe-uninitialized' warnings\r
+  // *incorrectly* emitted by some gcc versions.\r
+  //\r
+  *IoBase = 0;\r
+  *IoTranslation = 0;\r
+  *MmioBase = 0;\r
+  *MmioTranslation = 0;\r
+  *BusMin = 0;\r
+  *BusMax = 0;\r
+\r
+  //\r
+  // *IoSize and *MmioSize are initialized to zero because the logic below\r
+  // requires it. However, since they are also affected by the issue reported\r
+  // above, they are initialized early.\r
+  //\r
+  *IoSize = 0;\r
+  *MmioSize = 0;\r
+\r
+  Status = gBS->LocateProtocol (&gFdtClientProtocolGuid, NULL,\r
+                  (VOID **)&FdtClient);\r
+  ASSERT_EFI_ERROR (Status);\r
+\r
+  Status = FdtClient->FindCompatibleNode (FdtClient, "pci-host-ecam-generic",\r
+                        &Node);\r
+  if (EFI_ERROR (Status)) {\r
+    DEBUG ((EFI_D_INFO,\r
+      "%a: No 'pci-host-ecam-generic' compatible DT node found\n",\r
+      __FUNCTION__));\r
+    return EFI_NOT_FOUND;\r
+  }\r
+\r
+  DEBUG_CODE (\r
+    INT32 Tmp;\r
+\r
+    //\r
+    // A DT can legally describe multiple PCI host bridges, but we are not\r
+    // equipped to deal with that. So assert that there is only one.\r
+    //\r
+    Status = FdtClient->FindNextCompatibleNode (FdtClient,\r
+                          "pci-host-ecam-generic", Node, &Tmp);\r
+    ASSERT (Status == EFI_NOT_FOUND);\r
+  );\r
+\r
+  Status = FdtClient->GetNodeProperty (FdtClient, Node, "reg", &Prop, &Len);\r
+  if (EFI_ERROR (Status) || Len != 2 * sizeof (UINT64)) {\r
+    DEBUG ((EFI_D_ERROR, "%a: 'reg' property not found or invalid\n",\r
+      __FUNCTION__));\r
+    return EFI_PROTOCOL_ERROR;\r
+  }\r
+\r
+  //\r
+  // Fetch the ECAM window.\r
+  //\r
+  ConfigBase = SwapBytes64 (((CONST UINT64 *)Prop)[0]);\r
+  ConfigSize = SwapBytes64 (((CONST UINT64 *)Prop)[1]);\r
+\r
+  //\r
+  // Fetch the bus range (note: inclusive).\r
+  //\r
+  Status = FdtClient->GetNodeProperty (FdtClient, Node, "bus-range", &Prop,\r
+                        &Len);\r
+  if (EFI_ERROR (Status) || Len != 2 * sizeof (UINT32)) {\r
+    DEBUG ((EFI_D_ERROR, "%a: 'bus-range' not found or invalid\n",\r
+      __FUNCTION__));\r
+    return EFI_PROTOCOL_ERROR;\r
+  }\r
+  *BusMin = SwapBytes32 (((CONST UINT32 *)Prop)[0]);\r
+  *BusMax = SwapBytes32 (((CONST UINT32 *)Prop)[1]);\r
+\r
+  //\r
+  // Sanity check: the config space must accommodate all 4K register bytes of\r
+  // all 8 functions of all 32 devices of all buses.\r
+  //\r
+  if (*BusMax < *BusMin || *BusMax - *BusMin == MAX_UINT32 ||\r
+      DivU64x32 (ConfigSize, SIZE_4KB * 8 * 32) < *BusMax - *BusMin + 1) {\r
+    DEBUG ((EFI_D_ERROR, "%a: invalid 'bus-range' and/or 'reg'\n",\r
+      __FUNCTION__));\r
+    return EFI_PROTOCOL_ERROR;\r
+  }\r
+\r
+  //\r
+  // Iterate over "ranges".\r
+  //\r
+  Status = FdtClient->GetNodeProperty (FdtClient, Node, "ranges", &Prop, &Len);\r
+  if (EFI_ERROR (Status) || Len == 0 ||\r
+      Len % sizeof (DTB_PCI_HOST_RANGE_RECORD) != 0) {\r
+    DEBUG ((EFI_D_ERROR, "%a: 'ranges' not found or invalid\n", __FUNCTION__));\r
+    return EFI_PROTOCOL_ERROR;\r
+  }\r
+\r
+  for (RecordIdx = 0; RecordIdx < Len / sizeof (DTB_PCI_HOST_RANGE_RECORD);\r
+       ++RecordIdx) {\r
+    CONST DTB_PCI_HOST_RANGE_RECORD *Record;\r
+\r
+    Record = (CONST DTB_PCI_HOST_RANGE_RECORD *)Prop + RecordIdx;\r
+    switch (SwapBytes32 (Record->Type) & DTB_PCI_HOST_RANGE_TYPEMASK) {\r
+    case DTB_PCI_HOST_RANGE_IO:\r
+      *IoBase = SwapBytes64 (Record->ChildBase);\r
+      *IoSize = SwapBytes64 (Record->Size);\r
+      *IoTranslation = SwapBytes64 (Record->CpuBase) - *IoBase;\r
+      break;\r
+\r
+    case DTB_PCI_HOST_RANGE_MMIO32:\r
+      *MmioBase = SwapBytes64 (Record->ChildBase);\r
+      *MmioSize = SwapBytes64 (Record->Size);\r
+      *MmioTranslation = SwapBytes64 (Record->CpuBase) - *MmioBase;\r
+\r
+      if (*MmioBase > MAX_UINT32 || *MmioSize > MAX_UINT32 ||\r
+          *MmioBase + *MmioSize > SIZE_4GB) {\r
+        DEBUG ((EFI_D_ERROR, "%a: MMIO32 space invalid\n", __FUNCTION__));\r
+        return EFI_PROTOCOL_ERROR;\r
+      }\r
+\r
+      if (*MmioTranslation != 0) {\r
+        DEBUG ((EFI_D_ERROR, "%a: unsupported nonzero MMIO32 translation "\r
+          "0x%Lx\n", __FUNCTION__, *MmioTranslation));\r
+        return EFI_UNSUPPORTED;\r
+      }\r
+\r
+      break;\r
+    }\r
+  }\r
+  if (*IoSize == 0 || *MmioSize == 0) {\r
+    DEBUG ((EFI_D_ERROR, "%a: %a space empty\n", __FUNCTION__,\r
+      (*IoSize == 0) ? "IO" : "MMIO32"));\r
+    return EFI_PROTOCOL_ERROR;\r
+  }\r
+\r
+  //\r
+  // The dynamic PCD PcdPciExpressBaseAddress should have already been set,\r
+  // and should match the value we found in the DT node.\r
+  //\r
+  ASSERT (PcdGet64 (PcdPciExpressBaseAddress) == ConfigBase);\r
+\r
+  SetLinuxPciProbeOnlyProperty (FdtClient);\r
+\r
+  DEBUG ((EFI_D_INFO, "%a: Config[0x%Lx+0x%Lx) Bus[0x%x..0x%x] "\r
+    "Io[0x%Lx+0x%Lx)@0x%Lx Mem[0x%Lx+0x%Lx)@0x%Lx\n", __FUNCTION__, ConfigBase,\r
+    ConfigSize, *BusMin, *BusMax, *IoBase, *IoSize, *IoTranslation, *MmioBase,\r
+    *MmioSize, *MmioTranslation));\r
+  return EFI_SUCCESS;\r
+}\r
+\r
+\r
 /**\r
   Entry point of this driver\r
 \r
@@ -103,25 +327,32 @@ InitializePciHostBridge (
   UINTN                       Loop2;\r
   PCI_HOST_BRIDGE_INSTANCE    *HostBridge;\r
   PCI_ROOT_BRIDGE_INSTANCE    *PrivateData;\r
+  UINT64                      IoBase, IoSize, IoTranslation;\r
+  UINT64                      MmioBase, MmioSize, MmioTranslation;\r
+  UINT32                      BusMin, BusMax;\r
 \r
   if (PcdGet64 (PcdPciExpressBaseAddress) == 0) {\r
     DEBUG ((EFI_D_INFO, "%a: PCI host bridge not present\n", __FUNCTION__));\r
     return EFI_ABORTED;\r
   }\r
 \r
+  Status = ProcessPciHost (&IoBase, &IoSize, &IoTranslation, &MmioBase,\r
+             &MmioSize, &MmioTranslation, &BusMin, &BusMax);\r
+  if (EFI_ERROR (Status)) {\r
+    return Status;\r
+  }\r
+\r
   mDriverImageHandle = ImageHandle;\r
 \r
-  mResAperture[0][0].BusBase  = PcdGet32 (PcdPciBusMin);\r
-  mResAperture[0][0].BusLimit = PcdGet32 (PcdPciBusMax);\r
+  mResAperture[0][0].BusBase  = BusMin;\r
+  mResAperture[0][0].BusLimit = BusMax;\r
 \r
-  mResAperture[0][0].MemBase  = PcdGet32 (PcdPciMmio32Base);\r
-  mResAperture[0][0].MemLimit = (UINT64)PcdGet32 (PcdPciMmio32Base) +\r
-                                PcdGet32 (PcdPciMmio32Size) - 1;\r
+  mResAperture[0][0].MemBase  = MmioBase;\r
+  mResAperture[0][0].MemLimit = MmioBase + MmioSize - 1;\r
 \r
-  mResAperture[0][0].IoBase        = PcdGet64 (PcdPciIoBase);\r
-  mResAperture[0][0].IoLimit       = PcdGet64 (PcdPciIoBase) +\r
-                                     PcdGet64 (PcdPciIoSize) - 1;\r
-  mResAperture[0][0].IoTranslation = PcdGet64 (PcdPciIoTranslation);\r
+  mResAperture[0][0].IoBase        = IoBase;\r
+  mResAperture[0][0].IoLimit       = IoBase + IoSize - 1;\r
+  mResAperture[0][0].IoTranslation = IoTranslation;\r
 \r
   //\r
   // Add IO and MMIO memory space, so that resources can be allocated in the\r
@@ -129,8 +360,8 @@ InitializePciHostBridge (
   //\r
   Status = gDS->AddIoSpace (\r
                   EfiGcdIoTypeIo,\r
-                  PcdGet64 (PcdPciIoBase),\r
-                  PcdGet64 (PcdPciIoSize)\r
+                  IoBase,\r
+                  IoSize\r
                   );\r
   ASSERT_EFI_ERROR (Status);\r
 \r
@@ -139,8 +370,8 @@ InitializePciHostBridge (
 \r
   Status = gDS->AddMemorySpace (\r
                   EfiGcdMemoryTypeMemoryMappedIo,\r
-                  PcdGet32 (PcdPciMmio32Base),\r
-                  PcdGet32 (PcdPciMmio32Size),\r
+                  MmioBase,\r
+                  MmioSize,\r
                   MmioAttributes\r
                   );\r
   if (EFI_ERROR (Status)) {\r
@@ -149,8 +380,8 @@ InitializePciHostBridge (
   }\r
 \r
   Status = gDS->SetMemorySpaceAttributes (\r
-                  PcdGet32 (PcdPciMmio32Base),\r
-                  PcdGet32 (PcdPciMmio32Size),\r
+                  MmioBase,\r
+                  MmioSize,\r
                   MmioAttributes\r
                   );\r
   if (EFI_ERROR (Status)) {\r
index 8161b546ff87a443b04479af97c39ef586081387..647fe1a52a7d7f7c8bc9dbaaeee526ea6c6d1506 100644 (file)
@@ -24,6 +24,7 @@
 #include <Protocol/PciRootBridgeIo.h>\r
 #include <Protocol/Metronome.h>\r
 #include <Protocol/DevicePath.h>\r
+#include <Protocol/FdtClient.h>\r
 \r
 \r
 #include <Library/BaseLib.h>\r
index 6d782582e02d4c7ac1acbb1243d543a4dedbe033..41e134b24b0ef58318a5865f80759f35a74bc921 100644 (file)
@@ -22,8 +22,8 @@
   ENTRY_POINT                    = InitializePciHostBridge\r
 \r
 [Packages]\r
+  MdeModulePkg/MdeModulePkg.dec\r
   MdePkg/MdePkg.dec\r
-  ArmPlatformPkg/ArmPlatformPkg.dec\r
   ArmVirtPkg/ArmVirtPkg.dec\r
 \r
 [LibraryClasses]\r
   gEfiPciRootBridgeIoProtocolGuid                       ## PRODUCES\r
   gEfiMetronomeArchProtocolGuid                         ## CONSUMES\r
   gEfiDevicePathProtocolGuid                            ## PRODUCES\r
+  gFdtClientProtocolGuid                                ## CONSUMES\r
 \r
 [Pcd]\r
-  gArmPlatformTokenSpaceGuid.PcdPciBusMin\r
-  gArmPlatformTokenSpaceGuid.PcdPciBusMax\r
-  gArmPlatformTokenSpaceGuid.PcdPciIoBase\r
-  gArmPlatformTokenSpaceGuid.PcdPciIoSize\r
-  gArmPlatformTokenSpaceGuid.PcdPciIoTranslation\r
-  gArmPlatformTokenSpaceGuid.PcdPciMmio32Base\r
-  gArmPlatformTokenSpaceGuid.PcdPciMmio32Size\r
   gEfiMdePkgTokenSpaceGuid.PcdPciExpressBaseAddress\r
 \r
 [FeaturePcd]\r
   gArmVirtTokenSpaceGuid.PcdKludgeMapPciMmioAsCached\r
+  gArmVirtTokenSpaceGuid.PcdPureAcpiBoot\r
 \r
 [depex]\r
   gEfiMetronomeArchProtocolGuid AND\r
-  gEfiCpuArchProtocolGuid\r
+  gEfiCpuArchProtocolGuid AND\r
+  gFdtClientProtocolGuid\r