| /** @file | |
| AML Utility. | |
| Copyright (c) 2019 - 2021, Arm Limited. All rights reserved.<BR> | |
| SPDX-License-Identifier: BSD-2-Clause-Patent | |
| **/ | |
| #include <Utils/AmlUtility.h> | |
| #include <AmlCoreInterface.h> | |
| #include <Tree/AmlNode.h> | |
| #include <Tree/AmlTree.h> | |
| /** 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; | |
| } |