+/** @file\r
+ AML Print Function.\r
+\r
+ Copyright (c) 2010 - 2018, Intel Corporation. All rights reserved. <BR>\r
+ Copyright (c) 2019 - 2020, Arm Limited. All rights reserved.<BR>\r
+\r
+ SPDX-License-Identifier: BSD-2-Clause-Patent\r
+**/\r
+\r
+#include <AmlNodeDefines.h>\r
+#include <AmlDbgPrint/AmlDbgPrint.h>\r
+\r
+#include <AmlCoreInterface.h>\r
+#include <String/AmlString.h>\r
+#include <Tree/AmlNode.h>\r
+#include <Tree/AmlTreeTraversal.h>\r
+\r
+#if !defined (MDEPKG_NDEBUG)\r
+\r
+/** String table representing AML Data types as defined by EAML_NODE_DATA_TYPE.\r
+*/\r
+CONST CHAR8 * NodeDataTypeStrTbl[] = {\r
+ "EAmlNodeDataTypeNone",\r
+ "EAmlNodeDataTypeReserved1",\r
+ "EAmlNodeDataTypeReserved2",\r
+ "EAmlNodeDataTypeReserved3",\r
+ "EAmlNodeDataTypeReserved4",\r
+ "EAmlNodeDataTypeReserved5",\r
+ "EAmlNodeDataTypeNameString",\r
+ "EAmlNodeDataTypeString",\r
+ "EAmlNodeDataTypeUInt",\r
+ "EAmlNodeDataTypeRaw",\r
+ "EAmlNodeDataTypeResourceData",\r
+ "EAmlNodeDataTypeFieldPkgLen",\r
+ "EAmlNodeDataTypeMax"\r
+};\r
+\r
+/** String table representing AML Node types as defined by EAML_NODE_TYPE.\r
+*/\r
+CONST CHAR8 * NodeTypeStrTbl[] = {\r
+ "EAmlNodeUnknown",\r
+ "EAmlNodeRoot",\r
+ "EAmlNodeObject",\r
+ "EAmlNodeData",\r
+ "EAmlNodeMax"\r
+};\r
+\r
+/** Print Size chars at Buffer address.\r
+\r
+ @param [in] ErrorLevel Error level for the DEBUG macro.\r
+ @param [in] Buffer Buffer containing the chars.\r
+ @param [in] Size Number of chars to print.\r
+**/\r
+VOID\r
+EFIAPI\r
+AmlDbgPrintChars (\r
+ IN UINT32 ErrorLevel,\r
+ IN CONST CHAR8 * Buffer,\r
+ IN UINT32 Size\r
+ )\r
+{\r
+ UINT32 i;\r
+\r
+ if (Buffer == NULL) {\r
+ ASSERT (0);\r
+ return;\r
+ }\r
+\r
+ for (i = 0; i < Size; i++) {\r
+ DEBUG ((ErrorLevel, "%c", Buffer[i]));\r
+ }\r
+}\r
+\r
+/** Print an AML NameSeg.\r
+ Don't print trailing underscores ('_').\r
+\r
+ @param [in] Buffer Buffer containing an AML NameSeg.\r
+**/\r
+VOID\r
+EFIAPI\r
+AmlDbgPrintNameSeg (\r
+ IN CONST CHAR8 * Buffer\r
+ )\r
+{\r
+ if (Buffer == NULL) {\r
+ ASSERT (0);\r
+ return;\r
+ }\r
+\r
+ DEBUG ((DEBUG_INFO, "%c", Buffer[0]));\r
+ if ((Buffer[1] == AML_NAME_CHAR__) &&\r
+ (Buffer[2] == AML_NAME_CHAR__) &&\r
+ (Buffer[3] == AML_NAME_CHAR__)) {\r
+ return;\r
+ }\r
+ DEBUG ((DEBUG_INFO, "%c", Buffer[1]));\r
+ if ((Buffer[2] == AML_NAME_CHAR__) &&\r
+ (Buffer[3] == AML_NAME_CHAR__)) {\r
+ return;\r
+ }\r
+ DEBUG ((DEBUG_INFO, "%c", Buffer[2]));\r
+ if (Buffer[3] == AML_NAME_CHAR__) {\r
+ return;\r
+ }\r
+ DEBUG ((DEBUG_INFO, "%c", Buffer[3]));\r
+ return;\r
+}\r
+\r
+/** Print an AML NameString.\r
+\r
+ @param [in] Buffer Buffer containing an AML NameString.\r
+ @param [in] NewLine Print a newline char at the end of the NameString.\r
+**/\r
+VOID\r
+EFIAPI\r
+AmlDbgPrintNameString (\r
+ IN CONST CHAR8 * Buffer,\r
+ IN BOOLEAN NewLine\r
+ )\r
+{\r
+ UINT8 SegCount;\r
+ UINT8 Index;\r
+\r
+ if (Buffer == NULL) {\r
+ ASSERT (0);\r
+ return;\r
+ }\r
+\r
+ // Handle Root and Parent(s).\r
+ if (*Buffer == AML_ROOT_CHAR) {\r
+ Buffer++;\r
+ DEBUG ((DEBUG_INFO, "\\"));\r
+ } else if (*Buffer == AML_PARENT_PREFIX_CHAR) {\r
+ do {\r
+ Buffer++;\r
+ DEBUG ((DEBUG_INFO, "^"));\r
+ } while (*Buffer == AML_PARENT_PREFIX_CHAR);\r
+ }\r
+\r
+ // Handle SegCount(s).\r
+ if (*Buffer == AML_DUAL_NAME_PREFIX) {\r
+ Buffer++;\r
+ SegCount = 2;\r
+ } else if (*Buffer == AML_MULTI_NAME_PREFIX) {\r
+ Buffer++;\r
+ // For multi name prefix the seg count is in the second byte.\r
+ SegCount = *Buffer;\r
+ Buffer++;\r
+ } else if (AmlIsLeadNameChar (*Buffer)) {\r
+ // Only check the first char first to avoid overflow.\r
+ // Then the whole NameSeg can be checked.\r
+ if (!AmlIsNameSeg (Buffer)) {\r
+ ASSERT (0);\r
+ return;\r
+ }\r
+ SegCount = 1;\r
+ } else if (*Buffer == AML_ZERO_OP) {\r
+ SegCount = 0;\r
+ } else {\r
+ // Should not be possible.\r
+ ASSERT (0);\r
+ return;\r
+ }\r
+\r
+ if (SegCount != 0) {\r
+ AmlDbgPrintNameSeg (Buffer);\r
+ Buffer += AML_NAME_SEG_SIZE;\r
+ for (Index = 0; Index < SegCount - 1; Index++) {\r
+ DEBUG ((DEBUG_INFO, "."));\r
+ AmlDbgPrintNameSeg (Buffer);\r
+ Buffer += AML_NAME_SEG_SIZE;\r
+ }\r
+ }\r
+\r
+ if (NewLine) {\r
+ DEBUG ((DEBUG_INFO, "\n"));\r
+ }\r
+\r
+ return;\r
+}\r
+\r
+/** Print the information contained in the header of the Node.\r
+\r
+ @param [in] Node Pointer to a node.\r
+ @param [in] Level Level of the indentation.\r
+**/\r
+STATIC\r
+VOID\r
+EFIAPI\r
+AmlDbgPrintNodeHeader (\r
+ IN AML_NODE_HEADER * Node,\r
+ IN UINT8 Level\r
+ )\r
+{\r
+ if (!IS_AML_NODE_VALID (Node)) {\r
+ ASSERT (0);\r
+ return;\r
+ }\r
+\r
+ DEBUG ((\r
+ DEBUG_INFO,\r
+ "%3d | %-15s | ",\r
+ Level,\r
+ NodeTypeStrTbl[Node->NodeType]\r
+ ));\r
+}\r
+\r
+/** Print fields of a data node.\r
+\r
+ @param [in] DataNode Pointer to a data node.\r
+ @param [in] Level Level of the indentation.\r
+**/\r
+STATIC\r
+VOID\r
+EFIAPI\r
+AmlDbgPrintDataNode (\r
+ IN AML_DATA_NODE * DataNode,\r
+ IN UINT8 Level\r
+ )\r
+{\r
+ UINT32 Idx;\r
+\r
+ if (!IS_AML_DATA_NODE (DataNode)) {\r
+ ASSERT (0);\r
+ return;\r
+ }\r
+\r
+ AmlDbgPrintNodeHeader ((AML_NODE_HEADER*)DataNode, Level);\r
+\r
+ DEBUG ((DEBUG_INFO, "%-36s | ", NodeDataTypeStrTbl[DataNode->DataType]));\r
+ DEBUG ((DEBUG_INFO, "0x%04x | ", DataNode->Size));\r
+\r
+ if ((DataNode->DataType == EAmlNodeDataTypeNameString) ||\r
+ (DataNode->DataType == EAmlNodeDataTypeString)) {\r
+ AmlDbgPrintChars (\r
+ DEBUG_INFO,\r
+ (CONST CHAR8*)DataNode->Buffer,\r
+ DataNode->Size\r
+ );\r
+ } else if (DataNode->DataType == EAmlNodeDataTypeUInt) {\r
+ switch (DataNode->Size) {\r
+ case 1:\r
+ {\r
+ DEBUG ((DEBUG_INFO, "0x%0x", *((UINT8*)DataNode->Buffer)));\r
+ break;\r
+ }\r
+ case 2:\r
+ {\r
+ DEBUG ((DEBUG_INFO, "0x%0x", *((UINT16*)DataNode->Buffer)));\r
+ break;\r
+ }\r
+ case 4:\r
+ {\r
+ DEBUG ((DEBUG_INFO, "0x%0lx", *((UINT32*)DataNode->Buffer)));\r
+ break;\r
+ }\r
+ case 8:\r
+ {\r
+ DEBUG ((DEBUG_INFO, "0x%0llx", *((UINT64*)DataNode->Buffer)));\r
+ break;\r
+ }\r
+ default:\r
+ {\r
+ ASSERT (0);\r
+ return;\r
+ }\r
+ }\r
+ } else {\r
+ // No specific format.\r
+ for (Idx = 0; Idx < DataNode->Size; Idx++) {\r
+ DEBUG ((DEBUG_INFO, "%02x ", DataNode->Buffer[Idx]));\r
+ }\r
+ }\r
+\r
+ DEBUG ((DEBUG_INFO, "\n"));\r
+}\r
+\r
+/** Print fields of an object node.\r
+\r
+ @param [in] ObjectNode Pointer to an object node.\r
+ @param [in] Level Level of the indentation.\r
+**/\r
+STATIC\r
+VOID\r
+EFIAPI\r
+AmlDbgPrintObjectNode (\r
+ IN AML_OBJECT_NODE * ObjectNode,\r
+ IN UINT8 Level\r
+ )\r
+{\r
+ if (!IS_AML_OBJECT_NODE (ObjectNode)) {\r
+ ASSERT (0);\r
+ return;\r
+ }\r
+\r
+ AmlDbgPrintNodeHeader ((AML_NODE_HEADER*)ObjectNode, Level);\r
+\r
+ DEBUG ((DEBUG_INFO, "0x%02x | ", ObjectNode->AmlByteEncoding->OpCode));\r
+ DEBUG ((DEBUG_INFO, "0x%02x | ", ObjectNode->AmlByteEncoding->SubOpCode));\r
+\r
+ // Print a string corresponding to the field object OpCode/SubOpCode.\r
+ if (AmlNodeHasAttribute (ObjectNode, AML_IS_FIELD_ELEMENT)) {\r
+ DEBUG ((DEBUG_INFO, "%-15s ", AmlGetFieldOpCodeStr (\r
+ ObjectNode->AmlByteEncoding->OpCode,\r
+ 0\r
+ )));\r
+ } else {\r
+ // Print a string corresponding to the object OpCode/SubOpCode.\r
+ DEBUG ((DEBUG_INFO, "%-15s | ", AmlGetOpCodeStr (\r
+ ObjectNode->AmlByteEncoding->OpCode,\r
+ ObjectNode->AmlByteEncoding->SubOpCode)\r
+ ));\r
+ }\r
+\r
+ DEBUG ((DEBUG_INFO, "%3d | ", ObjectNode->AmlByteEncoding->MaxIndex));\r
+ DEBUG ((DEBUG_INFO, "0x%08x | ", ObjectNode->AmlByteEncoding->Attribute));\r
+ DEBUG ((DEBUG_INFO, "0x%04x | ", ObjectNode->PkgLen));\r
+ if (AmlNodeHasAttribute (ObjectNode, AML_IN_NAMESPACE)) {\r
+ AmlDbgPrintNameString (\r
+ AmlNodeGetName ((CONST AML_OBJECT_NODE*)ObjectNode),\r
+ FALSE\r
+ );\r
+ }\r
+\r
+ DEBUG ((DEBUG_INFO, "\n"));\r
+}\r
+\r
+/** Print fields of a root node.\r
+\r
+ @param [in] RootNode Pointer to a root node.\r
+ @param [in] Level Level of the indentation.\r
+**/\r
+STATIC\r
+VOID\r
+EFIAPI\r
+AmlDbgPrintRootNode (\r
+ IN AML_ROOT_NODE * RootNode,\r
+ IN UINT8 Level\r
+ )\r
+{\r
+ if (!IS_AML_ROOT_NODE (RootNode)) {\r
+ ASSERT (0);\r
+ return;\r
+ }\r
+\r
+ AmlDbgPrintNodeHeader ((AML_NODE_HEADER*)RootNode, Level);\r
+\r
+ DEBUG ((DEBUG_INFO, "%8x | ", RootNode->SdtHeader->Signature));\r
+ DEBUG ((DEBUG_INFO, "0x%08x | ", RootNode->SdtHeader->Length));\r
+ DEBUG ((DEBUG_INFO, "%3d | ", RootNode->SdtHeader->Revision));\r
+ DEBUG ((DEBUG_INFO, "0x%02x | ", RootNode->SdtHeader->Checksum));\r
+ DEBUG ((\r
+ DEBUG_INFO,\r
+ "%c%c%c%c%c%c | ",\r
+ RootNode->SdtHeader->OemId[0],\r
+ RootNode->SdtHeader->OemId[1],\r
+ RootNode->SdtHeader->OemId[2],\r
+ RootNode->SdtHeader->OemId[3],\r
+ RootNode->SdtHeader->OemId[4],\r
+ RootNode->SdtHeader->OemId[5]\r
+ ));\r
+ DEBUG ((DEBUG_INFO, "%-16llx | ", RootNode->SdtHeader->OemTableId));\r
+ DEBUG ((DEBUG_INFO, "%8x | ", RootNode->SdtHeader->OemRevision));\r
+ DEBUG ((DEBUG_INFO, "%8x | ", RootNode->SdtHeader->CreatorId));\r
+ DEBUG ((DEBUG_INFO, "%8x", RootNode->SdtHeader->CreatorRevision));\r
+ DEBUG ((DEBUG_INFO, "\n"));\r
+}\r
+\r
+/** Print a header to help interpreting node information.\r
+**/\r
+STATIC\r
+VOID\r
+EFIAPI\r
+AmlDbgPrintTableHeader (\r
+ VOID\r
+ )\r
+{\r
+ DEBUG ((DEBUG_INFO, "Lvl | Node Type |\n"));\r
+ DEBUG ((\r
+ DEBUG_INFO,\r
+ " | %-15s | Signature| Length | Rev | CSum | OemId | "\r
+ "OemTableId | OemRev | CreatorId| CreatorRev\n",\r
+ NodeTypeStrTbl[EAmlNodeRoot]\r
+ ));\r
+ DEBUG ((\r
+ DEBUG_INFO,\r
+ " | %-15s | Op | SubOp| OpName | MaxI| Attribute | "\r
+ "PkgLen | NodeName (opt)\n",\r
+ NodeTypeStrTbl[EAmlNodeObject]\r
+ ));\r
+ DEBUG ((\r
+ DEBUG_INFO,\r
+ " | %-15s | Data Type | Size | "\r
+ "Buffer\n",\r
+ NodeTypeStrTbl[EAmlNodeData]\r
+ ));\r
+ DEBUG ((\r
+ DEBUG_INFO,\r
+ "---------------------------------------"\r
+ "---------------------------------------\n"\r
+ ));\r
+}\r
+\r
+/** Recursively print the subtree under the Node.\r
+ This is an internal function.\r
+\r
+ @param [in] Node Pointer to the root of the subtree to print.\r
+ Can be a root/object/data node.\r
+ @param [in] Recurse If TRUE, recurse.\r
+ @param [in] Level Level in the tree.\r
+**/\r
+STATIC\r
+VOID\r
+EFIAPI\r
+AmlDbgPrintTreeInternal (\r
+ IN AML_NODE_HEADER * Node,\r
+ IN BOOLEAN Recurse,\r
+ IN UINT8 Level\r
+ )\r
+{\r
+ AML_NODE_HEADER * ChildNode;\r
+\r
+ if (!IS_AML_NODE_VALID (Node)) {\r
+ ASSERT (0);\r
+ return;\r
+ }\r
+\r
+ if (IS_AML_DATA_NODE (Node)) {\r
+ AmlDbgPrintDataNode ((AML_DATA_NODE*)Node, Level);\r
+ return;\r
+ } else if (IS_AML_OBJECT_NODE (Node)) {\r
+ AmlDbgPrintObjectNode ((AML_OBJECT_NODE*)Node, Level);\r
+ } else if (IS_AML_ROOT_NODE (Node)) {\r
+ AmlDbgPrintRootNode ((AML_ROOT_NODE*)Node, Level);\r
+ } else {\r
+ // Should not be possible.\r
+ ASSERT (0);\r
+ return;\r
+ }\r
+\r
+ if (!Recurse) {\r
+ return;\r
+ }\r
+\r
+ // Get the first child node.\r
+ ChildNode = AmlGetNextSibling (Node, NULL);\r
+ while (ChildNode != NULL) {\r
+ ASSERT (Level < MAX_UINT8);\r
+ AmlDbgPrintTreeInternal (ChildNode, Recurse, (UINT8)(Level + 1));\r
+ ChildNode = AmlGetNextSibling (Node, ChildNode);\r
+ }\r
+}\r
+\r
+/** Print Node information.\r
+\r
+ @param [in] Node Pointer to the Node to print.\r
+ Can be a root/object/data node.\r
+**/\r
+VOID\r
+EFIAPI\r
+AmlDbgPrintNode (\r
+ IN AML_NODE_HEADER * Node\r
+ )\r
+{\r
+ AmlDbgPrintTableHeader ();\r
+ AmlDbgPrintTreeInternal (Node, FALSE, 0);\r
+}\r
+\r
+/** Recursively print the subtree under the Node.\r
+\r
+ @param [in] Node Pointer to the root of the subtree to print.\r
+ Can be a root/object/data node.\r
+**/\r
+VOID\r
+EFIAPI\r
+AmlDbgPrintTree (\r
+ IN AML_NODE_HEADER * Node\r
+ )\r
+{\r
+ AmlDbgPrintTableHeader ();\r
+ AmlDbgPrintTreeInternal (Node, TRUE, 0);\r
+}\r
+\r
+/** This function performs a raw data dump of the ACPI table.\r
+\r
+ @param [in] Ptr Pointer to the start of the table buffer.\r
+ @param [in] Length The length of the buffer.\r
+**/\r
+VOID\r
+EFIAPI\r
+DumpRaw (\r
+ IN CONST UINT8 * Ptr,\r
+ IN UINT32 Length\r
+ )\r
+{\r
+ UINT32 ByteCount;\r
+ UINT32 PartLineChars;\r
+ UINT32 AsciiBufferIndex;\r
+ CHAR8 AsciiBuffer[17];\r
+\r
+ ByteCount = 0;\r
+ AsciiBufferIndex = 0;\r
+\r
+ DEBUG ((DEBUG_VERBOSE, "Address : 0x%p\n", Ptr));\r
+ DEBUG ((DEBUG_VERBOSE, "Length : %lld", Length));\r
+\r
+ while (ByteCount < Length) {\r
+ if ((ByteCount & 0x0F) == 0) {\r
+ AsciiBuffer[AsciiBufferIndex] = '\0';\r
+ DEBUG ((DEBUG_VERBOSE, " %a\n%08X : ", AsciiBuffer, ByteCount));\r
+ AsciiBufferIndex = 0;\r
+ } else if ((ByteCount & 0x07) == 0) {\r
+ DEBUG ((DEBUG_VERBOSE, "- "));\r
+ }\r
+\r
+ if ((*Ptr >= ' ') && (*Ptr < 0x7F)) {\r
+ AsciiBuffer[AsciiBufferIndex++] = *Ptr;\r
+ } else {\r
+ AsciiBuffer[AsciiBufferIndex++] = '.';\r
+ }\r
+\r
+ DEBUG ((DEBUG_VERBOSE, "%02X ", *Ptr++));\r
+\r
+ ByteCount++;\r
+ }\r
+\r
+ // Justify the final line using spaces before printing\r
+ // the ASCII data.\r
+ PartLineChars = (Length & 0x0F);\r
+ if (PartLineChars != 0) {\r
+ PartLineChars = 48 - (PartLineChars * 3);\r
+ if ((Length & 0x0F) <= 8) {\r
+ PartLineChars += 2;\r
+ }\r
+ while (PartLineChars > 0) {\r
+ DEBUG ((DEBUG_VERBOSE, " "));\r
+ PartLineChars--;\r
+ }\r
+ }\r
+\r
+ // Print ASCII data for the final line.\r
+ AsciiBuffer[AsciiBufferIndex] = '\0';\r
+ DEBUG ((DEBUG_VERBOSE, " %a\n\n", AsciiBuffer));\r
+}\r
+\r
+#endif // MDEPKG_NDEBUG\r