summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorgibbed <rick@gibbed.us>2020-12-04 14:57:48 -0600
committerRick Gibbed <rick@gibbed.us>2020-12-05 14:17:33 -0600
commit1513dd235bfe3a944c621e09e98a94bfbb0ab5a3 (patch)
tree3018ac6530e47584ec7ac790041032f757322b32
parentd420215de1a738c876d49c9b951c7a45ed5884cf (diff)
[Kernel] Code reentrance for guest fibers.
[Kernel] Code reentrance using exceptions for guest fibers.
-rw-r--r--src/xenia/cpu/processor.cc1
-rw-r--r--src/xenia/kernel/xboxkrnl/xboxkrnl_threading.cc28
-rw-r--r--src/xenia/kernel/xthread.cc68
-rw-r--r--src/xenia/kernel/xthread.h81
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);