Files
system76-edk2/DynamicTablesPkg/Library/Common/AmlLib/AmlDbgPrint/AmlDbgPrint.c
Pierre Gondois 340f8f4565 DynamicTablesPkg: AML debug logging
The AML debug print functions enable logging
of the operations on the AML tree and the data
output. The debug logging functionality is
enabled for debug builds when the DEBUG_INFO
or DEBUG_VERBOSE mask is enabled in the PCD
gEfiMdePkgTokenSpaceGuid.PcdDebugPrintErrorLevel

Signed-off-by: Pierre Gondois <pierre.gondois@arm.com>
Signed-off-by: Sami Mujawar <sami.mujawar@arm.com>
Reviewed-by: Alexei Fedorov <Alexei.Fedorov@arm.com>
2020-08-13 18:00:06 +00:00

547 lines
13 KiB
C

/** @file
AML Print Function.
Copyright (c) 2010 - 2018, Intel Corporation. All rights reserved. <BR>
Copyright (c) 2019 - 2020, 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) {
AmlDbgPrintNameSeg (Buffer);
Buffer += AML_NAME_SEG_SIZE;
for (Index = 0; Index < SegCount - 1; Index++) {
DEBUG ((DEBUG_INFO, "."));
AmlDbgPrintNameSeg (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 | %-15s | ",
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, "%-36s | ", NodeDataTypeStrTbl[DataNode->DataType]));
DEBUG ((DEBUG_INFO, "0x%04x | ", DataNode->Size));
if ((DataNode->DataType == EAmlNodeDataTypeNameString) ||
(DataNode->DataType == EAmlNodeDataTypeString)) {
AmlDbgPrintChars (
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, "%-15s ", AmlGetFieldOpCodeStr (
ObjectNode->AmlByteEncoding->OpCode,
0
)));
} else {
// Print a string corresponding to the object OpCode/SubOpCode.
DEBUG ((DEBUG_INFO, "%-15s | ", 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)) {
AmlDbgPrintNameString (
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,
" | %-15s | Signature| Length | Rev | CSum | OemId | "
"OemTableId | OemRev | CreatorId| CreatorRev\n",
NodeTypeStrTbl[EAmlNodeRoot]
));
DEBUG ((
DEBUG_INFO,
" | %-15s | Op | SubOp| OpName | MaxI| Attribute | "
"PkgLen | NodeName (opt)\n",
NodeTypeStrTbl[EAmlNodeObject]
));
DEBUG ((
DEBUG_INFO,
" | %-15s | 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
DumpRaw (
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