/** @file AML Parser. Copyright (c) 2019 - 2020, Arm Limited. All rights reserved.
SPDX-License-Identifier: BSD-2-Clause-Patent **/ #include #include #include #include #include #include #include #include #include /* AML Tree -------- Each ASL Statement is represented in AML as and ObjectNode. Each ObjectNode has an Opcode and has up to six FixedArguments followed by a list of VariableArguments. (ObjectNode) \ |- [0][1][2][3][4][5] # Fixed Arguments |- {(VarArg1)->(VarArg2)->(VarArg3)->...N} # Variable Arguments A RootNode is a special type of Object Node that does not have an Opcode or Fixed Arguments. It only has a list of VariableArguments (RootNode) \ |- {(VarArg1)->(VarArg2)->(VarArg3)->...N} # Variable Arguments A DataNode consists of a data buffer. A FixedArgument or VariableArgument can be either an ObjectNode or a DataNode. Example: ASL code sample: Device (DEV0) { Name (VAR0, 0x6) } Tree generated from the ASL code: (RootNode) \ |- {(Device statement (ObjectNode))} # Variable Arg of the \ # RootNode | |- [0] - Device Name (DataNode)(="DEV0") # Fixed Arg0 of the | # Device() statement | |- {(Name statement (ObjectNode))} # Variable Arg of the \ # Device() statement | |- [0] - Name statement(DataNode)(="VAR0") # Fixed Arg0 of the | # Name() statement |- [1] - Value(DataNode)(=0x6) # Fixed Arg1 of the # Name() statement */ // Forward declaration. STATIC EFI_STATUS EFIAPI AmlParseStream ( IN AML_NODE_HEADER *Node, IN OUT AML_STREAM *FStream, IN OUT LIST_ENTRY *NameSpaceRefList ); /** Function pointer to parse an AML construct. The expected format of the AML construct is passed in the ExpectedFormat argument. The available formats are available in the AML_PARSE_FORMAT enum definition. An object node or a data node is created in the function, and returned through the OutNode parameter. This node should be attached after this function returns. @param [in] ParentNode Parent node to which the parsed AML construct will be attached. @param [in] ExpectedFormat Format of the AML construct to parse. @param [in, out] FStream Forward stream containing the AML bytecode to parse. The stream must not be at its end. @param [out] OutNode Pointer holding the node created from the parsed AML bytecode. @retval EFI_SUCCESS The function completed successfully. @retval EFI_BUFFER_TOO_SMALL No space left in the buffer. @retval EFI_INVALID_PARAMETER Invalid parameter. @retval EFI_OUT_OF_RESOURCES Could not allocate memory. **/ typedef EFI_STATUS (EFIAPI *AML_PARSE_FUNCTION)( IN CONST AML_NODE_HEADER *Node, IN AML_PARSE_FORMAT ExpectedFormat, IN OUT AML_STREAM *FStream, OUT AML_NODE_HEADER **OutNode ); /** Parse a UInt (where X=8, 16, 32 or 64). A data node is created and returned through the OutNode parameter. @param [in] ParentNode Parent node to which the parsed AML construct will be attached. @param [in] ExpectedFormat Format of the AML construct to parse. @param [in, out] FStream Forward stream containing the AML bytecode to parse. The stream must not be at its end. @param [out] OutNode Pointer holding the node created from the parsed AML bytecode. @retval EFI_SUCCESS The function completed successfully. @retval EFI_BUFFER_TOO_SMALL No space left in the buffer. @retval EFI_INVALID_PARAMETER Invalid parameter. @retval EFI_OUT_OF_RESOURCES Could not allocate memory. **/ STATIC EFI_STATUS EFIAPI AmlParseUIntX ( IN CONST AML_NODE_HEADER *ParentNode, IN AML_PARSE_FORMAT ExpectedFormat, IN OUT AML_STREAM *FStream, OUT AML_NODE_HEADER **OutNode ) { EFI_STATUS Status; UINT32 UIntXSize; if ((!IS_AML_ROOT_NODE (ParentNode) && !IS_AML_OBJECT_NODE (ParentNode)) || ((ExpectedFormat != EAmlUInt8) && (ExpectedFormat != EAmlUInt16) && (ExpectedFormat != EAmlUInt32) && (ExpectedFormat != EAmlUInt64)) || !IS_STREAM (FStream) || IS_END_OF_STREAM (FStream) || !IS_STREAM_FORWARD (FStream) || (OutNode == NULL)) { ASSERT (0); return EFI_INVALID_PARAMETER; } switch (ExpectedFormat) { case EAmlUInt8: UIntXSize = 1; break; case EAmlUInt16: UIntXSize = 2; break; case EAmlUInt32: UIntXSize = 4; break; case EAmlUInt64: UIntXSize = 8; break; default: ASSERT (0); return EFI_INVALID_PARAMETER; } Status = AmlCreateDataNode ( AmlTypeToNodeDataType (ExpectedFormat), AmlStreamGetCurrPos (FStream), UIntXSize, (AML_DATA_NODE **)OutNode ); if (EFI_ERROR (Status)) { ASSERT (0); return Status; } AMLDBG_DUMP_RAW (AmlStreamGetCurrPos (FStream), UIntXSize); // Move stream forward by the size of UIntX. Status = AmlStreamProgress (FStream, UIntXSize); if (EFI_ERROR (Status)) { AmlDeleteTree (*OutNode); ASSERT (0); } return Status; } /** Parse an AML NameString. A data node is created and returned through the OutNode parameter. @param [in] ParentNode Parent node to which the parsed AML construct will be attached. @param [in] ExpectedFormat Format of the AML construct to parse. @param [in, out] FStream Forward stream containing the AML bytecode to parse. The stream must not be at its end. @param [out] OutNode Pointer holding the node created from the parsed AML bytecode. @retval EFI_SUCCESS The function completed successfully. @retval EFI_BUFFER_TOO_SMALL No space left in the buffer. @retval EFI_INVALID_PARAMETER Invalid parameter. @retval EFI_OUT_OF_RESOURCES Could not allocate memory. **/ STATIC EFI_STATUS EFIAPI AmlParseNameString ( IN CONST AML_NODE_HEADER *ParentNode, IN AML_PARSE_FORMAT ExpectedFormat, IN OUT AML_STREAM *FStream, OUT AML_NODE_HEADER **OutNode ) { EFI_STATUS Status; CONST UINT8 *Buffer; CONST AML_BYTE_ENCODING *ByteEncoding; UINT32 StrSize; if ((!IS_AML_ROOT_NODE (ParentNode) && !IS_AML_OBJECT_NODE (ParentNode)) || (ExpectedFormat != EAmlName) || !IS_STREAM (FStream) || IS_END_OF_STREAM (FStream) || !IS_STREAM_FORWARD (FStream) || (OutNode == NULL)) { ASSERT (0); return EFI_INVALID_PARAMETER; } Buffer = (CONST UINT8 *)AmlStreamGetCurrPos (FStream); ByteEncoding = AmlGetByteEncoding (Buffer); if ((ByteEncoding == NULL) || ((ByteEncoding->Attribute & AML_IS_NAME_CHAR) == 0)) { ASSERT (0); return EFI_INVALID_PARAMETER; } // Parse the NameString. Status = AmlGetNameStringSize ((CONST CHAR8 *)Buffer, &StrSize); if ((EFI_ERROR (Status)) || (StrSize > AmlStreamGetFreeSpace (FStream))) { ASSERT (0); return EFI_INVALID_PARAMETER; } Status = AmlCreateDataNode ( EAmlNodeDataTypeNameString, Buffer, StrSize, (AML_DATA_NODE **)OutNode ); if (EFI_ERROR (Status)) { ASSERT (0); return Status; } AMLDBG_DUMP_RAW (AmlStreamGetCurrPos (FStream), StrSize); // Move the stream forward by StrSize. Status = AmlStreamProgress (FStream, StrSize); if (EFI_ERROR (Status)) { AmlDeleteTree (*OutNode); ASSERT (0); } return Status; } /** Parse an AML String. A data node is created and returned through the OutNode parameter. @param [in] ParentNode Parent node to which the parsed AML construct will be attached. @param [in] ExpectedFormat Format of the AML construct to parse. @param [in, out] FStream Forward stream containing the AML bytecode to parse. The stream must not be at its end. @param [out] OutNode Pointer holding the node created from the parsed AML bytecode. @retval EFI_SUCCESS The function completed successfully. @retval EFI_BUFFER_TOO_SMALL No space left in the buffer. @retval EFI_INVALID_PARAMETER Invalid parameter. @retval EFI_OUT_OF_RESOURCES Could not allocate memory. **/ STATIC EFI_STATUS EFIAPI AmlParseString ( IN CONST AML_NODE_HEADER *ParentNode, IN AML_PARSE_FORMAT ExpectedFormat, IN OUT AML_STREAM *FStream, OUT AML_NODE_HEADER **OutNode ) { EFI_STATUS Status; UINT32 StrSize; UINT8 Byte; CONST UINT8 *Buffer; if ((!IS_AML_ROOT_NODE (ParentNode) && !IS_AML_OBJECT_NODE (ParentNode)) || (ExpectedFormat != EAmlString) || !IS_STREAM (FStream) || IS_END_OF_STREAM (FStream) || !IS_STREAM_FORWARD (FStream) || (OutNode == NULL)) { ASSERT (0); return EFI_INVALID_PARAMETER; } Buffer = (CONST UINT8 *)AmlStreamGetCurrPos (FStream); StrSize = 0; // AML String is NULL terminated. do { // Reading the stream moves the stream forward aswell. Status = AmlStreamReadByte (FStream, &Byte); if (EFI_ERROR (Status)) { ASSERT (0); return Status; } StrSize++; } while (Byte != '\0'); AMLDBG_DUMP_RAW (Buffer, StrSize); Status = AmlCreateDataNode ( AmlTypeToNodeDataType (ExpectedFormat), Buffer, StrSize, (AML_DATA_NODE **)OutNode ); ASSERT_EFI_ERROR (Status); return Status; } /** Parse an AML object. An object can be resolved as an AML object with an OpCode, or a NameString. An object node or a data node is created and returned through the OutNode parameter. @param [in] ParentNode Parent node to which the parsed AML construct will be attached. @param [in] ExpectedFormat Format of the AML construct to parse. @param [in, out] FStream Forward stream containing the AML bytecode to parse. The stream must not be at its end. @param [out] OutNode Pointer holding the node created from the parsed AML bytecode. @retval EFI_SUCCESS The function completed successfully. @retval EFI_BUFFER_TOO_SMALL No space left in the buffer. @retval EFI_INVALID_PARAMETER Invalid parameter. @retval EFI_OUT_OF_RESOURCES Could not allocate memory. **/ STATIC EFI_STATUS EFIAPI AmlParseObject ( IN CONST AML_NODE_HEADER *ParentNode, IN AML_PARSE_FORMAT ExpectedFormat, IN OUT AML_STREAM *FStream, OUT AML_NODE_HEADER **OutNode ) { EFI_STATUS Status; UINT8 OpCodeSize; UINT32 PkgLength; UINT32 PkgOffset; UINT32 FreeSpace; CONST AML_BYTE_ENCODING *AmlByteEncoding; CONST UINT8 *Buffer; if ((!IS_AML_ROOT_NODE (ParentNode) && !IS_AML_OBJECT_NODE (ParentNode)) || (ExpectedFormat != EAmlObject) || !IS_STREAM (FStream) || IS_END_OF_STREAM (FStream) || !IS_STREAM_FORWARD (FStream) || (OutNode == NULL)) { ASSERT (0); return EFI_INVALID_PARAMETER; } PkgLength = 0; // 0. Get the AML Byte encoding. AmlByteEncoding = AmlGetByteEncoding (AmlStreamGetCurrPos (FStream)); if (AmlByteEncoding == NULL) { ASSERT (0); return EFI_INVALID_PARAMETER; } // 1. Check for NameString. // Indeed a NameString can be found when an AML object is expected. // e.g. VAR0 = 3 // VAR0 is assigned an object which is a UINT. // VAR1 = VAR2 // VAR2 is a NameString. // If this is a NameString, return. A NameString can be a variable, a // method invocation, etc. if ((AmlByteEncoding->Attribute & AML_IS_NAME_CHAR) != 0) { Status = AmlParseNameString ( ParentNode, EAmlName, FStream, OutNode ); if (EFI_ERROR (Status)) { ASSERT (0); } return Status; } // 2. Determine the OpCode size to move the stream forward. Buffer = (CONST UINT8 *)AmlStreamGetCurrPos (FStream); if (*Buffer == AML_EXT_OP) { OpCodeSize = 2; } else { OpCodeSize = 1; } Status = AmlStreamProgress (FStream, OpCodeSize); if (EFI_ERROR (Status)) { ASSERT (0); return Status; } // Print the opcode. AMLDBG_DUMP_RAW (Buffer, OpCodeSize); if (!IS_END_OF_STREAM (FStream)) { // 3. Parse the PkgLength field, if present. if ((AmlByteEncoding->Attribute & AML_HAS_PKG_LENGTH) != 0) { Buffer = (CONST UINT8 *)AmlStreamGetCurrPos (FStream); PkgOffset = AmlGetPkgLength (Buffer, &PkgLength); if (PkgOffset == 0) { ASSERT (0); return EFI_INVALID_PARAMETER; } // Print the package length. AMLDBG_DUMP_RAW (Buffer, PkgOffset); // Adjust the size of the stream if it is valid package length. FreeSpace = AmlStreamGetFreeSpace (FStream); if (FreeSpace > PkgLength) { // Reduce the stream size by (FreeSpace - PkgLength) bytes. AmlStreamReduceMaxBufferSize (FStream, FreeSpace - PkgLength); } else if (FreeSpace != PkgLength) { ASSERT (0); return EFI_INVALID_PARAMETER; } Status = AmlStreamProgress (FStream, PkgOffset); if (EFI_ERROR (Status)) { ASSERT (0); return Status; } } } else if ((AmlByteEncoding->Attribute & AML_HAS_PKG_LENGTH) != 0) { // The stream terminated unexpectedly. A PkgLen had to be parsed. ASSERT (0); return EFI_INVALID_PARAMETER; } // 4. Create an Object Node. Status = AmlCreateObjectNode ( AmlByteEncoding, PkgLength, (AML_OBJECT_NODE **)OutNode ); ASSERT_EFI_ERROR (Status); return Status; } /** Parse a FieldPkgLen. A FieldPkgLen can only be found in a field list, i.e. in a NamedField field element. The PkgLen is otherwise part of the object node structure. A data node is created and returned through the OutNode parameter. @param [in] ParentNode Parent node to which the parsed AML construct will be attached. @param [in] ExpectedFormat Format of the AML construct to parse. @param [in, out] FStream Forward stream containing the AML bytecode to parse. The stream must not be at its end. @param [out] OutNode Pointer holding the node created from the parsed AML bytecode. @retval EFI_SUCCESS The function completed successfully. @retval EFI_BUFFER_TOO_SMALL No space left in the buffer. @retval EFI_INVALID_PARAMETER Invalid parameter. @retval EFI_OUT_OF_RESOURCES Could not allocate memory. **/ STATIC EFI_STATUS EFIAPI AmlParseFieldPkgLen ( IN CONST AML_NODE_HEADER *ParentNode, IN AML_PARSE_FORMAT ExpectedFormat, IN OUT AML_STREAM *FStream, OUT AML_NODE_HEADER **OutNode ) { EFI_STATUS Status; EFI_STATUS Status1; CONST UINT8 *Buffer; UINT32 PkgOffset; UINT32 PkgLength; if (!AmlNodeHasAttribute ( (CONST AML_OBJECT_NODE *)ParentNode, AML_IS_FIELD_ELEMENT ) || (ExpectedFormat != EAmlFieldPkgLen) || !IS_STREAM (FStream) || IS_END_OF_STREAM (FStream) || !IS_STREAM_FORWARD (FStream) || (OutNode == NULL)) { ASSERT (0); return EFI_INVALID_PARAMETER; } Buffer = (CONST UINT8 *)AmlStreamGetCurrPos (FStream); PkgOffset = AmlGetPkgLength (Buffer, &PkgLength); if (PkgOffset == 0) { ASSERT (0); return EFI_INVALID_PARAMETER; } // Warning: Since, updating of field elements is not supported, store the // FieldPkgLength in a Data Node as a raw buffer. Status = AmlCreateDataNode ( AmlTypeToNodeDataType (ExpectedFormat), Buffer, PkgOffset, (AML_DATA_NODE **)OutNode ); if (EFI_ERROR (Status)) { ASSERT (0); return Status; } AMLDBG_DUMP_RAW (Buffer, PkgOffset); Status = AmlStreamProgress (FStream, PkgOffset); if (EFI_ERROR (Status)) { Status1 = AmlDeleteNode (*OutNode); ASSERT_EFI_ERROR (Status1); ASSERT (0); } return Status; } /** Array of functions pointers to parse the AML constructs. The AML Byte encoding tables in Aml.c describe the format of the AML statements. The AML_PARSE_FORMAT enum definition lists these constructs and the corresponding parsing functions. */ AML_PARSE_FUNCTION mParseType[EAmlParseFormatMax] = { NULL, // EAmlNone AmlParseUIntX, // EAmlUInt8 AmlParseUIntX, // EAmlUInt16 AmlParseUIntX, // EAmlUInt32 AmlParseUIntX, // EAmlUInt64 AmlParseObject, // EAmlObject AmlParseNameString, // EAmlName AmlParseString, // EAmlString AmlParseFieldPkgLen // EAmlFieldPkgLen }; /** Check whether the NameString stored in the data node is a method invocation. If so, create a method invocation node and return it. @param [in] ParentNode Node to which the parsed AML construct will be attached. @param [in] DataNode Data node containing a NameString, potentially being a method invocation. @param [in, out] NameSpaceRefList List of namespace reference nodes. @param [out] OutNode Pointer holding the method invocation node if the NameString contained in the data node is a method invocation. NULL otherwise. @retval EFI_SUCCESS The function completed successfully. @retval EFI_BUFFER_TOO_SMALL No space left in the buffer. @retval EFI_INVALID_PARAMETER Invalid parameter. @retval EFI_OUT_OF_RESOURCES Could not allocate memory. **/ STATIC EFI_STATUS EFIAPI AmlCheckAndParseMethodInvoc ( IN CONST AML_NODE_HEADER *ParentNode, IN AML_DATA_NODE *DataNode, IN OUT LIST_ENTRY *NameSpaceRefList, OUT AML_OBJECT_NODE **OutNode ) { EFI_STATUS Status; AML_NAMESPACE_REF_NODE *NameSpaceRefNode; AML_OBJECT_NODE *MethodInvocationNode; AML_STREAM FStream; if ((!IS_AML_ROOT_NODE (ParentNode) && !IS_AML_OBJECT_NODE (ParentNode)) || !IS_AML_DATA_NODE (DataNode) || (DataNode->DataType != EAmlNodeDataTypeNameString) || (NameSpaceRefList == NULL) || (OutNode == NULL)) { ASSERT (0); return EFI_INVALID_PARAMETER; } // Initialize a stream containing the NameString which is checked. Status = AmlStreamInit ( &FStream, DataNode->Buffer, DataNode->Size, EAmlStreamDirectionForward ); if (EFI_ERROR (Status)) { ASSERT (0); return Status; } // Check whether the NameString is a method invocation. NameSpaceRefNode = NULL; Status = AmlIsMethodInvocation ( ParentNode, &FStream, NameSpaceRefList, &NameSpaceRefNode ); if (EFI_ERROR (Status)) { ASSERT (0); return Status; } MethodInvocationNode = NULL; if (NameSpaceRefNode != NULL) { // A matching method definition has been found. // Create a method invocation node. Status = AmlCreateMethodInvocationNode ( NameSpaceRefNode, (AML_DATA_NODE *)DataNode, &MethodInvocationNode ); if (EFI_ERROR (Status)) { ASSERT (0); return Status; } } *OutNode = MethodInvocationNode; return EFI_SUCCESS; } /** Call the appropriate function to parse the AML construct in the stream. The ExpectedFormat parameter allows to choose the right parsing function. An object node or a data node is created according to format. @param [in] ParentNode Node to which the parsed AML construct will be attached. @param [in] ExpectedFormat Format of the AML construct to parse. @param [in, out] FStream Forward stream containing the AML bytecode to parse. The stream must not be at its end. @param [in, out] NameSpaceRefList List of namespace reference nodes. @param [out] OutNode Pointer holding the node created from the parsed AML bytecode. @retval EFI_SUCCESS The function completed successfully. @retval EFI_BUFFER_TOO_SMALL No space left in the buffer. @retval EFI_INVALID_PARAMETER Invalid parameter. @retval EFI_OUT_OF_RESOURCES Could not allocate memory. **/ STATIC EFI_STATUS EFIAPI AmlParseArgument ( IN CONST AML_NODE_HEADER *ParentNode, IN AML_PARSE_FORMAT ExpectedFormat, IN OUT AML_STREAM *FStream, IN OUT LIST_ENTRY *NameSpaceRefList, OUT AML_NODE_HEADER **OutNode ) { EFI_STATUS Status; AML_PARSE_FUNCTION ParsingFunction; AML_DATA_NODE *DataNode; AML_OBJECT_NODE *MethodInvocationNode; if ((!IS_AML_ROOT_NODE (ParentNode) && !IS_AML_OBJECT_NODE (ParentNode)) || (ExpectedFormat >= EAmlParseFormatMax) || !IS_STREAM (FStream) || IS_END_OF_STREAM (FStream) || !IS_STREAM_FORWARD (FStream) || (NameSpaceRefList == NULL) || (OutNode == NULL)) { ASSERT (0); return EFI_INVALID_PARAMETER; } ParsingFunction = mParseType[ExpectedFormat]; if (ParsingFunction == NULL) { ASSERT (0); return EFI_INVALID_PARAMETER; } // Note: The ParsingFunction moves the stream forward as it // consumes the AML bytecode Status = ParsingFunction ( ParentNode, ExpectedFormat, FStream, OutNode ); if (EFI_ERROR (Status)) { ASSERT (0); return Status; } // Check whether the parsed argument is a NameString when an object // is expected. In such case, it could be a method invocation. DataNode = (AML_DATA_NODE *)*OutNode; if (IS_AML_DATA_NODE (DataNode) && (DataNode->DataType == EAmlNodeDataTypeNameString) && (ExpectedFormat == EAmlObject)) { Status = AmlCheckAndParseMethodInvoc ( ParentNode, (AML_DATA_NODE *)*OutNode, NameSpaceRefList, &MethodInvocationNode ); if (EFI_ERROR (Status)) { ASSERT (0); return Status; } // A method invocation node has been created and the DataNode containing // the NameString has been attached to the MethodInvocationNode. // Replace the OutNode with the MethodInvocationNode. if (MethodInvocationNode != NULL) { *OutNode = (AML_NODE_HEADER *)MethodInvocationNode; } } return Status; } /** Parse the Bytelist in the stream. According to the content of the stream, create data node(s) and add them to the variable list of arguments. The byte list may be a list of resource data element or a simple byte list. @param [in] BufferNode Object node having a byte list. @param [in, out] FStream Forward stream containing the AML bytecode to parse. 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. **/ STATIC EFI_STATUS EFIAPI AmlParseByteList ( IN AML_OBJECT_NODE *BufferNode, IN OUT AML_STREAM *FStream ) { EFI_STATUS Status; AML_NODE_HEADER *NewNode; CONST UINT8 *Buffer; UINT32 BufferSize; // Check whether the node is an Object Node and has byte list. 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; } // The buffer contains a list of resource data elements. if (AmlRdIsResourceDataBuffer (FStream)) { // Parse the resource data elements and add them as data nodes. // AmlParseResourceData() moves the stream forward. Status = AmlParseResourceData (BufferNode, FStream); if (EFI_ERROR (Status)) { ASSERT (0); } } else { // The buffer doesn't contain a list of resource data elements. // Create a single node holding the whole buffer data. // CreateDataNode checks the Buffer and BufferSize values. Buffer = (CONST UINT8 *)AmlStreamGetCurrPos (FStream); BufferSize = AmlStreamGetFreeSpace (FStream); Status = AmlCreateDataNode ( EAmlNodeDataTypeRaw, Buffer, BufferSize, (AML_DATA_NODE **)&NewNode ); if (EFI_ERROR (Status)) { ASSERT (0); return Status; } Status = AmlVarListAddTailInternal ( (AML_NODE_HEADER *)BufferNode, NewNode ); if (EFI_ERROR (Status)) { ASSERT (0); AmlDeleteTree (NewNode); return Status; } AMLDBG_DUMP_RAW (Buffer, BufferSize); // Move the stream forward as we have consumed the Buffer. Status = AmlStreamProgress (FStream, BufferSize); if (EFI_ERROR (Status)) { ASSERT (0); } } return Status; } /** Parse the list of fixed arguments of the input ObjectNode. For each argument, create a node and add it to the fixed argument list of the Node. If a fixed argument has children, parse them. @param [in] ObjectNode Object node to parse the fixed arguments from. @param [in] FStream Forward stream containing the AML bytecode to parse. The stream must not be at its end. @param [in] NameSpaceRefList List of namespace reference nodes. @retval EFI_SUCCESS The function completed successfully. @retval EFI_BUFFER_TOO_SMALL No space left in the buffer. @retval EFI_INVALID_PARAMETER Invalid parameter. @retval EFI_OUT_OF_RESOURCES Could not allocate memory. **/ EFI_STATUS EFIAPI AmlParseFixedArguments ( IN AML_OBJECT_NODE *ObjectNode, IN AML_STREAM *FStream, IN LIST_ENTRY *NameSpaceRefList ) { EFI_STATUS Status; AML_NODE_HEADER *FixedArgNode; AML_STREAM FixedArgFStream; EAML_PARSE_INDEX TermIndex; EAML_PARSE_INDEX MaxIndex; CONST AML_PARSE_FORMAT *Format; // Fixed arguments of method invocations node are handled differently. if (!IS_AML_OBJECT_NODE (ObjectNode) || AmlNodeCompareOpCode (ObjectNode, AML_METHOD_INVOC_OP, 0) || !IS_STREAM (FStream) || IS_END_OF_STREAM (FStream) || !IS_STREAM_FORWARD (FStream) || (NameSpaceRefList == NULL)) { ASSERT (0); return EFI_INVALID_PARAMETER; } TermIndex = EAmlParseIndexTerm0; MaxIndex = (EAML_PARSE_INDEX)AmlGetFixedArgumentCount ( (AML_OBJECT_NODE *)ObjectNode ); if ((ObjectNode->AmlByteEncoding != NULL) && (ObjectNode->AmlByteEncoding->Format != NULL)) { Format = ObjectNode->AmlByteEncoding->Format; } else { ASSERT (0); return EFI_INVALID_PARAMETER; } // Parse all the FixedArgs. while ((TermIndex < MaxIndex) && !IS_END_OF_STREAM (FStream) && (Format[TermIndex] != EAmlNone)) { // Initialize a FixedArgStream to parse the current fixed argument. Status = AmlStreamInitSubStream (FStream, &FixedArgFStream); if (EFI_ERROR (Status)) { ASSERT (0); return Status; } // Parse the current fixed argument. Status = AmlParseArgument ( (CONST AML_NODE_HEADER *)ObjectNode, Format[TermIndex], &FixedArgFStream, NameSpaceRefList, &FixedArgNode ); if (EFI_ERROR (Status)) { ASSERT (0); return Status; } // Add the fixed argument to the parent node's fixed argument list. // FixedArgNode can be an object or data node. Status = AmlSetFixedArgument ( (AML_OBJECT_NODE *)ObjectNode, TermIndex, FixedArgNode ); if (EFI_ERROR (Status)) { ASSERT (0); // Delete the sub-tree if the insertion failed. // Otherwise its reference will be lost. // Use DeleteTree because if the argument was a method invocation, // multiple nodes have been created. AmlDeleteTree (FixedArgNode); return Status; } // Parse the AML bytecode of the FixedArgNode if this is an object node. if (IS_AML_OBJECT_NODE (FixedArgNode) && !IS_END_OF_STREAM (&FixedArgFStream)) { Status = AmlParseStream ( FixedArgNode, &FixedArgFStream, NameSpaceRefList ); if (EFI_ERROR (Status)) { ASSERT (0); return Status; } } // Move the stream forward as we have consumed the sub-stream. Status = AmlStreamProgress ( FStream, AmlStreamGetIndex (&FixedArgFStream) ); if (EFI_ERROR (Status)) { ASSERT (0); return Status; } TermIndex++; } // while return EFI_SUCCESS; } /** Parse the variable list of arguments of the input ObjectNode. For each variable argument, create a node and add it to the variable list of arguments of the Node. If a variable argument has children, parse them recursively. The arguments of method invocation nodes are added to the variable list of arguments of the method invocation node. It is necessary to first get the number of arguments to parse for this kind of node. A method invocation can have at most 7 fixed arguments. @param [in] Node Node to parse the variable arguments from. @param [in] FStream Forward stream containing the AML bytecode to parse. The stream must not be at its end. @param [in] NameSpaceRefList List of namespace reference nodes. @retval EFI_SUCCESS The function completed successfully. @retval EFI_BUFFER_TOO_SMALL No space left in the buffer. @retval EFI_INVALID_PARAMETER Invalid parameter. @retval EFI_OUT_OF_RESOURCES Could not allocate memory. **/ EFI_STATUS EFIAPI AmlParseVariableArguments ( IN AML_NODE_HEADER *Node, IN AML_STREAM *FStream, IN LIST_ENTRY *NameSpaceRefList ) { EFI_STATUS Status; BOOLEAN IsMethodInvocation; UINT8 MethodInvocationArgCount; AML_NODE_HEADER *VarArgNode; AML_STREAM VarArgFStream; if ((!AmlNodeHasAttribute ( (CONST AML_OBJECT_NODE *)Node, AML_HAS_CHILD_OBJ ) && !IS_AML_ROOT_NODE (Node)) || !IS_STREAM (FStream) || IS_END_OF_STREAM (FStream) || !IS_STREAM_FORWARD (FStream) || (NameSpaceRefList == NULL)) { ASSERT (0); return EFI_INVALID_PARAMETER; } Status = AmlGetMethodInvocationArgCount ( (CONST AML_OBJECT_NODE *)Node, &IsMethodInvocation, &MethodInvocationArgCount ); if (EFI_ERROR (Status)) { ASSERT (0); return Status; } // Parse variable arguments while the Stream is not empty. while (!IS_END_OF_STREAM (FStream)) { // If the number of variable arguments are counted, decrement the counter. if ((IsMethodInvocation) && (MethodInvocationArgCount-- == 0)) { return EFI_SUCCESS; } // Initialize a VarArgStream to parse the current variable argument. Status = AmlStreamInitSubStream (FStream, &VarArgFStream); if (EFI_ERROR (Status)) { ASSERT (0); return Status; } // Parse the current variable argument. Status = AmlParseArgument ( Node, EAmlObject, &VarArgFStream, NameSpaceRefList, &VarArgNode ); if (EFI_ERROR (Status)) { ASSERT (0); return Status; } // Add the variable argument to its parent variable list of arguments. // VarArgNode can be an object or data node. Status = AmlVarListAddTailInternal ( (AML_NODE_HEADER *)Node, VarArgNode ); if (EFI_ERROR (Status)) { ASSERT (0); // Delete the sub-tree if the insertion failed. // Otherwise its reference will be lost. // Use DeleteTree because if the argument was a method invocation, // multiple nodes have been created. AmlDeleteTree (VarArgNode); return Status; } // Parse the AML bytecode of the VarArgNode if this is an object node. if (IS_AML_OBJECT_NODE (VarArgNode) && (!IS_END_OF_STREAM (&VarArgFStream))) { Status = AmlParseStream (VarArgNode, &VarArgFStream, NameSpaceRefList); if (EFI_ERROR (Status)) { ASSERT (0); return Status; } } // Move the stream forward as we have consumed the sub-stream. Status = AmlStreamProgress ( FStream, AmlStreamGetIndex (&VarArgFStream) ); if (EFI_ERROR (Status)) { ASSERT (0); return Status; } } // while // If the number of variable arguments are counted, check all the // MethodInvocationArgCount have been parsed. if (IsMethodInvocation && (MethodInvocationArgCount != 0)) { ASSERT (0); return EFI_INVALID_PARAMETER; } return Status; } /** Parse the AML stream and populate the root node. @param [in] RootNode RootNode to which the children are added. @param [in, out] FStream Forward stream containing the AML bytecode to parse. The stream must not be at its end. @param [in, out] NameSpaceRefList List of namespace reference nodes. @retval EFI_SUCCESS The function completed successfully. @retval EFI_BUFFER_TOO_SMALL No space left in the buffer. @retval EFI_INVALID_PARAMETER Invalid parameter. @retval EFI_OUT_OF_RESOURCES Could not allocate memory. **/ STATIC EFI_STATUS EFIAPI AmlPopulateRootNode ( IN AML_ROOT_NODE *RootNode, IN OUT AML_STREAM *FStream, IN OUT LIST_ENTRY *NameSpaceRefList ) { EFI_STATUS Status; if (!IS_AML_ROOT_NODE (RootNode) || !IS_STREAM (FStream) || IS_END_OF_STREAM (FStream) || !IS_STREAM_FORWARD (FStream) || (NameSpaceRefList == NULL)) { ASSERT (0); return EFI_INVALID_PARAMETER; } // A Root Node only has variable arguments. Status = AmlParseVariableArguments ( (AML_NODE_HEADER *)RootNode, FStream, NameSpaceRefList ); ASSERT_EFI_ERROR (Status); return Status; } /** Parse the AML stream an populate the object node. @param [in] ObjectNode ObjectNode to which the children are added. @param [in, out] FStream Forward stream containing the AML bytecode to parse. The stream must not be at its end. @param [in, out] NameSpaceRefList List of namespace reference nodes. @retval EFI_SUCCESS The function completed successfully. @retval EFI_BUFFER_TOO_SMALL No space left in the buffer. @retval EFI_INVALID_PARAMETER Invalid parameter. @retval EFI_OUT_OF_RESOURCES Could not allocate memory. **/ STATIC EFI_STATUS EFIAPI AmlPopulateObjectNode ( IN AML_OBJECT_NODE *ObjectNode, IN OUT AML_STREAM *FStream, IN OUT LIST_ENTRY *NameSpaceRefList ) { EFI_STATUS Status; if (!IS_AML_OBJECT_NODE (ObjectNode) || !IS_STREAM (FStream) || IS_END_OF_STREAM (FStream) || !IS_STREAM_FORWARD (FStream) || (NameSpaceRefList == NULL)) { ASSERT (0); return EFI_INVALID_PARAMETER; } Status = EFI_SUCCESS; // Don't parse the fixed arguments of method invocation nodes. // The AML encoding for method invocations in the ACPI specification 6.3 is: // MethodInvocation := NameString TermArgList // Since the AML specification does not define an OpCode for method // invocation, this AML parser defines a pseudo opcode and redefines the // grammar for simplicity as: // MethodInvocation := MethodInvocationOp NameString ArgumentCount TermArgList // ArgumentCount := ByteData // Due to this difference, the MethodInvocationOp and the fixed argument // i.e. ArgumentCount is not available in the AML stream and need to be // handled differently. if (!AmlNodeCompareOpCode (ObjectNode, AML_METHOD_INVOC_OP, 0)) { // Parse the fixed list of arguments. Status = AmlParseFixedArguments ( ObjectNode, FStream, NameSpaceRefList ); if (EFI_ERROR (Status)) { ASSERT (0); return Status; } } // Save the association [node reference/pathname] in the NameSpaceRefList. // This allows to identify method invocations from other namespace // paths. Method invocation need to be parsed differently. if (AmlNodeHasAttribute ( (CONST AML_OBJECT_NODE *)ObjectNode, AML_IN_NAMESPACE )) { Status = AmlAddNameSpaceReference ( (CONST AML_OBJECT_NODE *)ObjectNode, NameSpaceRefList ); if (EFI_ERROR (Status)) { ASSERT (0); return Status; } } if (!IS_END_OF_STREAM (FStream)) { // Parse the variable list of arguments if present. if (AmlNodeHasAttribute (ObjectNode, AML_HAS_CHILD_OBJ)) { Status = AmlParseVariableArguments ( (AML_NODE_HEADER *)ObjectNode, FStream, NameSpaceRefList ); } else if (AmlNodeHasAttribute (ObjectNode, AML_HAS_BYTE_LIST)) { // Parse the byte list if present. Status = AmlParseByteList ( ObjectNode, FStream ); } else if (AmlNodeHasAttribute (ObjectNode, AML_HAS_FIELD_LIST)) { // Parse the field list if present. Status = AmlParseFieldList ( ObjectNode, FStream, NameSpaceRefList ); } // Check status and assert if (EFI_ERROR (Status)) { ASSERT (0); } } return Status; } /** Invoke the appropriate parsing functions based on the Node type. @param [in] Node Node from which the children are parsed. Must be a root node or an object node. @param [in] FStream Forward stream containing the AML bytecode to parse. The stream must not be at its end. @param [in] NameSpaceRefList List of namespace reference nodes. @retval EFI_SUCCESS The function completed successfully. @retval EFI_BUFFER_TOO_SMALL No space left in the buffer. @retval EFI_INVALID_PARAMETER Invalid parameter. @retval EFI_OUT_OF_RESOURCES Could not allocate memory. **/ STATIC EFI_STATUS EFIAPI AmlParseStream ( IN AML_NODE_HEADER *Node, IN AML_STREAM *FStream, IN LIST_ENTRY *NameSpaceRefList ) { EFI_STATUS Status; if (IS_AML_ROOT_NODE (Node)) { Status = AmlPopulateRootNode ( (AML_ROOT_NODE *)Node, FStream, NameSpaceRefList ); if (EFI_ERROR (Status)) { ASSERT (0); } } else if (IS_AML_OBJECT_NODE (Node)) { Status = AmlPopulateObjectNode ( (AML_OBJECT_NODE *)Node, FStream, NameSpaceRefList ); if (EFI_ERROR (Status)) { ASSERT (0); } } else { // Data node or other. ASSERT (0); Status = EFI_INVALID_PARAMETER; } return Status; } /** Parse the definition block. This function parses the whole AML blob. It starts with the ACPI DSDT/SSDT header and then parses the AML bytestream. A tree structure is returned via the RootPtr. The tree must be deleted with the AmlDeleteTree function. @param [in] DefinitionBlock Pointer to the definition block. @param [out] RootPtr Pointer to the root node of the tree. @retval EFI_SUCCESS The function completed successfully. @retval EFI_BUFFER_TOO_SMALL No space left in the buffer. @retval EFI_INVALID_PARAMETER Invalid parameter. @retval EFI_OUT_OF_RESOURCES Could not allocate memory. **/ EFI_STATUS EFIAPI AmlParseDefinitionBlock ( IN CONST EFI_ACPI_DESCRIPTION_HEADER *DefinitionBlock, OUT AML_ROOT_NODE **RootPtr ) { EFI_STATUS Status; EFI_STATUS Status1; AML_STREAM Stream; AML_ROOT_NODE *Root; LIST_ENTRY NameSpaceRefList; UINT8 *Buffer; UINT32 MaxBufferSize; if ((DefinitionBlock == NULL) || (RootPtr == NULL)) { ASSERT (0); return EFI_INVALID_PARAMETER; } Buffer = (UINT8 *)DefinitionBlock + sizeof (EFI_ACPI_DESCRIPTION_HEADER); if (DefinitionBlock->Length < sizeof (EFI_ACPI_DESCRIPTION_HEADER)) { ASSERT (0); return EFI_INVALID_PARAMETER; } MaxBufferSize = DefinitionBlock->Length - (UINT32)sizeof (EFI_ACPI_DESCRIPTION_HEADER); // Create a root node. Status = AmlCreateRootNode ( (EFI_ACPI_DESCRIPTION_HEADER *)DefinitionBlock, &Root ); if (EFI_ERROR (Status)) { ASSERT (0); return Status; } *RootPtr = Root; if (MaxBufferSize == 0) { return EFI_SUCCESS; } // Initialize a stream to parse the AML bytecode. Status = AmlStreamInit ( &Stream, Buffer, MaxBufferSize, EAmlStreamDirectionForward ); if (EFI_ERROR (Status)) { ASSERT (0); goto error_handler; } // Initialize the NameSpaceRefList, holding references to nodes declaring // a name in the AML namespace. InitializeListHead (&NameSpaceRefList); // Parse the whole AML blob. Status = AmlParseStream ( (AML_NODE_HEADER *)Root, &Stream, &NameSpaceRefList ); if (EFI_ERROR (Status)) { ASSERT (0); goto error_handler; } // Check the whole AML blob has been parsed. if (!IS_END_OF_STREAM (&Stream)) { ASSERT (0); Status = EFI_INVALID_PARAMETER; goto error_handler; } // Print the list of NameSpace reference nodes. // AmlDbgPrintNameSpaceRefList (&NameSpaceRefList); // Delete the NameSpaceRefList goto exit_handler; error_handler: if (Root != NULL) { AmlDeleteTree ((AML_NODE_HEADER *)Root); } exit_handler: Status1 = AmlDeleteNameSpaceRefList (&NameSpaceRefList); if (EFI_ERROR (Status1)) { ASSERT (0); if (!EFI_ERROR (Status)) { return Status1; } } return Status; }