From f16817ec8491847c8544a9ef44b1777c9daf4ccc Mon Sep 17 00:00:00 2001 From: Pierre Gondois Date: Mon, 10 Jun 2024 14:00:00 +0200 Subject: DynamicTablesPkg: FdtHwInfoParserLib: Make Pci parser arch neutral To allow other architectures to potentially re-use the serial port parser and make the code arch neutral, remove the Arm prefixes. Also remove the check searching for a GIC version. Suggested-by: Sunil V L Signed-off-by: Pierre Gondois Reviewed-by: Sami Mujawar --- .../FdtHwInfoParserLib/Arm/ArmFdtHwInfoParser.c | 4 +- .../FdtHwInfoParserLib/FdtHwInfoParserLib.inf | 4 +- .../Pci/ArmPciConfigSpaceParser.c | 815 --------------------- .../Pci/ArmPciConfigSpaceParser.h | 143 ---- .../FdtHwInfoParserLib/Pci/PciConfigSpaceParser.c | 806 ++++++++++++++++++++ .../FdtHwInfoParserLib/Pci/PciConfigSpaceParser.h | 143 ++++ 6 files changed, 953 insertions(+), 962 deletions(-) delete mode 100644 DynamicTablesPkg/Library/FdtHwInfoParserLib/Pci/ArmPciConfigSpaceParser.c delete mode 100644 DynamicTablesPkg/Library/FdtHwInfoParserLib/Pci/ArmPciConfigSpaceParser.h create mode 100644 DynamicTablesPkg/Library/FdtHwInfoParserLib/Pci/PciConfigSpaceParser.c create mode 100644 DynamicTablesPkg/Library/FdtHwInfoParserLib/Pci/PciConfigSpaceParser.h (limited to 'DynamicTablesPkg') diff --git a/DynamicTablesPkg/Library/FdtHwInfoParserLib/Arm/ArmFdtHwInfoParser.c b/DynamicTablesPkg/Library/FdtHwInfoParserLib/Arm/ArmFdtHwInfoParser.c index 403402731a..49a7636dad 100644 --- a/DynamicTablesPkg/Library/FdtHwInfoParserLib/Arm/ArmFdtHwInfoParser.c +++ b/DynamicTablesPkg/Library/FdtHwInfoParserLib/Arm/ArmFdtHwInfoParser.c @@ -9,7 +9,7 @@ #include "Arm/BootArch/ArmBootArchParser.h" #include "Arm/GenericTimer/ArmGenericTimerParser.h" #include "Arm/Gic/ArmGicDispatcher.h" -#include "Pci/ArmPciConfigSpaceParser.h" +#include "Pci/PciConfigSpaceParser.h" #include "Serial/ArmSerialPortParser.h" /** Ordered table of parsers/dispatchers. @@ -25,7 +25,7 @@ STATIC CONST FDT_HW_INFO_PARSER_FUNC HwInfoParserTable[] = { ArmBootArchInfoParser, ArmGenericTimerInfoParser, ArmGicDispatcher, - ArmPciConfigInfoParser, + PciConfigInfoParser, SerialPortDispatcher }; diff --git a/DynamicTablesPkg/Library/FdtHwInfoParserLib/FdtHwInfoParserLib.inf b/DynamicTablesPkg/Library/FdtHwInfoParserLib/FdtHwInfoParserLib.inf index 3abc6a0fd2..55ec7d97fa 100644 --- a/DynamicTablesPkg/Library/FdtHwInfoParserLib/FdtHwInfoParserLib.inf +++ b/DynamicTablesPkg/Library/FdtHwInfoParserLib/FdtHwInfoParserLib.inf @@ -22,8 +22,8 @@ FdtHwInfoParser.h FdtUtility.c FdtUtility.h - Pci/ArmPciConfigSpaceParser.c - Pci/ArmPciConfigSpaceParser.h + Pci/PciConfigSpaceParser.c + Pci/PciConfigSpaceParser.h Serial/ArmSerialPortParser.c Serial/ArmSerialPortParser.h diff --git a/DynamicTablesPkg/Library/FdtHwInfoParserLib/Pci/ArmPciConfigSpaceParser.c b/DynamicTablesPkg/Library/FdtHwInfoParserLib/Pci/ArmPciConfigSpaceParser.c deleted file mode 100644 index 5d11863dfe..0000000000 --- a/DynamicTablesPkg/Library/FdtHwInfoParserLib/Pci/ArmPciConfigSpaceParser.c +++ /dev/null @@ -1,815 +0,0 @@ -/** @file - Arm PCI Configuration Space Parser. - - Copyright (c) 2021, ARM Limited. All rights reserved.
- SPDX-License-Identifier: BSD-2-Clause-Patent - - @par Reference(s): - - linux/Documentation/devicetree/bindings/pci/host-generic-pci.yaml - - PCI Firmware Specification - Revision 3.0 - - Open Firmware Recommended Practice: Interrupt Mapping, Version 0.9 - - Devicetree Specification Release v0.3 - - linux kernel code -**/ - -#include "CmObjectDescUtility.h" -#include - -#include "FdtHwInfoParser.h" -#include "Pci/ArmPciConfigSpaceParser.h" -#include "Arm/Gic/ArmGicDispatcher.h" - -/** List of "compatible" property values for host PCIe bridges nodes. - - Any other "compatible" value is not supported by this module. -*/ -STATIC CONST COMPATIBILITY_STR PciCompatibleStr[] = { - { "pci-host-ecam-generic" } -}; - -/** COMPATIBILITY_INFO structure for the PCIe. -*/ -STATIC CONST COMPATIBILITY_INFO PciCompatibleInfo = { - ARRAY_SIZE (PciCompatibleStr), - PciCompatibleStr -}; - -/** Get the Segment group (also called: Domain Id) of a host-pci node. - - kernel/Documentation/devicetree/bindings/pci/pci.txt: - "It is required to either not set this property at all or set it for all - host bridges in the system" - - The function checks the "linux,pci-domain" property of the host-pci node. - Either all host-pci nodes must have this property, or none of them. If the - property is available, read it. Otherwise dynamically assign the Ids. - - @param [in] Fdt Pointer to a Flattened Device Tree (Fdt). - @param [in] HostPciNode Offset of a host-pci node. - @param [out] SegGroup Segment group assigned to the host-pci controller. - - @retval EFI_SUCCESS The function completed successfully. - @retval EFI_ABORTED An error occurred. - @retval EFI_INVALID_PARAMETER Invalid parameter. -**/ -STATIC -EFI_STATUS -EFIAPI -GetPciSegGroup ( - IN CONST VOID *Fdt, - IN INT32 HostPciNode, - OUT INT32 *SegGroup - ) -{ - CONST UINT8 *Data; - INT32 DataSize; - STATIC INT32 LocalSegGroup = 0; - - if ((Fdt == NULL) || - (SegGroup == NULL)) - { - ASSERT (0); - return EFI_INVALID_PARAMETER; - } - - Data = fdt_getprop (Fdt, HostPciNode, "linux,pci-domain", &DataSize); - if ((Data == NULL) || (DataSize < 0)) { - // Did not find property, assign the DomainIds ourselves. - if (LocalSegGroup < 0) { - // "linux,pci-domain" property was defined for another node. - ASSERT (0); - return EFI_ABORTED; - } - - *SegGroup = LocalSegGroup++; - return EFI_SUCCESS; - } - - if ((DataSize > sizeof (UINT32)) || - (LocalSegGroup > 0)) - { - // Property on more than 1 cell or - // "linux,pci-domain" property was not defined for a node. - ASSERT (0); - return EFI_ABORTED; - } - - // If one node has the "linux,pci-domain" property, then all the host-pci - // nodes must have it. - LocalSegGroup = -1; - - *SegGroup = fdt32_to_cpu (*(UINT32 *)Data); - return EFI_SUCCESS; -} - -/** Parse the bus-range controlled by this host-pci node. - - @param [in] Fdt Pointer to a Flattened Device Tree (Fdt). - @param [in] HostPciNode Offset of a host-pci node. - @param [in, out] PciInfo PCI_PARSER_TABLE structure storing - information about the current host-pci. - - @retval EFI_SUCCESS The function completed successfully. - @retval EFI_ABORTED An error occurred. - @retval EFI_INVALID_PARAMETER Invalid parameter. -**/ -STATIC -EFI_STATUS -EFIAPI -PopulateBusRange ( - IN CONST VOID *Fdt, - IN INT32 HostPciNode, - IN OUT PCI_PARSER_TABLE *PciInfo - ) -{ - CONST UINT8 *Data; - INT32 DataSize; - UINT32 StartBus; - UINT32 EndBus; - - if ((Fdt == NULL) || - (PciInfo == NULL)) - { - ASSERT (0); - return EFI_INVALID_PARAMETER; - } - - Data = fdt_getprop (Fdt, HostPciNode, "bus-range", &DataSize); - if ((Data == NULL) || (DataSize < 0)) { - // No evidence this property is mandatory. Use default values. - StartBus = 0; - EndBus = 255; - } else if (DataSize == (2 * sizeof (UINT32))) { - // If available, the property is on two integers. - StartBus = fdt32_to_cpu (((UINT32 *)Data)[0]); - EndBus = fdt32_to_cpu (((UINT32 *)Data)[1]); - } else { - ASSERT (0); - return EFI_ABORTED; - } - - PciInfo->PciConfigSpaceInfo.StartBusNumber = StartBus; - PciInfo->PciConfigSpaceInfo.EndBusNumber = EndBus; - - return EFI_SUCCESS; -} - -/** Parse the PCI address map. - - The PCI address map is available in the "ranges" device-tree property. - - @param [in] Fdt Pointer to a Flattened Device Tree (Fdt). - @param [in] HostPciNode Offset of a host-pci node. - @param [in] AddressCells # of cells used to encode an address on - the parent bus. - @param [in, out] PciInfo PCI_PARSER_TABLE structure storing - information about the current host-pci. - - @retval EFI_SUCCESS The function completed successfully. - @retval EFI_ABORTED An error occurred. - @retval EFI_INVALID_PARAMETER Invalid parameter. - @retval EFI_OUT_OF_RESOURCES An allocation has failed. -**/ -STATIC -EFI_STATUS -EFIAPI -ParseAddressMap ( - IN CONST VOID *Fdt, - IN INT32 HostPciNode, - IN INT32 AddressCells, - IN OUT PCI_PARSER_TABLE *PciInfo - ) -{ - CONST UINT8 *Data; - INT32 DataSize; - UINT32 Index; - UINT32 Offset; - UINT32 AddressMapSize; - UINT32 Count; - UINT32 PciAddressAttr; - - CM_ARCH_COMMON_PCI_ADDRESS_MAP_INFO *PciAddressMapInfo; - UINT32 BufferSize; - - // The mapping is done on AddressMapSize bytes. - AddressMapSize = (PCI_ADDRESS_CELLS + AddressCells + PCI_SIZE_CELLS) * - sizeof (UINT32); - - Data = fdt_getprop (Fdt, HostPciNode, "ranges", &DataSize); - if ((Data == NULL) || - (DataSize < 0) || - ((DataSize % AddressMapSize) != 0)) - { - // If error or not on AddressMapSize bytes. - ASSERT (0); - return EFI_ABORTED; - } - - Count = DataSize / AddressMapSize; - - // Allocate a buffer to store each address mapping. - BufferSize = Count * sizeof (CM_ARCH_COMMON_PCI_ADDRESS_MAP_INFO); - PciAddressMapInfo = AllocateZeroPool (BufferSize); - if (PciAddressMapInfo == NULL) { - ASSERT (0); - return EFI_OUT_OF_RESOURCES; - } - - for (Index = 0; Index < Count; Index++) { - Offset = Index * AddressMapSize; - - // Pci address attributes - PciAddressAttr = fdt32_to_cpu (*(UINT32 *)&Data[Offset]); - PciAddressMapInfo[Index].SpaceCode = READ_PCI_SS (PciAddressAttr); - Offset += sizeof (UINT32); - - // Pci address - PciAddressMapInfo[Index].PciAddress = - fdt64_to_cpu (*(UINT64 *)&Data[Offset]); - Offset += (PCI_ADDRESS_CELLS - 1) * sizeof (UINT32); - - // Cpu address - if (AddressCells == 2) { - PciAddressMapInfo[Index].CpuAddress = - fdt64_to_cpu (*(UINT64 *)&Data[Offset]); - } else { - PciAddressMapInfo[Index].CpuAddress = - fdt32_to_cpu (*(UINT32 *)&Data[Offset]); - } - - Offset += AddressCells * sizeof (UINT32); - - // Address size - PciAddressMapInfo[Index].AddressSize = - fdt64_to_cpu (*(UINT64 *)&Data[Offset]); - Offset += PCI_SIZE_CELLS * sizeof (UINT32); - } // for - - PciInfo->Mapping[PciMappingTableAddress].ObjectId = - CREATE_CM_ARCH_COMMON_OBJECT_ID (EArchCommonObjPciAddressMapInfo); - PciInfo->Mapping[PciMappingTableAddress].Size = - sizeof (CM_ARCH_COMMON_PCI_ADDRESS_MAP_INFO) * Count; - PciInfo->Mapping[PciMappingTableAddress].Data = PciAddressMapInfo; - PciInfo->Mapping[PciMappingTableAddress].Count = Count; - - return EFI_SUCCESS; -} - -/** Parse the PCI interrupt map. - - The PCI interrupt map is available in the "interrupt-map" - and "interrupt-map-mask" device-tree properties. - - Cf Devicetree Specification Release v0.3, - s2.4.3 Interrupt Nexus Properties - - An interrupt-map must be as: - interrupt-map = < [child unit address] [child interrupt specifier] - [interrupt-parent] - [parent unit address] [parent interrupt specifier] > - - @param [in] Fdt Pointer to a Flattened Device Tree (Fdt). - @param [in] HostPciNode Offset of a host-pci node. - @param [in, out] PciInfo PCI_PARSER_TABLE structure storing - information about the current host-pci. - - @retval EFI_SUCCESS The function completed successfully. - @retval EFI_ABORTED An error occurred. - @retval EFI_INVALID_PARAMETER Invalid parameter. - @retval EFI_NOT_FOUND Not found. - @retval EFI_OUT_OF_RESOURCES An allocation has failed. -**/ -STATIC -EFI_STATUS -EFIAPI -ParseIrqMap ( - IN CONST VOID *Fdt, - IN INT32 HostPciNode, - IN OUT PCI_PARSER_TABLE *PciInfo - ) -{ - EFI_STATUS Status; - CONST UINT8 *Data; - INT32 DataSize; - UINT32 Index; - UINT32 Offset; - - INT32 IntcNode; - INT32 IntcAddressCells; - INT32 IntcCells; - - INT32 PciIntCells; - INT32 IntcPhandle; - - INT32 IrqMapSize; - UINT32 IrqMapCount; - CONST UINT8 *IrqMapMask; - INT32 IrqMapMaskSize; - - INT32 PHandleOffset; - UINT32 GicVersion; - - UINT32 PciAddressAttr; - - CM_ARCH_COMMON_PCI_INTERRUPT_MAP_INFO *PciInterruptMapInfo; - UINT32 BufferSize; - - Data = fdt_getprop (Fdt, HostPciNode, "interrupt-map", &DataSize); - if ((Data == NULL) || (DataSize <= 0)) { - DEBUG (( - DEBUG_WARN, - "Fdt parser: No Legacy interrupts found for PCI configuration space at " - "address: 0x%lx, group segment: %d\n", - PciInfo->PciConfigSpaceInfo.BaseAddress, - PciInfo->PciConfigSpaceInfo.PciSegmentGroupNumber - )); - return EFI_NOT_FOUND; - } - - // PCI interrupts are expected to be on 1 cell. Check it. - Status = FdtGetInterruptCellsInfo (Fdt, HostPciNode, &PciIntCells); - if (EFI_ERROR (Status)) { - ASSERT (0); - return Status; - } - - if (PciIntCells != PCI_INTERRUPTS_CELLS) { - ASSERT (0); - return EFI_ABORTED; - } - - IrqMapMask = fdt_getprop ( - Fdt, - HostPciNode, - "interrupt-map-mask", - &IrqMapMaskSize - ); - if ((IrqMapMask == NULL) || - (IrqMapMaskSize != - (PCI_ADDRESS_CELLS + PCI_INTERRUPTS_CELLS) * sizeof (UINT32))) - { - ASSERT (0); - return EFI_ABORTED; - } - - // Get the interrupt-controller of the first irq mapping. - PHandleOffset = (PCI_ADDRESS_CELLS + PciIntCells) * sizeof (UINT32); - if (PHandleOffset > DataSize) { - ASSERT (0); - return EFI_ABORTED; - } - - IntcPhandle = fdt32_to_cpu (*(UINT32 *)&Data[PHandleOffset]); - IntcNode = fdt_node_offset_by_phandle (Fdt, IntcPhandle); - if (IntcNode < 0) { - ASSERT (0); - return EFI_ABORTED; - } - - // Only support Gic(s) for now. - Status = GetGicVersion (Fdt, IntcNode, &GicVersion); - if (EFI_ERROR (Status)) { - ASSERT (0); - return Status; - } - - // Get the "address-cells" property of the IntcNode. - Status = FdtGetAddressInfo (Fdt, IntcNode, &IntcAddressCells, NULL); - if (EFI_ERROR (Status)) { - ASSERT (0); - return Status; - } - - // Get the "interrupt-cells" property of the IntcNode. - Status = FdtGetInterruptCellsInfo (Fdt, IntcNode, &IntcCells); - if (EFI_ERROR (Status)) { - ASSERT (0); - return Status; - } - - // An irq mapping is done on IrqMapSize bytes - // (which includes 1 cell for the PHandle). - IrqMapSize = (PCI_ADDRESS_CELLS + PciIntCells + 1 - + IntcAddressCells + IntcCells) * sizeof (UINT32); - if ((DataSize % IrqMapSize) != 0) { - // The mapping is not done on IrqMapSize bytes. - ASSERT (0); - return EFI_ABORTED; - } - - IrqMapCount = DataSize / IrqMapSize; - - // We assume the same interrupt-controller is used for all the mappings. - // Check this is correct. - for (Index = 0; Index < IrqMapCount; Index++) { - if (IntcPhandle != fdt32_to_cpu ( - *(UINT32 *)&Data[(Index * IrqMapSize) + PHandleOffset] - )) - { - ASSERT (0); - return EFI_ABORTED; - } - } - - // Allocate a buffer to store each interrupt mapping. - IrqMapCount = DataSize / IrqMapSize; - BufferSize = IrqMapCount * sizeof (CM_ARCH_COMMON_PCI_ADDRESS_MAP_INFO); - PciInterruptMapInfo = AllocateZeroPool (BufferSize); - if (PciInterruptMapInfo == NULL) { - ASSERT (0); - return EFI_OUT_OF_RESOURCES; - } - - for (Index = 0; Index < IrqMapCount; Index++) { - Offset = Index * IrqMapSize; - - // Pci address attributes - PciAddressAttr = fdt32_to_cpu ( - (*(UINT32 *)&Data[Offset]) & - (*(UINT32 *)&IrqMapMask[0]) - ); - PciInterruptMapInfo[Index].PciBus = READ_PCI_BBBBBBBB (PciAddressAttr); - PciInterruptMapInfo[Index].PciDevice = READ_PCI_DDDDD (PciAddressAttr); - Offset += PCI_ADDRESS_CELLS * sizeof (UINT32); - - // Pci irq - PciInterruptMapInfo[Index].PciInterrupt = fdt32_to_cpu ( - (*(UINT32 *)&Data[Offset]) & - (*(UINT32 *)&IrqMapMask[3 * sizeof (UINT32)]) - ); - // -1 to translate from device-tree (INTA=1) to ACPI (INTA=0) irq IDs. - PciInterruptMapInfo[Index].PciInterrupt -= 1; - Offset += PCI_INTERRUPTS_CELLS * sizeof (UINT32); - - // PHandle (skip it) - Offset += sizeof (UINT32); - - // "Parent unit address" (skip it) - Offset += IntcAddressCells * sizeof (UINT32); - - // Interrupt controller interrupt and flags - PciInterruptMapInfo[Index].IntcInterrupt.Interrupt = - FdtGetInterruptId ((UINT32 *)&Data[Offset]); - PciInterruptMapInfo[Index].IntcInterrupt.Flags = - FdtGetInterruptFlags ((UINT32 *)&Data[Offset]); - } // for - - PciInfo->Mapping[PciMappingTableInterrupt].ObjectId = - CREATE_CM_ARCH_COMMON_OBJECT_ID (EArchCommonObjPciInterruptMapInfo); - PciInfo->Mapping[PciMappingTableInterrupt].Size = - sizeof (CM_ARCH_COMMON_PCI_INTERRUPT_MAP_INFO) * IrqMapCount; - PciInfo->Mapping[PciMappingTableInterrupt].Data = PciInterruptMapInfo; - PciInfo->Mapping[PciMappingTableInterrupt].Count = IrqMapCount; - - return Status; -} - -/** Parse a Host-pci node. - - @param [in] Fdt Pointer to a Flattened Device Tree (Fdt). - @param [in] HostPciNode Offset of a host-pci node. - @param [in, out] PciInfo The CM_ARCH_COMMON_PCI_CONFIG_SPACE_INFO to populate. - - @retval EFI_SUCCESS The function completed successfully. - @retval EFI_ABORTED An error occurred. - @retval EFI_INVALID_PARAMETER Invalid parameter. - @retval EFI_OUT_OF_RESOURCES An allocation has failed. -**/ -STATIC -EFI_STATUS -EFIAPI -PciNodeParser ( - IN CONST VOID *Fdt, - IN INT32 HostPciNode, - IN OUT PCI_PARSER_TABLE *PciInfo - ) -{ - EFI_STATUS Status; - INT32 AddressCells; - INT32 SizeCells; - CONST UINT8 *Data; - INT32 DataSize; - INT32 SegGroup; - - if ((Fdt == NULL) || - (PciInfo == NULL)) - { - ASSERT (0); - return EFI_INVALID_PARAMETER; - } - - // Segment Group / DomainId - Status = GetPciSegGroup (Fdt, HostPciNode, &SegGroup); - if (EFI_ERROR (Status)) { - ASSERT (0); - return Status; - } - - PciInfo->PciConfigSpaceInfo.PciSegmentGroupNumber = SegGroup; - - // Bus range - Status = PopulateBusRange (Fdt, HostPciNode, PciInfo); - if (EFI_ERROR (Status)) { - ASSERT (0); - return Status; - } - - Status = FdtGetParentAddressInfo ( - Fdt, - HostPciNode, - &AddressCells, - &SizeCells - ); - if (EFI_ERROR (Status)) { - ASSERT (0); - return Status; - } - - // Only support 32/64 bits addresses. - if ((AddressCells < 1) || - (AddressCells > 2) || - (SizeCells < 1) || - (SizeCells > 2)) - { - ASSERT (0); - return EFI_ABORTED; - } - - Data = fdt_getprop (Fdt, HostPciNode, "reg", &DataSize); - if ((Data == NULL) || - (DataSize != ((AddressCells + SizeCells) * sizeof (UINT32)))) - { - // If error or wrong size. - ASSERT (0); - return EFI_ABORTED; - } - - // Base address - if (AddressCells == 2) { - PciInfo->PciConfigSpaceInfo.BaseAddress = fdt64_to_cpu (*(UINT64 *)Data); - } else { - PciInfo->PciConfigSpaceInfo.BaseAddress = fdt32_to_cpu (*(UINT32 *)Data); - } - - // Address map - Status = ParseAddressMap ( - Fdt, - HostPciNode, - AddressCells, - PciInfo - ); - if (EFI_ERROR (Status)) { - ASSERT (0); - return Status; - } - - // Irq map - Status = ParseIrqMap ( - Fdt, - HostPciNode, - PciInfo - ); - if (EFI_ERROR (Status) && (Status != EFI_NOT_FOUND)) { - ASSERT (0); - } - - return EFI_SUCCESS; -} - -/** Add the parsed Pci information to the Configuration Manager. - - CmObj of the following types are concerned: - - EArchCommonObjPciConfigSpaceInfo - - EArchCommonObjPciAddressMapInfo - - EArchCommonObjPciInterruptMapInfo - - @param [in] FdtParserHandle A handle to the parser instance. - @param [in] PciTableInfo PCI_PARSER_TABLE structure containing the - CmObjs to add. - - @retval EFI_SUCCESS The function completed successfully. - @retval EFI_INVALID_PARAMETER Invalid parameter. - @retval EFI_OUT_OF_RESOURCES An allocation has failed. -**/ -STATIC -EFI_STATUS -EFIAPI -PciInfoAdd ( - IN CONST FDT_HW_INFO_PARSER_HANDLE FdtParserHandle, - IN PCI_PARSER_TABLE *PciTableInfo - ) -{ - EFI_STATUS Status; - CM_ARCH_COMMON_PCI_CONFIG_SPACE_INFO *PciConfigSpaceInfo; - - if ((FdtParserHandle == NULL) || - (PciTableInfo == NULL)) - { - ASSERT (0); - return EFI_INVALID_PARAMETER; - } - - PciConfigSpaceInfo = &PciTableInfo->PciConfigSpaceInfo; - - // Add the address map space CmObj to the Configuration Manager. - Status = AddMultipleCmObjWithCmObjRef ( - FdtParserHandle, - &PciTableInfo->Mapping[PciMappingTableAddress], - &PciConfigSpaceInfo->AddressMapToken - ); - if (EFI_ERROR (Status)) { - ASSERT (0); - return Status; - } - - // Add the interrupt map space CmObj to the Configuration Manager. - // Possible to have no legacy interrupts, or no device described and - // thus no interrupt-mapping. - if (PciTableInfo->Mapping[PciMappingTableInterrupt].Count != 0) { - Status = AddMultipleCmObjWithCmObjRef ( - FdtParserHandle, - &PciTableInfo->Mapping[PciMappingTableInterrupt], - &PciConfigSpaceInfo->InterruptMapToken - ); - if (EFI_ERROR (Status)) { - ASSERT (0); - return Status; - } - } - - // Add the configuration space CmObj to the Configuration Manager. - Status = AddSingleCmObj ( - FdtParserHandle, - CREATE_CM_ARCH_COMMON_OBJECT_ID ( - EArchCommonObjPciConfigSpaceInfo - ), - &PciTableInfo->PciConfigSpaceInfo, - sizeof (CM_ARCH_COMMON_PCI_CONFIG_SPACE_INFO), - NULL - ); - ASSERT_EFI_ERROR (Status); - return Status; -} - -/** Free the CmObjDesc of the ParserTable. - - @param [in] PciTableInfo PCI_PARSER_TABLE structure containing the - CmObjs to free. - - @retval EFI_SUCCESS The function completed successfully. - @retval EFI_INVALID_PARAMETER Invalid parameter. -**/ -STATIC -EFI_STATUS -EFIAPI -FreeParserTable ( - IN PCI_PARSER_TABLE *PciTableInfo - ) -{ - UINT32 Index; - VOID *Data; - - if (PciTableInfo == NULL) { - ASSERT (0); - return EFI_INVALID_PARAMETER; - } - - for (Index = 0; Index < PciMappingTableMax; Index++) { - Data = PciTableInfo->Mapping[Index].Data; - if (Data != NULL) { - FreePool (Data); - } - } - - return EFI_SUCCESS; -} - -/** CM_ARCH_COMMON_PCI_CONFIG_SPACE_INFO parser function. - - The following structure is populated: - typedef struct CmArchCommonPciConfigSpaceInfo { - UINT64 BaseAddress; // {Populated} - UINT16 PciSegmentGroupNumber; // {Populated} - UINT8 StartBusNumber; // {Populated} - UINT8 EndBusNumber; // {Populated} - } CM_ARCH_COMMON_PCI_CONFIG_SPACE_INFO; - - typedef struct CmArchCommonPciAddressMapInfo { - UINT8 SpaceCode; // {Populated} - UINT64 PciAddress; // {Populated} - UINT64 CpuAddress; // {Populated} - UINT64 AddressSize; // {Populated} - } CM_ARCH_COMMON_PCI_ADDRESS_MAP_INFO; - - typedef struct CmArchCommonPciInterruptMapInfo { - UINT8 PciBus; // {Populated} - UINT8 PciDevice; // {Populated} - UINT8 PciInterrupt; // {Populated} - CM_ARCH_COMMON_GENERIC_INTERRUPT IntcInterrupt; // {Populated} - } CM_ARCH_COMMON_PCI_INTERRUPT_MAP_INFO; - - A parser parses a Device Tree to populate a specific CmObj type. None, - one or many CmObj can be created by the parser. - The created CmObj are then handed to the parser's caller through the - HW_INFO_ADD_OBJECT interface. - This can also be a dispatcher. I.e. a function that not parsing a - Device Tree but calling other parsers. - - @param [in] FdtParserHandle A handle to the parser instance. - @param [in] FdtBranch When searching for DT node name, restrict - the search to this Device Tree branch. - - @retval EFI_SUCCESS The function completed successfully. - @retval EFI_ABORTED An error occurred. - @retval EFI_INVALID_PARAMETER Invalid parameter. - @retval EFI_NOT_FOUND Not found. - @retval EFI_UNSUPPORTED Unsupported. -**/ -EFI_STATUS -EFIAPI -ArmPciConfigInfoParser ( - IN CONST FDT_HW_INFO_PARSER_HANDLE FdtParserHandle, - IN INT32 FdtBranch - ) -{ - EFI_STATUS Status; - UINT32 Index; - INT32 PciNode; - UINT32 PciNodeCount; - PCI_PARSER_TABLE PciTableInfo; - VOID *Fdt; - - if (FdtParserHandle == NULL) { - ASSERT (0); - return EFI_INVALID_PARAMETER; - } - - Fdt = FdtParserHandle->Fdt; - - // Only search host-pci devices. - // PCI Firmware Specification Revision 3.0, s4.1.2. "MCFG Table Description": - // "This table directly refers to PCI Segment Groups defined in the system - // via the _SEG object in the ACPI name space for the applicable host bridge - // device." - Status = FdtCountCompatNodeInBranch ( - Fdt, - FdtBranch, - &PciCompatibleInfo, - &PciNodeCount - ); - if (EFI_ERROR (Status)) { - ASSERT (0); - return Status; - } - - if (PciNodeCount == 0) { - return EFI_NOT_FOUND; - } - - // Parse each host-pci node in the branch. - PciNode = FdtBranch; - for (Index = 0; Index < PciNodeCount; Index++) { - ZeroMem (&PciTableInfo, sizeof (PCI_PARSER_TABLE)); - - Status = FdtGetNextCompatNodeInBranch ( - Fdt, - FdtBranch, - &PciCompatibleInfo, - &PciNode - ); - if (EFI_ERROR (Status)) { - ASSERT (0); - if (Status == EFI_NOT_FOUND) { - // Should have found the node. - Status = EFI_ABORTED; - } - - return Status; - } - - Status = PciNodeParser (Fdt, PciNode, &PciTableInfo); - if (EFI_ERROR (Status)) { - ASSERT (0); - goto error_handler; - } - - // Add Pci information to the Configuration Manager. - Status = PciInfoAdd (FdtParserHandle, &PciTableInfo); - if (EFI_ERROR (Status)) { - ASSERT (0); - goto error_handler; - } - - Status = FreeParserTable (&PciTableInfo); - if (EFI_ERROR (Status)) { - ASSERT (0); - return Status; - } - } // for - - return Status; - -error_handler: - FreeParserTable (&PciTableInfo); - return Status; -} diff --git a/DynamicTablesPkg/Library/FdtHwInfoParserLib/Pci/ArmPciConfigSpaceParser.h b/DynamicTablesPkg/Library/FdtHwInfoParserLib/Pci/ArmPciConfigSpaceParser.h deleted file mode 100644 index e680138385..0000000000 --- a/DynamicTablesPkg/Library/FdtHwInfoParserLib/Pci/ArmPciConfigSpaceParser.h +++ /dev/null @@ -1,143 +0,0 @@ -/** @file - Arm PCI Configuration Space Parser. - - Copyright (c) 2021, ARM Limited. All rights reserved.
- SPDX-License-Identifier: BSD-2-Clause-Patent - - @par Reference(s): - - linux/Documentation/devicetree/bindings/pci/host-generic-pci.yaml - - PCI Firmware Specification - Revision 3.0 - - Open Firmware Recommended Practice: Interrupt Mapping, Version 0.9 - - Devicetree Specification Release v0.3 - - linux kernel code -**/ - -#ifndef ARM_PCI_CONFIG_SPACE_PARSER_H_ -#define ARM_PCI_CONFIG_SPACE_PARSER_H_ - -/** Read LEN bits at OFF offsets bits of the ADDR. - - @param [in] ADDR Address to read the bits from. - @param [in] OFF Offset of the bits to read. - @param [in] LEN Number of bits to read. - - @return The bits read. -**/ -#define READ_BITS(ADDR, OFF, LEN) (((ADDR) >> (OFF)) & ((1<<(LEN))-1)) - -/* Pci address attributes. -*/ -/// 0 if relocatable. -#define READ_PCI_N(ADDR) READ_BITS((ADDR), 31, 1) -/// 1 if prefetchable. -#define READ_PCI_P(ADDR) READ_BITS((ADDR), 30, 1) -/// 1 if aliased. -#define READ_PCI_T(ADDR) READ_BITS((ADDR), 29, 1) - -/** Space code. - - 00: Configuration Space - 01: I/O Space - 10: 32-bit-address Memory Space - 11: 64-bit-address Memory Space -*/ -#define READ_PCI_SS(ADDR) READ_BITS((ADDR), 24, 2) -/// Bus number. -#define READ_PCI_BBBBBBBB(ADDR) READ_BITS((ADDR), 16, 8) -/// Device number. -#define READ_PCI_DDDDD(ADDR) READ_BITS((ADDR), 11, 5) - -/** Number of device-tree cells used for PCI nodes properties. - - Values are well defined, except the "#interrupt-cells" which - is assumed to be 1. -*/ -#define PCI_ADDRESS_CELLS 3U -#define PCI_SIZE_CELLS 2U -#define PCI_INTERRUPTS_CELLS 1U - -/** PCI interrupt flags for device-tree. - - Local Bus Specification Revision 3.0, s2.2.6., Interrupt Pins: - - 'Interrupts on PCI are optional and defined as "level sensitive," - asserted low (negative true)' -*/ -#define DT_PCI_IRQ_FLAGS(x) (((x) & 0xF) == BIT0) - -/** Indexes in the mapping table. -*/ -typedef enum PciMappingTable { - PciMappingTableAddress, ///< 0 - Address mapping - PciMappingTableInterrupt, ///< 1 - Interrupt mapping - PciMappingTableMax, ///< 2 - Max -} PCI_MAPPING_TABLE; - -#pragma pack(1) - -/** PCI parser table - - Multiple address-map and interrupt map can correspond to - one host-pci device. This structure allows to temporarily - store the CmObjects created and generate tokens once - the whole device tree is parsed. -*/ -typedef struct PciParserTable { - /// PCI Configuration Space Info - CM_ARCH_COMMON_PCI_CONFIG_SPACE_INFO PciConfigSpaceInfo; - - /// Store the address mapping and interrupt mapping as CmObjDesc - /// before adding them to the Configuration Manager. - CM_OBJ_DESCRIPTOR Mapping[PciMappingTableMax]; -} PCI_PARSER_TABLE; - -#pragma pack() - -/** CM_ARCH_COMMON_PCI_CONFIG_SPACE_INFO parser function. - - The following structure is populated: - typedef struct CmArchCommonPciConfigSpaceInfo { - UINT64 BaseAddress; // {Populated} - UINT16 PciSegmentGroupNumber; // {Populated} - UINT8 StartBusNumber; // {Populated} - UINT8 EndBusNumber; // {Populated} - } CM_ARCH_COMMON_PCI_CONFIG_SPACE_INFO; - - typedef struct CmArchCommonPciAddressMapInfo { - UINT8 SpaceCode; // {Populated} - UINT64 PciAddress; // {Populated} - UINT64 CpuAddress; // {Populated} - UINT64 AddressSize; // {Populated} - } CM_ARCH_COMMON_PCI_ADDRESS_MAP_INFO; - - typedef struct CmArchCommonPciInterruptMapInfo { - UINT8 PciBus; // {Populated} - UINT8 PciDevice; // {Populated} - UINT8 PciInterrupt; // {Populated} - CM_ARCH_COMMON_GENERIC_INTERRUPT IntcInterrupt; // {Populated} - } CM_ARCH_COMMON_PCI_INTERRUPT_MAP_INFO; - - A parser parses a Device Tree to populate a specific CmObj type. None, - one or many CmObj can be created by the parser. - The created CmObj are then handed to the parser's caller through the - HW_INFO_ADD_OBJECT interface. - This can also be a dispatcher. I.e. a function that not parsing a - Device Tree but calling other parsers. - - @param [in] FdtParserHandle A handle to the parser instance. - @param [in] FdtBranch When searching for DT node name, restrict - the search to this Device Tree branch. - - @retval EFI_SUCCESS The function completed successfully. - @retval EFI_ABORTED An error occurred. - @retval EFI_INVALID_PARAMETER Invalid parameter. - @retval EFI_NOT_FOUND Not found. - @retval EFI_UNSUPPORTED Unsupported. -**/ -EFI_STATUS -EFIAPI -ArmPciConfigInfoParser ( - IN CONST FDT_HW_INFO_PARSER_HANDLE FdtParserHandle, - IN INT32 FdtBranch - ); - -#endif // ARM_PCI_CONFIG_SPACE_PARSER_H_ diff --git a/DynamicTablesPkg/Library/FdtHwInfoParserLib/Pci/PciConfigSpaceParser.c b/DynamicTablesPkg/Library/FdtHwInfoParserLib/Pci/PciConfigSpaceParser.c new file mode 100644 index 0000000000..76f9efdf64 --- /dev/null +++ b/DynamicTablesPkg/Library/FdtHwInfoParserLib/Pci/PciConfigSpaceParser.c @@ -0,0 +1,806 @@ +/** @file + PCI Configuration Space Parser. + + Copyright (c) 2021, ARM Limited. All rights reserved.
+ SPDX-License-Identifier: BSD-2-Clause-Patent + + @par Reference(s): + - linux/Documentation/devicetree/bindings/pci/host-generic-pci.yaml + - PCI Firmware Specification - Revision 3.0 + - Open Firmware Recommended Practice: Interrupt Mapping, Version 0.9 + - Devicetree Specification Release v0.3 + - linux kernel code +**/ + +#include "CmObjectDescUtility.h" +#include + +#include "FdtHwInfoParser.h" +#include "Pci/PciConfigSpaceParser.h" + +/** List of "compatible" property values for host PCIe bridges nodes. + + Any other "compatible" value is not supported by this module. +*/ +STATIC CONST COMPATIBILITY_STR PciCompatibleStr[] = { + { "pci-host-ecam-generic" } +}; + +/** COMPATIBILITY_INFO structure for the PCIe. +*/ +STATIC CONST COMPATIBILITY_INFO PciCompatibleInfo = { + ARRAY_SIZE (PciCompatibleStr), + PciCompatibleStr +}; + +/** Get the Segment group (also called: Domain Id) of a host-pci node. + + kernel/Documentation/devicetree/bindings/pci/pci.txt: + "It is required to either not set this property at all or set it for all + host bridges in the system" + + The function checks the "linux,pci-domain" property of the host-pci node. + Either all host-pci nodes must have this property, or none of them. If the + property is available, read it. Otherwise dynamically assign the Ids. + + @param [in] Fdt Pointer to a Flattened Device Tree (Fdt). + @param [in] HostPciNode Offset of a host-pci node. + @param [out] SegGroup Segment group assigned to the host-pci controller. + + @retval EFI_SUCCESS The function completed successfully. + @retval EFI_ABORTED An error occurred. + @retval EFI_INVALID_PARAMETER Invalid parameter. +**/ +STATIC +EFI_STATUS +EFIAPI +GetPciSegGroup ( + IN CONST VOID *Fdt, + IN INT32 HostPciNode, + OUT INT32 *SegGroup + ) +{ + CONST UINT8 *Data; + INT32 DataSize; + STATIC INT32 LocalSegGroup = 0; + + if ((Fdt == NULL) || + (SegGroup == NULL)) + { + ASSERT (0); + return EFI_INVALID_PARAMETER; + } + + Data = fdt_getprop (Fdt, HostPciNode, "linux,pci-domain", &DataSize); + if ((Data == NULL) || (DataSize < 0)) { + // Did not find property, assign the DomainIds ourselves. + if (LocalSegGroup < 0) { + // "linux,pci-domain" property was defined for another node. + ASSERT (0); + return EFI_ABORTED; + } + + *SegGroup = LocalSegGroup++; + return EFI_SUCCESS; + } + + if ((DataSize > sizeof (UINT32)) || + (LocalSegGroup > 0)) + { + // Property on more than 1 cell or + // "linux,pci-domain" property was not defined for a node. + ASSERT (0); + return EFI_ABORTED; + } + + // If one node has the "linux,pci-domain" property, then all the host-pci + // nodes must have it. + LocalSegGroup = -1; + + *SegGroup = fdt32_to_cpu (*(UINT32 *)Data); + return EFI_SUCCESS; +} + +/** Parse the bus-range controlled by this host-pci node. + + @param [in] Fdt Pointer to a Flattened Device Tree (Fdt). + @param [in] HostPciNode Offset of a host-pci node. + @param [in, out] PciInfo PCI_PARSER_TABLE structure storing + information about the current host-pci. + + @retval EFI_SUCCESS The function completed successfully. + @retval EFI_ABORTED An error occurred. + @retval EFI_INVALID_PARAMETER Invalid parameter. +**/ +STATIC +EFI_STATUS +EFIAPI +PopulateBusRange ( + IN CONST VOID *Fdt, + IN INT32 HostPciNode, + IN OUT PCI_PARSER_TABLE *PciInfo + ) +{ + CONST UINT8 *Data; + INT32 DataSize; + UINT32 StartBus; + UINT32 EndBus; + + if ((Fdt == NULL) || + (PciInfo == NULL)) + { + ASSERT (0); + return EFI_INVALID_PARAMETER; + } + + Data = fdt_getprop (Fdt, HostPciNode, "bus-range", &DataSize); + if ((Data == NULL) || (DataSize < 0)) { + // No evidence this property is mandatory. Use default values. + StartBus = 0; + EndBus = 255; + } else if (DataSize == (2 * sizeof (UINT32))) { + // If available, the property is on two integers. + StartBus = fdt32_to_cpu (((UINT32 *)Data)[0]); + EndBus = fdt32_to_cpu (((UINT32 *)Data)[1]); + } else { + ASSERT (0); + return EFI_ABORTED; + } + + PciInfo->PciConfigSpaceInfo.StartBusNumber = StartBus; + PciInfo->PciConfigSpaceInfo.EndBusNumber = EndBus; + + return EFI_SUCCESS; +} + +/** Parse the PCI address map. + + The PCI address map is available in the "ranges" device-tree property. + + @param [in] Fdt Pointer to a Flattened Device Tree (Fdt). + @param [in] HostPciNode Offset of a host-pci node. + @param [in] AddressCells # of cells used to encode an address on + the parent bus. + @param [in, out] PciInfo PCI_PARSER_TABLE structure storing + information about the current host-pci. + + @retval EFI_SUCCESS The function completed successfully. + @retval EFI_ABORTED An error occurred. + @retval EFI_INVALID_PARAMETER Invalid parameter. + @retval EFI_OUT_OF_RESOURCES An allocation has failed. +**/ +STATIC +EFI_STATUS +EFIAPI +ParseAddressMap ( + IN CONST VOID *Fdt, + IN INT32 HostPciNode, + IN INT32 AddressCells, + IN OUT PCI_PARSER_TABLE *PciInfo + ) +{ + CONST UINT8 *Data; + INT32 DataSize; + UINT32 Index; + UINT32 Offset; + UINT32 AddressMapSize; + UINT32 Count; + UINT32 PciAddressAttr; + + CM_ARCH_COMMON_PCI_ADDRESS_MAP_INFO *PciAddressMapInfo; + UINT32 BufferSize; + + // The mapping is done on AddressMapSize bytes. + AddressMapSize = (PCI_ADDRESS_CELLS + AddressCells + PCI_SIZE_CELLS) * + sizeof (UINT32); + + Data = fdt_getprop (Fdt, HostPciNode, "ranges", &DataSize); + if ((Data == NULL) || + (DataSize < 0) || + ((DataSize % AddressMapSize) != 0)) + { + // If error or not on AddressMapSize bytes. + ASSERT (0); + return EFI_ABORTED; + } + + Count = DataSize / AddressMapSize; + + // Allocate a buffer to store each address mapping. + BufferSize = Count * sizeof (CM_ARCH_COMMON_PCI_ADDRESS_MAP_INFO); + PciAddressMapInfo = AllocateZeroPool (BufferSize); + if (PciAddressMapInfo == NULL) { + ASSERT (0); + return EFI_OUT_OF_RESOURCES; + } + + for (Index = 0; Index < Count; Index++) { + Offset = Index * AddressMapSize; + + // Pci address attributes + PciAddressAttr = fdt32_to_cpu (*(UINT32 *)&Data[Offset]); + PciAddressMapInfo[Index].SpaceCode = READ_PCI_SS (PciAddressAttr); + Offset += sizeof (UINT32); + + // Pci address + PciAddressMapInfo[Index].PciAddress = + fdt64_to_cpu (*(UINT64 *)&Data[Offset]); + Offset += (PCI_ADDRESS_CELLS - 1) * sizeof (UINT32); + + // Cpu address + if (AddressCells == 2) { + PciAddressMapInfo[Index].CpuAddress = + fdt64_to_cpu (*(UINT64 *)&Data[Offset]); + } else { + PciAddressMapInfo[Index].CpuAddress = + fdt32_to_cpu (*(UINT32 *)&Data[Offset]); + } + + Offset += AddressCells * sizeof (UINT32); + + // Address size + PciAddressMapInfo[Index].AddressSize = + fdt64_to_cpu (*(UINT64 *)&Data[Offset]); + Offset += PCI_SIZE_CELLS * sizeof (UINT32); + } // for + + PciInfo->Mapping[PciMappingTableAddress].ObjectId = + CREATE_CM_ARCH_COMMON_OBJECT_ID (EArchCommonObjPciAddressMapInfo); + PciInfo->Mapping[PciMappingTableAddress].Size = + sizeof (CM_ARCH_COMMON_PCI_ADDRESS_MAP_INFO) * Count; + PciInfo->Mapping[PciMappingTableAddress].Data = PciAddressMapInfo; + PciInfo->Mapping[PciMappingTableAddress].Count = Count; + + return EFI_SUCCESS; +} + +/** Parse the PCI interrupt map. + + The PCI interrupt map is available in the "interrupt-map" + and "interrupt-map-mask" device-tree properties. + + Cf Devicetree Specification Release v0.3, + s2.4.3 Interrupt Nexus Properties + + An interrupt-map must be as: + interrupt-map = < [child unit address] [child interrupt specifier] + [interrupt-parent] + [parent unit address] [parent interrupt specifier] > + + @param [in] Fdt Pointer to a Flattened Device Tree (Fdt). + @param [in] HostPciNode Offset of a host-pci node. + @param [in, out] PciInfo PCI_PARSER_TABLE structure storing + information about the current host-pci. + + @retval EFI_SUCCESS The function completed successfully. + @retval EFI_ABORTED An error occurred. + @retval EFI_INVALID_PARAMETER Invalid parameter. + @retval EFI_NOT_FOUND Not found. + @retval EFI_OUT_OF_RESOURCES An allocation has failed. +**/ +STATIC +EFI_STATUS +EFIAPI +ParseIrqMap ( + IN CONST VOID *Fdt, + IN INT32 HostPciNode, + IN OUT PCI_PARSER_TABLE *PciInfo + ) +{ + EFI_STATUS Status; + CONST UINT8 *Data; + INT32 DataSize; + UINT32 Index; + UINT32 Offset; + + INT32 IntcNode; + INT32 IntcAddressCells; + INT32 IntcCells; + + INT32 PciIntCells; + INT32 IntcPhandle; + + INT32 IrqMapSize; + UINT32 IrqMapCount; + CONST UINT8 *IrqMapMask; + INT32 IrqMapMaskSize; + + INT32 PHandleOffset; + + UINT32 PciAddressAttr; + + CM_ARCH_COMMON_PCI_INTERRUPT_MAP_INFO *PciInterruptMapInfo; + UINT32 BufferSize; + + Data = fdt_getprop (Fdt, HostPciNode, "interrupt-map", &DataSize); + if ((Data == NULL) || (DataSize <= 0)) { + DEBUG (( + DEBUG_WARN, + "Fdt parser: No Legacy interrupts found for PCI configuration space at " + "address: 0x%lx, group segment: %d\n", + PciInfo->PciConfigSpaceInfo.BaseAddress, + PciInfo->PciConfigSpaceInfo.PciSegmentGroupNumber + )); + return EFI_NOT_FOUND; + } + + // PCI interrupts are expected to be on 1 cell. Check it. + Status = FdtGetInterruptCellsInfo (Fdt, HostPciNode, &PciIntCells); + if (EFI_ERROR (Status)) { + ASSERT (0); + return Status; + } + + if (PciIntCells != PCI_INTERRUPTS_CELLS) { + ASSERT (0); + return EFI_ABORTED; + } + + IrqMapMask = fdt_getprop ( + Fdt, + HostPciNode, + "interrupt-map-mask", + &IrqMapMaskSize + ); + if ((IrqMapMask == NULL) || + (IrqMapMaskSize != + (PCI_ADDRESS_CELLS + PCI_INTERRUPTS_CELLS) * sizeof (UINT32))) + { + ASSERT (0); + return EFI_ABORTED; + } + + // Get the interrupt-controller of the first irq mapping. + PHandleOffset = (PCI_ADDRESS_CELLS + PciIntCells) * sizeof (UINT32); + if (PHandleOffset > DataSize) { + ASSERT (0); + return EFI_ABORTED; + } + + IntcPhandle = fdt32_to_cpu (*(UINT32 *)&Data[PHandleOffset]); + IntcNode = fdt_node_offset_by_phandle (Fdt, IntcPhandle); + if (IntcNode < 0) { + ASSERT (0); + return EFI_ABORTED; + } + + // Get the "address-cells" property of the IntcNode. + Status = FdtGetAddressInfo (Fdt, IntcNode, &IntcAddressCells, NULL); + if (EFI_ERROR (Status)) { + ASSERT (0); + return Status; + } + + // Get the "interrupt-cells" property of the IntcNode. + Status = FdtGetInterruptCellsInfo (Fdt, IntcNode, &IntcCells); + if (EFI_ERROR (Status)) { + ASSERT (0); + return Status; + } + + // An irq mapping is done on IrqMapSize bytes + // (which includes 1 cell for the PHandle). + IrqMapSize = (PCI_ADDRESS_CELLS + PciIntCells + 1 + + IntcAddressCells + IntcCells) * sizeof (UINT32); + if ((DataSize % IrqMapSize) != 0) { + // The mapping is not done on IrqMapSize bytes. + ASSERT (0); + return EFI_ABORTED; + } + + IrqMapCount = DataSize / IrqMapSize; + + // We assume the same interrupt-controller is used for all the mappings. + // Check this is correct. + for (Index = 0; Index < IrqMapCount; Index++) { + if (IntcPhandle != fdt32_to_cpu ( + *(UINT32 *)&Data[(Index * IrqMapSize) + PHandleOffset] + )) + { + ASSERT (0); + return EFI_ABORTED; + } + } + + // Allocate a buffer to store each interrupt mapping. + IrqMapCount = DataSize / IrqMapSize; + BufferSize = IrqMapCount * sizeof (CM_ARCH_COMMON_PCI_ADDRESS_MAP_INFO); + PciInterruptMapInfo = AllocateZeroPool (BufferSize); + if (PciInterruptMapInfo == NULL) { + ASSERT (0); + return EFI_OUT_OF_RESOURCES; + } + + for (Index = 0; Index < IrqMapCount; Index++) { + Offset = Index * IrqMapSize; + + // Pci address attributes + PciAddressAttr = fdt32_to_cpu ( + (*(UINT32 *)&Data[Offset]) & + (*(UINT32 *)&IrqMapMask[0]) + ); + PciInterruptMapInfo[Index].PciBus = READ_PCI_BBBBBBBB (PciAddressAttr); + PciInterruptMapInfo[Index].PciDevice = READ_PCI_DDDDD (PciAddressAttr); + Offset += PCI_ADDRESS_CELLS * sizeof (UINT32); + + // Pci irq + PciInterruptMapInfo[Index].PciInterrupt = fdt32_to_cpu ( + (*(UINT32 *)&Data[Offset]) & + (*(UINT32 *)&IrqMapMask[3 * sizeof (UINT32)]) + ); + // -1 to translate from device-tree (INTA=1) to ACPI (INTA=0) irq IDs. + PciInterruptMapInfo[Index].PciInterrupt -= 1; + Offset += PCI_INTERRUPTS_CELLS * sizeof (UINT32); + + // PHandle (skip it) + Offset += sizeof (UINT32); + + // "Parent unit address" (skip it) + Offset += IntcAddressCells * sizeof (UINT32); + + // Interrupt controller interrupt and flags + PciInterruptMapInfo[Index].IntcInterrupt.Interrupt = + FdtGetInterruptId ((UINT32 *)&Data[Offset]); + PciInterruptMapInfo[Index].IntcInterrupt.Flags = + FdtGetInterruptFlags ((UINT32 *)&Data[Offset]); + } // for + + PciInfo->Mapping[PciMappingTableInterrupt].ObjectId = + CREATE_CM_ARCH_COMMON_OBJECT_ID (EArchCommonObjPciInterruptMapInfo); + PciInfo->Mapping[PciMappingTableInterrupt].Size = + sizeof (CM_ARCH_COMMON_PCI_INTERRUPT_MAP_INFO) * IrqMapCount; + PciInfo->Mapping[PciMappingTableInterrupt].Data = PciInterruptMapInfo; + PciInfo->Mapping[PciMappingTableInterrupt].Count = IrqMapCount; + + return Status; +} + +/** Parse a Host-pci node. + + @param [in] Fdt Pointer to a Flattened Device Tree (Fdt). + @param [in] HostPciNode Offset of a host-pci node. + @param [in, out] PciInfo The CM_ARCH_COMMON_PCI_CONFIG_SPACE_INFO to populate. + + @retval EFI_SUCCESS The function completed successfully. + @retval EFI_ABORTED An error occurred. + @retval EFI_INVALID_PARAMETER Invalid parameter. + @retval EFI_OUT_OF_RESOURCES An allocation has failed. +**/ +STATIC +EFI_STATUS +EFIAPI +PciNodeParser ( + IN CONST VOID *Fdt, + IN INT32 HostPciNode, + IN OUT PCI_PARSER_TABLE *PciInfo + ) +{ + EFI_STATUS Status; + INT32 AddressCells; + INT32 SizeCells; + CONST UINT8 *Data; + INT32 DataSize; + INT32 SegGroup; + + if ((Fdt == NULL) || + (PciInfo == NULL)) + { + ASSERT (0); + return EFI_INVALID_PARAMETER; + } + + // Segment Group / DomainId + Status = GetPciSegGroup (Fdt, HostPciNode, &SegGroup); + if (EFI_ERROR (Status)) { + ASSERT (0); + return Status; + } + + PciInfo->PciConfigSpaceInfo.PciSegmentGroupNumber = SegGroup; + + // Bus range + Status = PopulateBusRange (Fdt, HostPciNode, PciInfo); + if (EFI_ERROR (Status)) { + ASSERT (0); + return Status; + } + + Status = FdtGetParentAddressInfo ( + Fdt, + HostPciNode, + &AddressCells, + &SizeCells + ); + if (EFI_ERROR (Status)) { + ASSERT (0); + return Status; + } + + // Only support 32/64 bits addresses. + if ((AddressCells < 1) || + (AddressCells > 2) || + (SizeCells < 1) || + (SizeCells > 2)) + { + ASSERT (0); + return EFI_ABORTED; + } + + Data = fdt_getprop (Fdt, HostPciNode, "reg", &DataSize); + if ((Data == NULL) || + (DataSize != ((AddressCells + SizeCells) * sizeof (UINT32)))) + { + // If error or wrong size. + ASSERT (0); + return EFI_ABORTED; + } + + // Base address + if (AddressCells == 2) { + PciInfo->PciConfigSpaceInfo.BaseAddress = fdt64_to_cpu (*(UINT64 *)Data); + } else { + PciInfo->PciConfigSpaceInfo.BaseAddress = fdt32_to_cpu (*(UINT32 *)Data); + } + + // Address map + Status = ParseAddressMap ( + Fdt, + HostPciNode, + AddressCells, + PciInfo + ); + if (EFI_ERROR (Status)) { + ASSERT (0); + return Status; + } + + // Irq map + Status = ParseIrqMap ( + Fdt, + HostPciNode, + PciInfo + ); + if (EFI_ERROR (Status) && (Status != EFI_NOT_FOUND)) { + ASSERT (0); + } + + return EFI_SUCCESS; +} + +/** Add the parsed Pci information to the Configuration Manager. + + CmObj of the following types are concerned: + - EArchCommonObjPciConfigSpaceInfo + - EArchCommonObjPciAddressMapInfo + - EArchCommonObjPciInterruptMapInfo + + @param [in] FdtParserHandle A handle to the parser instance. + @param [in] PciTableInfo PCI_PARSER_TABLE structure containing the + CmObjs to add. + + @retval EFI_SUCCESS The function completed successfully. + @retval EFI_INVALID_PARAMETER Invalid parameter. + @retval EFI_OUT_OF_RESOURCES An allocation has failed. +**/ +STATIC +EFI_STATUS +EFIAPI +PciInfoAdd ( + IN CONST FDT_HW_INFO_PARSER_HANDLE FdtParserHandle, + IN PCI_PARSER_TABLE *PciTableInfo + ) +{ + EFI_STATUS Status; + CM_ARCH_COMMON_PCI_CONFIG_SPACE_INFO *PciConfigSpaceInfo; + + if ((FdtParserHandle == NULL) || + (PciTableInfo == NULL)) + { + ASSERT (0); + return EFI_INVALID_PARAMETER; + } + + PciConfigSpaceInfo = &PciTableInfo->PciConfigSpaceInfo; + + // Add the address map space CmObj to the Configuration Manager. + Status = AddMultipleCmObjWithCmObjRef ( + FdtParserHandle, + &PciTableInfo->Mapping[PciMappingTableAddress], + &PciConfigSpaceInfo->AddressMapToken + ); + if (EFI_ERROR (Status)) { + ASSERT (0); + return Status; + } + + // Add the interrupt map space CmObj to the Configuration Manager. + // Possible to have no legacy interrupts, or no device described and + // thus no interrupt-mapping. + if (PciTableInfo->Mapping[PciMappingTableInterrupt].Count != 0) { + Status = AddMultipleCmObjWithCmObjRef ( + FdtParserHandle, + &PciTableInfo->Mapping[PciMappingTableInterrupt], + &PciConfigSpaceInfo->InterruptMapToken + ); + if (EFI_ERROR (Status)) { + ASSERT (0); + return Status; + } + } + + // Add the configuration space CmObj to the Configuration Manager. + Status = AddSingleCmObj ( + FdtParserHandle, + CREATE_CM_ARCH_COMMON_OBJECT_ID ( + EArchCommonObjPciConfigSpaceInfo + ), + &PciTableInfo->PciConfigSpaceInfo, + sizeof (CM_ARCH_COMMON_PCI_CONFIG_SPACE_INFO), + NULL + ); + ASSERT_EFI_ERROR (Status); + return Status; +} + +/** Free the CmObjDesc of the ParserTable. + + @param [in] PciTableInfo PCI_PARSER_TABLE structure containing the + CmObjs to free. + + @retval EFI_SUCCESS The function completed successfully. + @retval EFI_INVALID_PARAMETER Invalid parameter. +**/ +STATIC +EFI_STATUS +EFIAPI +FreeParserTable ( + IN PCI_PARSER_TABLE *PciTableInfo + ) +{ + UINT32 Index; + VOID *Data; + + if (PciTableInfo == NULL) { + ASSERT (0); + return EFI_INVALID_PARAMETER; + } + + for (Index = 0; Index < PciMappingTableMax; Index++) { + Data = PciTableInfo->Mapping[Index].Data; + if (Data != NULL) { + FreePool (Data); + } + } + + return EFI_SUCCESS; +} + +/** CM_ARCH_COMMON_PCI_CONFIG_SPACE_INFO parser function. + + The following structure is populated: + typedef struct CmArchCommonPciConfigSpaceInfo { + UINT64 BaseAddress; // {Populated} + UINT16 PciSegmentGroupNumber; // {Populated} + UINT8 StartBusNumber; // {Populated} + UINT8 EndBusNumber; // {Populated} + } CM_ARCH_COMMON_PCI_CONFIG_SPACE_INFO; + + typedef struct CmArchCommonPciAddressMapInfo { + UINT8 SpaceCode; // {Populated} + UINT64 PciAddress; // {Populated} + UINT64 CpuAddress; // {Populated} + UINT64 AddressSize; // {Populated} + } CM_ARCH_COMMON_PCI_ADDRESS_MAP_INFO; + + typedef struct CmArchCommonPciInterruptMapInfo { + UINT8 PciBus; // {Populated} + UINT8 PciDevice; // {Populated} + UINT8 PciInterrupt; // {Populated} + CM_ARCH_COMMON_GENERIC_INTERRUPT IntcInterrupt; // {Populated} + } CM_ARCH_COMMON_PCI_INTERRUPT_MAP_INFO; + + A parser parses a Device Tree to populate a specific CmObj type. None, + one or many CmObj can be created by the parser. + The created CmObj are then handed to the parser's caller through the + HW_INFO_ADD_OBJECT interface. + This can also be a dispatcher. I.e. a function that not parsing a + Device Tree but calling other parsers. + + @param [in] FdtParserHandle A handle to the parser instance. + @param [in] FdtBranch When searching for DT node name, restrict + the search to this Device Tree branch. + + @retval EFI_SUCCESS The function completed successfully. + @retval EFI_ABORTED An error occurred. + @retval EFI_INVALID_PARAMETER Invalid parameter. + @retval EFI_NOT_FOUND Not found. + @retval EFI_UNSUPPORTED Unsupported. +**/ +EFI_STATUS +EFIAPI +PciConfigInfoParser ( + IN CONST FDT_HW_INFO_PARSER_HANDLE FdtParserHandle, + IN INT32 FdtBranch + ) +{ + EFI_STATUS Status; + UINT32 Index; + INT32 PciNode; + UINT32 PciNodeCount; + PCI_PARSER_TABLE PciTableInfo; + VOID *Fdt; + + if (FdtParserHandle == NULL) { + ASSERT (0); + return EFI_INVALID_PARAMETER; + } + + Fdt = FdtParserHandle->Fdt; + + // Only search host-pci devices. + // PCI Firmware Specification Revision 3.0, s4.1.2. "MCFG Table Description": + // "This table directly refers to PCI Segment Groups defined in the system + // via the _SEG object in the ACPI name space for the applicable host bridge + // device." + Status = FdtCountCompatNodeInBranch ( + Fdt, + FdtBranch, + &PciCompatibleInfo, + &PciNodeCount + ); + if (EFI_ERROR (Status)) { + ASSERT (0); + return Status; + } + + if (PciNodeCount == 0) { + return EFI_NOT_FOUND; + } + + // Parse each host-pci node in the branch. + PciNode = FdtBranch; + for (Index = 0; Index < PciNodeCount; Index++) { + ZeroMem (&PciTableInfo, sizeof (PCI_PARSER_TABLE)); + + Status = FdtGetNextCompatNodeInBranch ( + Fdt, + FdtBranch, + &PciCompatibleInfo, + &PciNode + ); + if (EFI_ERROR (Status)) { + ASSERT (0); + if (Status == EFI_NOT_FOUND) { + // Should have found the node. + Status = EFI_ABORTED; + } + + return Status; + } + + Status = PciNodeParser (Fdt, PciNode, &PciTableInfo); + if (EFI_ERROR (Status)) { + ASSERT (0); + goto error_handler; + } + + // Add Pci information to the Configuration Manager. + Status = PciInfoAdd (FdtParserHandle, &PciTableInfo); + if (EFI_ERROR (Status)) { + ASSERT (0); + goto error_handler; + } + + Status = FreeParserTable (&PciTableInfo); + if (EFI_ERROR (Status)) { + ASSERT (0); + return Status; + } + } // for + + return Status; + +error_handler: + FreeParserTable (&PciTableInfo); + return Status; +} diff --git a/DynamicTablesPkg/Library/FdtHwInfoParserLib/Pci/PciConfigSpaceParser.h b/DynamicTablesPkg/Library/FdtHwInfoParserLib/Pci/PciConfigSpaceParser.h new file mode 100644 index 0000000000..aeffe7ac35 --- /dev/null +++ b/DynamicTablesPkg/Library/FdtHwInfoParserLib/Pci/PciConfigSpaceParser.h @@ -0,0 +1,143 @@ +/** @file + PCI Configuration Space Parser. + + Copyright (c) 2021, ARM Limited. All rights reserved.
+ SPDX-License-Identifier: BSD-2-Clause-Patent + + @par Reference(s): + - linux/Documentation/devicetree/bindings/pci/host-generic-pci.yaml + - PCI Firmware Specification - Revision 3.0 + - Open Firmware Recommended Practice: Interrupt Mapping, Version 0.9 + - Devicetree Specification Release v0.3 + - linux kernel code +**/ + +#ifndef PCI_CONFIG_SPACE_PARSER_H_ +#define PCI_CONFIG_SPACE_PARSER_H_ + +/** Read LEN bits at OFF offsets bits of the ADDR. + + @param [in] ADDR Address to read the bits from. + @param [in] OFF Offset of the bits to read. + @param [in] LEN Number of bits to read. + + @return The bits read. +**/ +#define READ_BITS(ADDR, OFF, LEN) (((ADDR) >> (OFF)) & ((1<<(LEN))-1)) + +/* Pci address attributes. +*/ +/// 0 if relocatable. +#define READ_PCI_N(ADDR) READ_BITS((ADDR), 31, 1) +/// 1 if prefetchable. +#define READ_PCI_P(ADDR) READ_BITS((ADDR), 30, 1) +/// 1 if aliased. +#define READ_PCI_T(ADDR) READ_BITS((ADDR), 29, 1) + +/** Space code. + + 00: Configuration Space + 01: I/O Space + 10: 32-bit-address Memory Space + 11: 64-bit-address Memory Space +*/ +#define READ_PCI_SS(ADDR) READ_BITS((ADDR), 24, 2) +/// Bus number. +#define READ_PCI_BBBBBBBB(ADDR) READ_BITS((ADDR), 16, 8) +/// Device number. +#define READ_PCI_DDDDD(ADDR) READ_BITS((ADDR), 11, 5) + +/** Number of device-tree cells used for PCI nodes properties. + + Values are well defined, except the "#interrupt-cells" which + is assumed to be 1. +*/ +#define PCI_ADDRESS_CELLS 3U +#define PCI_SIZE_CELLS 2U +#define PCI_INTERRUPTS_CELLS 1U + +/** PCI interrupt flags for device-tree. + + Local Bus Specification Revision 3.0, s2.2.6., Interrupt Pins: + - 'Interrupts on PCI are optional and defined as "level sensitive," + asserted low (negative true)' +*/ +#define DT_PCI_IRQ_FLAGS(x) (((x) & 0xF) == BIT0) + +/** Indexes in the mapping table. +*/ +typedef enum PciMappingTable { + PciMappingTableAddress, ///< 0 - Address mapping + PciMappingTableInterrupt, ///< 1 - Interrupt mapping + PciMappingTableMax, ///< 2 - Max +} PCI_MAPPING_TABLE; + +#pragma pack(1) + +/** PCI parser table + + Multiple address-map and interrupt map can correspond to + one host-pci device. This structure allows to temporarily + store the CmObjects created and generate tokens once + the whole device tree is parsed. +*/ +typedef struct PciParserTable { + /// PCI Configuration Space Info + CM_ARCH_COMMON_PCI_CONFIG_SPACE_INFO PciConfigSpaceInfo; + + /// Store the address mapping and interrupt mapping as CmObjDesc + /// before adding them to the Configuration Manager. + CM_OBJ_DESCRIPTOR Mapping[PciMappingTableMax]; +} PCI_PARSER_TABLE; + +#pragma pack() + +/** CM_ARCH_COMMON_PCI_CONFIG_SPACE_INFO parser function. + + The following structure is populated: + typedef struct CmArchCommonPciConfigSpaceInfo { + UINT64 BaseAddress; // {Populated} + UINT16 PciSegmentGroupNumber; // {Populated} + UINT8 StartBusNumber; // {Populated} + UINT8 EndBusNumber; // {Populated} + } CM_ARCH_COMMON_PCI_CONFIG_SPACE_INFO; + + typedef struct CmArchCommonPciAddressMapInfo { + UINT8 SpaceCode; // {Populated} + UINT64 PciAddress; // {Populated} + UINT64 CpuAddress; // {Populated} + UINT64 AddressSize; // {Populated} + } CM_ARCH_COMMON_PCI_ADDRESS_MAP_INFO; + + typedef struct CmArchCommonPciInterruptMapInfo { + UINT8 PciBus; // {Populated} + UINT8 PciDevice; // {Populated} + UINT8 PciInterrupt; // {Populated} + CM_ARCH_COMMON_GENERIC_INTERRUPT IntcInterrupt; // {Populated} + } CM_ARCH_COMMON_PCI_INTERRUPT_MAP_INFO; + + A parser parses a Device Tree to populate a specific CmObj type. None, + one or many CmObj can be created by the parser. + The created CmObj are then handed to the parser's caller through the + HW_INFO_ADD_OBJECT interface. + This can also be a dispatcher. I.e. a function that not parsing a + Device Tree but calling other parsers. + + @param [in] FdtParserHandle A handle to the parser instance. + @param [in] FdtBranch When searching for DT node name, restrict + the search to this Device Tree branch. + + @retval EFI_SUCCESS The function completed successfully. + @retval EFI_ABORTED An error occurred. + @retval EFI_INVALID_PARAMETER Invalid parameter. + @retval EFI_NOT_FOUND Not found. + @retval EFI_UNSUPPORTED Unsupported. +**/ +EFI_STATUS +EFIAPI +PciConfigInfoParser ( + IN CONST FDT_HW_INFO_PARSER_HANDLE FdtParserHandle, + IN INT32 FdtBranch + ); + +#endif // PCI_CONFIG_SPACE_PARSER_H_ -- cgit