From 1513dd235bfe3a944c621e09e98a94bfbb0ab5a3 Mon Sep 17 00:00:00 2001 From: gibbed Date: Fri, 4 Dec 2020 14:57:48 -0600 Subject: [PATCH] [Kernel] Code reentrance for guest fibers. [Kernel] Code reentrance using exceptions for guest fibers. --- src/xenia/cpu/processor.cc | 1 - .../kernel/xboxkrnl/xboxkrnl_threading.cc | 26 +++--- src/xenia/kernel/xthread.cc | 70 ++++++++++++---- src/xenia/kernel/xthread.h | 81 ++++++++++++++----- 4 files changed, 132 insertions(+), 46 deletions(-) diff --git a/src/xenia/cpu/processor.cc b/src/xenia/cpu/processor.cc index 7a787873d..95b016d17 100644 --- a/src/xenia/cpu/processor.cc +++ b/src/xenia/cpu/processor.cc @@ -358,7 +358,6 @@ bool Processor::ExecuteRaw(ThreadState* thread_state, uint32_t address) { return false; } - auto context = thread_state->context(); return function->Call(thread_state, 0xBCBCBCBC); } diff --git a/src/xenia/kernel/xboxkrnl/xboxkrnl_threading.cc b/src/xenia/kernel/xboxkrnl/xboxkrnl_threading.cc index bf399937e..bbe78ec87 100644 --- a/src/xenia/kernel/xboxkrnl/xboxkrnl_threading.cc +++ b/src/xenia/kernel/xboxkrnl/xboxkrnl_threading.cc @@ -205,22 +205,30 @@ dword_result_t NtSuspendThread(dword_t handle, lpdword_t suspend_count_ptr) { } DECLARE_XBOXKRNL_EXPORT1(NtSuspendThread, kThreading, kImplemented); -void KeSetCurrentStackPointers(lpvoid_t stack_ptr, - pointer_t cur_thread, +void KeSetCurrentStackPointers(lpvoid_t stack_ptr, pointer_t thread, lpvoid_t stack_alloc_base, lpvoid_t stack_base, lpvoid_t stack_limit) { - auto thread = XThread::GetCurrentThread(); - auto context = thread->thread_state()->context(); - context->r[1] = stack_ptr.guest_address(); + auto current_thread = XThread::GetCurrentThread(); + auto context = current_thread->thread_state()->context(); + auto pcr = kernel_memory()->TranslateVirtual( + static_cast(context->r[13])); - auto pcr = - kernel_memory()->TranslateVirtual((uint32_t)context->r[13]); + thread->stack_alloc_base = stack_alloc_base.value(); + thread->stack_base = stack_base.value(); + thread->stack_limit = stack_limit.value(); pcr->stack_base_ptr = stack_base.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(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, lpdword_t previous_affinity_ptr) { diff --git a/src/xenia/kernel/xthread.cc b/src/xenia/kernel/xthread.cc index baa014b01..46f1ef961 100644 --- a/src/xenia/kernel/xthread.cc +++ b/src/xenia/kernel/xthread.cc @@ -498,6 +498,16 @@ X_STATUS XThread::Terminate(int exit_code) { 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() { XELOGKERNEL("XThread::Execute thid {} (handle={:08X}, '{}', native={:08X})", thread_id_, handle(), thread_name_, thread_->system_id()); @@ -510,31 +520,61 @@ void XThread::Execute() { // have time to initialize shared structures AFTER CreateThread (RR). xe::threading::Sleep(std::chrono::milliseconds(10)); - int exit_code = 0; - // Dispatch any APCs that were queued before the thread was created first. DeliverAPCs(); + uint32_t address; + std::vector args; + bool want_exit_code; + int exit_code = 0; + // If a XapiThreadStartup value is present, we use that as a trampoline. // Otherwise, we are a raw thread. if (creation_params_.xapi_thread_startup) { - uint64_t args[] = {creation_params_.start_address, - creation_params_.start_context}; - kernel_state()->processor()->Execute(thread_state_, - creation_params_.xapi_thread_startup, - args, xe::countof(args)); + address = creation_params_.xapi_thread_startup; + args.push_back(creation_params_.start_address); + args.push_back(creation_params_.start_context); + want_exit_code = false; } else { // Run user code. - uint64_t args[] = {creation_params_.start_context}; - exit_code = static_cast(kernel_state()->processor()->Execute( - thread_state_, creation_params_.start_address, args, - 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. + address = creation_params_.start_address; + args.push_back(creation_params_.start_context); + want_exit_code = true; } - Exit(exit_code); + uint32_t next_address; + try { + exit_code = static_cast(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(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() { diff --git a/src/xenia/kernel/xthread.h b/src/xenia/kernel/xthread.h index ec0fe41ef..78a6591a1 100644 --- a/src/xenia/kernel/xthread.h +++ b/src/xenia/kernel/xthread.h @@ -70,35 +70,72 @@ struct XAPC { // Processor Control Region struct X_KPCR { xe::be tls_ptr; // 0x0 - char unk_04[0x2C]; // 0x4 + uint8_t unk_04[0x2C]; // 0x4 xe::be pcr_ptr; // 0x30 - char unk_34[0x3C]; // 0x34 + uint8_t unk_34[0x3C]; // 0x34 xe::be stack_base_ptr; // 0x70 Stack base address (high addr) xe::be stack_end_ptr; // 0x74 Stack end (low addr) - char unk_78[0x88]; // 0x78 + uint8_t unk_78[0x88]; // 0x78 xe::be current_thread; // 0x100 - char unk_104[0x8]; // 0x104 - xe::be current_cpu; // 0x10C - char unk_10D[0x43]; // 0x10D + uint8_t unk_104[0x8]; // 0x104 + uint8_t current_cpu; // 0x10C + uint8_t unk_10D[0x43]; // 0x10D xe::be dpc_active; // 0x150 }; struct X_KTHREAD { - X_DISPATCH_HEADER header; // 0x0 - char unk_10[0xAC]; // 0x10 - uint8_t suspend_count; // 0xBC - uint8_t unk_BD; // 0xBD - uint8_t unk_BE; // 0xBE - uint8_t current_cpu; // 0xBF - char unk_C0[0x70]; // 0xC0 - xe::be create_time; // 0x130 - xe::be exit_time; // 0x138 - xe::be exit_status; // 0x140 - char unk_144[0x8]; // 0x144 - xe::be thread_id; // 0x14C - char unk_150[0x10]; // 0x150 - xe::be last_error; // 0x160 - char unk_164[0x94C]; // 0x164 + X_DISPATCH_HEADER header; // 0x0 + xe::be unk_10; // 0x10 + xe::be unk_14; // 0x14 + uint8_t unk_18[0x28]; // 0x10 + xe::be unk_40; // 0x40 + xe::be unk_44; // 0x44 + xe::be unk_48; // 0x48 + xe::be unk_4C; // 0x4C + uint8_t unk_50[0x4]; // 0x50 + xe::be unk_54; // 0x54 + xe::be unk_56; // 0x56 + uint8_t unk_58[0x4]; // 0x58 + xe::be stack_base; // 0x5C + xe::be stack_limit; // 0x60 + uint8_t unk_64[0x4]; // 0x64 + xe::be tls_address; // 0x68 + uint8_t unk_6C; // 0x6C + uint8_t unk_6D[0x7]; // 0x6D + xe::be unk_74; // 0x74 + xe::be unk_78; // 0x78 + xe::be unk_7C; // 0x7C + xe::be unk_80; // 0x80 + xe::be unk_84; // 0x84 + uint8_t unk_88[0x3]; // 0x88 + uint8_t unk_8B; // 0x8B + uint8_t unk_8C[0x10]; // 0x8C + xe::be 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 stack_alloc_base; // 0xD0 + uint8_t unk_D4[0x5C]; // 0xD4 + xe::be create_time; // 0x130 + xe::be exit_time; // 0x138 + xe::be exit_status; // 0x140 + xe::be unk_144; // 0x144 + xe::be unk_148; // 0x148 + xe::be thread_id; // 0x14C + xe::be start_address; // 0x150 + xe::be unk_154; // 0x154 + xe::be unk_158; // 0x158 + uint8_t unk_15C[0x4]; // 0x15C + xe::be last_error; // 0x160 + xe::be fiber_ptr; // 0x164 + uint8_t unk_168[0x4]; // 0x168 + xe::be creation_flags; // 0x16C + uint8_t unk_170[0xC]; // 0x170 + xe::be unk_17C; // 0x17C + uint8_t unk_180[0x930]; // 0x180 // 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 Reenter(uint32_t address); + static void EnterCriticalRegion(); static void LeaveCriticalRegion(); uint32_t RaiseIrql(uint32_t new_irql);