/** @file AML Print Function. Copyright (c) 2010 - 2018, Intel Corporation. All rights reserved.
Copyright (c) 2019 - 2021, Arm Limited. All rights reserved.
SPDX-License-Identifier: BSD-2-Clause-Patent **/ #include #include #include #include #include #include #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