diff --git a/ArmVirtPkg/Library/FdtPciPcdProducerLib/FdtPciPcdProducerLib.c b/ArmVirtPkg/Library/FdtPciPcdProducerLib/FdtPciPcdProducerLib.c index cc60940d48..10b47560cb 100644 --- a/ArmVirtPkg/Library/FdtPciPcdProducerLib/FdtPciPcdProducerLib.c +++ b/ArmVirtPkg/Library/FdtPciPcdProducerLib/FdtPciPcdProducerLib.c @@ -22,6 +22,68 @@ #include +// +// 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) + +STATIC +RETURN_STATUS +GetPciIoTranslation ( + IN FDT_CLIENT_PROTOCOL *FdtClient, + IN INT32 Node, + OUT UINT64 *IoTranslation + ) +{ + UINT32 RecordIdx; + CONST VOID *Prop; + UINT32 Len; + EFI_STATUS Status; + UINT64 IoBase; + + // + // Iterate over "ranges". + // + Status = FdtClient->GetNodeProperty (FdtClient, Node, "ranges", &Prop, &Len); + if (EFI_ERROR (Status) || Len == 0 || + Len % sizeof (DTB_PCI_HOST_RANGE_RECORD) != 0) { + DEBUG ((EFI_D_ERROR, "%a: 'ranges' not found or invalid\n", __FUNCTION__)); + return RETURN_PROTOCOL_ERROR; + } + + for (RecordIdx = 0; RecordIdx < Len / sizeof (DTB_PCI_HOST_RANGE_RECORD); + ++RecordIdx) { + CONST DTB_PCI_HOST_RANGE_RECORD *Record; + UINT32 Type; + + Record = (CONST DTB_PCI_HOST_RANGE_RECORD *)Prop + RecordIdx; + Type = SwapBytes32 (Record->Type) & DTB_PCI_HOST_RANGE_TYPEMASK; + if (Type == DTB_PCI_HOST_RANGE_IO) { + IoBase = SwapBytes64 (Record->ChildBase); + *IoTranslation = SwapBytes64 (Record->CpuBase) - IoBase; + + return RETURN_SUCCESS; + } + } + return RETURN_NOT_FOUND; +} + RETURN_STATUS EFIAPI FdtPciPcdProducerLibConstructor ( @@ -31,11 +93,21 @@ FdtPciPcdProducerLibConstructor ( UINT64 PciExpressBaseAddress; FDT_CLIENT_PROTOCOL *FdtClient; CONST UINT64 *Reg; - UINT32 RegElemSize, RegSize; + UINT32 RegSize; EFI_STATUS Status; + INT32 Node; + RETURN_STATUS RetStatus; + UINT64 IoTranslation; PciExpressBaseAddress = PcdGet64 (PcdPciExpressBaseAddress); if (PciExpressBaseAddress != MAX_UINT64) { + // + // Assume that the fact that PciExpressBaseAddress has been changed from + // its default value of MAX_UINT64 implies that this code has been + // executed already, in the context of another module. That means we can + // assume that PcdPciIoTranslation has been discovered from the DT node + // as well. + // return EFI_SUCCESS; } @@ -43,17 +115,33 @@ FdtPciPcdProducerLibConstructor ( (VOID **)&FdtClient); ASSERT_EFI_ERROR (Status); - Status = FdtClient->FindCompatibleNodeReg (FdtClient, - "pci-host-ecam-generic", (CONST VOID **)&Reg, - &RegElemSize, &RegSize); + PciExpressBaseAddress = 0; + Status = FdtClient->FindCompatibleNode (FdtClient, "pci-host-ecam-generic", + &Node); - if (EFI_ERROR (Status)) { - PciExpressBaseAddress = 0; - } else { - ASSERT (RegElemSize == sizeof (UINT64)); - PciExpressBaseAddress = SwapBytes64 (*Reg); + if (!EFI_ERROR (Status)) { + Status = FdtClient->GetNodeProperty (FdtClient, Node, "reg", + (CONST VOID **)&Reg, &RegSize); - PcdSetBool (PcdPciDisableBusEnumeration, FALSE); + if (!EFI_ERROR (Status) && RegSize == 2 * sizeof (UINT64)) { + PciExpressBaseAddress = SwapBytes64 (*Reg); + + PcdSetBool (PcdPciDisableBusEnumeration, FALSE); + + RetStatus = GetPciIoTranslation (FdtClient, Node, &IoTranslation); + if (!RETURN_ERROR (RetStatus)) { + PcdSet64 (PcdPciIoTranslation, IoTranslation); + } else { + // + // Support for I/O BARs is not mandatory, and so it does not make sense + // to abort in the general case. So leave it up to the actual driver to + // complain about this if it wants to, and just issue a warning here. + // + DEBUG ((EFI_D_WARN, + "%a: 'pci-host-ecam-generic' device encountered with no I/O range\n", + __FUNCTION__)); + } + } } PcdSet64 (PcdPciExpressBaseAddress, PciExpressBaseAddress); diff --git a/ArmVirtPkg/Library/FdtPciPcdProducerLib/FdtPciPcdProducerLib.inf b/ArmVirtPkg/Library/FdtPciPcdProducerLib/FdtPciPcdProducerLib.inf index 1ba71abea7..cd138fa1aa 100644 --- a/ArmVirtPkg/Library/FdtPciPcdProducerLib/FdtPciPcdProducerLib.inf +++ b/ArmVirtPkg/Library/FdtPciPcdProducerLib/FdtPciPcdProducerLib.inf @@ -26,6 +26,7 @@ FdtPciPcdProducerLib.c [Packages] + ArmPkg/ArmPkg.dec ArmVirtPkg/ArmVirtPkg.dec MdeModulePkg/MdeModulePkg.dec MdePkg/MdePkg.dec @@ -40,6 +41,7 @@ gFdtClientProtocolGuid ## CONSUMES [Pcd] + gArmTokenSpaceGuid.PcdPciIoTranslation ## PRODUCES gEfiMdePkgTokenSpaceGuid.PcdPciExpressBaseAddress ## PRODUCES gEfiMdeModulePkgTokenSpaceGuid.PcdPciDisableBusEnumeration ## PRODUCES