[Kernel] Code reentrance for guest fibers.
[Kernel] Code reentrance using exceptions for guest fibers.
This commit is contained in:
parent
d420215de1
commit
1513dd235b
|
@ -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);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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) {
|
||||||
|
|
|
@ -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() {
|
||||||
|
|
|
@ -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
|
||||||
|
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 suspend_count; // 0xBC
|
||||||
uint8_t unk_BD; // 0xBD
|
uint8_t unk_BD; // 0xBD
|
||||||
uint8_t unk_BE; // 0xBE
|
uint8_t unk_BE; // 0xBE
|
||||||
uint8_t current_cpu; // 0xBF
|
uint8_t current_cpu; // 0xBF
|
||||||
char unk_C0[0x70]; // 0xC0
|
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> create_time; // 0x130
|
||||||
xe::be<uint64_t> exit_time; // 0x138
|
xe::be<uint64_t> exit_time; // 0x138
|
||||||
xe::be<uint32_t> exit_status; // 0x140
|
xe::be<uint32_t> exit_status; // 0x140
|
||||||
char unk_144[0x8]; // 0x144
|
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> thread_id; // 0x14C
|
||||||
char unk_150[0x10]; // 0x150
|
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> last_error; // 0x160
|
||||||
char unk_164[0x94C]; // 0x164
|
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);
|
||||||
|
|
Loading…
Reference in New Issue