[Kernel] Code reentrance for guest fibers.

[Kernel] Code reentrance using exceptions for guest fibers.
This commit is contained in:
gibbed 2020-12-04 14:57:48 -06:00 committed by Rick Gibbed
parent d420215de1
commit 1513dd235b
4 changed files with 132 additions and 46 deletions

View File

@ -358,7 +358,6 @@ bool Processor::ExecuteRaw(ThreadState* thread_state, uint32_t address) {
return false; return false;
} }
auto context = thread_state->context();
return function->Call(thread_state, 0xBCBCBCBC); return function->Call(thread_state, 0xBCBCBCBC);
} }

View File

@ -205,22 +205,30 @@ dword_result_t NtSuspendThread(dword_t handle, lpdword_t suspend_count_ptr) {
} }
DECLARE_XBOXKRNL_EXPORT1(NtSuspendThread, kThreading, kImplemented); DECLARE_XBOXKRNL_EXPORT1(NtSuspendThread, kThreading, kImplemented);
void KeSetCurrentStackPointers(lpvoid_t stack_ptr, void KeSetCurrentStackPointers(lpvoid_t stack_ptr, pointer_t<X_KTHREAD> thread,
pointer_t<X_KTHREAD> cur_thread,
lpvoid_t stack_alloc_base, lpvoid_t stack_base, lpvoid_t stack_alloc_base, lpvoid_t stack_base,
lpvoid_t stack_limit) { lpvoid_t stack_limit) {
auto thread = XThread::GetCurrentThread(); auto current_thread = XThread::GetCurrentThread();
auto context = thread->thread_state()->context(); auto context = current_thread->thread_state()->context();
context->r[1] = stack_ptr.guest_address(); auto pcr = kernel_memory()->TranslateVirtual<X_KPCR*>(
static_cast<uint32_t>(context->r[13]));
auto pcr = thread->stack_alloc_base = stack_alloc_base.value();
kernel_memory()->TranslateVirtual<X_KPCR*>((uint32_t)context->r[13]); thread->stack_base = stack_base.value();
thread->stack_limit = stack_limit.value();
pcr->stack_base_ptr = stack_base.guest_address(); pcr->stack_base_ptr = stack_base.guest_address();
pcr->stack_end_ptr = stack_limit.guest_address(); pcr->stack_end_ptr = stack_limit.guest_address();
context->r[1] = stack_ptr.guest_address();
// TODO: Do we need to set the stack info on cur_thread? // If a fiber is set, and the thread matches, reenter to avoid issues with
// host stack overflowing.
if (thread->fiber_ptr &&
current_thread->guest_object() == thread.guest_address()) {
current_thread->Reenter(static_cast<uint32_t>(context->lr));
}
} }
DECLARE_XBOXKRNL_EXPORT1(KeSetCurrentStackPointers, kThreading, kImplemented); DECLARE_XBOXKRNL_EXPORT2(KeSetCurrentStackPointers, kThreading, kImplemented,
kHighFrequency);
dword_result_t KeSetAffinityThread(lpvoid_t thread_ptr, dword_t affinity, dword_result_t KeSetAffinityThread(lpvoid_t thread_ptr, dword_t affinity,
lpdword_t previous_affinity_ptr) { lpdword_t previous_affinity_ptr) {

View File

@ -498,6 +498,16 @@ X_STATUS XThread::Terminate(int exit_code) {
return X_STATUS_SUCCESS; return X_STATUS_SUCCESS;
} }
class reenter_exception {
public:
reenter_exception(uint32_t address) : address_(address){};
virtual ~reenter_exception(){};
uint32_t address() const { return address_; }
private:
uint32_t address_;
};
void XThread::Execute() { void XThread::Execute() {
XELOGKERNEL("XThread::Execute thid {} (handle={:08X}, '{}', native={:08X})", XELOGKERNEL("XThread::Execute thid {} (handle={:08X}, '{}', native={:08X})",
thread_id_, handle(), thread_name_, thread_->system_id()); thread_id_, handle(), thread_name_, thread_->system_id());
@ -510,31 +520,61 @@ void XThread::Execute() {
// have time to initialize shared structures AFTER CreateThread (RR). // have time to initialize shared structures AFTER CreateThread (RR).
xe::threading::Sleep(std::chrono::milliseconds(10)); xe::threading::Sleep(std::chrono::milliseconds(10));
int exit_code = 0;
// Dispatch any APCs that were queued before the thread was created first. // Dispatch any APCs that were queued before the thread was created first.
DeliverAPCs(); DeliverAPCs();
uint32_t address;
std::vector<uint64_t> args;
bool want_exit_code;
int exit_code = 0;
// If a XapiThreadStartup value is present, we use that as a trampoline. // If a XapiThreadStartup value is present, we use that as a trampoline.
// Otherwise, we are a raw thread. // Otherwise, we are a raw thread.
if (creation_params_.xapi_thread_startup) { if (creation_params_.xapi_thread_startup) {
uint64_t args[] = {creation_params_.start_address, address = creation_params_.xapi_thread_startup;
creation_params_.start_context}; args.push_back(creation_params_.start_address);
kernel_state()->processor()->Execute(thread_state_, args.push_back(creation_params_.start_context);
creation_params_.xapi_thread_startup, want_exit_code = false;
args, xe::countof(args));
} else { } else {
// Run user code. // Run user code.
uint64_t args[] = {creation_params_.start_context}; address = creation_params_.start_address;
exit_code = static_cast<int>(kernel_state()->processor()->Execute( args.push_back(creation_params_.start_context);
thread_state_, creation_params_.start_address, args, want_exit_code = true;
xe::countof(args)));
// If we got here it means the execute completed without an exit being
// called.
// Treat the return code as an implicit exit code.
} }
Exit(exit_code); uint32_t next_address;
try {
exit_code = static_cast<int>(kernel_state()->processor()->Execute(
thread_state_, address, args.data(), args.size()));
next_address = 0;
} catch (const reenter_exception& ree) {
next_address = ree.address();
}
// See XThread::Reenter comments.
while (next_address != 0) {
try {
kernel_state()->processor()->ExecuteRaw(thread_state_, next_address);
next_address = 0;
if (want_exit_code) {
exit_code = static_cast<int>(thread_state_->context()->r[3]);
}
} catch (const reenter_exception& ree) {
next_address = ree.address();
}
}
// If we got here it means the execute completed without an exit being called.
// Treat the return code as an implicit exit code (if desired).
Exit(!want_exit_code ? 0 : exit_code);
}
void XThread::Reenter(uint32_t address) {
// TODO(gibbed): Maybe use setjmp/longjmp on Windows?
// https://docs.microsoft.com/en-us/cpp/c-runtime-library/reference/longjmp#remarks
// On Windows with /EH, setjmp/longjmp do stack unwinding.
// Is there a better solution than exceptions for stack unwinding?
throw reenter_exception(address);
} }
void XThread::EnterCriticalRegion() { void XThread::EnterCriticalRegion() {

View File

@ -70,35 +70,72 @@ struct XAPC {
// Processor Control Region // Processor Control Region
struct X_KPCR { struct X_KPCR {
xe::be<uint32_t> tls_ptr; // 0x0 xe::be<uint32_t> tls_ptr; // 0x0
char unk_04[0x2C]; // 0x4 uint8_t unk_04[0x2C]; // 0x4
xe::be<uint32_t> pcr_ptr; // 0x30 xe::be<uint32_t> pcr_ptr; // 0x30
char unk_34[0x3C]; // 0x34 uint8_t unk_34[0x3C]; // 0x34
xe::be<uint32_t> stack_base_ptr; // 0x70 Stack base address (high addr) xe::be<uint32_t> stack_base_ptr; // 0x70 Stack base address (high addr)
xe::be<uint32_t> stack_end_ptr; // 0x74 Stack end (low addr) xe::be<uint32_t> stack_end_ptr; // 0x74 Stack end (low addr)
char unk_78[0x88]; // 0x78 uint8_t unk_78[0x88]; // 0x78
xe::be<uint32_t> current_thread; // 0x100 xe::be<uint32_t> current_thread; // 0x100
char unk_104[0x8]; // 0x104 uint8_t unk_104[0x8]; // 0x104
xe::be<uint8_t> current_cpu; // 0x10C uint8_t current_cpu; // 0x10C
char unk_10D[0x43]; // 0x10D uint8_t unk_10D[0x43]; // 0x10D
xe::be<uint32_t> dpc_active; // 0x150 xe::be<uint32_t> dpc_active; // 0x150
}; };
struct X_KTHREAD { struct X_KTHREAD {
X_DISPATCH_HEADER header; // 0x0 X_DISPATCH_HEADER header; // 0x0
char unk_10[0xAC]; // 0x10 xe::be<uint32_t> unk_10; // 0x10
uint8_t suspend_count; // 0xBC xe::be<uint32_t> unk_14; // 0x14
uint8_t unk_BD; // 0xBD uint8_t unk_18[0x28]; // 0x10
uint8_t unk_BE; // 0xBE xe::be<uint32_t> unk_40; // 0x40
uint8_t current_cpu; // 0xBF xe::be<uint32_t> unk_44; // 0x44
char unk_C0[0x70]; // 0xC0 xe::be<uint32_t> unk_48; // 0x48
xe::be<uint64_t> create_time; // 0x130 xe::be<uint32_t> unk_4C; // 0x4C
xe::be<uint64_t> exit_time; // 0x138 uint8_t unk_50[0x4]; // 0x50
xe::be<uint32_t> exit_status; // 0x140 xe::be<uint16_t> unk_54; // 0x54
char unk_144[0x8]; // 0x144 xe::be<uint16_t> unk_56; // 0x56
xe::be<uint32_t> thread_id; // 0x14C uint8_t unk_58[0x4]; // 0x58
char unk_150[0x10]; // 0x150 xe::be<uint32_t> stack_base; // 0x5C
xe::be<uint32_t> last_error; // 0x160 xe::be<uint32_t> stack_limit; // 0x60
char unk_164[0x94C]; // 0x164 uint8_t unk_64[0x4]; // 0x64
xe::be<uint32_t> tls_address; // 0x68
uint8_t unk_6C; // 0x6C
uint8_t unk_6D[0x7]; // 0x6D
xe::be<uint32_t> unk_74; // 0x74
xe::be<uint32_t> unk_78; // 0x78
xe::be<uint32_t> unk_7C; // 0x7C
xe::be<uint32_t> unk_80; // 0x80
xe::be<uint32_t> unk_84; // 0x84
uint8_t unk_88[0x3]; // 0x88
uint8_t unk_8B; // 0x8B
uint8_t unk_8C[0x10]; // 0x8C
xe::be<uint32_t> unk_9C; // 0x9C
uint8_t unk_A0[0x1C]; // 0xA0
uint8_t suspend_count; // 0xBC
uint8_t unk_BD; // 0xBD
uint8_t unk_BE; // 0xBE
uint8_t current_cpu; // 0xBF
uint8_t unk_C0[0x10]; // 0xC0
xe::be<uint32_t> stack_alloc_base; // 0xD0
uint8_t unk_D4[0x5C]; // 0xD4
xe::be<uint64_t> create_time; // 0x130
xe::be<uint64_t> exit_time; // 0x138
xe::be<uint32_t> exit_status; // 0x140
xe::be<uint32_t> unk_144; // 0x144
xe::be<uint32_t> unk_148; // 0x148
xe::be<uint32_t> thread_id; // 0x14C
xe::be<uint32_t> start_address; // 0x150
xe::be<uint32_t> unk_154; // 0x154
xe::be<uint32_t> unk_158; // 0x158
uint8_t unk_15C[0x4]; // 0x15C
xe::be<uint32_t> last_error; // 0x160
xe::be<uint32_t> fiber_ptr; // 0x164
uint8_t unk_168[0x4]; // 0x168
xe::be<uint32_t> creation_flags; // 0x16C
uint8_t unk_170[0xC]; // 0x170
xe::be<uint32_t> unk_17C; // 0x17C
uint8_t unk_180[0x930]; // 0x180
// This struct is actually quite long... so uh, not filling this out! // This struct is actually quite long... so uh, not filling this out!
}; };
@ -151,6 +188,8 @@ class XThread : public XObject, public cpu::Thread {
virtual void Execute(); virtual void Execute();
virtual void Reenter(uint32_t address);
static void EnterCriticalRegion(); static void EnterCriticalRegion();
static void LeaveCriticalRegion(); static void LeaveCriticalRegion();
uint32_t RaiseIrql(uint32_t new_irql); uint32_t RaiseIrql(uint32_t new_irql);