diff --git a/ArmPlatformPkg/ArmVirtualizationPkg/ArmVirtualizationQemu.dsc b/ArmPlatformPkg/ArmVirtualizationPkg/ArmVirtualizationQemu.dsc index 81e71039ab..29d07788dc 100644 --- a/ArmPlatformPkg/ArmVirtualizationPkg/ArmVirtualizationQemu.dsc +++ b/ArmPlatformPkg/ArmVirtualizationPkg/ArmVirtualizationQemu.dsc @@ -156,11 +156,11 @@ gEfiIntelFrameworkModulePkgTokenSpaceGuid.PcdShellFile|{ 0x83, 0xA5, 0x04, 0x7C, 0x3E, 0x9E, 0x1C, 0x4F, 0xAD, 0x65, 0xE0, 0x52, 0x68, 0xD0, 0xB4, 0xD1 } !endif +[PcdsDynamicDefault.common] ## If TRUE, OvmfPkg/AcpiPlatformDxe will not wait for PCI # enumeration to complete before installing ACPI tables. gEfiMdeModulePkgTokenSpaceGuid.PcdPciDisableBusEnumeration|TRUE -[PcdsDynamicDefault.common] # System Memory Size -- 1 MB initially, actual size will be fetched from DT gArmTokenSpaceGuid.PcdSystemMemorySize|0x00100000 @@ -181,6 +181,15 @@ ## PL031 RealTimeClock gArmPlatformTokenSpaceGuid.PcdPL031RtcBase|0x0 + gArmPlatformTokenSpaceGuid.PcdPciBusMin|0x0 + gArmPlatformTokenSpaceGuid.PcdPciBusMax|0x0 + gArmPlatformTokenSpaceGuid.PcdPciIoBase|0x0 + gArmPlatformTokenSpaceGuid.PcdPciIoSize|0x0 + gArmPlatformTokenSpaceGuid.PcdPciIoTranslation|0x0 + gArmPlatformTokenSpaceGuid.PcdPciMmio32Base|0x0 + gArmPlatformTokenSpaceGuid.PcdPciMmio32Size|0x0 + gEfiMdePkgTokenSpaceGuid.PcdPciExpressBaseAddress|0x0 + gArmVirtualizationTokenSpaceGuid.PcdArmPsciMethod|0 gArmVirtualizationTokenSpaceGuid.PcdFwCfgSelectorAddress|0x0 diff --git a/ArmPlatformPkg/ArmVirtualizationPkg/VirtFdtDxe/VirtFdtDxe.c b/ArmPlatformPkg/ArmVirtualizationPkg/VirtFdtDxe/VirtFdtDxe.c index 751864d4db..be74d62ad7 100644 --- a/ArmPlatformPkg/ArmVirtualizationPkg/VirtFdtDxe/VirtFdtDxe.c +++ b/ArmPlatformPkg/ArmVirtualizationPkg/VirtFdtDxe/VirtFdtDxe.c @@ -46,23 +46,25 @@ typedef enum { PropertyTypeTimer, PropertyTypePsci, PropertyTypeFwCfg, + PropertyTypePciHost, } PROPERTY_TYPE; typedef struct { PROPERTY_TYPE Type; - CHAR8 Compatible[20]; + CHAR8 Compatible[32]; } PROPERTY; STATIC CONST PROPERTY CompatibleProperties[] = { - { PropertyTypeGic, "arm,cortex-a15-gic" }, - { PropertyTypeRtc, "arm,pl031" }, - { PropertyTypeVirtio, "virtio,mmio" }, - { PropertyTypeUart, "arm,pl011" }, - { PropertyTypeTimer, "arm,armv7-timer" }, - { PropertyTypeTimer, "arm,armv8-timer" }, - { PropertyTypePsci, "arm,psci-0.2" }, - { PropertyTypeFwCfg, "qemu,fw-cfg-mmio" }, - { PropertyTypeUnknown, "" } + { PropertyTypeGic, "arm,cortex-a15-gic" }, + { PropertyTypeRtc, "arm,pl031" }, + { PropertyTypeVirtio, "virtio,mmio" }, + { PropertyTypeUart, "arm,pl011" }, + { PropertyTypeTimer, "arm,armv7-timer" }, + { PropertyTypeTimer, "arm,armv8-timer" }, + { PropertyTypePsci, "arm,psci-0.2" }, + { PropertyTypeFwCfg, "qemu,fw-cfg-mmio" }, + { PropertyTypePciHost, "pci-host-ecam-generic" }, + { PropertyTypeUnknown, "" } }; typedef struct { @@ -96,6 +98,176 @@ GetTypeFromNode ( return PropertyTypeUnknown; } +// +// 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] DeviceTreeBase Pointer to the device tree. + + param[in] Node Offset of the device tree node whose "compatible" + property is "pci-host-ecam-generic". + + param[in] RegProp Pointer to the "reg" property of Node. The caller + is responsible for ensuring that the size of the + property is 4 UINT32 cells. + + @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 ( + IN CONST VOID *DeviceTreeBase, + IN INT32 Node, + IN CONST VOID *RegProp + ) +{ + UINT64 ConfigBase, ConfigSize; + CONST VOID *Prop; + INT32 Len; + UINT32 BusMin, BusMax; + UINT32 RecordIdx; + UINT64 IoBase, IoSize, IoTranslation; + UINT64 MmioBase, MmioSize, MmioTranslation; + + // + // Fetch the ECAM window. + // + ConfigBase = fdt64_to_cpu (((CONST UINT64 *)RegProp)[0]); + ConfigSize = fdt64_to_cpu (((CONST UINT64 *)RegProp)[1]); + + // + // Fetch the bus range (note: inclusive). + // + Prop = fdt_getprop (DeviceTreeBase, Node, "bus-range", &Len); + if (Prop == NULL || Len != 2 * sizeof(UINT32)) { + DEBUG ((EFI_D_ERROR, "%a: 'bus-range' not found or invalid\n", + __FUNCTION__)); + return EFI_PROTOCOL_ERROR; + } + BusMin = fdt32_to_cpu (((CONST UINT32 *)Prop)[0]); + BusMax = 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 (BusMax < BusMin || BusMax - BusMin == MAX_UINT32 || + DivU64x32 (ConfigSize, SIZE_4KB * 8 * 32) < BusMax - BusMin + 1) { + DEBUG ((EFI_D_ERROR, "%a: invalid 'bus-range' and/or 'reg'\n", + __FUNCTION__)); + return EFI_PROTOCOL_ERROR; + } + + // + // Iterate over "ranges". + // + Prop = fdt_getprop (DeviceTreeBase, Node, "ranges", &Len); + if (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; + } + + // + // IoBase, IoTranslation, MmioBase and MmioTranslation are initialized only + // in order to suppress '-Werror=maybe-uninitialized' warnings *incorrectly* + // emitted by some gcc versions. + // + IoBase = 0; + IoTranslation = 0; + MmioBase = 0; + MmioTranslation = 0; + + // + // IoSize and MmioSize are initialized to zero because the logic below + // requires it. + // + IoSize = 0; + MmioSize = 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: + IoBase = fdt64_to_cpu (Record->ChildBase); + IoSize = fdt64_to_cpu (Record->Size); + IoTranslation = fdt64_to_cpu (Record->CpuBase) - IoBase; + break; + + case DTB_PCI_HOST_RANGE_MMIO32: + MmioBase = fdt64_to_cpu (Record->ChildBase); + MmioSize = fdt64_to_cpu (Record->Size); + MmioTranslation = fdt64_to_cpu (Record->CpuBase) - MmioBase; + + if (MmioBase > MAX_UINT32 || MmioSize > MAX_UINT32 || + MmioBase + MmioSize > SIZE_4GB) { + DEBUG ((EFI_D_ERROR, "%a: MMIO32 space invalid\n", __FUNCTION__)); + return EFI_PROTOCOL_ERROR; + } + + if (MmioTranslation != 0) { + DEBUG ((EFI_D_ERROR, "%a: unsupported nonzero MMIO32 translation " + "0x%Lx\n", __FUNCTION__, MmioTranslation)); + return EFI_UNSUPPORTED; + } + + break; + } + } + if (IoSize == 0 || MmioSize == 0) { + DEBUG ((EFI_D_ERROR, "%a: %a space empty\n", __FUNCTION__, + (IoSize == 0) ? "IO" : "MMIO32")); + return EFI_PROTOCOL_ERROR; + } + + PcdSet64 (PcdPciExpressBaseAddress, ConfigBase); + + PcdSet32 (PcdPciBusMin, BusMin); + PcdSet32 (PcdPciBusMax, BusMax); + + PcdSet64 (PcdPciIoBase, IoBase); + PcdSet64 (PcdPciIoSize, IoSize); + PcdSet64 (PcdPciIoTranslation, IoTranslation); + + PcdSet32 (PcdPciMmio32Base, (UINT32)MmioBase); + PcdSet32 (PcdPciMmio32Size, (UINT32)MmioSize); + + PcdSetBool (PcdPciDisableBusEnumeration, FALSE); + + 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, BusMin, BusMax, IoBase, IoSize, IoTranslation, MmioBase, + MmioSize, MmioTranslation)); + return EFI_SUCCESS; +} + + EFI_STATUS EFIAPI InitializeVirtFdtDxe ( @@ -167,6 +339,12 @@ InitializeVirtFdtDxe ( (PropType == PropertyTypePsci)); switch (PropType) { + case PropertyTypePciHost: + ASSERT (Len == 2 * sizeof (UINT64)); + Status = ProcessPciHost (DeviceTreeBase, Node, RegProp); + ASSERT_EFI_ERROR (Status); + break; + case PropertyTypeFwCfg: ASSERT (Len == 2 * sizeof (UINT64)); diff --git a/ArmPlatformPkg/ArmVirtualizationPkg/VirtFdtDxe/VirtFdtDxe.inf b/ArmPlatformPkg/ArmVirtualizationPkg/VirtFdtDxe/VirtFdtDxe.inf index 514ce2fdf6..ff05c5e058 100644 --- a/ArmPlatformPkg/ArmVirtualizationPkg/VirtFdtDxe/VirtFdtDxe.inf +++ b/ArmPlatformPkg/ArmVirtualizationPkg/VirtFdtDxe/VirtFdtDxe.inf @@ -27,6 +27,7 @@ [Packages] MdePkg/MdePkg.dec + MdeModulePkg/MdeModulePkg.dec ArmPkg/ArmPkg.dec ArmPlatformPkg/ArmPlatformPkg.dec ArmPlatformPkg/ArmVirtualizationPkg/ArmVirtualizationPkg.dec @@ -57,6 +58,15 @@ gArmTokenSpaceGuid.PcdArmArchTimerVirtIntrNum gArmTokenSpaceGuid.PcdArmArchTimerHypIntrNum gArmPlatformTokenSpaceGuid.PcdPL031RtcBase + gArmPlatformTokenSpaceGuid.PcdPciBusMin + gArmPlatformTokenSpaceGuid.PcdPciBusMax + gArmPlatformTokenSpaceGuid.PcdPciIoBase + gArmPlatformTokenSpaceGuid.PcdPciIoSize + gArmPlatformTokenSpaceGuid.PcdPciIoTranslation + gArmPlatformTokenSpaceGuid.PcdPciMmio32Base + gArmPlatformTokenSpaceGuid.PcdPciMmio32Size + gEfiMdePkgTokenSpaceGuid.PcdPciExpressBaseAddress + gEfiMdeModulePkgTokenSpaceGuid.PcdPciDisableBusEnumeration [Protocols] gEfiDevicePathProtocolGuid