mirror of https://github.com/acidanthera/audk.git
329 lines
9.6 KiB
C
329 lines
9.6 KiB
C
/** @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;
|
|
}
|