diff --git a/ShellPkg/Library/UefiShellAcpiViewCommandLib/AcpiParser.h b/ShellPkg/Library/UefiShellAcpiViewCommandLib/AcpiParser.h index 1baf615415..6427ea7d8a 100644 --- a/ShellPkg/Library/UefiShellAcpiViewCommandLib/AcpiParser.h +++ b/ShellPkg/Library/UefiShellAcpiViewCommandLib/AcpiParser.h @@ -942,6 +942,27 @@ ParseAcpiMcfg ( IN UINT8 AcpiTableRevision ); +/** + This function parses the ACPI MPAM table. + When trace is enabled this function parses the MPAM table and + traces the ACPI table fields. + + 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 +ParseAcpiMpam ( + IN BOOLEAN Trace, + IN UINT8 *Ptr, + IN UINT32 AcpiTableLength, + IN UINT8 AcpiTableRevision + ); + /** This function parses the ACPI PCCT table including its sub-structures of type 0 through 4. diff --git a/ShellPkg/Library/UefiShellAcpiViewCommandLib/Parsers/Mpam/MpamParser.c b/ShellPkg/Library/UefiShellAcpiViewCommandLib/Parsers/Mpam/MpamParser.c new file mode 100644 index 0000000000..acbb859fc3 --- /dev/null +++ b/ShellPkg/Library/UefiShellAcpiViewCommandLib/Parsers/Mpam/MpamParser.c @@ -0,0 +1,1241 @@ +/** @file + MPAM table parser + + Copyright (c) 2024, Arm Limited. All rights reserved. + SPDX-License-Identifier: BSD-2-Clause-Patent + + @par Specification Reference: + - [1] ACPI for Memory System Resource Partitioning and Monitoring 2.0 + (https://developer.arm.com/documentation/den0065/latest) + + @par Glossary: + - MPAM - Memory System Resource Partitioning And Monitoring + - MSC - Memory System Component + - PCC - Platform Communication Channel + - RIS - Resource Instance Selection + - SMMU - Arm System Memory Management Unit + **/ + +#include +#include +#include +#include "AcpiParser.h" +#include "AcpiView.h" +#include "AcpiViewConfig.h" + +// Local variables +STATIC CONST UINT8 *MscInterfaceType; +STATIC CONST UINT8 *ResourceLocatorType; +STATIC UINT32 MpamMscNodeStart; +STATIC CONST UINT16 *MpamMscNodeLength; +STATIC CONST UINT32 *NumberOfMscResources; +STATIC CONST UINT32 *NumberOfFunctionalDependencies; +STATIC CONST UINT32 *NumberOfInterconnectDescriptors; +STATIC CONST UINT64 *InterconnectTableOffset; +STATIC ACPI_DESCRIPTION_HEADER_INFO AcpiHdrInfo; + +// Array of locator type names. New types should be added keeping the order +// preserved as locator type is used to index into the array while parsing. +STATIC CONST CHAR16 *MpamMscLocatorTitles[] = { + L"Processor cache", + L"Memory", + L"SMMU", + L"Memory cache", + L"ACPI device", + L"Interconnect" +}; + +/** + When the length of the table is insufficient to be parsed, this function could + be used to display an appropriate error message. + + @param [in] ErrorMsg Error message string that has to be appended to the + main error log. This string could explain the reason + why a insufficient length error was encountered in + the first place. +**/ +STATIC +VOID +EFIAPI +MpamLengthError ( + IN CONST CHAR16 *ErrorMsg + ) +{ + IncrementErrorCount (); + Print (L"\nERROR : "); + Print (ErrorMsg); + Print ( + L"\nError : Insufficient MPAM MSC Node length. Table length : %u.\n", + *(AcpiHdrInfo.Length) + ); +} + +/** + This function validates reserved fields. Any reserved field within the MPAM + specification must be 0. + + @param [in] Ptr Pointer to the start of the field data. + @param [in] Length Length of the field. + @param [in] Context Pointer to context specific information. For this + particular function, context holds the size of the + reserved field that needs to be validated. +**/ +STATIC +VOID +EFIAPI +ValidateReserved ( + IN UINT8 *Ptr, + IN UINT32 Length, + IN VOID *Context + ) +{ + while (Length > 0) { + if (Ptr[Length-1] != 0) { + IncrementErrorCount (); + Print (L"\nERROR : Reserved field must be 0\n"); + break; + } + + Length--; + } +} + +/** + This function validates bit-length reserved fields. Any reserved field within + the MPAM specification must be 0. + + @param [in] Ptr Pointer to the start of the field data. + @param [in] Length Length of the field. + @param [in] Context Pointer to context specific information. For this + particular function, context holds the size of the + reserved field that needs to be validated. +**/ +STATIC +VOID +EFIAPI +ValidateReservedBits ( + IN UINT8 *Ptr, + IN UINT32 Length, + IN VOID *Context + ) +{ + UINT32 ByteLength; + + ByteLength = (Length + 7) >> 3; + ValidateReserved (Ptr, ByteLength, Context); +} + +/** + This function validates the MMIO size within the MSC node body for MPAM ACPI + table. MPAM ACPI specification states that the MMIO size for an MSC having PCC + type interface should be zero. + + @param [in] Ptr Pointer to the start of the field data. + @param [in] Length Length of the field. + @param [in] Context Pointer to context specific information. For this + function, context holds the parent/double pointer to a + variable holding the interface type. Make sure to call + the function accordingly. +**/ +STATIC +VOID +EFIAPI +ValidateMmioSize ( + IN UINT8 *Ptr, + IN UINT32 Length, + IN VOID *Context + ) +{ + UINT8 InterfaceType; + UINT32 MmioSize; + + InterfaceType = *MscInterfaceType; + + if (InterfaceType == EFI_ACPI_MPAM_INTERFACE_PCC) { + MmioSize = *((UINT32 *)Ptr); + + if (MmioSize != 0) { + IncrementErrorCount (); + Print ( + L"\nERROR: MMIO size must be 0 for PCC interface type. Size - %u\n", + MmioSize + ); + } + } +} + +/** + This function decodes and validates the link type for MPAM's interconnect + descriptor. Valid links are of NUMA and PROC type. + + @param [in] Ptr Pointer to the start of the field data. + @param [in] Length Length of the field. + @param [in] Context Pointer to context specific information. For this + function, context is ignored. +**/ +STATIC +VOID +EFIAPI +DecodeLinkType ( + IN UINT8 *Ptr, + IN UINT32 Length, + IN VOID *Context + ) +{ + UINT8 LinkType; + + LinkType = *Ptr; + + if (LinkType == EFI_ACPI_MPAM_LINK_TYPE_NUMA) { + Print ( + L" (NUMA)" + ); + } else if (LinkType == EFI_ACPI_MPAM_LINK_TYPE_PROC) { + Print ( + L" (PROC)" + ); + } else { + IncrementErrorCount (); + Print ( + L"\nERROR: Invalid link type - %u\n", + (UINT32)LinkType + ); + } +} + +/** + This function decodes the hardware ID field present within MPAM ACPI table. + The specification states that the hardware ID has to be set to zero if not + being used. + + @param [in] Ptr Pointer to the start of the field data. + @param [in] Length Length of the field. + @param [in] Context Pointer to context specific information. For this + function, context is ignored. +**/ +STATIC +VOID +EFIAPI +DecodeHardwareId ( + IN UINT8 *Ptr, + IN UINT32 Length, + IN VOID *Context + ) +{ + UINT64 HardwareId; + + HardwareId = *((UINT64 *)Ptr); + + if (HardwareId != 0) { + Print (L" ("); + Dump8Chars (NULL, Ptr, Length); + Print (L")"); + } +} + +/** + This function decodes and validates the interface type for MPAM. Valid + interfaces are of MMIO and PCC type. + + @param [in] Ptr Pointer to the start of the field data. + @param [in] Length Length of the field. + @param [in] Context Pointer to context specific information. For this + function, context is ignored. +**/ +STATIC +VOID +EFIAPI +DecodeInterfaceType ( + IN UINT8 *Ptr, + IN UINT32 Length, + IN VOID *Context + ) +{ + UINT8 InterfaceType; + + InterfaceType = *Ptr; + + if (InterfaceType == EFI_ACPI_MPAM_INTERFACE_MMIO) { + Print (L" (MMIO)"); + } else if (InterfaceType == EFI_ACPI_MPAM_INTERFACE_PCC) { + Print (L" (PCC)"); + } else { + IncrementErrorCount (); + Print ( + L"\nERROR: Invalid interface type - %u\n", + (UINT32)InterfaceType + ); + } +} + +/** + This function decodes the interrupt mode flag for MPAM. Interrupt mode could + either be "edge triggered" or "level triggered". + + @param [in] Ptr Pointer to the start of the field data. + @param [in] Length Length of the field. + @param [in] Context Pointer to context specific information. For this + function, context holds the parent/double pointer to a + variable holding the interrupt gsiv. Make sure to call + the function accordingly. +**/ +STATIC +VOID +EFIAPI +DecodeInterruptMode ( + IN UINT8 *Ptr, + IN UINT32 Length, + IN VOID *Context + ) +{ + UINT8 InterruptMode; + + InterruptMode = *Ptr; + + if (InterruptMode == EFI_ACPI_MPAM_INTERRUPT_LEVEL_TRIGGERED) { + Print (L" (Level triggered)"); + } else { + Print (L" (Edge triggered)"); + } +} + +/** + This function decodes the interrupt type flag for MPAM. Interrupt type could + be "wired interrupt". Other values are reserved at this point. + + @param [in] Ptr Pointer to the start of the field data. + @param [in] Length Length of the field. + @param [in] Context Pointer to context specific information. For this + function, context holds the parent/double pointer to a + variable holding the interrupt gsiv. Make sure to call + the function accordingly. +**/ +STATIC +VOID +EFIAPI +DecodeInterruptType ( + IN UINT8 *Ptr, + IN UINT32 Length, + IN VOID *Context + ) +{ + UINT8 InterruptType; + + InterruptType = *Ptr; + + if (InterruptType == EFI_ACPI_MPAM_INTERRUPT_WIRED) { + Print (L" (Wired interrupt)"); + } else { + IncrementWarningCount (); + Print (L" (Reserved value!)"); + } +} + +/** + This function decodes the interrupt affinity valid flag for MPAM. Interrupt + affinity could be either be valid or not. + + @param [in] Ptr Pointer to the start of the field data. + @param [in] Length Length of the field. + @param [in] Context Pointer to context specific information. For this + function, context holds the parent/double pointer to a + variable holding the interrupt gsiv. Make sure to call + the function accordingly. +**/ +STATIC +VOID +EFIAPI +DecodeInterruptAffinityValid ( + IN UINT8 *Ptr, + IN UINT32 Length, + IN VOID *Context + ) +{ + UINT8 InterruptAffinityValid; + + InterruptAffinityValid = *Ptr; + + if (InterruptAffinityValid != EFI_ACPI_MPAM_INTERRUPT_AFFINITY_VALID) { + Print (L" (Affinity not valid)"); + } else { + Print (L" (Affinity valid)"); + } +} + +/** + This function decodes the interrupt affinity type flag for MPAM. Interrupt + affinity type could either be "Processor affinity" or "Processor container + affinity" + + @param [in] Ptr Pointer to the start of the field data. + @param [in] Length Length of the field. + @param [in] Context Pointer to context specific information. For this + function, context holds the parent/double pointer to a + variable holding the interrupt gsiv. Make sure to call + the function accordingly. +**/ +STATIC +VOID +EFIAPI +DecodeInterruptAffinityType ( + IN UINT8 *Ptr, + IN UINT32 Length, + IN VOID *Context + ) +{ + UINT8 InterruptAffinityType; + + InterruptAffinityType = *Ptr; + + if (InterruptAffinityType == EFI_ACPI_MPAM_INTERRUPT_PROCESSOR_AFFINITY) { + Print (L" (Processor affinity)"); + } else { + Print (L" (Processor container affinity)"); + } +} + +/** + This function decodes the locator type for a particular MPAM MSC resource. + + @param [in] Ptr Pointer to the start of the field data. + @param [in] Length Length of the field. + @param [in] Context Pointer to context specific information. For this + function, context holds the parent/double pointer to a + variable holding the interrupt gsiv. Make sure to call + the function accordingly. +**/ +STATIC +VOID +EFIAPI +DecodeLocatorType ( + IN UINT8 *Ptr, + IN UINT32 Length, + IN VOID *Context + ) +{ + UINT8 LocatorType; + + LocatorType = *Ptr; + + if (LocatorType <= EFI_ACPI_MPAM_LOCATION_INTERCONNECT) { + Print (L" (%s)", MpamMscLocatorTitles[LocatorType]); + } else if (LocatorType == EFI_ACPI_MPAM_LOCATION_UNKNOWN) { + Print (L" (Unknown)"); + } else { + Print (L" (Reserved)"); + } +} + +/** + ACPI_PARSER array describing MPAM MSC interrupt flags. +**/ +STATIC CONST ACPI_PARSER MpamMscInterruptFlagParser[] = { + { L"Interrupt Mode", 1, 0, L"%u", NULL, NULL, + DecodeInterruptMode, NULL }, + { L"Interrupt Type", 2, 1, L"%u", NULL, NULL, + DecodeInterruptType, NULL }, + { L"Affinity Type", 1, 3, L"%u", NULL, NULL, + DecodeInterruptAffinityType, NULL }, + { L"Affinity Valid", 1, 4, L"%u", NULL, NULL, + DecodeInterruptAffinityValid, NULL }, + { L"Reserved", 27, 5, NULL, DumpReservedBits, NULL, + ValidateReservedBits, NULL } +}; + +/** + This function traces MPAM MSC Interrupt Flags. + If no format string is specified the Format must be NULL. + + @param [in] Format Optional format string for tracing the data. + @param [in] Ptr Pointer to the start of the buffer. + @param [in] Length Length of the field. +**/ +STATIC +VOID +EFIAPI +DumpMpamMscInterruptFlags ( + IN CONST CHAR16 *Format OPTIONAL, + IN UINT8 *Ptr, + IN UINT32 Length + ) +{ + Print (L"%u\n", *(UINT32 *)Ptr); + + ParseAcpiBitFields ( + TRUE, + 2, + NULL, + Ptr, + 4, + PARSER_PARAMS (MpamMscInterruptFlagParser) + ); +} + +/** + ACPI_PARSER array describing the MPAM MSC processor cache locator field. +**/ +STATIC CONST ACPI_PARSER MpamMscProcessorCacheLocatorParser[] = { + { L"Cache reference", 8, 0, L"%lu", NULL, NULL, NULL, NULL }, + { L"Reserved", 4, 8, NULL, DumpReserved, NULL, + ValidateReserved, NULL } +}; + +/** + ACPI_PARSER array describing the MPAM MSC memory locator field. +**/ +STATIC CONST ACPI_PARSER MpamMscMemoryLocatorParser[] = { + { L"Proximity domain", 8, 0, L"%lu", NULL, NULL, NULL, NULL }, + { L"Reserved", 4, 8, NULL, DumpReserved, NULL, + ValidateReserved, NULL } +}; + +/** + ACPI_PARSER array describing the MPAM MSC SMMU locator field. +**/ +STATIC CONST ACPI_PARSER MpamMscSMMULocatorParser[] = { + { L"SMMU interface", 8, 0, L"%lu", NULL, NULL, NULL, NULL }, + { L"Reserved", 4, 8, NULL, DumpReserved, NULL, + ValidateReserved, NULL } +}; + +/** + ACPI_PARSER array describing the MPAM MSC memory cache locator field. +**/ +STATIC CONST ACPI_PARSER MpamMscMemoryCacheLocatorParser[] = { + { L"Reserved", 7, 0, NULL, DumpReserved, NULL, + ValidateReserved, NULL }, + { L"Level", 1, 7, L"%u", NULL, NULL,NULL, NULL }, + { L"Reference", 4, 8, L"%u", NULL, NULL,NULL, NULL } +}; + +/** + ACPI_PARSER array describing the MPAM MSC ACPI device locator field. +**/ +STATIC CONST ACPI_PARSER MpamMscAcpiDeviceLocatorParser[] = { + { L"ACPI hardware ID", 8, 0, L"0x%lx", NULL, NULL, + DecodeHardwareId, NULL }, + { L"ACPI unique ID", 4, 8, L"%u", NULL, NULL,NULL,NULL } +}; + +/** + ACPI_PARSER array describing the MPAM MSC interconnect locator field. +**/ +STATIC CONST ACPI_PARSER MpamMscInterconnectLocatorParser[] = { + { L"Interconnect desc tbl offset", 8, 0, L"%lu", NULL, + (VOID **)&InterconnectTableOffset, NULL, NULL }, + { L"Reserved", 4, 8, NULL, DumpReserved, + NULL, ValidateReserved, NULL } +}; + +/** + ACPI_PARSER array describing the MPAM MSC generic resource locator field. +**/ +STATIC CONST ACPI_PARSER MpamMscGenericLocatorParser[] = { + { L"Descriptor1", 8, 0, L"%lu", NULL, NULL, NULL, NULL }, + { L"Descriptor2", 4, 8, L"%u", NULL, NULL, NULL, NULL } +}; + +/** + This function parses the locator field within the resource node for ACPI MPAM + table. The parsing is based on the locator type field. + + @param [in] Format Optional format string for tracing the data. + @param [in] Ptr Pointer to the start of the buffer. + @param [in] Length Length of the field. +**/ +STATIC +VOID +EFIAPI +ParseLocator ( + IN CONST CHAR16 *Format OPTIONAL, + IN UINT8 *Ptr, + IN UINT32 Length + ) +{ + Print (L"\n"); + switch (*ResourceLocatorType) { + case EFI_ACPI_MPAM_LOCATION_PROCESSOR_CACHE: + ParseAcpi ( + TRUE, + 2, + NULL, + Ptr, + 12, + PARSER_PARAMS (MpamMscProcessorCacheLocatorParser) + ); + break; + case EFI_ACPI_MPAM_LOCATION_MEMORY: + ParseAcpi ( + TRUE, + 2, + NULL, + Ptr, + 12, + PARSER_PARAMS (MpamMscMemoryLocatorParser) + ); + break; + case EFI_ACPI_MPAM_LOCATION_SMMU: + ParseAcpi ( + TRUE, + 2, + NULL, + Ptr, + 12, + PARSER_PARAMS (MpamMscSMMULocatorParser) + ); + break; + case EFI_ACPI_MPAM_LOCATION_MEMORY_CACHE: + ParseAcpi ( + TRUE, + 2, + NULL, + Ptr, + 12, + PARSER_PARAMS (MpamMscMemoryCacheLocatorParser) + ); + break; + case EFI_ACPI_MPAM_LOCATION_ACPI_DEVICE: + ParseAcpi ( + TRUE, + 2, + NULL, + Ptr, + 12, + PARSER_PARAMS (MpamMscAcpiDeviceLocatorParser) + ); + break; + case EFI_ACPI_MPAM_LOCATION_INTERCONNECT: + ParseAcpi ( + TRUE, + 2, + NULL, + Ptr, + 12, + PARSER_PARAMS (MpamMscInterconnectLocatorParser) + ); + break; + // For both UNKNOWN and RESERVED locator types, the locator is parsed using + // the generic locator parser as the spec does not define any format. + case EFI_ACPI_MPAM_LOCATION_UNKNOWN: + ParseAcpi ( + TRUE, + 2, + NULL, + Ptr, + 12, + PARSER_PARAMS (MpamMscGenericLocatorParser) + ); + break; + default: + Print (L"\nWARNING : Reserved locator type\n"); + ParseAcpi ( + TRUE, + 2, + NULL, + Ptr, + 12, + PARSER_PARAMS (MpamMscGenericLocatorParser) + ); + IncrementWarningCount (); + break; + } // switch +} + +/** + ACPI_PARSER array describing the Generic ACPI MPAM table header. +**/ +STATIC CONST ACPI_PARSER MpamParser[] = { + PARSE_ACPI_HEADER (&AcpiHdrInfo) +}; + +/** + ACPI_PARSER array describing the MPAM MSC node object. +**/ +STATIC CONST ACPI_PARSER MpamMscNodeParser[] = { + { L"Length", 2, 0, L"%u", NULL, + (VOID **)&MpamMscNodeLength, NULL, NULL }, + // Once Interface type is decoded, the address of interface type field is + // captured into InterfaceType pointer so that it could be used to check if + // MMIO Size field is set as per the specification. + { L"Interface type", 1, 2, L"0x%x", NULL, + (VOID **)&MscInterfaceType, DecodeInterfaceType, NULL }, + { L"Reserved", 1, 3, NULL, DumpReserved, + NULL, ValidateReserved, NULL }, + { L"Identifier", 4, 4, L"%u", NULL, + NULL, NULL, NULL }, + { L"Base address", 8, 8, L"0x%lx", NULL, + NULL, NULL, NULL }, + { L"MMIO Size", 4, 16, L"0x%x", NULL, + NULL, ValidateMmioSize, (VOID **)&MscInterfaceType }, + { L"Overflow interrupt", 4, 20, L"%u", NULL, + NULL, NULL, NULL }, + { L"Overflow interrupt flags", 4, 24, NULL, DumpMpamMscInterruptFlags, + NULL, NULL, NULL }, + { L"Reserved1", 4, 28, NULL, DumpReserved, + NULL, ValidateReserved, NULL }, + { L"Overflow interrupt affinity", 4, 32, L"0x%x", NULL, + NULL, NULL, NULL }, + { L"Error interrupt", 4, 36, L"%u", NULL, + NULL, NULL, NULL }, + { L"Error interrupt flags", 4, 40, NULL, DumpMpamMscInterruptFlags, + NULL, NULL, NULL }, + { L"Reserved2", 4, 44, NULL, DumpReserved, + NULL, ValidateReserved, NULL }, + { L"Error interrupt affinity", 4, 48, L"0x%x", NULL, + NULL, NULL, NULL }, + { L"MAX_NRDY_USEC", 4, 52, L"0x%x", NULL, + NULL, NULL, NULL }, + { L"Hardware ID of linked device", 8, 56, L"0x%lx", NULL, + NULL, DecodeHardwareId, NULL }, + { L"Instance ID of linked device", 4, 64, L"0x%x", NULL, + NULL, NULL, NULL }, + { L"Number of resource nodes", 4, 68, L"%u", NULL, + (VOID **)&NumberOfMscResources, NULL, NULL } +}; + +/** + ACPI_PARSER array describing the MPAM MSC resource. +**/ +STATIC CONST ACPI_PARSER MpamMscResourceParser[] = { + { L"Identifier", 4, 0, L"%u", NULL, + NULL, NULL, NULL }, + { L"RIS index", 1, 4, L"%u", NULL, + NULL, NULL, NULL }, + { L"Reserved1", 2, 5, NULL, DumpReserved, + NULL, ValidateReserved, NULL }, + { L"Locator type", 1, 7, L"0x%x", NULL, + (VOID **)&ResourceLocatorType, + DecodeLocatorType, NULL }, + { L"Locator", 12, 8, NULL, ParseLocator, + NULL, NULL, NULL }, + { L"Number of func dependencies", 4, 20, L"%u", NULL, + (VOID **)&NumberOfFunctionalDependencies, NULL, NULL } +}; + +/** + ACPI_PARSER array describing the MPAM MSC resource's functional dependencies. +**/ +STATIC CONST ACPI_PARSER MpamMscFunctionalDependencyParser[] = { + { L"Producer", 4, 0, L"0x%x", NULL, NULL, NULL, NULL }, + { L"Reserved", 4, 4, NULL, DumpReserved, + NULL, ValidateReserved, NULL }, +}; + +/** + ACPI_PARSER array describing the interconnect descriptor table associated with + the interconnect locator type. +**/ +STATIC CONST ACPI_PARSER MpamInterconnectDescriptorTableParser[] = { + { L"Signature", 16, 0, + L"%x%x%x%x-%x%x-%x%x-%x%x-%x%x%x%x%x%x", Dump16Chars, NULL, NULL, NULL }, + { L"Number of Interconnect desc", 4, 16,L"0x%x", NULL, + (VOID **)&NumberOfInterconnectDescriptors, NULL, NULL } +}; + +/** + ACPI_PARSER array describing the interconnect descriptor associated with the + interconnect locator type. +**/ +STATIC CONST ACPI_PARSER MpamInterconnectDescriptorParser[] = { + { L"Source ID", 4, 0, L"%u", NULL, NULL, NULL, NULL }, + { L"Destination ID", 4, 4, L"%u", NULL, NULL, NULL, NULL }, + { L"Link type", 1, 8, L"0x%x", NULL, + NULL, DecodeLinkType, NULL }, + { L"Reserved", 3, 9, NULL, DumpReserved, NULL, + ValidateReserved, NULL } +}; + +/** + PrintBlockTitle could be used to print the title of blocks that + appear more than once in the MPAM ACPI table. + + @param [in] Indent Number of spaces to add to the global table + indent. The global table indent is 0 by + default; however this value is updated on + entry to the ParseAcpi() by adding the indent + value provided to ParseAcpi() and restored + back on exit. Therefore the total indent in + the output is dependent on from where this + function is called. + @param [in] Title Title string to be used for the block. + @param [in] Index Index of the block. +**/ +STATIC +VOID +EFIAPI +PrintBlockTitle ( + IN UINT32 Indent, + IN CONST CHAR16 *Title, + IN CONST UINT32 Index + ) +{ + Print (L"\n"); + PrintFieldName (Indent, Title); + Print (L"%u\n\n", Index); +} + +/** + This function parses the interconnect descriptor(s) associated with + an interconnect type locator object. + + @param [in] Ptr Pointer to the start of the buffer. + @param [in] AcpiTableLength Length of the ACPI table. + @param [in, out] Offset Pointer to current offset within Ptr. + +Returns: + + Status + + EFI_SUCCESS MPAM MSC nodes were parsed properly. + EFI_BAD_BUFFER_SIZE The buffer pointer provided as input is not + long enough to be parsed correctly. +**/ +STATIC +EFI_STATUS +EFIAPI +ParseInterconnectDescriptors ( + IN UINT8 *CONST Ptr, + IN CONST UINT32 AcpiTableLength, + IN OUT UINT32 *CONST Offset + ) +{ + UINT32 InterconnectDescriptorIndex; + + InterconnectDescriptorIndex = 0; + + if (NumberOfInterconnectDescriptors == NULL) { + MpamLengthError (L"Number of interconnect descriptors not set!"); + return EFI_BAD_BUFFER_SIZE; + } + + while (InterconnectDescriptorIndex < *NumberOfInterconnectDescriptors) { + PrintBlockTitle ( + 6, + L"* Interconnect descriptor *", + InterconnectDescriptorIndex + ); + + // Parse interconnect descriptor + *Offset += ParseAcpi ( + TRUE, + 4, + NULL, + Ptr + *Offset, + AcpiTableLength - *Offset, + PARSER_PARAMS (MpamInterconnectDescriptorParser) + ); + + InterconnectDescriptorIndex++; + } + + return EFI_SUCCESS; +} + +/** + This function parses the interconnect descriptor table associated with an + interconnect type locator object. It also performs necessary validation to + make sure the interconnect descriptor is at a valid location. + + @param [in] Ptr Pointer to the start of the buffer. + @param [in] AcpiTableLength Length of the ACPI table. + @param [in] Offset Pointer to current offset within Ptr. + @param [in] InterconnectOffset Offset to the interconnect descriptor table. + +Returns: + + Status + + EFI_SUCCESS MPAM MSC nodes were parsed properly. + EFI_BAD_BUFFER_SIZE The buffer pointer provided as input is not + long enough to be parsed correctly. + EFI_INVALID_PARAMETER The Offset parameter encoded within the Ptr + buffer is not valid. +**/ +STATIC +EFI_STATUS +EFIAPI +ParseInterconnectDescriptorTable ( + IN UINT8 *CONST Ptr, + IN CONST UINT32 AcpiTableLength, + IN UINT32 Offset, + IN CONST UINT64 InterconnectOffset + ) +{ + EFI_STATUS Status; + + // Lower bound check + if (Offset > (MpamMscNodeStart + InterconnectOffset)) { + IncrementErrorCount (); + Print (L"\nERROR : Parsing Interconnect descriptor table failed!\n"); + Print ( + L"ERROR : Offset overlaps with other objects within the MSC. Offset %u.\n", + InterconnectOffset + ); + + return EFI_INVALID_PARAMETER; + } + + // Upper bound check + if (InterconnectOffset > (*MpamMscNodeLength)) { + IncrementErrorCount (); + Print (L"\nERROR : Parsing Interconnect descriptor table failed!\n"); + Print ( + L"ERROR : Offset falls outside MSC's space. Offset %u.\n", + InterconnectOffset + ); + + return EFI_INVALID_PARAMETER; + } + + // It is safe to cast InterconnectOffset to UINT32 as IntercconnectOffset can + // never exceed the MPAM table length which is at max 2 bytes. + Offset = MpamMscNodeStart + (UINT32)InterconnectOffset; + + Print (L"\n"); + PrintFieldName (6, L"* Interconnect desc table *"); + Print (L"\n\n"); + + // Parse interconnect descriptor table + Offset += ParseAcpi ( + TRUE, + 4, + NULL, + Ptr + Offset, + AcpiTableLength - Offset, + PARSER_PARAMS (MpamInterconnectDescriptorTableParser) + ); + + Status = ParseInterconnectDescriptors ( + Ptr, + AcpiTableLength, + &Offset + ); + + return Status; +} + +/** + This function parses all the MPAM functional dependency nodes within a + single resource node. + + @param [in] Ptr Pointer to the start of the buffer. + @param [in] AcpiTableLength Length of the ACPI table. + @param [in, out] Offset Pointer to current offset within Ptr. + +Returns: + + Status + + EFI_SUCCESS MPAM MSC nodes were parsed properly. + EFI_BAD_BUFFER_SIZE The buffer pointer provided as input is not + long enough to be parsed correctly. +**/ +STATIC +EFI_STATUS +EFIAPI +ParseMpamMscFunctionalDependencies ( + IN UINT8 *CONST Ptr, + IN CONST UINT32 AcpiTableLength, + IN OUT UINT32 *CONST Offset + ) +{ + UINT32 FunctionalDependencyIndex; + + FunctionalDependencyIndex = 0; + + if (NumberOfFunctionalDependencies == NULL) { + MpamLengthError (L"Number of functional dependencies not set!"); + return EFI_BAD_BUFFER_SIZE; + } + + while (FunctionalDependencyIndex < *NumberOfFunctionalDependencies) { + PrintBlockTitle ( + 6, + L"* Functional dependency *", + FunctionalDependencyIndex + ); + + // Parse functional dependency + *Offset += ParseAcpi ( + TRUE, + 4, + NULL, + Ptr + *Offset, + AcpiTableLength - *Offset, + PARSER_PARAMS (MpamMscFunctionalDependencyParser) + ); + + FunctionalDependencyIndex++; + } + + return EFI_SUCCESS; +} + +/** + This function parses all the MPAM resource nodes within a single MSC + node within the MPAM ACPI table. It also invokes helper functions to + validate and parse locators and functional dependency descriptors. + + @param [in] Ptr Pointer to the start of the buffer. + @param [in] AcpiTableLength Length of the ACPI table. + @param [in] Offset Pointer to current offset within Ptr. + +Returns: + + Status + + EFI_SUCCESS MPAM MSC nodes were parsed properly. + EFI_BAD_BUFFER_SIZE The buffer pointer provided as input is not + long enough to be parsed correctly. +**/ +STATIC +EFI_STATUS +EFIAPI +ParseMpamMscResources ( + IN UINT8 *CONST Ptr, + IN CONST UINT32 AcpiTableLength, + IN UINT32 Offset + ) +{ + EFI_STATUS Status; + UINT32 ResourceIndex; + + ResourceIndex = 0; + + if (NumberOfMscResources == NULL) { + MpamLengthError (L"Number of MSC resource not set!"); + return EFI_BAD_BUFFER_SIZE; + } + + while (ResourceIndex < *NumberOfMscResources) { + PrintBlockTitle ( + 4, + L"* Resource *", + ResourceIndex + ); + + // Parse MPAM MSC resources within the MSC body. + Offset += ParseAcpi ( + TRUE, + 2, + NULL, + Ptr + Offset, + AcpiTableLength - Offset, + PARSER_PARAMS (MpamMscResourceParser) + ); + + Status = ParseMpamMscFunctionalDependencies (Ptr, AcpiTableLength, &Offset); + if (Status != EFI_SUCCESS) { + return Status; + } + + // If the InterconnectTableOffset field has been set, proceed to parse the + // interconnect descriptor table. Please note that the interconnect + // descriptors are placed within the MSC node body in the resource specific + // region. However since its easier to map an interconnect descriptor to + // its corresponding resource, proceed to parse it along with its parent + // resource. This design choice is made to keep the trace view as intuitive + // as possible. + // + // +---------------------+ + // | MPAM ACPI Header | + // +---------------------+------- + // | MSC Node 0 Hdr | ^ + // | +-----------------+ | | + // | | Res Node 0 | | | + // | | +-------------+ | | | + // | | | Res Node Hdr| | | | + // | | +-------------+ | | | + // | | | Res Data | | | | + // | | | | | | | + // | | | +---------+ | | | | +---------------------------+ + // | | | | Locator | | | | ..|..| Interconnect locator desc | + // | | | | | | | | | | Descriptor Table Offset |--points-to->+ + // | | | | | | | | | | Reserved [4] | | + // | | | +---------+ | | | | +---------------------------+ | + // | | | |FnDep Cnt| | | | | | + // | | | +---------+ | | | | | + // | | | |FnDep 1 | | | | | | + // | | | +---------+ | | | Interconnect | + // | | | |FnDep 2 | | | | descriptor | + // | | | +---------+ | | | table | + // | | | |FnDep n | | | | offset | + // | | | +---------+ | | | value | + // | | +-------------+ | | | | + // | +-----------------+ | | | + // | ... | | | + // | +-----------------+ | | | + // | | Res Node N | | | | + // | | +-------------+ | | | | + // | | | Res Node Hdr| | | | | + // | | +-------------+ | | | | + // | | | Res Data | | | | | + // | | | | | | | | + // | | | +---------+ | | | | | + // | | | | Locator | | | | | | + // | | | | | | | | | | + // | | | | | | | | | | + // | | | +---------+ | | | | | + // | | | |FnDep Cnt| | | | | | + // | | | +---------+ | | | | | + // | | | |FnDep 1 | | | | | | + // | | | +---------+ | | | | | + // | | | |FnDep 2 | | | | | | + // | | | +---------+ | | | | | + // | | | |FnDep n | | | | | | + // | | | +---------+ | | | | | + // | | +-------------+ | | | | + // | +-----------------+ | | | + // \ Resource-specific / v | + // / data region. \<-----------------------------------------------+ + // \ / + // +---------------------+ + // | MSC Node 1 Hdr | + // | ... | + // +---------------------+ + if ( (*ResourceLocatorType == EFI_ACPI_MPAM_LOCATION_INTERCONNECT) + && (InterconnectTableOffset != NULL)) + { + Status = ParseInterconnectDescriptorTable ( + Ptr, + AcpiTableLength, + Offset, + *InterconnectTableOffset + ); + if (Status != EFI_SUCCESS) { + return Status; + } + } + + ResourceIndex++; + } + + return EFI_SUCCESS; +} + +/** + This function parses all the MPAM MSC nodes within the MPAM ACPI table. It + also invokes a helper function to detect and parse resource nodes that maybe + present. + + @param [in] Ptr Pointer to the start of the buffer. + @param [in] AcpiTableLength Length of the ACPI table. + @param [in, out] Offset Pointer to the current offset within Ptr. + +Returns: + + Status + + EFI_SUCCESS MPAM MSC nodes were parsed properly. + EFI_BAD_BUFFER_SIZE The buffer pointer provided as input is not + long enough to be parsed correctly. +**/ +STATIC +EFI_STATUS +EFIAPI +ParseMpamMscNodes ( + IN UINT8 *CONST Ptr, + IN CONST UINT32 AcpiTableLength, + IN OUT UINT32 *CONST Offset + ) +{ + EFI_STATUS Status; + UINT32 MscIndex; + + MscIndex = 0; + + while (*Offset < AcpiTableLength) { + MpamMscNodeStart = *Offset; + + PrintBlockTitle (2, L"* MSC *", MscIndex); + // Parse MPAM MSC node + *Offset += ParseAcpi ( + TRUE, + 0, + NULL, + Ptr + *Offset, + AcpiTableLength - *Offset, + PARSER_PARAMS (MpamMscNodeParser) + ); + + if (MpamMscNodeLength == NULL) { + MpamLengthError (L"MPAM MSC node length not set!"); + return EFI_BAD_BUFFER_SIZE; + } + + if (*MpamMscNodeLength < sizeof (EFI_ACPI_MPAM_MSC_NODE)) { + IncrementErrorCount (); + Print (L"\nERROR: MSC length should be at least the size of node body! "); + Print (L"MSC Length %u\n", *MpamMscNodeLength); + return EFI_BAD_BUFFER_SIZE; + } + + // Parse MPAM MSC resources within the MSC body + Status = ParseMpamMscResources (Ptr, AcpiTableLength, *Offset); + if (Status != EFI_SUCCESS) { + return Status; + } + + *Offset = MpamMscNodeStart + *MpamMscNodeLength; + MscIndex++; + } + + return EFI_SUCCESS; +} + +/** + This function parses the MPAM ACPI table's generic header. It also invokes a + sub routine that would help with parsing rest of the table. + + @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 +ParseAcpiMpam ( + IN BOOLEAN Trace, + IN UINT8 *Ptr, + IN UINT32 AcpiTableLength, + IN UINT8 AcpiTableRevision + ) +{ + EFI_STATUS Status; + UINT32 Offset; + + if (!Trace) { + return; + } + + // Parse generic table header + Offset = ParseAcpi ( + TRUE, + 0, + "MPAM", + Ptr, + AcpiTableLength, + PARSER_PARAMS (MpamParser) + ); + + Status = ParseMpamMscNodes ( + Ptr, + AcpiTableLength, + &Offset + ); + + if (Status == EFI_SUCCESS) { + // Check if the length of all MPAM MSCs with the header, matches with the + // ACPI table's length field. + if (*(AcpiHdrInfo.Length) != Offset) { + IncrementErrorCount (); + Print (L"\nERROR: Length mismatch! : "); + Print (L"MSC Length total != MPAM table length."); + Print ( + L"Table length : %u MSC total : %u\n", + *(AcpiHdrInfo.Length), + Offset + ); + } + } +} diff --git a/ShellPkg/Library/UefiShellAcpiViewCommandLib/UefiShellAcpiViewCommandLib.c b/ShellPkg/Library/UefiShellAcpiViewCommandLib/UefiShellAcpiViewCommandLib.c index 81c58da45e..4f4254ac40 100644 --- a/ShellPkg/Library/UefiShellAcpiViewCommandLib/UefiShellAcpiViewCommandLib.c +++ b/ShellPkg/Library/UefiShellAcpiViewCommandLib/UefiShellAcpiViewCommandLib.c @@ -2,7 +2,7 @@ Main file for 'acpiview' Shell command function. Copyright (c) 2022, NVIDIA CORPORATION. All rights reserved. - Copyright (c) 2016 - 2021, Arm Limited. All rights reserved.
+ Copyright (c) 2016 - 2023, Arm Limited. All rights reserved.
SPDX-License-Identifier: BSD-2-Clause-Patent **/ @@ -66,6 +66,7 @@ ACPI_TABLE_PARSER ParserList[] = { { EFI_ACPI_6_2_MULTIPLE_APIC_DESCRIPTION_TABLE_SIGNATURE, ParseAcpiMadt }, { EFI_ACPI_6_2_PCI_EXPRESS_MEMORY_MAPPED_CONFIGURATION_SPACE_BASE_ADDRESS_DESCRIPTION_TABLE_SIGNATURE, ParseAcpiMcfg }, + { EFI_ACPI_MEMORY_SYSTEM_RESOURCE_PARTITIONING_AND_MONITORING_TABLE_SIGNATURE, ParseAcpiMpam }, { EFI_ACPI_6_4_PLATFORM_COMMUNICATIONS_CHANNEL_TABLE_SIGNATURE, ParseAcpiPcct }, { EFI_ACPI_6_4_PROCESSOR_PROPERTIES_TOPOLOGY_TABLE_STRUCTURE_SIGNATURE, diff --git a/ShellPkg/Library/UefiShellAcpiViewCommandLib/UefiShellAcpiViewCommandLib.inf b/ShellPkg/Library/UefiShellAcpiViewCommandLib/UefiShellAcpiViewCommandLib.inf index 87998b210d..54dddd0ee2 100644 --- a/ShellPkg/Library/UefiShellAcpiViewCommandLib/UefiShellAcpiViewCommandLib.inf +++ b/ShellPkg/Library/UefiShellAcpiViewCommandLib/UefiShellAcpiViewCommandLib.inf @@ -45,6 +45,7 @@ Parsers/Madt/MadtParser.c Parsers/Madt/MadtParser.h Parsers/Mcfg/McfgParser.c + Parsers/Mpam/MpamParser.c Parsers/Pcct/PcctParser.c Parsers/Pcct/PcctParser.h Parsers/Pptt/PpttParser.c diff --git a/ShellPkg/Library/UefiShellAcpiViewCommandLib/UefiShellAcpiViewCommandLib.uni b/ShellPkg/Library/UefiShellAcpiViewCommandLib/UefiShellAcpiViewCommandLib.uni index e4a9dd5b40..4079a8e51e 100644 --- a/ShellPkg/Library/UefiShellAcpiViewCommandLib/UefiShellAcpiViewCommandLib.uni +++ b/ShellPkg/Library/UefiShellAcpiViewCommandLib/UefiShellAcpiViewCommandLib.uni @@ -1,6 +1,6 @@ // /** // -// Copyright (c) 2016 - 2020, Arm Limited. All rights reserved.
+// Copyright (c) 2016 - 2023, Arm Limited. All rights reserved.
// SPDX-License-Identifier: BSD-2-Clause-Patent // // Module Name: @@ -89,6 +89,7 @@ " HMAT - Heterogeneous Memory Attributes Table\r\n" " IORT - IO Remapping Table\r\n" " MCFG - Memory Mapped Config Space Base Address Description Table\r\n" +" MPAM - Memory System Resource Partitioning and Monitoring Table\r\n" " PPTT - Processor Properties Topology Table\r\n" " RSDP - Root System Description Pointer\r\n" " SLIT - System Locality Information Table\r\n"