/** @file
ACPI Sdt Protocol Driver
Copyright (c) 2010, Intel Corporation. All rights reserved. <BR>
This program and the accompanying materials
are licensed and made available under the terms and conditions of the BSD License
which accompanies this distribution. The full text of the license may be found at
http://opensource.org/licenses/bsd-license.php
THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
**/
#include "AcpiTable.h"
/**
Construct node list according to the AML handle.
@param[in] AmlHandle AML handle.
@param[in] AmlRootNodeList AML root node list.
@param[in] AmlParentNodeList AML parent node list.
@retval EFI_SUCCESS Success.
@retval EFI_INVALID_PARAMETER AML handle does not refer to a valid ACPI object.
**/
EFI_STATUS
AmlConstructNodeList (
IN EFI_AML_HANDLE *AmlHandle,
IN EFI_AML_NODE_LIST *AmlRootNodeList,
IN EFI_AML_NODE_LIST *AmlParentNodeList
);
/**
Create AML Node.
@param[in] NameSeg AML NameSeg.
@param[in] Parent AML parent node list.
@param[in] AmlByteEncoding AML Byte Encoding.
@return AML Node.
**/
EFI_AML_NODE_LIST *
AmlCreateNode (
IN UINT8 *NameSeg,
IN EFI_AML_NODE_LIST *Parent,
IN AML_BYTE_ENCODING *AmlByteEncoding
)
{
EFI_AML_NODE_LIST *AmlNodeList;
AmlNodeList = AllocatePool (sizeof(*AmlNodeList));
ASSERT (AmlNodeList != NULL);
AmlNodeList->Signature = EFI_AML_NODE_LIST_SIGNATURE;
CopyMem (AmlNodeList->Name, NameSeg, AML_NAME_SEG_SIZE);
AmlNodeList->Buffer = NULL;
AmlNodeList->Size = 0;
InitializeListHead (&AmlNodeList->Link);
InitializeListHead (&AmlNodeList->Children);
AmlNodeList->Parent = Parent;
AmlNodeList->AmlByteEncoding = AmlByteEncoding;
return AmlNodeList;
}
/**
Find the AML NameSeg in the children of AmlParentNodeList.
@param[in] NameSeg AML NameSeg.
@param[in] AmlParentNodeList AML parent node list.
@param[in] Create TRUE means to create node if not found.
@return AmlChildNode whoes name is same as NameSeg.
**/
EFI_AML_NODE_LIST *
AmlFindNodeInThis (
IN UINT8 *NameSeg,
IN EFI_AML_NODE_LIST *AmlParentNodeList,
IN BOOLEAN Create
)
{
EFI_AML_NODE_LIST *CurrentAmlNodeList;
LIST_ENTRY *CurrentLink;
LIST_ENTRY *StartLink;
EFI_AML_NODE_LIST *AmlNodeList;
StartLink = &AmlParentNodeList->Children;
CurrentLink = StartLink->ForwardLink;
while (CurrentLink != StartLink) {
CurrentAmlNodeList = EFI_AML_NODE_LIST_FROM_LINK (CurrentLink);
//
// AML name is same as the one stored
//
if (CompareMem (CurrentAmlNodeList->Name, NameSeg, AML_NAME_SEG_SIZE) == 0) {
//
// Good! Found it
//
return CurrentAmlNodeList;
}
CurrentLink = CurrentLink->ForwardLink;
}
//
// Not found
//
if (!Create) {
return NULL;
}
//
// Create new node with NULL buffer - it means namespace not be returned.
//
AmlNodeList = AmlCreateNode (NameSeg, AmlParentNodeList, NULL);
InsertTailList (&AmlParentNodeList->Children, &AmlNodeList->Link);
return AmlNodeList;
}
/**
Find the AML NameString in the children of AmlParentNodeList or AmlRootNodeList.
@param[in] NameString AML NameString.
@param[in] AmlRootNodeList AML root node list.
@param[in] AmlParentNodeList AML parent node list.
@param[in] Create TRUE means to create node if not found.
@return AmlChildNode whoes name is same as NameSeg.
**/
EFI_AML_NODE_LIST *
AmlFindNodeInTheTree (
IN UINT8 *NameString,
IN EFI_AML_NODE_LIST *AmlRootNodeList,
IN EFI_AML_NODE_LIST *AmlParentNodeList,
IN BOOLEAN Create
)
{
UINT8 *Buffer;
EFI_AML_NODE_LIST *AmlNodeList;
EFI_AML_NODE_LIST *AmlCurrentNodeList;
UINT8 Index;
UINT8 SegCount;
Buffer = NameString;
//
// Handle root or parent prefix
//
if (*Buffer == AML_ROOT_CHAR) {
AmlCurrentNodeList = AmlRootNodeList;
Buffer += 1;
} else if (*Buffer == AML_PARENT_PREFIX_CHAR) {
AmlCurrentNodeList = AmlParentNodeList;
do {
if (AmlCurrentNodeList->Parent != NULL) {
AmlCurrentNodeList = AmlCurrentNodeList->Parent;
} else {
//
// Only root has no parent
//
ASSERT (AmlCurrentNodeList == AmlRootNodeList);
}
Buffer += 1;
} while (*Buffer == AML_PARENT_PREFIX_CHAR);
} else {
AmlCurrentNodeList = AmlParentNodeList;
}
//
// Handle name segment
//
if (*Buffer == AML_DUAL_NAME_PREFIX) {
Buffer += 1;
SegCount = 2;
} else if (*Buffer == AML_MULTI_NAME_PREFIX) {
Buffer += 1;
SegCount = *Buffer;
Buffer += 1;
} else if (*Buffer == 0) {
//
// NULL name, only for Root
//
ASSERT (AmlCurrentNodeList == AmlRootNodeList);
return AmlCurrentNodeList;
} else {
SegCount = 1;
}
//
// Handle NamePath
//
Index = 0;
do {
AmlNodeList = AmlFindNodeInThis (Buffer, AmlCurrentNodeList, Create);
if (AmlNodeList == NULL) {
return NULL;
}
AmlCurrentNodeList = AmlNodeList;
Buffer += AML_NAME_SEG_SIZE;
Index ++;
} while (Index < SegCount);
return AmlNodeList;
}
/**
Insert the NameString to the AmlNodeList.
@param[in] NameString AML NameString.
@param[in] Buffer Buffer for the Node.
@param[in] Size Size for the Node.
@param[in] AmlRootNodeList AML root node list.
@param[in] AmlParentNodeList AML parent node list.
@return AmlChildNode whoes name is NameString.
**/
EFI_AML_NODE_LIST *
AmlInsertNodeToTree (
IN UINT8 *NameString,
IN VOID *Buffer,
IN UINTN Size,
IN EFI_AML_NODE_LIST *AmlRootNodeList,
IN EFI_AML_NODE_LIST *AmlParentNodeList
)
{
EFI_AML_NODE_LIST *AmlNodeList;
AmlNodeList = AmlFindNodeInTheTree (
NameString,
AmlRootNodeList,
AmlParentNodeList,
TRUE // Find and Create
);
ASSERT (AmlNodeList != NULL);
if (AmlNodeList == NULL) {
return NULL;
}
//
// Check buffer
//
if (AmlNodeList->Buffer == NULL) {
//
// NULL means new added one or SCOPE_OP
//
if (*(UINT8 *)Buffer != AML_SCOPE_OP) {
//
// We need check if new one is SCOPE_OP, because SCOPE_OP just means namespace, not a real device.
// We should not return SCOPE_OP.
//
AmlNodeList->Buffer = Buffer;
AmlNodeList->Size = Size;
AmlNodeList->AmlByteEncoding = AmlSearchByOpByte (Buffer);
}
return AmlNodeList;
}
//
// Already added
//
if (*(UINT8 *)Buffer == AML_SCOPE_OP) {
//
// The new one is SCOPE_OP, OK just return;
//
return AmlNodeList;
}
//
// Oops!!!, There must be something wrong.
//
DEBUG ((EFI_D_ERROR, "AML: Override Happen - %a!\n", NameString));
DEBUG ((EFI_D_ERROR, "AML: Existing Node - %x\n", AmlNodeList->Buffer));
DEBUG ((EFI_D_ERROR, "AML: New Buffer - %x\n", Buffer));
return NULL;
}
/**
Construct child node list according to the AML handle.
@param[in] AmlHandle AML handle.
@param[in] AmlRootNodeList AML root node list.
@param[in] AmlParentNodeList AML parent node list.
@retval EFI_SUCCESS Success.
@retval EFI_INVALID_PARAMETER AML handle does not refer to a valid ACPI object.
**/
EFI_STATUS
AmlConstructNodeListForChild (
IN EFI_AML_HANDLE *AmlHandle,
IN EFI_AML_NODE_LIST *AmlRootNodeList,
IN EFI_AML_NODE_LIST *AmlParentNodeList
)
{
AML_BYTE_ENCODING *AmlByteEncoding;
UINT8 *Buffer;
UINTN BufferSize;
UINT8 *CurrentBuffer;
EFI_AML_HANDLE *AmlChildHandle;
EFI_STATUS Status;
AmlByteEncoding = AmlHandle->AmlByteEncoding;
Buffer = AmlHandle->Buffer;
BufferSize = AmlHandle->Size;
//
// Check if we need recursively add node
//
if ((AmlByteEncoding->Attribute & AML_HAS_CHILD_OBJ) == 0) {
//
// No more node need to be added
//
return EFI_SUCCESS;
}
//
// Do we need add node within METHOD?
// Yes, just add Object is OK. But we need filter NameString for METHOD invoke.
//
//
// Now, we get the last node.
//
Status = AmlGetOffsetAfterLastOption (AmlHandle, &CurrentBuffer);
if (EFI_ERROR (Status)) {
return EFI_INVALID_PARAMETER;
}
//
// Go through all the reset buffer.
//
while ((UINTN)CurrentBuffer < (UINTN)Buffer + BufferSize) {
//
// Find the child node.
//
Status = SdtOpenEx (CurrentBuffer, (UINTN)Buffer + BufferSize - (UINTN)CurrentBuffer, (EFI_ACPI_HANDLE *)&AmlChildHandle);
if (EFI_ERROR (Status)) {
//
// No child found, break now.
//
break;
}
//
// Good, find the child. Construct node recursively
//
Status = AmlConstructNodeList (
AmlChildHandle,
AmlRootNodeList,
AmlParentNodeList
);
if (EFI_ERROR (Status)) {
break;
}
//
// Parse next one
//
CurrentBuffer += AmlChildHandle->Size;
Close ((EFI_ACPI_HANDLE)AmlChildHandle);
}
return EFI_SUCCESS;
}
/**
Construct node list according to the AML handle.
@param[in] AmlHandle AML handle.
@param[in] AmlRootNodeList AML root node list.
@param[in] AmlParentNodeList AML parent node list.
@retval EFI_SUCCESS Success.
@retval EFI_INVALID_PARAMETER AML handle does not refer to a valid ACPI object.
**/
EFI_STATUS
AmlConstructNodeList (
IN EFI_AML_HANDLE *AmlHandle,
IN EFI_AML_NODE_LIST *AmlRootNodeList,
IN EFI_AML_NODE_LIST *AmlParentNodeList
)
{
VOID *NameString;
EFI_AML_NODE_LIST *AmlNodeList;
//
// 1. Check if there is need to construct node for this OpCode.
//
if ((AmlHandle->AmlByteEncoding->Attribute & AML_IN_NAMESPACE) == 0) {
//
// No need to construct node, so we just skip this OpCode.
//
return EFI_SUCCESS;
}
//
// 2. Now, we need construct node for this OpCode.
//
NameString = AmlGetObjectName (AmlHandle);
if (NameString == NULL) {
return EFI_INVALID_PARAMETER;
}
//
// Now, we need to insert node to the node list.
// NOTE: The name here could be AML NameString. So the callee need parse it.
//
AmlNodeList = AmlInsertNodeToTree (NameString, AmlHandle->Buffer, AmlHandle->Size, AmlRootNodeList, AmlParentNodeList);
ASSERT (AmlNodeList != NULL);
//
// 3. Ok, we need to parse the object list to see if there are more node to be added.
//
return AmlConstructNodeListForChild (AmlHandle, AmlRootNodeList, AmlNodeList);
}
/**
Destruct node list
@param[in] AmlParentNodeList AML parent node list.
**/
VOID
AmlDestructNodeList (
IN EFI_AML_NODE_LIST *AmlParentNodeList
)
{
EFI_AML_NODE_LIST *CurrentAmlNodeList;
LIST_ENTRY *CurrentLink;
LIST_ENTRY *StartLink;
//
// Get the children link
//
StartLink = &AmlParentNodeList->Children;
CurrentLink = StartLink->ForwardLink;
//
// Go through all the children
//
while (CurrentLink != StartLink) {
//
// Destruct the child's list recursively
//
CurrentAmlNodeList = EFI_AML_NODE_LIST_FROM_LINK (CurrentLink);
CurrentLink = CurrentLink->ForwardLink;
//
// Remove this child from list and free the node
//
RemoveEntryList (&(CurrentAmlNodeList->Link));
AmlDestructNodeList (CurrentAmlNodeList);
}
//
// Done.
//
FreePool (AmlParentNodeList);
return ;
}
/**
Dump node list
@param[in] AmlParentNodeList AML parent node list.
@param[in] Level Output debug level.
**/
VOID
AmlDumpNodeInfo (
IN EFI_AML_NODE_LIST *AmlParentNodeList,
IN UINTN Level
)
{
EFI_AML_NODE_LIST *CurrentAmlNodeList;
volatile LIST_ENTRY *CurrentLink;
UINTN Index;
CurrentLink = AmlParentNodeList->Children.ForwardLink;
if (Level == 0) {
DEBUG ((EFI_D_ERROR, "\\"));
} else {
for (Index = 0; Index < Level; Index++) {
DEBUG ((EFI_D_ERROR, " "));
}
AmlPrintNameSeg (AmlParentNodeList->Name);
}
DEBUG ((EFI_D_ERROR, "\n"));
while (CurrentLink != &AmlParentNodeList->Children) {
CurrentAmlNodeList = EFI_AML_NODE_LIST_FROM_LINK (CurrentLink);
AmlDumpNodeInfo (CurrentAmlNodeList, Level + 1);
CurrentLink = CurrentLink->ForwardLink;
}
return ;
}
/**
Returns the handle of the ACPI object representing the specified ACPI AML path
@param[in] AmlHandle Points to the handle of the object representing the starting point for the path search.
@param[in] AmlPath Points to the ACPI AML path.
@param[out] Buffer On return, points to the ACPI object which represents AcpiPath, relative to
HandleIn.
@param[in] FromRoot TRUE means to find AML path from \ (Root) Node.
FALSE means to find AML path from this Node (The HandleIn).
@retval EFI_SUCCESS Success
@retval EFI_INVALID_PARAMETER HandleIn does not refer to a valid ACPI object.
**/
EFI_STATUS
AmlFindPath (
IN EFI_AML_HANDLE *AmlHandle,
IN UINT8 *AmlPath,
OUT VOID **Buffer,
IN BOOLEAN FromRoot
)
{
EFI_AML_NODE_LIST *AmlRootNodeList;
EFI_STATUS Status;
EFI_AML_NODE_LIST *AmlNodeList;
UINT8 RootNameSeg[AML_NAME_SEG_SIZE];
EFI_AML_NODE_LIST *CurrentAmlNodeList;
LIST_ENTRY *CurrentLink;
//
// 1. create tree
//
//
// Create root handle
//
RootNameSeg[0] = AML_ROOT_CHAR;
RootNameSeg[1] = 0;
AmlRootNodeList = AmlCreateNode (RootNameSeg, NULL, AmlHandle->AmlByteEncoding);
Status = AmlConstructNodeList (
AmlHandle,
AmlRootNodeList, // Root
AmlRootNodeList // Parent
);
if (EFI_ERROR (Status)) {
return EFI_INVALID_PARAMETER;
}
DEBUG_CODE_BEGIN ();
DEBUG ((EFI_D_ERROR, "AcpiSdt: NameSpace:\n"));
AmlDumpNodeInfo (AmlRootNodeList, 0);
DEBUG_CODE_END ();
//
// 2. Search the node in the tree
//
if (FromRoot) {
//
// Search from Root
//
CurrentAmlNodeList = AmlRootNodeList;
} else {
//
// Search from this node, NOT ROOT.
// Since we insert node to ROOT one by one, we just get the first node and search from it.
//
CurrentLink = AmlRootNodeList->Children.ForwardLink;
if (CurrentLink != &AmlRootNodeList->Children) {
//
// First node
//
CurrentAmlNodeList = EFI_AML_NODE_LIST_FROM_LINK (CurrentLink);
} else {
//
// No child
//
CurrentAmlNodeList = NULL;
}
}
//
// Search
//
if (CurrentAmlNodeList != NULL) {
DEBUG_CODE_BEGIN ();
DEBUG ((EFI_D_ERROR, "AcpiSdt: Search from: \\"));
AmlPrintNameSeg (CurrentAmlNodeList->Name);
DEBUG ((EFI_D_ERROR, "\n"));
DEBUG_CODE_END ();
AmlNodeList = AmlFindNodeInTheTree (
AmlPath,
AmlRootNodeList, // Root
CurrentAmlNodeList, // Parent
FALSE
);
} else {
AmlNodeList = NULL;
}
*Buffer = NULL;
Status = EFI_SUCCESS;
if (AmlNodeList != NULL && AmlNodeList->Buffer != NULL) {
*Buffer = AmlNodeList->Buffer;
}
//
// 3. free the tree
//
AmlDestructNodeList (AmlRootNodeList);
return Status;
}