]> git.proxmox.com Git - mirror_edk2.git/blame - DynamicTablesPkg/Library/Common/AmlLib/Tree/AmlNodeInterface.c
DynamicTablesPkg: Change OPTIONAL keyword usage style
[mirror_edk2.git] / DynamicTablesPkg / Library / Common / AmlLib / Tree / AmlNodeInterface.c
CommitLineData
bcab901b
PG
1/** @file\r
2 AML Node Interface.\r
3\r
4 Copyright (c) 2019 - 2020, Arm Limited. All rights reserved.<BR>\r
5\r
6 SPDX-License-Identifier: BSD-2-Clause-Patent\r
7**/\r
8\r
9#include <AmlNodeDefines.h>\r
10\r
11#include <AmlCoreInterface.h>\r
12#include <ResourceData/AmlResourceData.h>\r
13#include <String/AmlString.h>\r
14#include <Tree/AmlNode.h>\r
15#include <Tree/AmlTree.h>\r
16#include <Utils/AmlUtility.h>\r
17\r
18/** Returns the tree node type (Root/Object/Data).\r
19\r
20 @param [in] Node Pointer to a Node.\r
21\r
22 @return The node type.\r
23 EAmlNodeUnknown if invalid parameter.\r
24**/\r
25EAML_NODE_TYPE\r
26EFIAPI\r
27AmlGetNodeType (\r
28 IN AML_NODE_HEADER * Node\r
29 )\r
30{\r
31 if (!IS_AML_NODE_VALID (Node)) {\r
32 ASSERT (0);\r
33 return EAmlNodeUnknown;\r
34 }\r
35\r
36 return Node->NodeType;\r
37}\r
38\r
39/** Get the RootNode information.\r
40 The Node must be a root node.\r
41\r
42 @param [in] RootNode Pointer to a root node.\r
43 @param [out] SdtHeaderBuffer Buffer to copy the ACPI DSDT/SSDT header to.\r
44\r
45 @retval EFI_SUCCESS The function completed successfully.\r
46 @retval EFI_INVALID_PARAMETER Invalid parameter.\r
47**/\r
48EFI_STATUS\r
49EFIAPI\r
50AmlGetRootNodeInfo (\r
51 IN AML_ROOT_NODE * RootNode,\r
52 OUT EFI_ACPI_DESCRIPTION_HEADER * SdtHeaderBuffer\r
53 )\r
54{\r
55 if (!IS_AML_ROOT_NODE (RootNode) ||\r
56 (SdtHeaderBuffer == NULL)) {\r
57 ASSERT (0);\r
58 return EFI_INVALID_PARAMETER;\r
59 }\r
60\r
61 CopyMem (\r
62 SdtHeaderBuffer,\r
63 RootNode->SdtHeader,\r
64 sizeof (EFI_ACPI_DESCRIPTION_HEADER)\r
65 );\r
66\r
67 return EFI_SUCCESS;\r
68}\r
69\r
70/** Get the ObjectNode information.\r
71 The Node must be an object node.\r
72\r
73 @ingroup NodeInterfaceApi\r
74\r
75 @param [in] ObjectNode Pointer to an object node.\r
76 @param [out] OpCode Pointer holding the OpCode.\r
77 Optional, can be NULL.\r
78 @param [out] SubOpCode Pointer holding the SubOpCode.\r
79 Optional, can be NULL.\r
80 @param [out] PkgLen Pointer holding the PkgLen.\r
81 The PkgLen is 0 for nodes\r
82 not having the Pkglen attribute.\r
83 Optional, can be NULL.\r
84 @param [out] IsNameSpaceNode Pointer holding TRUE if the node is defining\r
85 or changing the NameSpace scope.\r
86 E.g.: The "Name ()" and "Scope ()" ASL\r
87 statements add/modify the NameSpace scope.\r
88 Their corresponding node are NameSpace nodes.\r
89 Optional, can be NULL.\r
90\r
91 @retval EFI_SUCCESS The function completed successfully.\r
92 @retval EFI_INVALID_PARAMETER Invalid parameter.\r
93**/\r
94EFI_STATUS\r
95EFIAPI\r
96AmlGetObjectNodeInfo (\r
97 IN AML_OBJECT_NODE * ObjectNode,\r
fe2d8189
MK
98 OUT UINT8 * OpCode OPTIONAL,\r
99 OUT UINT8 * SubOpCode OPTIONAL,\r
100 OUT UINT32 * PkgLen OPTIONAL,\r
bcab901b
PG
101 OUT BOOLEAN * IsNameSpaceNode OPTIONAL\r
102 )\r
103{\r
104 if (!IS_AML_OBJECT_NODE (ObjectNode)) {\r
105 ASSERT (0);\r
106 return EFI_INVALID_PARAMETER;\r
107 }\r
108\r
109 if (OpCode != NULL) {\r
110 *OpCode = ObjectNode->AmlByteEncoding->OpCode;\r
111 }\r
112 if (SubOpCode != NULL) {\r
113 *SubOpCode = ObjectNode->AmlByteEncoding->SubOpCode;\r
114 }\r
115 if (PkgLen != NULL) {\r
116 *PkgLen = ObjectNode->PkgLen;\r
117 }\r
118 if (IsNameSpaceNode != NULL) {\r
119 *IsNameSpaceNode = AmlNodeHasAttribute (ObjectNode, AML_IN_NAMESPACE);\r
120 }\r
121\r
122 return EFI_SUCCESS;\r
123}\r
124\r
125/** Returns the count of the fixed arguments for the input Node.\r
126\r
127 @param [in] Node Pointer to an object node.\r
128\r
129 @return Number of fixed arguments of the object node.\r
130 Return 0 if the node is not an object node.\r
131**/\r
132UINT8\r
133AmlGetFixedArgumentCount (\r
134 IN AML_OBJECT_NODE * Node\r
135 )\r
136{\r
137 if (IS_AML_OBJECT_NODE (Node) &&\r
138 (Node->AmlByteEncoding != NULL)) {\r
139 return (UINT8)Node->AmlByteEncoding->MaxIndex;\r
140 }\r
141\r
142 return 0;\r
143}\r
144\r
145/** Get the data type of the DataNode.\r
146 The Node must be a data node.\r
147\r
148 @param [in] DataNode Pointer to a data node.\r
149 @param [out] DataType Pointer holding the data type of the data buffer.\r
150\r
151 @retval EFI_SUCCESS The function completed successfully.\r
152 @retval EFI_INVALID_PARAMETER Invalid parameter.\r
153**/\r
154EFI_STATUS\r
155EFIAPI\r
156AmlGetNodeDataType (\r
157 IN AML_DATA_NODE * DataNode,\r
158 OUT EAML_NODE_DATA_TYPE * DataType\r
159 )\r
160{\r
161 if (!IS_AML_DATA_NODE (DataNode) ||\r
162 (DataType == NULL)) {\r
163 ASSERT (0);\r
164 return EFI_INVALID_PARAMETER;\r
165 }\r
166\r
167 *DataType = DataNode->DataType;\r
168\r
169 return EFI_SUCCESS;\r
170}\r
171\r
172/** Get the descriptor Id of the resource data element\r
173 contained in the DataNode.\r
174\r
175 The Node must be a data node.\r
176 The Node must have the resource data type, i.e. have the\r
177 EAmlNodeDataTypeResourceData data type.\r
178\r
179 @param [in] DataNode Pointer to a data node containing a\r
180 resource data element.\r
181 @param [out] ResourceDataType Pointer holding the descriptor Id of\r
182 the resource data.\r
183\r
184 @retval EFI_SUCCESS The function completed successfully.\r
185 @retval EFI_INVALID_PARAMETER Invalid parameter.\r
186**/\r
187EFI_STATUS\r
188EFIAPI\r
189AmlGetResourceDataType (\r
190 IN AML_DATA_NODE * DataNode,\r
191 OUT AML_RD_HEADER * ResourceDataType\r
192 )\r
193{\r
194 if (!IS_AML_DATA_NODE (DataNode) ||\r
195 (ResourceDataType == NULL) ||\r
196 (DataNode->DataType != EAmlNodeDataTypeResourceData)) {\r
197 ASSERT (0);\r
198 return EFI_INVALID_PARAMETER;\r
199 }\r
200\r
201 *ResourceDataType = AmlRdGetDescId (DataNode->Buffer);\r
202\r
203 return EFI_SUCCESS;\r
204}\r
205\r
206/** Get the data buffer and size of the DataNode.\r
207 The Node must be a data node.\r
208\r
209 BufferSize is always updated to the size of buffer of the DataNode.\r
210\r
211 If:\r
212 - the content of BufferSize is >= to the DataNode's buffer size;\r
213 - Buffer is not NULL;\r
214 then copy the content of the DataNode's buffer in Buffer.\r
215\r
216 @param [in] DataNode Pointer to a data node.\r
217 @param [out] Buffer Buffer to write the data to.\r
218 Optional, if NULL, only update BufferSize.\r
219 @param [in, out] BufferSize Pointer holding:\r
220 - At entry, the size of the Buffer;\r
221 - At exit, the size of the DataNode's\r
222 buffer size.\r
223\r
224 @retval EFI_SUCCESS The function completed successfully.\r
225 @retval EFI_INVALID_PARAMETER Invalid parameter.\r
226**/\r
227EFI_STATUS\r
228EFIAPI\r
229AmlGetDataNodeBuffer (\r
230 IN AML_DATA_NODE * DataNode,\r
fe2d8189 231 OUT UINT8 * Buffer OPTIONAL,\r
bcab901b
PG
232 IN OUT UINT32 * BufferSize\r
233 )\r
234{\r
235 if (!IS_AML_DATA_NODE (DataNode) ||\r
236 (BufferSize == NULL)) {\r
237 ASSERT (0);\r
238 return EFI_INVALID_PARAMETER;\r
239 }\r
240\r
241 if ((*BufferSize >= DataNode->Size) &&\r
242 (Buffer != NULL)) {\r
243 CopyMem (Buffer, DataNode->Buffer, DataNode->Size);\r
244 }\r
245\r
246 *BufferSize = DataNode->Size;\r
247\r
248 return EFI_SUCCESS;\r
249}\r
250\r
251/** Update the ACPI DSDT/SSDT table header.\r
252\r
253 The input SdtHeader information is copied to the tree RootNode.\r
254 The table Length field is automatically updated.\r
255 The checksum field is only updated when serializing the tree.\r
256\r
257 @param [in] RootNode Pointer to a root node.\r
258 @param [in] SdtHeader Pointer to an ACPI DSDT/SSDT table header.\r
259\r
260 @retval EFI_SUCCESS The function completed successfully.\r
261 @retval EFI_INVALID_PARAMETER Invalid parameter.\r
262**/\r
263EFI_STATUS\r
264EFIAPI\r
265AmlUpdateRootNode (\r
266 IN AML_ROOT_NODE * RootNode,\r
267 IN CONST EFI_ACPI_DESCRIPTION_HEADER * SdtHeader\r
268 )\r
269{\r
270 EFI_STATUS Status;\r
271 UINT32 Length;\r
272\r
273 if (!IS_AML_ROOT_NODE (RootNode) ||\r
274 (SdtHeader == NULL) ||\r
275 ((SdtHeader->Signature !=\r
276 EFI_ACPI_6_3_SECONDARY_SYSTEM_DESCRIPTION_TABLE_SIGNATURE) &&\r
277 (SdtHeader->Signature !=\r
278 EFI_ACPI_6_3_DIFFERENTIATED_SYSTEM_DESCRIPTION_TABLE_SIGNATURE))) {\r
279 ASSERT (0);\r
280 return EFI_INVALID_PARAMETER;\r
281 }\r
282\r
283 CopyMem (\r
284 RootNode->SdtHeader,\r
285 SdtHeader,\r
286 sizeof (EFI_ACPI_DESCRIPTION_HEADER)\r
287 );\r
288\r
289 // Update the Length field.\r
290 Status = AmlComputeSize ((AML_NODE_HEADER*)RootNode, &Length);\r
291 if (EFI_ERROR (Status)) {\r
292 ASSERT (0);\r
293 return Status;\r
294 }\r
295\r
296 RootNode->SdtHeader->Length = Length +\r
297 (UINT32)sizeof (EFI_ACPI_DESCRIPTION_HEADER);\r
298\r
299 return Status;\r
300}\r
301\r
302/** Update an object node representing an integer with a new value.\r
303\r
304 The object node must have one of the following OpCodes:\r
305 - AML_BYTE_PREFIX\r
306 - AML_WORD_PREFIX\r
307 - AML_DWORD_PREFIX\r
308 - AML_QWORD_PREFIX\r
309 - AML_ZERO_OP\r
310 - AML_ONE_OP\r
311\r
312 The following OpCode is not supported:\r
313 - AML_ONES_OP\r
314\r
315 @param [in] IntegerOpNode Pointer an object node containing an integer.\r
316 Must not be an object node with an AML_ONES_OP\r
317 OpCode.\r
318 @param [in] NewInteger New integer value to set.\r
319\r
320 @retval EFI_SUCCESS The function completed successfully.\r
321 @retval EFI_INVALID_PARAMETER Invalid parameter.\r
322**/\r
323EFI_STATUS\r
324EFIAPI\r
325AmlUpdateInteger (\r
326 IN AML_OBJECT_NODE * IntegerOpNode,\r
327 IN UINT64 NewInteger\r
328 )\r
329{\r
330 EFI_STATUS Status;\r
331\r
332 INT8 ValueWidthDiff;\r
333\r
334 if (!IS_AML_OBJECT_NODE (IntegerOpNode) ||\r
335 (!IsIntegerNode (IntegerOpNode) &&\r
336 !IsSpecialIntegerNode (IntegerOpNode)) ||\r
337 AmlNodeCompareOpCode (IntegerOpNode, AML_ONES_OP, 0)) {\r
338 ASSERT (0);\r
339 return EFI_INVALID_PARAMETER;\r
340 }\r
341\r
342 Status = AmlNodeSetIntegerValue (IntegerOpNode, NewInteger, &ValueWidthDiff);\r
343 if (EFI_ERROR (Status)) {\r
344 ASSERT (0);\r
345 return Status;\r
346 }\r
347\r
348 // If the new size is different from the old size, propagate the new size.\r
349 if (ValueWidthDiff != 0) {\r
350 // Propagate the information.\r
351 Status = AmlPropagateInformation (\r
352 (AML_NODE_HEADER*)IntegerOpNode,\r
353 (ValueWidthDiff > 0) ? TRUE : FALSE,\r
354 ABS (ValueWidthDiff),\r
355 0\r
356 );\r
357 if (EFI_ERROR (Status)) {\r
358 ASSERT (0);\r
359 }\r
360 }\r
361\r
362 return Status;\r
363}\r
364\r
365/** Update the buffer of a data node.\r
366\r
367 Note: The data type of the buffer's content must match the data type of the\r
368 DataNode. This is a hard restriction to prevent undesired behaviour.\r
369\r
370 @param [in] DataNode Pointer to a data node.\r
371 @param [in] DataType Data type of the Buffer's content.\r
372 @param [in] Buffer Buffer containing the new data. The content of\r
373 the Buffer is copied.\r
374 @param [in] Size Size of the Buffer.\r
375\r
376 @retval EFI_SUCCESS The function completed successfully.\r
377 @retval EFI_INVALID_PARAMETER Invalid parameter.\r
378 @retval EFI_UNSUPPORTED Operation not supporter.\r
379**/\r
380EFI_STATUS\r
381EFIAPI\r
382AmlUpdateDataNode (\r
383 IN AML_DATA_NODE * DataNode,\r
384 IN EAML_NODE_DATA_TYPE DataType,\r
385 IN UINT8 * Buffer,\r
386 IN UINT32 Size\r
387 )\r
388{\r
389 EFI_STATUS Status;\r
390\r
391 UINT32 ExpectedSize;\r
392 AML_OBJECT_NODE * ParentNode;\r
393 EAML_NODE_DATA_TYPE ExpectedArgType;\r
394 EAML_PARSE_INDEX Index;\r
395\r
396 if (!IS_AML_DATA_NODE (DataNode) ||\r
397 (DataType > EAmlNodeDataTypeMax) ||\r
398 (Buffer == NULL) ||\r
399 (Size == 0)) {\r
400 ASSERT (0);\r
401 return EFI_INVALID_PARAMETER;\r
402 }\r
403\r
404 ParentNode = (AML_OBJECT_NODE*)AmlGetParent ((AML_NODE_HEADER*)DataNode);\r
405 if (!IS_AML_OBJECT_NODE (ParentNode)) {\r
406 ASSERT (0);\r
407 return EFI_INVALID_PARAMETER;\r
408 }\r
409\r
410 // The NewNode and OldNode must have the same type.\r
411 // We do not allow to change the argument type of a data node.\r
412 // If required, the initial ASL template should be modified\r
413 // accordingly.\r
414 // It is however possible to interchange a raw buffer and a\r
415 // resource data element, since raw data can be misinterpreted\r
416 // as a resource data element.\r
417 ExpectedArgType = DataNode->DataType;\r
418 if ((ExpectedArgType != DataType) &&\r
419 (((ExpectedArgType != EAmlNodeDataTypeRaw) &&\r
420 (ExpectedArgType != EAmlNodeDataTypeResourceData)) ||\r
421 ((DataType != EAmlNodeDataTypeRaw) &&\r
422 (DataType != EAmlNodeDataTypeResourceData)))) {\r
423 ASSERT (0);\r
424 return EFI_UNSUPPORTED;\r
425 }\r
426\r
427 // Perform some compatibility checks.\r
428 switch (DataType) {\r
429 case EAmlNodeDataTypeNameString:\r
430 {\r
431 // Check the name contained in the Buffer is an AML name\r
432 // with the right size.\r
433 Status = AmlGetNameStringSize ((CONST CHAR8*)Buffer, &ExpectedSize);\r
434 if (EFI_ERROR (Status) ||\r
435 (Size != ExpectedSize)) {\r
436 ASSERT (0);\r
437 return Status;\r
438 }\r
439 break;\r
440 }\r
441 case EAmlNodeDataTypeString:\r
442 {\r
443 ExpectedSize = 0;\r
444 while (ExpectedSize < Size) {\r
445 // Cf ACPI 6.3 specification 20.2.3 Data Objects Encoding.\r
446 // AsciiCharList := Nothing | <AsciiChar AsciiCharList>\r
447 // AsciiChar := 0x01 - 0x7F\r
448 // NullChar := 0x00\r
449 if (Buffer[ExpectedSize] > 0x7F) {\r
450 ASSERT (0);\r
451 return EFI_INVALID_PARAMETER;\r
452 }\r
453 ExpectedSize++;\r
454 }\r
455\r
456 if (ExpectedSize != Size) {\r
457 ASSERT (0);\r
458 return EFI_INVALID_PARAMETER;\r
459 }\r
460 break;\r
461 }\r
462 case EAmlNodeDataTypeUInt:\r
463 {\r
464 if (AmlIsNodeFixedArgument ((CONST AML_NODE_HEADER*)DataNode, &Index)) {\r
465 if ((ParentNode->AmlByteEncoding == NULL) ||\r
466 (ParentNode->AmlByteEncoding->Format == NULL)) {\r
467 ASSERT (0);\r
468 return EFI_INVALID_PARAMETER;\r
469 }\r
470\r
471 // It is not possible to change the size of a fixed length UintX.\r
472 // E.g. for PackageOp the first fixed argument is of type EAmlUInt8\r
473 // and represents the count of elements. This type cannot be changed.\r
474 if ((ParentNode->AmlByteEncoding->Format[Index] != EAmlObject) &&\r
475 (DataNode->Size != Size)) {\r
476 ASSERT (0);\r
477 return EFI_UNSUPPORTED;\r
478 }\r
479 }\r
480 break;\r
481 }\r
482 case EAmlNodeDataTypeRaw:\r
483 {\r
484 // Check if the parent node has the byte list flag set.\r
485 if (!AmlNodeHasAttribute (ParentNode, AML_HAS_BYTE_LIST)) {\r
486 ASSERT (0);\r
487 return EFI_INVALID_PARAMETER;\r
488 }\r
489 break;\r
490 }\r
491 case EAmlNodeDataTypeResourceData:\r
492 {\r
493 // The resource data can be either small or large resource data.\r
494 // Small resource data must be at least 1 byte.\r
495 // Large resource data must be at least as long as the header\r
496 // of a large resource data.\r
497 if (AML_RD_IS_LARGE (Buffer) &&\r
498 (Size < sizeof (ACPI_LARGE_RESOURCE_HEADER))) {\r
499 ASSERT (0);\r
500 return EFI_INVALID_PARAMETER;\r
501 }\r
502\r
503 // Check if the parent node has the byte list flag set.\r
504 if (!AmlNodeHasAttribute (ParentNode, AML_HAS_BYTE_LIST)) {\r
505 ASSERT (0);\r
506 return EFI_INVALID_PARAMETER;\r
507 }\r
508\r
509 // Check the size of the buffer is equal to the resource data size\r
510 // encoded in the input buffer.\r
511 ExpectedSize = AmlRdGetSize (Buffer);\r
512 if (ExpectedSize != Size) {\r
513 ASSERT (0);\r
514 return EFI_INVALID_PARAMETER;\r
515 }\r
7b2022d3
PG
516\r
517 Status = AmlSetRdListCheckSum (ParentNode, 0);\r
518 if (EFI_ERROR (Status)) {\r
519 ASSERT (0);\r
520 return Status;\r
521 }\r
522\r
bcab901b
PG
523 break;\r
524 }\r
525 case EAmlNodeDataTypeFieldPkgLen:\r
526 {\r
527 // Check the parent is a FieldNamed field element.\r
528 if (!AmlNodeCompareOpCode (ParentNode, AML_FIELD_NAMED_OP, 0)) {\r
529 ASSERT (0);\r
530 return EFI_INVALID_PARAMETER;\r
531 }\r
532 break;\r
533 }\r
534 // None and reserved types.\r
535 default:\r
536 {\r
537 ASSERT (0);\r
538 return EFI_INVALID_PARAMETER;\r
539 break;\r
540 }\r
541 } // switch\r
542\r
543 // If the new size is different from the old size, propagate the new size.\r
544 if (DataNode->Size != Size) {\r
545 // Propagate the information.\r
546 Status = AmlPropagateInformation (\r
547 DataNode->NodeHeader.Parent,\r
548 (Size > DataNode->Size) ? TRUE : FALSE,\r
549 (Size > DataNode->Size) ?\r
550 (Size - DataNode->Size) :\r
551 (DataNode->Size - Size),\r
552 0\r
553 );\r
554 if (EFI_ERROR (Status)) {\r
555 ASSERT (0);\r
556 return Status;\r
557 }\r
558\r
559 // Free the old DataNode buffer and allocate a new buffer to store the\r
560 // new data.\r
561 FreePool (DataNode->Buffer);\r
562 DataNode->Buffer = AllocateZeroPool (Size);\r
563 if (DataNode->Buffer == NULL) {\r
564 ASSERT (0);\r
565 return EFI_OUT_OF_RESOURCES;\r
566 }\r
567 DataNode->Size = Size;\r
568 }\r
569\r
570 CopyMem (DataNode->Buffer, Buffer, Size);\r
571\r
572 return EFI_SUCCESS;\r
573}\r