1 /** @file
2   AML Print Function.
3 
4   Copyright (c) 2010 - 2018, Intel Corporation. All rights reserved. <BR>
5   Copyright (c) 2019 - 2020, Arm Limited. All rights reserved.<BR>
6 
7   SPDX-License-Identifier: BSD-2-Clause-Patent
8 **/
9 
10 #include <AmlNodeDefines.h>
11 #include <AmlDbgPrint/AmlDbgPrint.h>
12 
13 #include <AmlCoreInterface.h>
14 #include <String/AmlString.h>
15 #include <Tree/AmlNode.h>
16 #include <Tree/AmlTreeTraversal.h>
17 
18 #if !defined (MDEPKG_NDEBUG)
19 
20 /** String table representing AML Data types as defined by EAML_NODE_DATA_TYPE.
21 */
22 CONST CHAR8 * NodeDataTypeStrTbl[] = {
23   "EAmlNodeDataTypeNone",
24   "EAmlNodeDataTypeReserved1",
25   "EAmlNodeDataTypeReserved2",
26   "EAmlNodeDataTypeReserved3",
27   "EAmlNodeDataTypeReserved4",
28   "EAmlNodeDataTypeReserved5",
29   "EAmlNodeDataTypeNameString",
30   "EAmlNodeDataTypeString",
31   "EAmlNodeDataTypeUInt",
32   "EAmlNodeDataTypeRaw",
33   "EAmlNodeDataTypeResourceData",
34   "EAmlNodeDataTypeFieldPkgLen",
35   "EAmlNodeDataTypeMax"
36 };
37 
38 /** String table representing AML Node types as defined by EAML_NODE_TYPE.
39 */
40 CONST CHAR8 * NodeTypeStrTbl[] = {
41   "EAmlNodeUnknown",
42   "EAmlNodeRoot",
43   "EAmlNodeObject",
44   "EAmlNodeData",
45   "EAmlNodeMax"
46 };
47 
48 /** Print Size chars at Buffer address.
49 
50   @param  [in]  ErrorLevel    Error level for the DEBUG macro.
51   @param  [in]  Buffer        Buffer containing the chars.
52   @param  [in]  Size          Number of chars to print.
53 **/
54 VOID
55 EFIAPI
AmlDbgPrintChars(IN UINT32 ErrorLevel,IN CONST CHAR8 * Buffer,IN UINT32 Size)56 AmlDbgPrintChars (
57   IN        UINT32      ErrorLevel,
58   IN  CONST CHAR8     * Buffer,
59   IN        UINT32      Size
60   )
61 {
62   UINT32  i;
63 
64   if (Buffer == NULL) {
65     ASSERT (0);
66     return;
67   }
68 
69   for (i = 0; i < Size; i++) {
70     DEBUG ((ErrorLevel, "%c", Buffer[i]));
71   }
72 }
73 
74 /** Print an AML NameSeg.
75     Don't print trailing underscores ('_').
76 
77   @param  [in] Buffer   Buffer containing an AML NameSeg.
78 **/
79 VOID
80 EFIAPI
AmlDbgPrintNameSeg(IN CONST CHAR8 * Buffer)81 AmlDbgPrintNameSeg (
82   IN  CONST CHAR8   * Buffer
83   )
84 {
85   if (Buffer == NULL) {
86     ASSERT (0);
87     return;
88   }
89 
90   DEBUG ((DEBUG_INFO, "%c", Buffer[0]));
91   if ((Buffer[1] == AML_NAME_CHAR__)  &&
92       (Buffer[2] == AML_NAME_CHAR__)  &&
93       (Buffer[3] == AML_NAME_CHAR__)) {
94     return;
95   }
96   DEBUG ((DEBUG_INFO, "%c", Buffer[1]));
97   if ((Buffer[2] == AML_NAME_CHAR__)  &&
98       (Buffer[3] == AML_NAME_CHAR__)) {
99     return;
100   }
101   DEBUG ((DEBUG_INFO, "%c", Buffer[2]));
102   if (Buffer[3] == AML_NAME_CHAR__) {
103     return;
104   }
105   DEBUG ((DEBUG_INFO, "%c", Buffer[3]));
106   return;
107 }
108 
109 /** Print an AML NameString.
110 
111   @param  [in] Buffer   Buffer containing an AML NameString.
112   @param  [in] NewLine  Print a newline char at the end of the NameString.
113 **/
114 VOID
115 EFIAPI
AmlDbgPrintNameString(IN CONST CHAR8 * Buffer,IN BOOLEAN NewLine)116 AmlDbgPrintNameString (
117   IN  CONST CHAR8   * Buffer,
118   IN        BOOLEAN   NewLine
119   )
120 {
121   UINT8     SegCount;
122   UINT8     Index;
123 
124   if (Buffer == NULL) {
125     ASSERT (0);
126     return;
127   }
128 
129   // Handle Root and Parent(s).
130   if (*Buffer == AML_ROOT_CHAR) {
131     Buffer++;
132     DEBUG ((DEBUG_INFO, "\\"));
133   } else if (*Buffer == AML_PARENT_PREFIX_CHAR) {
134     do {
135       Buffer++;
136       DEBUG ((DEBUG_INFO, "^"));
137     } while (*Buffer == AML_PARENT_PREFIX_CHAR);
138   }
139 
140   // Handle SegCount(s).
141   if (*Buffer == AML_DUAL_NAME_PREFIX) {
142     Buffer++;
143     SegCount = 2;
144   } else if (*Buffer == AML_MULTI_NAME_PREFIX) {
145     Buffer++;
146     // For multi name prefix the seg count is in the second byte.
147     SegCount = *Buffer;
148     Buffer++;
149   } else if (AmlIsLeadNameChar (*Buffer)) {
150     // Only check the first char first to avoid overflow.
151     // Then the whole NameSeg can be checked.
152     if (!AmlIsNameSeg (Buffer)) {
153       ASSERT (0);
154       return;
155     }
156     SegCount = 1;
157   } else if (*Buffer == AML_ZERO_OP) {
158     SegCount = 0;
159   } else {
160     // Should not be possible.
161     ASSERT (0);
162     return;
163   }
164 
165   if (SegCount != 0) {
166     AmlDbgPrintNameSeg (Buffer);
167     Buffer += AML_NAME_SEG_SIZE;
168     for (Index = 0; Index < SegCount - 1; Index++) {
169       DEBUG ((DEBUG_INFO, "."));
170       AmlDbgPrintNameSeg (Buffer);
171       Buffer += AML_NAME_SEG_SIZE;
172     }
173   }
174 
175   if (NewLine) {
176     DEBUG ((DEBUG_INFO, "\n"));
177   }
178 
179   return;
180 }
181 
182 /** Print the information contained in the header of the Node.
183 
184   @param  [in]  Node    Pointer to a node.
185   @param  [in]  Level   Level of the indentation.
186 **/
187 STATIC
188 VOID
189 EFIAPI
AmlDbgPrintNodeHeader(IN AML_NODE_HEADER * Node,IN UINT8 Level)190 AmlDbgPrintNodeHeader (
191   IN  AML_NODE_HEADER  * Node,
192   IN  UINT8              Level
193   )
194 {
195   if (!IS_AML_NODE_VALID (Node)) {
196     ASSERT (0);
197     return;
198   }
199 
200   DEBUG ((
201     DEBUG_INFO,
202     "%3d | %-15s | ",
203     Level,
204     NodeTypeStrTbl[Node->NodeType]
205     ));
206 }
207 
208 /** Print fields of a data node.
209 
210   @param  [in]  DataNode  Pointer to a data node.
211   @param  [in]  Level     Level of the indentation.
212 **/
213 STATIC
214 VOID
215 EFIAPI
AmlDbgPrintDataNode(IN AML_DATA_NODE * DataNode,IN UINT8 Level)216 AmlDbgPrintDataNode (
217   IN  AML_DATA_NODE   * DataNode,
218   IN  UINT8             Level
219   )
220 {
221   UINT32  Idx;
222 
223   if (!IS_AML_DATA_NODE (DataNode)) {
224     ASSERT (0);
225     return;
226   }
227 
228   AmlDbgPrintNodeHeader ((AML_NODE_HEADER*)DataNode, Level);
229 
230   DEBUG ((DEBUG_INFO, "%-36s | ", NodeDataTypeStrTbl[DataNode->DataType]));
231   DEBUG ((DEBUG_INFO, "0x%04x | ", DataNode->Size));
232 
233   if ((DataNode->DataType == EAmlNodeDataTypeNameString) ||
234       (DataNode->DataType == EAmlNodeDataTypeString)) {
235     AmlDbgPrintChars (
236       DEBUG_INFO,
237       (CONST CHAR8*)DataNode->Buffer,
238       DataNode->Size
239       );
240   } else if (DataNode->DataType == EAmlNodeDataTypeUInt) {
241     switch (DataNode->Size) {
242       case 1:
243       {
244         DEBUG ((DEBUG_INFO, "0x%0x", *((UINT8*)DataNode->Buffer)));
245         break;
246       }
247       case 2:
248       {
249         DEBUG ((DEBUG_INFO, "0x%0x", *((UINT16*)DataNode->Buffer)));
250         break;
251       }
252       case 4:
253       {
254         DEBUG ((DEBUG_INFO, "0x%0lx", *((UINT32*)DataNode->Buffer)));
255         break;
256       }
257       case 8:
258       {
259         DEBUG ((DEBUG_INFO, "0x%0llx", *((UINT64*)DataNode->Buffer)));
260         break;
261       }
262       default:
263       {
264         ASSERT (0);
265         return;
266       }
267     }
268   } else {
269     // No specific format.
270     for (Idx = 0; Idx < DataNode->Size; Idx++) {
271       DEBUG ((DEBUG_INFO, "%02x ", DataNode->Buffer[Idx]));
272     }
273   }
274 
275   DEBUG ((DEBUG_INFO, "\n"));
276 }
277 
278 /** Print fields of an object node.
279 
280   @param  [in]  ObjectNode  Pointer to an object node.
281   @param  [in]  Level       Level of the indentation.
282 **/
283 STATIC
284 VOID
285 EFIAPI
AmlDbgPrintObjectNode(IN AML_OBJECT_NODE * ObjectNode,IN UINT8 Level)286 AmlDbgPrintObjectNode (
287   IN  AML_OBJECT_NODE  * ObjectNode,
288   IN  UINT8              Level
289   )
290 {
291   if (!IS_AML_OBJECT_NODE (ObjectNode)) {
292     ASSERT (0);
293     return;
294   }
295 
296   AmlDbgPrintNodeHeader ((AML_NODE_HEADER*)ObjectNode, Level);
297 
298   DEBUG ((DEBUG_INFO, "0x%02x | ", ObjectNode->AmlByteEncoding->OpCode));
299   DEBUG ((DEBUG_INFO, "0x%02x | ", ObjectNode->AmlByteEncoding->SubOpCode));
300 
301   // Print a string corresponding to the field object OpCode/SubOpCode.
302   if (AmlNodeHasAttribute (ObjectNode, AML_IS_FIELD_ELEMENT)) {
303     DEBUG ((DEBUG_INFO, "%-15s ", AmlGetFieldOpCodeStr (
304                                     ObjectNode->AmlByteEncoding->OpCode,
305                                     0
306                                     )));
307   } else {
308     // Print a string corresponding to the object OpCode/SubOpCode.
309     DEBUG ((DEBUG_INFO, "%-15s | ", AmlGetOpCodeStr (
310                                       ObjectNode->AmlByteEncoding->OpCode,
311                                       ObjectNode->AmlByteEncoding->SubOpCode)
312                                       ));
313   }
314 
315   DEBUG ((DEBUG_INFO, "%3d | ", ObjectNode->AmlByteEncoding->MaxIndex));
316   DEBUG ((DEBUG_INFO, "0x%08x | ", ObjectNode->AmlByteEncoding->Attribute));
317   DEBUG ((DEBUG_INFO, "0x%04x | ", ObjectNode->PkgLen));
318   if (AmlNodeHasAttribute (ObjectNode, AML_IN_NAMESPACE)) {
319     AmlDbgPrintNameString (
320       AmlNodeGetName ((CONST AML_OBJECT_NODE*)ObjectNode),
321       FALSE
322       );
323   }
324 
325   DEBUG ((DEBUG_INFO, "\n"));
326 }
327 
328 /** Print fields of a root node.
329 
330   @param  [in]  RootNode  Pointer to a root node.
331   @param  [in]  Level     Level of the indentation.
332 **/
333 STATIC
334 VOID
335 EFIAPI
AmlDbgPrintRootNode(IN AML_ROOT_NODE * RootNode,IN UINT8 Level)336 AmlDbgPrintRootNode (
337   IN  AML_ROOT_NODE  * RootNode,
338   IN  UINT8            Level
339   )
340 {
341   if (!IS_AML_ROOT_NODE (RootNode)) {
342     ASSERT (0);
343     return;
344   }
345 
346   AmlDbgPrintNodeHeader ((AML_NODE_HEADER*)RootNode, Level);
347 
348   DEBUG ((DEBUG_INFO, "%8x | ", RootNode->SdtHeader->Signature));
349   DEBUG ((DEBUG_INFO, "0x%08x | ", RootNode->SdtHeader->Length));
350   DEBUG ((DEBUG_INFO, "%3d | ", RootNode->SdtHeader->Revision));
351   DEBUG ((DEBUG_INFO, "0x%02x | ", RootNode->SdtHeader->Checksum));
352   DEBUG ((
353     DEBUG_INFO,
354     "%c%c%c%c%c%c | ",
355     RootNode->SdtHeader->OemId[0],
356     RootNode->SdtHeader->OemId[1],
357     RootNode->SdtHeader->OemId[2],
358     RootNode->SdtHeader->OemId[3],
359     RootNode->SdtHeader->OemId[4],
360     RootNode->SdtHeader->OemId[5]
361     ));
362   DEBUG ((DEBUG_INFO, "%-16llx | ", RootNode->SdtHeader->OemTableId));
363   DEBUG ((DEBUG_INFO, "%8x | ", RootNode->SdtHeader->OemRevision));
364   DEBUG ((DEBUG_INFO, "%8x | ", RootNode->SdtHeader->CreatorId));
365   DEBUG ((DEBUG_INFO, "%8x", RootNode->SdtHeader->CreatorRevision));
366   DEBUG ((DEBUG_INFO, "\n"));
367 }
368 
369 /** Print a header to help interpreting node information.
370 **/
371 STATIC
372 VOID
373 EFIAPI
AmlDbgPrintTableHeader(VOID)374 AmlDbgPrintTableHeader (
375   VOID
376   )
377 {
378   DEBUG ((DEBUG_INFO, "Lvl | Node Type       |\n"));
379   DEBUG ((
380     DEBUG_INFO,
381     "    | %-15s | Signature| Length     | Rev | CSum | OemId  | "
382       "OemTableId       | OemRev   | CreatorId| CreatorRev\n",
383     NodeTypeStrTbl[EAmlNodeRoot]
384     ));
385   DEBUG ((
386     DEBUG_INFO,
387     "    | %-15s | Op   | SubOp| OpName          | MaxI| Attribute  | "
388       "PkgLen | NodeName (opt)\n",
389     NodeTypeStrTbl[EAmlNodeObject]
390     ));
391   DEBUG ((
392     DEBUG_INFO,
393     "    | %-15s | Data Type                            | Size   | "
394       "Buffer\n",
395     NodeTypeStrTbl[EAmlNodeData]
396     ));
397   DEBUG ((
398     DEBUG_INFO,
399     "---------------------------------------"
400       "---------------------------------------\n"
401     ));
402 }
403 
404 /** Recursively print the subtree under the Node.
405     This is an internal function.
406 
407   @param  [in]  Node            Pointer to the root of the subtree to print.
408                                 Can be a root/object/data node.
409   @param  [in]  Recurse         If TRUE, recurse.
410   @param  [in]  Level           Level in the tree.
411 **/
412 STATIC
413 VOID
414 EFIAPI
AmlDbgPrintTreeInternal(IN AML_NODE_HEADER * Node,IN BOOLEAN Recurse,IN UINT8 Level)415 AmlDbgPrintTreeInternal (
416   IN  AML_NODE_HEADER   * Node,
417   IN  BOOLEAN             Recurse,
418   IN  UINT8               Level
419   )
420 {
421   AML_NODE_HEADER   * ChildNode;
422 
423   if (!IS_AML_NODE_VALID (Node)) {
424     ASSERT (0);
425     return;
426   }
427 
428   if (IS_AML_DATA_NODE (Node)) {
429     AmlDbgPrintDataNode ((AML_DATA_NODE*)Node, Level);
430     return;
431   } else if (IS_AML_OBJECT_NODE (Node)) {
432     AmlDbgPrintObjectNode ((AML_OBJECT_NODE*)Node, Level);
433   } else if (IS_AML_ROOT_NODE (Node)) {
434     AmlDbgPrintRootNode ((AML_ROOT_NODE*)Node, Level);
435   } else {
436     // Should not be possible.
437     ASSERT (0);
438     return;
439   }
440 
441   if (!Recurse) {
442     return;
443   }
444 
445   // Get the first child node.
446   ChildNode = AmlGetNextSibling (Node, NULL);
447   while (ChildNode != NULL) {
448     ASSERT (Level < MAX_UINT8);
449     AmlDbgPrintTreeInternal (ChildNode, Recurse, (UINT8)(Level + 1));
450     ChildNode = AmlGetNextSibling (Node, ChildNode);
451   }
452 }
453 
454 /** Print Node information.
455 
456   @param  [in]  Node    Pointer to the Node to print.
457                         Can be a root/object/data node.
458 **/
459 VOID
460 EFIAPI
AmlDbgPrintNode(IN AML_NODE_HEADER * Node)461 AmlDbgPrintNode (
462   IN  AML_NODE_HEADER   * Node
463   )
464 {
465   AmlDbgPrintTableHeader ();
466   AmlDbgPrintTreeInternal (Node, FALSE, 0);
467 }
468 
469 /** Recursively print the subtree under the Node.
470 
471   @param  [in]  Node    Pointer to the root of the subtree to print.
472                         Can be a root/object/data node.
473 **/
474 VOID
475 EFIAPI
AmlDbgPrintTree(IN AML_NODE_HEADER * Node)476 AmlDbgPrintTree (
477   IN  AML_NODE_HEADER   * Node
478   )
479 {
480   AmlDbgPrintTableHeader ();
481   AmlDbgPrintTreeInternal (Node, TRUE, 0);
482 }
483 
484 /** This function performs a raw data dump of the ACPI table.
485 
486   @param  [in]  Ptr     Pointer to the start of the table buffer.
487   @param  [in]  Length  The length of the buffer.
488 **/
489 VOID
490 EFIAPI
DumpRaw(IN CONST UINT8 * Ptr,IN UINT32 Length)491 DumpRaw (
492   IN  CONST UINT8   * Ptr,
493   IN        UINT32    Length
494   )
495 {
496   UINT32  ByteCount;
497   UINT32  PartLineChars;
498   UINT32  AsciiBufferIndex;
499   CHAR8   AsciiBuffer[17];
500 
501   ByteCount = 0;
502   AsciiBufferIndex = 0;
503 
504   DEBUG ((DEBUG_VERBOSE, "Address  : 0x%p\n", Ptr));
505   DEBUG ((DEBUG_VERBOSE, "Length   : %lld", Length));
506 
507   while (ByteCount < Length) {
508     if ((ByteCount & 0x0F) == 0) {
509       AsciiBuffer[AsciiBufferIndex] = '\0';
510       DEBUG ((DEBUG_VERBOSE, "  %a\n%08X : ", AsciiBuffer, ByteCount));
511       AsciiBufferIndex = 0;
512     } else if ((ByteCount & 0x07) == 0) {
513       DEBUG ((DEBUG_VERBOSE, "- "));
514     }
515 
516     if ((*Ptr >= ' ') && (*Ptr < 0x7F)) {
517       AsciiBuffer[AsciiBufferIndex++] = *Ptr;
518     } else {
519       AsciiBuffer[AsciiBufferIndex++] = '.';
520     }
521 
522     DEBUG ((DEBUG_VERBOSE, "%02X ", *Ptr++));
523 
524     ByteCount++;
525   }
526 
527   // Justify the final line using spaces before printing
528   // the ASCII data.
529   PartLineChars = (Length & 0x0F);
530   if (PartLineChars != 0) {
531     PartLineChars = 48 - (PartLineChars * 3);
532     if ((Length & 0x0F) <= 8) {
533       PartLineChars += 2;
534     }
535     while (PartLineChars > 0) {
536       DEBUG ((DEBUG_VERBOSE, " "));
537       PartLineChars--;
538     }
539   }
540 
541   // Print ASCII data for the final line.
542   AsciiBuffer[AsciiBufferIndex] = '\0';
543   DEBUG ((DEBUG_VERBOSE, "  %a\n\n", AsciiBuffer));
544 }
545 
546 #endif // MDEPKG_NDEBUG
547