/** @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