From mboxrd@z Thu Jan 1 00:00:00 1970 Received: from foss.arm.com (foss.arm.com [217.140.110.172]) by mx.groups.io with SMTP id smtpd.web08.6398.1624449554142739647 for ; Wed, 23 Jun 2021 04:59:14 -0700 Authentication-Results: mx.groups.io; dkim=missing; spf=pass (domain: arm.com, ip: 217.140.110.172, mailfrom: pierre.gondois@arm.com) Received: from usa-sjc-imap-foss1.foss.arm.com (unknown [10.121.207.14]) by usa-sjc-mx-foss1.foss.arm.com (Postfix) with ESMTP id C5CE9ED1; Wed, 23 Jun 2021 04:59:13 -0700 (PDT) Received: from e120189.arm.com (unknown [10.57.78.245]) by usa-sjc-imap-foss1.foss.arm.com (Postfix) with ESMTPA id 278553F718; Wed, 23 Jun 2021 04:59:11 -0700 (PDT) From: "PierreGondois" To: devel@edk2.groups.io, Sami Mujawar , Alexei Fedorov Cc: Akanksha Jain , Alexandru Elisei Subject: [PATCH v1 7/7] DynamicTablesPkg: SSDT Pci express generator Date: Wed, 23 Jun 2021 12:58:34 +0100 Message-Id: <20210623115834.907-8-Pierre.Gondois@arm.com> X-Mailer: git-send-email 2.17.1 In-Reply-To: <20210623115834.907-1-Pierre.Gondois@arm.com> References: <20210623115834.907-1-Pierre.Gondois@arm.com> From: Pierre Gondois This generator allows to generate a SSDT table describing a Pci express Bus. It uses the following CmObj: - EArmObjCmRef - EArmObjPciConfigSpaceInfo - EArmObjPciAddressMapInfo - EArmObjPciInterruptMapInfo Signed-off-by: Pierre Gondois --- DynamicTablesPkg/DynamicTables.dsc.inc | 2 + DynamicTablesPkg/Include/AcpiTableGenerator.h | 5 + .../AcpiSsdtPcieLibArm/SsdtPcieGenerator.c | 1417 +++++++++++++++++ .../AcpiSsdtPcieLibArm/SsdtPcieGenerator.h | 134 ++ .../Arm/AcpiSsdtPcieLibArm/SsdtPcieLibArm.inf | 32 + .../SsdtPcieOscTemplate.asl | 80 + 6 files changed, 1670 insertions(+) create mode 100644 DynamicTablesPkg/Library/Acpi/Arm/AcpiSsdtPcieLibArm/SsdtPcieGenerator.c create mode 100644 DynamicTablesPkg/Library/Acpi/Arm/AcpiSsdtPcieLibArm/SsdtPcieGenerator.h create mode 100644 DynamicTablesPkg/Library/Acpi/Arm/AcpiSsdtPcieLibArm/SsdtPcieLibArm.inf create mode 100644 DynamicTablesPkg/Library/Acpi/Arm/AcpiSsdtPcieLibArm/SsdtPcieOscTemplate.asl diff --git a/DynamicTablesPkg/DynamicTables.dsc.inc b/DynamicTablesPkg/DynamicTables.dsc.inc index 292215c39456..60bcf4b199e8 100644 --- a/DynamicTablesPkg/DynamicTables.dsc.inc +++ b/DynamicTablesPkg/DynamicTables.dsc.inc @@ -39,6 +39,7 @@ [Components.common] # AML Codegen DynamicTablesPkg/Library/Acpi/Arm/AcpiSsdtCpuTopologyLibArm/SsdtCpuTopologyLibArm.inf + DynamicTablesPkg/Library/Acpi/Arm/AcpiSsdtPcieLibArm/SsdtPcieLibArm.inf # # Dynamic Table Factory Dxe @@ -62,6 +63,7 @@ [Components.common] # AML Codegen NULL|DynamicTablesPkg/Library/Acpi/Arm/AcpiSsdtCpuTopologyLibArm/SsdtCpuTopologyLibArm.inf + NULL|DynamicTablesPkg/Library/Acpi/Arm/AcpiSsdtPcieLibArm/SsdtPcieLibArm.inf } # diff --git a/DynamicTablesPkg/Include/AcpiTableGenerator.h b/DynamicTablesPkg/Include/AcpiTableGenerator.h index 45c808ba740d..58ec941f2a35 100644 --- a/DynamicTablesPkg/Include/AcpiTableGenerator.h +++ b/DynamicTablesPkg/Include/AcpiTableGenerator.h @@ -67,6 +67,10 @@ The Dynamic Tables Framework implements the following ACPI table generators: The SSDT Cpu-Topology generator collates the cpu and LPI information from the Configuration Manager and generates a SSDT table describing the CPU hierarchy. + - SSDT Pci-Express: + The SSDT Pci Express generator collates the Pci Express + information from the Configuration Manager and generates a + SSDT table describing a Pci Express bus. */ /** The ACPI_TABLE_GENERATOR_ID type describes ACPI table generator ID. @@ -93,6 +97,7 @@ typedef enum StdAcpiTableId { EStdAcpiTableIdSsdtSerialPort, ///< SSDT Serial-Port Generator EStdAcpiTableIdSsdtCmn600, ///< SSDT Cmn-600 Generator EStdAcpiTableIdSsdtCpuTopology, ///< SSDT Cpu Topology + EStdAcpiTableIdSsdtPciExpress, ///< SSDT Pci Express Generator EStdAcpiTableIdMax } ESTD_ACPI_TABLE_ID; diff --git a/DynamicTablesPkg/Library/Acpi/Arm/AcpiSsdtPcieLibArm/SsdtPcieGenerator.c b/DynamicTablesPkg/Library/Acpi/Arm/AcpiSsdtPcieLibArm/SsdtPcieGenerator.c new file mode 100644 index 000000000000..478bc60ef6f3 --- /dev/null +++ b/DynamicTablesPkg/Library/Acpi/Arm/AcpiSsdtPcieLibArm/SsdtPcieGenerator.c @@ -0,0 +1,1417 @@ +/** @file + SSDT Pcie Table Generator. + + Copyright (c) 2021, Arm Limited. All rights reserved.
+ + SPDX-License-Identifier: BSD-2-Clause-Patent + + @par Reference(s): + - PCI Firmware Specification - Revision 3.0 + - ACPI 6.4 specification: + - s6.2.13 "_PRT (PCI Routing Table)" + - s6.1.1 "_ADR (Address)" + - linux kernel code +**/ + +#include +#include +#include +#include +#include +#include +#include + +// Module specific include files. +#include +#include +#include +#include +#include +#include + +#include "SsdtPcieGenerator.h" + +/** ARM standard SSDT Pcie Table Generator. + +Requirements: + The following Configuration Manager Object(s) are required by + this Generator: + - EArmObjCmRef + - EArmObjPciConfigSpaceInfo + - EArmObjPciAddressMapInfo + - EArmObjPciInterruptMapInfo +*/ + +/** This macro expands to a function that retrieves the cross-CM-object- + reference information from the Configuration Manager. +*/ +GET_OBJECT_LIST ( + EObjNameSpaceArm, + EArmObjCmRef, + CM_ARM_OBJ_REF + ); + +/** This macro expands to a function that retrieves the Pci + Configuration Space Information from the Configuration Manager. +*/ +GET_OBJECT_LIST ( + EObjNameSpaceArm, + EArmObjPciConfigSpaceInfo, + CM_ARM_PCI_CONFIG_SPACE_INFO + ); + +/** This macro expands to a function that retrieves the Pci + Address Mapping Information from the Configuration Manager. +*/ +GET_OBJECT_LIST ( + EObjNameSpaceArm, + EArmObjPciAddressMapInfo, + CM_ARM_PCI_ADDRESS_MAP_INFO + ); + +/** This macro expands to a function that retrieves the Pci + Interrupt Mapping Information from the Configuration Manager. +*/ +GET_OBJECT_LIST ( + EObjNameSpaceArm, + EArmObjPciInterruptMapInfo, + CM_ARM_PCI_INTERRUPT_MAP_INFO + ); + +/** Initialize the MappingTable. + + @param [in] MappingTable The mapping table structure. + @param [in] Count Number of entries to allocate in the + MappingTable. + + @retval EFI_SUCCESS Success. + @retval EFI_INVALID_PARAMETER Invalid parameter. + @retval EFI_OUT_OF_RESOURCES Failed to allocate memory. +**/ +STATIC +EFI_STATUS +EFIAPI +MappingTableInitialize ( + IN MAPPING_TABLE * MappingTable, + IN UINT32 Count + ) +{ + UINT32 * Table; + + if ((MappingTable == NULL) || + (Count == 0)) { + ASSERT (0); + return EFI_INVALID_PARAMETER; + } + + Table = AllocateZeroPool (sizeof (*Table) * Count); + if (Table == NULL) { + ASSERT (0); + return EFI_OUT_OF_RESOURCES; + } + + MappingTable->Table = Table; + MappingTable->LastIndex = 0; + MappingTable->MaxIndex = Count; + + return EFI_SUCCESS; +} + +/** Free the MappingTable. + + @param [in, out] MappingTable The mapping table structure. +**/ +STATIC +VOID +EFIAPI +MappingTableFree ( + IN OUT MAPPING_TABLE * MappingTable + ) +{ + ASSERT (MappingTable != NULL); + ASSERT (MappingTable->Table != NULL); + + if (MappingTable->Table != NULL) { + FreePool (MappingTable->Table); + } +} + +/** Add a new entry to the MappingTable and return its index. + + If an entry with [Integer] is already available in the table, + return its index without adding a new entry. + + @param [in] MappingTable The mapping table structure. + @param [in] Integer New Integer entry to add. + + @retval The index of the Integer entry in the MappingTable. +**/ +STATIC +UINT32 +EFIAPI +MappingTableAdd ( + IN MAPPING_TABLE * MappingTable, + IN UINT32 Integer + ) +{ + UINT32 * Table; + UINT32 Index; + UINT32 LastIndex; + + ASSERT (MappingTable != NULL); + ASSERT (MappingTable->Table != NULL); + + Table = MappingTable->Table; + LastIndex = MappingTable->LastIndex; + + // Search if there is already an entry with this Integer. + for (Index = 0; Index < LastIndex; Index++) { + if (Table[Index] == Integer) { + return Index; + } + } + + ASSERT (LastIndex < MAX_PCI_LEGACY_INTERRUPT); + ASSERT (LastIndex < MappingTable->MaxIndex); + + // If no, create a new entry. + Table[LastIndex] = Integer; + + return MappingTable->LastIndex++; +} + +/** Generate required Pci device information. + + ASL code: + Name (_UID, [Uid]) // Uid of the Pci device + Name (_HID, EISAID ("PNP0A08")) // PCI Express Root Bridge + Name (_CID, EISAID ("PNP0A03")) // Compatible PCI Root Bridge + Name (_SEG, [Pci segment group]) // PCI Segment Group number + Name (_BBN, [Bus number]) // PCI Base Bus Number + Name (_CCA, 1) // Initially mark the PCI coherent + + @param [in] PciInfo Pci device information. + @param [in] Uid Unique Id of the Pci device. + @param [in, out] PciNode Pci node to amend. + + @retval EFI_SUCCESS Success. + @retval EFI_INVALID_PARAMETER Invalid parameter. + @retval EFI_OUT_OF_RESOURCES Failed to allocate memory. +**/ +STATIC +EFI_STATUS +EFIAPI +GeneratePciDeviceInfo ( + IN CONST CM_ARM_PCI_CONFIG_SPACE_INFO * PciInfo, + IN UINT32 Uid, + IN OUT AML_OBJECT_NODE_HANDLE PciNode + ) +{ + EFI_STATUS Status; + UINT32 EisaId; + + ASSERT (PciInfo != NULL); + ASSERT (PciNode != NULL); + + // ASL: Name (_UID, [Uid]) + Status = AmlCodeGenNameInteger ("_UID", Uid, PciNode, NULL); + if (EFI_ERROR (Status)) { + ASSERT (0); + return Status; + } + + // ASL: Name (_HID, EISAID ("PNP0A08")) + Status = AmlGetEisaIdFromString ("PNP0A08", &EisaId); + if (EFI_ERROR (Status)) { + ASSERT (0); + return Status; + } + Status = AmlCodeGenNameInteger ("_HID", EisaId, PciNode, NULL); + if (EFI_ERROR (Status)) { + ASSERT (0); + return Status; + } + + // ASL: Name (_CID, EISAID ("PNP0A03")) + Status = AmlGetEisaIdFromString ("PNP0A03", &EisaId); + if (EFI_ERROR (Status)) { + ASSERT (0); + return Status; + } + Status = AmlCodeGenNameInteger ("_CID", EisaId, PciNode, NULL); + if (EFI_ERROR (Status)) { + ASSERT (0); + return Status; + } + + // ASL: Name (_SEG, [Pci segment group]) + Status = AmlCodeGenNameInteger ( + "_SEG", + PciInfo->PciSegmentGroupNumber, + PciNode, + NULL + ); + if (EFI_ERROR (Status)) { + ASSERT (0); + return Status; + } + + // ASL: Name (_BBN, [Bus number]) + Status = AmlCodeGenNameInteger ( + "_BBN", + PciInfo->StartBusNumber, + PciNode, + NULL + ); + if (EFI_ERROR (Status)) { + ASSERT (0); + return Status; + } + + // ASL: Name (_CCA, 1) + // Must be aligned with the IORT CCA property in + // "Table 14 Memory access properties" + Status = AmlCodeGenNameInteger ("_CCA", 1, PciNode, NULL); + if (EFI_ERROR (Status)) { + ASSERT (0); + } + return Status; +} + +/** Generate a Link device. + + The Link device is added at the beginning of the ASL Pci device definition. + + Each Link device represents a Pci legacy interrupt (INTA-...-INTD). + + ASL code: + Device ([Link Name]) { + Name (_UID, [Uid]]) + Name (_HID, EISAID ("PNP0C0F")) + Name (_PRS, ResourceTemplate () { + Interrupt (ResourceProducer, Level, ActiveHigh, Exclusive) { [Irq]] } + }) + Method (_CRS, 0) { Return (_PRS) } + Method (_SRS, 1) { } + Method (_DIS) { } + } + + The list of objects to define is available at: + PCI Firmware Specification - Revision 3.3, + s3.5. "Device State at Firmware/Operating System Handoff" + + @param [in] Irq Interrupt controller interrupt. + @param [in] IrqFlags Interrupt flags. + @param [in] LinkIndex Legacy Pci interrupt index. + Must be between 0-INTA and 3-INTD. + @param [in, out] PciNode Pci node to amend. + + @retval EFI_SUCCESS Success. + @retval EFI_INVALID_PARAMETER Invalid parameter. + @retval EFI_OUT_OF_RESOURCES Failed to allocate memory. +**/ +STATIC +EFI_STATUS +EFIAPI +GenerateLinkDevice ( + IN UINT32 Irq, + IN UINT32 IrqFlags, + IN UINT32 LinkIndex, + IN OUT AML_OBJECT_NODE_HANDLE PciNode + ) +{ + EFI_STATUS Status; + CHAR8 AslName[AML_NAME_SEG_SIZE + 1]; + AML_OBJECT_NODE_HANDLE LinkNode; + AML_OBJECT_NODE_HANDLE CrsNode; + UINT32 EisaId; + + ASSERT (LinkIndex < 4); + ASSERT (PciNode != NULL); + + CopyMem (AslName, "LNKx", AML_NAME_SEG_SIZE + 1); + AslName[AML_NAME_SEG_SIZE - 1] = 'A' + LinkIndex; + + // ASL: Device (LNKx) {} + Status = AmlCodeGenDevice (AslName, NULL, &LinkNode); + if (EFI_ERROR (Status)) { + ASSERT (0); + return Status; + } + + // The LNKx devices must be defined before being referenced. + // Thus add it to the head of the Pci list of variable arguments. + Status = AmlAttachNode (PciNode, LinkNode); + if (EFI_ERROR (Status)) { + ASSERT (0); + // Failed to add. + AmlDeleteTree ((AML_NODE_HANDLE)LinkNode); + return Status; + } + + // ASL: Name (_UID, [Uid]) + Status = AmlCodeGenNameInteger ("_UID", LinkIndex, LinkNode, NULL); + if (EFI_ERROR (Status)) { + ASSERT (0); + return Status; + } + + // ASL: Name (_HID, EISAID ("PNP0C0F")) + Status = AmlGetEisaIdFromString ("PNP0C0F", &EisaId); + if (EFI_ERROR (Status)) { + ASSERT (0); + return Status; + } + Status = AmlCodeGenNameInteger ("_HID", EisaId, LinkNode, NULL); + if (EFI_ERROR (Status)) { + ASSERT (0); + return Status; + } + + // ASL: + // Name (_PRS, ResourceTemplate () { + // Interrupt (ResourceProducer, Level, ActiveHigh, Exclusive) { [Irq] } + // }) + Status = AmlCodeGenNameResourceTemplate ("_PRS", LinkNode, &CrsNode); + if (EFI_ERROR (Status)) { + ASSERT (0); + return Status; + } + Status = AmlCodeGenRdInterrupt ( + FALSE, + (IrqFlags & BIT0) != 0, + (IrqFlags & BIT1) != 0, + FALSE, + &Irq, + 1, + CrsNode, + NULL + ); + if (EFI_ERROR (Status)) { + ASSERT (0); + return Status; + } + + // ASL: + // Method (_CRS, 0) { + // Return (_PRS) + // } + Status = AmlCodeGenMethodRetNameString ( + "_CRS", + "_PRS", + 0, + FALSE, + 0, + LinkNode, + NULL + ); + if (EFI_ERROR (Status)) { + ASSERT (0); + return Status; + } + + // ASL: Method (_SRS, 1) {} + // Not possible to set interrupts. + Status = AmlCodeGenMethodRetNameString ( + "_SRS", + NULL, + 1, + FALSE, + 0, + LinkNode, + NULL + ); + if (EFI_ERROR (Status)) { + ASSERT (0); + return Status; + } + + // ASL:Method (_DIS, 1) {} + // Not possible to disable interrupts. + Status = AmlCodeGenMethodRetNameString ( + "_DIS", + NULL, + 0, + FALSE, + 0, + LinkNode, + NULL + ); + if (EFI_ERROR (Status)) { + ASSERT (0); + return Status; + } + + // _STA: + // ACPI 6.4, s6.3.7 "_STA (Device Status)": + // If a device object describes a device that is not on an enumerable bus + // and the device object does not have an _STA object, then OSPM assumes + // that the device is present, enabled, shown in the UI, and functioning. + + // _MAT: + // Not supported. Mainly used for processors. + + return Status; +} + +/** Generate Pci slots devices. + + PCI Firmware Specification - Revision 3.3, + s4.8 "Generic ACPI PCI Slot Description" requests to describe the PCI slot + used. It should be possible to enumerate them, but this is additional + information. + + @param [in] MappingTable The mapping table structure. + @param [in, out] PciNode Pci node to amend. + + @retval EFI_SUCCESS Success. + @retval EFI_INVALID_PARAMETER Invalid parameter. + @retval EFI_OUT_OF_RESOURCES Failed to allocate memory. +**/ +STATIC +EFI_STATUS +EFIAPI +GeneratePciSlots ( + IN CONST MAPPING_TABLE * MappingTable, + IN OUT AML_OBJECT_NODE_HANDLE PciNode + ) +{ + EFI_STATUS Status; + UINT32 Index; + UINT32 LastIndex; + UINT32 DeviceId; + CHAR8 AslName[AML_NAME_SEG_SIZE + 1]; + AML_OBJECT_NODE_HANDLE DeviceNode; + + ASSERT (MappingTable != NULL); + ASSERT (PciNode != NULL); + + // Generic device name is "Dxx". + CopyMem (AslName, "Dxx_", AML_NAME_SEG_SIZE + 1); + + LastIndex = MappingTable->LastIndex; + + // There are at most 32 devices on a Pci bus. + ASSERT (LastIndex < 32); + + for (Index = 0; Index < LastIndex; Index++) { + DeviceId = MappingTable->Table[Index]; + AslName[AML_NAME_SEG_SIZE - 3] = AsciiFromHex (DeviceId & 0xF); + AslName[AML_NAME_SEG_SIZE - 2] = AsciiFromHex ((DeviceId >> 4) & 0xF); + + // ASL: + // Device (Dxx) { + // Name (_ADR, [address value]) + // } + Status = AmlCodeGenDevice (AslName, PciNode, &DeviceNode); + if (EFI_ERROR (Status)) { + ASSERT (0); + return Status; + } + + /* ACPI 6.4 specification, Table 6.2: "ADR Object Address Encodings" + High word-Device #, Low word-Function #. (for example, device 3, + function 2 is 0x00030002). To refer to all the functions on a device #, + use a function number of FFFF). + */ + Status = AmlCodeGenNameInteger ( + "_ADR", + (DeviceId << 16) | 0xFFFF, + DeviceNode, + NULL + ); + if (EFI_ERROR (Status)) { + ASSERT (0); + return Status; + } + + // _SUN object is not generated as we don't know which slot will be used. + } + + return Status; +} + +/** Generate a _PRT object (Pci Routing Table) for the Pci device. + + Cf. ACPI 6.4 specification, s6.2.13 "_PRT (PCI Routing Table)" + + @param [in] Generator The SSDT Pci generator. + @param [in] CfgMgrProtocol Pointer to the Configuration Manager + Protocol interface. + @param [in] PciInfo Pci device information. + @param [in, out] PciNode Pci node to amend. + + @retval EFI_SUCCESS The function completed successfully. + @retval EFI_INVALID_PARAMETER Invalid parameter. + @retval EFI_OUT_OF_RESOURCES Could not allocate memory. +**/ +STATIC +EFI_STATUS +EFIAPI +GeneratePrt ( + IN ACPI_PCI_GENERATOR * Generator, + IN CONST EDKII_CONFIGURATION_MANAGER_PROTOCOL * CONST CfgMgrProtocol, + IN CONST CM_ARM_PCI_CONFIG_SPACE_INFO * PciInfo, + IN OUT AML_OBJECT_NODE_HANDLE PciNode + ) +{ + EFI_STATUS Status; + INT32 Index; + UINT32 IrqTableIndex; + AML_OBJECT_NODE_HANDLE PrtNode; + CHAR8 AslName[AML_NAME_SEG_SIZE + 1]; + CM_ARM_OBJ_REF * RefInfo; + UINT32 RefCount; + CM_ARM_PCI_INTERRUPT_MAP_INFO * IrqMapInfo; + UINT32 IrqFlags; + UINT32 PrevIrqFlags; + + ASSERT (Generator != NULL); + ASSERT (CfgMgrProtocol != NULL); + ASSERT (PciInfo != NULL); + ASSERT (PciNode != NULL); + + // Get the array of CM_ARM_OBJ_REF referencing the + // CM_ARM_PCI_INTERRUPT_MAP_INFO objects. + Status = GetEArmObjCmRef ( + CfgMgrProtocol, + PciInfo->InterruptMapToken, + &RefInfo, + &RefCount + ); + if (EFI_ERROR (Status)) { + ASSERT (0); + return Status; + } + + // Initialized IrqTable. + Status = MappingTableInitialize (&Generator->IrqTable, RefCount); + if (EFI_ERROR (Status)) { + ASSERT (0); + return Status; + } + + // Initialized DeviceTable. + Status = MappingTableInitialize (&Generator->DeviceTable, RefCount); + if (EFI_ERROR (Status)) { + ASSERT (0); + goto exit_handler; + } + + // ASL: Name (_PRT, Package () {}) + Status = AmlCodeGenNamePackage ("_PRT", PciNode, &PrtNode); + if (EFI_ERROR (Status)) { + ASSERT (0); + goto exit_handler; + } + + CopyMem (AslName, "LNKx", AML_NAME_SEG_SIZE + 1); + + for (Index = 0; Index < RefCount; Index++) { + // Get CM_ARM_PCI_INTERRUPT_MAP_INFO structures one by one. + Status = GetEArmObjPciInterruptMapInfo ( + CfgMgrProtocol, + RefInfo[Index].ReferenceToken, + &IrqMapInfo, + NULL + ); + if (EFI_ERROR (Status)) { + ASSERT (0); + goto exit_handler; + } + + // Add the interrupt in the IrqTable and get the link name. + IrqTableIndex = MappingTableAdd ( + &Generator->IrqTable, + IrqMapInfo->IntcInterrupt.Interrupt + ); + AslName[AML_NAME_SEG_SIZE - 1] = 'A' + IrqTableIndex; + + // Check that the interrupts flags are identical for all interrupts. + PrevIrqFlags = IrqFlags; + IrqFlags = IrqMapInfo->IntcInterrupt.Flags; + if ((Index > 0) && (PrevIrqFlags != IrqFlags)) { + ASSERT (0); + return EFI_INVALID_PARAMETER; + } + + // Add the device to the DeviceTable. + MappingTableAdd (&Generator->DeviceTable, IrqMapInfo->PciDevice); + + /* Add a _PRT entry. + ASL + Name (_PRT, Package () { + [OldPrtEntries], + [NewPrtEntry] + }) + + Address is set as: + ACPI 6.4 specification, Table 6.2: "ADR Object Address Encodings" + High word-Device #, Low word-Function #. (for example, device 3, + function 2 is 0x00030002). To refer to all the functions on a device #, + use a function number of FFFF). + */ + Status = AmlAddPrtEntry ( + (IrqMapInfo->PciDevice << 16) | 0xFFFF, + IrqMapInfo->PciInterrupt, + AslName, + 0, + PrtNode + ); + if (EFI_ERROR (Status)) { + ASSERT (0); + goto exit_handler; + } + } // for + + // Generate the LNKx devices now that we know all the interrupts used. + // To look nicer, do it in reverse order since LNKx are added to the head. + for (Index = Generator->IrqTable.LastIndex - 1; Index >= 0; Index--) { + Status = GenerateLinkDevice ( + Generator->IrqTable.Table[Index], + IrqFlags, + Index, + PciNode + ); + if (EFI_ERROR (Status)) { + ASSERT (0); + goto exit_handler; + } + } // for + + // Generate the Pci slots once all the device have been added. + Status = GeneratePciSlots (&Generator->DeviceTable, PciNode); + if (EFI_ERROR (Status)) { + ASSERT (0); + goto exit_handler; + } + +exit_handler: + MappingTableFree (&Generator->IrqTable); + MappingTableFree (&Generator->DeviceTable); + + return Status; +} + +/** Generate a _CRS method for the Pci device. + + @param [in] Generator The SSDT Pci generator. + @param [in] CfgMgrProtocol Pointer to the Configuration Manager + Protocol interface. + @param [in] PciInfo Pci device information. + @param [in, out] PciNode Pci node to amend. + + @retval EFI_SUCCESS The function completed successfully. + @retval EFI_INVALID_PARAMETER Invalid parameter. + @retval EFI_OUT_OF_RESOURCES Could not allocate memory. +**/ +STATIC +EFI_STATUS +EFIAPI +GeneratePciCrs ( + IN ACPI_PCI_GENERATOR * Generator, + IN CONST EDKII_CONFIGURATION_MANAGER_PROTOCOL * CONST CfgMgrProtocol, + IN CONST CM_ARM_PCI_CONFIG_SPACE_INFO * PciInfo, + IN OUT AML_OBJECT_NODE_HANDLE PciNode + ) +{ + EFI_STATUS Status; + BOOLEAN Translation; + UINT32 Index; + CM_ARM_OBJ_REF * RefInfo; + UINT32 RefCount; + CM_ARM_PCI_ADDRESS_MAP_INFO * AddrMapInfo; + AML_OBJECT_NODE_HANDLE CrsNode; + + ASSERT (Generator != NULL); + ASSERT (CfgMgrProtocol != NULL); + ASSERT (PciInfo != NULL); + ASSERT (PciNode != NULL); + + // ASL: Name (_CRS, ResourceTemplate () {}) + Status = AmlCodeGenNameResourceTemplate ("_CRS", PciNode, &CrsNode); + if (EFI_ERROR (Status)) { + ASSERT (0); + return Status; + } + + // ASL: + // WordBusNumber ( // Bus numbers assigned to this root + // ResourceProducer, MinFixed, MaxFixed, PosDecode, + // 0, // AddressGranularity + // [Start], // AddressMinimum - Minimum Bus Number + // [End], // AddressMaximum - Maximum Bus Number + // 0, // AddressTranslation - Set to 0 + // [End] - [Start] + 1 // RangeLength - Number of Busses + // ) + Status = AmlCodeGenRdWordBusNumber ( + FALSE, TRUE, TRUE, TRUE, + 0, + PciInfo->StartBusNumber, + PciInfo->EndBusNumber, + 0, + PciInfo->EndBusNumber - PciInfo->StartBusNumber + 1, + 0, + NULL, + CrsNode, + NULL + ); + if (EFI_ERROR (Status)) { + ASSERT (0); + return Status; + } + + // Get the array of CM_ARM_OBJ_REF referencing the + // CM_ARM_PCI_ADDRESS_MAP_INFO objects. + Status = GetEArmObjCmRef ( + CfgMgrProtocol, + PciInfo->AddressMapToken, + &RefInfo, + &RefCount + ); + if (EFI_ERROR (Status)) { + ASSERT (0); + return Status; + } + + for (Index = 0; Index < RefCount; Index++) { + // Get CM_ARM_PCI_ADDRESS_MAP_INFO structures one by one. + Status = GetEArmObjPciAddressMapInfo ( + CfgMgrProtocol, + RefInfo[Index].ReferenceToken, + &AddrMapInfo, + NULL + ); + if (EFI_ERROR (Status)) { + ASSERT (0); + return Status; + } + + Translation = (AddrMapInfo->CpuAddress != AddrMapInfo->PciAddress); + + switch (AddrMapInfo->SpaceCode) { + case PCI_SS_IO: + Status = AmlCodeGenRdDWordIo ( + FALSE, TRUE, TRUE, TRUE, 3, + 0, + AddrMapInfo->PciAddress, + AddrMapInfo->PciAddress + AddrMapInfo->AddressSize - 1, + Translation ? AddrMapInfo->CpuAddress : 0, + AddrMapInfo->AddressSize, + 0, NULL, + TRUE, + FALSE, + CrsNode, + NULL + ); + break; + + case PCI_SS_M32: + Status = AmlCodeGenRdDWordMemory ( + FALSE, TRUE, TRUE, TRUE, TRUE, TRUE, + 0, + AddrMapInfo->PciAddress, + AddrMapInfo->PciAddress + AddrMapInfo->AddressSize - 1, + Translation ? AddrMapInfo->CpuAddress : 0, + AddrMapInfo->AddressSize, + 0, NULL, + 0, + TRUE, + CrsNode, + NULL + ); + break; + + case PCI_SS_M64: + Status = AmlCodeGenRdQWordMemory ( + FALSE, TRUE, TRUE, TRUE, TRUE, TRUE, + 0, + AddrMapInfo->PciAddress, + AddrMapInfo->PciAddress + AddrMapInfo->AddressSize - 1, + Translation ? AddrMapInfo->CpuAddress : 0, + AddrMapInfo->AddressSize, + 0, NULL, + 0, + TRUE, + CrsNode, + NULL + ); + break; + + default: + Status = EFI_INVALID_PARAMETER; + } // switch + if (EFI_ERROR (Status)) { + ASSERT (0); + return Status; + } + } // for + return Status; +} + +/** Add an _OSC template method to the PciNode. + + The _OSC method is provided as an AML blob. The blob is + parsed and attached at the end of the PciNode list of variable elements. + + @param [in, out] PciNode Pci node to amend. + + @retval EFI_SUCCESS The function completed successfully. + @retval EFI_INVALID_PARAMETER Invalid parameter. + @retval EFI_OUT_OF_RESOURCES Could not allocate memory. +**/ +STATIC +EFI_STATUS +EFIAPI +AddOscMethod ( + IN OUT AML_OBJECT_NODE_HANDLE PciNode + ) +{ + EFI_STATUS Status; + EFI_STATUS Status1; + EFI_ACPI_DESCRIPTION_HEADER * SsdtPcieOscTemplate; + AML_ROOT_NODE_HANDLE RootNode; + AML_OBJECT_NODE_HANDLE OscNode; + + ASSERT (PciNode != NULL); + + // Parse the Ssdt Pci Osc Template. + SsdtPcieOscTemplate = (EFI_ACPI_DESCRIPTION_HEADER*) + ssdtpcieosctemplate_aml_code; + + RootNode = NULL; + Status = AmlParseDefinitionBlock ( + SsdtPcieOscTemplate, + &RootNode + ); + if (EFI_ERROR (Status)) { + DEBUG (( + DEBUG_ERROR, + "ERROR: SSDT-PCI-OSC: Failed to parse SSDT PCI OSC Template." + " Status = %r\n", + Status + )); + return Status; + } + + Status = AmlFindNode (RootNode, "\\_OSC", &OscNode); + if (EFI_ERROR (Status)) { + goto error_handler; + } + + Status = AmlDetachNode (OscNode); + if (EFI_ERROR (Status)) { + goto error_handler; + } + + Status = AmlAttachNode (PciNode, OscNode); + if (EFI_ERROR (Status)) { + goto error_handler; + } + +error_handler: + // Cleanup + Status1 = AmlDeleteTree (RootNode); + if (EFI_ERROR (Status1)) { + DEBUG (( + DEBUG_ERROR, + "ERROR: SSDT-PCI-OSC: Failed to cleanup AML tree." + " Status = %r\n", + Status1 + )); + // If Status was success but we failed to delete the AML Tree + // return Status1 else return the original error code, i.e. Status. + if (!EFI_ERROR (Status)) { + return Status1; + } + } + + return Status; +} + +/** Generate a Pci device. + + @param [in] Generator The SSDT Pci generator. + @param [in] CfgMgrProtocol Pointer to the Configuration Manager + Protocol interface. + @param [in] PciInfo Pci device information. + @param [in] Uid Unique Id of the Pci device. + @param [in, out] RootNode RootNode of the AML tree to populate. + + @retval EFI_SUCCESS The function completed successfully. + @retval EFI_INVALID_PARAMETER Invalid parameter. + @retval EFI_OUT_OF_RESOURCES Could not allocate memory. +**/ +STATIC +EFI_STATUS +EFIAPI +GeneratePciDevice ( + IN ACPI_PCI_GENERATOR * Generator, + IN CONST EDKII_CONFIGURATION_MANAGER_PROTOCOL * CONST CfgMgrProtocol, + IN CONST CM_ARM_PCI_CONFIG_SPACE_INFO * PciInfo, + IN UINT32 Uid, + IN OUT AML_ROOT_NODE_HANDLE * RootNode + ) +{ + EFI_STATUS Status; + + CHAR8 AslName[AML_NAME_SEG_SIZE + 1]; + AML_OBJECT_NODE_HANDLE ScopeNode; + AML_OBJECT_NODE_HANDLE PciNode; + + ASSERT (Generator != NULL); + ASSERT (CfgMgrProtocol != NULL); + ASSERT (PciInfo != NULL); + ASSERT (RootNode != NULL); + + PciNode = NULL; + + // ASL: Scope (\_SB) {} + Status = AmlCodeGenScope (SB_SCOPE, RootNode, &ScopeNode); + if (EFI_ERROR (Status)) { + ASSERT (0); + return Status; + } + + // Write the name of the PCI device. + CopyMem (AslName, "PCIx", AML_NAME_SEG_SIZE + 1); + AslName[AML_NAME_SEG_SIZE - 1] = AsciiFromHex (Uid); + + // ASL: Device (PCIx) {} + Status = AmlCodeGenDevice (AslName, ScopeNode, &PciNode); + if (EFI_ERROR (Status)) { + ASSERT (0); + return Status; + } + + // Populate the PCIx node with some Id values. + Status = GeneratePciDeviceInfo (PciInfo, Uid, PciNode); + if (EFI_ERROR (Status)) { + ASSERT (0); + return Status; + } + + // Generate the Pci Routing Table (_PRT). + if (PciInfo->InterruptMapToken != CM_NULL_TOKEN) { + Status = GeneratePrt ( + Generator, + CfgMgrProtocol, + PciInfo, + PciNode + ); + if (EFI_ERROR (Status)) { + ASSERT (0); + return Status; + } + } + + // Generate the _CRS method. + Status = GeneratePciCrs (Generator, CfgMgrProtocol, PciInfo, PciNode); + if (EFI_ERROR (Status)) { + ASSERT (0); + return Status; + } + + // Add the template _OSC method. + Status = AddOscMethod (PciNode); + if (EFI_ERROR (Status)) { + ASSERT (0); + } + + return Status; +} + +/** Build an Ssdt table describing a Pci device. + + @param [in] Generator The SSDT Pci generator. + @param [in] CfgMgrProtocol Pointer to the Configuration Manager + Protocol interface. + @param [in] PciInfo Pci device information. + @param [in] Uid Unique Id of the Pci device. + @param [out] Table If success, contains the created SSDT table. + + @retval EFI_SUCCESS The function completed successfully. + @retval EFI_INVALID_PARAMETER Invalid parameter. + @retval EFI_OUT_OF_RESOURCES Could not allocate memory. +**/ +STATIC +EFI_STATUS +EFIAPI +BuildSsdtPciTable ( + IN ACPI_PCI_GENERATOR * Generator, + IN CONST EDKII_CONFIGURATION_MANAGER_PROTOCOL * CONST CfgMgrProtocol, + IN CONST CM_ARM_PCI_CONFIG_SPACE_INFO * PciInfo, + IN UINT32 Uid, + OUT EFI_ACPI_DESCRIPTION_HEADER ** Table + ) +{ + EFI_STATUS Status; + EFI_STATUS Status1; + AML_ROOT_NODE_HANDLE RootNode; + CHAR8 OemTableId[9]; + + ASSERT (Generator != NULL); + ASSERT (CfgMgrProtocol != NULL); + ASSERT (PciInfo != NULL); + ASSERT (Table != NULL); + + CopyMem (OemTableId, "SSDTPCIx", sizeof (OemTableId) + 1); + OemTableId[7] = AsciiFromHex(Uid); + + // Create a new Ssdt table. + Status = AmlCodeGenDefinitionBlock ( + "SSDT", + "ARMLTD", + OemTableId, + 1, + &RootNode + ); + if (EFI_ERROR (Status)) { + ASSERT (0); + return Status; + } + + Status = GeneratePciDevice ( + Generator, + CfgMgrProtocol, + PciInfo, + Uid, + RootNode + ); + if (EFI_ERROR (Status)) { + ASSERT (0); + goto exit_handler; + } + + // Serialize the tree. + Status = AmlSerializeDefinitionBlock ( + RootNode, + Table + ); + if (EFI_ERROR (Status)) { + DEBUG (( + DEBUG_ERROR, + "ERROR: SSDT-PCI: Failed to Serialize SSDT Table Data." + " Status = %r\n", + Status + )); + } + +exit_handler: + // Cleanup + Status1 = AmlDeleteTree (RootNode); + if (EFI_ERROR (Status1)) { + DEBUG (( + DEBUG_ERROR, + "ERROR: SSDT-PCI: Failed to cleanup AML tree." + " Status = %r\n", + Status1 + )); + // If Status was success but we failed to delete the AML Tree + // return Status1 else return the original error code, i.e. Status. + if (!EFI_ERROR (Status)) { + return Status1; + } + } + + return Status; +} + +/** Construct SSDT tables describing Pci root complexes. + + This function invokes the Configuration Manager protocol interface + to get the required hardware information for generating the ACPI + table. + + If this function allocates any resources then they must be freed + in the FreeXXXXTableResourcesEx function. + + @param [in] This Pointer to the ACPI table generator. + @param [in] AcpiTableInfo Pointer to the ACPI table information. + @param [in] CfgMgrProtocol Pointer to the Configuration Manager + Protocol interface. + @param [out] Table Pointer to a list of generated ACPI table(s). + @param [out] TableCount Number of generated ACPI table(s). + + @retval EFI_SUCCESS Table generated successfully. + @retval EFI_BAD_BUFFER_SIZE The size returned by the Configuration + Manager is less than the Object size for + the requested object. + @retval EFI_INVALID_PARAMETER A parameter is invalid. + @retval EFI_NOT_FOUND Could not find information. + @retval EFI_OUT_OF_RESOURCES Could not allocate memory. + @retval EFI_UNSUPPORTED Unsupported configuration. +**/ +STATIC +EFI_STATUS +EFIAPI +BuildSsdtPciTableEx ( + IN CONST ACPI_TABLE_GENERATOR * This, + IN CONST CM_STD_OBJ_ACPI_TABLE_INFO * CONST AcpiTableInfo, + IN CONST EDKII_CONFIGURATION_MANAGER_PROTOCOL * CONST CfgMgrProtocol, + OUT EFI_ACPI_DESCRIPTION_HEADER *** Table, + OUT UINTN * CONST TableCount + ) +{ + EFI_STATUS Status; + CM_ARM_PCI_CONFIG_SPACE_INFO * PciInfo; + UINT32 PciCount; + UINTN Index; + EFI_ACPI_DESCRIPTION_HEADER ** TableList; + ACPI_PCI_GENERATOR * Generator; + + ASSERT (This != NULL); + ASSERT (AcpiTableInfo != NULL); + ASSERT (CfgMgrProtocol != NULL); + ASSERT (Table != NULL); + ASSERT (TableCount != NULL); + ASSERT (AcpiTableInfo->TableGeneratorId == This->GeneratorID); + ASSERT (AcpiTableInfo->AcpiTableSignature == This->AcpiTableSignature); + + *TableCount = 0; + Generator = (ACPI_PCI_GENERATOR*)This; + + Status = GetEArmObjPciConfigSpaceInfo ( + CfgMgrProtocol, + CM_NULL_TOKEN, + &PciInfo, + &PciCount + ); + if (EFI_ERROR (Status)) { + ASSERT (0); + return Status; + } + + if (PciCount > MAX_PCI_ROOT_COMPLEXES_SUPPORTED) { + DEBUG (( + DEBUG_ERROR, + "ERROR: SSDT-PCI: Too many Pci root complexes: %d." + " Maximum Pci root complexes supported = %d.\n", + PciCount, + MAX_PCI_ROOT_COMPLEXES_SUPPORTED + )); + return EFI_INVALID_PARAMETER; + } + + // Allocate a table to store pointers to the SSDT tables. + TableList = (EFI_ACPI_DESCRIPTION_HEADER**) + AllocateZeroPool ( + (sizeof (EFI_ACPI_DESCRIPTION_HEADER*) * PciCount) + ); + if (TableList == NULL) { + Status = EFI_OUT_OF_RESOURCES; + DEBUG (( + DEBUG_ERROR, + "ERROR: SSDT-PCI: Failed to allocate memory for Table List." + " Status = %r\n", + Status + )); + return Status; + } + + // Setup the table list early so that appropriate cleanup + // can be done in case of failure. + *Table = TableList; + + for (Index = 0; Index < PciCount; Index++) { + // Build a SSDT table describing the Pci devices. + Status = BuildSsdtPciTable ( + Generator, + CfgMgrProtocol, + &PciInfo[Index], + Index, + &TableList[Index] + ); + if (EFI_ERROR (Status)) { + DEBUG (( + DEBUG_ERROR, + "ERROR: SSDT-PCI: Failed to build associated SSDT table." + " Status = %r\n", + Status + )); + goto error_handler; + } + + *TableCount += 1; + } // for + +error_handler: + // Note: Table list and Table count have been setup. The + // error handler does nothing here as the framework will invoke + // FreeSsdtPciTableEx () even on failure. + return Status; +} + +/** Free any resources allocated for constructing the tables. + + @param [in] This Pointer to the ACPI table generator. + @param [in] AcpiTableInfo Pointer to the ACPI Table Info. + @param [in] CfgMgrProtocol Pointer to the Configuration Manager + Protocol Interface. + @param [in, out] Table Pointer to an array of pointers + to ACPI Table(s). + @param [in] TableCount Number of ACPI table(s). + + @retval EFI_SUCCESS The resources were freed successfully. + @retval EFI_INVALID_PARAMETER The table pointer is NULL or invalid. +**/ +STATIC +EFI_STATUS +EFIAPI +FreeSsdtPciTableEx ( + IN CONST ACPI_TABLE_GENERATOR * CONST This, + IN CONST CM_STD_OBJ_ACPI_TABLE_INFO * CONST AcpiTableInfo, + IN CONST EDKII_CONFIGURATION_MANAGER_PROTOCOL * CONST CfgMgrProtocol, + IN OUT EFI_ACPI_DESCRIPTION_HEADER *** CONST Table, + IN CONST UINTN TableCount + ) +{ + EFI_ACPI_DESCRIPTION_HEADER ** TableList; + UINTN Index; + + ASSERT (This != NULL); + ASSERT (AcpiTableInfo != NULL); + ASSERT (CfgMgrProtocol != NULL); + ASSERT (AcpiTableInfo->TableGeneratorId == This->GeneratorID); + ASSERT (AcpiTableInfo->AcpiTableSignature == This->AcpiTableSignature); + + if ((Table == NULL) || + (*Table == NULL) || + (TableCount == 0)) { + DEBUG ((DEBUG_ERROR, "ERROR: SSDT-PCI: Invalid Table Pointer\n")); + return EFI_INVALID_PARAMETER; + } + + TableList = *Table; + for (Index = 0; Index < TableCount; Index++) { + if ((TableList[Index] != NULL) && + (TableList[Index]->Signature == + EFI_ACPI_6_3_SECONDARY_SYSTEM_DESCRIPTION_TABLE_SIGNATURE)) { + FreePool (TableList[Index]); + } else { + DEBUG (( + DEBUG_ERROR, + "ERROR: SSDT-PCI: Could not free SSDT table at index %d.", + Index + )); + return EFI_INVALID_PARAMETER; + } + } //for + + // Free the table list. + FreePool (*Table); + + return EFI_SUCCESS; +} + +/** This macro defines the SSDT Pci Table Generator revision. +*/ +#define SSDT_PCI_GENERATOR_REVISION CREATE_REVISION (1, 0) + +/** The interface for the SSDT Pci Table Generator. +*/ +STATIC +ACPI_PCI_GENERATOR SsdtPcieGenerator = { + // ACPI table generator header + { + // Generator ID + CREATE_STD_ACPI_TABLE_GEN_ID (EStdAcpiTableIdSsdtPciExpress), + // Generator Description + L"ACPI.STD.SSDT.PCI.GENERATOR", + // ACPI Table Signature + EFI_ACPI_6_3_SECONDARY_SYSTEM_DESCRIPTION_TABLE_SIGNATURE, + // ACPI Table Revision - Unused + 0, + // Minimum ACPI Table Revision - Unused + 0, + // Creator ID + TABLE_GENERATOR_CREATOR_ID_ARM, + // Creator Revision + SSDT_PCI_GENERATOR_REVISION, + // Build table function. Use the extended version instead. + NULL, + // Free table function. Use the extended version instead. + NULL, + // Extended Build table function. + BuildSsdtPciTableEx, + // Extended free function. + FreeSsdtPciTableEx + }, + + // Private fields are defined from here. + + // IrqTable + { + // Table + NULL, + // LastIndex + 0, + // MaxIndex + 0 + }, + // DeviceTable + { + // Table + NULL, + // LastIndex + 0, + // MaxIndex + 0 + }, +}; + +/** Register the Generator with the ACPI Table Factory. + + @param [in] ImageHandle The handle to the image. + @param [in] SystemTable Pointer to the System Table. + + @retval EFI_SUCCESS The Generator is registered. + @retval EFI_INVALID_PARAMETER A parameter is invalid. + @retval EFI_ALREADY_STARTED The Generator for the Table ID + is already registered. +**/ +EFI_STATUS +EFIAPI +AcpiSsdtPcieLibConstructor ( + IN EFI_HANDLE ImageHandle, + IN EFI_SYSTEM_TABLE * SystemTable + ) +{ + EFI_STATUS Status; + Status = RegisterAcpiTableGenerator (&SsdtPcieGenerator.Header); + DEBUG (( + DEBUG_INFO, + "SSDT-PCI: Register Generator. Status = %r\n", + Status + )); + ASSERT_EFI_ERROR (Status); + + return Status; +} + +/** Deregister the Generator from the ACPI Table Factory. + + @param [in] ImageHandle The handle to the image. + @param [in] SystemTable Pointer to the System Table. + + @retval EFI_SUCCESS The Generator is deregistered. + @retval EFI_INVALID_PARAMETER A parameter is invalid. + @retval EFI_NOT_FOUND The Generator is not registered. +**/ +EFI_STATUS +EFIAPI +AcpiSsdtPcieLibDestructor ( + IN EFI_HANDLE ImageHandle, + IN EFI_SYSTEM_TABLE * SystemTable + ) +{ + EFI_STATUS Status; + Status = DeregisterAcpiTableGenerator (&SsdtPcieGenerator.Header); + DEBUG (( + DEBUG_INFO, + "SSDT-PCI: Deregister Generator. Status = %r\n", + Status + )); + ASSERT_EFI_ERROR (Status); + return Status; +} diff --git a/DynamicTablesPkg/Library/Acpi/Arm/AcpiSsdtPcieLibArm/SsdtPcieGenerator.h b/DynamicTablesPkg/Library/Acpi/Arm/AcpiSsdtPcieLibArm/SsdtPcieGenerator.h new file mode 100644 index 000000000000..2b7f40447d87 --- /dev/null +++ b/DynamicTablesPkg/Library/Acpi/Arm/AcpiSsdtPcieLibArm/SsdtPcieGenerator.h @@ -0,0 +1,134 @@ +/** @file + SSDT Pcie Table Generator. + + Copyright (c) 2021, Arm Limited. All rights reserved.
+ SPDX-License-Identifier: BSD-2-Clause-Patent + + @par Reference(s): + - PCI Firmware Specification - Revision 3.0 + - ACPI 6.4 specification: + - s6.2.13 "_PRT (PCI Routing Table)" + - s6.1.1 "_ADR (Address)" + - linux kernel code +**/ + +#ifndef SSDT_PCIE_GENERATOR_H_ +#define SSDT_PCIE_GENERATOR_H_ + +/** Pci address attributes. +*/ +#define PCI_SS_CONFIG 0 +#define PCI_SS_IO 1 +#define PCI_SS_M32 2 +#define PCI_SS_M64 3 + +/** Maximum Pci root complexes supported by this generator. + + Note: This is not a hard limitation and can be extended if needed. + Corresponding changes would be needed to support the Name and + UID fields describing the Pci root complexes. +*/ +#define MAX_PCI_ROOT_COMPLEXES_SUPPORTED 16 + +/** Maximum number of Pci legacy interrupts. + + Currently 4 for INTA-INTB-INTC-INTD. +*/ +#define MAX_PCI_LEGACY_INTERRUPT 4 + +// _SB scope of the AML namespace. +#define SB_SCOPE "\\_SB_" + +/** C array containing the compiled AML template. + This symbol is defined in the auto generated C file + containing the AML bytecode array. +*/ +extern CHAR8 ssdtpcieosctemplate_aml_code[]; + +#pragma pack(1) + +/** Structure used to map integer to an index. +*/ +typedef struct MappingTable { + /// Mapping table. + /// Contains the Index <-> integer mapping + UINT32 * Table; + + /// Last used index of the Table. + /// Bound by MaxIndex. + UINT32 LastIndex; + + /// Number of entries in the Table. + UINT32 MaxIndex; +} MAPPING_TABLE; + +/** A structure holding the Pcie generator and additional private data. +*/ +typedef struct AcpiPcieGenerator { + /// ACPI Table generator header + ACPI_TABLE_GENERATOR Header; + + // Private fields are defined from here. + + /** A structure used to handle the Address and Interrupt Map referencing. + + A CM_ARM_PCI_CONFIG_SPACE_INFO structure references two CM_ARM_OBJ_REF: + - one for the address mapping, referencing + CM_ARM_PCI_ADDRESS_MAP_INFO structures. + - one for the address mapping, referencing + CM_ARM_PCI_INTERRUPT_MAP_INFO structures. + + Example (for the interrupt mapping): + (Pci0) + CM_ARM_PCI_CONFIG_SPACE_INFO + | + v + (List of references to address mappings) + CM_ARM_OBJ_REF + | + +---------------------------------------- + | | + v v + (A first interrupt mapping) (A second interrupt mapping) + CM_ARM_PCI_INTERRUPT_MAP_INFO[0] CM_ARM_PCI_INTERRUPT_MAP_INFO[1] + + The CM_ARM_PCI_INTERRUPT_MAP_INFO objects cannot be handled individually. + Device's Pci legacy interrupts that are mapped to the same CPU interrupt + are grouped under a Link device. + For instance, the following mapping: + - [INTA of device 0] mapped on [GIC irq 168] + - [INTB of device 1] mapped on [GIC irq 168] + will be represented in an SSDT table as: + - [INTA of device 0] mapped on [Link device A] + - [INTB of device 1] mapped on [Link device A] + - [Link device A] mapped on [GIC irq 168] + + Counting the number of Cpu interrupts used and grouping them in Link + devices is done through this IRQ_TABLE. + + ASL code: + Scope (_SB) { + Device (LNKA) { + [...] + Name (_PRS, ResourceTemplate () { + Interrupt (ResourceProducer, Level, ActiveHigh, Exclusive) { 168 } + }) + } + + Device (PCI0) { + Name (_PRT, Package () { + Package (0x0FFFF, 0, LNKA, 0) // INTA of device 0 <-> LNKA + Package (0x1FFFF, 1, LNKA, 0) // INTB of device 1 <-> LNKA + }) + } + } + */ + MAPPING_TABLE IrqTable; + + /// Table to map: Index <-> Pci device + MAPPING_TABLE DeviceTable; +} ACPI_PCI_GENERATOR; + +#pragma pack() + +#endif // SSDT_PCIE_GENERATOR_H_ diff --git a/DynamicTablesPkg/Library/Acpi/Arm/AcpiSsdtPcieLibArm/SsdtPcieLibArm.inf b/DynamicTablesPkg/Library/Acpi/Arm/AcpiSsdtPcieLibArm/SsdtPcieLibArm.inf new file mode 100644 index 000000000000..283b5648017c --- /dev/null +++ b/DynamicTablesPkg/Library/Acpi/Arm/AcpiSsdtPcieLibArm/SsdtPcieLibArm.inf @@ -0,0 +1,32 @@ +## @file +# Ssdt Serial Port Table Generator +# +# Copyright (c) 2021, Arm Limited. All rights reserved.
+# +# SPDX-License-Identifier: BSD-2-Clause-Patent +## + +[Defines] + INF_VERSION = 0x0001001B + BASE_NAME = SsdtPcieLibArm + FILE_GUID = E431D7FD-26BF-4E3D-9064-5B13B0439057 + VERSION_STRING = 1.0 + MODULE_TYPE = DXE_DRIVER + LIBRARY_CLASS = NULL|DXE_DRIVER + CONSTRUCTOR = AcpiSsdtPcieLibConstructor + DESTRUCTOR = AcpiSsdtPcieLibDestructor + +[Sources] + SsdtPcieGenerator.c + SsdtPcieGenerator.h + SsdtPcieOscTemplate.asl + +[Packages] + DynamicTablesPkg/DynamicTablesPkg.dec + EmbeddedPkg/EmbeddedPkg.dec + MdePkg/MdePkg.dec + +[LibraryClasses] + AcpiHelperLib + AmlLib + BaseLib diff --git a/DynamicTablesPkg/Library/Acpi/Arm/AcpiSsdtPcieLibArm/SsdtPcieOscTemplate.asl b/DynamicTablesPkg/Library/Acpi/Arm/AcpiSsdtPcieLibArm/SsdtPcieOscTemplate.asl new file mode 100644 index 000000000000..feaf56b53384 --- /dev/null +++ b/DynamicTablesPkg/Library/Acpi/Arm/AcpiSsdtPcieLibArm/SsdtPcieOscTemplate.asl @@ -0,0 +1,80 @@ +/** @file + SSDT Pci Osc (Operating System Capabilities) + + Copyright (c) 2021, Arm Limited. All rights reserved.
+ + SPDX-License-Identifier: BSD-2-Clause-Patent + + @par Reference(s): + - PCI Firmware Specification - Revision 3.3 + - ACPI 6.4 specification: + - s6.2.13 "_PRT (PCI Routing Table)" + - s6.1.1 "_ADR (Address)" + - linux kernel code +**/ + +DefinitionBlock ("SsdtPciOsc.aml", "SSDT", 2, "ARMLTD", "PCI-OSC", 1) { + + // This table is just a template and is never installed as a table. + // Pci devices are dynamically created at runtime as: + // ASL: + // Device (PCIx) { + // ... + // } + // and the _OSC method available below is appended to the PCIx device as: + // ASL: + // Device (PCIx) { + // ... + // Method (_OSC, 4 { + // ... + // }) + // } + Method (_OSC, 4) { + // + // OS Control Handoff + // + Name (SUPP, Zero) // PCI _OSC Support Field value + Name (CTRL, Zero) // PCI _OSC Control Field value + + // Create DWord-addressable fields from the Capabilities Buffer + CreateDWordField (Arg3, 0, CDW1) + CreateDWordField (Arg3, 4, CDW2) + CreateDWordField (Arg3, 8, CDW3) + + // Check for proper UUID + If (LEqual (Arg0,ToUUID ("33DB4D5B-1FF7-401C-9657-7441C03DD766"))) { + + // Save Capabilities DWord2 & 3 + Store (CDW2, SUPP) + Store (CDW3, CTRL) + + // Only allow native hot plug control if OS supports: + // * ASPM + // * Clock PM + // * MSI/MSI-X + If (LNotEqual (And (SUPP, 0x16), 0x16)) { + And (CTRL, 0x1E, CTRL) // Mask bit 0 (and undefined bits) + } + + // Always allow native PME, AER (no dependencies) + + // Never allow SHPC (no SHPC controller in this system) + And (CTRL, 0x1D, CTRL) + + If (LNotEqual (Arg1, One)) { // Unknown revision + Or (CDW1, 0x08, CDW1) + } + + If (LNotEqual (CDW3, CTRL)) { // Capabilities bits were masked + Or (CDW1, 0x10, CDW1) + } + + // Update DWORD3 in the buffer + Store (CTRL,CDW3) + Return (Arg3) + } Else { + Or (CDW1, 4, CDW1) // Unrecognized UUID + Return (Arg3) + } // If + } // _OSC +} -- 2.17.1