723 lines
19 KiB
C
723 lines
19 KiB
C
|
/*** ctxt.c - Context Block handling functions
|
||
|
*
|
||
|
* Copyright (c) 1996,1997 Microsoft Corporation
|
||
|
* Author: Michael Tsang (MikeTs)
|
||
|
* Created 06/13/97
|
||
|
*
|
||
|
* MODIFICATION HISTORY
|
||
|
*/
|
||
|
|
||
|
#include "pch.h"
|
||
|
|
||
|
#ifdef LOCKABLE_PRAGMA
|
||
|
#pragma ACPI_LOCKABLE_DATA
|
||
|
#pragma ACPI_LOCKABLE_CODE
|
||
|
#endif
|
||
|
|
||
|
NTSTATUS
|
||
|
LOCAL
|
||
|
NewContext(
|
||
|
PPCTXT ppctxt
|
||
|
)
|
||
|
/*++
|
||
|
|
||
|
Routine Description:
|
||
|
|
||
|
Allocate a new context structure from tne NonPaged Lookaside
|
||
|
List. Also keep track of the high water marks so that the OS
|
||
|
can intelligently decide what the "appropriate" number of
|
||
|
contexts that it should allocate.
|
||
|
|
||
|
Note that this code raises the possibility that if we detect
|
||
|
an out-of-memory error, we might be able to save a registry
|
||
|
key that includes a new larger number (to prevent this problem
|
||
|
in the first place). Adaptive recovery?
|
||
|
|
||
|
Arguments:
|
||
|
|
||
|
ppctxt - address of to hold the newly created context
|
||
|
|
||
|
Return Value:
|
||
|
|
||
|
NTSTATUS
|
||
|
|
||
|
--*/
|
||
|
{
|
||
|
TRACENAME("NEWCONTEXT")
|
||
|
KIRQL oldIrql;
|
||
|
NTSTATUS rc = STATUS_SUCCESS;
|
||
|
|
||
|
ENTER(2, ("NewContext(ppctxt=%x)\n", ppctxt));
|
||
|
|
||
|
*ppctxt = ExAllocateFromNPagedLookasideList(
|
||
|
&AMLIContextLookAsideList
|
||
|
);
|
||
|
if (*ppctxt == NULL) {
|
||
|
|
||
|
AMLI_WARN(("NewContext: Could not Allocate New Context"));
|
||
|
rc = AMLIERR_OUT_OF_MEM;
|
||
|
|
||
|
} else {
|
||
|
|
||
|
//
|
||
|
// Bookkeeping for memory resources to determine the high
|
||
|
// water mark
|
||
|
//
|
||
|
KeAcquireSpinLock(&gdwGContextSpinLock, &oldIrql );
|
||
|
gdwcCTObjs++;
|
||
|
if (gdwcCTObjs > 0 &&
|
||
|
(ULONG) gdwcCTObjs > gdwcCTObjsMax) {
|
||
|
|
||
|
gdwcCTObjsMax = gdwcCTObjs;
|
||
|
|
||
|
}
|
||
|
KeReleaseSpinLock(&gdwGContextSpinLock, oldIrql );
|
||
|
|
||
|
//
|
||
|
// Context Initialization
|
||
|
//
|
||
|
InitContext(*ppctxt, gdwCtxtBlkSize);
|
||
|
AcquireMutex(&gmutCtxtList);
|
||
|
ListInsertTail(&(*ppctxt)->listCtxt, &gplistCtxtHead);
|
||
|
ReleaseMutex(&gmutCtxtList);
|
||
|
|
||
|
}
|
||
|
|
||
|
EXIT(2, ("NewContext=%x (pctxt=%x)\n", rc, *ppctxt));
|
||
|
return rc;
|
||
|
} //NewContext
|
||
|
|
||
|
VOID
|
||
|
LOCAL
|
||
|
FreeContext(
|
||
|
PCTXT pctxt
|
||
|
)
|
||
|
/*++
|
||
|
|
||
|
Routine Description:
|
||
|
|
||
|
This is routine is called when a context is no longer required
|
||
|
and should be returned to the system's LookAside list
|
||
|
|
||
|
Arguments:
|
||
|
|
||
|
pctxt - Address of the context to be freed
|
||
|
|
||
|
Return Value:
|
||
|
|
||
|
None
|
||
|
|
||
|
--*/
|
||
|
{
|
||
|
TRACENAME("FREECONTEXT")
|
||
|
KIRQL oldIrql;
|
||
|
|
||
|
ENTER(2, ("FreeContext(pctxt=%x)\n", pctxt));
|
||
|
ASSERT(pctxt->powner == NULL);
|
||
|
|
||
|
//
|
||
|
// Need to hold the proper mutex to touch the global ctxt list
|
||
|
//
|
||
|
AcquireMutex(&gmutCtxtList);
|
||
|
ListRemoveEntry(&pctxt->listCtxt, &gplistCtxtHead);
|
||
|
|
||
|
if (pctxt->pplistCtxtQueue != NULL) {
|
||
|
|
||
|
ListRemoveEntry(&pctxt->listQueue, pctxt->pplistCtxtQueue);
|
||
|
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// Done with the global mutex
|
||
|
//
|
||
|
ReleaseMutex(&gmutCtxtList);
|
||
|
|
||
|
//
|
||
|
// Release any allocated storage that might not have been cleaned up
|
||
|
//
|
||
|
FreeDataBuffs(&pctxt->Result, 1);
|
||
|
|
||
|
//
|
||
|
// Bookkeeping for memory resources to determine the high
|
||
|
// water mark
|
||
|
//
|
||
|
KeAcquireSpinLock(&gdwGContextSpinLock, &oldIrql );
|
||
|
gdwcCTObjs--;
|
||
|
ASSERT(gdwcCTObjs >= 0);
|
||
|
KeReleaseSpinLock(&gdwGContextSpinLock, oldIrql );
|
||
|
|
||
|
//
|
||
|
// Log the end of a method
|
||
|
//
|
||
|
ACPIWMILOGEVENT((1,
|
||
|
EVENT_TRACE_TYPE_END,
|
||
|
GUID_List[AMLI_LOG_GUID],
|
||
|
"Object = %s",
|
||
|
GetObjectPath(pctxt->pnsObj)
|
||
|
));
|
||
|
|
||
|
|
||
|
//
|
||
|
// Return the context to the nonpaged lookaside list
|
||
|
//
|
||
|
ExFreeToNPagedLookasideList(&AMLIContextLookAsideList, pctxt);
|
||
|
EXIT(2, ("FreeContext!\n"));
|
||
|
} //FreeContext
|
||
|
|
||
|
/***LP InitContext - initialize a given context block
|
||
|
*
|
||
|
* ENTRY
|
||
|
* pctxt -> CTXT
|
||
|
* dwLen - length of context block
|
||
|
*
|
||
|
* EXIT
|
||
|
* None
|
||
|
*/
|
||
|
|
||
|
VOID LOCAL InitContext(PCTXT pctxt, ULONG dwLen)
|
||
|
{
|
||
|
TRACENAME("INITCONTEXT")
|
||
|
|
||
|
ENTER(2, ("InitContext(pctxt=%x,Len=%d)\n", pctxt, dwLen));
|
||
|
|
||
|
MEMZERO(pctxt, sizeof(CTXT) - sizeof(HEAP));
|
||
|
pctxt->dwSig = SIG_CTXT;
|
||
|
pctxt->pbCtxtEnd = (PUCHAR)pctxt + dwLen;
|
||
|
pctxt->pheapCurrent = &pctxt->LocalHeap;
|
||
|
// #ifdef DEBUGGER
|
||
|
// KeQuerySystemTime(&pctxt->Timestamp);
|
||
|
// #endif
|
||
|
KeInitializeDpc(&pctxt->Dpc, TimeoutCallback, pctxt);
|
||
|
KeInitializeTimer(&pctxt->Timer);
|
||
|
InitHeap(&pctxt->LocalHeap,
|
||
|
(ULONG)(pctxt->pbCtxtEnd - (PUCHAR)&pctxt->LocalHeap));
|
||
|
pctxt->LocalHeap.pheapHead = &pctxt->LocalHeap;
|
||
|
|
||
|
EXIT(2, ("InitContext!\n"));
|
||
|
} //InitContext
|
||
|
|
||
|
/***LP IsStackEmpty - determine if the stack is empty
|
||
|
*
|
||
|
* ENTRY
|
||
|
* pctxt -> CTXT
|
||
|
*
|
||
|
* EXIT-SUCCESS
|
||
|
* returns TRUE - stack is empty
|
||
|
* EXIT-FAILURE
|
||
|
* returns FALSE
|
||
|
*/
|
||
|
|
||
|
BOOLEAN LOCAL IsStackEmpty(PCTXT pctxt)
|
||
|
{
|
||
|
TRACENAME("ISSTACKEMPTY")
|
||
|
BOOLEAN rc;
|
||
|
|
||
|
ENTER(2, ("IsStackEmpty(pctxt=%p)\n", pctxt));
|
||
|
|
||
|
rc = (BOOLEAN)(pctxt->LocalHeap.pbHeapEnd == pctxt->pbCtxtEnd);
|
||
|
|
||
|
EXIT(2, ("IsStackEmpty=%x\n", rc));
|
||
|
return rc;
|
||
|
} //IsStackEmpty
|
||
|
|
||
|
/***LP PushFrame - Push a new frame on the stack
|
||
|
*
|
||
|
* ENTRY
|
||
|
* pctxt -> CTXT
|
||
|
* dwSig - frame object signature
|
||
|
* dwLen - size of the frame object
|
||
|
* pfnParse -> frame object parse function
|
||
|
* ppvFrame -> to hold pointer to the newly pushed frame (can be NULL)
|
||
|
*
|
||
|
* EXIT-SUCCESS
|
||
|
* returns STATUS_SUCCESS
|
||
|
* EXIT-FAILURE
|
||
|
* returns AMLIERR_ code
|
||
|
*/
|
||
|
|
||
|
NTSTATUS LOCAL PushFrame(PCTXT pctxt, ULONG dwSig, ULONG dwLen,
|
||
|
PFNPARSE pfnParse, PVOID *ppvFrame)
|
||
|
{
|
||
|
TRACENAME("PUSHFRAME")
|
||
|
NTSTATUS rc = STATUS_SUCCESS;
|
||
|
|
||
|
ENTER(2, ("PushFrame(pctxt=%p,Sig=%s,Len=%d,pfnParse=%p,ppvFrame=%p)\n",
|
||
|
pctxt, NameSegString(dwSig), dwLen, pfnParse, ppvFrame));
|
||
|
//
|
||
|
// Check to see if we have enough space, make sure it doesn't run into the
|
||
|
// heap.
|
||
|
//
|
||
|
if (pctxt->LocalHeap.pbHeapEnd - dwLen >= pctxt->LocalHeap.pbHeapTop)
|
||
|
{
|
||
|
PFRAMEHDR pfh;
|
||
|
|
||
|
pctxt->LocalHeap.pbHeapEnd -= dwLen;
|
||
|
pfh = (PFRAMEHDR)pctxt->LocalHeap.pbHeapEnd;
|
||
|
MEMZERO(pfh, dwLen);
|
||
|
pfh->dwSig = dwSig;
|
||
|
pfh->dwLen = dwLen;
|
||
|
pfh->pfnParse = pfnParse;
|
||
|
|
||
|
if (ppvFrame != NULL)
|
||
|
{
|
||
|
*ppvFrame = pfh;
|
||
|
}
|
||
|
|
||
|
#ifdef DEBUG
|
||
|
if ((ULONG)(pctxt->pbCtxtEnd - pctxt->LocalHeap.pbHeapEnd) >
|
||
|
gdwLocalStackMax)
|
||
|
{
|
||
|
gdwLocalStackMax = (ULONG)(pctxt->pbCtxtEnd -
|
||
|
pctxt->LocalHeap.pbHeapEnd);
|
||
|
}
|
||
|
#endif
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
rc = AMLI_LOGERR(AMLIERR_STACK_OVERFLOW,
|
||
|
("PushFrame: stack ran out of space"));
|
||
|
}
|
||
|
|
||
|
EXIT(2, ("PushFrame=%x (StackTop=%x)\n", rc, pctxt->LocalHeap.pbHeapEnd));
|
||
|
return rc;
|
||
|
} //PushFrame
|
||
|
|
||
|
/***LP PopFrame - Pop a frame off the stack
|
||
|
*
|
||
|
* ENTRY
|
||
|
* pctxt -> CTXT
|
||
|
*
|
||
|
* EXIT
|
||
|
* None
|
||
|
*/
|
||
|
|
||
|
VOID LOCAL PopFrame(PCTXT pctxt)
|
||
|
{
|
||
|
TRACENAME("POPFRAME")
|
||
|
|
||
|
ENTER(2, ("PopFrame(pctxt=%p)\n", pctxt));
|
||
|
|
||
|
ASSERT(!IsStackEmpty(pctxt));
|
||
|
ASSERT(((PFRAMEHDR)pctxt->LocalHeap.pbHeapEnd)->dwSig != 0);
|
||
|
pctxt->LocalHeap.pbHeapEnd +=
|
||
|
((PFRAMEHDR)pctxt->LocalHeap.pbHeapEnd)->dwLen;
|
||
|
|
||
|
EXIT(2, ("PopFrame! (StackTop=%p)\n", pctxt->LocalHeap.pbHeapEnd));
|
||
|
} //PopFrame
|
||
|
|
||
|
/***LP PushPost - Push a Post frame on the stack
|
||
|
*
|
||
|
* ENTRY
|
||
|
* pctxt -> CTXT
|
||
|
* pfnPost -> post processing function
|
||
|
* uipData1 - data1
|
||
|
* uipData2 - data2
|
||
|
* pdataResult -> result object
|
||
|
*
|
||
|
* EXIT-SUCCESS
|
||
|
* returns STATUS_SUCCESS
|
||
|
* EXIT-FAILURE
|
||
|
* returns AMLIERR_ code
|
||
|
*/
|
||
|
|
||
|
NTSTATUS LOCAL PushPost(PCTXT pctxt, PFNPARSE pfnPost, ULONG_PTR uipData1,
|
||
|
ULONG_PTR uipData2, POBJDATA pdataResult)
|
||
|
{
|
||
|
TRACENAME("PUSHPOST")
|
||
|
NTSTATUS rc = STATUS_SUCCESS;
|
||
|
PPOST ppost;
|
||
|
|
||
|
ENTER(2, ("PushPost(pctxt=%x,pfnPost=%x,Data1=%x,Data2=%x,pdataResult=%x)\n",
|
||
|
pctxt, pfnPost, uipData1, uipData2, pdataResult));
|
||
|
|
||
|
if ((rc = PushFrame(pctxt, SIG_POST, sizeof(POST), pfnPost, &ppost)) ==
|
||
|
STATUS_SUCCESS)
|
||
|
{
|
||
|
ppost->uipData1 = uipData1;
|
||
|
ppost->uipData2 = uipData2;
|
||
|
ppost->pdataResult = pdataResult;
|
||
|
}
|
||
|
|
||
|
EXIT(2, ("PushPost=%x (ppost=%x)\n", rc, ppost));
|
||
|
return rc;
|
||
|
} //PushPost
|
||
|
|
||
|
/***LP PushScope - Push a ParseScope frame on the stack
|
||
|
*
|
||
|
* ENTRY
|
||
|
* pctxt -> CTXT
|
||
|
* pbOpBegin -> beginning of scope
|
||
|
* pbOpEnd -> end of scope
|
||
|
* pbOpRet -> return address after end of scope (NULL if continue on)
|
||
|
* pnsScope -> new scope
|
||
|
* powner -> new owner
|
||
|
* pheap -> new heap
|
||
|
* pdataResult -> result object
|
||
|
*
|
||
|
* EXIT-SUCCESS
|
||
|
* returns STATUS_SUCCESS
|
||
|
* EXIT-FAILURE
|
||
|
* returns AMLIERR_ code
|
||
|
*/
|
||
|
|
||
|
NTSTATUS LOCAL PushScope(PCTXT pctxt, PUCHAR pbOpBegin, PUCHAR pbOpEnd,
|
||
|
PUCHAR pbOpRet, PNSOBJ pnsScope, POBJOWNER powner,
|
||
|
PHEAP pheap, POBJDATA pdataResult)
|
||
|
{
|
||
|
TRACENAME("PUSHSCOPE")
|
||
|
NTSTATUS rc = STATUS_SUCCESS;
|
||
|
PSCOPE pscope;
|
||
|
|
||
|
ENTER(2, ("PushScope(pctxt=%x,pbOpBegin=%x,pbOpEnd=%x,pbOpRet=%x,pnsScope=%x,pheap=%x,pdataResult=%x)\n",
|
||
|
pctxt, pbOpBegin, pbOpEnd, pbOpRet, pnsScope, pheap,
|
||
|
pdataResult));
|
||
|
|
||
|
if ((rc = PushFrame(pctxt, SIG_SCOPE, sizeof(SCOPE), ParseScope, &pscope))
|
||
|
== STATUS_SUCCESS)
|
||
|
{
|
||
|
pctxt->pbOp = pbOpBegin;
|
||
|
pscope->pbOpEnd = pbOpEnd;
|
||
|
pscope->pbOpRet = pbOpRet;
|
||
|
pscope->pnsPrevScope = pctxt->pnsScope;
|
||
|
pctxt->pnsScope = pnsScope;
|
||
|
pscope->pownerPrev = pctxt->powner;
|
||
|
pctxt->powner = powner;
|
||
|
pscope->pheapPrev = pctxt->pheapCurrent;
|
||
|
pctxt->pheapCurrent = pheap;
|
||
|
pscope->pdataResult = pdataResult;
|
||
|
}
|
||
|
|
||
|
EXIT(2, ("PushScope=%x (pscope=%x)\n", rc, pscope));
|
||
|
return rc;
|
||
|
} //PushScope
|
||
|
|
||
|
/***LP PushCall - Push a Call frame on the stack
|
||
|
*
|
||
|
* ENTRY
|
||
|
* pctxt -> CTXT
|
||
|
* pnsMethod -> method object
|
||
|
* pdataResult -> result object
|
||
|
*
|
||
|
* EXIT-SUCCESS
|
||
|
* returns STATUS_SUCCESS
|
||
|
* EXIT-FAILURE
|
||
|
* returns AMLIERR_ code
|
||
|
*/
|
||
|
|
||
|
NTSTATUS LOCAL PushCall(PCTXT pctxt, PNSOBJ pnsMethod, POBJDATA pdataResult)
|
||
|
{
|
||
|
TRACENAME("PUSHCALL")
|
||
|
NTSTATUS rc = STATUS_SUCCESS;
|
||
|
PCALL pcall;
|
||
|
|
||
|
ENTER(2, ("PushCall(pctxt=%x,pnsMethod=%s,pdataResult=%x)\n",
|
||
|
pctxt, GetObjectPath(pnsMethod), pdataResult));
|
||
|
|
||
|
ASSERT((pnsMethod == NULL) ||
|
||
|
(pnsMethod->ObjData.dwDataType == OBJTYPE_METHOD));
|
||
|
|
||
|
if ((rc = PushFrame(pctxt, SIG_CALL, sizeof(CALL), ParseCall, &pcall))
|
||
|
== STATUS_SUCCESS)
|
||
|
{
|
||
|
if (pnsMethod != NULL)
|
||
|
{
|
||
|
PMETHODOBJ pm = (PMETHODOBJ)pnsMethod->ObjData.pbDataBuff;
|
||
|
|
||
|
pcall->pnsMethod = pnsMethod;
|
||
|
if (pm->bMethodFlags & METHOD_SERIALIZED)
|
||
|
{
|
||
|
pcall->FrameHdr.dwfFrame |= CALLF_NEED_MUTEX;
|
||
|
}
|
||
|
pcall->icArgs = (int)(pm->bMethodFlags & METHOD_NUMARG_MASK);
|
||
|
if (pcall->icArgs > 0)
|
||
|
{
|
||
|
if ((pcall->pdataArgs = NEWODOBJ(pctxt->pheapCurrent,
|
||
|
sizeof(OBJDATA)*pcall->icArgs))
|
||
|
== NULL)
|
||
|
{
|
||
|
rc = AMLI_LOGERR(AMLIERR_OUT_OF_MEM,
|
||
|
("PushCall: failed to allocate argument objects"));
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
MEMZERO(pcall->pdataArgs, sizeof(OBJDATA)*pcall->icArgs);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
//
|
||
|
// This is a dummy call frame for AMLILoadDDB. We just need it
|
||
|
// for its Locals array in case there is ASL referencing them.
|
||
|
// But we don't really want to parse a call frame, so let's set
|
||
|
// it to final clean up stage.
|
||
|
//
|
||
|
ASSERT(pctxt->pcall == NULL);
|
||
|
pctxt->pcall = pcall;
|
||
|
pcall->FrameHdr.dwfFrame = 4;
|
||
|
}
|
||
|
|
||
|
pcall->pdataResult = pdataResult;
|
||
|
}
|
||
|
|
||
|
EXIT(2, ("PushCall=%x (pcall=%x)\n", rc, pcall));
|
||
|
return rc;
|
||
|
} //PushCall
|
||
|
|
||
|
/***LP PushTerm - Push a Term frame on the stack
|
||
|
*
|
||
|
* ENTRY
|
||
|
* pctxt -> CTXT
|
||
|
* pbOpTerm -> term opcode
|
||
|
* pbScopeEnd -> end of current scope
|
||
|
* pamlterm -> AMLTERM
|
||
|
* pdataResult -> result object
|
||
|
*
|
||
|
* EXIT-SUCCESS
|
||
|
* returns STATUS_SUCCESS
|
||
|
* EXIT-FAILURE
|
||
|
* returns AMLIERR_ code
|
||
|
*/
|
||
|
|
||
|
NTSTATUS LOCAL PushTerm(PCTXT pctxt, PUCHAR pbOpTerm, PUCHAR pbScopeEnd,
|
||
|
PAMLTERM pamlterm, POBJDATA pdataResult)
|
||
|
{
|
||
|
TRACENAME("PUSHTERM")
|
||
|
NTSTATUS rc = STATUS_SUCCESS;
|
||
|
PTERM pterm;
|
||
|
|
||
|
ENTER(2, ("PushTerm(pctxt=%x,pbOpTerm=%x,pbScopeEnd=%x,pamlterm=%x,pdataResult=%x)\n",
|
||
|
pctxt, pbOpTerm, pbScopeEnd, pamlterm, pdataResult));
|
||
|
|
||
|
if ((rc = PushFrame(pctxt, SIG_TERM, sizeof(TERM), ParseTerm, &pterm)) ==
|
||
|
STATUS_SUCCESS)
|
||
|
{
|
||
|
pterm->pbOpTerm = pbOpTerm;
|
||
|
pterm->pbScopeEnd = pbScopeEnd;
|
||
|
pterm->pamlterm = pamlterm;
|
||
|
pterm->pdataResult = pdataResult;
|
||
|
pterm->icArgs = pamlterm->pszArgTypes? STRLEN(pamlterm->pszArgTypes): 0;
|
||
|
if (pterm->icArgs > 0)
|
||
|
{
|
||
|
if ((pterm->pdataArgs = NEWODOBJ(pctxt->pheapCurrent,
|
||
|
sizeof(OBJDATA)*pterm->icArgs)) ==
|
||
|
NULL)
|
||
|
{
|
||
|
rc = AMLI_LOGERR(AMLIERR_OUT_OF_MEM,
|
||
|
("PushTerm: failed to allocate argument objects"));
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
MEMZERO(pterm->pdataArgs, sizeof(OBJDATA)*pterm->icArgs);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
EXIT(2, ("PushTerm=%x (pterm=%x)\n", rc, pterm));
|
||
|
return rc;
|
||
|
} //PushTerm
|
||
|
|
||
|
/***LP RunContext - Run a context
|
||
|
*
|
||
|
* ENTRY
|
||
|
* pctxt -> CTXT
|
||
|
*
|
||
|
* EXIT-SUCCESS
|
||
|
* returns STATUS_SUCCESS
|
||
|
* EXIT-FAILURE
|
||
|
* returns AMLIERR_ code
|
||
|
*
|
||
|
* NOTE
|
||
|
* Caller must own the scheduler lock such that the context flags can
|
||
|
* be updated properly. The lock is dropped and re-obtain around
|
||
|
* execution of the target context.
|
||
|
*/
|
||
|
|
||
|
NTSTATUS LOCAL RunContext(PCTXT pctxt)
|
||
|
{
|
||
|
TRACENAME("RUNCONTEXT")
|
||
|
NTSTATUS rc;
|
||
|
PFRAMEHDR pfh;
|
||
|
PKTHREAD pkthSave = gReadyQueue.pkthCurrent;
|
||
|
PCTXT pctxtSave = gReadyQueue.pctxtCurrent;
|
||
|
|
||
|
ENTER(2, ("RunContext(pctxt=%x)\n", pctxt));
|
||
|
|
||
|
//
|
||
|
// Better be a Ready Context structure.
|
||
|
//
|
||
|
ASSERT(pctxt->dwSig == SIG_CTXT);
|
||
|
ASSERT(pctxt->dwfCtxt & CTXTF_READY);
|
||
|
|
||
|
//
|
||
|
// Remember previous context and thread.
|
||
|
//
|
||
|
gReadyQueue.pctxtCurrent = pctxt;
|
||
|
gReadyQueue.pkthCurrent = KeGetCurrentThread();
|
||
|
|
||
|
LOGSCHEDEVENT('RUNC', (ULONG_PTR)pctxt, (ULONG_PTR)
|
||
|
(pctxt->pnctxt? pctxt->pnctxt->pnsObj: pctxt->pnsObj),
|
||
|
(ULONG_PTR)pctxt->dwfCtxt);
|
||
|
|
||
|
//
|
||
|
// As long as the context is ready, execute it.
|
||
|
//
|
||
|
for (;;)
|
||
|
{
|
||
|
//
|
||
|
// Transistion context from Ready to Running.
|
||
|
//
|
||
|
rc = STATUS_SUCCESS;
|
||
|
pctxt->dwfCtxt &= ~CTXTF_READY;
|
||
|
pctxt->dwfCtxt |= CTXTF_RUNNING;
|
||
|
|
||
|
//
|
||
|
// Drop scheduler lock and execute context.
|
||
|
//
|
||
|
ReleaseMutex(&gReadyQueue.mutCtxtQ);
|
||
|
|
||
|
//
|
||
|
// Go for as long as there's work to perform.
|
||
|
//
|
||
|
while (!IsStackEmpty(pctxt))
|
||
|
{
|
||
|
CHKDEBUGGERREQ();
|
||
|
pfh = (PFRAMEHDR)pctxt->LocalHeap.pbHeapEnd;
|
||
|
ASSERT(pfh->pfnParse != NULL);
|
||
|
|
||
|
rc = pfh->pfnParse(pctxt, pfh, rc);
|
||
|
if ((rc == AMLISTA_PENDING) || (rc == AMLISTA_DONE))
|
||
|
{
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// Get the scheduler lock, and clear the running flag.
|
||
|
//
|
||
|
AcquireMutex(&gReadyQueue.mutCtxtQ);
|
||
|
|
||
|
//
|
||
|
// If we are in nested eval and the nested context is done,
|
||
|
// we must not clear the running flag because the parent thread
|
||
|
// is still running.
|
||
|
//
|
||
|
if (!(pctxt->dwfCtxt & CTXTF_NEST_EVAL) || (rc != AMLISTA_DONE))
|
||
|
{
|
||
|
pctxt->dwfCtxt &= ~CTXTF_RUNNING;
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// If the context is no longer ready, we're done.
|
||
|
//
|
||
|
if (!(pctxt->dwfCtxt & CTXTF_READY))
|
||
|
{
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// Context became Ready during a pending operation, keep
|
||
|
// dispatching.
|
||
|
//
|
||
|
ASSERT (rc == AMLISTA_PENDING);
|
||
|
}
|
||
|
|
||
|
if (rc == AMLISTA_PENDING)
|
||
|
{
|
||
|
pctxt->dwfCtxt |= CTXTF_NEED_CALLBACK;
|
||
|
}
|
||
|
else if (rc == AMLISTA_DONE)
|
||
|
{
|
||
|
if (pctxt->pnctxt == NULL)
|
||
|
{
|
||
|
pctxt->dwfCtxt &= ~CTXTF_NEST_EVAL;
|
||
|
}
|
||
|
rc = STATUS_SUCCESS;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
ReleaseMutex(&gReadyQueue.mutCtxtQ);
|
||
|
if ((rc == STATUS_SUCCESS) && (pctxt->pdataCallBack != NULL))
|
||
|
{
|
||
|
rc = DupObjData(gpheapGlobal, pctxt->pdataCallBack, &pctxt->Result);
|
||
|
}
|
||
|
|
||
|
if (pctxt->dwfCtxt & CTXTF_NEED_CALLBACK)
|
||
|
{
|
||
|
AsyncCallBack(pctxt, rc);
|
||
|
|
||
|
if(pctxt->dwfCtxt & CTXTF_ASYNC_EVAL)
|
||
|
{
|
||
|
rc = AMLISTA_PENDING;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// Free any owned resources the context may not have freed.
|
||
|
//
|
||
|
while (pctxt->plistResources != NULL)
|
||
|
{
|
||
|
PRESOURCE pres;
|
||
|
|
||
|
pres = CONTAINING_RECORD(pctxt->plistResources, RESOURCE, list);
|
||
|
ASSERT (pres->pctxtOwner == pctxt);
|
||
|
|
||
|
//
|
||
|
// Note that it is the responsibility of the corresponding
|
||
|
// resource release functions (e.g. ReleaseASLMutex) to dequeue
|
||
|
// the resource from the list and free it.
|
||
|
//
|
||
|
switch (pres->dwResType)
|
||
|
{
|
||
|
case RESTYPE_MUTEX:
|
||
|
ReleaseASLMutex(pctxt, pres->pvResObj);
|
||
|
break;
|
||
|
|
||
|
default:
|
||
|
//
|
||
|
// We should never come here. In case we do, we need to
|
||
|
// dequeue the unknown resource object and free it.
|
||
|
//
|
||
|
pres = CONTAINING_RECORD(
|
||
|
ListRemoveHead(&pctxt->plistResources),
|
||
|
RESOURCE, list);
|
||
|
ASSERT(pres == NULL);
|
||
|
FREECROBJ(pres);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
FreeContext(pctxt);
|
||
|
|
||
|
AcquireMutex(&gReadyQueue.mutCtxtQ);
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// Restore previous context and thread.
|
||
|
//
|
||
|
gReadyQueue.pkthCurrent = pkthSave;
|
||
|
gReadyQueue.pctxtCurrent = pctxtSave;
|
||
|
|
||
|
if ((gReadyQueue.dwfCtxtQ & CQF_FLUSHING) && (gplistCtxtHead == NULL))
|
||
|
{
|
||
|
//
|
||
|
// We just flushed the last pending ctxt, let's go into paused state.
|
||
|
//
|
||
|
gReadyQueue.dwfCtxtQ &= ~CQF_FLUSHING;
|
||
|
gReadyQueue.dwfCtxtQ |= CQF_PAUSED;
|
||
|
if (gReadyQueue.pfnPauseCallback != NULL)
|
||
|
{
|
||
|
//
|
||
|
// We are in paused state and all pending contexts are flushed,
|
||
|
// tell core driver that we are done flushing.
|
||
|
//
|
||
|
gReadyQueue.pfnPauseCallback(gReadyQueue.PauseCBContext);
|
||
|
LOGSCHEDEVENT('PACB', (ULONG_PTR)pctxt, (ULONG_PTR)rc, 0);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
LOGSCHEDEVENT('RUN!', (ULONG_PTR)pctxt, (ULONG_PTR)rc, 0);
|
||
|
|
||
|
EXIT(2, ("RunContext=%x\n", rc));
|
||
|
return rc;
|
||
|
} //RunContext
|