diff --git a/src/xenia/core/memory.cc b/src/xenia/core/memory.cc index 4cb2f8249..2b8283deb 100644 --- a/src/xenia/core/memory.cc +++ b/src/xenia/core/memory.cc @@ -163,6 +163,13 @@ uint8_t *xe_memory_addr(xe_memory_ref memory, size_t guest_addr) { return (uint8_t*)memory->ptr + guest_addr; } +void xe_memory_copy(xe_memory_ref memory, + uint32_t dest, uint32_t src, uint32_t size) { + uint8_t* pdest = (uint8_t*)memory->ptr + dest; + uint8_t* psrc = (uint8_t*)memory->ptr + src; + XEIGNORE(xe_copy_memory(pdest, size, psrc, size)); +} + uint32_t xe_memory_search_aligned(xe_memory_ref memory, size_t start, size_t end, const uint32_t *values, const size_t value_count) { diff --git a/src/xenia/core/memory.h b/src/xenia/core/memory.h index 0175b5600..4561b68d9 100644 --- a/src/xenia/core/memory.h +++ b/src/xenia/core/memory.h @@ -30,6 +30,9 @@ void xe_memory_release(xe_memory_ref memory); size_t xe_memory_get_length(xe_memory_ref memory); uint8_t *xe_memory_addr(xe_memory_ref memory, size_t guest_addr = 0); +void xe_memory_copy(xe_memory_ref memory, + uint32_t dest, uint32_t src, uint32_t size); + uint32_t xe_memory_search_aligned(xe_memory_ref memory, size_t start, size_t end, const uint32_t *values, const size_t value_count); diff --git a/src/xenia/kernel/modules/xboxkrnl/objects/xthread.cc b/src/xenia/kernel/modules/xboxkrnl/objects/xthread.cc index 336fe8af8..0eba85b85 100644 --- a/src/xenia/kernel/modules/xboxkrnl/objects/xthread.cc +++ b/src/xenia/kernel/modules/xboxkrnl/objects/xthread.cc @@ -9,6 +9,8 @@ #include +#include + using namespace xe; using namespace xe::kernel; @@ -48,6 +50,9 @@ XThread::~XThread() { if (processor_state_) { kernel_state()->processor()->DeallocThread(processor_state_); } + if (tls_address_) { + xe_memory_heap_free(kernel_state()->memory(), tls_address_, 0); + } if (thread_state_address_) { xe_memory_heap_free(kernel_state()->memory(), thread_state_address_, 0); } @@ -82,20 +87,37 @@ X_STATUS XThread::Create() { // (like GetLastError/etc) will poke it directly. // We try to use it as our primary store of data just to keep things all // consistent. - thread_state_address_ = xe_memory_heap_alloc(memory(), 0, 2048, 0); - if (!thread_state_address_) { - XELOGW("Unable to allocate thread state block"); - return X_STATUS_NO_MEMORY; - } - - // Setup the thread state block (last error/etc). + // 0x000: pointer to tls data // 0x100: pointer to self? // 0x14C: thread id // 0x150: if >0 then error states don't get set // 0x160: last error // So, at offset 0x100 we have a 4b pointer to offset 200, then have the // structure. + thread_state_address_ = xe_memory_heap_alloc(memory(), 0, 2048, 0); + if (!thread_state_address_) { + XELOGW("Unable to allocate thread state block"); + return X_STATUS_NO_MEMORY; + } + + // Allocate TLS block. + XModule* module = kernel_state()->GetExecutableModule(); + const xe_xex2_header_t* header = module->xex_header(); + uint32_t tls_size = header->tls_info.slot_count * header->tls_info.data_size; + tls_address_ = xe_memory_heap_alloc(memory(), 0, tls_size, 0); + if (!tls_address_) { + XELOGW("Unable to allocate thread local storage block"); + return X_STATUS_NO_MEMORY; + } + + // Copy in default TLS info. + // TODO(benvanik): is this correct? + xe_memory_copy(memory(), + tls_address_, header->tls_info.raw_data_address, tls_size); + + // Setup the thread state block (last error/etc). uint8_t *p = xe_memory_addr(memory(), thread_state_address_); + XESETUINT32BE(p + 0x000, tls_address_); XESETUINT32BE(p + 0x100, thread_state_address_); XESETUINT32BE(p + 0x14C, thread_id_); XESETUINT32BE(p + 0x150, 0); // ? diff --git a/src/xenia/kernel/modules/xboxkrnl/objects/xthread.h b/src/xenia/kernel/modules/xboxkrnl/objects/xthread.h index 32fdc2810..360a0f19a 100644 --- a/src/xenia/kernel/modules/xboxkrnl/objects/xthread.h +++ b/src/xenia/kernel/modules/xboxkrnl/objects/xthread.h @@ -55,6 +55,7 @@ private: uint32_t thread_id_; void* thread_handle_; + uint32_t tls_address_; uint32_t thread_state_address_; cpu::ThreadState* processor_state_; };