/****************************************************************************** * * Module Name: dsmethod - Parser/Interpreter interface - control method parsing * *****************************************************************************/ /* * Copyright (C) 2000 - 2011, Intel Corp. * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions, and the following disclaimer, * without modification. * 2. Redistributions in binary form must reproduce at minimum a disclaimer * substantially similar to the "NO WARRANTY" disclaimer below * ("Disclaimer") and any redistribution must be conditioned upon * including a substantially similar Disclaimer requirement for further * binary redistribution. * 3. Neither the names of the above-listed copyright holders nor the names * of any contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * * Alternatively, this software may be distributed under the terms of the * GNU General Public License ("GPL") version 2 as published by the Free * Software Foundation. * * NO WARRANTY * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT * HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGES. */ #define __DSMETHOD_C__ #include "acpi.h" #include "accommon.h" #include "acdispat.h" #include "acinterp.h" #include "acnamesp.h" #include "acdisasm.h" #define _COMPONENT ACPI_DISPATCHER ACPI_MODULE_NAME ("dsmethod") /* Local prototypes */ static ACPI_STATUS AcpiDsCreateMethodMutex ( ACPI_OPERAND_OBJECT *MethodDesc); /******************************************************************************* * * FUNCTION: AcpiDsMethodError * * PARAMETERS: Status - Execution status * WalkState - Current state * * RETURN: Status * * DESCRIPTION: Called on method error. Invoke the global exception handler if * present, dump the method data if the disassembler is configured * * Note: Allows the exception handler to change the status code * ******************************************************************************/ ACPI_STATUS AcpiDsMethodError ( ACPI_STATUS Status, ACPI_WALK_STATE *WalkState) { ACPI_FUNCTION_ENTRY (); /* Ignore AE_OK and control exception codes */ if (ACPI_SUCCESS (Status) || (Status & AE_CODE_CONTROL)) { return (Status); } /* Invoke the global exception handler */ if (AcpiGbl_ExceptionHandler) { /* Exit the interpreter, allow handler to execute methods */ AcpiExExitInterpreter (); /* * Handler can map the exception code to anything it wants, including * AE_OK, in which case the executing method will not be aborted. */ Status = AcpiGbl_ExceptionHandler (Status, WalkState->MethodNode ? WalkState->MethodNode->Name.Integer : 0, WalkState->Opcode, WalkState->AmlOffset, NULL); AcpiExEnterInterpreter (); } AcpiDsClearImplicitReturn (WalkState); #ifdef ACPI_DISASSEMBLER if (ACPI_FAILURE (Status)) { /* Display method locals/args if disassembler is present */ AcpiDmDumpMethodInfo (Status, WalkState, WalkState->Op); } #endif return (Status); } /******************************************************************************* * * FUNCTION: AcpiDsCreateMethodMutex * * PARAMETERS: ObjDesc - The method object * * RETURN: Status * * DESCRIPTION: Create a mutex object for a serialized control method * ******************************************************************************/ static ACPI_STATUS AcpiDsCreateMethodMutex ( ACPI_OPERAND_OBJECT *MethodDesc) { ACPI_OPERAND_OBJECT *MutexDesc; ACPI_STATUS Status; ACPI_FUNCTION_TRACE (DsCreateMethodMutex); /* Create the new mutex object */ MutexDesc = AcpiUtCreateInternalObject (ACPI_TYPE_MUTEX); if (!MutexDesc) { return_ACPI_STATUS (AE_NO_MEMORY); } /* Create the actual OS Mutex */ Status = AcpiOsCreateMutex (&MutexDesc->Mutex.OsMutex); if (ACPI_FAILURE (Status)) { return_ACPI_STATUS (Status); } MutexDesc->Mutex.SyncLevel = MethodDesc->Method.SyncLevel; MethodDesc->Method.Mutex = MutexDesc; return_ACPI_STATUS (AE_OK); } /******************************************************************************* * * FUNCTION: AcpiDsBeginMethodExecution * * PARAMETERS: MethodNode - Node of the method * ObjDesc - The method object * WalkState - current state, NULL if not yet executing * a method. * * RETURN: Status * * DESCRIPTION: Prepare a method for execution. Parses the method if necessary, * increments the thread count, and waits at the method semaphore * for clearance to execute. * ******************************************************************************/ ACPI_STATUS AcpiDsBeginMethodExecution ( ACPI_NAMESPACE_NODE *MethodNode, ACPI_OPERAND_OBJECT *ObjDesc, ACPI_WALK_STATE *WalkState) { ACPI_STATUS Status = AE_OK; ACPI_FUNCTION_TRACE_PTR (DsBeginMethodExecution, MethodNode); if (!MethodNode) { return_ACPI_STATUS (AE_NULL_ENTRY); } /* Prevent wraparound of thread count */ if (ObjDesc->Method.ThreadCount == ACPI_UINT8_MAX) { ACPI_ERROR ((AE_INFO, "Method reached maximum reentrancy limit (255)")); return_ACPI_STATUS (AE_AML_METHOD_LIMIT); } /* * If this method is serialized, we need to acquire the method mutex. */ if (ObjDesc->Method.InfoFlags & ACPI_METHOD_SERIALIZED) { /* * Create a mutex for the method if it is defined to be Serialized * and a mutex has not already been created. We defer the mutex creation * until a method is actually executed, to minimize the object count */ if (!ObjDesc->Method.Mutex) { Status = AcpiDsCreateMethodMutex (ObjDesc); if (ACPI_FAILURE (Status)) { return_ACPI_STATUS (Status); } } /* * The CurrentSyncLevel (per-thread) must be less than or equal to * the sync level of the method. This mechanism provides some * deadlock prevention * * Top-level method invocation has no walk state at this point */ if (WalkState && (WalkState->Thread->CurrentSyncLevel > ObjDesc->Method.Mutex->Mutex.SyncLevel)) { ACPI_ERROR ((AE_INFO, "Cannot acquire Mutex for method [%4.4s], current SyncLevel is too large (%u)", AcpiUtGetNodeName (MethodNode), WalkState->Thread->CurrentSyncLevel)); return_ACPI_STATUS (AE_AML_MUTEX_ORDER); } /* * Obtain the method mutex if necessary. Do not acquire mutex for a * recursive call. */ if (!WalkState || !ObjDesc->Method.Mutex->Mutex.ThreadId || (WalkState->Thread->ThreadId != ObjDesc->Method.Mutex->Mutex.ThreadId)) { /* * Acquire the method mutex. This releases the interpreter if we * block (and reacquires it before it returns) */ Status = AcpiExSystemWaitMutex (ObjDesc->Method.Mutex->Mutex.OsMutex, ACPI_WAIT_FOREVER); if (ACPI_FAILURE (Status)) { return_ACPI_STATUS (Status); } /* Update the mutex and walk info and save the original SyncLevel */ if (WalkState) { ObjDesc->Method.Mutex->Mutex.OriginalSyncLevel = WalkState->Thread->CurrentSyncLevel; ObjDesc->Method.Mutex->Mutex.ThreadId = WalkState->Thread->ThreadId; WalkState->Thread->CurrentSyncLevel = ObjDesc->Method.SyncLevel; } else { ObjDesc->Method.Mutex->Mutex.OriginalSyncLevel = ObjDesc->Method.Mutex->Mutex.SyncLevel; } } /* Always increase acquisition depth */ ObjDesc->Method.Mutex->Mutex.AcquisitionDepth++; } /* * Allocate an Owner ID for this method, only if this is the first thread * to begin concurrent execution. We only need one OwnerId, even if the * method is invoked recursively. */ if (!ObjDesc->Method.OwnerId) { Status = AcpiUtAllocateOwnerId (&ObjDesc->Method.OwnerId); if (ACPI_FAILURE (Status)) { goto Cleanup; } } /* * Increment the method parse tree thread count since it has been * reentered one more time (even if it is the same thread) */ ObjDesc->Method.ThreadCount++; AcpiMethodCount++; return_ACPI_STATUS (Status); Cleanup: /* On error, must release the method mutex (if present) */ if (ObjDesc->Method.Mutex) { AcpiOsReleaseMutex (ObjDesc->Method.Mutex->Mutex.OsMutex); } return_ACPI_STATUS (Status); } /******************************************************************************* * * FUNCTION: AcpiDsCallControlMethod * * PARAMETERS: Thread - Info for this thread * ThisWalkState - Current walk state * Op - Current Op to be walked * * RETURN: Status * * DESCRIPTION: Transfer execution to a called control method * ******************************************************************************/ ACPI_STATUS AcpiDsCallControlMethod ( ACPI_THREAD_STATE *Thread, ACPI_WALK_STATE *ThisWalkState, ACPI_PARSE_OBJECT *Op) { ACPI_STATUS Status; ACPI_NAMESPACE_NODE *MethodNode; ACPI_WALK_STATE *NextWalkState = NULL; ACPI_OPERAND_OBJECT *ObjDesc; ACPI_EVALUATE_INFO *Info; UINT32 i; ACPI_FUNCTION_TRACE_PTR (DsCallControlMethod, ThisWalkState); ACPI_DEBUG_PRINT ((ACPI_DB_DISPATCH, "Calling method %p, currentstate=%p\n", ThisWalkState->PrevOp, ThisWalkState)); /* * Get the namespace entry for the control method we are about to call */ MethodNode = ThisWalkState->MethodCallNode; if (!MethodNode) { return_ACPI_STATUS (AE_NULL_ENTRY); } ObjDesc = AcpiNsGetAttachedObject (MethodNode); if (!ObjDesc) { return_ACPI_STATUS (AE_NULL_OBJECT); } /* Init for new method, possibly wait on method mutex */ Status = AcpiDsBeginMethodExecution (MethodNode, ObjDesc, ThisWalkState); if (ACPI_FAILURE (Status)) { return_ACPI_STATUS (Status); } /* Begin method parse/execution. Create a new walk state */ NextWalkState = AcpiDsCreateWalkState (ObjDesc->Method.OwnerId, NULL, ObjDesc, Thread); if (!NextWalkState) { Status = AE_NO_MEMORY; goto Cleanup; } /* * The resolved arguments were put on the previous walk state's operand * stack. Operands on the previous walk state stack always * start at index 0. Also, null terminate the list of arguments */ ThisWalkState->Operands [ThisWalkState->NumOperands] = NULL; /* * Allocate and initialize the evaluation information block * TBD: this is somewhat inefficient, should change interface to * DsInitAmlWalk. For now, keeps this struct off the CPU stack */ Info = ACPI_ALLOCATE_ZEROED (sizeof (ACPI_EVALUATE_INFO)); if (!Info) { return_ACPI_STATUS (AE_NO_MEMORY); } Info->Parameters = &ThisWalkState->Operands[0]; Status = AcpiDsInitAmlWalk (NextWalkState, NULL, MethodNode, ObjDesc->Method.AmlStart, ObjDesc->Method.AmlLength, Info, ACPI_IMODE_EXECUTE); ACPI_FREE (Info); if (ACPI_FAILURE (Status)) { goto Cleanup; } /* * Delete the operands on the previous walkstate operand stack * (they were copied to new objects) */ for (i = 0; i < ObjDesc->Method.ParamCount; i++) { AcpiUtRemoveReference (ThisWalkState->Operands [i]); ThisWalkState->Operands [i] = NULL; } /* Clear the operand stack */ ThisWalkState->NumOperands = 0; ACPI_DEBUG_PRINT ((ACPI_DB_DISPATCH, "**** Begin nested execution of [%4.4s] **** WalkState=%p\n", MethodNode->Name.Ascii, NextWalkState)); /* Invoke an internal method if necessary */ if (ObjDesc->Method.InfoFlags & ACPI_METHOD_INTERNAL_ONLY) { Status = ObjDesc->Method.Dispatch.Implementation (NextWalkState); if (Status == AE_OK) { Status = AE_CTRL_TERMINATE; } } return_ACPI_STATUS (Status); Cleanup: /* On error, we must terminate the method properly */ AcpiDsTerminateControlMethod (ObjDesc, NextWalkState); if (NextWalkState) { AcpiDsDeleteWalkState (NextWalkState); } return_ACPI_STATUS (Status); } /******************************************************************************* * * FUNCTION: AcpiDsRestartControlMethod * * PARAMETERS: WalkState - State for preempted method (caller) * ReturnDesc - Return value from the called method * * RETURN: Status * * DESCRIPTION: Restart a method that was preempted by another (nested) method * invocation. Handle the return value (if any) from the callee. * ******************************************************************************/ ACPI_STATUS AcpiDsRestartControlMethod ( ACPI_WALK_STATE *WalkState, ACPI_OPERAND_OBJECT *ReturnDesc) { ACPI_STATUS Status; int SameAsImplicitReturn; ACPI_FUNCTION_TRACE_PTR (DsRestartControlMethod, WalkState); ACPI_DEBUG_PRINT ((ACPI_DB_DISPATCH, "****Restart [%4.4s] Op %p ReturnValueFromCallee %p\n", AcpiUtGetNodeName (WalkState->MethodNode), WalkState->MethodCallOp, ReturnDesc)); ACPI_DEBUG_PRINT ((ACPI_DB_DISPATCH, " ReturnFromThisMethodUsed?=%X ResStack %p Walk %p\n", WalkState->ReturnUsed, WalkState->Results, WalkState)); /* Did the called method return a value? */ if (ReturnDesc) { /* Is the implicit return object the same as the return desc? */ SameAsImplicitReturn = (WalkState->ImplicitReturnObj == ReturnDesc); /* Are we actually going to use the return value? */ if (WalkState->ReturnUsed) { /* Save the return value from the previous method */ Status = AcpiDsResultPush (ReturnDesc, WalkState); if (ACPI_FAILURE (Status)) { AcpiUtRemoveReference (ReturnDesc); return_ACPI_STATUS (Status); } /* * Save as THIS method's return value in case it is returned * immediately to yet another method */ WalkState->ReturnDesc = ReturnDesc; } /* * The following code is the optional support for the so-called * "implicit return". Some AML code assumes that the last value of the * method is "implicitly" returned to the caller, in the absence of an * explicit return value. * * Just save the last result of the method as the return value. * * NOTE: this is optional because the ASL language does not actually * support this behavior. */ else if (!AcpiDsDoImplicitReturn (ReturnDesc, WalkState, FALSE) || SameAsImplicitReturn) { /* * Delete the return value if it will not be used by the * calling method or remove one reference if the explicit return * is the same as the implicit return value. */ AcpiUtRemoveReference (ReturnDesc); } } return_ACPI_STATUS (AE_OK); } /******************************************************************************* * * FUNCTION: AcpiDsTerminateControlMethod * * PARAMETERS: MethodDesc - Method object * WalkState - State associated with the method * * RETURN: None * * DESCRIPTION: Terminate a control method. Delete everything that the method * created, delete all locals and arguments, and delete the parse * tree if requested. * * MUTEX: Interpreter is locked * ******************************************************************************/ void AcpiDsTerminateControlMethod ( ACPI_OPERAND_OBJECT *MethodDesc, ACPI_WALK_STATE *WalkState) { ACPI_FUNCTION_TRACE_PTR (DsTerminateControlMethod, WalkState); /* MethodDesc is required, WalkState is optional */ if (!MethodDesc) { return_VOID; } if (WalkState) { /* Delete all arguments and locals */ AcpiDsMethodDataDeleteAll (WalkState); /* * If method is serialized, release the mutex and restore the * current sync level for this thread */ if (MethodDesc->Method.Mutex) { /* Acquisition Depth handles recursive calls */ MethodDesc->Method.Mutex->Mutex.AcquisitionDepth--; if (!MethodDesc->Method.Mutex->Mutex.AcquisitionDepth) { WalkState->Thread->CurrentSyncLevel = MethodDesc->Method.Mutex->Mutex.OriginalSyncLevel; AcpiOsReleaseMutex (MethodDesc->Method.Mutex->Mutex.OsMutex); MethodDesc->Method.Mutex->Mutex.ThreadId = 0; } } /* * Delete any namespace objects created anywhere within the * namespace by the execution of this method. Unless: * 1) This method is a module-level executable code method, in which * case we want make the objects permanent. * 2) There are other threads executing the method, in which case we * will wait until the last thread has completed. */ if (!(MethodDesc->Method.InfoFlags & ACPI_METHOD_MODULE_LEVEL) && (MethodDesc->Method.ThreadCount == 1)) { /* Delete any direct children of (created by) this method */ AcpiNsDeleteNamespaceSubtree (WalkState->MethodNode); /* * Delete any objects that were created by this method * elsewhere in the namespace (if any were created). * Use of the ACPI_METHOD_MODIFIED_NAMESPACE optimizes the * deletion such that we don't have to perform an entire * namespace walk for every control method execution. */ if (MethodDesc->Method.InfoFlags & ACPI_METHOD_MODIFIED_NAMESPACE) { AcpiNsDeleteNamespaceByOwner (MethodDesc->Method.OwnerId); MethodDesc->Method.InfoFlags &= ~ACPI_METHOD_MODIFIED_NAMESPACE; } } } /* Decrement the thread count on the method */ if (MethodDesc->Method.ThreadCount) { MethodDesc->Method.ThreadCount--; } else { ACPI_ERROR ((AE_INFO, "Invalid zero thread count in method")); } /* Are there any other threads currently executing this method? */ if (MethodDesc->Method.ThreadCount) { /* * Additional threads. Do not release the OwnerId in this case, * we immediately reuse it for the next thread executing this method */ ACPI_DEBUG_PRINT ((ACPI_DB_DISPATCH, "*** Completed execution of one thread, %u threads remaining\n", MethodDesc->Method.ThreadCount)); } else { /* This is the only executing thread for this method */ /* * Support to dynamically change a method from NotSerialized to * Serialized if it appears that the method is incorrectly written and * does not support multiple thread execution. The best example of this * is if such a method creates namespace objects and blocks. A second * thread will fail with an AE_ALREADY_EXISTS exception. * * This code is here because we must wait until the last thread exits * before marking the method as serialized. */ if (MethodDesc->Method.InfoFlags & ACPI_METHOD_SERIALIZED_PENDING) { if (WalkState) { ACPI_INFO ((AE_INFO, "Marking method %4.4s as Serialized because of AE_ALREADY_EXISTS error", WalkState->MethodNode->Name.Ascii)); } /* * Method tried to create an object twice and was marked as * "pending serialized". The probable cause is that the method * cannot handle reentrancy. * * The method was created as NotSerialized, but it tried to create * a named object and then blocked, causing the second thread * entrance to begin and then fail. Workaround this problem by * marking the method permanently as Serialized when the last * thread exits here. */ MethodDesc->Method.InfoFlags &= ~ACPI_METHOD_SERIALIZED_PENDING; MethodDesc->Method.InfoFlags |= ACPI_METHOD_SERIALIZED; MethodDesc->Method.SyncLevel = 0; } /* No more threads, we can free the OwnerId */ if (!(MethodDesc->Method.InfoFlags & ACPI_METHOD_MODULE_LEVEL)) { AcpiUtReleaseOwnerId (&MethodDesc->Method.OwnerId); } } return_VOID; }