diff --git a/src/xenia/kernel/objects/xevent.cc b/src/xenia/kernel/objects/xevent.cc index f1e7a39d5..8ef30bfd1 100644 --- a/src/xenia/kernel/objects/xevent.cc +++ b/src/xenia/kernel/objects/xevent.cc @@ -28,11 +28,11 @@ void XEvent::Initialize(bool manual_reset, bool initial_state) { native_handle_ = CreateEvent(NULL, manual_reset, initial_state, NULL); } -void XEvent::InitializeNative(void* native_ptr, DISPATCH_HEADER& header) { +void XEvent::InitializeNative(void* native_ptr, X_DISPATCH_HEADER& header) { assert_null(native_handle_); bool manual_reset; - switch ((header.type_flags >> 24) & 0xFF) { + switch (header.type) { case 0x00: // EventNotificationObject (manual reset) manual_reset = true; break; diff --git a/src/xenia/kernel/objects/xevent.h b/src/xenia/kernel/objects/xevent.h index 68c06044f..9def93869 100644 --- a/src/xenia/kernel/objects/xevent.h +++ b/src/xenia/kernel/objects/xevent.h @@ -22,7 +22,7 @@ class XEvent : public XObject { virtual ~XEvent(); void Initialize(bool manual_reset, bool initial_state); - void InitializeNative(void* native_ptr, DISPATCH_HEADER& header); + void InitializeNative(void* native_ptr, X_DISPATCH_HEADER& header); int32_t Set(uint32_t priority_increment, bool wait); int32_t Pulse(uint32_t priority_increment, bool wait); diff --git a/src/xenia/kernel/objects/xmutant.cc b/src/xenia/kernel/objects/xmutant.cc index 006697def..a26e8c79f 100644 --- a/src/xenia/kernel/objects/xmutant.cc +++ b/src/xenia/kernel/objects/xmutant.cc @@ -27,7 +27,7 @@ void XMutant::Initialize(bool initial_owner) { native_handle_ = CreateMutex(NULL, initial_owner ? TRUE : FALSE, NULL); } -void XMutant::InitializeNative(void* native_ptr, DISPATCH_HEADER& header) { +void XMutant::InitializeNative(void* native_ptr, X_DISPATCH_HEADER& header) { assert_null(native_handle_); // Haven't seen this yet, but it's possible. diff --git a/src/xenia/kernel/objects/xmutant.h b/src/xenia/kernel/objects/xmutant.h index bc62a1b53..359970209 100644 --- a/src/xenia/kernel/objects/xmutant.h +++ b/src/xenia/kernel/objects/xmutant.h @@ -22,7 +22,7 @@ class XMutant : public XObject { virtual ~XMutant(); void Initialize(bool initial_owner); - void InitializeNative(void* native_ptr, DISPATCH_HEADER& header); + void InitializeNative(void* native_ptr, X_DISPATCH_HEADER& header); X_STATUS ReleaseMutant(uint32_t priority_increment, bool abandon, bool wait); diff --git a/src/xenia/kernel/objects/xsemaphore.cc b/src/xenia/kernel/objects/xsemaphore.cc index 72d473d96..4f39b09a7 100644 --- a/src/xenia/kernel/objects/xsemaphore.cc +++ b/src/xenia/kernel/objects/xsemaphore.cc @@ -24,10 +24,12 @@ XSemaphore::~XSemaphore() { void XSemaphore::Initialize(int32_t initial_count, int32_t maximum_count) { assert_null(native_handle_); + CreateNative(sizeof(X_SEMAPHORE)); + native_handle_ = CreateSemaphore(NULL, initial_count, maximum_count, NULL); } -void XSemaphore::InitializeNative(void* native_ptr, DISPATCH_HEADER& header) { +void XSemaphore::InitializeNative(void* native_ptr, X_DISPATCH_HEADER& header) { assert_null(native_handle_); // NOT IMPLEMENTED diff --git a/src/xenia/kernel/objects/xsemaphore.h b/src/xenia/kernel/objects/xsemaphore.h index fd693d72a..97dbc7942 100644 --- a/src/xenia/kernel/objects/xsemaphore.h +++ b/src/xenia/kernel/objects/xsemaphore.h @@ -16,13 +16,18 @@ namespace xe { namespace kernel { +struct X_SEMAPHORE { + X_DISPATCH_HEADER header; + // TODO: Make this not empty! +}; + class XSemaphore : public XObject { public: XSemaphore(KernelState* kernel_state); virtual ~XSemaphore(); void Initialize(int32_t initial_count, int32_t maximum_count); - void InitializeNative(void* native_ptr, DISPATCH_HEADER& header); + void InitializeNative(void* native_ptr, X_DISPATCH_HEADER& header); int32_t ReleaseSemaphore(int32_t release_count); diff --git a/src/xenia/kernel/objects/xthread.cc b/src/xenia/kernel/objects/xthread.cc index 0222443e1..6192a3456 100644 --- a/src/xenia/kernel/objects/xthread.cc +++ b/src/xenia/kernel/objects/xthread.cc @@ -143,6 +143,14 @@ uint8_t GetFakeCpuNumber(uint8_t proc_mask) { } X_STATUS XThread::Create() { + // Thread kernel object + // This call will also setup the native pointer for us. + uint8_t* guest_object = CreateNative(sizeof(X_THREAD)); + if (!guest_object) { + XELOGW("Unable to allocate thread object"); + return X_STATUS_NO_MEMORY; + } + // Allocate thread state block from heap. // This is set as r13 for user code and some special inlined Win32 calls // (like GetLastError/etc) will poke it directly. @@ -162,9 +170,6 @@ X_STATUS XThread::Create() { return X_STATUS_NO_MEMORY; } - // Set native info. - SetNativePointer(thread_state_address_, true); - auto module = kernel_state()->GetExecutableModule(); // Allocate thread scratch. @@ -276,8 +281,6 @@ X_STATUS XThread::Create() { xe::store_and_swap(p + 0x16C, creation_params_.creation_flags); xe::store_and_swap(p + 0x17C, 1); - SetNativePointer(thread_state_address_); - X_STATUS return_code = PlatformCreate(); if (XFAILED(return_code)) { XELOGW("Unable to create platform thread (%.8X)", return_code); diff --git a/src/xenia/kernel/objects/xthread.h b/src/xenia/kernel/objects/xthread.h index ee2f441d0..a97d85182 100644 --- a/src/xenia/kernel/objects/xthread.h +++ b/src/xenia/kernel/objects/xthread.h @@ -60,6 +60,19 @@ struct XAPC { } }; +// http://www.nirsoft.net/kernel_struct/vista/KTHREAD.html +struct X_THREAD { + X_DISPATCH_HEADER header; + xe::be cycle_time; + xe::be high_cycle_time; // FIXME: Needed? + xe::be quantum_target; + xe::be initial_stack_ptr; + xe::be stack_limit_ptr; + xe::be kernel_stack_ptr; + + // This struct is actually quite long... so uh, not filling this out! +}; + class XThread : public XObject { public: XThread(KernelState* kernel_state, uint32_t stack_size, @@ -74,6 +87,7 @@ class XThread : public XObject { uint32_t pcr_ptr() const { return pcr_address_; } uint32_t thread_state_ptr() const { return thread_state_address_; } + cpu::ThreadState* thread_state() const { return thread_state_; } uint32_t thread_id() const { return thread_id_; } uint32_t last_error(); diff --git a/src/xenia/kernel/xboxkrnl_ob.cc b/src/xenia/kernel/xboxkrnl_ob.cc index a3f154865..a3ddc4507 100644 --- a/src/xenia/kernel/xboxkrnl_ob.cc +++ b/src/xenia/kernel/xboxkrnl_ob.cc @@ -10,6 +10,7 @@ #include "xenia/base/logging.h" #include "xenia/kernel/kernel_state.h" #include "xenia/kernel/objects/xthread.h" +#include "xenia/kernel/objects/xsemaphore.h" #include "xenia/kernel/util/shim_utils.h" #include "xenia/kernel/xboxkrnl_private.h" #include "xenia/kernel/xobject.h" @@ -76,7 +77,7 @@ SHIM_CALL ObReferenceObjectByHandle_shim(PPCContext* ppc_context, } break;*/ case XObject::kTypeThread: { auto thread = object.get(); - native_ptr = thread->thread_state_ptr(); + native_ptr = thread->guest_object(); } break; default: { assert_unhandled_case(object->type()); @@ -85,13 +86,16 @@ SHIM_CALL ObReferenceObjectByHandle_shim(PPCContext* ppc_context, } } break; case 0xD017BEEF: { // ExSemaphoreObjectType - // TODO(benvanik): implement. - assert_unhandled_case(object_type_ptr); - native_ptr = 0xDEADF00D; + assert(object->type() == XObject::kTypeSemaphore); + auto sem = object.get(); + + native_ptr = sem->guest_object(); } break; case 0xD01BBEEF: { // ExThreadObjectType + assert(object->type() == XObject::kTypeThread); auto thread = object.get(); - native_ptr = thread->thread_state_ptr(); + + native_ptr = thread->guest_object(); } break; default: { assert_unhandled_case(object_type_ptr); diff --git a/src/xenia/kernel/xobject.cc b/src/xenia/kernel/xobject.cc index 850bb8c0c..29639316f 100644 --- a/src/xenia/kernel/xobject.cc +++ b/src/xenia/kernel/xobject.cc @@ -24,7 +24,9 @@ XObject::XObject(KernelState* kernel_state, Type type) handle_ref_count_(0), pointer_ref_count_(1), type_(type), - handle_(X_INVALID_HANDLE_VALUE) { + handle_(X_INVALID_HANDLE_VALUE), + guest_object_ptr_(0), + allocated_guest_object_(false) { // Added pointer check to support usage without a kernel_state if (kernel_state != nullptr) { kernel_state->object_table()->AddHandle(this, &handle_); @@ -34,6 +36,18 @@ XObject::XObject(KernelState* kernel_state, Type type) XObject::~XObject() { assert_zero(handle_ref_count_); assert_zero(pointer_ref_count_); + + if (allocated_guest_object_) { + uint32_t ptr = guest_object_ptr_ - sizeof(X_OBJECT_HEADER); + auto header = memory()->TranslateVirtual(ptr); + + // Free the object creation info + if (header->object_create_info) { + memory()->SystemHeapFree(header->object_create_info); + } + + memory()->SystemHeapFree(ptr); + } } Emulator* XObject::emulator() const { return kernel_state_->emulator_; } @@ -161,11 +175,45 @@ X_STATUS XObject::WaitMultiple(uint32_t count, XObject** objects, return result; } +uint8_t* XObject::CreateNative(uint32_t size) { + std::lock_guard lock(kernel_state_->object_mutex()); + + uint32_t total_size = size + sizeof(X_OBJECT_HEADER); + + auto mem = memory()->SystemHeapAlloc(total_size); + if (!mem) { + // Out of memory! + return nullptr; + } + + allocated_guest_object_ = true; + memory()->Zero(mem, total_size); + SetNativePointer(mem + sizeof(X_OBJECT_HEADER), true); + + auto header = memory()->TranslateVirtual(mem); + + auto creation_info = + memory()->SystemHeapAlloc(sizeof(X_OBJECT_CREATE_INFORMATION)); + if (creation_info) { + memory()->Zero(creation_info, sizeof(X_OBJECT_CREATE_INFORMATION)); + + // Set it up in the header. + // Some kernel method is accessing this struct and dereferencing a member. + // With our current definition that member is non_paged_pool_charge. + header->object_create_info = creation_info; + } + + return memory()->TranslateVirtual(guest_object_ptr_); +} + void XObject::SetNativePointer(uint32_t native_ptr, bool uninitialized) { std::lock_guard lock(kernel_state_->object_mutex()); + // If hit: We've already setup the native ptr with CreateNative! + assert_zero(guest_object_ptr_); + auto header = - kernel_state_->memory()->TranslateVirtual(native_ptr); + kernel_state_->memory()->TranslateVirtual(native_ptr); // Memory uninitialized, so don't bother with the check. if (!uninitialized) { @@ -173,10 +221,13 @@ void XObject::SetNativePointer(uint32_t native_ptr, bool uninitialized) { } // Stash pointer in struct. + // FIXME: This assumes the object has a dispatch header (some don't!) uint64_t object_ptr = reinterpret_cast(this); object_ptr |= 0x1; header->wait_list_flink = (uint32_t)(object_ptr >> 32); header->wait_list_blink = (uint32_t)(object_ptr & 0xFFFFFFFF); + + guest_object_ptr_ = native_ptr; } object_ref XObject::GetNativeObject(KernelState* kernel_state, @@ -195,10 +246,10 @@ object_ref XObject::GetNativeObject(KernelState* kernel_state, std::lock_guard lock(kernel_state->object_mutex()); - auto header = reinterpret_cast(native_ptr); + auto header = reinterpret_cast(native_ptr); if (as_type == -1) { - as_type = (header->type_flags >> 24) & 0xFF; + as_type = header->type; } if (header->wait_list_blink & 0x1) { @@ -251,6 +302,7 @@ object_ref XObject::GetNativeObject(KernelState* kernel_state, } // Stash pointer in struct. + // FIXME: This assumes the object contains a dispatch header (some don't!) uint64_t object_ptr = reinterpret_cast(object); object_ptr |= 0x1; header->wait_list_flink = (uint32_t)(object_ptr >> 32); diff --git a/src/xenia/kernel/xobject.h b/src/xenia/kernel/xobject.h index 751eff15c..e2a12119c 100644 --- a/src/xenia/kernel/xobject.h +++ b/src/xenia/kernel/xobject.h @@ -29,11 +29,67 @@ class object_ref; // http://www.nirsoft.net/kernel_struct/vista/DISPATCHER_HEADER.html typedef struct { - xe::be type_flags; + struct { + uint8_t type; + + union { + uint8_t abandoned; + uint8_t absolute; + uint8_t npx_irql; + uint8_t signalling; + }; + union { + uint8_t size; + uint8_t hand; + }; + union { + uint8_t inserted; + uint8_t debug_active; + uint8_t dpc_active; + }; + }; + xe::be signal_state; xe::be wait_list_flink; xe::be wait_list_blink; -} DISPATCH_HEADER; +} X_DISPATCH_HEADER; + +// http://www.nirsoft.net/kernel_struct/vista/OBJECT_HEADER.html +struct X_OBJECT_HEADER { + xe::be pointer_count; + union { + xe::be handle_count; + xe::be next_to_free; + }; + xe::be object_type_ptr; + uint8_t name_info_offset; + uint8_t handle_info_offset; + uint8_t quota_info_offset; + uint8_t flags; + union { + xe::be object_create_info; // X_OBJECT_CREATE_INFORMATION + xe::be quota_block_charged; + }; + xe::be security_descriptor; + + // Object lives after this header. + // (There's actually a body field here which is the object itself) +}; + +// http://www.nirsoft.net/kernel_struct/vista/OBJECT_CREATE_INFORMATION.html +struct X_OBJECT_CREATE_INFORMATION { + xe::be attributes; + xe::be root_directory_ptr; + xe::be parse_context_ptr; + xe::be probe_mode; + xe::be paged_pool_charge; + xe::be non_paged_pool_charge; + xe::be security_descriptor_charge; + xe::be security_descriptor; + xe::be security_qos_ptr; + + // Security QoS here (SECURITY_QUALITY_OF_SERVICE) too! +}; class XObject { public: @@ -59,6 +115,7 @@ class XObject { Type type(); X_HANDLE handle() const; const std::string& name() const { return name_; } + uint32_t guest_object() const { return guest_object_ptr_; } void RetainHandle(); bool ReleaseHandle(); @@ -94,6 +151,8 @@ class XObject { virtual void* GetWaitHandle() { return 0; } protected: + // Creates the kernel object for guest code to use. Typically not needed. + uint8_t* CreateNative(uint32_t size); void SetNativePointer(uint32_t native_ptr, bool uninitialized = false); static uint32_t TimeoutTicksToMs(int64_t timeout_ticks); @@ -107,6 +166,11 @@ class XObject { Type type_; X_HANDLE handle_; std::string name_; // May be zero length. + + // Guest pointer for kernel object. Remember: X_OBJECT_HEADER precedes this + // if we allocated it! + uint32_t guest_object_ptr_; + bool allocated_guest_object_; }; template