1 /** @file 2 AML Serialize. 3 4 Copyright (c) 2019 - 2020, Arm Limited. All rights reserved.<BR> 5 6 SPDX-License-Identifier: BSD-2-Clause-Patent 7 **/ 8 9 #include <AmlNodeDefines.h> 10 11 #include <AmlCoreInterface.h> 12 #include <Stream/AmlStream.h> 13 #include <Tree/AmlNode.h> 14 #include <Tree/AmlTree.h> 15 #include <Utils/AmlUtility.h> 16 17 /** Callback function to copy the AML bytecodes contained in a node 18 to the Stream stored in the Context. 19 The SDT header data contained in the root node is not serialized 20 by this function. 21 22 @param [in] Node Pointer to the node to copy the AML bytecodes 23 from. 24 @param [in, out] Context Contains a forward Stream to write to. 25 (AML_STREAM*)Context. DataEncoder()26 @param [in, out] Status At entry, contains the status returned by the 27 last call to this exact function during the 28 enumeration. 29 As exit, contains the returned status of the 30 call to this function. 31 Optional, can be NULL. 32 33 @retval TRUE if the enumeration can continue or has finished without 34 interruption. 35 @retval FALSE if the enumeration needs to stopped or has stopped. 36 **/ 37 STATIC 38 BOOLEAN 39 EFIAPI 40 AmlSerializeNodeCallback ( 41 IN AML_NODE_HEADER * Node, 42 IN OUT VOID * Context, OPTIONAL 43 IN OUT EFI_STATUS * Status OPTIONAL 44 ) 45 { 46 EFI_STATUS Status1; 47 48 CONST AML_DATA_NODE * DataNode; 49 CONST AML_OBJECT_NODE * ObjectNode; 50 AML_STREAM * FStream; 51 52 // Bytes needed to store OpCode[1] + SubOpcode[1] + MaxPkgLen[4] = 6 bytes. 53 UINT8 ObjectNodeInfoArray[6]; 54 UINT32 Index; 55 BOOLEAN ContinueEnum; 56 57 CONST AML_OBJECT_NODE * ParentNode; 58 EAML_PARSE_INDEX IndexPtr; 59 60 if (!IS_AML_NODE_VALID (Node) || 61 (Context == NULL)) { 62 ASSERT (0); 63 Status1 = EFI_INVALID_PARAMETER; 64 ContinueEnum = FALSE; 65 goto error_handler; 66 } 67 68 // Ignore the second fixed argument of method invocation nodes 69 // as the information stored there (the argument count) is not in the 70 // ACPI specification. 71 ParentNode = (CONST AML_OBJECT_NODE*)AmlGetParent ((AML_NODE_HEADER*)Node); 72 if (IS_AML_OBJECT_NODE (ParentNode) && 73 AmlNodeCompareOpCode (ParentNode, AML_METHOD_INVOC_OP, 0) && 74 AmlIsNodeFixedArgument (Node, &IndexPtr)) { 75 if (IndexPtr == EAmlParseIndexTerm1) { 76 if (Status != NULL) { 77 *Status = EFI_SUCCESS; 78 } 79 return TRUE; 80 } 81 } 82 83 Status1 = EFI_SUCCESS; 84 ContinueEnum = TRUE; 85 FStream = (AML_STREAM*)Context; 86 87 if (IS_AML_DATA_NODE (Node)) { 88 // Copy the content of the Buffer for a DataNode. 89 DataNode = (AML_DATA_NODE*)Node; 90 Status1 = AmlStreamWrite ( 91 FStream, 92 DataNode->Buffer, 93 DataNode->Size 94 ); 95 if (EFI_ERROR (Status1)) { 96 ASSERT (0); 97 ContinueEnum = FALSE; 98 goto error_handler; 99 } 100 101 } else if (IS_AML_OBJECT_NODE (Node) && 102 !AmlNodeHasAttribute ( 103 (CONST AML_OBJECT_NODE*)Node, 104 AML_IS_PSEUDO_OPCODE)) { 105 // Ignore pseudo-opcodes as they are not part of the 106 // ACPI specification. 107 108 ObjectNode = (AML_OBJECT_NODE*)Node; 109 110 Index = 0; 111 // Copy the opcode(s). 112 ObjectNodeInfoArray[Index++] = ObjectNode->AmlByteEncoding->OpCode; 113 if (ObjectNode->AmlByteEncoding->OpCode == AML_EXT_OP) { 114 ObjectNodeInfoArray[Index++] = ObjectNode->AmlByteEncoding->SubOpCode; 115 } 116 117 // Copy the PkgLen. 118 if (AmlNodeHasAttribute (ObjectNode, AML_HAS_PKG_LENGTH)) { 119 Index += AmlSetPkgLength ( 120 ObjectNode->PkgLen, 121 &ObjectNodeInfoArray[Index] 122 ); 123 } 124 125 Status1 = AmlStreamWrite ( 126 FStream, 127 ObjectNodeInfoArray, 128 Index 129 ); 130 if (EFI_ERROR (Status1)) { 131 ASSERT (0); 132 ContinueEnum = FALSE; 133 goto error_handler; 134 } 135 } // IS_AML_OBJECT_NODE (Node) 136 137 error_handler: 138 if (Status != NULL) { 139 *Status = Status1; 140 } 141 return ContinueEnum; 142 } 143 144 /** Serialize a tree to create an ACPI DSDT/SSDT table. 145 146 If: 147 - the content of BufferSize is >= to the size needed to serialize the 148 definition block; 149 - Buffer is not NULL; 150 first serialize the ACPI DSDT/SSDT header from the root node, PutUnsigned(uint32_t offset,uint32_t byte_size,uint64_t value)151 then serialize the AML blob from the rest of the tree. 152 153 The content of BufferSize is always updated to the size needed to 154 serialize the definition block. 155 156 @param [in] RootNode Pointer to a root node. 157 @param [in] Buffer Buffer to write the DSDT/SSDT table to. 158 If Buffer is NULL, the size needed to 159 serialize the DSDT/SSDT table is returned 160 in BufferSize. 161 @param [in, out] BufferSize Pointer holding the size of the Buffer. 162 Its content is always updated to the size 163 needed to serialize the DSDT/SSDT table. 164 165 @retval EFI_SUCCESS The function completed successfully. 166 @retval EFI_INVALID_PARAMETER Invalid parameter. 167 @retval EFI_BUFFER_TOO_SMALL No space left in the buffer. 168 **/ 169 EFI_STATUS 170 EFIAPI 171 AmlSerializeTree ( 172 IN AML_ROOT_NODE * RootNode, 173 IN UINT8 * Buffer, OPTIONAL 174 IN OUT UINT32 * BufferSize 175 ) 176 { 177 EFI_STATUS Status; 178 AML_STREAM FStream; 179 UINT32 TableSize; 180 181 if (!IS_AML_ROOT_NODE (RootNode) || 182 (BufferSize == NULL)) { 183 ASSERT (0); 184 return EFI_INVALID_PARAMETER; 185 } 186 187 // Compute the total size of the AML blob. 188 Status = AmlComputeSize ( 189 (CONST AML_NODE_HEADER*)RootNode, 190 &TableSize 191 ); 192 if (EFI_ERROR (Status)) { 193 ASSERT (0); 194 return Status; 195 } 196 197 // Add the size of the ACPI header. 198 TableSize += (UINT32)sizeof (EFI_ACPI_DESCRIPTION_HEADER); 199 200 // Check the size against the SDT header. 201 // The Length field in the SDT Header is updated if the tree has 202 // been modified. 203 if (TableSize != RootNode->SdtHeader->Length) { 204 ASSERT (0); 205 return EFI_INVALID_PARAMETER; 206 } 207 208 // Buffer is not big enough, or NULL. 209 if ((TableSize < *BufferSize) || (Buffer == NULL)) { 210 *BufferSize = TableSize; 211 return EFI_SUCCESS; 212 } 213 214 // Initialize the stream to the TableSize that is needed. 215 Status = AmlStreamInit ( 216 &FStream, 217 Buffer, 218 TableSize, 219 EAmlStreamDirectionForward 220 ); 221 if (EFI_ERROR (Status)) { 222 ASSERT (0); 223 return Status; 224 } 225 226 // Serialize the header. 227 Status = AmlStreamWrite ( 228 &FStream, 229 (UINT8*)RootNode->SdtHeader, 230 sizeof (EFI_ACPI_DESCRIPTION_HEADER) 231 ); 232 if (EFI_ERROR (Status)) { 233 ASSERT (0); 234 return Status; 235 } 236 237 Status = EFI_SUCCESS; 238 AmlEnumTree ( 239 (AML_NODE_HEADER*)RootNode, 240 AmlSerializeNodeCallback, 241 (VOID*)&FStream, 242 &Status 243 ); 244 if (EFI_ERROR (Status)) { 245 ASSERT (0); 246 return Status; 247 } 248 249 // Update the checksum. 250 return AcpiPlatformChecksum ((EFI_ACPI_DESCRIPTION_HEADER*)Buffer); 251 } 252 253 /** Serialize an AML definition block. 254 255 This functions allocates memory with the "AllocateZeroPool ()" 256 function. This memory is used to serialize the AML tree and is 257 returned in the Table. 258 259 @param [in] RootNode Root node of the tree. 260 @param [out] Table On return, hold the serialized 261 definition block. 262 263 @retval EFI_SUCCESS The function completed successfully. 264 @retval EFI_INVALID_PARAMETER Invalid parameter. 265 @retval EFI_OUT_OF_RESOURCES Could not allocate memory. 266 **/ 267 EFI_STATUS 268 EFIAPI 269 AmlSerializeDefinitionBlock ( 270 IN AML_ROOT_NODE * RootNode, 271 OUT EFI_ACPI_DESCRIPTION_HEADER ** Table 272 ) 273 { 274 EFI_STATUS Status; 275 UINT8 * TableBuffer; 276 UINT32 TableSize; 277 278 if (!IS_AML_ROOT_NODE (RootNode) || 279 (Table == NULL)) { 280 ASSERT (0); 281 return EFI_INVALID_PARAMETER; 282 } 283 284 *Table = NULL; 285 TableBuffer = NULL; 286 TableSize = 0; 287 288 // Get the size of the SSDT table. 289 Status = AmlSerializeTree ( 290 RootNode, 291 TableBuffer, 292 &TableSize 293 ); 294 if (EFI_ERROR (Status)) { 295 ASSERT (0); 296 return Status; 297 } 298 299 TableBuffer = (UINT8*)AllocateZeroPool (TableSize); 300 if (TableBuffer == NULL) { 301 DEBUG (( 302 DEBUG_ERROR, 303 "ERROR: Failed to allocate memory for Table Buffer." 304 )); 305 ASSERT (0); 306 return EFI_OUT_OF_RESOURCES; 307 } 308 309 // Serialize the tree to a SSDT table. 310 Status = AmlSerializeTree ( 311 RootNode, 312 TableBuffer, 313 &TableSize 314 ); 315 if (EFI_ERROR (Status)) { 316 FreePool (TableBuffer); 317 ASSERT (0); 318 } else { 319 // Save the allocated Table buffer in the table list 320 *Table = (EFI_ACPI_DESCRIPTION_HEADER*)TableBuffer; 321 } 322 323 return Status; 324 } 325