Add per-table execution of module-level code, early region handlers

1) Execute any module-level code after each ACPI table (DSDT or SSDT)
is loaded into the namespace (rather than after all AML tables have
been loaded. This matches the behavior of other ACPI
implementations and is required to support BIOS code that
depends on this behavior.

2) Install default operation region handlers for the "basic"
address spaces - Memory, I/O, and PCI_Config. Not only are these
spaces required to be "always available" by the ACPI specification,
these handlers are required in order to execute the module-level
code described in (1). This will support BIOS code that depends
on these operation regions.
This commit is contained in:
Robert Moore 2015-12-04 09:12:38 -08:00
parent b01414f5a8
commit 071eff738c
9 changed files with 198 additions and 117 deletions

View File

@ -132,6 +132,12 @@ AcpiEvInstallHandler (
void *Context,
void **ReturnValue);
static ACPI_OPERAND_OBJECT *
AcpiEvFindRegionHandler (
ACPI_ADR_SPACE_TYPE SpaceId,
ACPI_OPERAND_OBJECT *HandlerObj);
/* These are the address spaces that will get default handlers */
UINT8 AcpiGbl_DefaultAddressSpaces[ACPI_NUM_DEFAULT_SPACES] =
@ -407,6 +413,46 @@ AcpiEvInstallHandler (
}
/*******************************************************************************
*
* FUNCTION: AcpiEvFindRegionHandler
*
* PARAMETERS: SpaceId - The address space ID
* HandlerObj - Head of the handler object list
*
* RETURN: Matching handler object. NULL if space ID not matched
*
* DESCRIPTION: Search a handler object list for a match on the address
* space ID.
*
******************************************************************************/
static ACPI_OPERAND_OBJECT *
AcpiEvFindRegionHandler (
ACPI_ADR_SPACE_TYPE SpaceId,
ACPI_OPERAND_OBJECT *HandlerObj)
{
/* Walk the handler list for this device */
while (HandlerObj)
{
/* Same SpaceId indicates a handler is installed */
if (HandlerObj->AddressSpace.SpaceId == SpaceId)
{
return (HandlerObj);
}
/* Next handler object */
HandlerObj = HandlerObj->AddressSpace.Next;
}
return (NULL);
}
/*******************************************************************************
*
* FUNCTION: AcpiEvInstallSpaceHandler
@ -434,7 +480,7 @@ AcpiEvInstallSpaceHandler (
{
ACPI_OPERAND_OBJECT *ObjDesc;
ACPI_OPERAND_OBJECT *HandlerObj;
ACPI_STATUS Status;
ACPI_STATUS Status = AE_OK;
ACPI_OBJECT_TYPE Type;
UINT8 Flags = 0;
@ -443,8 +489,8 @@ AcpiEvInstallSpaceHandler (
/*
* This registration is valid for only the types below and the root. This
* is where the default handlers get placed.
* This registration is valid for only the types below and the root.
* The root node is where the default handlers get installed.
*/
if ((Node->Type != ACPI_TYPE_DEVICE) &&
(Node->Type != ACPI_TYPE_PROCESSOR) &&
@ -517,47 +563,53 @@ AcpiEvInstallSpaceHandler (
if (ObjDesc)
{
/*
* The attached device object already exists. Make sure the handler
* is not already installed.
* The attached device object already exists. Now make sure
* the handler is not already installed.
*/
HandlerObj = ObjDesc->Device.Handler;
HandlerObj = AcpiEvFindRegionHandler (SpaceId,
ObjDesc->Device.Handler);
/* Walk the handler list for this device */
while (HandlerObj)
if (HandlerObj)
{
/* Same SpaceId indicates a handler already installed */
if (HandlerObj->AddressSpace.SpaceId == SpaceId)
if (HandlerObj->AddressSpace.Handler == Handler)
{
if (HandlerObj->AddressSpace.Handler == Handler)
/*
* It is (relatively) OK to attempt to install the SAME
* handler twice. This can easily happen with the
* PCI_Config space.
*/
Status = AE_SAME_HANDLER;
goto UnlockAndExit;
}
else
{
/*
* A handler is already installed but does not match the
* incoming handler. However, we must allow overwrite
* of the default handlers (11/2015).
*/
if (HandlerObj->AddressSpace.HandlerFlags &
ACPI_ADDR_HANDLER_DEFAULT_INSTALLED)
{
/*
* It is (relatively) OK to attempt to install the SAME
* handler twice. This can easily happen with the
* PCI_Config space.
*/
Status = AE_SAME_HANDLER;
goto UnlockAndExit;
HandlerObj->AddressSpace.HandlerFlags = 0;
HandlerObj->AddressSpace.Handler = Handler;
HandlerObj->AddressSpace.Context = Context;
HandlerObj->AddressSpace.Setup = Setup;
}
else
{
/* A handler is already installed */
Status = AE_ALREADY_EXISTS;
}
goto UnlockAndExit;
}
/* Walk the linked list of handlers */
HandlerObj = HandlerObj->AddressSpace.Next;
goto UnlockAndExit;
}
}
else
{
ACPI_DEBUG_PRINT ((ACPI_DB_OPREGION,
"Creating object on Device %p while installing handler\n", Node));
"Creating object on Device %p while installing handler\n",
Node));
/* ObjDesc does not exist, create one */
@ -596,7 +648,8 @@ AcpiEvInstallSpaceHandler (
}
ACPI_DEBUG_PRINT ((ACPI_DB_OPREGION,
"Installing address handler for region %s(%X) on Device %4.4s %p(%p)\n",
"Installing address handler for region %s(%X) "
"on Device %4.4s %p(%p)\n",
AcpiUtGetRegionName (SpaceId), SpaceId,
AcpiUtGetNodeName (Node), Node, ObjDesc));
@ -621,7 +674,7 @@ AcpiEvInstallSpaceHandler (
HandlerObj->AddressSpace.Node = Node;
HandlerObj->AddressSpace.Handler = Handler;
HandlerObj->AddressSpace.Context = Context;
HandlerObj->AddressSpace.Setup = Setup;
HandlerObj->AddressSpace.Setup = Setup;
/* Install at head of Device.AddressSpace list */
@ -634,20 +687,30 @@ AcpiEvInstallSpaceHandler (
ObjDesc->Device.Handler = HandlerObj;
/*
* Walk the namespace finding all of the regions this
* handler will manage.
*
* Start at the device and search the branch toward
* the leaf nodes until either the leaf is encountered or
* a device is detected that has an address handler of the
* same type.
*
* In either case, back up and search down the remainder
* of the branch
* Walk namespace for regions only if basic initialization is
* complete and the entire namespace is loaded. Only then is
* there any point in doing this. This is basically an optimization
* for the early installation of region handlers -- before there
* is a namespace.
*/
Status = AcpiNsWalkNamespace (ACPI_TYPE_ANY, Node, ACPI_UINT32_MAX,
ACPI_NS_WALK_UNLOCK, AcpiEvInstallHandler, NULL,
HandlerObj, NULL);
if (!AcpiGbl_EarlyInitialization)
{
/*
* Walk the namespace finding all of the regions this
* handler will manage.
*
* Start at the device and search the branch toward
* the leaf nodes until either the leaf is encountered or
* a device is detected that has an address handler of the
* same type.
*
* In either case, back up and search down the remainder
* of the branch
*/
Status = AcpiNsWalkNamespace (ACPI_TYPE_ANY, Node,
ACPI_UINT32_MAX, ACPI_NS_WALK_UNLOCK,
AcpiEvInstallHandler, NULL, HandlerObj, NULL);
}
UnlockAndExit:
return_ACPI_STATUS (Status);

View File

@ -210,6 +210,12 @@ AcpiEvInitializeOpRegions (
* DESCRIPTION: Dispatch an address space or operation region access to
* a previously installed handler.
*
* NOTE: During early initialization, we always install the default region
* handlers for Memory, I/O and PCI_Config. This ensures that these operation
* region address spaces are always available as per the ACPI specification.
* This is especially needed in order to support the execution of
* module-level AML code during loading of the ACPI tables.
*
******************************************************************************/
ACPI_STATUS

View File

@ -689,7 +689,7 @@ AcpiEvInitializeRegion (
/*
* The following loop depends upon the root Node having no parent
* ie: AcpiGbl_RootNode->ParentEntry being set to NULL
* ie: AcpiGbl_RootNode->Parent being set to NULL
*/
while (Node)
{
@ -754,6 +754,16 @@ AcpiEvInitializeRegion (
Status = AcpiEvAttachRegion (HandlerObj, RegionObj,
AcpiNsLocked);
/*
* During early initialization, we will not be running the
* the _REG methods for Memory, I/O and PCI_config because
* these regions are defined to be always available.
*/
if (AcpiGbl_EarlyInitialization)
{
return_ACPI_STATUS (AE_OK);
}
/*
* Tell all users that this region is usable by
* running the _REG method
@ -767,7 +777,8 @@ AcpiEvInitializeRegion (
}
}
Status = AcpiEvExecuteRegMethod (RegionObj, ACPI_REG_CONNECT);
Status = AcpiEvExecuteRegMethod (
RegionObj, ACPI_REG_CONNECT);
if (AcpiNsLocked)
{

View File

@ -594,7 +594,8 @@ AcpiExDumpObject (
if (Next)
{
AcpiOsPrintf ("(%s %2.2X)",
AcpiUtGetObjectTypeName (Next), Next->Common.Type);
AcpiUtGetObjectTypeName (Next),
Next->AddressSpace.SpaceId);
while (Next->AddressSpace.Next)
{
@ -606,7 +607,8 @@ AcpiExDumpObject (
Next = Next->AddressSpace.Next;
AcpiOsPrintf ("->%p(%s %2.2X)", Next,
AcpiUtGetObjectTypeName (Next), Next->Common.Type);
AcpiUtGetObjectTypeName (Next),
Next->AddressSpace.SpaceId);
if ((Next == Start) || (Next == Data))
{

View File

@ -491,7 +491,7 @@ AcpiNsExecModuleCodeList (
*
* DESCRIPTION: Execute a control method containing a block of module-level
* executable AML code. The control method is temporarily
* installed to the root node, then evaluated.
* installed to a local copy of the root node, then evaluated.
*
******************************************************************************/
@ -500,10 +500,9 @@ AcpiNsExecModuleCode (
ACPI_OPERAND_OBJECT *MethodObj,
ACPI_EVALUATE_INFO *Info)
{
ACPI_OPERAND_OBJECT *ParentObj;
ACPI_NAMESPACE_NODE *ParentNode;
ACPI_OBJECT_TYPE Type;
ACPI_STATUS Status;
ACPI_NAMESPACE_NODE *TempNode;
ACPI_NAMESPACE_NODE *ParentNode;
ACPI_FUNCTION_TRACE (NsExecModuleCode);
@ -515,21 +514,18 @@ AcpiNsExecModuleCode (
*/
ParentNode = ACPI_CAST_PTR (ACPI_NAMESPACE_NODE,
MethodObj->Method.NextObject);
Type = AcpiNsGetType (ParentNode);
/*
* Get the region handler and save it in the method object. We may need
* this if an operation region declaration causes a _REG method to be run.
*
* We can't do this in AcpiPsLinkModuleCode because
* AcpiGbl_RootNode->Object is NULL at PASS1.
*/
if ((Type == ACPI_TYPE_DEVICE) && ParentNode->Object)
/* Take a copy of the parent node to act as parent of this method */
TempNode = ACPI_ALLOCATE (sizeof (ACPI_NAMESPACE_NODE));
if (!TempNode)
{
MethodObj->Method.Dispatch.Handler =
ParentNode->Object->Device.Handler;
return_VOID;
}
memcpy (TempNode, ParentNode, sizeof (ACPI_NAMESPACE_NODE));
TempNode->Object = NULL; /* Clear the subobject */
/* Must clear NextObject (AcpiNsAttachObject needs the field) */
MethodObj->Method.NextObject = NULL;
@ -537,26 +533,14 @@ AcpiNsExecModuleCode (
/* Initialize the evaluation information block */
memset (Info, 0, sizeof (ACPI_EVALUATE_INFO));
Info->PrefixNode = ParentNode;
/*
* Get the currently attached parent object. Add a reference, because the
* ref count will be decreased when the method object is installed to
* the parent node.
*/
ParentObj = AcpiNsGetAttachedObject (ParentNode);
if (ParentObj)
{
AcpiUtAddReference (ParentObj);
}
Info->PrefixNode = TempNode;
/* Install the method (module-level code) in the parent node */
Status = AcpiNsAttachObject (ParentNode, MethodObj,
ACPI_TYPE_METHOD);
Status = AcpiNsAttachObject (TempNode, MethodObj, ACPI_TYPE_METHOD);
if (ACPI_FAILURE (Status))
{
goto Exit;
goto Cleanup;
}
/* Execute the parent node as a control method */
@ -574,25 +558,7 @@ AcpiNsExecModuleCode (
AcpiUtRemoveReference (Info->ReturnObject);
}
/* Detach the temporary method object */
AcpiNsDetachObject (ParentNode);
/* Restore the original parent object */
if (ParentObj)
{
Status = AcpiNsAttachObject (ParentNode, ParentObj, Type);
}
else
{
ParentNode->Type = (UINT8) Type;
}
Exit:
if (ParentObj)
{
AcpiUtRemoveReference (ParentObj);
}
Cleanup:
ACPI_FREE (TempNode);
return_VOID;
}

View File

@ -239,6 +239,24 @@ Unlock:
ACPI_DEBUG_PRINT ((ACPI_DB_INFO,
"**** Completed Table Object Initialization\n"));
/*
* Execute any module-level code that was detected during the table load
* phase. Although illegal since ACPI 2.0, there are many machines that
* contain this type of code. Each block of detected executable AML code
* outside of any control method is wrapped with a temporary control
* method object and placed on a global list. The methods on this list
* are executed below.
*
* This case executes the module-level code for each table immediately
* after the table has been loaded. This provides compatibility with
* other ACPI implementations. Optionally, the execution can be deferred
* until later, see AcpiInitializeObjects.
*/
if (!AcpiGbl_GroupModuleLevelCode)
{
AcpiNsExecModuleCodeList ();
}
return_ACPI_STATUS (Status);
}

View File

@ -291,6 +291,7 @@ AcpiUtInitGlobals (
AcpiGbl_DSDT = NULL;
AcpiGbl_CmSingleStep = FALSE;
AcpiGbl_Shutdown = FALSE;
AcpiGbl_EarlyInitialization = TRUE;
AcpiGbl_NsLookupCount = 0;
AcpiGbl_PsFindCount = 0;
AcpiGbl_AcpiHardwarePresent = TRUE;

View File

@ -204,6 +204,22 @@ AcpiInitializeSubsystem (
return_ACPI_STATUS (Status);
}
/*
* Install the default operation region handlers. These are the
* handlers that are defined by the ACPI specification to be
* "always accessible" -- namely, SystemMemory, SystemIO, and
* PCI_Config. This also means that no _REG methods need to be
* run for these address spaces. We need to have these handlers
* installed before any AML code can be executed, especially any
* module-level code (11/2015).
*/
Status = AcpiEvInstallRegionHandlers ();
if (ACPI_FAILURE (Status))
{
ACPI_EXCEPTION ((AE_INFO, Status, "During Region initialization"));
return_ACPI_STATUS (Status);
}
return_ACPI_STATUS (AE_OK);
}
@ -233,6 +249,14 @@ AcpiEnableSubsystem (
ACPI_FUNCTION_TRACE (AcpiEnableSubsystem);
/*
* The early initialization phase is complete. The namespace is loaded,
* and we can now support address spaces other than Memory, I/O, and
* PCI_Config.
*/
AcpiGbl_EarlyInitialization = FALSE;
#if (!ACPI_REDUCED_HARDWARE)
/* Enable ACPI mode */
@ -265,26 +289,6 @@ AcpiEnableSubsystem (
}
}
#endif /* !ACPI_REDUCED_HARDWARE */
/*
* Install the default OpRegion handlers. These are installed unless
* other handlers have already been installed via the
* InstallAddressSpaceHandler interface.
*/
if (!(Flags & ACPI_NO_ADDRESS_SPACE_INIT))
{
ACPI_DEBUG_PRINT ((ACPI_DB_EXEC,
"[Init] Installing default address space handlers\n"));
Status = AcpiEvInstallRegionHandlers ();
if (ACPI_FAILURE (Status))
{
return_ACPI_STATUS (Status);
}
}
#if (!ACPI_REDUCED_HARDWARE)
/*
* Initialize ACPI Event handling (Fixed and General Purpose)
*
@ -392,8 +396,15 @@ AcpiInitializeObjects (
* outside of any control method is wrapped with a temporary control
* method object and placed on a global list. The methods on this list
* are executed below.
*
* This case executes the module-level code for all tables only after
* all of the tables have been loaded. It is a legacy option and is
* not compatible with other ACPI implementations. See AcpiNsLoadTable.
*/
AcpiNsExecModuleCodeList ();
if (AcpiGbl_GroupModuleLevelCode)
{
AcpiNsExecModuleCodeList ();
}
/*
* Initialize the objects that remain uninitialized. This runs the

View File

@ -158,6 +158,8 @@ ACPI_GLOBAL (UINT8, AcpiGbl_IntegerBitWidth);
ACPI_GLOBAL (UINT8, AcpiGbl_IntegerByteWidth);
ACPI_GLOBAL (UINT8, AcpiGbl_IntegerNybbleWidth);
ACPI_INIT_GLOBAL (UINT8, AcpiGbl_GroupModuleLevelCode, FALSE);
/*****************************************************************************
*
@ -220,6 +222,7 @@ ACPI_GLOBAL (ACPI_CACHE_T *, AcpiGbl_OperandCache);
ACPI_INIT_GLOBAL (UINT32, AcpiGbl_StartupFlags, 0);
ACPI_INIT_GLOBAL (BOOLEAN, AcpiGbl_Shutdown, TRUE);
ACPI_INIT_GLOBAL (BOOLEAN, AcpiGbl_EarlyInitialization, TRUE);
/* Global handlers */