diff --git a/src/xenia/kernel/kernel_state.cc b/src/xenia/kernel/kernel_state.cc index b5e79d111..bfc6d188b 100644 --- a/src/xenia/kernel/kernel_state.cc +++ b/src/xenia/kernel/kernel_state.cc @@ -1274,7 +1274,7 @@ void KernelState::InitializeProcess(X_KPROCESS* process, uint32_t type, process->unk_19 = unk_19; process->unk_1A = unk_1A; util::XeInitializeListHead(&process->thread_list, thread_list_guest_ptr); - process->unk_0C = 60; + process->quantum = 60; // doubt any guest code uses this ptr, which i think probably has something to // do with the page table process->clrdataa_masked_ptr = 0; @@ -1374,7 +1374,7 @@ void KernelState::InitializeKernelGuestGlobals() { auto idle_process = memory()->TranslateVirtual(GetIdleProcess()); InitializeProcess(idle_process, X_PROCTYPE_IDLE, 0, 0, 0); - idle_process->unk_0C = 0x7F; + idle_process->quantum = 0x7F; auto system_process = memory()->TranslateVirtual(GetSystemProcess()); InitializeProcess(system_process, X_PROCTYPE_SYSTEM, 2, 5, 9); diff --git a/src/xenia/kernel/kernel_state.h b/src/xenia/kernel/kernel_state.h index 9cb57dcc5..375e388e6 100644 --- a/src/xenia/kernel/kernel_state.h +++ b/src/xenia/kernel/kernel_state.h @@ -58,7 +58,8 @@ struct X_KPROCESS { // list of threads in this process, guarded by the spinlock above X_LIST_ENTRY thread_list; - xe::be unk_0C; + // quantum value assigned to each thread of the process + xe::be quantum; // kernel sets this to point to a section of size 0x2F700 called CLRDATAA, // except it clears bit 31 of the pointer. in 17559 the address is 0x801C0000, // so it sets this ptr to 0x1C0000 diff --git a/src/xenia/kernel/xam/xam_task.cc b/src/xenia/kernel/xam/xam_task.cc index e893ae63f..354267f39 100644 --- a/src/xenia/kernel/xam/xam_task.cc +++ b/src/xenia/kernel/xam/xam_task.cc @@ -32,7 +32,7 @@ struct XTASK_MESSAGE { }; struct XAM_TASK_ARGS { - be value1; + be flags; be value2; // i think there might be another value here, it might be padding }; @@ -48,7 +48,7 @@ dword_result_t XamTaskSchedule_entry(lpvoid_t callback, if (optional_ptr) { auto option = ctx->TranslateVirtual(optional_ptr); - auto v1 = option->value1; + auto v1 = option->flags; auto v2 = option->value2; // typically 0? XELOGI("Got xam task args: v1 = {:08X}, v2 = {:08X}", v1.get(), v2.get()); diff --git a/src/xenia/kernel/xboxkrnl/xboxkrnl_threading.cc b/src/xenia/kernel/xboxkrnl/xboxkrnl_threading.cc index 845a85745..03f6a9457 100644 --- a/src/xenia/kernel/xboxkrnl/xboxkrnl_threading.cc +++ b/src/xenia/kernel/xboxkrnl/xboxkrnl_threading.cc @@ -797,14 +797,14 @@ dword_result_t NtCreateMutant_entry( } DECLARE_XBOXKRNL_EXPORT1(NtCreateMutant, kThreading, kImplemented); -dword_result_t NtReleaseMutant_entry(dword_t mutant_handle, dword_t unknown) { +dword_result_t NtReleaseMutant_entry(dword_t mutant_handle, + lpdword_t previous_count) { // This doesn't seem to be supported. // int32_t previous_count_ptr = SHIM_GET_ARG_32(2); // Whatever arg 1 is all games seem to set it to 0, so whether it's // abandon or wait we just say false. Which is good, cause they are // both ignored. - assert_zero(unknown); uint32_t priority_increment = 0; bool abandon = false; bool wait = false; @@ -861,11 +861,11 @@ DECLARE_XBOXKRNL_EXPORT1(NtCreateTimer, kThreading, kImplemented); dword_result_t NtSetTimerEx_entry(dword_t timer_handle, lpqword_t due_time_ptr, lpvoid_t routine_ptr /*PTIMERAPCROUTINE*/, - dword_t unk_one, lpvoid_t routine_arg, + dword_t mode, lpvoid_t routine_arg, dword_t resume, dword_t period_ms, - dword_t unk_zero) { - assert_true(unk_one == 1); - assert_true(unk_zero == 0); + lpdword_t unk_zero) { + assert_true(mode == 1); + assert_true(!unk_zero); uint64_t due_time = *due_time_ptr; diff --git a/src/xenia/kernel/xobject.cc b/src/xenia/kernel/xobject.cc index d7f1d85bd..562a9ef7c 100644 --- a/src/xenia/kernel/xobject.cc +++ b/src/xenia/kernel/xobject.cc @@ -349,7 +349,7 @@ void XObject::SetNativePointer(uint32_t native_ptr, bool uninitialized) { // Memory uninitialized, so don't bother with the check. if (!uninitialized) { - assert_true(!(header->wait_list_blink & 0x1)); + assert_true(!(header->wait_list.blink_ptr & 0x1)); } // Stash pointer in struct. @@ -370,8 +370,8 @@ object_ref XObject::GetNativeObject(KernelState* kernel_state, // we don't have to worry about PPC code poking the struct. Because of that, // we init on first use, store our handle in the struct, and dereference it // each time. - // We identify this by setting wait_list_flink to a magic value. When set, - // wait_list_blink will hold a handle to our object. + // We identify this by setting wait_list.flink_ptr to a magic value. When set, + // wait_list.blink_ptr will hold a handle to our object. if (!already_locked) { global_critical_region::mutex().lock(); } @@ -383,10 +383,10 @@ object_ref XObject::GetNativeObject(KernelState* kernel_state, as_type = header->type; } - if (header->wait_list_flink == kXObjSignature) { + if (header->wait_list.flink_ptr == kXObjSignature) { // Already initialized. // TODO: assert if the type of the object != as_type - uint32_t handle = header->wait_list_blink; + uint32_t handle = header->wait_list.blink_ptr; result = kernel_state->object_table() ->LookupObject(handle, true) .release(); diff --git a/src/xenia/kernel/xobject.h b/src/xenia/kernel/xobject.h index 0717c2bdc..d012ebde7 100644 --- a/src/xenia/kernel/xobject.h +++ b/src/xenia/kernel/xobject.h @@ -57,8 +57,7 @@ typedef struct { }; xe::be signal_state; - xe::be wait_list_flink; - xe::be wait_list_blink; + X_LIST_ENTRY wait_list; } X_DISPATCH_HEADER; static_assert_size(X_DISPATCH_HEADER, 0x10); @@ -245,8 +244,8 @@ class XObject { // Stash native pointer into X_DISPATCH_HEADER static void StashHandle(X_DISPATCH_HEADER* header, uint32_t handle) { - header->wait_list_flink = kXObjSignature; - header->wait_list_blink = handle; + header->wait_list.flink_ptr = kXObjSignature; + header->wait_list.blink_ptr = handle; } static uint32_t TimeoutTicksToMs(int64_t timeout_ticks); diff --git a/src/xenia/kernel/xthread.cc b/src/xenia/kernel/xthread.cc index a3266a6eb..8f113f829 100644 --- a/src/xenia/kernel/xthread.cc +++ b/src/xenia/kernel/xthread.cc @@ -174,12 +174,15 @@ void XThread::InitializeGuestObject() { guest_thread->unk_10 = (thread_guest_ptr + 0x10); guest_thread->unk_14 = (thread_guest_ptr + 0x10); - guest_thread->unk_40 = (thread_guest_ptr + 0x20); - guest_thread->unk_44 = (thread_guest_ptr + 0x20); - guest_thread->unk_48 = (thread_guest_ptr); + guest_thread->wait_timeout_block.wait_list_entry.flink_ptr = + thread_guest_ptr + 0x20; + guest_thread->wait_timeout_block.wait_list_entry.blink_ptr = + thread_guest_ptr + 0x20; + guest_thread->wait_timeout_block.thread = thread_guest_ptr; uint32_t v6 = thread_guest_ptr + 0x18; - *(uint32_t*)&guest_thread->unk_54 = 16777729; - guest_thread->unk_4C = (v6); + guest_thread->wait_timeout_block.wait_result_xstatus = 0x0100; + guest_thread->wait_timeout_block.wait_type = 0x0201; + guest_thread->wait_timeout_block.object = v6; guest_thread->stack_base = (this->stack_base_); guest_thread->stack_limit = (this->stack_limit_); guest_thread->stack_kernel = (this->stack_base_ - 240); @@ -207,14 +210,14 @@ void XThread::InitializeGuestObject() { guest_thread->process = process_info_block_address; guest_thread->stack_alloc_base = this->stack_base_; guest_thread->create_time = Clock::QueryGuestSystemTime(); - guest_thread->unk_144 = thread_guest_ptr + 324; - guest_thread->unk_148 = thread_guest_ptr + 324; + guest_thread->timer_list.flink_ptr = thread_guest_ptr + 324; + guest_thread->timer_list.blink_ptr = thread_guest_ptr + 324; guest_thread->thread_id = this->thread_id_; guest_thread->start_address = this->creation_params_.start_address; - guest_thread->unk_154 = thread_guest_ptr + 340; + guest_thread->unk_154.flink_ptr = thread_guest_ptr + 340; uint32_t v9 = thread_guest_ptr; guest_thread->last_error = 0; - guest_thread->unk_158 = v9 + 340; + guest_thread->unk_154.blink_ptr = v9 + 340; guest_thread->creation_flags = this->creation_params_.creation_flags; guest_thread->unk_17C = 1; diff --git a/src/xenia/kernel/xthread.h b/src/xenia/kernel/xthread.h index d8664e5a2..6a6bd2882 100644 --- a/src/xenia/kernel/xthread.h +++ b/src/xenia/kernel/xthread.h @@ -29,6 +29,39 @@ constexpr fourcc_t kThreadSaveSignature = make_fourcc("THRD"); class XEvent; +enum IRQL_FLAGS : uint8_t { + IRQL_PASSIVE = 0, + IRQL_APC = 1, + IRQL_DISPATCH = 2, + IRQL_DPC = 3, + IRQL_AUDIO = 68, // used a few times in the audio driver + IRQL_CLOCK = 116, // irql used by the clock interrupt + IRQL_HIGHEST = 124 +}; + +enum X_DISPATCHER_FLAGS { + DISPATCHER_MANUAL_RESET_EVENT = 0, + DISPATCHER_AUTO_RESET_EVENT = 1, + DISPATCHER_MUTANT = 2, + DISPATCHER_QUEUE = 4, + DISPATCHER_SEMAPHORE = 5, + DISPATCHER_THREAD = 6, + DISPATCHER_MANUAL_RESET_TIMER = 8, + DISPATCHER_AUTO_RESET_TIMER = 9, +}; + +// https://www.geoffchappell.com/studies/windows/km/ntoskrnl/inc/ntos/ke/kthread_state.htm +enum X_KTHREAD_STATE_FLAGS : uint8_t { + KTHREAD_STATE_INITIALIZED = 0, + KTHREAD_STATE_READY = 1, + KTHREAD_STATE_RUNNING = 2, + KTHREAD_STATE_STANDBY = 3, + KTHREAD_STATE_TERMINATED = 4, + KTHREAD_STATE_WAITING = 5, + KTHREAD_STATE_UNKNOWN = 6, //"Transition" except that makes no sense here, so + // 6 likely has a different meaning on xboxkrnl +}; + constexpr uint32_t X_CREATE_SUSPENDED = 0x00000001; constexpr uint32_t X_TLS_OUT_OF_INDEXES = UINT32_MAX; @@ -82,7 +115,7 @@ struct X_KTHREAD; struct X_KPROCESS; struct X_KPRCB { TypedGuestPointer current_thread; // 0x0 - TypedGuestPointer unk_4; // 0x4 + TypedGuestPointer next_thread; // 0x4 TypedGuestPointer idle_thread; // 0x8 uint8_t current_cpu; // 0xC uint8_t unk_D[3]; // 0xD @@ -104,14 +137,14 @@ struct X_KPRCB { xe::be unk_3C; // 0x3C xe::be dpc_related_40; // 0x40 // must be held to modify any dpc-related fields in the kprcb - xe::be dpc_lock; // 0x44 - X_LIST_ENTRY queued_dpcs_list_head; // 0x48 - xe::be dpc_active; // 0x50 - X_KSPINLOCK spin_lock; // 0x54 - xe::be unk_58; // 0x58 + xe::be dpc_lock; // 0x44 + X_LIST_ENTRY queued_dpcs_list_head; // 0x48 + xe::be dpc_active; // 0x50 + X_KSPINLOCK spin_lock; // 0x54 + TypedGuestPointer running_idle_thread; // 0x58 // definitely scheduler related - X_SINGLE_LIST_ENTRY unk_5C; // 0x5C - xe::be unk_60; // 0x60 + X_SINGLE_LIST_ENTRY enqueued_threads_list; // 0x5C + xe::be has_ready_thread_by_priority; // 0x60 // i think the following mask has something to do with the array that comes // after xe::be unk_mask_64; // 0x64 @@ -123,7 +156,7 @@ struct X_KPRCB { // thread_exit_dpc's routine drains this list and frees each threads threadid, // kernel stack and dereferences the thread X_LIST_ENTRY terminating_threads_list; // 0x184 - XDPC unk_18C; // 0x18C + XDPC switch_thread_processor_dpc; // 0x18C }; // Processor Control Region struct X_KPCR { @@ -132,19 +165,25 @@ struct X_KPCR { union { xe::be software_interrupt_state; // 0x8 struct { - uint8_t unknown_8; // 0x8 + uint8_t generic_software_interrupt; // 0x8 uint8_t apc_software_interrupt_state; // 0x9 }; }; - uint8_t unk_0A[2]; // 0xA + xe::be unk_0A; // 0xA uint8_t processtype_value_in_dpc; // 0xC - uint8_t unk_0D[3]; // 0xD + uint8_t timeslice_ended; // 0xD + uint8_t timer_pending; // 0xE + uint8_t unk_0F; // 0xF // used in KeSaveFloatingPointState / its vmx counterpart - xe::be thread_fpu_related; // 0x10 - xe::be thread_vmx_related; // 0x14 - uint8_t current_irql; // 0x18 - uint8_t unk_19[0x17]; // 0x19 - xe::be pcr_ptr; // 0x30 + xe::be thread_fpu_related; // 0x10 + xe::be thread_vmx_related; // 0x14 + uint8_t current_irql; // 0x18 + uint8_t background_scheduling_active; // 0x19 + uint8_t background_scheduling_1A; // 0x1A + uint8_t background_scheduling_1B; // 0x1B + xe::be timer_related; // 0x1C + uint8_t unk_20[0x10]; // 0x20 + xe::be pcr_ptr; // 0x30 // this seems to be just garbage data? we can stash a pointer to context here // as a hack for now @@ -174,27 +213,54 @@ struct X_KPCR { uint8_t unk_2AC[0x2C]; // 0x2AC }; +enum : uint16_t { + WAIT_ALL = 0, + WAIT_ANY = 1, +}; + +// https://www.geoffchappell.com/studies/windows/km/ntoskrnl/inc/ntos/ke_x/kwait_block.htm +// pretty much the vista KWAIT_BLOCK verbatim, except that sparebyte is gone +// and WaitType is 2 bytes instead of 1 +struct X_KWAIT_BLOCK { + X_LIST_ENTRY wait_list_entry; // 0x0 + TypedGuestPointer thread; + TypedGuestPointer object; + TypedGuestPointer next_wait_block; + // this isnt the official vista name, but i think its better. + // this value is what will be returned to the waiter if this particular wait + // is satisfied + xe::be wait_result_xstatus; + // WAIT_ALL or WAIT_ANY + xe::be wait_type; +}; + +static_assert_size(X_KWAIT_BLOCK, 0x18); + +struct X_KTIMER { + X_DISPATCH_HEADER header; // 0x0 + xe::be due_time; // 0x10 + X_LIST_ENTRY table_bucket_entry; // 0x18 + TypedGuestPointer dpc; // 0x20 + xe::be period; // 0x24 +}; +static_assert_size(X_KTIMER, 0x28); + struct X_KTHREAD { - 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 - xe::be stack_kernel; // 0x64 - xe::be tls_address; // 0x68 + X_DISPATCH_HEADER header; // 0x0 + xe::be unk_10; // 0x10 + xe::be unk_14; // 0x14 + X_KTIMER wait_timeout_timer; // 0x18 + X_KWAIT_BLOCK wait_timeout_block; // 0x40 + uint8_t unk_58[0x4]; // 0x58 + xe::be stack_base; // 0x5C + xe::be stack_limit; // 0x60 + xe::be stack_kernel; // 0x64 + xe::be tls_address; // 0x68 // state = is thread running, suspended, etc uint8_t thread_state; // 0x6C // 0x70 = priority? - uint8_t unk_6D[0x3]; // 0x6D + uint8_t alerted[2]; // 0x6D + uint8_t alertable; // 0x6F uint8_t priority; // 0x70 uint8_t fpu_exceptions_on; // 0x71 // these two process types both get set to the same thing, process_type is @@ -205,50 +271,56 @@ struct X_KTHREAD { // apc_mode determines which list an apc goes into util::X_TYPED_LIST apc_lists[2]; TypedGuestPointer process; // 0x84 - uint8_t unk_88[0x3]; // 0x88 - uint8_t may_queue_apcs; // 0x8B - X_KSPINLOCK apc_lock; // 0x8C - uint8_t unk_90[0xC]; // 0x90 - xe::be msr_mask; // 0x9C - uint8_t unk_A0[4]; // 0xA0 - uint8_t unk_A4; // 0xA4 - uint8_t unk_A5[0xB]; // 0xA5 - int32_t apc_disable_count; // 0xB0 - uint8_t unk_B4[4]; // 0xB4 - uint8_t unk_B8; // 0xB8 - uint8_t unk_B9; // 0xB9 - uint8_t unk_BA; // 0xBA - uint8_t boost_disabled; // 0xBB - uint8_t suspend_count; // 0xBC - uint8_t unk_BD; // 0xBD - uint8_t terminated; // 0xBE - uint8_t current_cpu; // 0xBF + uint8_t executing_kernel_apc; // 0x88 + // when context switch happens, this is copied into + // apc_software_interrupt_state for kpcr + uint8_t deferred_apc_software_interrupt_state; // 0x89 + uint8_t user_apc_pending; // 0x8A + uint8_t may_queue_apcs; // 0x8B + X_KSPINLOCK apc_lock; // 0x8C + xe::be num_context_switches_to; // 0x90 + X_LIST_ENTRY ready_prcb_entry; // 0x94 + xe::be msr_mask; // 0x9C + xe::be wait_result; // 0xA0 + uint8_t wait_irql; // 0xA4 + uint8_t unk_A5[0xB]; // 0xA5 + int32_t apc_disable_count; // 0xB0 + xe::be quantum; // 0xB4 + uint8_t unk_B8; // 0xB8 + uint8_t unk_B9; // 0xB9 + uint8_t unk_BA; // 0xBA + uint8_t boost_disabled; // 0xBB + uint8_t suspend_count; // 0xBC + uint8_t was_preempted; // 0xBD + uint8_t terminated; // 0xBE + uint8_t current_cpu; // 0xBF // these two pointers point to KPRCBs, but seem to be rarely referenced, if at // all TypedGuestPointer a_prcb_ptr; // 0xC0 TypedGuestPointer another_prcb_ptr; // 0xC4 - uint8_t unk_C8[8]; // 0xC8 + uint8_t unk_C8; // 0xC8 + uint8_t unk_C9; // 0xC9 + uint8_t unk_CA; // 0xCA + uint8_t unk_CB; // 0xCB + X_KSPINLOCK timer_list_lock; // 0xCC xe::be stack_alloc_base; // 0xD0 - // uint8_t unk_D4[0x5C]; // 0xD4 - XAPC on_suspend; // 0xD4 - X_KSEMAPHORE unk_FC; // 0xFC + XAPC on_suspend; // 0xD4 + X_KSEMAPHORE suspend_sema; // 0xFC // this is an entry in - X_LIST_ENTRY process_threads; // 0x110 - xe::be unk_118; // 0x118 - xe::be unk_11C; // 0x11C - xe::be unk_120; // 0x120 - xe::be unk_124; // 0x124 - xe::be unk_128; // 0x128 - xe::be unk_12C; // 0x12C - 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 + X_LIST_ENTRY process_threads; // 0x110 + xe::be unk_118; // 0x118 + X_LIST_ENTRY queue_related; // 0x11C + xe::be unk_124; // 0x124 + xe::be unk_128; // 0x128 + xe::be unk_12C; // 0x12C + xe::be create_time; // 0x130 + xe::be exit_time; // 0x138 + xe::be exit_status; // 0x140 + // tracks all pending timers that have apcs which target this thread + X_LIST_ENTRY timer_list; // 0x144 xe::be thread_id; // 0x14C xe::be start_address; // 0x150 - xe::be unk_154; // 0x154 - xe::be unk_158; // 0x158 + X_LIST_ENTRY unk_154; // 0x154 uint8_t unk_15C[0x4]; // 0x15C xe::be last_error; // 0x160 xe::be fiber_ptr; // 0x164