/** @file HMAT table parser Copyright (c) 2020, Arm Limited. SPDX-License-Identifier: BSD-2-Clause-Patent @par Reference(s): - ACPI 6.3 Specification - January 2019 @par Glossary: - MPDA - Memory Proximity Domain Attributes - SLLBI - System Locality Latency and Bandwidth Information - MSCI - Memory Side Cache Information - Dom - Domain **/ #include #include #include #include "AcpiParser.h" #include "AcpiView.h" // Maximum Memory Domain matrix print size. #define MAX_MEMORY_DOMAIN_TARGET_PRINT_MATRIX 10 // Local variables STATIC CONST UINT16 *HmatStructureType; STATIC CONST UINT32 *HmatStructureLength; STATIC CONST UINT32 *NumberInitiatorProximityDomain; STATIC CONST UINT32 *NumberTargetProximityDomain; STATIC CONST EFI_ACPI_6_4_HMAT_STRUCTURE_SYSTEM_LOCALITY_LATENCY_AND_BANDWIDTH_INFO_FLAGS * SllbiFlags; STATIC CONST UINT8 *SllbiDataType; STATIC CONST UINT16 *NumberSMBIOSHandles; STATIC ACPI_DESCRIPTION_HEADER_INFO AcpiHdrInfo; /** Names of System Locality Latency Bandwidth Information (SLLBI) data types **/ STATIC CONST CHAR16 *SllbiNames[] = { L"Access %sLatency%s", L"Read %sLatency%s", L"Write %sLatency%s", L"Access %sBandwidth%s", L"Read %sBandwidth%s", L"Write %sBandwidth%s" }; /** This function validates the Cache Attributes field. @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 ValidateCacheAttributes ( IN UINT8 *Ptr, IN VOID *Context ) { EFI_ACPI_6_4_HMAT_STRUCTURE_MEMORY_SIDE_CACHE_INFO_CACHE_ATTRIBUTES * Attributes; Attributes = (EFI_ACPI_6_4_HMAT_STRUCTURE_MEMORY_SIDE_CACHE_INFO_CACHE_ATTRIBUTES *)Ptr; if (Attributes->TotalCacheLevels > 0x3) { IncrementErrorCount (); Print ( L"\nERROR: Attributes bits [3:0] have invalid value: 0x%x", Attributes->TotalCacheLevels ); } if (Attributes->CacheLevel > 0x3) { IncrementErrorCount (); Print ( L"\nERROR: Attributes bits [7:4] have invalid value: 0x%x", Attributes->CacheLevel ); } if (Attributes->CacheAssociativity > 0x2) { IncrementErrorCount (); Print ( L"\nERROR: Attributes bits [11:8] have invalid value: 0x%x", Attributes->CacheAssociativity ); } if (Attributes->WritePolicy > 0x2) { IncrementErrorCount (); Print ( L"\nERROR: Attributes bits [15:12] have invalid value: 0x%x", Attributes->WritePolicy ); } } /** Dumps the cache attributes field @param [in] Format Optional format string for tracing the data. @param [in] Ptr Pointer to the start of the buffer. **/ STATIC VOID EFIAPI DumpCacheAttributes ( IN CONST CHAR16 *Format OPTIONAL, IN UINT8 *Ptr ) { EFI_ACPI_6_4_HMAT_STRUCTURE_MEMORY_SIDE_CACHE_INFO_CACHE_ATTRIBUTES * Attributes; Attributes = (EFI_ACPI_6_4_HMAT_STRUCTURE_MEMORY_SIDE_CACHE_INFO_CACHE_ATTRIBUTES *)Ptr; Print (L"\n"); PrintFieldName (4, L"Total Cache Levels"); Print (L"%d\n", Attributes->TotalCacheLevels); PrintFieldName (4, L"Cache Level"); Print (L"%d\n", Attributes->CacheLevel); PrintFieldName (4, L"Cache Associativity"); Print (L"%d\n", Attributes->CacheAssociativity); PrintFieldName (4, L"Write Policy"); Print (L"%d\n", Attributes->WritePolicy); PrintFieldName (4, L"Cache Line Size"); Print (L"%d\n", Attributes->CacheLineSize); } /** An ACPI_PARSER array describing the ACPI HMAT Table. */ STATIC CONST ACPI_PARSER HmatParser[] = { PARSE_ACPI_HEADER (&AcpiHdrInfo), { L"Reserved", 4,36, NULL, NULL, NULL, NULL, NULL } }; /** An ACPI_PARSER array describing the HMAT structure header. */ STATIC CONST ACPI_PARSER HmatStructureHeaderParser[] = { { L"Type", 2, 0, NULL, NULL, (VOID **)&HmatStructureType, NULL, NULL }, { L"Reserved", 2, 2, NULL, NULL, NULL, NULL, NULL }, { L"Length", 4, 4, NULL, NULL, (VOID **)&HmatStructureLength, NULL, NULL } }; /** An ACPI PARSER array describing the Memory Proximity Domain Attributes Structure - Type 0. */ STATIC CONST ACPI_PARSER MemProximityDomainAttributeParser[] = { { L"Type", 2, 0, L"0x%x", NULL, NULL, NULL, NULL }, { L"Reserved", 2, 2, L"0x%x", NULL, NULL, NULL, NULL }, { L"Length", 4, 4, L"%d", NULL, NULL, NULL, NULL }, { L"Flags", 2, 8, L"0x%x", NULL, NULL, NULL, NULL }, { L"Reserved", 2, 10, L"0x%x", NULL, NULL, NULL, NULL }, { L"Proximity Dom for initiator", 4, 12, L"0x%x", NULL, NULL, NULL, NULL }, { L"Proximity Dom for memory", 4, 16, L"0x%x", NULL, NULL, NULL, NULL }, { L"Reserved", 4, 20, L"0x%x", NULL, NULL, NULL, NULL }, { L"Reserved", 8, 24, L"0x%lx", NULL, NULL, NULL, NULL }, { L"Reserved", 8, 32, L"0x%lx", NULL, NULL, NULL, NULL } }; /** An ACPI PARSER array describing the System Locality Latency and Bandwidth Information Structure - Type 1. */ STATIC CONST ACPI_PARSER SllbiParser[] = { { L"Type", 2, 0, L"0x%x", NULL, NULL, NULL, NULL }, { L"Reserved", 2, 2, L"0x%x", NULL, NULL, NULL, NULL }, { L"Length", 4, 4, L"%d", NULL, NULL, NULL, NULL }, { L"Flags", 1, 8, L"0x%x", NULL, (VOID **)&SllbiFlags, NULL, NULL }, { L"Data type", 1, 9, L"0x%x", NULL, (VOID **)&SllbiDataType, NULL, NULL }, { L"Min Transfer Size", 1, 10, L"%d", NULL, NULL, NULL, NULL }, { L"Reserved", 1, 11, L"0x%x", NULL, NULL, NULL, NULL }, { L"Initiator Proximity Dom Count", 4, 12, L"%d", NULL, (VOID **)&NumberInitiatorProximityDomain, NULL, NULL }, { L"Target Proximity Dom Count", 4, 16, L"%d", NULL, (VOID **)&NumberTargetProximityDomain, NULL, NULL }, { L"Reserved", 4, 20, L"0x%x", NULL, NULL, NULL, NULL }, { L"Entry Base Unit", 8, 24, L"0x%lx", NULL, NULL, NULL, NULL } // initiator Proximity Domain list ... // target Proximity Domain list ... // Latency/Bandwidth matrix ... }; /** An ACPI PARSER array describing the Memory Side Cache Information Structure - Type 2. */ STATIC CONST ACPI_PARSER MemSideCacheInfoParser[] = { { L"Type", 2, 0, L"0x%x", NULL, NULL, NULL, NULL }, { L"Reserved", 2, 2, L"0x%x", NULL, NULL, NULL, NULL }, { L"Length", 4, 4, L"%d", NULL, NULL, NULL, NULL }, { L"Proximity Dom for memory", 4, 8, L"0x%x", NULL, NULL, NULL, NULL }, { L"Reserved", 4, 12, L"0x%x", NULL, NULL, NULL, NULL }, { L"Memory Side Cache Size", 8, 16, L"0x%lx", NULL, NULL, NULL, NULL }, { L"Cache Attributes", 4, 24, NULL, DumpCacheAttributes, NULL, ValidateCacheAttributes, NULL }, { L"Reserved", 2, 28, L"0x%x", NULL, NULL, NULL, NULL }, { L"SMBIOS Handle Count", 2, 30, L"%d", NULL, (VOID **)&NumberSMBIOSHandles, NULL, NULL } // SMBIOS handles List ... }; /** This function parses the Memory Proximity Domain Attributes Structure (Type 0). @param [in] Ptr Pointer to the start of the Memory Proximity Domain Attributes Structure data. @param [in] Length Length of the Memory Proximity Domain Attributes Structure. **/ STATIC VOID DumpMpda ( IN UINT8 *Ptr, IN UINT32 Length ) { ParseAcpi ( TRUE, 2, "Memory Proximity Domain Attributes Structure", Ptr, Length, PARSER_PARAMS (MemProximityDomainAttributeParser) ); } /** This function parses the System Locality Latency and Bandwidth Information Structure (Type 1). @param [in] Ptr Pointer to the start of the System Locality Latency and Bandwidth Information Structure data. @param [in] Length Length of the System Locality Latency and Bandwidth Information Structure. **/ STATIC VOID DumpSllbi ( IN UINT8 *Ptr, IN UINT32 Length ) { CONST UINT32 *InitiatorProximityDomainList; CONST UINT32 *TargetProximityDomainList; CONST UINT16 *LatencyBandwidthMatrix; UINT32 Offset; CHAR16 Buffer[OUTPUT_FIELD_COLUMN_WIDTH]; CHAR16 SecondBuffer[OUTPUT_FIELD_COLUMN_WIDTH]; UINT32 RequiredTableSize; UINT32 Index; UINT32 IndexInitiator; UINT32 IndexTarget; UINT32 TargetStartOffset; Offset = ParseAcpi ( TRUE, 2, "System Locality Latency and Bandwidth Information Structure", Ptr, Length, PARSER_PARAMS (SllbiParser) ); // Check if the values used to control the parsing logic have been // successfully read. if ((SllbiFlags == NULL) || (SllbiDataType == NULL) || (NumberInitiatorProximityDomain == NULL) || (NumberTargetProximityDomain == NULL)) { IncrementErrorCount (); Print ( L"ERROR: Insufficient remaining table buffer length to read the " \ L"SLLBI structure header. Length = %d.\n", Length ); return; } RequiredTableSize = (*NumberInitiatorProximityDomain * sizeof (UINT32)) + (*NumberTargetProximityDomain * sizeof (UINT32)) + (*NumberInitiatorProximityDomain * *NumberTargetProximityDomain * sizeof (UINT16)) + Offset; if (RequiredTableSize > Length) { IncrementErrorCount (); Print ( L"ERROR: Insufficient System Locality Latency and Bandwidth" \ L"Information Structure length. TableLength = %d. " \ L"RequiredTableLength = %d.\n", Length, RequiredTableSize ); return; } InitiatorProximityDomainList = (UINT32 *)(Ptr + Offset); TargetProximityDomainList = InitiatorProximityDomainList + *NumberInitiatorProximityDomain; LatencyBandwidthMatrix = (UINT16 *)(TargetProximityDomainList + *NumberTargetProximityDomain); // Display each element of the Initiator Proximity Domain list for (Index = 0; Index < *NumberInitiatorProximityDomain; Index++) { UnicodeSPrint ( Buffer, sizeof (Buffer), L"Initiator Proximity Dom [%d]", Index ); PrintFieldName (4, Buffer); Print ( L"0x%x\n", InitiatorProximityDomainList[Index] ); } // Display each element of the Target Proximity Domain list for (Index = 0; Index < *NumberTargetProximityDomain; Index++) { UnicodeSPrint ( Buffer, sizeof (Buffer), L"Target Proximity Dom [%d]", Index ); PrintFieldName (4, Buffer); Print ( L"0x%x\n", TargetProximityDomainList[Index] ); } // Create base name depending on Data Type in this Structure if (*SllbiDataType >= ARRAY_SIZE (SllbiNames)) { IncrementErrorCount (); Print (L"Error: Unkown Data Type. DataType = 0x%x.\n", *SllbiDataType); return; } StrCpyS (Buffer, sizeof (Buffer), SllbiNames[*SllbiDataType]); // Adjust base name depending on Memory Hierarchy in this Structure switch (SllbiFlags->MemoryHierarchy) { case 0: UnicodeSPrint ( SecondBuffer, sizeof (SecondBuffer), Buffer, L"", L"%s" ); break; case 1: case 2: case 3: UnicodeSPrint ( SecondBuffer, sizeof (SecondBuffer), Buffer, L"Hit ", L"%s" ); break; default: IncrementErrorCount (); Print ( L"Error: Invalid Memory Hierarchy. MemoryHierarchy = %d.\n", SllbiFlags->MemoryHierarchy ); return; } // switch if (*NumberTargetProximityDomain <= MAX_MEMORY_DOMAIN_TARGET_PRINT_MATRIX) { // Display the latency/bandwidth matrix as a matrix UnicodeSPrint ( Buffer, sizeof (Buffer), SecondBuffer, L"" ); PrintFieldName (4, Buffer); Print (L"\n Target : X-axis (Horizontal)"); Print (L"\n Initiator : Y-axis (Vertical)"); Print (L"\n |"); for (IndexTarget = 0; IndexTarget < *NumberTargetProximityDomain; IndexTarget++) { Print (L" %2d", IndexTarget); } Print (L"\n ---+"); for (IndexTarget = 0; IndexTarget < *NumberTargetProximityDomain; IndexTarget++) { Print (L"------"); } Print (L"\n"); TargetStartOffset = 0; for (IndexInitiator = 0; IndexInitiator < *NumberInitiatorProximityDomain; IndexInitiator++) { Print (L" %2d |", IndexInitiator); for (IndexTarget = 0; IndexTarget < *NumberTargetProximityDomain; IndexTarget++) { Print ( L" %5d", LatencyBandwidthMatrix[TargetStartOffset + IndexTarget] ); } // for Target Print (L"\n"); TargetStartOffset += (*NumberTargetProximityDomain); } // for Initiator Print (L"\n"); } else { // Display the latency/bandwidth matrix as a list UnicodeSPrint ( Buffer, sizeof (Buffer), SecondBuffer, L" [%d][%d]" ); TargetStartOffset = 0; for (IndexInitiator = 0; IndexInitiator < *NumberInitiatorProximityDomain; IndexInitiator++) { for (IndexTarget = 0; IndexTarget < *NumberTargetProximityDomain; IndexTarget++) { UnicodeSPrint ( SecondBuffer, sizeof (SecondBuffer), Buffer, IndexInitiator, IndexTarget ); PrintFieldName (4, SecondBuffer); Print ( L"%d\n", LatencyBandwidthMatrix[TargetStartOffset + IndexTarget] ); } // for Target TargetStartOffset += (*NumberTargetProximityDomain); } // for Initiator } } /** This function parses the Memory Side Cache Information Structure (Type 2). @param [in] Ptr Pointer to the start of the Memory Side Cache Information Structure data. @param [in] Length Length of the Memory Side Cache Information Structure. **/ STATIC VOID DumpMsci ( IN UINT8 *Ptr, IN UINT32 Length ) { CONST UINT16 *SMBIOSHandlesList; CHAR16 Buffer[OUTPUT_FIELD_COLUMN_WIDTH]; UINT32 Offset; UINT16 Index; Offset = ParseAcpi ( TRUE, 2, "Memory Side Cache Information Structure", Ptr, Length, PARSER_PARAMS (MemSideCacheInfoParser) ); // Check if the values used to control the parsing logic have been // successfully read. if (NumberSMBIOSHandles == NULL) { IncrementErrorCount (); Print ( L"ERROR: Insufficient remaining table buffer length to read the " \ L"MSCI structure header. Length = %d.\n", Length ); return; } if ((*NumberSMBIOSHandles * sizeof (UINT16)) > (Length - Offset)) { IncrementErrorCount (); Print ( L"ERROR: Invalid Number of SMBIOS Handles. SMBIOSHandlesCount = %d." \ L"RemainingBufferLength = %d.\n", *NumberSMBIOSHandles, Length - Offset ); return; } SMBIOSHandlesList = (UINT16 *)(Ptr + Offset); for (Index = 0; Index < *NumberSMBIOSHandles; Index++) { UnicodeSPrint ( Buffer, sizeof (Buffer), L"SMBIOS Handles [%d]", Index ); PrintFieldName (4, Buffer); Print ( L"0x%x\n", SMBIOSHandlesList[Index] ); } } /** This function parses the ACPI HMAT table. When trace is enabled this function parses the HMAT table and traces the ACPI table fields. This function parses the following HMAT structures: - Memory Proximity Domain Attributes Structure (Type 0) - System Locality Latency and Bandwidth Info Structure (Type 1) - Memory Side Cache Info structure (Type 2) 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 ParseAcpiHmat ( IN BOOLEAN Trace, IN UINT8 *Ptr, IN UINT32 AcpiTableLength, IN UINT8 AcpiTableRevision ) { UINT32 Offset; UINT8 *HmatStructurePtr; if (!Trace) { return; } Offset = ParseAcpi ( Trace, 0, "HMAT", Ptr, AcpiTableLength, PARSER_PARAMS (HmatParser) ); HmatStructurePtr = Ptr + Offset; while (Offset < AcpiTableLength) { // Parse HMAT Structure Header to obtain Type and Length. ParseAcpi ( FALSE, 0, NULL, HmatStructurePtr, AcpiTableLength - Offset, PARSER_PARAMS (HmatStructureHeaderParser) ); // Check if the values used to control the parsing logic have been // successfully read. if ((HmatStructureType == NULL) || (HmatStructureLength == NULL)) { IncrementErrorCount (); Print ( L"ERROR: Insufficient remaining table buffer length to read the " \ L"HMAT structure header. Length = %d.\n", AcpiTableLength - Offset ); return; } // Validate HMAT Structure length. if ((*HmatStructureLength == 0) || ((Offset + (*HmatStructureLength)) > AcpiTableLength)) { IncrementErrorCount (); Print ( L"ERROR: Invalid HMAT Structure length. " \ L"Length = %d. Offset = %d. AcpiTableLength = %d.\n", *HmatStructureLength, Offset, AcpiTableLength ); return; } switch (*HmatStructureType) { case EFI_ACPI_6_4_HMAT_TYPE_MEMORY_PROXIMITY_DOMAIN_ATTRIBUTES: DumpMpda ( HmatStructurePtr, *HmatStructureLength ); break; case EFI_ACPI_6_4_HMAT_TYPE_SYSTEM_LOCALITY_LATENCY_AND_BANDWIDTH_INFO: DumpSllbi ( HmatStructurePtr, *HmatStructureLength ); break; case EFI_ACPI_6_4_HMAT_TYPE_MEMORY_SIDE_CACHE_INFO: DumpMsci ( HmatStructurePtr, *HmatStructureLength ); break; default: IncrementErrorCount (); Print ( L"ERROR: Unknown HMAT structure:" L" Type = %d, Length = %d\n", *HmatStructureType, *HmatStructureLength ); break; } // switch HmatStructurePtr += *HmatStructureLength; Offset += *HmatStructureLength; } // while }