#include <Library/DevicePathLib.h>\r
#include <Library/PcdLib.h>\r
#include <Library/DxeServicesLib.h>\r
+#include <Library/HobLib.h>\r
#include <libfdt.h>\r
+#include <Library/XenIoMmioLib.h>\r
\r
#include <Guid/Fdt.h>\r
#include <Guid/VirtioMmioTransport.h>\r
+#include <Guid/FdtHob.h>\r
\r
#pragma pack (1)\r
typedef struct {\r
PropertyTypeTimer,\r
PropertyTypePsci,\r
PropertyTypeFwCfg,\r
+ PropertyTypePciHost,\r
+ PropertyTypeGicV3,\r
+ PropertyTypeXen,\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
+ { PropertyTypeGicV3, "arm,gic-v3" },\r
+ { PropertyTypeXen, "xen,xen" },\r
+ { PropertyTypeUnknown, "" }\r
};\r
\r
typedef struct {\r
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
IN EFI_SYSTEM_TABLE *SystemTable\r
)\r
{\r
+ VOID *Hob;\r
VOID *DeviceTreeBase;\r
INT32 Node, Prev;\r
INT32 RtcNode;\r
VIRTIO_TRANSPORT_DEVICE_PATH *DevicePath;\r
EFI_HANDLE Handle;\r
UINT64 RegBase;\r
- UINT64 DistBase, CpuBase;\r
+ UINT64 DistBase, CpuBase, RedistBase;\r
CONST INTERRUPT_PROPERTY *InterruptProp;\r
INT32 SecIntrNum, IntrNum, VirtIntrNum, HypIntrNum;\r
CONST CHAR8 *PsciMethod;\r
UINT64 FwCfgDataAddress;\r
UINT64 FwCfgDataSize;\r
\r
- DeviceTreeBase = (VOID *)(UINTN)PcdGet64 (PcdDeviceTreeBaseAddress);\r
- ASSERT (DeviceTreeBase != NULL);\r
+ Hob = GetFirstGuidHob(&gFdtHobGuid);\r
+ if (Hob == NULL || GET_GUID_HOB_DATA_SIZE (Hob) != sizeof (UINT64)) {\r
+ return EFI_NOT_FOUND;\r
+ }\r
+ DeviceTreeBase = (VOID *)(UINTN)*(UINT64 *)GET_GUID_HOB_DATA (Hob);\r
\r
if (fdt_check_header (DeviceTreeBase) != 0) {\r
DEBUG ((EFI_D_ERROR, "%a: No DTB found @ 0x%p\n", __FUNCTION__, DeviceTreeBase));\r
(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
DEBUG ((EFI_D_INFO, "Found GIC @ 0x%Lx/0x%Lx\n", DistBase, CpuBase));\r
break;\r
\r
+ case PropertyTypeGicV3:\r
+ //\r
+ // The GIC v3 DT binding describes a series of at least 3 physical (base\r
+ // addresses, size) pairs: the distributor interface (GICD), at least one\r
+ // redistributor region (GICR) containing dedicated redistributor\r
+ // interfaces for all individual CPUs, and the CPU interface (GICC).\r
+ // Under virtualization, we assume that the first redistributor region\r
+ // listed covers the boot CPU. Also, our GICv3 driver only supports the\r
+ // system register CPU interface, so we can safely ignore the MMIO version\r
+ // which is listed after the sequence of redistributor interfaces.\r
+ // This means we are only interested in the first two memory regions\r
+ // supplied, and ignore everything else.\r
+ //\r
+ ASSERT (Len >= 32);\r
+\r
+ // RegProp[0..1] == { GICD base, GICD size }\r
+ DistBase = fdt64_to_cpu (((UINT64 *)RegProp)[0]);\r
+ ASSERT (DistBase < MAX_UINT32);\r
+\r
+ // RegProp[2..3] == { GICR base, GICR size }\r
+ RedistBase = fdt64_to_cpu (((UINT64 *)RegProp)[2]);\r
+ ASSERT (RedistBase < MAX_UINT32);\r
+\r
+ PcdSet32 (PcdGicDistributorBase, (UINT32)DistBase);\r
+ PcdSet32 (PcdGicRedistributorsBase, (UINT32)RedistBase);\r
+\r
+ DEBUG ((EFI_D_INFO, "Found GIC v3 (re)distributor @ 0x%Lx (0x%Lx)\n",\r
+ DistBase, RedistBase));\r
+ break;\r
+\r
case PropertyTypeRtc:\r
ASSERT (Len == 16);\r
\r
// hypervisor timers, in that order.\r
//\r
InterruptProp = fdt_getprop (DeviceTreeBase, Node, "interrupts", &Len);\r
- ASSERT (Len == 48);\r
+ ASSERT (Len == 36 || Len == 48);\r
\r
SecIntrNum = fdt32_to_cpu (InterruptProp[0].Number)\r
+ (InterruptProp[0].Type ? 16 : 0);\r
+ (InterruptProp[1].Type ? 16 : 0);\r
VirtIntrNum = fdt32_to_cpu (InterruptProp[2].Number)\r
+ (InterruptProp[2].Type ? 16 : 0);\r
- HypIntrNum = fdt32_to_cpu (InterruptProp[3].Number)\r
- + (InterruptProp[3].Type ? 16 : 0);\r
+ HypIntrNum = Len < 48 ? 0 : fdt32_to_cpu (InterruptProp[3].Number)\r
+ + (InterruptProp[3].Type ? 16 : 0);\r
\r
DEBUG ((EFI_D_INFO, "Found Timer interrupts %d, %d, %d, %d\n",\r
SecIntrNum, IntrNum, VirtIntrNum, HypIntrNum));\r
}\r
break;\r
\r
+ case PropertyTypeXen:\r
+ ASSERT (Len == 16);\r
+\r
+ //\r
+ // Retrieve the reg base from this node and wire it up to the\r
+ // MMIO flavor of the XenBus root device I/O protocol\r
+ //\r
+ RegBase = fdt64_to_cpu (((UINT64 *)RegProp)[0]);\r
+ Handle = NULL;\r
+ Status = XenIoMmioInstall (&Handle, RegBase);\r
+ if (EFI_ERROR (Status)) {\r
+ DEBUG ((EFI_D_ERROR, "%a: XenIoMmioInstall () failed on a new handle "\r
+ "(Status == %r)\n", __FUNCTION__, Status));\r
+ break;\r
+ }\r
+\r
+ DEBUG ((EFI_D_INFO, "Found Xen node with Grant table @ 0x%Lx\n", RegBase));\r
+\r
+ break;\r
+\r
default:\r
break;\r
}\r