/** @file AML Utility. Copyright (c) 2019 - 2021, Arm Limited. All rights reserved.
SPDX-License-Identifier: BSD-2-Clause-Patent **/ #include #include #include #include /** This function computes and updates the ACPI table checksum. @param [in] AcpiTable Pointer to an Acpi table. @retval EFI_SUCCESS The function completed successfully. @retval EFI_INVALID_PARAMETER Invalid parameter. **/ EFI_STATUS EFIAPI AcpiPlatformChecksum ( IN EFI_ACPI_DESCRIPTION_HEADER *AcpiTable ) { UINT8 *Ptr; UINT8 Sum; UINT32 Size; if (AcpiTable == NULL) { ASSERT (0); return EFI_INVALID_PARAMETER; } Ptr = (UINT8 *)AcpiTable; Size = AcpiTable->Length; Sum = 0; // Set the checksum field to 0 first. AcpiTable->Checksum = 0; // Compute the checksum. while ((Size--) != 0) { Sum = (UINT8)(Sum + (*Ptr++)); } // Set the checksum. AcpiTable->Checksum = (UINT8)(0xFF - Sum + 1); return EFI_SUCCESS; } /** A callback function that computes the size of a Node and adds it to the Size pointer stored in the Context. Calling this function on the root node will compute the total size of the AML bytestream. @param [in] Node Node to compute the size. @param [in, out] Context Pointer holding the computed size. (UINT32 *) Context. @param [in, out] Status Pointer holding: - At entry, the Status returned by the last call to this exact function during the enumeration; - At exit, he returned status of the call to this function. Optional, can be NULL. @retval TRUE if the enumeration can continue or has finished without interruption. @retval FALSE if the enumeration needs to stopped or has stopped. **/ STATIC BOOLEAN EFIAPI AmlComputeSizeCallback ( IN AML_NODE_HEADER *Node, IN OUT VOID *Context, IN OUT EFI_STATUS *Status OPTIONAL ) { UINT32 Size; EAML_PARSE_INDEX IndexPtr; CONST AML_OBJECT_NODE *ParentNode; if (!IS_AML_NODE_VALID (Node) || (Context == NULL)) { ASSERT (0); if (Status != NULL) { *Status = EFI_INVALID_PARAMETER; } return FALSE; } // Ignore the second fixed argument of method invocation nodes // as the information stored there (the argument count) is not in the // ACPI specification. ParentNode = (CONST AML_OBJECT_NODE *)AmlGetParent (Node); if (IS_AML_OBJECT_NODE (ParentNode) && AmlNodeCompareOpCode (ParentNode, AML_METHOD_INVOC_OP, 0) && AmlIsNodeFixedArgument (Node, &IndexPtr)) { if (IndexPtr == EAmlParseIndexTerm1) { if (Status != NULL) { *Status = EFI_SUCCESS; } return TRUE; } } Size = *((UINT32 *)Context); if (IS_AML_DATA_NODE (Node)) { Size += ((AML_DATA_NODE *)Node)->Size; } else if (IS_AML_OBJECT_NODE (Node) && !AmlNodeHasAttribute ( (CONST AML_OBJECT_NODE *)Node, AML_IS_PSEUDO_OPCODE )) { // Ignore pseudo-opcodes as they are not part of the // ACPI specification. Size += (((AML_OBJECT_NODE *)Node)->AmlByteEncoding->OpCode == AML_EXT_OP) ? 2 : 1; // Add the size of the PkgLen. if (AmlNodeHasAttribute ( (AML_OBJECT_NODE *)Node, AML_HAS_PKG_LENGTH )) { Size += AmlComputePkgLengthWidth (((AML_OBJECT_NODE *)Node)->PkgLen); } } // Check for overflow. // The root node has a null size, thus the strict comparison. if (*((UINT32 *)Context) > Size) { ASSERT (0); *Status = EFI_INVALID_PARAMETER; return FALSE; } *((UINT32 *)Context) = Size; if (Status != NULL) { *Status = EFI_SUCCESS; } return TRUE; } /** Compute the size of a tree/sub-tree. @param [in] Node Node to compute the size. @param [in, out] Size Pointer holding the computed size. @retval EFI_SUCCESS The function completed successfully. @retval EFI_INVALID_PARAMETER Invalid parameter. **/ EFI_STATUS EFIAPI AmlComputeSize ( IN CONST AML_NODE_HEADER *Node, IN OUT UINT32 *Size ) { EFI_STATUS Status; if (!IS_AML_NODE_VALID (Node) || (Size == NULL)) { ASSERT (0); return EFI_INVALID_PARAMETER; } *Size = 0; AmlEnumTree ( (AML_NODE_HEADER *)Node, AmlComputeSizeCallback, (VOID *)Size, &Status ); return Status; } /** Get the value contained in an integer node. @param [in] Node Pointer to an integer node. Must be an object node. @param [out] Value Value contained in the integer node. @retval EFI_SUCCESS The function completed successfully. @retval EFI_INVALID_PARAMETER Invalid parameter. **/ EFI_STATUS EFIAPI AmlNodeGetIntegerValue ( IN AML_OBJECT_NODE *Node, OUT UINT64 *Value ) { AML_DATA_NODE *DataNode; if ((!IsIntegerNode (Node) && !IsSpecialIntegerNode (Node)) || (Value == NULL)) { ASSERT (0); return EFI_INVALID_PARAMETER; } // For ZeroOp and OneOp, there is no data node. if (IsSpecialIntegerNode (Node)) { if (AmlNodeCompareOpCode (Node, AML_ZERO_OP, 0)) { *Value = 0; } else if (AmlNodeCompareOpCode (Node, AML_ONE_OP, 0)) { *Value = 1; } else { // OnesOp cannot be handled: it represents a maximum value. ASSERT (0); return EFI_INVALID_PARAMETER; } return EFI_SUCCESS; } // For integer nodes, the value is in the first fixed argument. DataNode = (AML_DATA_NODE *)Node->FixedArgs[EAmlParseIndexTerm0]; if (!IS_AML_DATA_NODE (DataNode) || (DataNode->DataType != EAmlNodeDataTypeUInt)) { ASSERT (0); return EFI_INVALID_PARAMETER; } switch (DataNode->Size) { case 1: { *Value = *((UINT8 *)(DataNode->Buffer)); break; } case 2: { *Value = *((UINT16 *)(DataNode->Buffer)); break; } case 4: { *Value = *((UINT32 *)(DataNode->Buffer)); break; } case 8: { *Value = *((UINT64 *)(DataNode->Buffer)); break; } default: { ASSERT (0); return EFI_INVALID_PARAMETER; } } // switch return EFI_SUCCESS; } /** Replace a Zero (AML_ZERO_OP) or One (AML_ONE_OP) object node with a byte integer (AML_BYTE_PREFIX) object node having the same value. @param [in] Node Pointer to an integer node. Must be an object node having ZeroOp or OneOp. @retval EFI_SUCCESS The function completed successfully. @retval EFI_INVALID_PARAMETER Invalid parameter. **/ STATIC EFI_STATUS EFIAPI AmlUnwindSpecialInteger ( IN AML_OBJECT_NODE *Node ) { EFI_STATUS Status; AML_DATA_NODE *NewDataNode; UINT8 Value; CONST AML_BYTE_ENCODING *ByteEncoding; if (!IsSpecialIntegerNode (Node)) { ASSERT (0); return EFI_INVALID_PARAMETER; } // Find the value. if (AmlNodeCompareOpCode (Node, AML_ZERO_OP, 0)) { Value = 0; } else if (AmlNodeCompareOpCode (Node, AML_ONE_OP, 0)) { Value = 1; } else { // OnesOp cannot be handled: it represents a maximum value. ASSERT (0); return EFI_INVALID_PARAMETER; } Status = AmlCreateDataNode ( EAmlNodeDataTypeUInt, &Value, sizeof (UINT8), (AML_DATA_NODE **)&NewDataNode ); if (EFI_ERROR (Status)) { ASSERT (0); return Status; } // Change the encoding of the special node to a ByteOp encoding. ByteEncoding = AmlGetByteEncodingByOpCode (AML_BYTE_PREFIX, 0); if (ByteEncoding == NULL) { ASSERT (0); Status = EFI_INVALID_PARAMETER; goto error_handler; } // Update the ByteEncoding from ZERO_OP/ONE_OP to AML_BYTE_PREFIX. Node->AmlByteEncoding = ByteEncoding; // Add the data node as the first fixed argument of the ByteOp object. Status = AmlSetFixedArgument ( (AML_OBJECT_NODE *)Node, EAmlParseIndexTerm0, (AML_NODE_HEADER *)NewDataNode ); if (EFI_ERROR (Status)) { ASSERT (0); goto error_handler; } return Status; error_handler: AmlDeleteTree ((AML_NODE_HEADER *)NewDataNode); return Status; } /** Set the value contained in an integer node. The OpCode is updated accordingly to the new value (e.g.: If the original value was a UINT8 value, then the OpCode would be AML_BYTE_PREFIX. If it the new value is a UINT16 value then the OpCode will be updated to AML_WORD_PREFIX). @param [in] Node Pointer to an integer node. Must be an object node. @param [in] NewValue New value to write in the integer node. @param [out] ValueWidthDiff Difference in number of bytes used to store the new value. Can be negative. @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 AmlNodeSetIntegerValue ( IN AML_OBJECT_NODE *Node, IN UINT64 NewValue, OUT INT8 *ValueWidthDiff ) { EFI_STATUS Status; AML_DATA_NODE *DataNode; UINT8 NewOpCode; UINT8 NumberOfBytes; if ((!IsIntegerNode (Node) && !IsSpecialIntegerNode (Node)) || (ValueWidthDiff == NULL)) { ASSERT (0); return EFI_INVALID_PARAMETER; } *ValueWidthDiff = 0; // For ZeroOp and OneOp, there is no data node. // Thus the object node is converted to a byte object node holding 0 or 1. if (IsSpecialIntegerNode (Node)) { switch (NewValue) { case AML_ZERO_OP: Node->AmlByteEncoding = AmlGetByteEncodingByOpCode (AML_ZERO_OP, 0); return EFI_SUCCESS; case AML_ONE_OP: Node->AmlByteEncoding = AmlGetByteEncodingByOpCode (AML_ONE_OP, 0); return EFI_SUCCESS; default: { Status = AmlUnwindSpecialInteger (Node); if (EFI_ERROR (Status)) { ASSERT (0); return Status; } // The AmlUnwindSpecialInteger functions converts a special integer // node to a UInt8/Byte data node. Thus, the size increments by one: // special integer are encoded as one byte (the opcode only) while byte // integers are encoded as two bytes (the opcode + the value). *ValueWidthDiff += sizeof (UINT8); } } // switch } // IsSpecialIntegerNode (Node) // For integer nodes, the value is in the first fixed argument. DataNode = (AML_DATA_NODE *)Node->FixedArgs[EAmlParseIndexTerm0]; if (!IS_AML_DATA_NODE (DataNode) || (DataNode->DataType != EAmlNodeDataTypeUInt)) { ASSERT (0); return EFI_INVALID_PARAMETER; } // The value can be encoded with a special 0 or 1 OpCode. // The AML_ONES_OP is not handled. if (NewValue <= 1) { NewOpCode = (NewValue == 0) ? AML_ZERO_OP : AML_ONE_OP; Node->AmlByteEncoding = AmlGetByteEncodingByOpCode (NewOpCode, 0); // The value is encoded with a AML_ZERO_OP or AML_ONE_OP. // This means there is no need for a DataNode containing the value. // The change in size is equal to the size of the DataNode's buffer. *ValueWidthDiff = -((INT8)DataNode->Size); // Detach and free the DataNode containing the integer value. DataNode->NodeHeader.Parent = NULL; Node->FixedArgs[EAmlParseIndexTerm0] = NULL; Status = AmlDeleteNode ((AML_NODE_HEADER *)DataNode); if (EFI_ERROR (Status)) { ASSERT (0); return Status; } return EFI_SUCCESS; } // Check the number of bits needed to represent the value. if (NewValue > MAX_UINT32) { // Value is 64 bits. NewOpCode = AML_QWORD_PREFIX; NumberOfBytes = 8; } else if (NewValue > MAX_UINT16) { // Value is 32 bits. NewOpCode = AML_DWORD_PREFIX; NumberOfBytes = 4; } else if (NewValue > MAX_UINT8) { // Value is 16 bits. NewOpCode = AML_WORD_PREFIX; NumberOfBytes = 2; } else { // Value is 8 bits. NewOpCode = AML_BYTE_PREFIX; NumberOfBytes = 1; } *ValueWidthDiff += (INT8)(NumberOfBytes - DataNode->Size); // Update the ByteEncoding as it may have changed between [8 .. 64] bits. Node->AmlByteEncoding = AmlGetByteEncodingByOpCode (NewOpCode, 0); if (Node->AmlByteEncoding == NULL) { ASSERT (0); return EFI_INVALID_PARAMETER; } // Free the old DataNode buffer and allocate a buffer with the right size // to store the new data. if (*ValueWidthDiff != 0) { FreePool (DataNode->Buffer); DataNode->Buffer = AllocateZeroPool (NumberOfBytes); if (DataNode->Buffer == NULL) { ASSERT (0); return EFI_OUT_OF_RESOURCES; } DataNode->Size = NumberOfBytes; } // Write the new value. CopyMem (DataNode->Buffer, &NewValue, NumberOfBytes); return EFI_SUCCESS; } /** Increment/decrement the value contained in the IntegerNode. @param [in] IntegerNode Pointer to an object node containing an integer. @param [in] IsIncrement Choose the operation to do: - TRUE: Increment the Node's size and the Node's count; - FALSE: Decrement the Node's size and the Node's count. @param [in] Diff Value to add/subtract to the integer. @param [out] ValueWidthDiff When modifying the integer, it can be promoted/demoted, e.g. from UINT8 to UINT16. Stores the change in width. Can be negative. @retval EFI_SUCCESS The function completed successfully. @retval EFI_INVALID_PARAMETER Invalid parameter. **/ STATIC EFI_STATUS EFIAPI AmlNodeUpdateIntegerValue ( IN AML_OBJECT_NODE *IntegerNode, IN BOOLEAN IsIncrement, IN UINT64 Diff, OUT INT8 *ValueWidthDiff ) { EFI_STATUS Status; UINT64 Value; if (ValueWidthDiff == NULL) { ASSERT (0); return EFI_INVALID_PARAMETER; } // Get the current value. // Checks on the IntegerNode are done in the call. Status = AmlNodeGetIntegerValue (IntegerNode, &Value); if (EFI_ERROR (Status)) { ASSERT (0); return Status; } // Check for UINT64 over/underflow. if ((IsIncrement && (Value > (MAX_UINT64 - Diff))) || (!IsIncrement && (Value < Diff))) { ASSERT (0); return EFI_INVALID_PARAMETER; } // Compute the new value. if (IsIncrement) { Value += Diff; } else { Value -= Diff; } Status = AmlNodeSetIntegerValue ( IntegerNode, Value, ValueWidthDiff ); ASSERT_EFI_ERROR (Status); return Status; } /** Propagate the size information up the tree. The length of the ACPI table is updated in the RootNode, but not the checksum. @param [in] Node Pointer to a node. Must be a root node or an object node. @param [in] IsIncrement Choose the operation to do: - TRUE: Increment the Node's size and the Node's count; - FALSE: Decrement the Node's size and the Node's count. @param [in] Diff Value to add/subtract to the Node's size. @retval EFI_SUCCESS The function completed successfully. @retval EFI_INVALID_PARAMETER Invalid parameter. **/ STATIC EFI_STATUS EFIAPI AmlPropagateSize ( IN AML_NODE_HEADER *Node, IN BOOLEAN IsIncrement, IN UINT32 *Diff ) { EFI_STATUS Status; AML_OBJECT_NODE *ObjectNode; AML_NODE_HEADER *ParentNode; UINT32 Value; UINT32 InitialPkgLenWidth; UINT32 NewPkgLenWidth; UINT32 ReComputedPkgLenWidth; INT8 FieldWidthChange; if (!IS_AML_OBJECT_NODE (Node) && !IS_AML_ROOT_NODE (Node)) { ASSERT (0); return EFI_INVALID_PARAMETER; } if (IS_AML_OBJECT_NODE (Node)) { ObjectNode = (AML_OBJECT_NODE *)Node; // For BufferOp, the buffer size is stored in BufferSize. Therefore, // BufferOp needs special handling to update the BufferSize. // BufferSize must be updated before the PkgLen to accommodate any // increment resulting from the update of the BufferSize. // DefBuffer := BufferOp PkgLength BufferSize ByteList // BufferOp := 0x11 // BufferSize := TermArg => Integer if (AmlNodeCompareOpCode (ObjectNode, AML_BUFFER_OP, 0)) { // First fixed argument of BufferOp is an integer (BufferSize) // (can be a BYTE, WORD, DWORD or QWORD). // BufferSize is an object node. Status = AmlNodeUpdateIntegerValue ( (AML_OBJECT_NODE *)AmlGetFixedArgument ( ObjectNode, EAmlParseIndexTerm0 ), IsIncrement, (UINT64)(*Diff), &FieldWidthChange ); if (EFI_ERROR (Status)) { ASSERT (0); return Status; } // FieldWidthChange is an integer. // It must be positive if IsIncrement is TRUE, negative otherwise. if ((IsIncrement && (FieldWidthChange < 0)) || (!IsIncrement && (FieldWidthChange > 0))) { ASSERT (0); return EFI_INVALID_PARAMETER; } // Check for UINT32 overflow. if (*Diff > (MAX_UINT32 - (UINT32)ABS (FieldWidthChange))) { ASSERT (0); return EFI_INVALID_PARAMETER; } // Update Diff if the field width changed. *Diff = (UINT32)(*Diff + ABS (FieldWidthChange)); } // AML_BUFFER_OP node. // Update the PgkLen. // Needs to be done at last to reflect the potential field width changes. if (AmlNodeHasAttribute (ObjectNode, AML_HAS_PKG_LENGTH)) { Value = ObjectNode->PkgLen; // Subtract the size of the PkgLen encoding. The size of the PkgLen // encoding must be computed after having updated Value. InitialPkgLenWidth = AmlComputePkgLengthWidth (Value); Value -= InitialPkgLenWidth; // Check for an over/underflows. // PkgLen is a 28 bit value, cf 20.2.4 Package Length Encoding // i.e. the maximum value is (2^28 - 1) = ((BIT0 << 28) - 1). if ((IsIncrement && ((((BIT0 << 28) - 1) - Value) < *Diff)) || (!IsIncrement && (Value < *Diff))) { ASSERT (0); return EFI_INVALID_PARAMETER; } // Update the size. if (IsIncrement) { Value += *Diff; } else { Value -= *Diff; } // Compute the new PkgLenWidth. NewPkgLenWidth = AmlComputePkgLengthWidth (Value); if (NewPkgLenWidth == 0) { ASSERT (0); return EFI_INVALID_PARAMETER; } // Add it to the Value. Value += NewPkgLenWidth; // Check that adding the PkgLenWidth didn't trigger a domino effect, // increasing the encoding width of the PkgLen again. // The PkgLen is encoded on at most 4 bytes. It is possible to increase // the PkgLen width if its encoding is on less than 3 bytes. ReComputedPkgLenWidth = AmlComputePkgLengthWidth (Value); if (ReComputedPkgLenWidth != NewPkgLenWidth) { if ((ReComputedPkgLenWidth != 0) && (ReComputedPkgLenWidth < 4)) { // No need to recompute the PkgLen since a new threshold cannot // be reached by incrementing the value by one. Value += 1; } else { ASSERT (0); return EFI_INVALID_PARAMETER; } } *Diff += (InitialPkgLenWidth > ReComputedPkgLenWidth) ? (InitialPkgLenWidth - ReComputedPkgLenWidth) : (ReComputedPkgLenWidth - InitialPkgLenWidth); ObjectNode->PkgLen = Value; } // PkgLen update. // During CodeGeneration, the tree is incomplete and // there is no root node at the top of the tree. Stop // propagating the new size when finding a root node // OR when a NULL parent is found. ParentNode = AmlGetParent ((AML_NODE_HEADER *)Node); if (ParentNode != NULL) { // Propagate the size up the tree. Status = AmlPropagateSize ( Node->Parent, IsIncrement, Diff ); if (EFI_ERROR (Status)) { ASSERT (0); return Status; } } } else if (IS_AML_ROOT_NODE (Node)) { // Update the length field in the SDT header. Value = ((AML_ROOT_NODE *)Node)->SdtHeader->Length; // Check for an over/underflows. if ((IsIncrement && (Value > (MAX_UINT32 - *Diff))) || (!IsIncrement && (Value < *Diff))) { ASSERT (0); return EFI_INVALID_PARAMETER; } // Update the size. if (IsIncrement) { Value += *Diff; } else { Value -= *Diff; } ((AML_ROOT_NODE *)Node)->SdtHeader->Length = Value; } return EFI_SUCCESS; } /** Propagate the node count information up the tree. @param [in] ObjectNode Pointer to an object node. @param [in] IsIncrement Choose the operation to do: - TRUE: Increment the Node's size and the Node's count; - FALSE: Decrement the Node's size and the Node's count. @param [in] NodeCount Number of nodes added/removed (depends on the value of Operation). @param [out] FieldWidthChange When modifying the integer, it can be promoted/demoted, e.g. from UINT8 to UINT16. Stores the change in width. Can be negative. @retval EFI_SUCCESS The function completed successfully. @retval EFI_INVALID_PARAMETER Invalid parameter. **/ STATIC EFI_STATUS EFIAPI AmlPropagateNodeCount ( IN AML_OBJECT_NODE *ObjectNode, IN BOOLEAN IsIncrement, IN UINT8 NodeCount, OUT INT8 *FieldWidthChange ) { EFI_STATUS Status; AML_NODE_HEADER *NodeCountArg; UINT8 CurrNodeCount; // Currently there is no use case where (NodeCount > 1). if (!IS_AML_OBJECT_NODE (ObjectNode) || (FieldWidthChange == NULL) || (NodeCount > 1)) { ASSERT (0); return EFI_INVALID_PARAMETER; } *FieldWidthChange = 0; // Update the number of elements stored in PackageOp and VarPackageOp. // The number of elements is stored as the first fixed argument. // DefPackage := PackageOp PkgLength NumElements PackageElementList // PackageOp := 0x12 // DefVarPackage := VarPackageOp PkgLength VarNumElements PackageElementList // VarPackageOp := 0x13 // NumElements := ByteData // VarNumElements := TermArg => Integer NodeCountArg = AmlGetFixedArgument (ObjectNode, EAmlParseIndexTerm0); if (AmlNodeCompareOpCode (ObjectNode, AML_PACKAGE_OP, 0)) { // First fixed argument of PackageOp stores the number of elements // in the package. It is an UINT8. // Check for over/underflow. CurrNodeCount = *(((AML_DATA_NODE *)NodeCountArg)->Buffer); if ((IsIncrement && (CurrNodeCount == MAX_UINT8)) || (!IsIncrement && (CurrNodeCount == 0))) { ASSERT (0); return EFI_INVALID_PARAMETER; } // Update the node count in the DataNode. CurrNodeCount = IsIncrement ? (CurrNodeCount + 1) : (CurrNodeCount - 1); *(((AML_DATA_NODE *)NodeCountArg)->Buffer) = CurrNodeCount; } else if (AmlNodeCompareOpCode (ObjectNode, AML_VAR_PACKAGE_OP, 0)) { // First fixed argument of PackageOp stores the number of elements // in the package. It is an integer (can be a BYTE, WORD, DWORD, QWORD). Status = AmlNodeUpdateIntegerValue ( (AML_OBJECT_NODE *)NodeCountArg, IsIncrement, NodeCount, FieldWidthChange ); if (EFI_ERROR (Status)) { ASSERT (0); return Status; } } return EFI_SUCCESS; } /** Propagate information up the tree. The information can be a new size, a new number of arguments. @param [in] Node Pointer to a node. Must be a root node or an object node. @param [in] IsIncrement Choose the operation to do: - TRUE: Increment the Node's size and the Node's count; - FALSE: Decrement the Node's size and the Node's count. @param [in] Diff Value to add/subtract to the Node's size. @param [in] NodeCount Number of nodes added/removed. @retval EFI_SUCCESS The function completed successfully. @retval EFI_INVALID_PARAMETER Invalid parameter. **/ EFI_STATUS EFIAPI AmlPropagateInformation ( IN AML_NODE_HEADER *Node, IN BOOLEAN IsIncrement, IN UINT32 Diff, IN UINT8 NodeCount ) { EFI_STATUS Status; INT8 FieldWidthChange; // Currently there is no use case where (NodeCount > 1). if ((!IS_AML_ROOT_NODE (Node) && !IS_AML_OBJECT_NODE (Node)) || (NodeCount > 1)) { ASSERT (0); return EFI_INVALID_PARAMETER; } // Propagate the node count first as it may change the number of bytes // needed to store the node count, and then impact FieldWidthChange. if ((NodeCount != 0) && IS_AML_OBJECT_NODE (Node)) { Status = AmlPropagateNodeCount ( (AML_OBJECT_NODE *)Node, IsIncrement, NodeCount, &FieldWidthChange ); if (EFI_ERROR (Status)) { ASSERT (0); return Status; } // Propagate the potential field width change. // Maximum change is between UINT8/UINT64: 8 bytes. if ((ABS (FieldWidthChange) > 8) || (IsIncrement && ((FieldWidthChange < 0) || ((Diff + (UINT8)FieldWidthChange) > MAX_UINT32))) || (!IsIncrement && ((FieldWidthChange > 0) || (Diff < (UINT32)ABS (FieldWidthChange))))) { ASSERT (0); return EFI_INVALID_PARAMETER; } Diff = (UINT32)(Diff + (UINT8)ABS (FieldWidthChange)); } // Diff can be zero if some data is updated without modifying the data size. if (Diff != 0) { Status = AmlPropagateSize (Node, IsIncrement, &Diff); if (EFI_ERROR (Status)) { ASSERT (0); return Status; } } return EFI_SUCCESS; } /** Find and set the EndTag's Checksum of a list of Resource Data elements. Lists of Resource Data elements end with an EndTag (most of the time). This function finds the EndTag (if present) in a list of Resource Data elements and sets the checksum. ACPI 6.4, s6.4.2.9 "End Tag": "This checksum is generated such that adding it to the sum of all the data bytes will produce a zero sum." "If the checksum field is zero, the resource data is treated as if the checksum operation succeeded. Configuration proceeds normally." To avoid re-computing checksums, if a new resource data elements is added/removed/modified in a list of resource data elements, the AmlLib resets the checksum to 0. @param [in] BufferOpNode Node having a list of Resource Data elements. @param [in] CheckSum CheckSum to store in the EndTag. To ignore/avoid computing the checksum, give 0. @retval EFI_SUCCESS The function completed successfully. @retval EFI_INVALID_PARAMETER Invalid parameter. @retval EFI_NOT_FOUND No EndTag found. **/ EFI_STATUS EFIAPI AmlSetRdListCheckSum ( IN AML_OBJECT_NODE *BufferOpNode, IN UINT8 CheckSum ) { EFI_STATUS Status; AML_DATA_NODE *LastRdNode; AML_RD_HEADER RdDataType; if (!AmlNodeCompareOpCode (BufferOpNode, AML_BUFFER_OP, 0)) { ASSERT (0); return EFI_INVALID_PARAMETER; } // Get the last Resource data node in the variable list of // argument of the BufferOp node. LastRdNode = (AML_DATA_NODE *)AmlGetPreviousVariableArgument ( (AML_NODE_HEADER *)BufferOpNode, NULL ); if ((LastRdNode == NULL) || !IS_AML_DATA_NODE (LastRdNode) || (LastRdNode->DataType != EAmlNodeDataTypeResourceData)) { ASSERT (0); return EFI_INVALID_PARAMETER; } Status = AmlGetResourceDataType (LastRdNode, &RdDataType); if (EFI_ERROR (Status)) { ASSERT (0); return Status; } // Check the LastRdNode is an EndTag. // It is possible to have only one Resource Data in a BufferOp with // no EndTag. Return EFI_NOT_FOUND is such case. if (!AmlRdCompareDescId ( &RdDataType, AML_RD_BUILD_SMALL_DESC_ID (ACPI_SMALL_END_TAG_DESCRIPTOR_NAME) )) { ASSERT (0); return EFI_NOT_FOUND; } Status = AmlRdSetEndTagChecksum (LastRdNode->Buffer, CheckSum); ASSERT_EFI_ERROR (Status); return Status; }