/** @file AML Resource Data Parser. Copyright (c) 2019 - 2020, Arm Limited. All rights reserved.
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 #include #include #include #include /** 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; }