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