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