]> git.proxmox.com Git - mirror_edk2.git/blob - DynamicTablesPkg/Library/Common/AmlLib/Tree/AmlNode.c
2c80440a447ad261290a2cf84a8edd3434cf9e5e
[mirror_edk2.git] / DynamicTablesPkg / Library / Common / AmlLib / Tree / AmlNode.c
1 /** @file
2 AML Node.
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 <Tree/AmlNode.h>
10
11 #include <AmlCoreInterface.h>
12 #include <Tree/AmlTree.h>
13
14 /** Initialize an AML_NODE_HEADER structure.
15
16 @param [in] Node Pointer to a node header.
17 @param [in] NodeType NodeType to initialize the Node with.
18 Must be an EAML_NODE_TYPE.
19
20 @retval EFI_SUCCESS The function completed successfully.
21 @retval EFI_INVALID_PARAMETER Invalid parameter.
22 **/
23 STATIC
24 EFI_STATUS
25 EFIAPI
26 AmlInitializeNodeHeader (
27 IN AML_NODE_HEADER * Node,
28 IN EAML_NODE_TYPE NodeType
29 )
30 {
31 if (Node == NULL) {
32 ASSERT (0);
33 return EFI_INVALID_PARAMETER;
34 }
35
36 InitializeListHead (&Node->Link);
37
38 Node->Parent = NULL;
39 Node->NodeType = NodeType;
40
41 return EFI_SUCCESS;
42 }
43
44 /** Delete a root node and its ACPI DSDT/SSDT header.
45
46 It is the caller's responsibility to check the RootNode has been removed
47 from the tree and is not referencing any other node in the tree.
48
49 @param [in] RootNode Pointer to a root node.
50
51 @retval EFI_SUCCESS The function completed successfully.
52 @retval EFI_INVALID_PARAMETER Invalid parameter.
53 **/
54 STATIC
55 EFI_STATUS
56 EFIAPI
57 AmlDeleteRootNode (
58 IN AML_ROOT_NODE * RootNode
59 )
60 {
61 if (!IS_AML_ROOT_NODE (RootNode)) {
62 ASSERT (0);
63 return EFI_INVALID_PARAMETER;
64 }
65
66 if ((RootNode->SdtHeader != NULL)) {
67 FreePool (RootNode->SdtHeader);
68 } else {
69 ASSERT (0);
70 return EFI_INVALID_PARAMETER;
71 }
72
73 FreePool (RootNode);
74 return EFI_SUCCESS;
75 }
76
77 /** Create an AML_ROOT_NODE.
78 This node will be the root of the tree.
79
80 @param [in] SdtHeader Pointer to an ACPI DSDT/SSDT header to copy
81 the data from.
82 @param [out] NewRootNodePtr The created AML_ROOT_NODE.
83
84 @retval EFI_SUCCESS The function completed successfully.
85 @retval EFI_INVALID_PARAMETER Invalid parameter.
86 @retval EFI_OUT_OF_RESOURCES Could not allocate memory.
87 **/
88 EFI_STATUS
89 EFIAPI
90 AmlCreateRootNode (
91 IN CONST EFI_ACPI_DESCRIPTION_HEADER * SdtHeader,
92 OUT AML_ROOT_NODE ** NewRootNodePtr
93 )
94 {
95 EFI_STATUS Status;
96 AML_ROOT_NODE * RootNode;
97
98 if ((SdtHeader == NULL) ||
99 (NewRootNodePtr == NULL)) {
100 ASSERT (0);
101 return EFI_INVALID_PARAMETER;
102 }
103
104 RootNode = AllocateZeroPool (sizeof (AML_ROOT_NODE));
105 if (RootNode == NULL) {
106 ASSERT (0);
107 return EFI_OUT_OF_RESOURCES;
108 }
109
110 Status = AmlInitializeNodeHeader (&RootNode->NodeHeader, EAmlNodeRoot);
111 if (EFI_ERROR (Status)) {
112 FreePool (RootNode);
113 ASSERT (0);
114 return Status;
115 }
116
117 InitializeListHead (&RootNode->VariableArgs);
118
119 RootNode->SdtHeader = AllocateCopyPool (
120 sizeof (EFI_ACPI_DESCRIPTION_HEADER),
121 SdtHeader
122 );
123 if (RootNode->SdtHeader == NULL) {
124 ASSERT (0);
125 AmlDeleteRootNode (RootNode);
126 return EFI_OUT_OF_RESOURCES;
127 }
128
129 *NewRootNodePtr = RootNode;
130
131 return EFI_SUCCESS;
132 }
133
134 /** Delete an object node.
135
136 It is the caller's responsibility to check the ObjectNode has been removed
137 from the tree and is not referencing any other node in the tree.
138
139 @param [in] ObjectNode Pointer to an object node.
140
141 @retval EFI_SUCCESS The function completed successfully.
142 @retval EFI_INVALID_PARAMETER Invalid parameter.
143 **/
144 STATIC
145 EFI_STATUS
146 EFIAPI
147 AmlDeleteObjectNode (
148 IN AML_OBJECT_NODE * ObjectNode
149 )
150 {
151 if (!IS_AML_OBJECT_NODE (ObjectNode)) {
152 ASSERT (0);
153 return EFI_INVALID_PARAMETER;
154 }
155
156 FreePool (ObjectNode);
157 return EFI_SUCCESS;
158 }
159
160 /** Create an AML_OBJECT_NODE.
161
162 @param [in] AmlByteEncoding Byte encoding entry.
163 @param [in] PkgLength PkgLength of the node if the AmlByteEncoding
164 has the PkgLen attribute.
165 0 otherwise.
166 @param [out] NewObjectNodePtr The created AML_OBJECT_NODE.
167
168 @retval EFI_SUCCESS The function completed successfully.
169 @retval EFI_INVALID_PARAMETER Invalid parameter.
170 @retval EFI_OUT_OF_RESOURCES Could not allocate memory.
171 **/
172 EFI_STATUS
173 EFIAPI
174 AmlCreateObjectNode (
175 IN CONST AML_BYTE_ENCODING * AmlByteEncoding,
176 IN UINT32 PkgLength,
177 OUT AML_OBJECT_NODE ** NewObjectNodePtr
178 )
179 {
180 EFI_STATUS Status;
181 AML_OBJECT_NODE * ObjectNode;
182
183 if ((AmlByteEncoding == NULL) ||
184 (NewObjectNodePtr == NULL)) {
185 ASSERT (0);
186 return EFI_INVALID_PARAMETER;
187 }
188
189 ObjectNode = AllocateZeroPool (sizeof (AML_OBJECT_NODE));
190 if (ObjectNode == NULL) {
191 ASSERT (0);
192 return EFI_OUT_OF_RESOURCES;
193 }
194
195 Status = AmlInitializeNodeHeader (&ObjectNode->NodeHeader, EAmlNodeObject);
196 if (EFI_ERROR (Status)) {
197 FreePool (ObjectNode);
198 ASSERT (0);
199 return Status;
200 }
201
202 InitializeListHead (&ObjectNode->VariableArgs);
203
204 // ObjectNode->FixedArgs[...] is already initialised to NULL as the
205 // ObjectNode is Zero allocated.
206 ObjectNode->AmlByteEncoding = AmlByteEncoding;
207 ObjectNode->PkgLen = PkgLength;
208
209 *NewObjectNodePtr = ObjectNode;
210
211 return EFI_SUCCESS;
212 }
213
214 /** Delete a data node and its buffer.
215
216 It is the caller's responsibility to check the DataNode has been removed
217 from the tree and is not referencing any other node in the tree.
218
219 @param [in] DataNode Pointer to a data node.
220
221 @retval EFI_SUCCESS The function completed successfully.
222 @retval EFI_INVALID_PARAMETER Invalid parameter.
223 @retval EFI_OUT_OF_RESOURCES Could not allocate memory.
224 **/
225 STATIC
226 EFI_STATUS
227 EFIAPI
228 AmlDeleteDataNode (
229 IN AML_DATA_NODE * DataNode
230 )
231 {
232 if (!IS_AML_DATA_NODE (DataNode)) {
233 ASSERT (0);
234 return EFI_INVALID_PARAMETER;
235 }
236
237 if (DataNode->Buffer != NULL) {
238 FreePool (DataNode->Buffer);
239 } else {
240 ASSERT (0);
241 return EFI_INVALID_PARAMETER;
242 }
243
244 FreePool (DataNode);
245 return EFI_SUCCESS;
246 }
247
248 /** Create an AML_DATA_NODE.
249
250 @param [in] DataType DataType of the node.
251 @param [in] Data Pointer to the AML bytecode corresponding to
252 this node. Data is copied from there.
253 @param [in] DataSize Number of bytes to consider at the address
254 pointed by Data.
255 @param [out] NewDataNodePtr The created AML_DATA_NODE.
256
257 @retval EFI_SUCCESS The function completed successfully.
258 @retval EFI_INVALID_PARAMETER Invalid parameter.
259 @retval EFI_OUT_OF_RESOURCES Could not allocate memory.
260 **/
261 EFI_STATUS
262 EFIAPI
263 AmlCreateDataNode (
264 IN EAML_NODE_DATA_TYPE DataType,
265 IN CONST UINT8 * Data,
266 IN UINT32 DataSize,
267 OUT AML_DATA_NODE ** NewDataNodePtr
268 )
269 {
270 EFI_STATUS Status;
271 AML_DATA_NODE * DataNode;
272
273 // A data node must not be created for certain data types.
274 if ((DataType == EAmlNodeDataTypeNone) ||
275 (DataType == EAmlNodeDataTypeReserved1) ||
276 (DataType == EAmlNodeDataTypeReserved2) ||
277 (DataType == EAmlNodeDataTypeReserved3) ||
278 (DataType == EAmlNodeDataTypeReserved4) ||
279 (DataType == EAmlNodeDataTypeReserved5) ||
280 (Data == NULL) ||
281 (DataSize == 0) ||
282 (NewDataNodePtr == NULL)) {
283 ASSERT (0);
284 return EFI_INVALID_PARAMETER;
285 }
286
287 DataNode = AllocateZeroPool (sizeof (AML_DATA_NODE));
288 if (DataNode == NULL) {
289 ASSERT (0);
290 return EFI_OUT_OF_RESOURCES;
291 }
292
293 Status = AmlInitializeNodeHeader (&DataNode->NodeHeader, EAmlNodeData);
294 if (EFI_ERROR (Status)) {
295 FreePool (DataNode);
296 ASSERT (0);
297 return Status;
298 }
299
300 DataNode->Buffer = AllocateCopyPool (DataSize, Data);
301 if (DataNode->Buffer == NULL) {
302 AmlDeleteDataNode (DataNode);
303 ASSERT (0);
304 return EFI_OUT_OF_RESOURCES;
305 }
306
307 DataNode->DataType = DataType;
308 DataNode->Size = DataSize;
309
310 *NewDataNodePtr = DataNode;
311
312 return EFI_SUCCESS;
313 }
314
315 /** Delete a Node.
316
317 @param [in] Node Pointer to a Node.
318
319 @retval EFI_SUCCESS The function completed successfully.
320 @retval EFI_INVALID_PARAMETER Invalid parameter.
321 **/
322 EFI_STATUS
323 EFIAPI
324 AmlDeleteNode (
325 IN AML_NODE_HEADER * Node
326 )
327 {
328 EFI_STATUS Status;
329 EAML_PARSE_INDEX Index;
330
331 // Check that the node being deleted is unlinked.
332 // When removing the node, its parent and list are reset
333 // with InitializeListHead. Thus it must be empty.
334 if (!IS_AML_NODE_VALID (Node) ||
335 !AML_NODE_IS_DETACHED (Node)) {
336 ASSERT (0);
337 return EFI_INVALID_PARAMETER;
338 }
339
340 switch (Node->NodeType) {
341 case EAmlNodeRoot:
342 {
343 // Check the variable list of arguments has been cleaned.
344 if (!IsListEmpty (AmlNodeGetVariableArgList (Node))) {
345 ASSERT (0);
346 return EFI_INVALID_PARAMETER;
347 }
348
349 Status = AmlDeleteRootNode ((AML_ROOT_NODE*)Node);
350 if (EFI_ERROR (Status)) {
351 ASSERT (0);
352 }
353 break;
354 }
355
356 case EAmlNodeObject:
357 {
358 // Check the variable list of arguments has been cleaned.
359 if (!IsListEmpty (AmlNodeGetVariableArgList (Node))) {
360 ASSERT (0);
361 return EFI_INVALID_PARAMETER;
362 }
363
364 // Check the fixed argument list has been cleaned.
365 for (Index = EAmlParseIndexTerm0; Index < EAmlParseIndexMax; Index++) {
366 if (((AML_OBJECT_NODE*)Node)->FixedArgs[Index] != NULL) {
367 ASSERT (0);
368 return EFI_INVALID_PARAMETER;
369 }
370 }
371
372 Status = AmlDeleteObjectNode ((AML_OBJECT_NODE*)Node);
373 if (EFI_ERROR (Status)) {
374 ASSERT (0);
375 }
376 break;
377 }
378
379 case EAmlNodeData:
380 {
381 Status = AmlDeleteDataNode ((AML_DATA_NODE*)Node);
382 if (EFI_ERROR (Status)) {
383 ASSERT (0);
384 }
385 break;
386 }
387
388 default:
389 {
390 ASSERT (0);
391 Status = EFI_INVALID_PARAMETER;
392 break;
393 }
394 } // switch
395
396 return Status;
397 }
398
399 /** Check whether ObjectNode has the input attribute.
400 This function can be used to check ObjectNode is an object node
401 at the same time.
402
403 @param [in] ObjectNode Pointer to an object node.
404 @param [in] Attribute Attribute to check for.
405
406 @retval TRUE The node is an AML object and the attribute is present.
407 @retval FALSE Otherwise.
408 **/
409 BOOLEAN
410 EFIAPI
411 AmlNodeHasAttribute (
412 IN CONST AML_OBJECT_NODE * ObjectNode,
413 IN AML_OP_ATTRIBUTE Attribute
414 )
415 {
416 if (!IS_AML_OBJECT_NODE (ObjectNode) ||
417 (ObjectNode->AmlByteEncoding == NULL)) {
418 return FALSE;
419 }
420
421 return ((ObjectNode->AmlByteEncoding->Attribute &
422 Attribute) == 0 ? FALSE : TRUE);
423 }
424
425 /** Check whether ObjectNode has the input OpCode/SubOpcode couple.
426
427 @param [in] ObjectNode Pointer to an object node.
428 @param [in] OpCode OpCode to check
429 @param [in] SubOpCode SubOpCode to check
430
431 @retval TRUE The node is an AML object and
432 the Opcode and the SubOpCode match.
433 @retval FALSE Otherwise.
434 **/
435 BOOLEAN
436 EFIAPI
437 AmlNodeCompareOpCode (
438 IN CONST AML_OBJECT_NODE * ObjectNode,
439 IN UINT8 OpCode,
440 IN UINT8 SubOpCode
441 )
442 {
443 if (!IS_AML_OBJECT_NODE (ObjectNode) ||
444 (ObjectNode->AmlByteEncoding == NULL)) {
445 return FALSE;
446 }
447
448 ASSERT (AmlIsOpCodeValid (OpCode, SubOpCode));
449
450 return ((ObjectNode->AmlByteEncoding->OpCode == OpCode) &&
451 (ObjectNode->AmlByteEncoding->SubOpCode == SubOpCode)) ?
452 TRUE : FALSE;
453 }
454
455 /** Check whether a Node is an integer node.
456
457 By integer node we mean an object node having one of the following opcode:
458 - AML_BYTE_PREFIX;
459 - AML_WORD_PREFIX;
460 - AML_DWORD_PREFIX;
461 - AML_QWORD_PREFIX.
462
463 @param [in] Node The node to check.
464
465 @retval TRUE The Node is an integer node.
466 @retval FALSE Otherwise.
467 */
468 BOOLEAN
469 EFIAPI
470 IsIntegerNode (
471 IN AML_OBJECT_NODE * Node
472 )
473 {
474 UINT8 OpCode;
475
476 if (!IS_AML_OBJECT_NODE (Node) ||
477 (Node->AmlByteEncoding == NULL)) {
478 return FALSE;
479 }
480
481 // Check Node is an integer node.
482 OpCode = Node->AmlByteEncoding->OpCode;
483 if ((OpCode != AML_BYTE_PREFIX) &&
484 (OpCode != AML_WORD_PREFIX) &&
485 (OpCode != AML_DWORD_PREFIX) &&
486 (OpCode != AML_QWORD_PREFIX)) {
487 return FALSE;
488 }
489
490 return TRUE;
491 }
492
493 /** Check whether a Node is a ZeroOp, a OneOp or a OnesOp.
494
495 These two objects don't have a data node holding
496 a value. This require special handling.
497
498 @param [in] Node The node to check.
499
500 @retval TRUE The Node is a ZeroOp or OneOp.
501 @retval FALSE Otherwise.
502 */
503 BOOLEAN
504 EFIAPI
505 IsSpecialIntegerNode (
506 IN AML_OBJECT_NODE * Node
507 )
508 {
509 UINT8 OpCode;
510
511 if (!IS_AML_OBJECT_NODE (Node) ||
512 (Node->AmlByteEncoding == NULL)) {
513 return FALSE;
514 }
515
516 OpCode = Node->AmlByteEncoding->OpCode;
517
518 if ((OpCode != AML_ZERO_OP) &&
519 (OpCode != AML_ONE_OP) &&
520 (OpCode != AML_ONES_OP)) {
521 return FALSE;
522 }
523
524 return TRUE;
525 }
526
527 /** Check whether Node corresponds to a method definition.
528
529 A method definition can be introduced:
530 - By a method object, having an AML_METHOD_OP OpCode;
531 - By an external definition of a method, having an AML_EXTERNAL_OP OpCode
532 and an ObjectType byte set to the MethodObj.
533
534 Note:
535 An alias node, having an AML_ALIAS_OP, can be resolved to a method
536 definition. This function doesn't handle this case.
537
538 @param [in] Node Node to check whether it is a method definition.
539
540 @retval TRUE The Node is a method definition.
541 @retval FALSE Otherwise.
542 **/
543 BOOLEAN
544 EFIAPI
545 AmlIsMethodDefinitionNode (
546 IN CONST AML_OBJECT_NODE * Node
547 )
548 {
549 AML_DATA_NODE * ObjectType;
550
551 // Node is checked to be an object node aswell.
552 if (AmlNodeCompareOpCode (Node, AML_METHOD_OP, 0)) {
553 return TRUE;
554 } else if (AmlNodeCompareOpCode (Node, AML_EXTERNAL_OP, 0)) {
555 // If the node is an external definition, check this is a method.
556 // DefExternal := ExternalOp NameString ObjectType ArgumentCount
557 // ExternalOp := 0x15
558 // ObjectType := ByteData
559 // ArgumentCount := ByteData (0 – 7)
560 ObjectType = (AML_DATA_NODE*)AmlGetFixedArgument (
561 (AML_OBJECT_NODE*)Node,
562 EAmlParseIndexTerm1
563 );
564 if (IS_AML_DATA_NODE (ObjectType) &&
565 (ObjectType->DataType == EAmlNodeDataTypeUInt) &&
566 ((ObjectType->Size == 1))) {
567 if (*((UINT8*)ObjectType->Buffer) == (UINT8)EAmlObjTypeMethodObj) {
568 // The external definition is a method.
569 return TRUE;
570 } else {
571 // The external definition is not a method.
572 return FALSE;
573 }
574 } else {
575 // The tree is inconsistent.
576 ASSERT (0);
577 return FALSE;
578 }
579 }
580
581 // This is not a method definition.
582 return FALSE;
583 }
584
585 /** Get the index at which the name of the node is stored.
586
587 @param [in] ObjectNode Pointer to an object node.
588 Must have the AML_IN_NAMESPACE attribute.
589 @param [out] Index Index of the name in the fixed list of arguments.
590
591 @retval EFI_SUCCESS The function completed successfully.
592 @retval EFI_INVALID_PARAMETER Invalid parameter.
593 **/
594 EFI_STATUS
595 AmlNodeGetNameIndex (
596 IN CONST AML_OBJECT_NODE * ObjectNode,
597 OUT EAML_PARSE_INDEX * Index
598 )
599 {
600 EAML_PARSE_INDEX NameIndex;
601
602 if (!AmlNodeHasAttribute (ObjectNode, AML_IN_NAMESPACE) ||
603 (ObjectNode->AmlByteEncoding == NULL) ||
604 (Index == NULL)) {
605 ASSERT (0);
606 return EFI_INVALID_PARAMETER;
607 }
608
609 NameIndex = ObjectNode->AmlByteEncoding->NameIndex;
610
611 if ((NameIndex > ObjectNode->AmlByteEncoding->MaxIndex) ||
612 (ObjectNode->AmlByteEncoding->Format[NameIndex] != EAmlName)) {
613 ASSERT (0);
614 return EFI_INVALID_PARAMETER;
615 }
616
617 *Index = NameIndex;
618
619 return EFI_SUCCESS;
620 }
621
622 /** Get the name of the Node.
623
624 Node must be part of the namespace.
625
626 @param [in] ObjectNode Pointer to an object node,
627 which is part of the namespace.
628
629 @return A pointer to the name.
630 NULL otherwise.
631 Return NULL for the root node.
632 **/
633 CHAR8 *
634 EFIAPI
635 AmlNodeGetName (
636 IN CONST AML_OBJECT_NODE * ObjectNode
637 )
638 {
639 EFI_STATUS Status;
640 EAML_PARSE_INDEX NameIndex;
641 AML_DATA_NODE * DataNode;
642
643 if (!AmlNodeHasAttribute (ObjectNode, AML_IN_NAMESPACE)) {
644 ASSERT (0);
645 return NULL;
646 }
647
648 // Get the index at which the name is stored in the fixed arguments list.
649 Status = AmlNodeGetNameIndex (ObjectNode, &NameIndex);
650 if (EFI_ERROR (Status)) {
651 ASSERT (0);
652 return NULL;
653 }
654
655 // The name is stored in a Data node.
656 DataNode = (AML_DATA_NODE*)ObjectNode->FixedArgs[NameIndex];
657 if (IS_AML_DATA_NODE (DataNode) &&
658 (DataNode->DataType == EAmlNodeDataTypeNameString)) {
659 return (CHAR8*)DataNode->Buffer;
660 }
661
662 /* Return NULL if no name is found.
663 This can occur if the name of a node is defined as a further
664 fixed argument.
665 E.g.: CreateField (BD03, 0x28, Add (ID03 + 0x08), BF33)
666 ^
667 The parser is here.
668 The parent of the Add statement is the CreateField statement. This
669 statement defines a name in the AML namespace. This name defined as
670 the fourth fixed argument. It hasn't been parsed yet.
671 */
672 return NULL;
673 }