/** @file AML Node Interface. Copyright (c) 2019 - 2020, Arm Limited. All rights reserved.
SPDX-License-Identifier: BSD-2-Clause-Patent **/ #include #include #include #include #include #include #include /** Returns the tree node type (Root/Object/Data). @param [in] Node Pointer to a Node. @return The node type. EAmlNodeUnknown if invalid parameter. **/ EAML_NODE_TYPE EFIAPI AmlGetNodeType ( IN AML_NODE_HEADER *Node ) { if (!IS_AML_NODE_VALID (Node)) { ASSERT (0); return EAmlNodeUnknown; } return Node->NodeType; } /** Get the RootNode information. The Node must be a root node. @param [in] RootNode Pointer to a root node. @param [out] SdtHeaderBuffer Buffer to copy the ACPI DSDT/SSDT header to. @retval EFI_SUCCESS The function completed successfully. @retval EFI_INVALID_PARAMETER Invalid parameter. **/ EFI_STATUS EFIAPI AmlGetRootNodeInfo ( IN AML_ROOT_NODE *RootNode, OUT EFI_ACPI_DESCRIPTION_HEADER *SdtHeaderBuffer ) { if (!IS_AML_ROOT_NODE (RootNode) || (SdtHeaderBuffer == NULL)) { ASSERT (0); return EFI_INVALID_PARAMETER; } CopyMem ( SdtHeaderBuffer, RootNode->SdtHeader, sizeof (EFI_ACPI_DESCRIPTION_HEADER) ); return EFI_SUCCESS; } /** Get the ObjectNode information. The Node must be an object node. @ingroup NodeInterfaceApi @param [in] ObjectNode Pointer to an object node. @param [out] OpCode Pointer holding the OpCode. Optional, can be NULL. @param [out] SubOpCode Pointer holding the SubOpCode. Optional, can be NULL. @param [out] PkgLen Pointer holding the PkgLen. The PkgLen is 0 for nodes not having the Pkglen attribute. Optional, can be NULL. @param [out] IsNameSpaceNode Pointer holding TRUE if the node is defining or changing the NameSpace scope. E.g.: The "Name ()" and "Scope ()" ASL statements add/modify the NameSpace scope. Their corresponding node are NameSpace nodes. Optional, can be NULL. @retval EFI_SUCCESS The function completed successfully. @retval EFI_INVALID_PARAMETER Invalid parameter. **/ EFI_STATUS EFIAPI AmlGetObjectNodeInfo ( IN AML_OBJECT_NODE *ObjectNode, OUT UINT8 *OpCode OPTIONAL, OUT UINT8 *SubOpCode OPTIONAL, OUT UINT32 *PkgLen OPTIONAL, OUT BOOLEAN *IsNameSpaceNode OPTIONAL ) { if (!IS_AML_OBJECT_NODE (ObjectNode)) { ASSERT (0); return EFI_INVALID_PARAMETER; } if (OpCode != NULL) { *OpCode = ObjectNode->AmlByteEncoding->OpCode; } if (SubOpCode != NULL) { *SubOpCode = ObjectNode->AmlByteEncoding->SubOpCode; } if (PkgLen != NULL) { *PkgLen = ObjectNode->PkgLen; } if (IsNameSpaceNode != NULL) { *IsNameSpaceNode = AmlNodeHasAttribute (ObjectNode, AML_IN_NAMESPACE); } return EFI_SUCCESS; } /** Returns the count of the fixed arguments for the input Node. @param [in] Node Pointer to an object node. @return Number of fixed arguments of the object node. Return 0 if the node is not an object node. **/ UINT8 AmlGetFixedArgumentCount ( IN AML_OBJECT_NODE *Node ) { if (IS_AML_OBJECT_NODE (Node) && (Node->AmlByteEncoding != NULL)) { return (UINT8)Node->AmlByteEncoding->MaxIndex; } return 0; } /** Get the data type of the DataNode. The Node must be a data node. @param [in] DataNode Pointer to a data node. @param [out] DataType Pointer holding the data type of the data buffer. @retval EFI_SUCCESS The function completed successfully. @retval EFI_INVALID_PARAMETER Invalid parameter. **/ EFI_STATUS EFIAPI AmlGetNodeDataType ( IN AML_DATA_NODE *DataNode, OUT EAML_NODE_DATA_TYPE *DataType ) { if (!IS_AML_DATA_NODE (DataNode) || (DataType == NULL)) { ASSERT (0); return EFI_INVALID_PARAMETER; } *DataType = DataNode->DataType; return EFI_SUCCESS; } /** Get the descriptor Id of the resource data element contained in the DataNode. The Node must be a data node. The Node must have the resource data type, i.e. have the EAmlNodeDataTypeResourceData data type. @param [in] DataNode Pointer to a data node containing a resource data element. @param [out] ResourceDataType Pointer holding the descriptor Id of the resource data. @retval EFI_SUCCESS The function completed successfully. @retval EFI_INVALID_PARAMETER Invalid parameter. **/ EFI_STATUS EFIAPI AmlGetResourceDataType ( IN AML_DATA_NODE *DataNode, OUT AML_RD_HEADER *ResourceDataType ) { if (!IS_AML_DATA_NODE (DataNode) || (ResourceDataType == NULL) || (DataNode->DataType != EAmlNodeDataTypeResourceData)) { ASSERT (0); return EFI_INVALID_PARAMETER; } *ResourceDataType = AmlRdGetDescId (DataNode->Buffer); return EFI_SUCCESS; } /** Get the data buffer and size of the DataNode. The Node must be a data node. BufferSize is always updated to the size of buffer of the DataNode. If: - the content of BufferSize is >= to the DataNode's buffer size; - Buffer is not NULL; then copy the content of the DataNode's buffer in Buffer. @param [in] DataNode Pointer to a data node. @param [out] Buffer Buffer to write the data to. Optional, if NULL, only update BufferSize. @param [in, out] BufferSize Pointer holding: - At entry, the size of the Buffer; - At exit, the size of the DataNode's buffer size. @retval EFI_SUCCESS The function completed successfully. @retval EFI_INVALID_PARAMETER Invalid parameter. **/ EFI_STATUS EFIAPI AmlGetDataNodeBuffer ( IN AML_DATA_NODE *DataNode, OUT UINT8 *Buffer OPTIONAL, IN OUT UINT32 *BufferSize ) { if (!IS_AML_DATA_NODE (DataNode) || (BufferSize == NULL)) { ASSERT (0); return EFI_INVALID_PARAMETER; } if ((*BufferSize >= DataNode->Size) && (Buffer != NULL)) { CopyMem (Buffer, DataNode->Buffer, DataNode->Size); } *BufferSize = DataNode->Size; return EFI_SUCCESS; } /** Update the ACPI DSDT/SSDT table header. The input SdtHeader information is copied to the tree RootNode. The table Length field is automatically updated. The checksum field is only updated when serializing the tree. @param [in] RootNode Pointer to a root node. @param [in] SdtHeader Pointer to an ACPI DSDT/SSDT table header. @retval EFI_SUCCESS The function completed successfully. @retval EFI_INVALID_PARAMETER Invalid parameter. **/ EFI_STATUS EFIAPI AmlUpdateRootNode ( IN AML_ROOT_NODE *RootNode, IN CONST EFI_ACPI_DESCRIPTION_HEADER *SdtHeader ) { EFI_STATUS Status; UINT32 Length; if (!IS_AML_ROOT_NODE (RootNode) || (SdtHeader == NULL) || ((SdtHeader->Signature != EFI_ACPI_6_3_SECONDARY_SYSTEM_DESCRIPTION_TABLE_SIGNATURE) && (SdtHeader->Signature != EFI_ACPI_6_3_DIFFERENTIATED_SYSTEM_DESCRIPTION_TABLE_SIGNATURE))) { ASSERT (0); return EFI_INVALID_PARAMETER; } CopyMem ( RootNode->SdtHeader, SdtHeader, sizeof (EFI_ACPI_DESCRIPTION_HEADER) ); // Update the Length field. Status = AmlComputeSize ((AML_NODE_HEADER *)RootNode, &Length); if (EFI_ERROR (Status)) { ASSERT (0); return Status; } RootNode->SdtHeader->Length = Length + (UINT32)sizeof (EFI_ACPI_DESCRIPTION_HEADER); return Status; } /** Update an object node representing an integer with a new value. The object node must have one of the following OpCodes: - AML_BYTE_PREFIX - AML_WORD_PREFIX - AML_DWORD_PREFIX - AML_QWORD_PREFIX - AML_ZERO_OP - AML_ONE_OP The following OpCode is not supported: - AML_ONES_OP @param [in] IntegerOpNode Pointer an object node containing an integer. Must not be an object node with an AML_ONES_OP OpCode. @param [in] NewInteger New integer value to set. @retval EFI_SUCCESS The function completed successfully. @retval EFI_INVALID_PARAMETER Invalid parameter. **/ EFI_STATUS EFIAPI AmlUpdateInteger ( IN AML_OBJECT_NODE *IntegerOpNode, IN UINT64 NewInteger ) { EFI_STATUS Status; INT8 ValueWidthDiff; if (!IS_AML_OBJECT_NODE (IntegerOpNode) || (!IsIntegerNode (IntegerOpNode) && !IsSpecialIntegerNode (IntegerOpNode)) || AmlNodeCompareOpCode (IntegerOpNode, AML_ONES_OP, 0)) { ASSERT (0); return EFI_INVALID_PARAMETER; } Status = AmlNodeSetIntegerValue (IntegerOpNode, NewInteger, &ValueWidthDiff); if (EFI_ERROR (Status)) { ASSERT (0); return Status; } // If the new size is different from the old size, propagate the new size. if (ValueWidthDiff != 0) { // Propagate the information. Status = AmlPropagateInformation ( (AML_NODE_HEADER *)IntegerOpNode, (ValueWidthDiff > 0) ? TRUE : FALSE, ABS (ValueWidthDiff), 0 ); if (EFI_ERROR (Status)) { ASSERT (0); } } return Status; } /** Update the buffer of a data node. Note: The data type of the buffer's content must match the data type of the DataNode. This is a hard restriction to prevent undesired behaviour. @param [in] DataNode Pointer to a data node. @param [in] DataType Data type of the Buffer's content. @param [in] Buffer Buffer containing the new data. The content of the Buffer is copied. @param [in] Size Size of the Buffer. @retval EFI_SUCCESS The function completed successfully. @retval EFI_INVALID_PARAMETER Invalid parameter. @retval EFI_UNSUPPORTED Operation not supporter. **/ EFI_STATUS EFIAPI AmlUpdateDataNode ( IN AML_DATA_NODE *DataNode, IN EAML_NODE_DATA_TYPE DataType, IN UINT8 *Buffer, IN UINT32 Size ) { EFI_STATUS Status; UINT32 ExpectedSize; AML_OBJECT_NODE *ParentNode; EAML_NODE_DATA_TYPE ExpectedArgType; EAML_PARSE_INDEX Index; if (!IS_AML_DATA_NODE (DataNode) || (DataType > EAmlNodeDataTypeMax) || (Buffer == NULL) || (Size == 0)) { ASSERT (0); return EFI_INVALID_PARAMETER; } ParentNode = (AML_OBJECT_NODE *)AmlGetParent ((AML_NODE_HEADER *)DataNode); if (!IS_AML_OBJECT_NODE (ParentNode)) { ASSERT (0); return EFI_INVALID_PARAMETER; } // The NewNode and OldNode must have the same type. // We do not allow to change the argument type of a data node. // If required, the initial ASL template should be modified // accordingly. // It is however possible to interchange a raw buffer and a // resource data element, since raw data can be misinterpreted // as a resource data element. ExpectedArgType = DataNode->DataType; if ((ExpectedArgType != DataType) && (((ExpectedArgType != EAmlNodeDataTypeRaw) && (ExpectedArgType != EAmlNodeDataTypeResourceData)) || ((DataType != EAmlNodeDataTypeRaw) && (DataType != EAmlNodeDataTypeResourceData)))) { ASSERT (0); return EFI_UNSUPPORTED; } // Perform some compatibility checks. switch (DataType) { case EAmlNodeDataTypeNameString: { // Check the name contained in the Buffer is an AML name // with the right size. Status = AmlGetNameStringSize ((CONST CHAR8 *)Buffer, &ExpectedSize); if (EFI_ERROR (Status) || (Size != ExpectedSize)) { ASSERT (0); return Status; } break; } case EAmlNodeDataTypeString: { ExpectedSize = 0; while (ExpectedSize < Size) { // Cf ACPI 6.3 specification 20.2.3 Data Objects Encoding. // AsciiCharList := Nothing | // AsciiChar := 0x01 - 0x7F // NullChar := 0x00 if (Buffer[ExpectedSize] > 0x7F) { ASSERT (0); return EFI_INVALID_PARAMETER; } ExpectedSize++; } if (ExpectedSize != Size) { ASSERT (0); return EFI_INVALID_PARAMETER; } break; } case EAmlNodeDataTypeUInt: { if (AmlIsNodeFixedArgument ((CONST AML_NODE_HEADER *)DataNode, &Index)) { if ((ParentNode->AmlByteEncoding == NULL) || (ParentNode->AmlByteEncoding->Format == NULL)) { ASSERT (0); return EFI_INVALID_PARAMETER; } // It is not possible to change the size of a fixed length UintX. // E.g. for PackageOp the first fixed argument is of type EAmlUInt8 // and represents the count of elements. This type cannot be changed. if ((ParentNode->AmlByteEncoding->Format[Index] != EAmlObject) && (DataNode->Size != Size)) { ASSERT (0); return EFI_UNSUPPORTED; } } break; } case EAmlNodeDataTypeRaw: { // Check if the parent node has the byte list flag set. if (!AmlNodeHasAttribute (ParentNode, AML_HAS_BYTE_LIST)) { ASSERT (0); return EFI_INVALID_PARAMETER; } break; } case EAmlNodeDataTypeResourceData: { // The resource data can be either small or large resource data. // Small resource data must be at least 1 byte. // Large resource data must be at least as long as the header // of a large resource data. if (AML_RD_IS_LARGE (Buffer) && (Size < sizeof (ACPI_LARGE_RESOURCE_HEADER))) { ASSERT (0); return EFI_INVALID_PARAMETER; } // Check if the parent node has the byte list flag set. if (!AmlNodeHasAttribute (ParentNode, AML_HAS_BYTE_LIST)) { ASSERT (0); return EFI_INVALID_PARAMETER; } // Check the size of the buffer is equal to the resource data size // encoded in the input buffer. ExpectedSize = AmlRdGetSize (Buffer); if (ExpectedSize != Size) { ASSERT (0); return EFI_INVALID_PARAMETER; } Status = AmlSetRdListCheckSum (ParentNode, 0); if (EFI_ERROR (Status)) { ASSERT (0); return Status; } break; } case EAmlNodeDataTypeFieldPkgLen: { // Check the parent is a FieldNamed field element. if (!AmlNodeCompareOpCode (ParentNode, AML_FIELD_NAMED_OP, 0)) { ASSERT (0); return EFI_INVALID_PARAMETER; } break; } // None and reserved types. default: { ASSERT (0); return EFI_INVALID_PARAMETER; break; } } // switch // If the new size is different from the old size, propagate the new size. if (DataNode->Size != Size) { // Propagate the information. Status = AmlPropagateInformation ( DataNode->NodeHeader.Parent, (Size > DataNode->Size) ? TRUE : FALSE, (Size > DataNode->Size) ? (Size - DataNode->Size) : (DataNode->Size - Size), 0 ); if (EFI_ERROR (Status)) { ASSERT (0); return Status; } // Free the old DataNode buffer and allocate a new buffer to store the // new data. FreePool (DataNode->Buffer); DataNode->Buffer = AllocateZeroPool (Size); if (DataNode->Buffer == NULL) { ASSERT (0); return EFI_OUT_OF_RESOURCES; } DataNode->Size = Size; } CopyMem (DataNode->Buffer, Buffer, Size); return EFI_SUCCESS; }