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