/** @file IORT table parser Copyright (c) 2016 - 2020, ARM Limited. All rights reserved. SPDX-License-Identifier: BSD-2-Clause-Patent @par Reference(s): - IO Remapping Table, Platform Design Document, Revision D, March 2018 **/ #include #include #include #include "AcpiParser.h" #include "AcpiTableParser.h" #include "AcpiViewConfig.h" // Local variables STATIC ACPI_DESCRIPTION_HEADER_INFO AcpiHdrInfo; STATIC CONST UINT32 *IortNodeCount; STATIC CONST UINT32 *IortNodeOffset; STATIC CONST UINT8 *IortNodeType; STATIC CONST UINT16 *IortNodeLength; STATIC CONST UINT32 *IortIdMappingCount; STATIC CONST UINT32 *IortIdMappingOffset; STATIC CONST UINT32 *InterruptContextCount; STATIC CONST UINT32 *InterruptContextOffset; STATIC CONST UINT32 *PmuInterruptCount; STATIC CONST UINT32 *PmuInterruptOffset; STATIC CONST UINT32 *ItsCount; /** This function validates the ID Mapping array count for the ITS node. @param [in] Ptr Pointer to the start of the field data. @param [in] Context Pointer to context specific information e.g. this could be a pointer to the ACPI table header. **/ STATIC VOID EFIAPI ValidateItsIdMappingCount ( IN UINT8 *Ptr, IN VOID *Context ) { if (*(UINT32 *)Ptr != 0) { IncrementErrorCount (); Print (L"\nERROR: IORT ID Mapping count must be zero."); } } /** This function validates the ID Mapping array count for the Performance Monitoring Counter Group (PMCG) node. @param [in] Ptr Pointer to the start of the field data. @param [in] Context Pointer to context specific information e.g. this could be a pointer to the ACPI table header. **/ STATIC VOID EFIAPI ValidatePmcgIdMappingCount ( IN UINT8 *Ptr, IN VOID *Context ) { if (*(UINT32 *)Ptr > 1) { IncrementErrorCount (); Print (L"\nERROR: IORT ID Mapping count must not be greater than 1."); } } /** This function validates the ID Mapping array offset for the ITS node. @param [in] Ptr Pointer to the start of the field data. @param [in] Context Pointer to context specific information e.g. this could be a pointer to the ACPI table header. **/ STATIC VOID EFIAPI ValidateItsIdArrayReference ( IN UINT8 *Ptr, IN VOID *Context ) { if (*(UINT32 *)Ptr != 0) { IncrementErrorCount (); Print (L"\nERROR: IORT ID Mapping offset must be zero."); } } /** Helper Macro for populating the IORT Node header in the ACPI_PARSER array. @param [out] ValidateIdMappingCount Optional pointer to a function for validating the ID Mapping count. @param [out] ValidateIdArrayReference Optional pointer to a function for validating the ID Array reference. **/ #define PARSE_IORT_NODE_HEADER(ValidateIdMappingCount, \ ValidateIdArrayReference) \ { L"Type", 1, 0, L"%d", NULL, (VOID**)&IortNodeType, NULL, NULL }, \ { L"Length", 2, 1, L"%d", NULL, (VOID**)&IortNodeLength, NULL, NULL }, \ { L"Revision", 1, 3, L"%d", NULL, NULL, NULL, NULL }, \ { L"Reserved", 4, 4, L"0x%x", NULL, NULL, NULL, NULL }, \ { L"Number of ID mappings", 4, 8, L"%d", NULL, \ (VOID**)&IortIdMappingCount, ValidateIdMappingCount, NULL }, \ { L"Reference to ID Array", 4, 12, L"0x%x", NULL, \ (VOID**)&IortIdMappingOffset, ValidateIdArrayReference, NULL } /** An ACPI_PARSER array describing the ACPI IORT Table **/ STATIC CONST ACPI_PARSER IortParser[] = { PARSE_ACPI_HEADER (&AcpiHdrInfo), { L"Number of IORT Nodes", 4, 36, L"%d", NULL, (VOID **)&IortNodeCount, NULL, NULL }, { L"Offset to Array of IORT Nodes",4, 40, L"0x%x", NULL, (VOID **)&IortNodeOffset, NULL, NULL }, { L"Reserved", 4, 44, L"0x%x", NULL,NULL,NULL, NULL } }; /** An ACPI_PARSER array describing the IORT node header structure. **/ STATIC CONST ACPI_PARSER IortNodeHeaderParser[] = { PARSE_IORT_NODE_HEADER (NULL, NULL) }; /** An ACPI_PARSER array describing the IORT SMMUv1/2 node. **/ STATIC CONST ACPI_PARSER IortNodeSmmuV1V2Parser[] = { PARSE_IORT_NODE_HEADER (NULL, NULL), { L"Base Address", 8, 16, L"0x%lx", NULL, NULL, NULL, NULL }, { L"Span", 8, 24, L"0x%lx", NULL, NULL, NULL, NULL }, { L"Model", 4, 32, L"%d", NULL, NULL, NULL, NULL }, { L"Flags", 4, 36, L"0x%x", NULL, NULL, NULL, NULL }, { L"Reference to Global Interrupt Array",4, 40, L"0x%x", NULL, NULL, NULL, NULL }, { L"Number of context interrupts", 4, 44, L"%d", NULL, (VOID **)&InterruptContextCount, NULL, NULL }, { L"Reference to Context Interrupt Array",4, 48, L"0x%x", NULL, (VOID **)&InterruptContextOffset, NULL, NULL }, { L"Number of PMU Interrupts", 4, 52, L"%d", NULL, (VOID **)&PmuInterruptCount, NULL, NULL }, { L"Reference to PMU Interrupt Array",4, 56, L"0x%x", NULL, (VOID **)&PmuInterruptOffset, NULL, NULL }, // Interrupt Array { L"SMMU_NSgIrpt", 4, 60, L"0x%x", NULL, NULL, NULL, NULL }, { L"SMMU_NSgIrpt interrupt flags", 4, 64, L"0x%x", NULL, NULL, NULL, NULL }, { L"SMMU_NSgCfgIrpt", 4, 68, L"0x%x", NULL, NULL, NULL, NULL }, { L"SMMU_NSgCfgIrpt interrupt flags",4, 72, L"0x%x", NULL, NULL, NULL, NULL } }; /** An ACPI_PARSER array describing the SMMUv1/2 Node Interrupt Array. **/ STATIC CONST ACPI_PARSER InterruptArrayParser[] = { { L"Interrupt GSIV", 4, 0, L"0x%x", NULL, NULL, NULL, NULL }, { L"Flags", 4, 4, L"0x%x", NULL, NULL, NULL, NULL } }; /** An ACPI_PARSER array describing the IORT ID Mapping. **/ STATIC CONST ACPI_PARSER IortNodeIdMappingParser[] = { { L"Input base", 4, 0, L"0x%x", NULL, NULL, NULL, NULL }, { L"Number of IDs", 4, 4, L"0x%x", NULL, NULL, NULL, NULL }, { L"Output base", 4, 8, L"0x%x", NULL, NULL, NULL, NULL }, { L"Output reference", 4, 12, L"0x%x", NULL, NULL, NULL, NULL }, { L"Flags", 4, 16, L"0x%x", NULL, NULL, NULL, NULL } }; /** An ACPI_PARSER array describing the IORT SMMUv3 node. **/ STATIC CONST ACPI_PARSER IortNodeSmmuV3Parser[] = { PARSE_IORT_NODE_HEADER (NULL, NULL), { L"Base Address", 8, 16, L"0x%lx", NULL, NULL, NULL, NULL }, { L"Flags", 4, 24, L"0x%x", NULL, NULL, NULL, NULL }, { L"Reserved", 4, 28, L"0x%x", NULL, NULL, NULL, NULL }, { L"VATOS Address", 8, 32, L"0x%lx", NULL, NULL, NULL, NULL }, { L"Model", 4, 40, L"%d", NULL, NULL, NULL, NULL }, { L"Event", 4, 44, L"0x%x", NULL, NULL, NULL, NULL }, { L"PRI", 4, 48, L"0x%x", NULL, NULL, NULL, NULL }, { L"GERR", 4, 52, L"0x%x", NULL, NULL, NULL, NULL }, { L"Sync", 4, 56, L"0x%x", NULL, NULL, NULL, NULL }, { L"Proximity domain", 4, 60, L"0x%x", NULL, NULL, NULL, NULL }, { L"Device ID mapping index", 4, 64, L"%d", NULL, NULL, NULL, NULL } }; /** An ACPI_PARSER array describing the IORT ITS node. **/ STATIC CONST ACPI_PARSER IortNodeItsParser[] = { PARSE_IORT_NODE_HEADER ( ValidateItsIdMappingCount, ValidateItsIdArrayReference ), { L"Number of ITSs", 4,16, L"%d", NULL, (VOID **)&ItsCount, NULL } }; /** An ACPI_PARSER array describing the ITS ID. **/ STATIC CONST ACPI_PARSER ItsIdParser[] = { { L"GIC ITS Identifier", 4, 0, L"%d", NULL, NULL, NULL } }; /** An ACPI_PARSER array describing the IORT Names Component node. **/ STATIC CONST ACPI_PARSER IortNodeNamedComponentParser[] = { PARSE_IORT_NODE_HEADER (NULL, NULL), { L"Node Flags", 4, 16, L"%d", NULL, NULL, NULL, NULL }, { L"Memory access properties",8, 20, L"0x%lx", NULL, NULL, NULL, NULL }, { L"Device memory address size limit",1, 28, L"%d", NULL, NULL, NULL, NULL } }; /** An ACPI_PARSER array describing the IORT Root Complex node. **/ STATIC CONST ACPI_PARSER IortNodeRootComplexParser[] = { PARSE_IORT_NODE_HEADER (NULL, NULL), { L"Memory access properties",8, 16, L"0x%lx", NULL, NULL, NULL, NULL }, { L"ATS Attribute", 4, 24, L"0x%x", NULL, NULL, NULL, NULL }, { L"PCI Segment number", 4, 28, L"0x%x", NULL, NULL, NULL, NULL }, { L"Memory access size limit",1, 32, L"0x%x", NULL, NULL, NULL, NULL }, { L"Reserved", 3, 33, L"%x %x %x", Dump3Chars, NULL, NULL, NULL } }; /** An ACPI_PARSER array describing the IORT PMCG node. **/ STATIC CONST ACPI_PARSER IortNodePmcgParser[] = { PARSE_IORT_NODE_HEADER (ValidatePmcgIdMappingCount, NULL), { L"Page 0 Base Address", 8, 16, L"0x%lx", NULL, NULL, NULL, NULL }, { L"Overflow interrupt GSIV", 4, 24, L"0x%x", NULL, NULL, NULL, NULL }, { L"Node reference", 4, 28, L"0x%x", NULL, NULL, NULL, NULL }, { L"Page 1 Base Address", 8, 32, L"0x%lx", NULL, NULL, NULL, NULL } }; /** This function parses the IORT Node Id Mapping array. @param [in] Ptr Pointer to the start of the ID mapping array. @param [in] Length Length of the buffer. @param [in] MappingCount The ID Mapping count. **/ STATIC VOID DumpIortNodeIdMappings ( IN UINT8 *Ptr, IN UINT32 Length, IN UINT32 MappingCount ) { UINT32 Index; UINT32 Offset; CHAR8 Buffer[40]; // Used for AsciiName param of ParseAcpi Index = 0; Offset = 0; while ((Index < MappingCount) && (Offset < Length)) { AsciiSPrint ( Buffer, sizeof (Buffer), "ID Mapping [%d]", Index ); Offset += ParseAcpi ( TRUE, 4, Buffer, Ptr + Offset, Length - Offset, PARSER_PARAMS (IortNodeIdMappingParser) ); Index++; } } /** This function parses the IORT SMMUv1/2 node. @param [in] Ptr Pointer to the start of the buffer. @param [in] Length Length of the buffer. @param [in] MappingCount The ID Mapping count. @param [in] MappingOffset The offset of the ID Mapping array from the start of the IORT table. **/ STATIC VOID DumpIortNodeSmmuV1V2 ( IN UINT8 *Ptr, IN UINT16 Length, IN UINT32 MappingCount, IN UINT32 MappingOffset ) { UINT32 Index; UINT32 Offset; CHAR8 Buffer[50]; // Used for AsciiName param of ParseAcpi ParseAcpi ( TRUE, 2, "SMMUv1 or SMMUv2 Node", Ptr, Length, PARSER_PARAMS (IortNodeSmmuV1V2Parser) ); // Check if the values used to control the parsing logic have been // successfully read. if ((InterruptContextCount == NULL) || (InterruptContextOffset == NULL) || (PmuInterruptCount == NULL) || (PmuInterruptOffset == NULL)) { IncrementErrorCount (); Print ( L"ERROR: Insufficient SMMUv1/2 node length. Length = %d\n", Length ); return; } Offset = *InterruptContextOffset; Index = 0; while ((Index < *InterruptContextCount) && (Offset < Length)) { AsciiSPrint ( Buffer, sizeof (Buffer), "Context Interrupts Array [%d]", Index ); Offset += ParseAcpi ( TRUE, 4, Buffer, Ptr + Offset, Length - Offset, PARSER_PARAMS (InterruptArrayParser) ); Index++; } Offset = *PmuInterruptOffset; Index = 0; while ((Index < *PmuInterruptCount) && (Offset < Length)) { AsciiSPrint ( Buffer, sizeof (Buffer), "PMU Interrupts Array [%d]", Index ); Offset += ParseAcpi ( TRUE, 4, Buffer, Ptr + Offset, Length - Offset, PARSER_PARAMS (InterruptArrayParser) ); Index++; } DumpIortNodeIdMappings ( Ptr + MappingOffset, Length - MappingOffset, MappingCount ); } /** This function parses the IORT SMMUv3 node. @param [in] Ptr Pointer to the start of the buffer. @param [in] Length Length of the buffer. @param [in] MappingCount The ID Mapping count. @param [in] MappingOffset The offset of the ID Mapping array from the start of the IORT table. **/ STATIC VOID DumpIortNodeSmmuV3 ( IN UINT8 *Ptr, IN UINT16 Length, IN UINT32 MappingCount, IN UINT32 MappingOffset ) { ParseAcpi ( TRUE, 2, "SMMUV3 Node", Ptr, Length, PARSER_PARAMS (IortNodeSmmuV3Parser) ); DumpIortNodeIdMappings ( Ptr + MappingOffset, Length - MappingOffset, MappingCount ); } /** This function parses the IORT ITS node. @param [in] Ptr Pointer to the start of the buffer. @param [in] Length Length of the buffer. **/ STATIC VOID DumpIortNodeIts ( IN UINT8 *Ptr, IN UINT16 Length ) { UINT32 Offset; UINT32 Index; CHAR8 Buffer[80]; // Used for AsciiName param of ParseAcpi Offset = ParseAcpi ( TRUE, 2, "ITS Node", Ptr, Length, PARSER_PARAMS (IortNodeItsParser) ); // Check if the values used to control the parsing logic have been // successfully read. if (ItsCount == NULL) { IncrementErrorCount (); Print ( L"ERROR: Insufficient ITS group length. Length = %d.\n", Length ); return; } Index = 0; while ((Index < *ItsCount) && (Offset < Length)) { AsciiSPrint ( Buffer, sizeof (Buffer), "GIC ITS Identifier Array [%d]", Index ); Offset += ParseAcpi ( TRUE, 4, Buffer, Ptr + Offset, Length - Offset, PARSER_PARAMS (ItsIdParser) ); Index++; } // Note: ITS does not have the ID Mappings Array } /** This function parses the IORT Named Component node. @param [in] Ptr Pointer to the start of the buffer. @param [in] Length Length of the buffer. @param [in] MappingCount The ID Mapping count. @param [in] MappingOffset The offset of the ID Mapping array from the start of the IORT table. **/ STATIC VOID DumpIortNodeNamedComponent ( IN UINT8 *Ptr, IN UINT16 Length, IN UINT32 MappingCount, IN UINT32 MappingOffset ) { UINT32 Offset; Offset = ParseAcpi ( TRUE, 2, "Named Component Node", Ptr, Length, PARSER_PARAMS (IortNodeNamedComponentParser) ); // Estimate the Device Name length PrintFieldName (2, L"Device Object Name"); while ((*(Ptr + Offset) != 0) && (Offset < Length)) { Print (L"%c", *(Ptr + Offset)); Offset++; } Print (L"\n"); DumpIortNodeIdMappings ( Ptr + MappingOffset, Length - MappingOffset, MappingCount ); } /** This function parses the IORT Root Complex node. @param [in] Ptr Pointer to the start of the buffer. @param [in] Length Length of the buffer. @param [in] MappingCount The ID Mapping count. @param [in] MappingOffset The offset of the ID Mapping array from the start of the IORT table. **/ STATIC VOID DumpIortNodeRootComplex ( IN UINT8 *Ptr, IN UINT16 Length, IN UINT32 MappingCount, IN UINT32 MappingOffset ) { ParseAcpi ( TRUE, 2, "Root Complex Node", Ptr, Length, PARSER_PARAMS (IortNodeRootComplexParser) ); DumpIortNodeIdMappings ( Ptr + MappingOffset, Length - MappingOffset, MappingCount ); } /** This function parses the IORT PMCG node. @param [in] Ptr Pointer to the start of the buffer. @param [in] Length Length of the buffer. @param [in] MappingCount The ID Mapping count. @param [in] MappingOffset The offset of the ID Mapping array from the start of the IORT table. **/ STATIC VOID DumpIortNodePmcg ( IN UINT8 *Ptr, IN UINT16 Length, IN UINT32 MappingCount, IN UINT32 MappingOffset ) { ParseAcpi ( TRUE, 2, "PMCG Node", Ptr, Length, PARSER_PARAMS (IortNodePmcgParser) ); DumpIortNodeIdMappings ( Ptr + MappingOffset, Length - MappingOffset, MappingCount ); } /** This function parses the ACPI IORT table. When trace is enabled this function parses the IORT table and traces the ACPI fields. This function also parses the following nodes: - ITS Group - Named Component - Root Complex - SMMUv1/2 - SMMUv3 - PMCG This function also performs validation of the ACPI table fields. @param [in] Trace If TRUE, trace the ACPI fields. @param [in] Ptr Pointer to the start of the buffer. @param [in] AcpiTableLength Length of the ACPI table. @param [in] AcpiTableRevision Revision of the ACPI table. **/ VOID EFIAPI ParseAcpiIort ( IN BOOLEAN Trace, IN UINT8 *Ptr, IN UINT32 AcpiTableLength, IN UINT8 AcpiTableRevision ) { UINT32 Offset; UINT32 Index; UINT8 *NodePtr; if (!Trace) { return; } ParseAcpi ( TRUE, 0, "IORT", Ptr, AcpiTableLength, PARSER_PARAMS (IortParser) ); // Check if the values used to control the parsing logic have been // successfully read. if ((IortNodeCount == NULL) || (IortNodeOffset == NULL)) { IncrementErrorCount (); Print ( L"ERROR: Insufficient table length. AcpiTableLength = %d.\n", AcpiTableLength ); return; } Offset = *IortNodeOffset; NodePtr = Ptr + Offset; Index = 0; // Parse the specified number of IORT nodes or the IORT table buffer length. // Whichever is minimum. while ((Index++ < *IortNodeCount) && (Offset < AcpiTableLength)) { // Parse the IORT Node Header ParseAcpi ( FALSE, 0, "IORT Node Header", NodePtr, AcpiTableLength - Offset, PARSER_PARAMS (IortNodeHeaderParser) ); // Check if the values used to control the parsing logic have been // successfully read. if ((IortNodeType == NULL) || (IortNodeLength == NULL) || (IortIdMappingCount == NULL) || (IortIdMappingOffset == NULL)) { IncrementErrorCount (); Print ( L"ERROR: Insufficient remaining table buffer length to read the " \ L"IORT node header. Length = %d.\n", AcpiTableLength - Offset ); return; } // Validate IORT Node length if ((*IortNodeLength == 0) || ((Offset + (*IortNodeLength)) > AcpiTableLength)) { IncrementErrorCount (); Print ( L"ERROR: Invalid IORT Node length. " \ L"Length = %d. Offset = %d. AcpiTableLength = %d.\n", *IortNodeLength, Offset, AcpiTableLength ); return; } PrintFieldName (2, L"* Node Offset *"); Print (L"0x%x\n", Offset); switch (*IortNodeType) { case EFI_ACPI_IORT_TYPE_ITS_GROUP: DumpIortNodeIts ( NodePtr, *IortNodeLength ); break; case EFI_ACPI_IORT_TYPE_NAMED_COMP: DumpIortNodeNamedComponent ( NodePtr, *IortNodeLength, *IortIdMappingCount, *IortIdMappingOffset ); break; case EFI_ACPI_IORT_TYPE_ROOT_COMPLEX: DumpIortNodeRootComplex ( NodePtr, *IortNodeLength, *IortIdMappingCount, *IortIdMappingOffset ); break; case EFI_ACPI_IORT_TYPE_SMMUv1v2: DumpIortNodeSmmuV1V2 ( NodePtr, *IortNodeLength, *IortIdMappingCount, *IortIdMappingOffset ); break; case EFI_ACPI_IORT_TYPE_SMMUv3: DumpIortNodeSmmuV3 ( NodePtr, *IortNodeLength, *IortIdMappingCount, *IortIdMappingOffset ); break; case EFI_ACPI_IORT_TYPE_PMCG: DumpIortNodePmcg ( NodePtr, *IortNodeLength, *IortIdMappingCount, *IortIdMappingOffset ); break; default: IncrementErrorCount (); Print (L"ERROR: Unsupported IORT Node type = %d\n", *IortNodeType); } // switch NodePtr += (*IortNodeLength); Offset += (*IortNodeLength); } // while }