298 lines
10 KiB
C++
298 lines
10 KiB
C++
/**
|
|
******************************************************************************
|
|
* Xenia : Xbox 360 Emulator Research Project *
|
|
******************************************************************************
|
|
* Copyright 2020 Ben Vanik. All rights reserved. *
|
|
* Released under the BSD license - see LICENSE in the root for more details. *
|
|
******************************************************************************
|
|
*/
|
|
|
|
#ifndef XENIA_KERNEL_XTHREAD_H_
|
|
#define XENIA_KERNEL_XTHREAD_H_
|
|
|
|
#include <atomic>
|
|
#include <string>
|
|
|
|
#include "xenia/base/mutex.h"
|
|
#include "xenia/base/threading.h"
|
|
#include "xenia/cpu/thread.h"
|
|
#include "xenia/cpu/thread_state.h"
|
|
#include "xenia/kernel/util/native_list.h"
|
|
#include "xenia/kernel/xmutant.h"
|
|
#include "xenia/kernel/xobject.h"
|
|
#include "xenia/xbox.h"
|
|
|
|
namespace xe {
|
|
namespace kernel {
|
|
|
|
constexpr fourcc_t kThreadSaveSignature = make_fourcc("THRD");
|
|
|
|
class XEvent;
|
|
|
|
constexpr uint32_t X_CREATE_SUSPENDED = 0x00000001;
|
|
|
|
constexpr uint32_t X_TLS_OUT_OF_INDEXES = UINT32_MAX;
|
|
|
|
struct XAPC {
|
|
static const uint32_t kSize = 40;
|
|
static const uint32_t kDummyKernelRoutine = 0xF00DFF00;
|
|
static const uint32_t kDummyRundownRoutine = 0xF00DFF01;
|
|
|
|
// KAPC is 0x28(40) bytes? (what's passed to ExAllocatePoolWithTag)
|
|
// This is 4b shorter than NT - looks like the reserved dword at +4 is gone.
|
|
// NOTE: stored in guest memory.
|
|
uint8_t type; // +0
|
|
uint8_t unk1; // +1
|
|
uint8_t processor_mode; // +2
|
|
uint8_t enqueued; // +3
|
|
xe::be<uint32_t> thread_ptr; // +4
|
|
xe::be<uint32_t> flink; // +8
|
|
xe::be<uint32_t> blink; // +12
|
|
xe::be<uint32_t> kernel_routine; // +16
|
|
xe::be<uint32_t> rundown_routine; // +20
|
|
xe::be<uint32_t> normal_routine; // +24
|
|
xe::be<uint32_t> normal_context; // +28
|
|
xe::be<uint32_t> arg1; // +32
|
|
xe::be<uint32_t> arg2; // +36
|
|
|
|
void Initialize() {
|
|
type = 18; // ApcObject
|
|
unk1 = 0;
|
|
processor_mode = 0;
|
|
enqueued = 0;
|
|
thread_ptr = 0;
|
|
flink = blink = 0;
|
|
kernel_routine = 0;
|
|
normal_routine = 0;
|
|
normal_context = 0;
|
|
arg1 = arg2 = 0;
|
|
}
|
|
};
|
|
|
|
// Processor Control Region
|
|
struct X_KPCR {
|
|
xe::be<uint32_t> tls_ptr; // 0x0
|
|
uint8_t unk_04[0x2C]; // 0x4
|
|
xe::be<uint32_t> pcr_ptr; // 0x30
|
|
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)
|
|
uint8_t unk_78[0x88]; // 0x78
|
|
xe::be<uint32_t> current_thread; // 0x100
|
|
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
|
|
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[0x10]; // 0xA0
|
|
int32_t apc_disable_count; // 0xB0
|
|
uint8_t unk_B4[0x8]; // 0xB4
|
|
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!
|
|
};
|
|
static_assert_size(X_KTHREAD, 0xAB0);
|
|
|
|
class XThread : public XObject, public cpu::Thread {
|
|
public:
|
|
static const XObject::Type kObjectType = XObject::Type::Thread;
|
|
|
|
static constexpr uint32_t kStackAddressRangeBegin = 0x70000000;
|
|
static constexpr uint32_t kStackAddressRangeEnd = 0x7F000000;
|
|
|
|
struct CreationParams {
|
|
uint32_t stack_size;
|
|
uint32_t xapi_thread_startup;
|
|
uint32_t start_address;
|
|
uint32_t start_context;
|
|
uint32_t creation_flags;
|
|
};
|
|
|
|
XThread(KernelState* kernel_state);
|
|
XThread(KernelState* kernel_state, uint32_t stack_size,
|
|
uint32_t xapi_thread_startup, uint32_t start_address,
|
|
uint32_t start_context, uint32_t creation_flags, bool guest_thread,
|
|
bool main_thread = false);
|
|
~XThread() override;
|
|
|
|
static bool IsInThread(XThread* other);
|
|
static bool IsInThread();
|
|
static XThread* GetCurrentThread();
|
|
static uint32_t GetCurrentThreadHandle();
|
|
static uint32_t GetCurrentThreadId();
|
|
|
|
static uint32_t GetLastError();
|
|
static void SetLastError(uint32_t error_code);
|
|
|
|
const CreationParams* creation_params() const { return &creation_params_; }
|
|
uint32_t tls_ptr() const { return tls_static_address_; }
|
|
uint32_t pcr_ptr() const { return pcr_address_; }
|
|
// True if the thread is created by the guest app.
|
|
bool is_guest_thread() const { return guest_thread_; }
|
|
bool main_thread() const { return main_thread_; }
|
|
bool is_running() const { return running_; }
|
|
|
|
uint32_t thread_id() const { return thread_id_; }
|
|
uint32_t last_error();
|
|
void set_last_error(uint32_t error_code);
|
|
void set_name(const std::string_view name);
|
|
|
|
X_STATUS Create();
|
|
X_STATUS Exit(int exit_code);
|
|
X_STATUS Terminate(int exit_code);
|
|
|
|
virtual void Execute();
|
|
|
|
virtual void Reenter(uint32_t address);
|
|
|
|
void EnterCriticalRegion();
|
|
void LeaveCriticalRegion();
|
|
uint32_t RaiseIrql(uint32_t new_irql);
|
|
void LowerIrql(uint32_t new_irql);
|
|
|
|
void CheckApcs();
|
|
void LockApc();
|
|
void UnlockApc(bool queue_delivery);
|
|
util::NativeList* apc_list() { return &apc_list_; }
|
|
void EnqueueApc(uint32_t normal_routine, uint32_t normal_context,
|
|
uint32_t arg1, uint32_t arg2);
|
|
|
|
int32_t priority() const { return priority_; }
|
|
int32_t QueryPriority();
|
|
void SetPriority(int32_t increment);
|
|
|
|
// Xbox thread IDs:
|
|
// 0 - core 0, thread 0 - user
|
|
// 1 - core 0, thread 1 - user
|
|
// 2 - core 1, thread 0 - sometimes xcontent
|
|
// 3 - core 1, thread 1 - user
|
|
// 4 - core 2, thread 0 - xaudio
|
|
// 5 - core 2, thread 1 - user
|
|
void SetAffinity(uint32_t affinity);
|
|
uint8_t active_cpu() const;
|
|
void SetActiveCpu(uint8_t cpu_index);
|
|
|
|
bool GetTLSValue(uint32_t slot, uint32_t* value_out);
|
|
bool SetTLSValue(uint32_t slot, uint32_t value);
|
|
|
|
uint32_t suspend_count();
|
|
X_STATUS Resume(uint32_t* out_suspend_count = nullptr);
|
|
X_STATUS Suspend(uint32_t* out_suspend_count = nullptr);
|
|
X_STATUS Delay(uint32_t processor_mode, uint32_t alertable,
|
|
uint64_t interval);
|
|
|
|
xe::threading::Thread* thread() { return thread_.get(); }
|
|
|
|
virtual bool Save(ByteStream* stream) override;
|
|
static object_ref<XThread> Restore(KernelState* kernel_state,
|
|
ByteStream* stream);
|
|
|
|
// Internal - do not use.
|
|
void AcquireMutantOnStartup(object_ref<XMutant> mutant) {
|
|
pending_mutant_acquires_.push_back(mutant);
|
|
}
|
|
|
|
protected:
|
|
bool AllocateStack(uint32_t size);
|
|
void FreeStack();
|
|
void InitializeGuestObject();
|
|
|
|
void DeliverAPCs();
|
|
void RundownAPCs();
|
|
|
|
xe::threading::WaitHandle* GetWaitHandle() override { return thread_.get(); }
|
|
|
|
CreationParams creation_params_ = {0};
|
|
|
|
std::vector<object_ref<XMutant>> pending_mutant_acquires_;
|
|
|
|
uint32_t thread_id_ = 0;
|
|
uint32_t scratch_address_ = 0;
|
|
uint32_t scratch_size_ = 0;
|
|
uint32_t tls_static_address_ = 0;
|
|
uint32_t tls_dynamic_address_ = 0;
|
|
uint32_t tls_total_size_ = 0;
|
|
uint32_t pcr_address_ = 0;
|
|
uint32_t stack_alloc_base_ = 0; // Stack alloc base
|
|
uint32_t stack_alloc_size_ = 0; // Stack alloc size
|
|
uint32_t stack_base_ = 0; // High address
|
|
uint32_t stack_limit_ = 0; // Low address
|
|
bool guest_thread_ = false;
|
|
bool main_thread_ = false; // Entry-point thread
|
|
bool running_ = false;
|
|
|
|
int32_t priority_ = 0;
|
|
|
|
xe::global_critical_region global_critical_region_;
|
|
std::atomic<uint32_t> irql_ = {0};
|
|
util::NativeList apc_list_;
|
|
};
|
|
|
|
class XHostThread : public XThread {
|
|
public:
|
|
XHostThread(KernelState* kernel_state, uint32_t stack_size,
|
|
uint32_t creation_flags, std::function<int()> host_fn);
|
|
|
|
virtual void Execute();
|
|
|
|
private:
|
|
std::function<int()> host_fn_;
|
|
};
|
|
|
|
} // namespace kernel
|
|
} // namespace xe
|
|
|
|
#endif // XENIA_KERNEL_XTHREAD_H_
|