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.
 
 
 
 
 
 

943 lines
25 KiB

/*++
Copyright (c) 1995-1998 Microsoft Corporation
Module Name:
suspend.c
Abstract:
This module implements CpuSuspendThread, CpuGetContext and CpuSetContext for
AMD64.
Author:
12-Dec-2001 Samer Arafeh (samera)
Revision History:
--*/
#define _WOW64CPUAPI_
#ifdef _X86_
#include "amd6432.h"
#else
#include <nt.h>
#include <ntrtl.h>
#include <nturtl.h>
#include <ntos.h>
#include "wow64.h"
#include "wow64cpu.h"
#include "amd64cpu.h"
#endif
#include "cpup.h"
#include <stdio.h>
#include <stdarg.h>
ASSERTNAME;
ULONG_PTR ia32ShowContext = 0;
VOID
CpupDebugPrint(
IN ULONG_PTR Flags,
IN PCHAR Format,
...)
{
va_list ArgList;
int BytesWritten;
CHAR Buffer[ 512 ];
if ((ia32ShowContext & Flags) || (Flags == ERRORLOG))
{
va_start(ArgList, Format);
BytesWritten = _vsnprintf(Buffer,
sizeof(Buffer) - 1,
Format,
ArgList);
if (BytesWritten > 0)
{
DbgPrint(Buffer);
}
va_end(ArgList);
}
return;
}
VOID
CpupPrintContext(
IN PCHAR str,
IN PCPUCONTEXT cpu
)
/*++
Routine Description:
Print out the ia32 context based on the passed in cpu context
Arguments:
str - String to print out as a header
cpu - Pointer to the per-thread wow64 ia32 context.
Return Value:
none
--*/
{
DbgPrint(str);
DbgPrint("Context addr(0x%p): EIP=0x%08x\n", &(cpu->Context), cpu->Context.Eip);
DbgPrint("Context EAX=0x%08x, EBX=0x%08x, ECX=0x%08x, EDX=0x%08x\n",
cpu->Context.Eax,
cpu->Context.Ebx,
cpu->Context.Ecx,
cpu->Context.Edx);
DbgPrint("Context ESP=0x%08x, EBP=0x%08x, ESI=0x%08x, EDI=0x%08x\n",
cpu->Context.Esp,
cpu->Context.Ebp,
cpu->Context.Esi,
cpu->Context.Edi);
try {
//
// The stack may not yet be fully formed, so don't
// let a missing stack cause the process to abort
//
DbgPrint("Context stack=0x%08x 0x%08x 0x%08x 0x%08x\n",
*((PULONG) cpu->Context.Esp),
*(((PULONG) cpu->Context.Esp) + 1),
*(((PULONG) cpu->Context.Esp) + 2),
*(((PULONG) cpu->Context.Esp) + 3));
} except ((GetExceptionCode() == STATUS_ACCESS_VIOLATION)?1:0) {
//
// Got an access violation, so don't print any of the stack
//
DbgPrint("Context stack: Can't get stack contents\n");
}
DbgPrint("Context EFLAGS=0x%08x\n", cpu->Context.EFlags);
}
WOW64CPUAPI
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.
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.
--*/
{
return STATUS_SUCCESS;
}
NTSTATUS CpupReadBuffer(
IN HANDLE ProcessHandle,
IN PVOID Source,
OUT PVOID Destination,
IN ULONG Size)
/*++
Routine Description:
This routine setup the arguments for the remoted SuspendThread call.
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;
if (ProcessHandle == NtCurrentProcess ()) {
try {
RtlCopyMemory (Destination,
Source,
Size);
} except (EXCEPTION_EXECUTE_HANDLER) {
NtStatus = GetExceptionCode ();
}
} else {
NtStatus = NtReadVirtualMemory (ProcessHandle,
Source,
Destination,
Size,
NULL);
}
return NtStatus;
}
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.
--*/
{
NTSTATUS NtStatus = STATUS_SUCCESS;
if (ProcessHandle == NtCurrentProcess ()) {
try {
RtlCopyMemory (Target,
Source,
Size);
} except (EXCEPTION_EXECUTE_HANDLER) {
NtStatus = GetExceptionCode ();
}
} else {
NtStatus = NtWriteVirtualMemory (ProcessHandle,
Target,
Source,
Size,
NULL);
}
return NtStatus;
}
NTSTATUS
GetContextRecord(
IN PCPUCONTEXT cpu,
IN PCONTEXT ContextAmd64 OPTIONAL,
IN OUT PCONTEXT32 Context
)
/*++
Routine Description:
Retrevies the context record of the specified CPU. This routine updates
only the registers that are saved on transition to 64-bit mode, and SHOULD
be kept in sync with the thunk-transition code.
Arguments:
cpu - CPU to retreive the context record for.
ContextAmd64 - Full native context.
Context - IN/OUT pointer to CONTEXT32 to fill in. Context->ContextFlags
should be used to determine how much of the context to copy.
Return Value:
NTSTATUS
--*/
{
NTSTATUS NtStatus = STATUS_SUCCESS;
ULONG ContextFlags;
try {
ContextFlags = Context->ContextFlags;
//
// Get the initial 32-bit context.
//
//
// Caller will fillup Context with the native context and pass ContextAmd64 as NULL.
//
if (ARGUMENT_PRESENT (ContextAmd64)) {
Wow64CtxFromAmd64 (ContextFlags,
ContextAmd64,
Context);
//
// If the thread is running 32-bit code at the time of retreiving the context,
// the direct conversion is enough.
//
if (ContextAmd64->SegCs == (KGDT64_R3_CMCODE | RPL_MASK)) {
return NtStatus;
}
}
if (ContextFlags & CONTEXT_AMD64) {
LOGPRINT((ERRORLOG, "CpuGetContext: Request for amd64 context (0x%x) being FAILED\n", ContextFlags));
ASSERT((ContextFlags & CONTEXT_AMD64) == 0);
}
if ((ContextFlags & CONTEXT32_CONTROL) == CONTEXT32_CONTROL) {
//
// control registers
//
Context->Ebp = cpu->Context.Ebp;
Context->Eip = cpu->Context.Eip;
Context->SegCs = (KGDT64_R3_CMCODE | RPL_MASK);
Context->EFlags = SANITIZE_X86EFLAGS (cpu->Context.EFlags);
Context->Esp = cpu->Context.Esp;
Context->SegSs = (KGDT64_R3_DATA | RPL_MASK);
}
if ((ContextFlags & CONTEXT32_INTEGER) == CONTEXT32_INTEGER) {
//
// i386 integer registers are:
// edi, esi, ebx, edx, ecx, eax
//
Context->Edi = cpu->Context.Edi;
Context->Esi = cpu->Context.Esi;
Context->Ebx = cpu->Context.Ebx;
Context->Edx = cpu->Context.Edx;
Context->Ecx = cpu->Context.Ecx;
Context->Eax = cpu->Context.Eax;
}
if ((ContextFlags & CONTEXT32_SEGMENTS) == CONTEXT32_SEGMENTS) {
//
// i386 segment registers are:
// ds, es, fs, gs
// And since they are a constant, force them to be the right values
//
Context->SegDs = (KGDT64_R3_DATA | RPL_MASK);
Context->SegEs = (KGDT64_R3_DATA | RPL_MASK);
Context->SegFs = (KGDT64_R3_CMTEB | RPL_MASK);
Context->SegGs = (KGDT64_R3_DATA | RPL_MASK);
}
if ((ContextFlags & CONTEXT32_FLOATING_POINT) == CONTEXT32_FLOATING_POINT) {
//
// floating point (legacy) registers (ST0-ST7)
//
//
// This must have already been done.
//
}
if ((ContextFlags & CONTEXT32_DEBUG_REGISTERS) == CONTEXT32_DEBUG_REGISTERS) {
//
// Debug registers (Dr0-Dr7)
//
//
// This must have already been done
//
}
if ((ContextFlags & CONTEXT32_EXTENDED_REGISTERS) == CONTEXT32_EXTENDED_REGISTERS) {
//
// extended floating point registers (XMM0-XMM7)
//
}
} except (EXCEPTION_EXECUTE_HANDLER) {
NtStatus = GetExceptionCode ();
}
if (ia32ShowContext & LOG_CONTEXT_GETSET) {
CpupPrintContext ("Getting ia32 context: ", cpu);
}
return NtStatus;
}
NTSTATUS
CpupGetContext(
IN PCONTEXT ContextAmd64,
IN OUT PCONTEXT32 Context
)
/*++
Routine Description:
This routine extracts the context record for the currently executing thread.
Arguments:
ContextAmd64 - Full native context
Context - Context record to fill
Return Value:
NTSTATUS.
--*/
{
DECLARE_CPU;
return GetContextRecord (cpu, ContextAmd64, Context);
}
NTSTATUS
CpupGetContextThread(
IN HANDLE ThreadHandle,
IN HANDLE ProcessHandle,
IN PTEB Teb,
IN OUT PCONTEXT32 Context)
/*++
Routine Description:
This routine extract the context record of any thread. This is a generic routine.
When entered, if the target thread isn't the current thread, then it should be
guaranteed that the target thread is suspended at a proper CPU state.
Arguments:
ThreadHandle - Target thread handle to retreive the context for
ProcessHandle - Open handle to the process that the thread runs in
Teb - Pointer to the target's thread TEB
Context - Context record to fill
Return Value:
NTSTATUS.
--*/
{
NTSTATUS NtStatus = STATUS_SUCCESS;
CONTEXT ContextEM;
PCPUCONTEXT CpuRemoteContext;
CPUCONTEXT CpuContext;
//
// Get the whole native context
//
ContextEM.ContextFlags = (CONTEXT_FULL |
CONTEXT_DEBUG_REGISTERS |
CONTEXT_SEGMENTS);
NtStatus = NtGetContextThread (ThreadHandle,
&ContextEM);
if (!NT_SUCCESS(NtStatus)) {
LOGPRINT((ERRORLOG, "CpupGetContextThread: NtGetContextThread (%lx) failed - %lx\n",
ThreadHandle, NtStatus));
return NtStatus;
}
//
// If we are running 64-bit code, then get the context off the 64-bit
// thread stack, which was spilled by the transition code...
//
if (ContextEM.SegCs != (KGDT64_R3_CMCODE | RPL_MASK)) {
LOGPRINT((TRACELOG, "Getting context while thread is executing 64-bit instructions\n"));
NtStatus = CpupReadBuffer( ProcessHandle,
((PCHAR)Teb + FIELD_OFFSET(TEB, TlsSlots[WOW64_TLS_CPURESERVED])),
&CpuRemoteContext,
sizeof(CpuRemoteContext));
if (NT_SUCCESS(NtStatus)) {
NtStatus = CpupReadBuffer (ProcessHandle,
CpuRemoteContext,
&CpuContext,
sizeof(CpuContext));
if (!NT_SUCCESS(NtStatus)) {
LOGPRINT((ERRORLOG, "CpupGetContextThread: Couldn't read CPU context %lx - %lx\n",
CpuRemoteContext, NtStatus));
}
} else {
LOGPRINT((ERRORLOG, "CpupGetContextThread: Couldn't read CPU context address - %lx\n",
NtStatus));
}
}
//
// Get the actual context context for the caller
//
if (NT_SUCCESS (NtStatus)) {
NtStatus = GetContextRecord (&CpuContext, &ContextEM, Context);
}
return NtStatus;
}
WOW64CPUAPI
NTSTATUS
CpuGetContext(
IN HANDLE ThreadHandle,
IN HANDLE ProcessHandle,
IN PTEB Teb,
OUT PCONTEXT32 Context)
/*++
Routine Description:
Extracts the cpu context of the specified thread.
When entered, it is guaranteed that the target thread is suspended at
a proper CPU state.
Arguments:
ThreadHandle - Target thread handle to retreive the context for
ProcessHandle - Open handle to the process that the thread runs in
Teb - Pointer to the target's thread TEB
Context - Context record to fill
Return Value:
NTSTATUS.
--*/
{
return CpupGetContextThread(ThreadHandle,
ProcessHandle,
Teb,
Context);
}
NTSTATUS
SetContextRecord(
IN OUT PCPUCONTEXT cpu,
IN OUT PCONTEXT ContextAmd64 OPTIONAL,
IN PCONTEXT32 Context,
OUT PBOOLEAN UpdateNativeContext
)
/*++
Routine Description:
Update the CPU's register set for the specified CPU. This routine updates
only the registers that are saved on transition to 64-bit mode, and SHOULD
be kept in sync with the thunk-transition code.
Arguments:
cpu - CPU to update its registers
Context - Pointer to CONTEXT32 to use. Context->ContextFlags
should be used to determine how much of the context to update.
ContextAmd64 - Full native context, and will hold the updated 32-bit context.
UpdateNativeContext - Updated on return to indicate if a NtSetContextThread call is required.
Return Value:
NTSTATUS
--*/
{
NTSTATUS NtStatus = STATUS_SUCCESS;
ULONG ContextFlags;
ULONG NewContextFlags = 0;
BOOLEAN CmMode;
try {
*UpdateNativeContext = FALSE;
ContextFlags = Context->ContextFlags;
if (ContextFlags & CONTEXT_AMD64) {
LOGPRINT((ERRORLOG, "CpuSetContext: Request with amd64 context (0x%x) FAILED\n", ContextFlags));
ASSERT((ContextFlags & CONTEXT_AMD64) == 0);
}
//
// Caller might pass NativeContext == NULL if they already know what to do.
//
if (ARGUMENT_PRESENT (ContextAmd64)) {
CmMode = (ContextAmd64->SegCs == (KGDT64_R3_CMCODE | RPL_MASK));
Wow64CtxToAmd64 (ContextFlags,
Context,
ContextAmd64);
//
// If we are running 32-bit code
//
if (CmMode == TRUE) {
*UpdateNativeContext = TRUE;
return NtStatus;
}
NewContextFlags = 0;
}
if ((ContextFlags & CONTEXT32_CONTROL) == CONTEXT32_CONTROL) {
//
// Control registers
//
cpu->Context.Ebp = Context->Ebp;
cpu->Context.Eip = Context->Eip;
cpu->Context.SegCs = (KGDT64_R3_CMCODE | RPL_MASK);
cpu->Context.EFlags = SANITIZE_X86EFLAGS (Context->EFlags);
cpu->Context.Esp = Context->Esp;
cpu->Context.SegSs = Context->SegSs;
}
if ((ContextFlags & CONTEXT32_INTEGER) == CONTEXT32_INTEGER) {
//
// Integer registers
//
cpu->Context.Edi = Context->Edi;
cpu->Context.Esi = Context->Esi;
cpu->Context.Ebx = Context->Ebx;
cpu->Context.Edx = Context->Edx;
cpu->Context.Ecx = Context->Ecx;
cpu->Context.Eax = Context->Eax;
}
if ((ContextFlags & CONTEXT32_SEGMENTS) == CONTEXT32_SEGMENTS) {
//
// Segment registers
//
//
// shouldn't be touched
//
}
if ((ContextFlags & CONTEXT32_FLOATING_POINT) == CONTEXT32_FLOATING_POINT) {
//
// floating point registers
//
NewContextFlags |= CONTEXT_FLOATING_POINT;
}
if ((ContextFlags & CONTEXT32_DEBUG_REGISTERS) == CONTEXT32_DEBUG_REGISTERS) {
//
// debug registers (Dr0-Dr7)
//
NewContextFlags |= CONTEXT_DEBUG_REGISTERS;
}
if ((ContextFlags & CONTEXT32_EXTENDED_REGISTERS) == CONTEXT32_EXTENDED_REGISTERS) {
//
// extended floating point registers (ST0-ST7)
//
NewContextFlags |= CONTEXT_FLOATING_POINT;
//
// Save the extended registers so that the trap frame may restore them.
// The system will ALWAYS scrub XMM0-XMM5 on return from system calls.
//
RtlCopyMemory (cpu->Context.ExtendedRegisters,
Context->ExtendedRegisters,
sizeof (Context->ExtendedRegisters));
}
//
// Whatever they passed in before, it's an X86 context now...
//
cpu->Context.ContextFlags = ContextFlags;
} except (EXCEPTION_EXECUTE_HANDLER) {
NtStatus = GetExceptionCode();
}
if (ia32ShowContext & LOG_CONTEXT_GETSET) {
CpupPrintContext("Setting ia32 context: ", cpu);
}
if (NewContextFlags != 0) {
if (ARGUMENT_PRESENT (ContextAmd64)) {
ContextAmd64->ContextFlags = NewContextFlags;
*UpdateNativeContext = TRUE;
}
}
return NtStatus;
}
NTSTATUS
CpupSetContextThread(
IN HANDLE ThreadHandle,
IN HANDLE ProcessHandle,
IN PTEB Teb,
IN OUT PCONTEXT32 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:
ThreadHandle - Target thread handle to retreive the context for
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;
CONTEXT ContextEM;
PCPUCONTEXT CpuRemoteContext;
CPUCONTEXT CpuContext;
BOOLEAN CmMode;
BOOLEAN UpdateNativeContext;
//
// Get the whole native context
//
ContextEM.ContextFlags = (CONTEXT_FULL |
CONTEXT_DEBUG_REGISTERS |
CONTEXT_SEGMENTS);
NtStatus = NtGetContextThread (ThreadHandle,
&ContextEM);
if (!NT_SUCCESS(NtStatus)) {
LOGPRINT((ERRORLOG, "CpupGetContextThread: NtGetContextThread (%lx) failed - %lx\n",
ThreadHandle, NtStatus));
return NtStatus;
}
CmMode = (ContextEM.SegCs == (KGDT64_R3_CMCODE | RPL_MASK));
//
// If we are running 64-bit code, make sure to update the cpu context off the
// 64-bit thread stack...
//
if (CmMode == FALSE) {
LOGPRINT((TRACELOG, "Setting context while thread is executing 64-bit instructions\n"));
NtStatus = CpupReadBuffer (ProcessHandle,
((PCHAR)Teb + FIELD_OFFSET(TEB, TlsSlots[WOW64_TLS_CPURESERVED])),
&CpuRemoteContext,
sizeof(CpuRemoteContext));
if (NT_SUCCESS(NtStatus)) {
NtStatus = CpupReadBuffer (ProcessHandle,
CpuRemoteContext,
&CpuContext,
sizeof(CpuContext));
} else {
LOGPRINT((ERRORLOG, "CpupSetContextThread: Couldn't read CPU context address - %lx\n",
NtStatus));
}
}
//
// We are ready to set the context now
//
if (NT_SUCCESS (NtStatus)) {
NtStatus = SetContextRecord (&CpuContext,
&ContextEM,
Context,
&UpdateNativeContext);
if (NT_SUCCESS (NtStatus)) {
if (CmMode == FALSE) {
//
// If the call is coming thru Wow64, then restore the volatile XMMI and integer registers on the next
// tranisition to compatibility mode
//
if (ThreadHandle == NtCurrentThread ()) {
if (((Context->ContextFlags & CONTEXT32_EXTENDED_REGISTERS) == CONTEXT32_EXTENDED_REGISTERS) ||
((Context->ContextFlags & CONTEXT32_INTEGER) == CONTEXT32_INTEGER)) {
CpuContext.TrapFrameFlags = TRAP_FRAME_RESTORE_VOLATILE;
}
}
NtStatus = CpupWriteBuffer(ProcessHandle,
CpuRemoteContext,
&CpuContext,
sizeof(CpuContext));
if (!NT_SUCCESS (NtStatus)) {
LOGPRINT((ERRORLOG, "CpupSetContextThread: Couldn't write remote context %lx - %lx\n",
CpuRemoteContext, NtStatus));
}
}
//
// Set the context ultimately. This shouldn't change except the FP and debug
// state if the thread is executing in 64-bit (long) mode.
//
if ((NT_SUCCESS (NtStatus)) &&
(UpdateNativeContext == TRUE)) {
NtStatus = NtSetContextThread (ThreadHandle, &ContextEM);
}
} else {
LOGPRINT((ERRORLOG, "CpupSetContextThread: Couldn't update native context %lx - %lx\n",
&ContextEM, NtStatus));
}
}
return NtStatus;
}
WOW64CPUAPI
NTSTATUS
CpuSetContext(
IN HANDLE ThreadHandle,
IN HANDLE ProcessHandle,
IN PTEB Teb,
PCONTEXT32 Context)
/*++
Routine Description:
Sets the cpu context for the specified thread.
When entered, if the target thread isn't the currently executing thread, then it is
guaranteed that the target thread is suspended at a proper CPU state.
Arguments:
ThreadHandle - Target thread handle to retreive the context for
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.
--*/
{
return CpupSetContextThread(ThreadHandle,
ProcessHandle,
Teb,
Context);
}