windows-nt/Source/XPSP1/NT/base/wow64/mscpu/fraglib/suspend.c
2020-09-26 16:20:57 +08:00

927 lines
22 KiB
C

/*++
Copyright (c) 1999-1998 Microsoft Corporation
Module Name:
suspend.c
Abstract:
This module implements CpuSuspendThread, CpuGetContext and CpuSetContext.
Author:
14-Dec-1999 SamerA
Revision History:
--*/
#include <nt.h>
#include <ntrtl.h>
#include <nturtl.h>
#include <windows.h>
#include <stdio.h>
#define _WX86CPUAPI_
#include "wx86.h"
#include "wx86nt.h"
#include "wx86cpu.h"
#include "cpuassrt.h"
#ifdef MSCCPU
#include "ccpu.h"
#include "msccpup.h"
#undef GET_BYTE
#undef GET_SHORT
#undef GET_LONG
#else
#include "threadst.h"
#include "instr.h"
#include "frag.h"
ASSERTNAME;
#endif
#include "fragp.h"
#include "cpunotif.h"
VOID
RemoteSuspendAtNativeCode (
VOID);
NTSTATUS
CpupFreeSuspendMsg(
PCPU_SUSPEND_MSG CpuSuspendMsg)
/*++
Routine Description:
This routine frees the resources associated with the suspend message structure on
the remote side.
Arguments:
CpuSuspendMsg - Address of Suspend message structure
Return Value:
NTSTATUS.
--*/
{
SIZE_T RegionSize;
NtClose(CpuSuspendMsg->StartSuspendCallEvent);
NtClose(CpuSuspendMsg->EndSuspendCallEvent);
RegionSize = sizeof(*CpuSuspendMsg);
NtFreeVirtualMemory(NtCurrentProcess(),
&CpuSuspendMsg,
&RegionSize,
MEM_RELEASE);
return STATUS_SUCCESS;
}
VOID
CpupSuspendAtNativeCode(
PCONTEXT Context,
PCPU_SUSPEND_MSG SuspendMsg)
/*++
Routine Description:
Prepares the current to get suspended. This routine is executed as a result
of calling RtlRemoteCall on this current thread. This routine will
update the CPUCONTEXT of the current thread with the passed SuspendM Message
and notify the CPU that the current thread needs to be suspended.
This routine must call NtContinue at the end to continue execution at
the point where it has been interrupted.
NOTE : Any change to the parameter list of this function must accompany
a change to the RtlRemoteCall in CpuSuspendThread() and
RemoteSuspendAtNativeCode().
Arguments:
Context - Context to return to execute at
SuspendMsg - Suspend message address
Return Value:
NONE
--*/
{
DECLARE_CPU;
InterlockedCompareExchangePointer(&cpu->SuspendMsg,
SuspendMsg,
NULL);
if (cpu->SuspendMsg == SuspendMsg)
{
cpu->CpuNotify |= CPUNOTIFY_SUSPEND;
}
else
{
CpupFreeSuspendMsg(SuspendMsg);
}
if (Context)
{
NtContinue(Context,FALSE);
}
CPUASSERT(FALSE);
return;
}
NTSTATUS
CpupSuspendCurrentThread(
VOID)
/*++
Routine Description:
This routine is called from the main CPU loop after leaving the translation cache,
and start running native code.
Now it's the best time to suspend the currently executing thread.
Arguments:
None.
Return Value:
NTSTATUS.
--*/
{
NTSTATUS NtStatus;
LARGE_INTEGER TimeOut;
PCPU_SUSPEND_MSG CpuSuspendMsg;
SIZE_T RegionSize;
DECLARE_CPU;
CpuSuspendMsg = cpu->SuspendMsg;
NtStatus = NtSetEvent(CpuSuspendMsg->StartSuspendCallEvent, NULL);
if (NT_SUCCESS(NtStatus))
{
TimeOut.QuadPart = UInt32x32To64( 40000, 10000 );
TimeOut.QuadPart *= -1;
NtStatus = NtWaitForSingleObject(CpuSuspendMsg->EndSuspendCallEvent,
FALSE,
&TimeOut);
}
else
{
LOGPRINT((TRACELOG, "CpupSuspendCurrentThread: Couldn't signal Start suspendcall event (%lx) -%lx\n",
CpuSuspendMsg->StartSuspendCallEvent, NtStatus));
}
CpupFreeSuspendMsg(CpuSuspendMsg);
cpu->SuspendMsg = NULL;
return NtStatus;
}
NTSTATUS CpupReadBuffer(
IN HANDLE ProcessHandle,
IN PVOID Source,
OUT PVOID Destination,
IN ULONG Size)
/*++
Routine Description:
This routine reads the source buffer into the destination buffer. It
optimizes calls to NtReadVirtualMemory by checking whether the
source buffer is in the currnt process or not.
Arguments:
ProcessHandle - Target process handle to read data from
Source - Target base address to read data from
Destination - Address of buffer to receive data read from the specified address space
Size - Size of data to read
Return Value:
NTSTATUS.
--*/
{
NTSTATUS NtStatus = STATUS_SUCCESS;
return NtReadVirtualMemory(ProcessHandle,
Source,
Destination,
Size,
NULL);
}
NTSTATUS
CpupWriteBuffer(
IN HANDLE ProcessHandle,
IN PVOID Target,
IN PVOID Source,
IN ULONG Size)
/*++
Routine Description:
Writes data to memory taken into consideration if the write is cross-process
or not
Arguments:
ProcessHandle - Target process handle to write data into
Target - Target base address to write data at
Source - Address of contents to write in the specified address space
Size - Size of data to write
Return Value:
NTSTATUS.
--*/
{
return NtWriteVirtualMemory(ProcessHandle,
Target,
Source,
Size,
NULL);
}
NTSTATUS
CpupSetupSuspendCallParamters(
IN HANDLE RemoteProcessHandle,
IN PCPU_SUSPEND_MSG SuspendMsg,
OUT PVOID *Arguments)
/*++
Routine Description:
This routine setup the arguments for the remoted call to
CpupSuspendAtNativeCode.
Arguments:
RemoteProcessHandle - Handle of process to setup the arguments in
SuspendMsg - Suspend message to remote to the target process
Arguments - Pointer to an array of parameters
Return Value:
NTSTATUS.
--*/
{
NTSTATUS NtStatus = STATUS_SUCCESS;
CPU_SUSPEND_MSG RemoteSuspendMsg;
SIZE_T RegionSize;
NtStatus = NtDuplicateObject(NtCurrentProcess(),
SuspendMsg->StartSuspendCallEvent,
RemoteProcessHandle,
&RemoteSuspendMsg.StartSuspendCallEvent,
0,
0,
DUPLICATE_SAME_ACCESS);
if (!NT_SUCCESS(NtStatus))
{
LOGPRINT((ERRORLOG, "CpupSetupSuspendCallParamters: Couldn't duplicate event (%lx) into %lx -%lx\n",
SuspendMsg->StartSuspendCallEvent, RemoteProcessHandle, NtStatus));
return NtStatus;
}
NtStatus = NtDuplicateObject(NtCurrentProcess(),
SuspendMsg->EndSuspendCallEvent,
RemoteProcessHandle,
&RemoteSuspendMsg.EndSuspendCallEvent,
0,
0,
DUPLICATE_SAME_ACCESS);
if (!NT_SUCCESS(NtStatus))
{
LOGPRINT((ERRORLOG, "CpupSetupSuspendCallParamters: Couldn't duplicate event (%lx) into %lx -%lx\n",
SuspendMsg->EndSuspendCallEvent, RemoteProcessHandle, NtStatus));
return NtStatus;
}
RegionSize = sizeof(RemoteSuspendMsg);
*Arguments = NULL;
NtStatus = NtAllocateVirtualMemory(RemoteProcessHandle,
Arguments,
0,
&RegionSize,
MEM_RESERVE | MEM_COMMIT,
PAGE_READWRITE);
if (NT_SUCCESS(NtStatus))
{
NtStatus = NtWriteVirtualMemory(RemoteProcessHandle,
*Arguments,
&RemoteSuspendMsg,
sizeof(RemoteSuspendMsg),
NULL);
if (!NT_SUCCESS(NtStatus))
{
LOGPRINT((ERRORLOG, "CpupSetupSuspendCallParamters: Couldn't write parameters in target process (%lx) -%lx\n",
RemoteProcessHandle,NtStatus));
NtFreeVirtualMemory(RemoteProcessHandle,
Arguments,
&RegionSize,
MEM_RELEASE);
}
}
else
{
LOGPRINT((ERRORLOG, "CpupSetupSuspendCallParamters: Couldn't allocate parameters space in target process (%lx) -%lx\n",
RemoteProcessHandle,NtStatus));
}
return NtStatus;
}
NTSTATUS
CpuSuspendThread(
IN HANDLE ThreadHandle,
IN HANDLE ProcessHandle,
IN PTEB Teb,
OUT PULONG PreviousSuspendCount OPTIONAL)
/*++
Routine Description:
This routine is entered while the target thread is actually suspended, however, it's
not known if the target thread is in a consistent state relative to
the CPU. This routine guarantees that the target thread to suspend isn't the
currently executing thread. It will establish a handshake protocol to suspend the
target thread at a consistent cpu state.
Arguments:
ThreadHandle - Handle of target thread to suspend
ProcessHandle - Handle of target thread's process
Teb - Address of the target thread's TEB
PreviousSuspendCount - Previous suspend count
Return Value:
NTSTATUS.
--*/
{
NTSTATUS NtStatus = STATUS_SUCCESS, WaitStatus;
ULONG_PTR CpuSimulationFlag;
CPU_SUSPEND_MSG CpuSuspendMsg;
PVOID Arguments;
LARGE_INTEGER TimeOut;
CpuSuspendMsg.StartSuspendCallEvent = INVALID_HANDLE_VALUE;
CpuSuspendMsg.EndSuspendCallEvent = INVALID_HANDLE_VALUE;
//
// Are we in CPU simulation
//
NtStatus = CpupReadBuffer(ProcessHandle,
((PCHAR)Teb + FIELD_OFFSET(TEB, TlsSlots[WOW64_TLS_INCPUSIMULATION])),
&CpuSimulationFlag,
sizeof(CpuSimulationFlag));
if (!NT_SUCCESS(NtStatus))
{
LOGPRINT((ERRORLOG, "CpuSuspendThread: Couldn't read INCPUSIMULATION flag (%lx) -%lx\n",
CpuSimulationFlag, NtStatus));
goto Cleanup;
}
if (!CpuSimulationFlag)
{
LOGPRINT((TRACELOG, "CpuSuspendThread: Thread is not running simulated code, so leave it suspended (%lx)",
ThreadHandle));
goto Cleanup;
}
NtStatus = NtCreateEvent(&CpuSuspendMsg.StartSuspendCallEvent,
EVENT_ALL_ACCESS,
NULL,
SynchronizationEvent,
FALSE);
if (!NT_SUCCESS(NtStatus))
{
LOGPRINT((ERRORLOG, "CpuSuspendThread: Couldn't create StartSuspendCallEvent -%lx\n",
NtStatus));
goto Cleanup;
}
NtStatus = NtCreateEvent(&CpuSuspendMsg.EndSuspendCallEvent,
EVENT_ALL_ACCESS,
NULL,
SynchronizationEvent,
FALSE);
if (!NT_SUCCESS(NtStatus))
{
LOGPRINT((ERRORLOG, "CpuSuspendThread: Couldn't create EndSuspendCallEvent -%lx\n",
NtStatus));
goto Cleanup;
}
NtStatus = CpupSetupSuspendCallParamters(ProcessHandle,
&CpuSuspendMsg,
&Arguments);
if (NT_SUCCESS(NtStatus))
{
NtStatus = RtlRemoteCall(ProcessHandle,
ThreadHandle,
(PVOID)RemoteSuspendAtNativeCode,
1,
(PULONG_PTR)&Arguments,
TRUE,
TRUE);
if (NT_SUCCESS(NtStatus))
{
NtStatus = NtResumeThread(ThreadHandle, NULL);
if (!NT_SUCCESS(NtStatus))
{
LOGPRINT((ERRORLOG, "CpuSuspendThread: Couldn't resume thread (%lx) -%lx\n",
ThreadHandle, NtStatus));
goto Cleanup;
}
TimeOut.QuadPart = UInt32x32To64( 20000, 10000 );
TimeOut.QuadPart *= -1;
WaitStatus = NtWaitForSingleObject(CpuSuspendMsg.StartSuspendCallEvent,
FALSE,
&TimeOut);
NtStatus = NtSuspendThread(ThreadHandle, PreviousSuspendCount);
if (!NT_SUCCESS(WaitStatus))
{
LOGPRINT((ERRORLOG, "CpuSuspendThread: Couldn't wait for StartSuspendCallEvent -%lx\n",
NtStatus));
goto Cleanup;
}
if (WaitStatus == STATUS_TIMEOUT)
{
LOGPRINT((ERRORLOG, "CpuSuspendThread: Timeout on StartSuspendCallEvent -%lx. Thread %lx may already be waiting.\n",
NtStatus, ThreadHandle));
}
if (NT_SUCCESS(NtStatus))
{
NtSetEvent(CpuSuspendMsg.EndSuspendCallEvent, NULL);
}
}
else
{
LOGPRINT((ERRORLOG, "CpuSuspendThread: RtlRemoteCall failed -%lx\n",
NtStatus));
}
}
Cleanup:
;
if (CpuSuspendMsg.StartSuspendCallEvent != INVALID_HANDLE_VALUE)
{
NtClose(CpuSuspendMsg.StartSuspendCallEvent);
}
if (CpuSuspendMsg.EndSuspendCallEvent != INVALID_HANDLE_VALUE)
{
NtClose(CpuSuspendMsg.EndSuspendCallEvent);
}
return NtStatus;
}
NTSTATUS
GetContextRecord(
PCPUCONTEXT cpu,
PCONTEXT_WX86 Context
)
/*++
Routine Description:
This routine extracts the context record out of the specified cpu context.
Arguments:
cpu - CPU context structure
Context - Context record to fill
Return Value:
NTSTATUS.
--*/
{
NTSTATUS NtStatus = STATUS_SUCCESS;
ULONG ContextFlags;
try
{
ContextFlags = Context->ContextFlags;
if ((ContextFlags & CONTEXT_CONTROL_WX86) == CONTEXT_CONTROL_WX86)
{
Context->EFlags = GetEfl(cpu);
Context->SegCs = CS;
Context->Esp = esp;
Context->SegSs = SS;
Context->Ebp = ebp;
Context->Eip = eip;
//Context->Eip = cpu->eipReg.i4;
}
if ((ContextFlags & CONTEXT_SEGMENTS_WX86) == CONTEXT_SEGMENTS_WX86)
{
Context->SegGs = GS;
Context->SegFs = FS;
Context->SegEs = ES;
Context->SegDs = DS;
}
if ((ContextFlags & CONTEXT_INTEGER_WX86) == CONTEXT_INTEGER_WX86)
{
Context->Eax = eax;
Context->Ebx = ebx;
Context->Ecx = ecx;
Context->Edx = edx;
Context->Edi = edi;
Context->Esi = esi;
}
if ((ContextFlags & CONTEXT_FLOATING_POINT_WX86) == CONTEXT_FLOATING_POINT_WX86)
{
//
// FpuSaveContext() is the same as FNSAVE, except FNSAVE resets the
// FPU when its done.
//
CALLFRAG1(FpuSaveContext, (PBYTE)&Context->FloatSave);
Context->FloatSave.Cr0NpxState = 1; // (Math Present)
}
// if ((ContextFlags & CONTEXT_DEBUG_WX86) == CONTEXT_DEBUG_WX86)
// {
// }
}
except (EXCEPTION_EXECUTE_HANDLER)
{
NtStatus = GetExceptionCode();
}
return NtStatus;
}
NTSTATUS
CpupGetContextRecord(
IN PCPUCONTEXT cpu,
IN OUT PCONTEXT_WX86 Context)
/*++
Routine Description:
This routine extracts the context record out of the specified cpu context.
Arguments:
cpu - CPU context structure
Context - Context record to fill
Return Value:
NTSTATUS.
--*/
{
return GetContextRecord(cpu, Context);
}
NTSTATUS
MsCpuGetContext(
IN OUT PCONTEXT_WX86 Context
)
/*++
Routine Description:
This routine extracts the context record for the currently
executing thread.
Arguments:
Context - Context record to fill
Return Value:
NTSTATUS.
--*/
{
DECLARE_CPU;
return CpupGetContextRecord(cpu, Context);
}
NTSTATUS
MsCpuGetContextThread(
IN HANDLE ProcessHandle,
IN PTEB Teb,
IN OUT PCONTEXT_WX86 Context)
/*++
Routine Description:
This routine extracts the context record of any thread. This is a generic routine.
When entered, if the target thread isn't the currently executing thread, then it should be
guaranteed that the target thread is suspended at a proper CPU state.
Arguments:
ProcessHandle - Open handle to the process that the thread runs in
Teb - Pointer to the target's thread TEB
optimization purposes.
Context - Context record to fill
Return Value:
NTSTATUS.
--*/
{
NTSTATUS NtStatus = STATUS_SUCCESS;
PCPUCONTEXT CpuRemoteContext;
CPUCONTEXT CpuContext;
NtStatus = CpupReadBuffer(ProcessHandle,
((PCHAR)Teb + FIELD_OFFSET(TEB, TlsSlots[WOW64_TLS_CPURESERVED])),
&CpuRemoteContext,
sizeof(CpuRemoteContext));
if (NT_SUCCESS(NtStatus))
{
NtStatus = CpupReadBuffer(ProcessHandle,
CpuRemoteContext,
&CpuContext,
FIELD_OFFSET(CPUCONTEXT, FpData));
if (NT_SUCCESS(NtStatus))
{
NtStatus = CpupGetContextRecord(&CpuContext, Context);
}
else
{
LOGPRINT((ERRORLOG, "MsCpuGetContextThread: Couldn't read CPU context %lx -%lx\n",
CpuRemoteContext, NtStatus));
}
}
else
{
LOGPRINT((ERRORLOG, "MsCpuGetContextThread: Couldn't read CPU context address-%lx\n",
NtStatus));
}
return NtStatus;
}
NTSTATUS
SetContextRecord(
PCPUCONTEXT cpu,
PCONTEXT_WX86 Context
)
/*++
Routine Description:
This routine sets the passed context record for the specified CPUCONTEXT.
Arguments:
cpu - CPU context structure
Context - Context record to set
Return Value:
NTSTATUS.
--*/
{
NTSTATUS NtStatus = STATUS_SUCCESS;
ULONG ContextFlags;
try
{
ContextFlags = Context->ContextFlags;
if ((ContextFlags & CONTEXT_CONTROL_WX86) == CONTEXT_CONTROL_WX86)
{
SetEfl(cpu, Context->EFlags);
CS = (USHORT)Context->SegCs;
esp = Context->Esp;
SS = (USHORT)Context->SegSs;
ebp = Context->Ebp;
eip = Context->Eip;
#if MSCCPU
eipTemp = Context->Eip;
#endif
}
if ((ContextFlags & CONTEXT_SEGMENTS_WX86) == CONTEXT_SEGMENTS_WX86)
{
GS = (USHORT)Context->SegGs;
FS = (USHORT)Context->SegFs;
ES = (USHORT)Context->SegEs;
DS = (USHORT)Context->SegDs;
}
if ((ContextFlags & CONTEXT_INTEGER_WX86) == CONTEXT_INTEGER_WX86)
{
eax = Context->Eax;
ebx = Context->Ebx;
ecx = Context->Ecx;
edx = Context->Edx;
edi = Context->Edi;
esi = Context->Esi;
}
if ((ContextFlags & CONTEXT_FLOATING_POINT_WX86) == CONTEXT_FLOATING_POINT_WX86)
{
CALLFRAG1(FRSTOR, (PBYTE)&Context->FloatSave);
// Ignore: Context->FloatSave.Cr0NpxState
}
// if ((ContextFlags & CONTEXT_DEBUG_WX86) == CONTEXT_DEBUG_WX86)
// {
// }
}
except (EXCEPTION_EXECUTE_HANDLER)
{
NtStatus = GetExceptionCode();
}
return NtStatus;
}
NTSTATUS
CpupSetContextRecord(
PCPUCONTEXT cpu,
PCONTEXT_WX86 Context
)
/*++
Routine Description:
This routine sets the passed context record for the specified CPU.
Arguments:
cpu - CPU context structure
Context - Context record to set
Return Value:
NTSTATUS.
--*/
{
return SetContextRecord(cpu, Context);
}
NTSTATUS
MsCpuSetContext(
PCONTEXT_WX86 Context
)
/*++
Routine Description:
This routine sets the context record for the currently executing thread.
Arguments:
Context - Context record to fill
Return Value:
NTSTATUS.
--*/
{
DECLARE_CPU;
return CpupSetContextRecord(cpu, Context);
}
NTSTATUS
MsCpuSetContextThread(
IN HANDLE ProcessHandle,
IN PTEB Teb,
IN OUT PCONTEXT_WX86 Context)
/*++
Routine Description:
This routine sets the context record of any thread. This is a generic routine.
When entered, if the target thread isn't the currently executing thread, then it should be
guaranteed that the target thread is suspended at a proper CPU state.
Arguments:
ProcessHandle - Open handle to the process that the thread runs in
Teb - Pointer to the target's thread TEB
Context - Context record to set
Return Value:
NTSTATUS.
--*/
{
NTSTATUS NtStatus = STATUS_SUCCESS;
PCPUCONTEXT CpuRemoteContext;
CPUCONTEXT CpuContext;
NtStatus = CpupReadBuffer(ProcessHandle,
((PCHAR)Teb + FIELD_OFFSET(TEB, TlsSlots[WOW64_TLS_CPURESERVED])),
&CpuRemoteContext,
sizeof(CpuRemoteContext));
if (NT_SUCCESS(NtStatus))
{
NtStatus = CpupReadBuffer(ProcessHandle,
CpuRemoteContext,
&CpuContext,
FIELD_OFFSET(CPUCONTEXT, FpData));
if (NT_SUCCESS(NtStatus))
{
NtStatus = CpupSetContextRecord(&CpuContext, Context);
if (NT_SUCCESS(NtStatus))
{
NtStatus = CpupWriteBuffer(ProcessHandle,
CpuRemoteContext,
&CpuContext,
FIELD_OFFSET(CPUCONTEXT, FpData));
if (!NT_SUCCESS(NtStatus))
{
LOGPRINT((ERRORLOG, "MsCpuSetContextThread: Couldn't write CPU context %lx -%lx\n",
CpuRemoteContext, NtStatus));
}
}
}
}
else
{
LOGPRINT((ERRORLOG, "MsCpuSetContextThread: Couldn't read CPU context address-%lx\n",
NtStatus));
}
return NtStatus;
}