| /** @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 <IndustryStandard/Mpam.h> | |
| #include <Library/PrintLib.h> | |
| #include <Library/UefiLib.h> | |
| #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 | |
| ); | |
| } | |
| } | |
| } |