| /** @file | |
| AML Resource Data Parser. | |
| Copyright (c) 2019 - 2020, Arm Limited. All rights reserved.<BR> | |
| SPDX-License-Identifier: BSD-2-Clause-Patent | |
| @par Glossary: | |
| - Rd or RD - Resource Data | |
| - Rds or RDS - Resource Data Small | |
| - Rdl or RDL - Resource Data Large | |
| **/ | |
| #include <Parser/AmlResourceDataParser.h> | |
| #include <AmlCoreInterface.h> | |
| #include <AmlDbgPrint/AmlDbgPrint.h> | |
| #include <Tree/AmlNode.h> | |
| #include <Tree/AmlTree.h> | |
| /** Get the size of a resource data element using a stream. | |
| If the resource data element is of the large type, the Header | |
| is expected to be at least 3 bytes long. | |
| The use of a stream makes this function safer | |
| than the version without stream. | |
| @param [in] FStream Forward stream pointing to a resource data | |
| element. | |
| The stream must not be at its end. | |
| @return The size of the resource data element. | |
| Zero if error. | |
| **/ | |
| UINT32 | |
| EFIAPI | |
| AmlRdStreamGetRdSize ( | |
| IN CONST AML_STREAM *FStream | |
| ) | |
| { | |
| CONST AML_RD_HEADER *CurrRdElement; | |
| if (!IS_STREAM (FStream) || | |
| IS_END_OF_STREAM (FStream) || | |
| !IS_STREAM_FORWARD (FStream)) | |
| { | |
| ASSERT (0); | |
| return 0; | |
| } | |
| CurrRdElement = (CONST AML_RD_HEADER *)AmlStreamGetCurrPos (FStream); | |
| if (CurrRdElement == NULL) { | |
| ASSERT (0); | |
| return 0; | |
| } | |
| // If the resource data element is of the large type, check for overflow. | |
| if (AML_RD_IS_LARGE (CurrRdElement) && | |
| (AmlStreamGetFreeSpace (FStream) < | |
| sizeof (ACPI_LARGE_RESOURCE_HEADER))) | |
| { | |
| return 0; | |
| } | |
| return AmlRdGetSize (CurrRdElement); | |
| } | |
| /** Check the nesting of resource data elements that are dependent | |
| function descriptors. | |
| @param [in] FStream Forward stream pointing to a resource data | |
| element. The stream is not | |
| modified/progressing. | |
| The stream must not be at its end. | |
| @param [in, out] InFunctionDesc Pointer holding the nesting of the | |
| resource data buffer. | |
| InFunctionDesc holds TRUE if the resource | |
| data at the address of Buffer is currently | |
| in a dependent function descriptor list. | |
| @retval FALSE The Header being parsed is ending a function descriptor | |
| list when none started. This should not be possible for a | |
| resource data buffer. | |
| @retval TRUE Otherwise. | |
| **/ | |
| STATIC | |
| BOOLEAN | |
| EFIAPI | |
| AmlRdCheckFunctionDescNesting ( | |
| IN CONST AML_STREAM *FStream, | |
| IN OUT BOOLEAN *InFunctionDesc | |
| ) | |
| { | |
| CONST AML_RD_HEADER *CurrRdElement; | |
| if (!IS_STREAM (FStream) || | |
| IS_END_OF_STREAM (FStream) || | |
| (InFunctionDesc == NULL)) | |
| { | |
| ASSERT (0); | |
| return FALSE; | |
| } | |
| CurrRdElement = AmlStreamGetCurrPos (FStream); | |
| if (CurrRdElement == NULL) { | |
| ASSERT (0); | |
| return FALSE; | |
| } | |
| // Starting a dependent function descriptor. | |
| // It is possible to start one when one has already started. | |
| if (AmlRdCompareDescId ( | |
| CurrRdElement, | |
| AML_RD_BUILD_SMALL_DESC_ID ( | |
| ACPI_SMALL_START_DEPENDENT_DESCRIPTOR_NAME | |
| ) | |
| )) | |
| { | |
| *InFunctionDesc = TRUE; | |
| return TRUE; | |
| } | |
| // Ending a dependent function descriptor. | |
| if (AmlRdCompareDescId ( | |
| CurrRdElement, | |
| AML_RD_BUILD_SMALL_DESC_ID ( | |
| ACPI_SMALL_END_DEPENDENT_DESCRIPTOR_NAME | |
| ) | |
| )) | |
| { | |
| if (*InFunctionDesc) { | |
| *InFunctionDesc = FALSE; | |
| return TRUE; | |
| } | |
| // It should not be possible to end a dependent function descriptor | |
| // when none started. | |
| return FALSE; | |
| } | |
| return TRUE; | |
| } | |
| /** Check whether the input stream is pointing to a valid list | |
| of resource data elements. | |
| The check is based on the size of resource data elements. | |
| This means that a buffer can pass this check with non-existing descriptor Ids | |
| that have a correct size. | |
| A list of resource data elements can contain one unique resource data | |
| element, without an end tag resource data. This is the case for | |
| a FieldList. | |
| @param [in] FStream Forward stream ideally pointing to a resource | |
| data element. The stream is not | |
| modified/progressing. | |
| The stream must not be at its end. | |
| @retval TRUE The buffer is holding a valid list of resource data elements. | |
| @retval FALSE Otherwise. | |
| **/ | |
| BOOLEAN | |
| EFIAPI | |
| AmlRdIsResourceDataBuffer ( | |
| IN CONST AML_STREAM *FStream | |
| ) | |
| { | |
| EFI_STATUS Status; | |
| UINT32 FreeSpace; | |
| AML_STREAM SubStream; | |
| CONST AML_RD_HEADER *CurrRdElement; | |
| UINT32 CurrRdElementSize; | |
| BOOLEAN InFunctionDesc; | |
| if (!IS_STREAM (FStream) || | |
| IS_END_OF_STREAM (FStream) || | |
| !IS_STREAM_FORWARD (FStream)) | |
| { | |
| ASSERT (0); | |
| return FALSE; | |
| } | |
| // Create a sub-stream from the input stream to leave it untouched. | |
| Status = AmlStreamInitSubStream (FStream, &SubStream); | |
| if (EFI_ERROR (Status)) { | |
| ASSERT (0); | |
| return FALSE; | |
| } | |
| CurrRdElement = AmlStreamGetCurrPos (&SubStream); | |
| if (CurrRdElement == NULL) { | |
| ASSERT (0); | |
| return FALSE; | |
| } | |
| // The first element cannot be an end tag. | |
| if (AmlRdCompareDescId ( | |
| CurrRdElement, | |
| AML_RD_BUILD_SMALL_DESC_ID (ACPI_SMALL_END_TAG_DESCRIPTOR_NAME) | |
| )) | |
| { | |
| return FALSE; | |
| } | |
| InFunctionDesc = FALSE; | |
| while (TRUE) { | |
| FreeSpace = AmlStreamGetFreeSpace (&SubStream); | |
| CurrRdElement = AmlStreamGetCurrPos (&SubStream); | |
| CurrRdElementSize = AmlRdStreamGetRdSize (&SubStream); | |
| if ((FreeSpace == 0) || | |
| (CurrRdElement == NULL) || | |
| (CurrRdElementSize == 0)) | |
| { | |
| return FALSE; | |
| } | |
| if (!AmlRdCheckFunctionDescNesting (&SubStream, &InFunctionDesc)) { | |
| return FALSE; | |
| } | |
| if (CurrRdElementSize > FreeSpace) { | |
| return FALSE; | |
| } else if (CurrRdElementSize == FreeSpace) { | |
| return TRUE; | |
| } | |
| // @todo Might want to check the CRC when available. | |
| // An end tag resource data element must be the last element of the list. | |
| // Thus the function should have already returned. | |
| if (AmlRdCompareDescId ( | |
| CurrRdElement, | |
| AML_RD_BUILD_SMALL_DESC_ID (ACPI_SMALL_END_TAG_DESCRIPTOR_NAME) | |
| )) | |
| { | |
| return FALSE; | |
| } | |
| Status = AmlStreamProgress (&SubStream, CurrRdElementSize); | |
| if (EFI_ERROR (Status)) { | |
| ASSERT (0); | |
| return FALSE; | |
| } | |
| } // while | |
| return FALSE; | |
| } | |
| /** Parse a ResourceDataBuffer. | |
| For each resource data element, create a data node | |
| and add them to the variable list of arguments of the BufferNode. | |
| The input stream is expected to point to a valid list of resource data | |
| elements. A function is available to check it for the caller. | |
| @param [in] BufferNode Buffer node. | |
| @param [in] FStream Forward stream pointing to a resource data | |
| element. | |
| The stream must not be at its end. | |
| @retval EFI_SUCCESS The function completed successfully. | |
| @retval EFI_INVALID_PARAMETER Invalid parameter. | |
| @retval EFI_OUT_OF_RESOURCES Could not allocate memory. | |
| **/ | |
| EFI_STATUS | |
| EFIAPI | |
| AmlParseResourceData ( | |
| IN AML_OBJECT_NODE *BufferNode, | |
| IN AML_STREAM *FStream | |
| ) | |
| { | |
| EFI_STATUS Status; | |
| AML_DATA_NODE *NewNode; | |
| UINT32 FreeSpace; | |
| CONST AML_RD_HEADER *CurrRdElement; | |
| UINT32 CurrRdElementSize; | |
| // Check that BufferNode is an ObjectNode and has a ByteList. | |
| if (!AmlNodeHasAttribute (BufferNode, AML_HAS_BYTE_LIST) || | |
| !IS_STREAM (FStream) || | |
| IS_END_OF_STREAM (FStream) || | |
| !IS_STREAM_FORWARD (FStream)) | |
| { | |
| ASSERT (0); | |
| return EFI_INVALID_PARAMETER; | |
| } | |
| // Iterate through the resource data elements and create nodes. | |
| // We assume the Buffer has already been validated as a list of | |
| // resource data elements, so less checks are made. | |
| while (TRUE) { | |
| FreeSpace = AmlStreamGetFreeSpace (FStream); | |
| if (FreeSpace == 0) { | |
| break; | |
| } | |
| CurrRdElement = (CONST AML_RD_HEADER *)AmlStreamGetCurrPos (FStream); | |
| CurrRdElementSize = AmlRdStreamGetRdSize (FStream); | |
| Status = AmlCreateDataNode ( | |
| EAmlNodeDataTypeResourceData, | |
| (CONST UINT8 *)CurrRdElement, | |
| CurrRdElementSize, | |
| &NewNode | |
| ); | |
| if (EFI_ERROR (Status)) { | |
| ASSERT (0); | |
| return Status; | |
| } | |
| Status = AmlVarListAddTailInternal ( | |
| (AML_NODE_HEADER *)BufferNode, | |
| (AML_NODE_HEADER *)NewNode | |
| ); | |
| if (EFI_ERROR (Status)) { | |
| ASSERT (0); | |
| AmlDeleteTree ((AML_NODE_HEADER *)NewNode); | |
| return Status; | |
| } | |
| Status = AmlStreamProgress (FStream, CurrRdElementSize); | |
| if (EFI_ERROR (Status)) { | |
| ASSERT (0); | |
| return Status; | |
| } | |
| AMLDBG_DUMP_RAW (CurrRdElement, CurrRdElementSize); | |
| // Exit the loop when finding the resource data end tag. | |
| if (AmlRdCompareDescId ( | |
| CurrRdElement, | |
| AML_RD_BUILD_SMALL_DESC_ID (ACPI_SMALL_END_TAG_DESCRIPTOR_NAME) | |
| )) | |
| { | |
| if (FreeSpace != CurrRdElementSize) { | |
| ASSERT (0); | |
| return EFI_INVALID_PARAMETER; | |
| } | |
| break; | |
| } | |
| } // while | |
| return EFI_SUCCESS; | |
| } |