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