diff options
| author | gibbed <rick@gibbed.us> | 2020-12-04 14:57:48 -0600 |
|---|---|---|
| committer | Rick Gibbed <rick@gibbed.us> | 2020-12-05 14:17:33 -0600 |
| commit | 1513dd235bfe3a944c621e09e98a94bfbb0ab5a3 (patch) | |
| tree | 3018ac6530e47584ec7ac790041032f757322b32 | |
| parent | d420215de1a738c876d49c9b951c7a45ed5884cf (diff) | |
[Kernel] Code reentrance for guest fibers.
[Kernel] Code reentrance using exceptions for guest fibers.
| -rw-r--r-- | src/xenia/cpu/processor.cc | 1 | ||||
| -rw-r--r-- | src/xenia/kernel/xboxkrnl/xboxkrnl_threading.cc | 28 | ||||
| -rw-r--r-- | src/xenia/kernel/xthread.cc | 68 | ||||
| -rw-r--r-- | 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<X_KTHREAD> cur_thread, +void KeSetCurrentStackPointers(lpvoid_t stack_ptr, pointer_t<X_KTHREAD> 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 pcr = - kernel_memory()->TranslateVirtual<X_KPCR*>((uint32_t)context->r[13]); + auto current_thread = XThread::GetCurrentThread(); + auto context = current_thread->thread_state()->context(); + auto pcr = kernel_memory()->TranslateVirtual<X_KPCR*>( + static_cast<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<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, 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<uint64_t> 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}; + address = creation_params_.start_address; + args.push_back(creation_params_.start_context); + want_exit_code = true; + } + + uint32_t next_address; + try { exit_code = static_cast<int>(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. + thread_state_, address, args.data(), args.size())); + next_address = 0; + } catch (const reenter_exception& ree) { + next_address = ree.address(); } - Exit(exit_code); + // 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() { 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<uint32_t> tls_ptr; // 0x0 - char unk_04[0x2C]; // 0x4 + uint8_t unk_04[0x2C]; // 0x4 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_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 - char unk_104[0x8]; // 0x104 - xe::be<uint8_t> 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<uint32_t> 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<uint64_t> create_time; // 0x130 - xe::be<uint64_t> exit_time; // 0x138 - xe::be<uint32_t> exit_status; // 0x140 - char unk_144[0x8]; // 0x144 - xe::be<uint32_t> thread_id; // 0x14C - char unk_150[0x10]; // 0x150 - xe::be<uint32_t> last_error; // 0x160 - char unk_164[0x94C]; // 0x164 + X_DISPATCH_HEADER header; // 0x0 + xe::be<uint32_t> unk_10; // 0x10 + xe::be<uint32_t> unk_14; // 0x14 + uint8_t unk_18[0x28]; // 0x10 + xe::be<uint32_t> unk_40; // 0x40 + xe::be<uint32_t> unk_44; // 0x44 + xe::be<uint32_t> unk_48; // 0x48 + xe::be<uint32_t> unk_4C; // 0x4C + uint8_t unk_50[0x4]; // 0x50 + xe::be<uint16_t> unk_54; // 0x54 + xe::be<uint16_t> unk_56; // 0x56 + uint8_t unk_58[0x4]; // 0x58 + xe::be<uint32_t> stack_base; // 0x5C + xe::be<uint32_t> stack_limit; // 0x60 + 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! }; @@ -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); |
