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