From 375683654d46380e4e557502141e9823f6b68445 Mon Sep 17 00:00:00 2001 From: Pierre Gondois Date: Thu, 6 Aug 2020 14:59:32 +0100 Subject: DynamicTablesPkg: Add SSDT CMN-600 Table generator The Generic ACPI for Arm Components 1.0 Platform Design Document, s2.6.4 "ASL code examples" provides information to describe an Arm CoreLink CMN-600 Coherent Mesh Network using an ASL definition block table. The SSDT CMN-600 Table Generator uses the Configuration Manager protocol to obtain the following information about the CMN-600 device on the platform: - the PERIPHBASE address location and address range; - the ROOTNODEBASE address location; - the number of Debug and Trace Controller (DTC) and their respective interrupt number; The CMN-600 mesh is described using the CM_ARM_CMN_600_INFO and CM_ARM_EXTENDED_INTERRUPT structures in the Configuration Manager. The SSDT CMN-600 Table generator: - gets the CMN-600 hardware information from the configuration manager. - uses the AmlLib interfaces to parse the AML template BLOB and construct an AML tree. - uses the AmlLib to update: - the "_UID" value; - the address location and range of the PERIPHBASE; - the address location of the ROOTNODEBASE; - the number of Debug and Trace Controller (DTC) and their respective interrupt number; - serializes the AML tree to an output buffer. This output buffer contains the fixed-up AML code, which is then installed as an ACPI SSDT table. Signed-off-by: Pierre Gondois Co-authored-by: Sami Mujawar Reviewed-by: Alexei Fedorov --- DynamicTablesPkg/DynamicTables.dsc.inc | 2 + DynamicTablesPkg/DynamicTablesPkg.ci.yaml | 4 + DynamicTablesPkg/Include/AcpiTableGenerator.h | 5 + DynamicTablesPkg/Include/ArmNameSpaceObjects.h | 64 +- .../Arm/AcpiSsdtCmn600LibArm/SsdtCmn600Generator.c | 708 +++++++++++++++++++++ .../Arm/AcpiSsdtCmn600LibArm/SsdtCmn600Generator.h | 51 ++ .../Arm/AcpiSsdtCmn600LibArm/SsdtCmn600LibArm.inf | 34 + .../AcpiSsdtCmn600LibArm/SsdtCmn600Template.asl | 81 +++ 8 files changed, 943 insertions(+), 6 deletions(-) create mode 100644 DynamicTablesPkg/Library/Acpi/Arm/AcpiSsdtCmn600LibArm/SsdtCmn600Generator.c create mode 100644 DynamicTablesPkg/Library/Acpi/Arm/AcpiSsdtCmn600LibArm/SsdtCmn600Generator.h create mode 100644 DynamicTablesPkg/Library/Acpi/Arm/AcpiSsdtCmn600LibArm/SsdtCmn600LibArm.inf create mode 100644 DynamicTablesPkg/Library/Acpi/Arm/AcpiSsdtCmn600LibArm/SsdtCmn600Template.asl (limited to 'DynamicTablesPkg') diff --git a/DynamicTablesPkg/DynamicTables.dsc.inc b/DynamicTablesPkg/DynamicTables.dsc.inc index 7fb14d8d14..fa33b7ee67 100644 --- a/DynamicTablesPkg/DynamicTables.dsc.inc +++ b/DynamicTablesPkg/DynamicTables.dsc.inc @@ -34,6 +34,7 @@ # AML Fixup DynamicTablesPkg/Library/Acpi/Arm/AcpiSsdtSerialPortLibArm/SsdtSerialPortLibArm.inf + DynamicTablesPkg/Library/Acpi/Arm/AcpiSsdtCmn600LibArm/SsdtCmn600LibArm.inf # # Dynamic Table Factory Dxe @@ -53,6 +54,7 @@ # AML Fixup NULL|DynamicTablesPkg/Library/Acpi/Arm/AcpiSsdtSerialPortLibArm/SsdtSerialPortLibArm.inf + NULL|DynamicTablesPkg/Library/Acpi/Arm/AcpiSsdtCmn600LibArm/SsdtCmn600LibArm.inf } # diff --git a/DynamicTablesPkg/DynamicTablesPkg.ci.yaml b/DynamicTablesPkg/DynamicTablesPkg.ci.yaml index ecb05f06fb..211615bc80 100644 --- a/DynamicTablesPkg/DynamicTablesPkg.ci.yaml +++ b/DynamicTablesPkg/DynamicTablesPkg.ci.yaml @@ -70,6 +70,7 @@ # in matching files "ExtendWords": [ "ARMHB", # ARMHB000 + "ARMHC", # ARMHC600 "ARMLTD", "AMLDBG", "EISAID", @@ -82,8 +83,11 @@ "lgreater", "lless", "MPIDR", + "PERIPHBASE", "pytool", "Roadmap", + "ROOTNODEBASE", + "ssdtcmn", "ssdtserialporttemplate", "SMMUV", "standardised", diff --git a/DynamicTablesPkg/Include/AcpiTableGenerator.h b/DynamicTablesPkg/Include/AcpiTableGenerator.h index ef5018c312..352331d6dc 100644 --- a/DynamicTablesPkg/Include/AcpiTableGenerator.h +++ b/DynamicTablesPkg/Include/AcpiTableGenerator.h @@ -59,6 +59,10 @@ The Dynamic Tables Framework implements the following ACPI table generators: The SSDT Serial generator collates the Serial port information from the Configuration Manager and patches the SSDT Serial Port template to build the SSDT Serial port table. + - SSDT CMN-600: + The SSDT CMN-600 generator collates the CMN-600 information + from the Configuration Manager and patches the SSDT CMN-600 + template to build the SSDT CMN-600 table. */ /** The ACPI_TABLE_GENERATOR_ID type describes ACPI table generator ID. @@ -83,6 +87,7 @@ typedef enum StdAcpiTableId { EStdAcpiTableIdPptt, ///< PPTT Generator EStdAcpiTableIdSrat, ///< SRAT Generator EStdAcpiTableIdSsdtSerialPort, ///< SSDT Serial-Port Generator + EStdAcpiTableIdSsdtCmn600, ///< SSDT Cmn-600 Generator EStdAcpiTableIdMax } ESTD_ACPI_TABLE_ID; diff --git a/DynamicTablesPkg/Include/ArmNameSpaceObjects.h b/DynamicTablesPkg/Include/ArmNameSpaceObjects.h index b2534a6505..f065486644 100644 --- a/DynamicTablesPkg/Include/ArmNameSpaceObjects.h +++ b/DynamicTablesPkg/Include/ArmNameSpaceObjects.h @@ -57,6 +57,7 @@ typedef enum ArmObjectID { EArmObjDeviceHandlePci, ///< 33 - Device Handle Pci EArmObjGenericInitiatorAffinityInfo, ///< 34 - Generic Initiator Affinity EArmObjSerialPortInfo, ///< 35 - Generic Serial Port Info + EArmObjCmn600Info, ///< 36 - CMN-600 Info EArmObjMax } EARM_OBJECT_ID; @@ -653,18 +654,37 @@ typedef struct CmArmIdMapping { UINT32 Flags; } CM_ARM_ID_MAPPING; -/** A structure that describes the - SMMU interrupts for the Platform. - - ID: EArmObjSmmuInterruptArray +/** A structure that describes the Arm + Generic Interrupts. */ -typedef struct CmArmSmmuInterrupt { +typedef struct CmArmGenericInterrupt { /// Interrupt number UINT32 Interrupt; /// Flags UINT32 Flags; -} CM_ARM_SMMU_INTERRUPT; +} CM_ARM_GENERIC_INTERRUPT; + +/** A structure that describes the SMMU interrupts for the Platform. + + Interrupt Interrupt number. + Flags Interrupt flags as defined for SMMU node. + + ID: EArmObjSmmuInterruptArray +*/ +typedef CM_ARM_GENERIC_INTERRUPT CM_ARM_SMMU_INTERRUPT; + +/** A structure that describes the AML Extended Interrupts. + + Interrupt Interrupt number. + Flags Interrupt flags as defined by the Interrupt + Vector Flags (Byte 3) of the Extended Interrupt + resource descriptor. + See EFI_ACPI_EXTENDED_INTERRUPT_FLAG_xxx in Acpi10.h + + ID: EArmObjExtendedInterruptInfo +*/ +typedef CM_ARM_GENERIC_INTERRUPT CM_ARM_EXTENDED_INTERRUPT; /** A structure that describes the Processor Hierarchy Node (Type 0) in PPTT @@ -825,6 +845,38 @@ typedef struct CmArmGenericInitiatorAffinityInfo { CM_OBJECT_TOKEN DeviceHandleToken; } CM_ARM_GENERIC_INITIATOR_AFFINITY_INFO; +/** A structure that describes the CMN-600 hardware. + + ID: EArmObjCmn600Info +*/ +typedef struct CmArmCmn600Info { + /// The PERIPHBASE address. + /// Corresponds to the Configuration Node Region (CFGR) base address. + UINT64 PeriphBaseAddress; + + /// The PERIPHBASE address length. + /// Corresponds to the CFGR base address length. + UINT64 PeriphBaseAddressLength; + + /// The ROOTNODEBASE address. + /// Corresponds to the Root node (ROOT) base address. + UINT64 RootNodeBaseAddress; + + /// The Debug and Trace Logic Controller (DTC) count. + /// CMN-600 can have maximum 4 DTCs. + UINT8 DtcCount; + + /// DTC Interrupt list. + /// The first interrupt resource descriptor pertains to + /// DTC[0], the second to DTC[1] and so on. + /// DtcCount determines the number of DTC Interrupts that + /// are populated. If DTC count is 2 then DtcInterrupt[2] + /// and DtcInterrupt[3] are ignored. + /// Note: The size of CM_ARM_CMN_600_INFO structure remains + /// constant and does not vary with the DTC count. + CM_ARM_EXTENDED_INTERRUPT DtcInterrupt[4]; +} CM_ARM_CMN_600_INFO; + #pragma pack() #endif // ARM_NAMESPACE_OBJECTS_H_ diff --git a/DynamicTablesPkg/Library/Acpi/Arm/AcpiSsdtCmn600LibArm/SsdtCmn600Generator.c b/DynamicTablesPkg/Library/Acpi/Arm/AcpiSsdtCmn600LibArm/SsdtCmn600Generator.c new file mode 100644 index 0000000000..97a5c55fa3 --- /dev/null +++ b/DynamicTablesPkg/Library/Acpi/Arm/AcpiSsdtCmn600LibArm/SsdtCmn600Generator.c @@ -0,0 +1,708 @@ +/** @file + SSDT CMN-600 AML Table Generator. + + Copyright (c) 2020, Arm Limited. All rights reserved.
+ + SPDX-License-Identifier: BSD-2-Clause-Patent + + @par Reference(s): + - Arm CoreLink CMN-600 Coherent Mesh Network Technical Reference Manual r3p0 + - Generic ACPI for Arm Components 1.0 Platform Design Document +**/ + +#include +#include +#include +#include +#include +#include +#include +#include + +// Module specific include files. +#include +#include +#include +#include +#include +#include +#include "SsdtCmn600Generator.h" + +/** C array containing the compiled AML template. + This symbol is defined in the auto generated C file + containing the AML bytecode array. +*/ +extern CHAR8 ssdtcmn600template_aml_code[]; + +/** SSDT CMN-600 Table Generator. + + Requirements: + The following Configuration Manager Object(s) are required by + this Generator: + - EArmObjCmn600Info +*/ + +/** This macro expands to a function that retrieves the CMN-600 + Information from the Configuration Manager. +*/ +GET_OBJECT_LIST ( + EObjNameSpaceArm, + EArmObjCmn600Info, + CM_ARM_CMN_600_INFO + ); + +/** Check the CMN-600 Information. + + @param [in] Cmn600InfoList Array of CMN-600 information structure. + @param [in] Cmn600Count Count of CMN-600 information structure. + + @retval EFI_SUCCESS The function completed successfully. + @retval EFI_INVALID_PARAMETER Invalid parameter. +**/ +STATIC +EFI_STATUS +EFIAPI +ValidateCmn600Info ( + IN CONST CM_ARM_CMN_600_INFO * Cmn600InfoList, + IN CONST UINT32 Cmn600Count + ) +{ + UINT32 Index; + UINT32 DtcIndex; + CONST CM_ARM_CMN_600_INFO * Cmn600Info; + CONST CM_ARM_GENERIC_INTERRUPT * DtcInterrupt; + + if ((Cmn600InfoList == NULL) || + (Cmn600Count == 0)) { + return EFI_INVALID_PARAMETER; + } + + // Validate each Cmn600Info structure. + for (Index = 0; Index < Cmn600Count; Index++) { + Cmn600Info = &Cmn600InfoList[Index]; + + // At least one DTC is required. + if ((Cmn600Info->DtcCount == 0) || + (Cmn600Info->DtcCount > MAX_DTC_COUNT)) { + DEBUG (( + DEBUG_ERROR, + "ERROR: SSDT-CMN-600: Invalid DTC configuration:\n" + )); + goto error_handler; + } + + // Check PERIPHBASE and ROOTNODEBASE address spaces are initialized. + if ((Cmn600Info->PeriphBaseAddress == 0) || + (Cmn600Info->RootNodeBaseAddress == 0)) { + DEBUG (( + DEBUG_ERROR, + "ERROR: SSDT-CMN-600: Invalid PERIPHBASE or ROOTNODEBASE.\n" + )); + goto error_handler; + } + + // The PERIPHBASE address must be 64MB aligned for a (X < 4) && (Y < 4) + // dimension mesh, and 256MB aligned otherwise. + // Check it is a least 64MB aligned. + if ((Cmn600Info->PeriphBaseAddress & + (PERIPHBASE_MIN_ADDRESS_LENGTH - 1)) != 0) { + DEBUG (( + DEBUG_ERROR, + "ERROR: SSDT-CMN-600: PERIPHBASE address must be 64MB aligned.\n" + )); + goto error_handler; + } + + // The PERIPHBASE address is at most 64MB for a (X < 4) && (Y < 4) + // dimension mesh, and 256MB otherwise. Check it is not more than 256MB. + if (Cmn600Info->PeriphBaseAddressLength > PERIPHBASE_MAX_ADDRESS_LENGTH) { + DEBUG (( + DEBUG_ERROR, + "ERROR: SSDT-CMN-600: PERIPHBASE address range must be < 256MB.\n" + )); + goto error_handler; + } + + // Check the 16 KB alignment of the ROOTNODEBASE address. + if ((Cmn600Info->PeriphBaseAddress & + (ROOTNODEBASE_ADDRESS_LENGTH - 1)) != 0) { + DEBUG (( + DEBUG_ERROR, + "ERROR: SSDT-CMN-600: Root base address must be 16KB aligned.\n" + )); + goto error_handler; + } + + // The ROOTNODEBASE address space should be included in the PERIPHBASE + // address space. + if ((Cmn600Info->PeriphBaseAddress > Cmn600Info->RootNodeBaseAddress) || + ((Cmn600Info->PeriphBaseAddress + Cmn600Info->PeriphBaseAddressLength) < + (Cmn600Info->RootNodeBaseAddress + ROOTNODEBASE_ADDRESS_LENGTH))) { + DEBUG (( + DEBUG_ERROR, + "ERROR: SSDT-CMN-600:" + " ROOTNODEBASE address space not in PERIPHBASE address space.\n" + )); + goto error_handler; + } + + for (DtcIndex = 0; DtcIndex < Cmn600Info->DtcCount; DtcIndex++) { + DtcInterrupt = &Cmn600Info->DtcInterrupt[DtcIndex]; + if (((DtcInterrupt->Flags & + EFI_ACPI_EXTENDED_INTERRUPT_FLAG_PRODUCER_CONSUMER_MASK) == 0)) { + DEBUG (( + DEBUG_ERROR, + "ERROR: SSDT-CMN-600: DTC Interrupt must be consumer.\n" + )); + goto error_handler; + } + } // for DTC Interrupt + + } //for Cmn600InfoList + + return EFI_SUCCESS; + +error_handler: + + DEBUG (( + DEBUG_ERROR, + "PeriphBaseAddress = 0x%llx\n" + "PeriphBaseAddressLength = 0x%llx\n" + "RootNodeBaseAddress = 0x%llx\n" + "DtcCount = %u\n", + Cmn600Info->PeriphBaseAddress, + Cmn600Info->PeriphBaseAddressLength, + Cmn600Info->RootNodeBaseAddress, + Cmn600Info->DtcCount + )); + + DEBUG_CODE ( + for (DtcIndex = 0; DtcIndex < Cmn600Info->DtcCount; DtcIndex++) { + DtcInterrupt = &Cmn600Info->DtcInterrupt[DtcIndex]; + DEBUG (( + DEBUG_ERROR, + " DTC[%d]:\n", + DtcIndex + )); + DEBUG (( + DEBUG_ERROR, + " Interrupt = 0x%lx\n", + DtcInterrupt->Interrupt + )); + DEBUG (( + DEBUG_ERROR, + " Flags = 0x%lx\n", + DtcInterrupt->Flags + )); + } // for + ); + + return EFI_INVALID_PARAMETER; +} + +/** Build a SSDT table describing the CMN-600 device. + + The table created by this function must be freed by FreeSsdtCmn600Table. + + @param [in] Cmn600Info Pointer to a Cmn600 structure. + @param [in] Name The Name to give to the Device. + Must be a NULL-terminated ASL NameString + e.g.: "DEV0", "DV15.DEV0", etc. + @param [in] Uid UID for the CMN600 device. + @param [out] Table If success, pointer to the created SSDT table. + + @retval EFI_SUCCESS Table generated successfully. + @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. +**/ +STATIC +EFI_STATUS +EFIAPI +FixupCmn600Info ( + IN CONST CM_ARM_CMN_600_INFO * Cmn600Info, + IN CONST CHAR8 * Name, + IN CONST UINT64 Uid, + OUT EFI_ACPI_DESCRIPTION_HEADER ** Table + ) +{ + EFI_STATUS Status; + EFI_STATUS Status1; + UINT8 Index; + CONST CM_ARM_GENERIC_INTERRUPT * DtcInt; + + EFI_ACPI_DESCRIPTION_HEADER * SsdtCmn600Template; + AML_ROOT_NODE_HANDLE RootNodeHandle; + AML_OBJECT_NODE_HANDLE NameOpIdNode; + AML_OBJECT_NODE_HANDLE NameOpCrsNode; + AML_DATA_NODE_HANDLE CmnPeriphBaseRdNode; + AML_DATA_NODE_HANDLE CmnRootNodeBaseRdNode; + AML_OBJECT_NODE_HANDLE DeviceNode; + + // Parse the Ssdt CMN-600 Template. + SsdtCmn600Template = (EFI_ACPI_DESCRIPTION_HEADER*) + ssdtcmn600template_aml_code; + + RootNodeHandle = NULL; + Status = AmlParseDefinitionBlock ( + SsdtCmn600Template, + &RootNodeHandle + ); + if (EFI_ERROR (Status)) { + DEBUG (( + DEBUG_ERROR, + "ERROR: SSDT-CMN-600: Failed to parse SSDT CMN-600 Template." + " Status = %r\n", + Status + )); + return Status; + } + + // Get the _UID NameOp object defined by the "Name ()" statement, + // and update its value. + Status = AmlFindNode ( + RootNodeHandle, + "\\_SB_.CMN0._UID", + &NameOpIdNode + ); + if (EFI_ERROR (Status)) { + goto error_handler; + } + + Status = AmlNameOpUpdateInteger (NameOpIdNode, (UINT64)Uid); + if (EFI_ERROR (Status)) { + goto error_handler; + } + + // Get the _CRS object defined by the "Name ()" statement. + Status = AmlFindNode ( + RootNodeHandle, + "\\_SB.CMN0._CRS", + &NameOpCrsNode + ); + if (EFI_ERROR (Status)) { + goto error_handler; + } + + // Get the first Rd node in the "_CRS" object. + // This is the PERIPHBASE node. + Status = AmlNameOpCrsGetFirstRdNode (NameOpCrsNode, &CmnPeriphBaseRdNode); + if (EFI_ERROR (Status)) { + goto error_handler; + } + + if (CmnPeriphBaseRdNode == NULL) { + Status = EFI_INVALID_PARAMETER; + goto error_handler; + } + + // Update the PERIPHBASE base address and length. + Status = AmlUpdateRdQWord ( + CmnPeriphBaseRdNode, + Cmn600Info->PeriphBaseAddress, + Cmn600Info->PeriphBaseAddressLength + ); + if (EFI_ERROR (Status)) { + goto error_handler; + } + + // Get the QWord node corresponding to the ROOTNODEBASE. + // It is the second Resource Data element in the BufferNode's + // variable list of arguments. + Status = AmlNameOpCrsGetNextRdNode ( + CmnPeriphBaseRdNode, + &CmnRootNodeBaseRdNode + ); + if (EFI_ERROR (Status)) { + goto error_handler; + } + + if (CmnRootNodeBaseRdNode == NULL) { + Status = EFI_INVALID_PARAMETER; + goto error_handler; + } + + // Update the ROOTNODEBASE base address and length. + Status = AmlUpdateRdQWord ( + CmnRootNodeBaseRdNode, + Cmn600Info->RootNodeBaseAddress, + ROOTNODEBASE_ADDRESS_LENGTH + ); + if (EFI_ERROR (Status)) { + goto error_handler; + } + + // Add the Interrupt node(s). + // Generate Resource Data node(s) corresponding to the "Interrupt ()" + // ASL function and add it at the last position in the list of + // Resource Data nodes. + for (Index = 0; Index < Cmn600Info->DtcCount; Index++) { + DtcInt = &Cmn600Info->DtcInterrupt[Index]; + Status = AmlCodeGenCrsAddRdInterrupt ( + NameOpCrsNode, + ((DtcInt->Flags & + EFI_ACPI_EXTENDED_INTERRUPT_FLAG_PRODUCER_CONSUMER_MASK) != 0), + ((DtcInt->Flags & + EFI_ACPI_EXTENDED_INTERRUPT_FLAG_MODE_MASK) != 0), + ((DtcInt->Flags & + EFI_ACPI_EXTENDED_INTERRUPT_FLAG_POLARITY_MASK) != 0), + ((DtcInt->Flags & + EFI_ACPI_EXTENDED_INTERRUPT_FLAG_SHARABLE_MASK) != 0), + (UINT32*)&DtcInt->Interrupt, + 1 + ); + if (EFI_ERROR (Status)) { + goto error_handler; + } + } // for + + // Fixup the CMN600 device name. + // This MUST be done at the end, otherwise AML paths won't be valid anymore. + // Get the CMN0 variable defined by the "Device ()" statement. + Status = AmlFindNode (RootNodeHandle, "\\_SB_.CMN0", &DeviceNode); + if (EFI_ERROR (Status)) { + goto error_handler; + } + + // Update the CMN600 Device's name. + Status = AmlDeviceOpUpdateName (DeviceNode, (CHAR8*)Name); + if (EFI_ERROR (Status)) { + goto error_handler; + } + + // Serialise the definition block + Status = AmlSerializeDefinitionBlock ( + RootNodeHandle, + Table + ); + if (EFI_ERROR (Status)) { + DEBUG (( + DEBUG_ERROR, + "ERROR: SSDT-CMN-600: Failed to Serialize SSDT Table Data." + " Status = %r\n", + Status + )); + } + +error_handler: + // Cleanup + if (RootNodeHandle != NULL) { + Status1 = AmlDeleteTree (RootNodeHandle); + if (EFI_ERROR (Status1)) { + DEBUG (( + DEBUG_ERROR, + "ERROR: SSDT-CMN-600: 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; +} + +/** Free any resources allocated for constructing the SSDT tables for CMN-600. + + @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 +FreeSsdtCmn600TableResourcesEx ( + 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-CMN-600: 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-CMN-600: Could not free SSDT table at index %d." + " Status = %r\n", + Index, + EFI_INVALID_PARAMETER + )); + return EFI_INVALID_PARAMETER; + } + } //for + + // Free the table list. + FreePool (*Table); + *Table = NULL; + return EFI_SUCCESS; +} + +/** Construct SSDT tables for describing CMN-600 meshes. + + 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 +BuildSsdtCmn600TableEx ( + 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; + UINT64 Index; + CM_ARM_CMN_600_INFO * Cmn600Info; + UINT32 Cmn600Count; + CHAR8 NewName[5]; + EFI_ACPI_DESCRIPTION_HEADER ** TableList; + + 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); + + *Table = NULL; + + // Get CMN-600 information. + Status = GetEArmObjCmn600Info ( + CfgMgrProtocol, + CM_NULL_TOKEN, + &Cmn600Info, + &Cmn600Count + ); + if (EFI_ERROR (Status)) { + DEBUG (( + DEBUG_ERROR, + "ERROR: SSDT-CMN-600: Failed to get the CMN-600 information." + " Status = %r\n", + Status + )); + return Status; + } + + if ((Cmn600Count == 0) || (Cmn600Count > MAX_CMN600_DEVICES_SUPPORTED)) { + DEBUG (( + DEBUG_ERROR, + "ERROR: SSDT-CMN-600: CMN600 peripheral count = %d." + " This must be between 1 to 16.\n", + Cmn600Count + )); + return EFI_INVALID_PARAMETER; + } + + // Validate the CMN-600 Info. + Status = ValidateCmn600Info (Cmn600Info, Cmn600Count); + if (EFI_ERROR (Status)) { + DEBUG (( + DEBUG_ERROR, + "ERROR: SSDT-CMN-600: Invalid CMN600 information. Status = %r\n", + Status + )); + return Status; + } + + // Allocate a table to store pointers to the SSDT tables. + TableList = (EFI_ACPI_DESCRIPTION_HEADER**) + AllocateZeroPool ( + (sizeof (EFI_ACPI_DESCRIPTION_HEADER*) * Cmn600Count) + ); + if (TableList == NULL) { + Status = EFI_OUT_OF_RESOURCES; + DEBUG (( + DEBUG_ERROR, + "ERROR: SSDT-CMN-600: Failed to allocate memory for Table List." + " Status = %r\n", + Status + )); + return Status; + } + + // Setup the table list early so that that appropriate cleanup + // can be done in case of failure. + *Table = TableList; + + NewName[0] = 'C'; + NewName[1] = 'M'; + NewName[2] = 'N'; + NewName[4] = '\0'; + for (Index = 0; Index < Cmn600Count; Index++) { + NewName[3] = AsciiFromHex ((UINT8)(Index)); + + // Build a SSDT table describing the CMN600 device. + Status = FixupCmn600Info ( + &Cmn600Info[Index], + NewName, + Index, + &TableList[Index] + ); + if (EFI_ERROR (Status)) { + DEBUG (( + DEBUG_ERROR, + "ERROR: SSDT-CMN-600: Failed to build associated SSDT table." + " Status = %r\n", + Status + )); + break; + } + + // Increment the table count here so that appropriate clean-up + // can be done in case of failure. + *TableCount += 1; + } // for + + // Note: Table list and CMN600 device count has been setup. The + // framework will invoke FreeSsdtCmn600TableResourcesEx() even + // on failure, so appropriate clean-up will be done. + return Status; +} + +/** This macro defines the Raw Generator revision. +*/ +#define SSDT_CMN_600_GENERATOR_REVISION CREATE_REVISION (1, 0) + +/** The interface for the Raw Table Generator. +*/ +STATIC +CONST +ACPI_TABLE_GENERATOR SsdtCmn600Generator = { + // Generator ID + CREATE_STD_ACPI_TABLE_GEN_ID (EStdAcpiTableIdSsdtCmn600), + // Generator Description + L"ACPI.STD.SSDT.CMN600.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_CMN_600_GENERATOR_REVISION, + // Build table function. Use the extended version instead. + NULL, + // Free table function. Use the extended version instead. + NULL, + // Build Table function + BuildSsdtCmn600TableEx, + // Free Resource function + FreeSsdtCmn600TableResourcesEx +}; + +/** 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 +AcpiSsdtCmn600LibConstructor ( + IN EFI_HANDLE ImageHandle, + IN EFI_SYSTEM_TABLE * SystemTable + ) +{ + EFI_STATUS Status; + + Status = RegisterAcpiTableGenerator (&SsdtCmn600Generator); + DEBUG (( + DEBUG_INFO, + "SSDT-CMN-600: 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 +AcpiSsdtCmn600LibDestructor ( + IN EFI_HANDLE ImageHandle, + IN EFI_SYSTEM_TABLE * SystemTable + ) +{ + EFI_STATUS Status; + + Status = DeregisterAcpiTableGenerator (&SsdtCmn600Generator); + DEBUG (( + DEBUG_INFO, + "SSDT-CMN-600: Deregister Generator. Status = %r\n", + Status + )); + ASSERT_EFI_ERROR (Status); + return Status; +} diff --git a/DynamicTablesPkg/Library/Acpi/Arm/AcpiSsdtCmn600LibArm/SsdtCmn600Generator.h b/DynamicTablesPkg/Library/Acpi/Arm/AcpiSsdtCmn600LibArm/SsdtCmn600Generator.h new file mode 100644 index 0000000000..ab03b72236 --- /dev/null +++ b/DynamicTablesPkg/Library/Acpi/Arm/AcpiSsdtCmn600LibArm/SsdtCmn600Generator.h @@ -0,0 +1,51 @@ +/** @file + + Copyright (c) 2020, Arm Limited. All rights reserved.
+ + SPDX-License-Identifier: BSD-2-Clause-Patent + + @par Glossary: + - Cm or CM - Configuration Manager + - Obj or OBJ - Object + - Std or STD - Standard + + @par Reference(s): + - Arm CoreLink CMN-600 Coherent Mesh Network Technical Reference Manual r3p0 + - Generic ACPI for Arm Components 1.0 Platform Design Document +**/ + +#ifndef SSDT_CMN600_GENERATOR_H_ +#define SSDT_CMN600_GENERATOR_H_ + +/** PeriphBase maximum address length is 256MB (0x10000000) + for a (X >= 4) || (Y >= 4) dimensions mesh. +*/ +#define PERIPHBASE_MAX_ADDRESS_LENGTH SIZE_256MB + +/** PeriphBase minimum address length is 64MB (0x04000000) + for a (X < 4) && (Y < 4) dimensions mesh. +*/ +#define PERIPHBASE_MIN_ADDRESS_LENGTH SIZE_64MB + +/** RootNodeBase address length is 16KB (0x00004000). +*/ +#define ROOTNODEBASE_ADDRESS_LENGTH SIZE_16KB + +/** Maximum number of CMN-600 Debug and Trace Logic Controllers (DTC). +*/ +#define MAX_DTC_COUNT 4 + +/** Starting value for the UID to represent the CMN600 devices. +*/ +#define CMN600_DEVICE_START_UID 0 + +/** Maximum CMN-600 devices supported by this generator. + This generator supports a maximum of 16 CMN-600 devices. + 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 serial port. + +*/ +#define MAX_CMN600_DEVICES_SUPPORTED 16 + +#endif // SSDT_CMN600_GENERATOR_H_ diff --git a/DynamicTablesPkg/Library/Acpi/Arm/AcpiSsdtCmn600LibArm/SsdtCmn600LibArm.inf b/DynamicTablesPkg/Library/Acpi/Arm/AcpiSsdtCmn600LibArm/SsdtCmn600LibArm.inf new file mode 100644 index 0000000000..821c0d531b --- /dev/null +++ b/DynamicTablesPkg/Library/Acpi/Arm/AcpiSsdtCmn600LibArm/SsdtCmn600LibArm.inf @@ -0,0 +1,34 @@ +## @file +# Ssdt CMN-600 Table Generator +# +# Copyright (c) 2020, Arm Limited. All rights reserved.
+# +# SPDX-License-Identifier: BSD-2-Clause-Patent +## + +[Defines] + INF_VERSION = 0x0001001B + BASE_NAME = SsdtCmn600LibArm + FILE_GUID = CEDB450D-8F0E-4ACC-8FB7-F72EC7D216A4 + VERSION_STRING = 1.0 + MODULE_TYPE = DXE_DRIVER + LIBRARY_CLASS = NULL|DXE_DRIVER + CONSTRUCTOR = AcpiSsdtCmn600LibConstructor + DESTRUCTOR = AcpiSsdtCmn600LibDestructor + +[Sources] + SsdtCmn600Generator.c + SsdtCmn600Generator.h + SsdtCmn600Template.asl + +[Packages] + MdePkg/MdePkg.dec + MdeModulePkg/MdeModulePkg.dec + EmbeddedPkg/EmbeddedPkg.dec + ArmPlatformPkg/ArmPlatformPkg.dec + DynamicTablesPkg/DynamicTablesPkg.dec + +[LibraryClasses] + AmlLib + BaseLib + diff --git a/DynamicTablesPkg/Library/Acpi/Arm/AcpiSsdtCmn600LibArm/SsdtCmn600Template.asl b/DynamicTablesPkg/Library/Acpi/Arm/AcpiSsdtCmn600LibArm/SsdtCmn600Template.asl new file mode 100644 index 0000000000..023a89e2ab --- /dev/null +++ b/DynamicTablesPkg/Library/Acpi/Arm/AcpiSsdtCmn600LibArm/SsdtCmn600Template.asl @@ -0,0 +1,81 @@ +/** @file + SSDT CMN-600 Template + + Copyright (c) 2020, Arm Limited. All rights reserved.
+ + SPDX-License-Identifier: BSD-2-Clause-Patent + + @par Reference(s): + - Arm CoreLink CMN-600 Coherent Mesh Network Technical Reference Manual r3p0 + - Generic ACPI for Arm Components 1.0 Platform Design Document + + @par Glossary: + - {template} - Data fixed up using AML Fixup APIs. + - {codegen} - Data generated using AML Codegen APIs. +**/ + +DefinitionBlock ("SsdtCmn600.aml", "SSDT", 2, "ARMLTD", "CMN-600", 1) { + Scope (_SB) { + // CMN-600 device object for a X * Y mesh, where (X >= 4) || (Y >= 4). + Device (CMN0) { // {template} + Name (_HID, "ARMHC600") + Name (_UID, 0x0) // {template} + + Name (_CRS, ResourceTemplate () { + // Descriptor for 256 MB of the CFG region at offset PERIPHBASE. + QWordMemory ( + ResourceConsumer, // bit 0 of general flags is 0. + PosDecode, + MinFixed, // Range is fixed. + MaxFixed, // Range is Fixed. + NonCacheable, + ReadWrite, + 0x00000000, // Granularity + 0xA0000000, // MinAddress // {template} + 0xAFFFFFFF, // MaxAddress // {template} + 0x00000000, // Translation + 0x10000000, // RangeLength // {template} + , // ResourceSourceIndex + , // ResourceSource + CFGR // DescriptorName + ) // QWordMemory + + // Descriptor for the root node. This is a 16 KB region at offset + // ROOTNODEBASE. In this example, ROOTNODEBASE starts at the 16 KB + // aligned offset of PERIPHBASE. + QWordMemory ( + ResourceConsumer, // bit 0 of general flags is 0. + PosDecode, + MinFixed, // Range is fixed. + MaxFixed, // Range is Fixed. + NonCacheable, + ReadWrite, + 0x00000000, // Granularity + 0xA0000000, // MinAddress // {template} + 0xAFFFFFFF, // MaxAddress // {template} + 0x00000000, // Translation + 0x10000000, // RangeLength // {template} + , // ResourceSourceIndex + , // ResourceSource + ROOT // DescriptorName + ) // QWordMemory + + // The Interrupt information is generated using AmlCodegen. + // Interrupt on PMU0 overflow, attached to DTC [0], with GSIV = . + // + // Interrupt ( // {codegen} + // ResourceConsumer, // ResourceUsage + // Level, // EdgeLevel + // ActiveHigh, // ActiveLevel + // Exclusive, // Shared + // , // ResourceSourceIndex + // , // ResourceSource + // // DescriptorName + // ) { + // 0xA5 // + // } // Interrupt + + }) // Name + } // Device + } // _SB +} // DefinitionBlock -- cgit v1.2.3