#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
\r
typedef enum {\r
PropertyTypeUnknown,\r
- PropertyTypeGic,\r
- PropertyTypeRtc,\r
PropertyTypeVirtio,\r
- PropertyTypeUart,\r
- PropertyTypeTimer,\r
- PropertyTypePsci,\r
- PropertyTypeFwCfg,\r
- PropertyTypePciHost,\r
- PropertyTypeGicV3,\r
PropertyTypeXen,\r
} PROPERTY_TYPE;\r
\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
- { PropertyTypePciHost, "pci-host-ecam-generic" },\r
- { PropertyTypeGicV3, "arm,gic-v3" },\r
{ PropertyTypeXen, "xen,xen" },\r
{ PropertyTypeUnknown, "" }\r
};\r
\r
-typedef struct {\r
- UINT32 Type;\r
- UINT32 Number;\r
- UINT32 Flags;\r
-} INTERRUPT_PROPERTY;\r
-\r
STATIC\r
PROPERTY_TYPE\r
GetTypeFromNode (\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
VOID *Hob;\r
VOID *DeviceTreeBase;\r
INT32 Node, Prev;\r
- INT32 RtcNode;\r
EFI_STATUS Status;\r
CONST CHAR8 *Type;\r
INT32 Len;\r
VIRTIO_TRANSPORT_DEVICE_PATH *DevicePath;\r
EFI_HANDLE Handle;\r
UINT64 RegBase;\r
- UINT64 DistBase, CpuBase, RedistBase;\r
- CONST INTERRUPT_PROPERTY *InterruptProp;\r
- INT32 SecIntrNum, IntrNum, VirtIntrNum, HypIntrNum;\r
- CONST CHAR8 *PsciMethod;\r
- UINT64 FwCfgSelectorAddress;\r
- UINT64 FwCfgSelectorSize;\r
- UINT64 FwCfgDataAddress;\r
- UINT64 FwCfgDataSize;\r
\r
Hob = GetFirstGuidHob(&gFdtHobGuid);\r
if (Hob == NULL || GET_GUID_HOB_DATA_SIZE (Hob) != sizeof (UINT64)) {\r
return EFI_NOT_FOUND;\r
}\r
\r
- Status = gBS->InstallConfigurationTable (&gFdtTableGuid, DeviceTreeBase);\r
- ASSERT_EFI_ERROR (Status);\r
-\r
DEBUG ((EFI_D_INFO, "%a: DTB @ 0x%p\n", __FUNCTION__, DeviceTreeBase));\r
\r
- RtcNode = -1;\r
//\r
// Now enumerate the nodes and install peripherals that we are interested in,\r
// i.e., GIC, RTC and virtio MMIO nodes\r
// TODO use #cells root properties instead\r
//\r
RegProp = fdt_getprop (DeviceTreeBase, Node, "reg", &Len);\r
- ASSERT ((RegProp != NULL) || (PropType == PropertyTypeTimer) ||\r
- (PropType == PropertyTypePsci));\r
+ ASSERT (RegProp != NULL);\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
- FwCfgDataAddress = fdt64_to_cpu (((UINT64 *)RegProp)[0]);\r
- FwCfgDataSize = 8;\r
- FwCfgSelectorAddress = FwCfgDataAddress + FwCfgDataSize;\r
- FwCfgSelectorSize = 2;\r
-\r
- //\r
- // The following ASSERT()s express\r
- //\r
- // Address + Size - 1 <= MAX_UINTN\r
- //\r
- // for both registers, that is, that the last byte in each MMIO range is\r
- // expressible as a MAX_UINTN. The form below is mathematically\r
- // equivalent, and it also prevents any unsigned overflow before the\r
- // comparison.\r
- //\r
- ASSERT (FwCfgSelectorAddress <= MAX_UINTN - FwCfgSelectorSize + 1);\r
- ASSERT (FwCfgDataAddress <= MAX_UINTN - FwCfgDataSize + 1);\r
-\r
- PcdSet64 (PcdFwCfgSelectorAddress, FwCfgSelectorAddress);\r
- PcdSet64 (PcdFwCfgDataAddress, FwCfgDataAddress);\r
-\r
- DEBUG ((EFI_D_INFO, "Found FwCfg @ 0x%Lx/0x%Lx\n", FwCfgSelectorAddress,\r
- FwCfgDataAddress));\r
- break;\r
-\r
case PropertyTypeVirtio:\r
ASSERT (Len == 16);\r
//\r
}\r
break;\r
\r
- case PropertyTypeGic:\r
- ASSERT (Len == 32);\r
-\r
- DistBase = fdt64_to_cpu (((UINT64 *)RegProp)[0]);\r
- CpuBase = fdt64_to_cpu (((UINT64 *)RegProp)[2]);\r
- ASSERT (DistBase < MAX_UINT32);\r
- ASSERT (CpuBase < MAX_UINT32);\r
-\r
- PcdSet32 (PcdGicDistributorBase, (UINT32)DistBase);\r
- PcdSet32 (PcdGicInterruptInterfaceBase, (UINT32)CpuBase);\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
- RegBase = fdt64_to_cpu (((UINT64 *)RegProp)[0]);\r
- ASSERT (RegBase < MAX_UINT32);\r
-\r
- PcdSet32 (PcdPL031RtcBase, (UINT32)RegBase);\r
-\r
- DEBUG ((EFI_D_INFO, "Found PL031 RTC @ 0x%Lx\n", RegBase));\r
- RtcNode = Node;\r
- break;\r
-\r
- case PropertyTypeTimer:\r
- //\r
- // - interrupts : Interrupt list for secure, non-secure, virtual and\r
- // hypervisor timers, in that order.\r
- //\r
- InterruptProp = fdt_getprop (DeviceTreeBase, Node, "interrupts", &Len);\r
- ASSERT (Len == 36 || Len == 48);\r
-\r
- SecIntrNum = fdt32_to_cpu (InterruptProp[0].Number)\r
- + (InterruptProp[0].Type ? 16 : 0);\r
- IntrNum = fdt32_to_cpu (InterruptProp[1].Number)\r
- + (InterruptProp[1].Type ? 16 : 0);\r
- VirtIntrNum = fdt32_to_cpu (InterruptProp[2].Number)\r
- + (InterruptProp[2].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
- PcdSet32 (PcdArmArchTimerSecIntrNum, SecIntrNum);\r
- PcdSet32 (PcdArmArchTimerIntrNum, IntrNum);\r
- PcdSet32 (PcdArmArchTimerVirtIntrNum, VirtIntrNum);\r
- PcdSet32 (PcdArmArchTimerHypIntrNum, HypIntrNum);\r
- break;\r
-\r
- case PropertyTypePsci:\r
- PsciMethod = fdt_getprop (DeviceTreeBase, Node, "method", &Len);\r
-\r
- if (PsciMethod && AsciiStrnCmp (PsciMethod, "hvc", 3) == 0) {\r
- PcdSet32 (PcdArmPsciMethod, 1);\r
- } else if (PsciMethod && AsciiStrnCmp (PsciMethod, "smc", 3) == 0) {\r
- PcdSet32 (PcdArmPsciMethod, 2);\r
- } else {\r
- DEBUG ((EFI_D_ERROR, "%a: Unknown PSCI method \"%a\"\n", __FUNCTION__,\r
- PsciMethod));\r
- }\r
- break;\r
-\r
case PropertyTypeXen:\r
ASSERT (Len == 16);\r
\r
}\r
}\r
\r
- //\r
- // UEFI takes ownership of the RTC hardware, and exposes its functionality\r
- // through the UEFI Runtime Services GetTime, SetTime, etc. This means we\r
- // need to disable it in the device tree to prevent the OS from attaching its\r
- // device driver as well.\r
- //\r
- if ((RtcNode != -1) &&\r
- fdt_setprop_string (DeviceTreeBase, RtcNode, "status",\r
- "disabled") != 0) {\r
- DEBUG ((EFI_D_WARN, "Failed to set PL031 status to 'disabled'\n"));\r
- }\r
return EFI_SUCCESS;\r
}\r