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