summaryrefslogtreecommitdiffstats
path: root/DynamicTablesPkg/Library/Common/AmlLib/Utils/AmlUtility.c
blob: 811b7d22022853df68345531a2fa48dab408c415 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
1001
1002
1003
1004
1005
1006
1007
1008
1009
1010
1011
/** @file
  AML Utility.

  Copyright (c) 2019 - 2021, Arm Limited. All rights reserved.<BR>

  SPDX-License-Identifier: BSD-2-Clause-Patent
**/

#include <Utils/AmlUtility.h>

#include <AmlCoreInterface.h>
#include <Tree/AmlNode.h>
#include <Tree/AmlTree.h>

/** This function computes and updates the ACPI table checksum.

  @param  [in]  AcpiTable   Pointer to an Acpi table.

  @retval EFI_SUCCESS   The function completed successfully.
  @retval EFI_INVALID_PARAMETER   Invalid parameter.
**/
EFI_STATUS
EFIAPI
AcpiPlatformChecksum (
  IN  EFI_ACPI_DESCRIPTION_HEADER  *AcpiTable
  )
{
  UINT8   *Ptr;
  UINT8   Sum;
  UINT32  Size;

  if (AcpiTable == NULL) {
    ASSERT (0);
    return EFI_INVALID_PARAMETER;
  }

  Ptr  = (UINT8 *)AcpiTable;
  Size = AcpiTable->Length;
  Sum  = 0;

  // Set the checksum field to 0 first.
  AcpiTable->Checksum = 0;

  // Compute the checksum.
  while ((Size--) != 0) {
    Sum = (UINT8)(Sum + (*Ptr++));
  }

  // Set the checksum.
  AcpiTable->Checksum = (UINT8)(0xFF - Sum + 1);

  return EFI_SUCCESS;
}

/** A callback function that computes the size of a Node and adds it to the
    Size pointer stored in the Context.
    Calling this function on the root node will compute the total size of the
    AML bytestream.

  @param  [in]      Node      Node to compute the size.
  @param  [in, out] Context   Pointer holding the computed size.
                              (UINT32 *) Context.
  @param  [in, out] Status    Pointer holding:
                               - At entry, the Status returned by the
                                 last call to this exact function during
                                 the enumeration;
                               - At exit, he returned status of the
                                 call to this function.
                              Optional, can be NULL.

  @retval TRUE if the enumeration can continue or has finished without
          interruption.
  @retval FALSE if the enumeration needs to stopped or has stopped.
**/
STATIC
BOOLEAN
EFIAPI
AmlComputeSizeCallback (
  IN      AML_NODE_HEADER  *Node,
  IN  OUT VOID             *Context,
  IN  OUT EFI_STATUS       *Status   OPTIONAL
  )
{
  UINT32                 Size;
  EAML_PARSE_INDEX       IndexPtr;
  CONST AML_OBJECT_NODE  *ParentNode;

  if (!IS_AML_NODE_VALID (Node) ||
      (Context == NULL))
  {
    ASSERT (0);
    if (Status != NULL) {
      *Status = EFI_INVALID_PARAMETER;
    }

    return FALSE;
  }

  // Ignore the second fixed argument of method invocation nodes
  // as the information stored there (the argument count) is not in the
  // ACPI specification.
  ParentNode = (CONST AML_OBJECT_NODE *)AmlGetParent (Node);
  if (IS_AML_OBJECT_NODE (ParentNode)                             &&
      AmlNodeCompareOpCode (ParentNode, AML_METHOD_INVOC_OP, 0)   &&
      AmlIsNodeFixedArgument (Node, &IndexPtr))
  {
    if (IndexPtr == EAmlParseIndexTerm1) {
      if (Status != NULL) {
        *Status = EFI_SUCCESS;
      }

      return TRUE;
    }
  }

  Size = *((UINT32 *)Context);

  if (IS_AML_DATA_NODE (Node)) {
    Size += ((AML_DATA_NODE *)Node)->Size;
  } else if (IS_AML_OBJECT_NODE (Node)  &&
             !AmlNodeHasAttribute (
                (CONST AML_OBJECT_NODE *)Node,
                AML_IS_PSEUDO_OPCODE
                ))
  {
    // Ignore pseudo-opcodes as they are not part of the
    // ACPI specification.

    Size += (((AML_OBJECT_NODE *)Node)->AmlByteEncoding->OpCode ==
             AML_EXT_OP) ? 2 : 1;

    // Add the size of the PkgLen.
    if (AmlNodeHasAttribute (
          (AML_OBJECT_NODE *)Node,
          AML_HAS_PKG_LENGTH
          ))
    {
      Size += AmlComputePkgLengthWidth (((AML_OBJECT_NODE *)Node)->PkgLen);
    }
  }

  // Check for overflow.
  // The root node has a null size, thus the strict comparison.
  if (*((UINT32 *)Context) > Size) {
    ASSERT (0);
    *Status = EFI_INVALID_PARAMETER;
    return FALSE;
  }

  *((UINT32 *)Context) = Size;

  if (Status != NULL) {
    *Status = EFI_SUCCESS;
  }

  return TRUE;
}

/** Compute the size of a tree/sub-tree.

  @param  [in]      Node      Node to compute the size.
  @param  [in, out] Size      Pointer holding the computed size.

  @retval EFI_SUCCESS             The function completed successfully.
  @retval EFI_INVALID_PARAMETER   Invalid parameter.
**/
EFI_STATUS
EFIAPI
AmlComputeSize (
  IN      CONST AML_NODE_HEADER  *Node,
  IN  OUT       UINT32           *Size
  )
{
  EFI_STATUS  Status;

  if (!IS_AML_NODE_VALID (Node) ||
      (Size == NULL))
  {
    ASSERT (0);
    return EFI_INVALID_PARAMETER;
  }

  *Size = 0;

  AmlEnumTree (
    (AML_NODE_HEADER *)Node,
    AmlComputeSizeCallback,
    (VOID *)Size,
    &Status
    );

  return Status;
}

/** Get the value contained in an integer node.

  @param  [in]  Node    Pointer to an integer node.
                        Must be an object node.
  @param  [out] Value   Value contained in the integer node.

  @retval EFI_SUCCESS             The function completed successfully.
  @retval EFI_INVALID_PARAMETER   Invalid parameter.
**/
EFI_STATUS
EFIAPI
AmlNodeGetIntegerValue (
  IN  AML_OBJECT_NODE  *Node,
  OUT UINT64           *Value
  )
{
  AML_DATA_NODE  *DataNode;

  if ((!IsIntegerNode (Node)            &&
       !IsSpecialIntegerNode (Node))    ||
      (Value == NULL))
  {
    ASSERT (0);
    return EFI_INVALID_PARAMETER;
  }

  // For ZeroOp and OneOp, there is no data node.
  if (IsSpecialIntegerNode (Node)) {
    if (AmlNodeCompareOpCode (Node, AML_ZERO_OP, 0)) {
      *Value = 0;
    } else if (AmlNodeCompareOpCode (Node, AML_ONE_OP, 0)) {
      *Value = 1;
    } else {
      // OnesOp cannot be handled: it represents a maximum value.
      ASSERT (0);
      return EFI_INVALID_PARAMETER;
    }

    return EFI_SUCCESS;
  }

  // For integer nodes, the value is in the first fixed argument.
  DataNode = (AML_DATA_NODE *)Node->FixedArgs[EAmlParseIndexTerm0];
  if (!IS_AML_DATA_NODE (DataNode) ||
      (DataNode->DataType != EAmlNodeDataTypeUInt))
  {
    ASSERT (0);
    return EFI_INVALID_PARAMETER;
  }

  switch (DataNode->Size) {
    case 1:
    {
      *Value = *((UINT8 *)(DataNode->Buffer));
      break;
    }
    case 2:
    {
      *Value = *((UINT16 *)(DataNode->Buffer));
      break;
    }
    case 4:
    {
      *Value = *((UINT32 *)(DataNode->Buffer));
      break;
    }
    case 8:
    {
      *Value = *((UINT64 *)(DataNode->Buffer));
      break;
    }
    default:
    {
      ASSERT (0);
      return EFI_INVALID_PARAMETER;
    }
  } // switch

  return EFI_SUCCESS;
}

/** Replace a Zero (AML_ZERO_OP) or One (AML_ONE_OP) object node
    with a byte integer (AML_BYTE_PREFIX) object node having the same value.

  @param  [in]  Node    Pointer to an integer node.
                        Must be an object node having ZeroOp or OneOp.

  @retval EFI_SUCCESS             The function completed successfully.
  @retval EFI_INVALID_PARAMETER   Invalid parameter.
**/
STATIC
EFI_STATUS
EFIAPI
AmlUnwindSpecialInteger (
  IN  AML_OBJECT_NODE  *Node
  )
{
  EFI_STATUS  Status;

  AML_DATA_NODE            *NewDataNode;
  UINT8                    Value;
  CONST AML_BYTE_ENCODING  *ByteEncoding;

  if (!IsSpecialIntegerNode (Node)) {
    ASSERT (0);
    return EFI_INVALID_PARAMETER;
  }

  // Find the value.
  if (AmlNodeCompareOpCode (Node, AML_ZERO_OP, 0)) {
    Value = 0;
  } else if (AmlNodeCompareOpCode (Node, AML_ONE_OP, 0)) {
    Value = 1;
  } else {
    // OnesOp cannot be handled: it represents a maximum value.
    ASSERT (0);
    return EFI_INVALID_PARAMETER;
  }

  Status = AmlCreateDataNode (
             EAmlNodeDataTypeUInt,
             &Value,
             sizeof (UINT8),
             (AML_DATA_NODE **)&NewDataNode
             );
  if (EFI_ERROR (Status)) {
    ASSERT (0);
    return Status;
  }

  // Change the encoding of the special node to a ByteOp encoding.
  ByteEncoding = AmlGetByteEncodingByOpCode (AML_BYTE_PREFIX, 0);
  if (ByteEncoding == NULL) {
    ASSERT (0);
    Status = EFI_INVALID_PARAMETER;
    goto error_handler;
  }

  // Update the ByteEncoding from ZERO_OP/ONE_OP to AML_BYTE_PREFIX.
  Node->AmlByteEncoding = ByteEncoding;

  // Add the data node as the first fixed argument of the ByteOp object.
  Status = AmlSetFixedArgument (
             (AML_OBJECT_NODE *)Node,
             EAmlParseIndexTerm0,
             (AML_NODE_HEADER *)NewDataNode
             );
  if (EFI_ERROR (Status)) {
    ASSERT (0);
    goto error_handler;
  }

  return Status;

error_handler:
  AmlDeleteTree ((AML_NODE_HEADER *)NewDataNode);
  return Status;
}

/** Set the value contained in an integer node.

  The OpCode is updated accordingly to the new value
  (e.g.: If the original value was a UINT8 value, then the OpCode
         would be AML_BYTE_PREFIX. If it the new value is a UINT16
         value then the OpCode will be updated to AML_WORD_PREFIX).

  @param  [in]  Node            Pointer to an integer node.
                                Must be an object node.
  @param  [in]  NewValue        New value to write in the integer node.
  @param  [out] ValueWidthDiff  Difference in number of bytes used to store
                                the new value.
                                Can be negative.

  @retval EFI_SUCCESS             The function completed successfully.
  @retval EFI_INVALID_PARAMETER   Invalid parameter.
  @retval EFI_OUT_OF_RESOURCES    Could not allocate memory.
**/
EFI_STATUS
EFIAPI
AmlNodeSetIntegerValue (
  IN  AML_OBJECT_NODE  *Node,
  IN  UINT64           NewValue,
  OUT INT8             *ValueWidthDiff
  )
{
  EFI_STATUS     Status;
  AML_DATA_NODE  *DataNode;

  UINT8  NewOpCode;
  UINT8  NumberOfBytes;

  if ((!IsIntegerNode (Node)            &&
       !IsSpecialIntegerNode (Node))    ||
      (ValueWidthDiff == NULL))
  {
    ASSERT (0);
    return EFI_INVALID_PARAMETER;
  }

  *ValueWidthDiff = 0;
  // For ZeroOp and OneOp, there is no data node.
  // Thus the object node is converted to a byte object node holding 0 or 1.
  if (IsSpecialIntegerNode (Node)) {
    switch (NewValue) {
      case AML_ZERO_OP:
        Node->AmlByteEncoding = AmlGetByteEncodingByOpCode (AML_ZERO_OP, 0);
        return EFI_SUCCESS;
      case AML_ONE_OP:
        Node->AmlByteEncoding = AmlGetByteEncodingByOpCode (AML_ONE_OP, 0);
        return EFI_SUCCESS;
      default:
      {
        Status = AmlUnwindSpecialInteger (Node);
        if (EFI_ERROR (Status)) {
          ASSERT (0);
          return Status;
        }

        // The AmlUnwindSpecialInteger functions converts a special integer
        // node to a UInt8/Byte data node. Thus, the size increments by one:
        // special integer are encoded as one byte (the opcode only) while byte
        // integers are encoded as two bytes (the opcode + the value).
        *ValueWidthDiff += sizeof (UINT8);
      }
    } // switch
  } // IsSpecialIntegerNode (Node)

  // For integer nodes, the value is in the first fixed argument.
  DataNode = (AML_DATA_NODE *)Node->FixedArgs[EAmlParseIndexTerm0];
  if (!IS_AML_DATA_NODE (DataNode) ||
      (DataNode->DataType != EAmlNodeDataTypeUInt))
  {
    ASSERT (0);
    return EFI_INVALID_PARAMETER;
  }

  // The value can be encoded with a special 0 or 1 OpCode.
  // The AML_ONES_OP is not handled.
  if (NewValue <= 1) {
    NewOpCode             = (NewValue == 0) ? AML_ZERO_OP : AML_ONE_OP;
    Node->AmlByteEncoding = AmlGetByteEncodingByOpCode (NewOpCode, 0);

    // The value is encoded with a AML_ZERO_OP or AML_ONE_OP.
    // This means there is no need for a DataNode containing the value.
    // The change in size is equal to the size of the DataNode's buffer.
    *ValueWidthDiff = -((INT8)DataNode->Size);

    // Detach and free the DataNode containing the integer value.
    DataNode->NodeHeader.Parent          = NULL;
    Node->FixedArgs[EAmlParseIndexTerm0] = NULL;
    Status                               = AmlDeleteNode ((AML_NODE_HEADER *)DataNode);
    if (EFI_ERROR (Status)) {
      ASSERT (0);
      return Status;
    }

    return EFI_SUCCESS;
  }

  // Check the number of bits needed to represent the value.
  if (NewValue > MAX_UINT32) {
    // Value is 64 bits.
    NewOpCode     = AML_QWORD_PREFIX;
    NumberOfBytes = 8;
  } else if (NewValue > MAX_UINT16) {
    // Value is 32 bits.
    NewOpCode     = AML_DWORD_PREFIX;
    NumberOfBytes = 4;
  } else if (NewValue > MAX_UINT8) {
    // Value is 16 bits.
    NewOpCode     = AML_WORD_PREFIX;
    NumberOfBytes = 2;
  } else {
    // Value is 8 bits.
    NewOpCode     = AML_BYTE_PREFIX;
    NumberOfBytes = 1;
  }

  *ValueWidthDiff += (INT8)(NumberOfBytes - DataNode->Size);

  // Update the ByteEncoding as it may have changed between [8 .. 64] bits.
  Node->AmlByteEncoding = AmlGetByteEncodingByOpCode (NewOpCode, 0);
  if (Node->AmlByteEncoding == NULL) {
    ASSERT (0);
    return EFI_INVALID_PARAMETER;
  }

  // Free the old DataNode buffer and allocate a buffer with the right size
  // to store the new data.
  if (*ValueWidthDiff != 0) {
    FreePool (DataNode->Buffer);
    DataNode->Buffer = AllocateZeroPool (NumberOfBytes);
    if (DataNode->Buffer == NULL) {
      ASSERT (0);
      return EFI_OUT_OF_RESOURCES;
    }

    DataNode->Size = NumberOfBytes;
  }

  // Write the new value.
  CopyMem (DataNode->Buffer, &NewValue, NumberOfBytes);

  return EFI_SUCCESS;
}

/** Increment/decrement the value contained in the IntegerNode.

  @param  [in]  IntegerNode     Pointer to an object node containing
                                an integer.
  @param  [in]  IsIncrement     Choose the operation to do:
                                 - TRUE:  Increment the Node's size and
                                          the Node's count;
                                 - FALSE: Decrement the Node's size and
                                          the Node's count.
  @param  [in]  Diff            Value to add/subtract to the integer.
  @param  [out] ValueWidthDiff  When modifying the integer, it can be
                                promoted/demoted, e.g. from UINT8 to UINT16.
                                Stores the change in width.
                                Can be negative.

  @retval EFI_SUCCESS             The function completed successfully.
  @retval EFI_INVALID_PARAMETER   Invalid parameter.
**/
STATIC
EFI_STATUS
EFIAPI
AmlNodeUpdateIntegerValue (
  IN  AML_OBJECT_NODE  *IntegerNode,
  IN  BOOLEAN          IsIncrement,
  IN  UINT64           Diff,
  OUT INT8             *ValueWidthDiff
  )
{
  EFI_STATUS  Status;
  UINT64      Value;

  if (ValueWidthDiff == NULL) {
    ASSERT (0);
    return EFI_INVALID_PARAMETER;
  }

  // Get the current value.
  // Checks on the IntegerNode are done in the call.
  Status = AmlNodeGetIntegerValue (IntegerNode, &Value);
  if (EFI_ERROR (Status)) {
    ASSERT (0);
    return Status;
  }

  // Check for UINT64 over/underflow.
  if ((IsIncrement && (Value > (MAX_UINT64 - Diff))) ||
      (!IsIncrement && (Value < Diff)))
  {
    ASSERT (0);
    return EFI_INVALID_PARAMETER;
  }

  // Compute the new value.
  if (IsIncrement) {
    Value += Diff;
  } else {
    Value -= Diff;
  }

  Status = AmlNodeSetIntegerValue (
             IntegerNode,
             Value,
             ValueWidthDiff
             );
  ASSERT_EFI_ERROR (Status);
  return Status;
}

/** Propagate the size information up the tree.

  The length of the ACPI table is updated in the RootNode,
  but not the checksum.

  @param  [in]  Node          Pointer to a node.
                              Must be a root node or an object node.
  @param  [in]  IsIncrement   Choose the operation to do:
                               - TRUE:  Increment the Node's size and
                                        the Node's count;
                               - FALSE: Decrement the Node's size and
                                        the Node's count.
  @param  [in]  Diff          Value to add/subtract to the Node's size.

  @retval EFI_SUCCESS             The function completed successfully.
  @retval EFI_INVALID_PARAMETER   Invalid parameter.
**/
STATIC
EFI_STATUS
EFIAPI
AmlPropagateSize (
  IN  AML_NODE_HEADER  *Node,
  IN  BOOLEAN          IsIncrement,
  IN  UINT32           *Diff
  )
{
  EFI_STATUS       Status;
  AML_OBJECT_NODE  *ObjectNode;
  AML_NODE_HEADER  *ParentNode;

  UINT32  Value;
  UINT32  InitialPkgLenWidth;
  UINT32  NewPkgLenWidth;
  UINT32  ReComputedPkgLenWidth;
  INT8    FieldWidthChange;

  if (!IS_AML_OBJECT_NODE (Node) &&
      !IS_AML_ROOT_NODE (Node))
  {
    ASSERT (0);
    return EFI_INVALID_PARAMETER;
  }

  if (IS_AML_OBJECT_NODE (Node)) {
    ObjectNode = (AML_OBJECT_NODE *)Node;

    // For BufferOp, the buffer size is stored in BufferSize. Therefore,
    // BufferOp needs special handling to update the BufferSize.
    // BufferSize must be updated before the PkgLen to accommodate any
    // increment resulting from the update of the BufferSize.
    // DefBuffer := BufferOp PkgLength BufferSize ByteList
    // BufferOp := 0x11
    // BufferSize := TermArg => Integer
    if (AmlNodeCompareOpCode (ObjectNode, AML_BUFFER_OP, 0)) {
      // First fixed argument of BufferOp is an integer (BufferSize)
      // (can be a BYTE, WORD, DWORD or QWORD).
      // BufferSize is an object node.
      Status = AmlNodeUpdateIntegerValue (
                 (AML_OBJECT_NODE *)AmlGetFixedArgument (
                                      ObjectNode,
                                      EAmlParseIndexTerm0
                                      ),
                 IsIncrement,
                 (UINT64)(*Diff),
                 &FieldWidthChange
                 );
      if (EFI_ERROR (Status)) {
        ASSERT (0);
        return Status;
      }

      // FieldWidthChange is an integer.
      // It must be positive if IsIncrement is TRUE, negative otherwise.
      if ((IsIncrement              &&
           (FieldWidthChange < 0))  ||
          (!IsIncrement             &&
           (FieldWidthChange > 0)))
      {
        ASSERT (0);
        return EFI_INVALID_PARAMETER;
      }

      // Check for UINT32 overflow.
      if (*Diff > (MAX_UINT32 - (UINT32)ABS (FieldWidthChange))) {
        ASSERT (0);
        return EFI_INVALID_PARAMETER;
      }

      // Update Diff if the field width changed.
      *Diff = (UINT32)(*Diff + ABS (FieldWidthChange));
    } // AML_BUFFER_OP node.

    // Update the PgkLen.
    // Needs to be done at last to reflect the potential field width changes.
    if (AmlNodeHasAttribute (ObjectNode, AML_HAS_PKG_LENGTH)) {
      Value = ObjectNode->PkgLen;

      // Subtract the size of the PkgLen encoding. The size of the PkgLen
      // encoding must be computed after having updated Value.
      InitialPkgLenWidth = AmlComputePkgLengthWidth (Value);
      Value             -= InitialPkgLenWidth;

      // Check for an over/underflows.
      // PkgLen is a 28 bit value, cf 20.2.4 Package Length Encoding
      // i.e. the maximum value is (2^28 - 1) = ((BIT0 << 28) - 1).
      if ((IsIncrement && ((((BIT0 << 28) - 1) - Value) < *Diff))   ||
          (!IsIncrement && (Value < *Diff)))
      {
        ASSERT (0);
        return EFI_INVALID_PARAMETER;
      }

      // Update the size.
      if (IsIncrement) {
        Value += *Diff;
      } else {
        Value -= *Diff;
      }

      // Compute the new PkgLenWidth.
      NewPkgLenWidth = AmlComputePkgLengthWidth (Value);
      if (NewPkgLenWidth == 0) {
        ASSERT (0);
        return EFI_INVALID_PARAMETER;
      }

      // Add it to the Value.
      Value += NewPkgLenWidth;

      // Check that adding the PkgLenWidth didn't trigger a domino effect,
      // increasing the encoding width of the PkgLen again.
      // The PkgLen is encoded on at most 4 bytes. It is possible to increase
      // the PkgLen width if its encoding is on less than 3 bytes.
      ReComputedPkgLenWidth = AmlComputePkgLengthWidth (Value);
      if (ReComputedPkgLenWidth != NewPkgLenWidth) {
        if ((ReComputedPkgLenWidth != 0)   &&
            (ReComputedPkgLenWidth < 4))
        {
          // No need to recompute the PkgLen since a new threshold cannot
          // be reached by incrementing the value by one.
          Value += 1;
        } else {
          ASSERT (0);
          return EFI_INVALID_PARAMETER;
        }
      }

      *Diff += (InitialPkgLenWidth > ReComputedPkgLenWidth) ?
               (InitialPkgLenWidth - ReComputedPkgLenWidth) :
               (ReComputedPkgLenWidth - InitialPkgLenWidth);
      ObjectNode->PkgLen = Value;
    } // PkgLen update.

    // During CodeGeneration, the tree is incomplete and
    // there is no root node at the top of the tree. Stop
    // propagating the new size when finding a root node
    // OR when a NULL parent is found.
    ParentNode = AmlGetParent ((AML_NODE_HEADER *)Node);
    if (ParentNode != NULL) {
      // Propagate the size up the tree.
      Status = AmlPropagateSize (
                 Node->Parent,
                 IsIncrement,
                 Diff
                 );
      if (EFI_ERROR (Status)) {
        ASSERT (0);
        return Status;
      }
    }
  } else if (IS_AML_ROOT_NODE (Node)) {
    // Update the length field in the SDT header.
    Value = ((AML_ROOT_NODE *)Node)->SdtHeader->Length;

    // Check for an over/underflows.
    if ((IsIncrement && (Value > (MAX_UINT32 - *Diff))) ||
        (!IsIncrement && (Value < *Diff)))
    {
      ASSERT (0);
      return EFI_INVALID_PARAMETER;
    }

    // Update the size.
    if (IsIncrement) {
      Value += *Diff;
    } else {
      Value -= *Diff;
    }

    ((AML_ROOT_NODE *)Node)->SdtHeader->Length = Value;
  }

  return EFI_SUCCESS;
}

/** Propagate the node count information up the tree.

  @param  [in]  ObjectNode        Pointer to an object node.
  @param  [in]  IsIncrement       Choose the operation to do:
                                   - TRUE:  Increment the Node's size and
                                            the Node's count;
                                   - FALSE: Decrement the Node's size and
                                           the Node's count.
  @param  [in]  NodeCount         Number of nodes added/removed (depends on the
                                  value of Operation).
  @param  [out] FieldWidthChange  When modifying the integer, it can be
                                  promoted/demoted, e.g. from UINT8 to UINT16.
                                  Stores the change in width.
                                  Can be negative.

  @retval EFI_SUCCESS             The function completed successfully.
  @retval EFI_INVALID_PARAMETER   Invalid parameter.
**/
STATIC
EFI_STATUS
EFIAPI
AmlPropagateNodeCount (
  IN  AML_OBJECT_NODE  *ObjectNode,
  IN  BOOLEAN          IsIncrement,
  IN  UINT8            NodeCount,
  OUT INT8             *FieldWidthChange
  )
{
  EFI_STATUS  Status;

  AML_NODE_HEADER  *NodeCountArg;
  UINT8            CurrNodeCount;

  // Currently there is no use case where (NodeCount > 1).
  if (!IS_AML_OBJECT_NODE (ObjectNode)  ||
      (FieldWidthChange == NULL)        ||
      (NodeCount > 1))
  {
    ASSERT (0);
    return EFI_INVALID_PARAMETER;
  }

  *FieldWidthChange = 0;

  // Update the number of elements stored in PackageOp and VarPackageOp.
  // The number of elements is stored as the first fixed argument.
  // DefPackage := PackageOp PkgLength NumElements PackageElementList
  // PackageOp := 0x12
  // DefVarPackage := VarPackageOp PkgLength VarNumElements PackageElementList
  // VarPackageOp := 0x13
  // NumElements := ByteData
  // VarNumElements := TermArg => Integer
  NodeCountArg = AmlGetFixedArgument (ObjectNode, EAmlParseIndexTerm0);
  if (AmlNodeCompareOpCode (ObjectNode, AML_PACKAGE_OP, 0)) {
    // First fixed argument of PackageOp stores the number of elements
    // in the package. It is an UINT8.

    // Check for over/underflow.
    CurrNodeCount = *(((AML_DATA_NODE *)NodeCountArg)->Buffer);
    if ((IsIncrement && (CurrNodeCount == MAX_UINT8)) ||
        (!IsIncrement && (CurrNodeCount == 0)))
    {
      ASSERT (0);
      return EFI_INVALID_PARAMETER;
    }

    // Update the node count in the DataNode.
    CurrNodeCount                              = IsIncrement ? (CurrNodeCount + 1) : (CurrNodeCount - 1);
    *(((AML_DATA_NODE *)NodeCountArg)->Buffer) =  CurrNodeCount;
  } else if (AmlNodeCompareOpCode (ObjectNode, AML_VAR_PACKAGE_OP, 0)) {
    // First fixed argument of PackageOp stores the number of elements
    // in the package. It is an integer (can be a BYTE, WORD, DWORD, QWORD).
    Status = AmlNodeUpdateIntegerValue (
               (AML_OBJECT_NODE *)NodeCountArg,
               IsIncrement,
               NodeCount,
               FieldWidthChange
               );
    if (EFI_ERROR (Status)) {
      ASSERT (0);
      return Status;
    }
  }

  return EFI_SUCCESS;
}

/** Propagate information up the tree.

  The information can be a new size, a new number of arguments.

  @param  [in]  Node          Pointer to a node.
                              Must be a root node or an object node.
  @param  [in]  IsIncrement   Choose the operation to do:
                               - TRUE:  Increment the Node's size and
                                        the Node's count;
                               - FALSE: Decrement the Node's size and
                                        the Node's count.
  @param  [in]  Diff          Value to add/subtract to the Node's size.
  @param  [in]  NodeCount     Number of nodes added/removed.

  @retval EFI_SUCCESS             The function completed successfully.
  @retval EFI_INVALID_PARAMETER   Invalid parameter.
**/
EFI_STATUS
EFIAPI
AmlPropagateInformation (
  IN  AML_NODE_HEADER  *Node,
  IN  BOOLEAN          IsIncrement,
  IN  UINT32           Diff,
  IN  UINT8            NodeCount
  )
{
  EFI_STATUS  Status;
  INT8        FieldWidthChange;

  // Currently there is no use case where (NodeCount > 1).
  if ((!IS_AML_ROOT_NODE (Node)     &&
       !IS_AML_OBJECT_NODE (Node))  ||
      (NodeCount > 1))
  {
    ASSERT (0);
    return EFI_INVALID_PARAMETER;
  }

  // Propagate the node count first as it may change the number of bytes
  // needed to store the node count, and then impact FieldWidthChange.
  if ((NodeCount != 0) &&
      IS_AML_OBJECT_NODE (Node))
  {
    Status = AmlPropagateNodeCount (
               (AML_OBJECT_NODE *)Node,
               IsIncrement,
               NodeCount,
               &FieldWidthChange
               );
    if (EFI_ERROR (Status)) {
      ASSERT (0);
      return Status;
    }

    // Propagate the potential field width change.
    // Maximum change is between UINT8/UINT64: 8 bytes.
    if ((ABS (FieldWidthChange) > 8)                          ||
        (IsIncrement                                          &&
         ((FieldWidthChange < 0)                             ||
          ((Diff + (UINT8)FieldWidthChange) > MAX_UINT32)))  ||
        (!IsIncrement                                         &&
         ((FieldWidthChange > 0)                             ||
          (Diff < (UINT32)ABS (FieldWidthChange)))))
    {
      ASSERT (0);
      return EFI_INVALID_PARAMETER;
    }

    Diff = (UINT32)(Diff + (UINT8)ABS (FieldWidthChange));
  }

  // Diff can be zero if some data is updated without modifying the data size.
  if (Diff != 0) {
    Status = AmlPropagateSize (Node, IsIncrement, &Diff);
    if (EFI_ERROR (Status)) {
      ASSERT (0);
      return Status;
    }
  }

  return EFI_SUCCESS;
}

/** Find and set the EndTag's Checksum of a list of Resource Data elements.

  Lists of Resource Data elements end with an EndTag (most of the time). This
  function finds the EndTag (if present) in a list of Resource Data elements
  and sets the checksum.

  ACPI 6.4, s6.4.2.9 "End Tag":
  "This checksum is generated such that adding it to the sum of all the data
  bytes will produce a zero sum."
  "If the checksum field is zero, the resource data is treated as if the
  checksum operation succeeded. Configuration proceeds normally."

  To avoid re-computing checksums, if a new resource data elements is
  added/removed/modified in a list of resource data elements, the AmlLib
  resets the checksum to 0.

  @param [in]  BufferOpNode   Node having a list of Resource Data elements.
  @param [in]  CheckSum       CheckSum to store in the EndTag.
                              To ignore/avoid computing the checksum,
                              give 0.

  @retval EFI_SUCCESS             The function completed successfully.
  @retval EFI_INVALID_PARAMETER   Invalid parameter.
  @retval EFI_NOT_FOUND           No EndTag found.
**/
EFI_STATUS
EFIAPI
AmlSetRdListCheckSum (
  IN  AML_OBJECT_NODE  *BufferOpNode,
  IN  UINT8            CheckSum
  )
{
  EFI_STATUS     Status;
  AML_DATA_NODE  *LastRdNode;
  AML_RD_HEADER  RdDataType;

  if (!AmlNodeCompareOpCode (BufferOpNode, AML_BUFFER_OP, 0)) {
    ASSERT (0);
    return EFI_INVALID_PARAMETER;
  }

  // Get the last Resource data node in the variable list of
  // argument of the BufferOp node.
  LastRdNode = (AML_DATA_NODE *)AmlGetPreviousVariableArgument (
                                  (AML_NODE_HEADER *)BufferOpNode,
                                  NULL
                                  );
  if ((LastRdNode == NULL)             ||
      !IS_AML_DATA_NODE (LastRdNode)   ||
      (LastRdNode->DataType != EAmlNodeDataTypeResourceData))
  {
    ASSERT (0);
    return EFI_INVALID_PARAMETER;
  }

  Status = AmlGetResourceDataType (LastRdNode, &RdDataType);
  if (EFI_ERROR (Status)) {
    ASSERT (0);
    return Status;
  }

  // Check the LastRdNode is an EndTag.
  // It is possible to have only one Resource Data in a BufferOp with
  // no EndTag. Return EFI_NOT_FOUND is such case.
  if (!AmlRdCompareDescId (
         &RdDataType,
         AML_RD_BUILD_SMALL_DESC_ID (ACPI_SMALL_END_TAG_DESCRIPTOR_NAME)
         ))
  {
    ASSERT (0);
    return EFI_NOT_FOUND;
  }

  Status = AmlRdSetEndTagChecksum (LastRdNode->Buffer, CheckSum);
  ASSERT_EFI_ERROR (Status);

  return Status;
}