]> git.proxmox.com Git - mirror_edk2.git/blob - DynamicTablesPkg/Library/Common/AmlLib/Utils/AmlUtility.c
DynamicTablesPkg: Apply uncrustify changes
[mirror_edk2.git] / DynamicTablesPkg / Library / Common / AmlLib / Utils / AmlUtility.c
1 /** @file
2 AML Utility.
3
4 Copyright (c) 2019 - 2021, Arm Limited. All rights reserved.<BR>
5
6 SPDX-License-Identifier: BSD-2-Clause-Patent
7 **/
8
9 #include <Utils/AmlUtility.h>
10
11 #include <AmlCoreInterface.h>
12 #include <Tree/AmlNode.h>
13 #include <Tree/AmlTree.h>
14
15 /** This function computes and updates the ACPI table checksum.
16
17 @param [in] AcpiTable Pointer to an Acpi table.
18
19 @retval EFI_SUCCESS The function completed successfully.
20 @retval EFI_INVALID_PARAMETER Invalid parameter.
21 **/
22 EFI_STATUS
23 EFIAPI
24 AcpiPlatformChecksum (
25 IN EFI_ACPI_DESCRIPTION_HEADER *AcpiTable
26 )
27 {
28 UINT8 *Ptr;
29 UINT8 Sum;
30 UINT32 Size;
31
32 if (AcpiTable == NULL) {
33 ASSERT (0);
34 return EFI_INVALID_PARAMETER;
35 }
36
37 Ptr = (UINT8 *)AcpiTable;
38 Size = AcpiTable->Length;
39 Sum = 0;
40
41 // Set the checksum field to 0 first.
42 AcpiTable->Checksum = 0;
43
44 // Compute the checksum.
45 while ((Size--) != 0) {
46 Sum = (UINT8)(Sum + (*Ptr++));
47 }
48
49 // Set the checksum.
50 AcpiTable->Checksum = (UINT8)(0xFF - Sum + 1);
51
52 return EFI_SUCCESS;
53 }
54
55 /** A callback function that computes the size of a Node and adds it to the
56 Size pointer stored in the Context.
57 Calling this function on the root node will compute the total size of the
58 AML bytestream.
59
60 @param [in] Node Node to compute the size.
61 @param [in, out] Context Pointer holding the computed size.
62 (UINT32 *) Context.
63 @param [in, out] Status Pointer holding:
64 - At entry, the Status returned by the
65 last call to this exact function during
66 the enumeration;
67 - At exit, he returned status of the
68 call to this function.
69 Optional, can be NULL.
70
71 @retval TRUE if the enumeration can continue or has finished without
72 interruption.
73 @retval FALSE if the enumeration needs to stopped or has stopped.
74 **/
75 STATIC
76 BOOLEAN
77 EFIAPI
78 AmlComputeSizeCallback (
79 IN AML_NODE_HEADER *Node,
80 IN OUT VOID *Context,
81 IN OUT EFI_STATUS *Status OPTIONAL
82 )
83 {
84 UINT32 Size;
85 EAML_PARSE_INDEX IndexPtr;
86 CONST AML_OBJECT_NODE *ParentNode;
87
88 if (!IS_AML_NODE_VALID (Node) ||
89 (Context == NULL))
90 {
91 ASSERT (0);
92 if (Status != NULL) {
93 *Status = EFI_INVALID_PARAMETER;
94 }
95
96 return FALSE;
97 }
98
99 // Ignore the second fixed argument of method invocation nodes
100 // as the information stored there (the argument count) is not in the
101 // ACPI specification.
102 ParentNode = (CONST AML_OBJECT_NODE *)AmlGetParent (Node);
103 if (IS_AML_OBJECT_NODE (ParentNode) &&
104 AmlNodeCompareOpCode (ParentNode, AML_METHOD_INVOC_OP, 0) &&
105 AmlIsNodeFixedArgument (Node, &IndexPtr))
106 {
107 if (IndexPtr == EAmlParseIndexTerm1) {
108 if (Status != NULL) {
109 *Status = EFI_SUCCESS;
110 }
111
112 return TRUE;
113 }
114 }
115
116 Size = *((UINT32 *)Context);
117
118 if (IS_AML_DATA_NODE (Node)) {
119 Size += ((AML_DATA_NODE *)Node)->Size;
120 } else if (IS_AML_OBJECT_NODE (Node) &&
121 !AmlNodeHasAttribute (
122 (CONST AML_OBJECT_NODE *)Node,
123 AML_IS_PSEUDO_OPCODE
124 ))
125 {
126 // Ignore pseudo-opcodes as they are not part of the
127 // ACPI specification.
128
129 Size += (((AML_OBJECT_NODE *)Node)->AmlByteEncoding->OpCode ==
130 AML_EXT_OP) ? 2 : 1;
131
132 // Add the size of the PkgLen.
133 if (AmlNodeHasAttribute (
134 (AML_OBJECT_NODE *)Node,
135 AML_HAS_PKG_LENGTH
136 ))
137 {
138 Size += AmlComputePkgLengthWidth (((AML_OBJECT_NODE *)Node)->PkgLen);
139 }
140 }
141
142 // Check for overflow.
143 // The root node has a null size, thus the strict comparison.
144 if (*((UINT32 *)Context) > Size) {
145 ASSERT (0);
146 *Status = EFI_INVALID_PARAMETER;
147 return FALSE;
148 }
149
150 *((UINT32 *)Context) = Size;
151
152 if (Status != NULL) {
153 *Status = EFI_SUCCESS;
154 }
155
156 return TRUE;
157 }
158
159 /** Compute the size of a tree/sub-tree.
160
161 @param [in] Node Node to compute the size.
162 @param [in, out] Size Pointer holding the computed size.
163
164 @retval EFI_SUCCESS The function completed successfully.
165 @retval EFI_INVALID_PARAMETER Invalid parameter.
166 **/
167 EFI_STATUS
168 EFIAPI
169 AmlComputeSize (
170 IN CONST AML_NODE_HEADER *Node,
171 IN OUT UINT32 *Size
172 )
173 {
174 EFI_STATUS Status;
175
176 if (!IS_AML_NODE_VALID (Node) ||
177 (Size == NULL))
178 {
179 ASSERT (0);
180 return EFI_INVALID_PARAMETER;
181 }
182
183 *Size = 0;
184
185 AmlEnumTree (
186 (AML_NODE_HEADER *)Node,
187 AmlComputeSizeCallback,
188 (VOID *)Size,
189 &Status
190 );
191
192 return Status;
193 }
194
195 /** Get the value contained in an integer node.
196
197 @param [in] Node Pointer to an integer node.
198 Must be an object node.
199 @param [out] Value Value contained in the integer node.
200
201 @retval EFI_SUCCESS The function completed successfully.
202 @retval EFI_INVALID_PARAMETER Invalid parameter.
203 **/
204 EFI_STATUS
205 EFIAPI
206 AmlNodeGetIntegerValue (
207 IN AML_OBJECT_NODE *Node,
208 OUT UINT64 *Value
209 )
210 {
211 AML_DATA_NODE *DataNode;
212
213 if ((!IsIntegerNode (Node) &&
214 !IsSpecialIntegerNode (Node)) ||
215 (Value == NULL))
216 {
217 ASSERT (0);
218 return EFI_INVALID_PARAMETER;
219 }
220
221 // For ZeroOp and OneOp, there is no data node.
222 if (IsSpecialIntegerNode (Node)) {
223 if (AmlNodeCompareOpCode (Node, AML_ZERO_OP, 0)) {
224 *Value = 0;
225 } else if (AmlNodeCompareOpCode (Node, AML_ONE_OP, 0)) {
226 *Value = 1;
227 } else {
228 // OnesOp cannot be handled: it represents a maximum value.
229 ASSERT (0);
230 return EFI_INVALID_PARAMETER;
231 }
232
233 return EFI_SUCCESS;
234 }
235
236 // For integer nodes, the value is in the first fixed argument.
237 DataNode = (AML_DATA_NODE *)Node->FixedArgs[EAmlParseIndexTerm0];
238 if (!IS_AML_DATA_NODE (DataNode) ||
239 (DataNode->DataType != EAmlNodeDataTypeUInt))
240 {
241 ASSERT (0);
242 return EFI_INVALID_PARAMETER;
243 }
244
245 switch (DataNode->Size) {
246 case 1:
247 {
248 *Value = *((UINT8 *)(DataNode->Buffer));
249 break;
250 }
251 case 2:
252 {
253 *Value = *((UINT16 *)(DataNode->Buffer));
254 break;
255 }
256 case 4:
257 {
258 *Value = *((UINT32 *)(DataNode->Buffer));
259 break;
260 }
261 case 8:
262 {
263 *Value = *((UINT64 *)(DataNode->Buffer));
264 break;
265 }
266 default:
267 {
268 ASSERT (0);
269 return EFI_INVALID_PARAMETER;
270 }
271 } // switch
272
273 return EFI_SUCCESS;
274 }
275
276 /** Replace a Zero (AML_ZERO_OP) or One (AML_ONE_OP) object node
277 with a byte integer (AML_BYTE_PREFIX) object node having the same value.
278
279 @param [in] Node Pointer to an integer node.
280 Must be an object node having ZeroOp or OneOp.
281
282 @retval EFI_SUCCESS The function completed successfully.
283 @retval EFI_INVALID_PARAMETER Invalid parameter.
284 **/
285 STATIC
286 EFI_STATUS
287 EFIAPI
288 AmlUnwindSpecialInteger (
289 IN AML_OBJECT_NODE *Node
290 )
291 {
292 EFI_STATUS Status;
293
294 AML_DATA_NODE *NewDataNode;
295 UINT8 Value;
296 CONST AML_BYTE_ENCODING *ByteEncoding;
297
298 if (!IsSpecialIntegerNode (Node)) {
299 ASSERT (0);
300 return EFI_INVALID_PARAMETER;
301 }
302
303 // Find the value.
304 if (AmlNodeCompareOpCode (Node, AML_ZERO_OP, 0)) {
305 Value = 0;
306 } else if (AmlNodeCompareOpCode (Node, AML_ONE_OP, 0)) {
307 Value = 1;
308 } else {
309 // OnesOp cannot be handled: it represents a maximum value.
310 ASSERT (0);
311 return EFI_INVALID_PARAMETER;
312 }
313
314 Status = AmlCreateDataNode (
315 EAmlNodeDataTypeUInt,
316 &Value,
317 sizeof (UINT8),
318 (AML_DATA_NODE **)&NewDataNode
319 );
320 if (EFI_ERROR (Status)) {
321 ASSERT (0);
322 return Status;
323 }
324
325 // Change the encoding of the special node to a ByteOp encoding.
326 ByteEncoding = AmlGetByteEncodingByOpCode (AML_BYTE_PREFIX, 0);
327 if (ByteEncoding == NULL) {
328 ASSERT (0);
329 Status = EFI_INVALID_PARAMETER;
330 goto error_handler;
331 }
332
333 // Update the ByteEncoding from ZERO_OP/ONE_OP to AML_BYTE_PREFIX.
334 Node->AmlByteEncoding = ByteEncoding;
335
336 // Add the data node as the first fixed argument of the ByteOp object.
337 Status = AmlSetFixedArgument (
338 (AML_OBJECT_NODE *)Node,
339 EAmlParseIndexTerm0,
340 (AML_NODE_HEADER *)NewDataNode
341 );
342 if (EFI_ERROR (Status)) {
343 ASSERT (0);
344 goto error_handler;
345 }
346
347 return Status;
348
349 error_handler:
350 AmlDeleteTree ((AML_NODE_HEADER *)NewDataNode);
351 return Status;
352 }
353
354 /** Set the value contained in an integer node.
355
356 The OpCode is updated accordingly to the new value
357 (e.g.: If the original value was a UINT8 value, then the OpCode
358 would be AML_BYTE_PREFIX. If it the new value is a UINT16
359 value then the OpCode will be updated to AML_WORD_PREFIX).
360
361 @param [in] Node Pointer to an integer node.
362 Must be an object node.
363 @param [in] NewValue New value to write in the integer node.
364 @param [out] ValueWidthDiff Difference in number of bytes used to store
365 the new value.
366 Can be negative.
367
368 @retval EFI_SUCCESS The function completed successfully.
369 @retval EFI_INVALID_PARAMETER Invalid parameter.
370 @retval EFI_OUT_OF_RESOURCES Could not allocate memory.
371 **/
372 EFI_STATUS
373 EFIAPI
374 AmlNodeSetIntegerValue (
375 IN AML_OBJECT_NODE *Node,
376 IN UINT64 NewValue,
377 OUT INT8 *ValueWidthDiff
378 )
379 {
380 EFI_STATUS Status;
381 AML_DATA_NODE *DataNode;
382
383 UINT8 NewOpCode;
384 UINT8 NumberOfBytes;
385
386 if ((!IsIntegerNode (Node) &&
387 !IsSpecialIntegerNode (Node)) ||
388 (ValueWidthDiff == NULL))
389 {
390 ASSERT (0);
391 return EFI_INVALID_PARAMETER;
392 }
393
394 *ValueWidthDiff = 0;
395 // For ZeroOp and OneOp, there is no data node.
396 // Thus the object node is converted to a byte object node holding 0 or 1.
397 if (IsSpecialIntegerNode (Node)) {
398 switch (NewValue) {
399 case AML_ZERO_OP:
400 Node->AmlByteEncoding = AmlGetByteEncodingByOpCode (AML_ZERO_OP, 0);
401 return EFI_SUCCESS;
402 case AML_ONE_OP:
403 Node->AmlByteEncoding = AmlGetByteEncodingByOpCode (AML_ONE_OP, 0);
404 return EFI_SUCCESS;
405 default:
406 {
407 Status = AmlUnwindSpecialInteger (Node);
408 if (EFI_ERROR (Status)) {
409 ASSERT (0);
410 return Status;
411 }
412
413 // The AmlUnwindSpecialInteger functions converts a special integer
414 // node to a UInt8/Byte data node. Thus, the size increments by one:
415 // special integer are encoded as one byte (the opcode only) while byte
416 // integers are encoded as two bytes (the opcode + the value).
417 *ValueWidthDiff += sizeof (UINT8);
418 }
419 } // switch
420 } // IsSpecialIntegerNode (Node)
421
422 // For integer nodes, the value is in the first fixed argument.
423 DataNode = (AML_DATA_NODE *)Node->FixedArgs[EAmlParseIndexTerm0];
424 if (!IS_AML_DATA_NODE (DataNode) ||
425 (DataNode->DataType != EAmlNodeDataTypeUInt))
426 {
427 ASSERT (0);
428 return EFI_INVALID_PARAMETER;
429 }
430
431 // The value can be encoded with a special 0 or 1 OpCode.
432 // The AML_ONES_OP is not handled.
433 if (NewValue <= 1) {
434 NewOpCode = (NewValue == 0) ? AML_ZERO_OP : AML_ONE_OP;
435 Node->AmlByteEncoding = AmlGetByteEncodingByOpCode (NewOpCode, 0);
436
437 // The value is encoded with a AML_ZERO_OP or AML_ONE_OP.
438 // This means there is no need for a DataNode containing the value.
439 // The change in size is equal to the size of the DataNode's buffer.
440 *ValueWidthDiff = -((INT8)DataNode->Size);
441
442 // Detach and free the DataNode containing the integer value.
443 DataNode->NodeHeader.Parent = NULL;
444 Node->FixedArgs[EAmlParseIndexTerm0] = NULL;
445 Status = AmlDeleteNode ((AML_NODE_HEADER *)DataNode);
446 if (EFI_ERROR (Status)) {
447 ASSERT (0);
448 return Status;
449 }
450
451 return EFI_SUCCESS;
452 }
453
454 // Check the number of bits needed to represent the value.
455 if (NewValue > MAX_UINT32) {
456 // Value is 64 bits.
457 NewOpCode = AML_QWORD_PREFIX;
458 NumberOfBytes = 8;
459 } else if (NewValue > MAX_UINT16) {
460 // Value is 32 bits.
461 NewOpCode = AML_DWORD_PREFIX;
462 NumberOfBytes = 4;
463 } else if (NewValue > MAX_UINT8) {
464 // Value is 16 bits.
465 NewOpCode = AML_WORD_PREFIX;
466 NumberOfBytes = 2;
467 } else {
468 // Value is 8 bits.
469 NewOpCode = AML_BYTE_PREFIX;
470 NumberOfBytes = 1;
471 }
472
473 *ValueWidthDiff += (INT8)(NumberOfBytes - DataNode->Size);
474
475 // Update the ByteEncoding as it may have changed between [8 .. 64] bits.
476 Node->AmlByteEncoding = AmlGetByteEncodingByOpCode (NewOpCode, 0);
477 if (Node->AmlByteEncoding == NULL) {
478 ASSERT (0);
479 return EFI_INVALID_PARAMETER;
480 }
481
482 // Free the old DataNode buffer and allocate a buffer with the right size
483 // to store the new data.
484 if (*ValueWidthDiff != 0) {
485 FreePool (DataNode->Buffer);
486 DataNode->Buffer = AllocateZeroPool (NumberOfBytes);
487 if (DataNode->Buffer == NULL) {
488 ASSERT (0);
489 return EFI_OUT_OF_RESOURCES;
490 }
491
492 DataNode->Size = NumberOfBytes;
493 }
494
495 // Write the new value.
496 CopyMem (DataNode->Buffer, &NewValue, NumberOfBytes);
497
498 return EFI_SUCCESS;
499 }
500
501 /** Increment/decrement the value contained in the IntegerNode.
502
503 @param [in] IntegerNode Pointer to an object node containing
504 an integer.
505 @param [in] IsIncrement Choose the operation to do:
506 - TRUE: Increment the Node's size and
507 the Node's count;
508 - FALSE: Decrement the Node's size and
509 the Node's count.
510 @param [in] Diff Value to add/subtract to the integer.
511 @param [out] ValueWidthDiff When modifying the integer, it can be
512 promoted/demoted, e.g. from UINT8 to UINT16.
513 Stores the change in width.
514 Can be negative.
515
516 @retval EFI_SUCCESS The function completed successfully.
517 @retval EFI_INVALID_PARAMETER Invalid parameter.
518 **/
519 STATIC
520 EFI_STATUS
521 EFIAPI
522 AmlNodeUpdateIntegerValue (
523 IN AML_OBJECT_NODE *IntegerNode,
524 IN BOOLEAN IsIncrement,
525 IN UINT64 Diff,
526 OUT INT8 *ValueWidthDiff
527 )
528 {
529 EFI_STATUS Status;
530 UINT64 Value;
531
532 if (ValueWidthDiff == NULL) {
533 ASSERT (0);
534 return EFI_INVALID_PARAMETER;
535 }
536
537 // Get the current value.
538 // Checks on the IntegerNode are done in the call.
539 Status = AmlNodeGetIntegerValue (IntegerNode, &Value);
540 if (EFI_ERROR (Status)) {
541 ASSERT (0);
542 return Status;
543 }
544
545 // Check for UINT64 over/underflow.
546 if ((IsIncrement && (Value > (MAX_UINT64 - Diff))) ||
547 (!IsIncrement && (Value < Diff)))
548 {
549 ASSERT (0);
550 return EFI_INVALID_PARAMETER;
551 }
552
553 // Compute the new value.
554 if (IsIncrement) {
555 Value += Diff;
556 } else {
557 Value -= Diff;
558 }
559
560 Status = AmlNodeSetIntegerValue (
561 IntegerNode,
562 Value,
563 ValueWidthDiff
564 );
565 ASSERT_EFI_ERROR (Status);
566 return Status;
567 }
568
569 /** Propagate the size information up the tree.
570
571 The length of the ACPI table is updated in the RootNode,
572 but not the checksum.
573
574 @param [in] Node Pointer to a node.
575 Must be a root node or an object node.
576 @param [in] IsIncrement Choose the operation to do:
577 - TRUE: Increment the Node's size and
578 the Node's count;
579 - FALSE: Decrement the Node's size and
580 the Node's count.
581 @param [in] Diff Value to add/subtract to the Node's size.
582
583 @retval EFI_SUCCESS The function completed successfully.
584 @retval EFI_INVALID_PARAMETER Invalid parameter.
585 **/
586 STATIC
587 EFI_STATUS
588 EFIAPI
589 AmlPropagateSize (
590 IN AML_NODE_HEADER *Node,
591 IN BOOLEAN IsIncrement,
592 IN UINT32 *Diff
593 )
594 {
595 EFI_STATUS Status;
596 AML_OBJECT_NODE *ObjectNode;
597 AML_NODE_HEADER *ParentNode;
598
599 UINT32 Value;
600 UINT32 InitialPkgLenWidth;
601 UINT32 NewPkgLenWidth;
602 UINT32 ReComputedPkgLenWidth;
603 INT8 FieldWidthChange;
604
605 if (!IS_AML_OBJECT_NODE (Node) &&
606 !IS_AML_ROOT_NODE (Node))
607 {
608 ASSERT (0);
609 return EFI_INVALID_PARAMETER;
610 }
611
612 if (IS_AML_OBJECT_NODE (Node)) {
613 ObjectNode = (AML_OBJECT_NODE *)Node;
614
615 // For BufferOp, the buffer size is stored in BufferSize. Therefore,
616 // BufferOp needs special handling to update the BufferSize.
617 // BufferSize must be updated before the PkgLen to accommodate any
618 // increment resulting from the update of the BufferSize.
619 // DefBuffer := BufferOp PkgLength BufferSize ByteList
620 // BufferOp := 0x11
621 // BufferSize := TermArg => Integer
622 if (AmlNodeCompareOpCode (ObjectNode, AML_BUFFER_OP, 0)) {
623 // First fixed argument of BufferOp is an integer (BufferSize)
624 // (can be a BYTE, WORD, DWORD or QWORD).
625 // BufferSize is an object node.
626 Status = AmlNodeUpdateIntegerValue (
627 (AML_OBJECT_NODE *)AmlGetFixedArgument (
628 ObjectNode,
629 EAmlParseIndexTerm0
630 ),
631 IsIncrement,
632 (UINT64)(*Diff),
633 &FieldWidthChange
634 );
635 if (EFI_ERROR (Status)) {
636 ASSERT (0);
637 return Status;
638 }
639
640 // FieldWidthChange is an integer.
641 // It must be positive if IsIncrement is TRUE, negative otherwise.
642 if ((IsIncrement &&
643 (FieldWidthChange < 0)) ||
644 (!IsIncrement &&
645 (FieldWidthChange > 0)))
646 {
647 ASSERT (0);
648 return EFI_INVALID_PARAMETER;
649 }
650
651 // Check for UINT32 overflow.
652 if (*Diff > (MAX_UINT32 - (UINT32)ABS (FieldWidthChange))) {
653 ASSERT (0);
654 return EFI_INVALID_PARAMETER;
655 }
656
657 // Update Diff if the field width changed.
658 *Diff = (UINT32)(*Diff + ABS (FieldWidthChange));
659 } // AML_BUFFER_OP node.
660
661 // Update the PgkLen.
662 // Needs to be done at last to reflect the potential field width changes.
663 if (AmlNodeHasAttribute (ObjectNode, AML_HAS_PKG_LENGTH)) {
664 Value = ObjectNode->PkgLen;
665
666 // Subtract the size of the PkgLen encoding. The size of the PkgLen
667 // encoding must be computed after having updated Value.
668 InitialPkgLenWidth = AmlComputePkgLengthWidth (Value);
669 Value -= InitialPkgLenWidth;
670
671 // Check for an over/underflows.
672 // PkgLen is a 28 bit value, cf 20.2.4 Package Length Encoding
673 // i.e. the maximum value is (2^28 - 1) = ((BIT0 << 28) - 1).
674 if ((IsIncrement && ((((BIT0 << 28) - 1) - Value) < *Diff)) ||
675 (!IsIncrement && (Value < *Diff)))
676 {
677 ASSERT (0);
678 return EFI_INVALID_PARAMETER;
679 }
680
681 // Update the size.
682 if (IsIncrement) {
683 Value += *Diff;
684 } else {
685 Value -= *Diff;
686 }
687
688 // Compute the new PkgLenWidth.
689 NewPkgLenWidth = AmlComputePkgLengthWidth (Value);
690 if (NewPkgLenWidth == 0) {
691 ASSERT (0);
692 return EFI_INVALID_PARAMETER;
693 }
694
695 // Add it to the Value.
696 Value += NewPkgLenWidth;
697
698 // Check that adding the PkgLenWidth didn't trigger a domino effect,
699 // increasing the encoding width of the PkgLen again.
700 // The PkgLen is encoded on at most 4 bytes. It is possible to increase
701 // the PkgLen width if its encoding is on less than 3 bytes.
702 ReComputedPkgLenWidth = AmlComputePkgLengthWidth (Value);
703 if (ReComputedPkgLenWidth != NewPkgLenWidth) {
704 if ((ReComputedPkgLenWidth != 0) &&
705 (ReComputedPkgLenWidth < 4))
706 {
707 // No need to recompute the PkgLen since a new threshold cannot
708 // be reached by incrementing the value by one.
709 Value += 1;
710 } else {
711 ASSERT (0);
712 return EFI_INVALID_PARAMETER;
713 }
714 }
715
716 *Diff += (InitialPkgLenWidth > ReComputedPkgLenWidth) ?
717 (InitialPkgLenWidth - ReComputedPkgLenWidth) :
718 (ReComputedPkgLenWidth - InitialPkgLenWidth);
719 ObjectNode->PkgLen = Value;
720 } // PkgLen update.
721
722 // During CodeGeneration, the tree is incomplete and
723 // there is no root node at the top of the tree. Stop
724 // propagating the new size when finding a root node
725 // OR when a NULL parent is found.
726 ParentNode = AmlGetParent ((AML_NODE_HEADER *)Node);
727 if (ParentNode != NULL) {
728 // Propagate the size up the tree.
729 Status = AmlPropagateSize (
730 Node->Parent,
731 IsIncrement,
732 Diff
733 );
734 if (EFI_ERROR (Status)) {
735 ASSERT (0);
736 return Status;
737 }
738 }
739 } else if (IS_AML_ROOT_NODE (Node)) {
740 // Update the length field in the SDT header.
741 Value = ((AML_ROOT_NODE *)Node)->SdtHeader->Length;
742
743 // Check for an over/underflows.
744 if ((IsIncrement && (Value > (MAX_UINT32 - *Diff))) ||
745 (!IsIncrement && (Value < *Diff)))
746 {
747 ASSERT (0);
748 return EFI_INVALID_PARAMETER;
749 }
750
751 // Update the size.
752 if (IsIncrement) {
753 Value += *Diff;
754 } else {
755 Value -= *Diff;
756 }
757
758 ((AML_ROOT_NODE *)Node)->SdtHeader->Length = Value;
759 }
760
761 return EFI_SUCCESS;
762 }
763
764 /** Propagate the node count information up the tree.
765
766 @param [in] ObjectNode Pointer to an object node.
767 @param [in] IsIncrement Choose the operation to do:
768 - TRUE: Increment the Node's size and
769 the Node's count;
770 - FALSE: Decrement the Node's size and
771 the Node's count.
772 @param [in] NodeCount Number of nodes added/removed (depends on the
773 value of Operation).
774 @param [out] FieldWidthChange When modifying the integer, it can be
775 promoted/demoted, e.g. from UINT8 to UINT16.
776 Stores the change in width.
777 Can be negative.
778
779 @retval EFI_SUCCESS The function completed successfully.
780 @retval EFI_INVALID_PARAMETER Invalid parameter.
781 **/
782 STATIC
783 EFI_STATUS
784 EFIAPI
785 AmlPropagateNodeCount (
786 IN AML_OBJECT_NODE *ObjectNode,
787 IN BOOLEAN IsIncrement,
788 IN UINT8 NodeCount,
789 OUT INT8 *FieldWidthChange
790 )
791 {
792 EFI_STATUS Status;
793
794 AML_NODE_HEADER *NodeCountArg;
795 UINT8 CurrNodeCount;
796
797 // Currently there is no use case where (NodeCount > 1).
798 if (!IS_AML_OBJECT_NODE (ObjectNode) ||
799 (FieldWidthChange == NULL) ||
800 (NodeCount > 1))
801 {
802 ASSERT (0);
803 return EFI_INVALID_PARAMETER;
804 }
805
806 *FieldWidthChange = 0;
807
808 // Update the number of elements stored in PackageOp and VarPackageOp.
809 // The number of elements is stored as the first fixed argument.
810 // DefPackage := PackageOp PkgLength NumElements PackageElementList
811 // PackageOp := 0x12
812 // DefVarPackage := VarPackageOp PkgLength VarNumElements PackageElementList
813 // VarPackageOp := 0x13
814 // NumElements := ByteData
815 // VarNumElements := TermArg => Integer
816 NodeCountArg = AmlGetFixedArgument (ObjectNode, EAmlParseIndexTerm0);
817 if (AmlNodeCompareOpCode (ObjectNode, AML_PACKAGE_OP, 0)) {
818 // First fixed argument of PackageOp stores the number of elements
819 // in the package. It is an UINT8.
820
821 // Check for over/underflow.
822 CurrNodeCount = *(((AML_DATA_NODE *)NodeCountArg)->Buffer);
823 if ((IsIncrement && (CurrNodeCount == MAX_UINT8)) ||
824 (!IsIncrement && (CurrNodeCount == 0)))
825 {
826 ASSERT (0);
827 return EFI_INVALID_PARAMETER;
828 }
829
830 // Update the node count in the DataNode.
831 CurrNodeCount = IsIncrement ? (CurrNodeCount + 1) : (CurrNodeCount - 1);
832 *(((AML_DATA_NODE *)NodeCountArg)->Buffer) = CurrNodeCount;
833 } else if (AmlNodeCompareOpCode (ObjectNode, AML_VAR_PACKAGE_OP, 0)) {
834 // First fixed argument of PackageOp stores the number of elements
835 // in the package. It is an integer (can be a BYTE, WORD, DWORD, QWORD).
836 Status = AmlNodeUpdateIntegerValue (
837 (AML_OBJECT_NODE *)NodeCountArg,
838 IsIncrement,
839 NodeCount,
840 FieldWidthChange
841 );
842 if (EFI_ERROR (Status)) {
843 ASSERT (0);
844 return Status;
845 }
846 }
847
848 return EFI_SUCCESS;
849 }
850
851 /** Propagate information up the tree.
852
853 The information can be a new size, a new number of arguments.
854
855 @param [in] Node Pointer to a node.
856 Must be a root node or an object node.
857 @param [in] IsIncrement Choose the operation to do:
858 - TRUE: Increment the Node's size and
859 the Node's count;
860 - FALSE: Decrement the Node's size and
861 the Node's count.
862 @param [in] Diff Value to add/subtract to the Node's size.
863 @param [in] NodeCount Number of nodes added/removed.
864
865 @retval EFI_SUCCESS The function completed successfully.
866 @retval EFI_INVALID_PARAMETER Invalid parameter.
867 **/
868 EFI_STATUS
869 EFIAPI
870 AmlPropagateInformation (
871 IN AML_NODE_HEADER *Node,
872 IN BOOLEAN IsIncrement,
873 IN UINT32 Diff,
874 IN UINT8 NodeCount
875 )
876 {
877 EFI_STATUS Status;
878 INT8 FieldWidthChange;
879
880 // Currently there is no use case where (NodeCount > 1).
881 if ((!IS_AML_ROOT_NODE (Node) &&
882 !IS_AML_OBJECT_NODE (Node)) ||
883 (NodeCount > 1))
884 {
885 ASSERT (0);
886 return EFI_INVALID_PARAMETER;
887 }
888
889 // Propagate the node count first as it may change the number of bytes
890 // needed to store the node count, and then impact FieldWidthChange.
891 if ((NodeCount != 0) &&
892 IS_AML_OBJECT_NODE (Node))
893 {
894 Status = AmlPropagateNodeCount (
895 (AML_OBJECT_NODE *)Node,
896 IsIncrement,
897 NodeCount,
898 &FieldWidthChange
899 );
900 if (EFI_ERROR (Status)) {
901 ASSERT (0);
902 return Status;
903 }
904
905 // Propagate the potential field width change.
906 // Maximum change is between UINT8/UINT64: 8 bytes.
907 if ((ABS (FieldWidthChange) > 8) ||
908 (IsIncrement &&
909 ((FieldWidthChange < 0) ||
910 ((Diff + (UINT8)FieldWidthChange) > MAX_UINT32))) ||
911 (!IsIncrement &&
912 ((FieldWidthChange > 0) ||
913 (Diff < (UINT32)ABS (FieldWidthChange)))))
914 {
915 ASSERT (0);
916 return EFI_INVALID_PARAMETER;
917 }
918
919 Diff = (UINT32)(Diff + (UINT8)ABS (FieldWidthChange));
920 }
921
922 // Diff can be zero if some data is updated without modifying the data size.
923 if (Diff != 0) {
924 Status = AmlPropagateSize (Node, IsIncrement, &Diff);
925 if (EFI_ERROR (Status)) {
926 ASSERT (0);
927 return Status;
928 }
929 }
930
931 return EFI_SUCCESS;
932 }
933
934 /** Find and set the EndTag's Checksum of a list of Resource Data elements.
935
936 Lists of Resource Data elements end with an EndTag (most of the time). This
937 function finds the EndTag (if present) in a list of Resource Data elements
938 and sets the checksum.
939
940 ACPI 6.4, s6.4.2.9 "End Tag":
941 "This checksum is generated such that adding it to the sum of all the data
942 bytes will produce a zero sum."
943 "If the checksum field is zero, the resource data is treated as if the
944 checksum operation succeeded. Configuration proceeds normally."
945
946 To avoid re-computing checksums, if a new resource data elements is
947 added/removed/modified in a list of resource data elements, the AmlLib
948 resets the checksum to 0.
949
950 @param [in] BufferOpNode Node having a list of Resource Data elements.
951 @param [in] CheckSum CheckSum to store in the EndTag.
952 To ignore/avoid computing the checksum,
953 give 0.
954
955 @retval EFI_SUCCESS The function completed successfully.
956 @retval EFI_INVALID_PARAMETER Invalid parameter.
957 @retval EFI_NOT_FOUND No EndTag found.
958 **/
959 EFI_STATUS
960 EFIAPI
961 AmlSetRdListCheckSum (
962 IN AML_OBJECT_NODE *BufferOpNode,
963 IN UINT8 CheckSum
964 )
965 {
966 EFI_STATUS Status;
967 AML_DATA_NODE *LastRdNode;
968 AML_RD_HEADER RdDataType;
969
970 if (!AmlNodeCompareOpCode (BufferOpNode, AML_BUFFER_OP, 0)) {
971 ASSERT (0);
972 return EFI_INVALID_PARAMETER;
973 }
974
975 // Get the last Resource data node in the variable list of
976 // argument of the BufferOp node.
977 LastRdNode = (AML_DATA_NODE *)AmlGetPreviousVariableArgument (
978 (AML_NODE_HEADER *)BufferOpNode,
979 NULL
980 );
981 if ((LastRdNode == NULL) ||
982 !IS_AML_DATA_NODE (LastRdNode) ||
983 (LastRdNode->DataType != EAmlNodeDataTypeResourceData))
984 {
985 ASSERT (0);
986 return EFI_INVALID_PARAMETER;
987 }
988
989 Status = AmlGetResourceDataType (LastRdNode, &RdDataType);
990 if (EFI_ERROR (Status)) {
991 ASSERT (0);
992 return Status;
993 }
994
995 // Check the LastRdNode is an EndTag.
996 // It is possible to have only one Resource Data in a BufferOp with
997 // no EndTag. Return EFI_NOT_FOUND is such case.
998 if (!AmlRdCompareDescId (
999 &RdDataType,
1000 AML_RD_BUILD_SMALL_DESC_ID (ACPI_SMALL_END_TAG_DESCRIPTOR_NAME)
1001 ))
1002 {
1003 ASSERT (0);
1004 return EFI_NOT_FOUND;
1005 }
1006
1007 Status = AmlRdSetEndTagChecksum (LastRdNode->Buffer, CheckSum);
1008 ASSERT_EFI_ERROR (Status);
1009
1010 return Status;
1011 }