Leaked source code of windows server 2003
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

402 lines
11 KiB

/*++
Copyright (c) 1998-2000 Microsoft Corporation
Module Name:
callback.c
Abstract:
Provides generic 64-to-32 transfer routines.
Author:
20-May-1998 BarryBo
Revision History:
2-Sept-1999 [askhalid] Removing some 32bit alpha specific code and using right context.
--*/
#define _WOW64DLLAPI_
#include <nt.h>
#include <ntrtl.h>
#include <nturtl.h>
#include "wow64p.h"
#include "wow64cpu.h"
ASSERTNAME;
VOID
WOW64DLLAPI
Wow64ApcRoutine(
ULONG_PTR ApcContext,
ULONG_PTR Arg2,
ULONG_PTR Arg3
)
/*++
Routine Description:
Call a 32-bit APC function.
Arguments:
ApcContext - wow64 APC context data
Arg2 - second arg to APC routine
Arg3 - third arg to APC routine
Return Value:
None. Returns contrl back to NTDLL's APC handler, which will
call native NtContinue to resume execution.
--*/
{
CONTEXT32 NewContext32;
ULONG SP;
PULONG Ptr;
USER_APC_ENTRY UserApcEntry;
//
// Grab the current 32-bit context
//
NewContext32.ContextFlags = CONTEXT32_INTEGER|CONTEXT32_CONTROL;
CpuGetContext(NtCurrentThread(),
NtCurrentProcess(),
NtCurrentTeb(),
&NewContext32);
//
// Build up the APC callback state in NewContext32
//
SP = CpuGetStackPointer() & (~3);
SP -= 4*sizeof(ULONG)+sizeof(CONTEXT32);
Ptr = (PULONG)SP;
Ptr[0] = (ULONG)(ApcContext >> 32); // NormalRoutine
Ptr[1] = (ULONG)ApcContext; // NormalContext
Ptr[2] = (ULONG)Arg2; // SystemArgument1
Ptr[3] = (ULONG)Arg3; // SystemArgument2
((PCONTEXT32)(&Ptr[4]))->ContextFlags = CONTEXT32_FULL;
CpuGetContext(NtCurrentThread(),
NtCurrentProcess(),
NtCurrentTeb(),
(PCONTEXT32)&Ptr[4]); // ContinueContext (BYVAL!)
CpuSetStackPointer(SP);
CpuSetInstructionPointer(Ntdll32KiUserApcDispatcher);
//
// Link this APC into the list of outstanding APCs
//
UserApcEntry.Next = (PUSER_APC_ENTRY)Wow64TlsGetValue(WOW64_TLS_APCLIST);
UserApcEntry.pContext32 = (PCONTEXT32)&Ptr[4];
Wow64TlsSetValue(WOW64_TLS_APCLIST, &UserApcEntry);
//
// Call the 32-bit APC function. 32-bit NtContinue will longjmp
// back when the APC function is done.
//
if (setjmp(UserApcEntry.JumpBuffer) == 0) {
RunCpuSimulation();
}
//
// If we get here, Wow64NtContinue has done a longjmp back, so
// return back to the caller (in ntdll.dll), which will do a
// native NtContinue and restore the native stack pointer and
// context back.
//
// This is critical to do. The x86 CONTEXT above has an out-of-date
// value for EAX. It still contains the system-service number for
// whatever kernel call was made that allowed the APC to run. On
// an x86 machine, the x86 CONTEXT above would have had STATUS_USER_APC
// or some other code like it, but on WOW64 we don't know what value
// to use. The correct value is sitting in the 64-bit CONTEXT up
// the stack from where we are. So... by returning here via longjmp,
// native ntdll.dll will do an NtContinue to resume execution in the
// native Nt* API that allowed the native APC to fire. It will load
// the return register with the right NTSTATUS code, so the whNt*
// thunk will see the correct code, and reflect it into EAX.
//
}
NTSTATUS
WOW64DLLAPI
Wow64WrapApcProc(
PVOID *pApcRoutine,
PVOID *pApcContext
)
/*++
Routine Description:
Thunk a 32-bit ApcRoutine/ApcContext pair to 64-bit
Arguments:
pApcRoutine - pointer to pointer to APC routine. IN is the 32-bit
routine. OUT is the 64-bit wow64 thunk
pApcContext - pointer to pointer to APC context. IN is the 32-bit
context. OUT is the 64-bit wow64 thunk
Return Value:
NTSTATUS
--*/
{
NTSTATUS NtStatus = STATUS_SUCCESS;
if (*pApcRoutine) {
//
// Dispatch the call to the jacket routine inside Wow64
//
*pApcContext = (PVOID)((ULONG_PTR)*pApcContext | ((ULONG_PTR)*pApcRoutine << 32));
*pApcRoutine = Wow64ApcRoutine;
} else {
NtStatus = STATUS_INVALID_PARAMETER;
}
return NtStatus;
}
ULONG
Wow64KiUserCallbackDispatcher(
PUSERCALLBACKDATA pUserCallbackData,
ULONG ApiNumber,
ULONG ApiArgument,
ULONG ApiSize
)
/*++
Routine Description:
Make a call from a 64-to-32 user callback thunk into 32-bit code.
This function calls ntdll32's KiUserCallbackDispatcher, and returns
when 32-bit code calls NtCallbackReturn/ZwCallbackReturn.
Arguments:
pUserCallbackData - OUT struct to use for tracking this callback
ApiNumber - index of API to call
ApiArgument - 32-bit pointer to 32-bit argument to the API
ApiSize - size of *ApiArgument
Return Value:
Return value from the API call
--*/
{
CONTEXT32 OldContext;
ULONG ExceptionList;
PTEB32 Teb32;
ULONG NewStack;
//
// Handle nested callbacks
//
pUserCallbackData->PreviousUserCallbackData = Wow64TlsGetValue(WOW64_TLS_USERCALLBACKDATA);
//
// Store the callback data in the TEB. whNtCallbackReturn will
// use this pointer to pass information back here via a longjmp.
//
Wow64TlsSetValue(WOW64_TLS_USERCALLBACKDATA, pUserCallbackData);
if (!setjmp(pUserCallbackData->JumpBuffer)) {
//
// Make the call to ntdll32. whNtCallbackReturn will
// longjmp back to this routine when it is called.
//
OldContext.ContextFlags = CONTEXT32_FULL;
CpuGetContext(NtCurrentThread(),
NtCurrentProcess(),
NtCurrentTeb(),
&OldContext);
NewStack = OldContext.Esp - ApiSize;
RtlCopyMemory((PVOID)NewStack, (PVOID)ApiArgument, ApiSize);
*(PULONG)(NewStack - 4) = 0; // InputLength
*(PULONG)(NewStack - 8) = NewStack;
*(PULONG)(NewStack - 12) = ApiNumber;
*(PULONG)(NewStack - 16) = 0;
NewStack -= 16;
CpuSetStackPointer(NewStack);
CpuSetInstructionPointer(Ntdll32KiUserCallbackDispatcher);
//
// Save the exception list in case another handler is defined during
// the callout.
//
Teb32 = NtCurrentTeb32();
ExceptionList = Teb32->NtTib.ExceptionList;
//
// Execte the callback
//
RunCpuSimulation();
//
// This never returns. When 32-bit code is done, it calls
// NtCallbackReturn. The thunk does a longjmp back to this
// routine and lands in the 'else' clause below:
//
} else {
//
// Made it back from the NtCallbackReturn thunk. Restore the
// 32-bit context as it was before the callback, and return
// back to our caller. Our caller will call 64-bit
// NtCallbackReturn to finish blowing off the 64-bit stack.
//
CpuSetContext(NtCurrentThread(),
NtCurrentProcess(),
NtCurrentTeb(),
&OldContext);
//
// Restore exception list.
//
NtCurrentTeb32()->NtTib.ExceptionList = ExceptionList;
return pUserCallbackData->Status;
}
//
// Should never get here.
//
WOWASSERT(FALSE);
return STATUS_UNSUCCESSFUL;
}
NTSTATUS
Wow64NtCallbackReturn(
PVOID OutputBuffer,
ULONG OutputLength,
NTSTATUS Status
)
{
PUSERCALLBACKDATA pUserCallbackData;
//
// Find the callback data stuffed in TLS by Wow64KiUserCallbackDispatcher
//
pUserCallbackData = (PUSERCALLBACKDATA)Wow64TlsGetValue(WOW64_TLS_USERCALLBACKDATA);
if (pUserCallbackData) {
//
// Restore previous User Callback context
//
Wow64TlsSetValue(WOW64_TLS_USERCALLBACKDATA, pUserCallbackData->PreviousUserCallbackData);
//
// Jump back to Wow64KiUserCallbackDispatcher
//
pUserCallbackData->UserBuffer = NULL;
pUserCallbackData->OutputBuffer = OutputBuffer;
//
// Realign the buffer
//
if (((SIZE_T)OutputBuffer & (sizeof(ULONGLONG)-1)) != 0) {
pUserCallbackData->OutputBuffer = Wow64AllocateHeap ( OutputLength );
if (pUserCallbackData->OutputBuffer == NULL) {
pUserCallbackData->OutputBuffer = OutputBuffer;
pUserCallbackData->Status = STATUS_NO_MEMORY;
} else {
RtlCopyMemory (pUserCallbackData->OutputBuffer, OutputBuffer, OutputLength );
pUserCallbackData->UserBuffer = OutputBuffer; // works as a flag
}
}
pUserCallbackData->OutputLength = OutputLength;
pUserCallbackData->Status = Status;
longjmp(pUserCallbackData->JumpBuffer, 1);
//
// We would never come here
//
}
//
// No callback data. Probably a non-nested NtCallbackReturn call.
// The kernel fails these with this return value.
//
return STATUS_NO_CALLBACK_ACTIVE;
}
WOW64DLLAPI
NTSTATUS
Wow64NtContinue(
IN PCONTEXT ContextRecord, // really a PCONTEXT32
IN BOOLEAN TestAlert
)
/*++
Routine Description:
32-bit wrapper for NtContinue. Loads the new CONTEXT32 into the CPU
and optionally allows usermode APCs to run.
Arguments:
ContextRecord - new 32-bit CONTEXT to use
TestAlert - TRUE if usermode APCs may be fired
Return Value:
NTSTATUS.
--*/
{
PCONTEXT32 Context32 = (PCONTEXT32)ContextRecord;
PUSER_APC_ENTRY pApcEntry;
PUSER_APC_ENTRY pApcEntryPrev;
CpuSetContext(NtCurrentThread(),
NtCurrentProcess(),
NtCurrentTeb(),
Context32);
pApcEntryPrev = NULL;
pApcEntry = (PUSER_APC_ENTRY)Wow64TlsGetValue(WOW64_TLS_APCLIST);
while (pApcEntry) {
if (pApcEntry->pContext32 == Context32) {
//
// Found an outstanding usermode APC on this thread, and this
// NtContinue call matches it. Unwind back to the right place
// on the native stack and have it do an NtContinue too.
//
if (pApcEntryPrev) {
pApcEntryPrev->Next = pApcEntry->Next;
} else {
Wow64TlsSetValue(WOW64_TLS_APCLIST, pApcEntry->Next);
}
longjmp(pApcEntry->JumpBuffer, 1);
}
pApcEntryPrev = pApcEntry;
pApcEntry = pApcEntry->Next;
}
//
// No usermode APC is outstanding for this context record. Don't
// unwind the native stack because there is no place to go... just
// continue the simulation.
//
if (TestAlert) {
NtTestAlert();
}
return Context32->Eax;
}