]> git.proxmox.com Git - mirror_edk2.git/commitdiff
ArmVirtualizationPkg/VirtFdtDxe: parse "pci-host-ecam-generic" properties
authorLaszlo Ersek <lersek@redhat.com>
Mon, 23 Feb 2015 16:02:50 +0000 (16:02 +0000)
committerlersek <lersek@Edk2>
Mon, 23 Feb 2015 16:02:50 +0000 (16:02 +0000)
In the Linux kernel tree,
"Documentation/devicetree/bindings/pci/host-generic-pci.txt" describes the
device tree bindings of a Generic PCI host controller.

Recent QEMU patches from Alexander Graf implement such a controller on the
"virt" machine type of qemu-system-(aarch64|arm):

  pcie@10000000 {
    //
    //                    (devfn<<8, 0, 0)  PCI irq
    //                    ----------------  -------
    interrupt-map-mask = <0x1800 0x0 0x0    0x7>;

    //                                        gic      irq
    //               (devfn<<8, 0, 0)  pin+1  phandle  (type, nr, level)
    //               ----------------  -----  -------- -----------------
    interrupt-map = <   0x0 0x0 0x0    0x1    0x8001   0x0 0x3 0x4
                        0x0 0x0 0x0    0x2    0x8001   0x0 0x4 0x4
                        0x0 0x0 0x0    0x3    0x8001   0x0 0x5 0x4
                        0x0 0x0 0x0    0x4    0x8001   0x0 0x6 0x4
                      0x800 0x0 0x0    0x1    0x8001   0x0 0x4 0x4
                      0x800 0x0 0x0    0x2    0x8001   0x0 0x5 0x4
                      0x800 0x0 0x0    0x3    0x8001   0x0 0x6 0x4
                      0x800 0x0 0x0    0x4    0x8001   0x0 0x3 0x4
                     0x1000 0x0 0x0    0x1    0x8001   0x0 0x5 0x4
                     0x1000 0x0 0x0    0x2    0x8001   0x0 0x6 0x4
                     0x1000 0x0 0x0    0x3    0x8001   0x0 0x3 0x4
                     0x1000 0x0 0x0    0x4    0x8001   0x0 0x4 0x4
                     0x1800 0x0 0x0    0x1    0x8001   0x0 0x6 0x4
                     0x1800 0x0 0x0    0x2    0x8001   0x0 0x3 0x4
                     0x1800 0x0 0x0    0x3    0x8001   0x0 0x4 0x4
                     0x1800 0x0 0x0    0x4    0x8001   0x0 0x5 0x4>;

    #interrupt-cells = <0x1>;

    //
    //                   child base      cpu base
    //        type       address         address         size
    //        ---------  --------------  --------------  --------------
    ranges = <0x1000000  0x0        0x0  0x0 0x3eff0000  0x0    0x10000
              0x2000000  0x0 0x10000000  0x0 0x10000000  0x0 0x2eff0000>;

    //
    //     PCIe config     PCIe config
    //     space base      space size
    //     --------------  -------------
    reg = <0x0 0x3f000000  0x0 0x1000000>;

    //
    // allowed bus numbers; inclusive range
    //
    bus-range = <0x0 0xf>;

    #size-cells = <0x2>;
    #address-cells = <0x3>;
    device_type = "pci";
    compatible = "pci-host-ecam-generic";
  };

Parse those properties of the compatible="pci-host-ecam-generic" node into
PCDs that are relevant for PCI enumeration in edk2:

- gEfiMdePkgTokenSpaceGuid.PcdPciExpressBaseAddress controls
  MdePkg/Library/BasePciExpressLib,

- gEfiMdeModulePkgTokenSpaceGuid.PcdPciDisableBusEnumeration controls
  OvmfPkg/AcpiPlatformDxe at this point,

- the rest have been introduced earlier in this patchset, and will control
  PCI range checks and translation.

Contributed-under: TianoCore Contribution Agreement 1.0
Signed-off-by: Laszlo Ersek <lersek@redhat.com>
Reviewed-by: Olivier Martin <olivier.martin@arm.com>
git-svn-id: https://svn.code.sf.net/p/edk2/code/trunk/edk2@16894 6f19259b-4bc3-4df7-8a09-765794883524

ArmPlatformPkg/ArmVirtualizationPkg/ArmVirtualizationQemu.dsc
ArmPlatformPkg/ArmVirtualizationPkg/VirtFdtDxe/VirtFdtDxe.c
ArmPlatformPkg/ArmVirtualizationPkg/VirtFdtDxe/VirtFdtDxe.inf

index 81e71039ab011e3810b859410f66f9ff9f110cb1..29d07788dcd40304003c870b882b784e3f88a941 100644 (file)
   gEfiIntelFrameworkModulePkgTokenSpaceGuid.PcdShellFile|{ 0x83, 0xA5, 0x04, 0x7C, 0x3E, 0x9E, 0x1C, 0x4F, 0xAD, 0x65, 0xE0, 0x52, 0x68, 0xD0, 0xB4, 0xD1 }\r
 !endif\r
 \r
+[PcdsDynamicDefault.common]\r
   ## If TRUE, OvmfPkg/AcpiPlatformDxe will not wait for PCI\r
   #  enumeration to complete before installing ACPI tables.\r
   gEfiMdeModulePkgTokenSpaceGuid.PcdPciDisableBusEnumeration|TRUE\r
 \r
-[PcdsDynamicDefault.common]\r
   # System Memory Size -- 1 MB initially, actual size will be fetched from DT\r
   gArmTokenSpaceGuid.PcdSystemMemorySize|0x00100000\r
 \r
   ## PL031 RealTimeClock\r
   gArmPlatformTokenSpaceGuid.PcdPL031RtcBase|0x0\r
 \r
+  gArmPlatformTokenSpaceGuid.PcdPciBusMin|0x0\r
+  gArmPlatformTokenSpaceGuid.PcdPciBusMax|0x0\r
+  gArmPlatformTokenSpaceGuid.PcdPciIoBase|0x0\r
+  gArmPlatformTokenSpaceGuid.PcdPciIoSize|0x0\r
+  gArmPlatformTokenSpaceGuid.PcdPciIoTranslation|0x0\r
+  gArmPlatformTokenSpaceGuid.PcdPciMmio32Base|0x0\r
+  gArmPlatformTokenSpaceGuid.PcdPciMmio32Size|0x0\r
+  gEfiMdePkgTokenSpaceGuid.PcdPciExpressBaseAddress|0x0\r
+\r
   gArmVirtualizationTokenSpaceGuid.PcdArmPsciMethod|0\r
 \r
   gArmVirtualizationTokenSpaceGuid.PcdFwCfgSelectorAddress|0x0\r
index 751864d4db9cff1a33f3580bb0e88eeeafd568ec..be74d62ad748fe14d74682f2a3b7753589379a3b 100644 (file)
@@ -46,23 +46,25 @@ typedef enum {
   PropertyTypeTimer,\r
   PropertyTypePsci,\r
   PropertyTypeFwCfg,\r
+  PropertyTypePciHost,\r
 } PROPERTY_TYPE;\r
 \r
 typedef struct {\r
   PROPERTY_TYPE Type;\r
-  CHAR8         Compatible[20];\r
+  CHAR8         Compatible[32];\r
 } PROPERTY;\r
 \r
 STATIC CONST PROPERTY CompatibleProperties[] = {\r
-  { PropertyTypeGic,     "arm,cortex-a15-gic"  },\r
-  { PropertyTypeRtc,     "arm,pl031"           },\r
-  { PropertyTypeVirtio,  "virtio,mmio"         },\r
-  { PropertyTypeUart,    "arm,pl011"           },\r
-  { PropertyTypeTimer,   "arm,armv7-timer"     },\r
-  { PropertyTypeTimer,   "arm,armv8-timer"     },\r
-  { PropertyTypePsci,    "arm,psci-0.2"        },\r
-  { PropertyTypeFwCfg,   "qemu,fw-cfg-mmio"    },\r
-  { PropertyTypeUnknown, ""                    }\r
+  { PropertyTypeGic,     "arm,cortex-a15-gic"    },\r
+  { PropertyTypeRtc,     "arm,pl031"             },\r
+  { PropertyTypeVirtio,  "virtio,mmio"           },\r
+  { PropertyTypeUart,    "arm,pl011"             },\r
+  { PropertyTypeTimer,   "arm,armv7-timer"       },\r
+  { PropertyTypeTimer,   "arm,armv8-timer"       },\r
+  { PropertyTypePsci,    "arm,psci-0.2"          },\r
+  { PropertyTypeFwCfg,   "qemu,fw-cfg-mmio"      },\r
+  { PropertyTypePciHost, "pci-host-ecam-generic" },\r
+  { PropertyTypeUnknown, ""                      }\r
 };\r
 \r
 typedef struct {\r
@@ -96,6 +98,176 @@ GetTypeFromNode (
   return PropertyTypeUnknown;\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
+/**\r
+  Process the device tree node describing the generic PCI host controller.\r
+\r
+  param[in] DeviceTreeBase  Pointer to the device tree.\r
+\r
+  param[in] Node            Offset of the device tree node whose "compatible"\r
+                            property is "pci-host-ecam-generic".\r
+\r
+  param[in] RegProp         Pointer to the "reg" property of Node. The caller\r
+                            is responsible for ensuring that the size of the\r
+                            property is 4 UINT32 cells.\r
+\r
+  @retval EFI_SUCCESS         Parsing successful, properties parsed from Node\r
+                              have been stored in dynamic PCDs.\r
+\r
+  @retval EFI_PROTOCOL_ERROR  Parsing failed. PCDs are left unchanged.\r
+**/\r
+STATIC\r
+EFI_STATUS\r
+EFIAPI\r
+ProcessPciHost (\r
+  IN CONST VOID *DeviceTreeBase,\r
+  IN INT32      Node,\r
+  IN CONST VOID *RegProp\r
+  )\r
+{\r
+  UINT64     ConfigBase, ConfigSize;\r
+  CONST VOID *Prop;\r
+  INT32      Len;\r
+  UINT32     BusMin, BusMax;\r
+  UINT32     RecordIdx;\r
+  UINT64     IoBase, IoSize, IoTranslation;\r
+  UINT64     MmioBase, MmioSize, MmioTranslation;\r
+\r
+  //\r
+  // Fetch the ECAM window.\r
+  //\r
+  ConfigBase = fdt64_to_cpu (((CONST UINT64 *)RegProp)[0]);\r
+  ConfigSize = fdt64_to_cpu (((CONST UINT64 *)RegProp)[1]);\r
+\r
+  //\r
+  // Fetch the bus range (note: inclusive).\r
+  //\r
+  Prop = fdt_getprop (DeviceTreeBase, Node, "bus-range", &Len);\r
+  if (Prop == NULL || 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 = fdt32_to_cpu (((CONST UINT32 *)Prop)[0]);\r
+  BusMax = fdt32_to_cpu (((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
+  Prop = fdt_getprop (DeviceTreeBase, Node, "ranges", &Len);\r
+  if (Prop == NULL || 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
+  //\r
+  // IoBase, IoTranslation, MmioBase and MmioTranslation are initialized only\r
+  // in order to suppress '-Werror=maybe-uninitialized' warnings *incorrectly*\r
+  // emitted by some gcc versions.\r
+  //\r
+  IoBase = 0;\r
+  IoTranslation = 0;\r
+  MmioBase = 0;\r
+  MmioTranslation = 0;\r
+\r
+  //\r
+  // IoSize and MmioSize are initialized to zero because the logic below\r
+  // requires it.\r
+  //\r
+  IoSize = 0;\r
+  MmioSize = 0;\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 (fdt32_to_cpu (Record->Type) & DTB_PCI_HOST_RANGE_TYPEMASK) {\r
+    case DTB_PCI_HOST_RANGE_IO:\r
+      IoBase = fdt64_to_cpu (Record->ChildBase);\r
+      IoSize = fdt64_to_cpu (Record->Size);\r
+      IoTranslation = fdt64_to_cpu (Record->CpuBase) - IoBase;\r
+      break;\r
+\r
+    case DTB_PCI_HOST_RANGE_MMIO32:\r
+      MmioBase = fdt64_to_cpu (Record->ChildBase);\r
+      MmioSize = fdt64_to_cpu (Record->Size);\r
+      MmioTranslation = fdt64_to_cpu (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
+  PcdSet64 (PcdPciExpressBaseAddress, ConfigBase);\r
+\r
+  PcdSet32 (PcdPciBusMin, BusMin);\r
+  PcdSet32 (PcdPciBusMax, BusMax);\r
+\r
+  PcdSet64 (PcdPciIoBase,        IoBase);\r
+  PcdSet64 (PcdPciIoSize,        IoSize);\r
+  PcdSet64 (PcdPciIoTranslation, IoTranslation);\r
+\r
+  PcdSet32 (PcdPciMmio32Base, (UINT32)MmioBase);\r
+  PcdSet32 (PcdPciMmio32Size, (UINT32)MmioSize);\r
+\r
+  PcdSetBool (PcdPciDisableBusEnumeration, FALSE);\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
 EFI_STATUS\r
 EFIAPI\r
 InitializeVirtFdtDxe (\r
@@ -167,6 +339,12 @@ InitializeVirtFdtDxe (
       (PropType == PropertyTypePsci));\r
 \r
     switch (PropType) {\r
+    case PropertyTypePciHost:\r
+      ASSERT (Len == 2 * sizeof (UINT64));\r
+      Status = ProcessPciHost (DeviceTreeBase, Node, RegProp);\r
+      ASSERT_EFI_ERROR (Status);\r
+      break;\r
+\r
     case PropertyTypeFwCfg:\r
       ASSERT (Len == 2 * sizeof (UINT64));\r
 \r
index 514ce2fdf658966997f01d00d57b0fb5617a507e..ff05c5e058fb3e198e854a28b54fc5899bffed8f 100644 (file)
@@ -27,6 +27,7 @@
 \r
 [Packages]\r
   MdePkg/MdePkg.dec\r
+  MdeModulePkg/MdeModulePkg.dec\r
   ArmPkg/ArmPkg.dec\r
   ArmPlatformPkg/ArmPlatformPkg.dec\r
   ArmPlatformPkg/ArmVirtualizationPkg/ArmVirtualizationPkg.dec\r
   gArmTokenSpaceGuid.PcdArmArchTimerVirtIntrNum\r
   gArmTokenSpaceGuid.PcdArmArchTimerHypIntrNum\r
   gArmPlatformTokenSpaceGuid.PcdPL031RtcBase\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
+  gEfiMdeModulePkgTokenSpaceGuid.PcdPciDisableBusEnumeration\r
 \r
 [Protocols]\r
   gEfiDevicePathProtocolGuid\r