/** @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;
}