[Kernel] Implement per-process TLS allocation
Previous global TLS bitmap was shared across all processes. Now per-process TLS bitmaps stored X_KPROCESS structures which should better match Xbox360 architecture.
This commit is contained in:
@@ -68,9 +68,6 @@ KernelState::KernelState(Emulator* emulator)
|
|||||||
InitializeKernelGuestGlobals();
|
InitializeKernelGuestGlobals();
|
||||||
kernel_version_ = KernelVersion(cvars::kernel_build_version);
|
kernel_version_ = KernelVersion(cvars::kernel_build_version);
|
||||||
|
|
||||||
// Hardcoded maximum of 2048 TLS slots.
|
|
||||||
tls_bitmap_.Resize(2048);
|
|
||||||
|
|
||||||
auto hc_loc_heap = memory_->LookupHeap(strange_hardcoded_page_);
|
auto hc_loc_heap = memory_->LookupHeap(strange_hardcoded_page_);
|
||||||
bool fixed_alloc_worked = hc_loc_heap->AllocFixed(
|
bool fixed_alloc_worked = hc_loc_heap->AllocFixed(
|
||||||
strange_hardcoded_page_, 65536, 0,
|
strange_hardcoded_page_, 65536, 0,
|
||||||
@@ -141,18 +138,132 @@ const std::unique_ptr<xam::SpaInfo> KernelState::module_xdbf(
|
|||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
uint32_t KernelState::AllocateTLS() { return uint32_t(tls_bitmap_.Acquire()); }
|
uint32_t KernelState::AllocateTLS(cpu::ppc::PPCContext* context) {
|
||||||
|
auto globals =
|
||||||
|
memory()->TranslateVirtual<KernelGuestGlobals*>(GetKernelGuestGlobals());
|
||||||
|
auto tls_lock = &globals->tls_lock;
|
||||||
|
auto old_irql = xboxkrnl::xeKeKfAcquireSpinLock(context, tls_lock);
|
||||||
|
|
||||||
void KernelState::FreeTLS(uint32_t slot) {
|
int result = -1;
|
||||||
|
|
||||||
|
auto current_thread = XThread::GetCurrentThread();
|
||||||
|
if (!current_thread) {
|
||||||
|
XELOGE("AllocateTLS: No current thread");
|
||||||
|
xboxkrnl::xeKeKfReleaseSpinLock(context, tls_lock, old_irql);
|
||||||
|
return X_TLS_OUT_OF_INDEXES;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto process_ptr = memory()->TranslateVirtual(
|
||||||
|
current_thread->guest_object<X_KTHREAD>()->process);
|
||||||
|
if (!process_ptr) {
|
||||||
|
XELOGE("AllocateTLS: Failed to translate process pointer");
|
||||||
|
xboxkrnl::xeKeKfReleaseSpinLock(context, tls_lock, old_irql);
|
||||||
|
return X_TLS_OUT_OF_INDEXES;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Search for a free TLS slot in the process bitmap
|
||||||
|
// Bitmap format: 1 = free, 0 = allocated
|
||||||
|
// 8 x 32-bit words = 256 total TLS slots
|
||||||
|
for (xe::be<uint32_t>* i = &process_ptr->tls_slot_bitmap[0];
|
||||||
|
i < &process_ptr->tls_slot_bitmap[8]; ++i) {
|
||||||
|
// Read bitmap value (handles big-endian conversion)
|
||||||
|
uint32_t bitmap_value = static_cast<uint32_t>(*i);
|
||||||
|
|
||||||
|
// Find highest free slot using lzcnt (leading zero count)
|
||||||
|
// Returns 0-31 if a bit is set, 32 if no bits are set
|
||||||
|
uint32_t leading_zeros = xe::lzcnt(bitmap_value);
|
||||||
|
|
||||||
|
if (leading_zeros != 32) {
|
||||||
|
// Calculate absolute slot index from bitmap position and bit offset
|
||||||
|
// Each bitmap word represents 32 slots
|
||||||
|
size_t bitmap_index = i - &process_ptr->tls_slot_bitmap[0];
|
||||||
|
uint32_t base_slot = static_cast<uint32_t>(bitmap_index) * 32;
|
||||||
|
int calculated_slot = base_slot + leading_zeros;
|
||||||
|
|
||||||
|
// Validate slot is within Xbox 360 TLS range
|
||||||
|
if (calculated_slot >= 0 && calculated_slot < 256) {
|
||||||
|
result = calculated_slot;
|
||||||
|
|
||||||
|
// Clear the bit to mark as allocated
|
||||||
|
// lzcnt returns 0 for bit 31, 31 for bit 0
|
||||||
|
uint32_t bit_index = 31 - leading_zeros;
|
||||||
|
*i = bitmap_value & ~(1U << bit_index);
|
||||||
|
break;
|
||||||
|
} else {
|
||||||
|
XELOGE("AllocateTLS: Invalid slot calculation: {}", calculated_slot);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (result == -1) {
|
||||||
|
XELOGW("AllocateTLS: All TLS slots exhausted for current process");
|
||||||
|
}
|
||||||
|
|
||||||
|
xboxkrnl::xeKeKfReleaseSpinLock(context, tls_lock, old_irql);
|
||||||
|
return static_cast<uint32_t>(result);
|
||||||
|
}
|
||||||
|
|
||||||
|
void KernelState::FreeTLS(cpu::ppc::PPCContext* context, uint32_t slot) {
|
||||||
|
if (slot >= 256) {
|
||||||
|
XELOGE("FreeTLS: Invalid slot index {}", slot);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto current_thread = XThread::GetCurrentThread();
|
||||||
|
if (!current_thread) {
|
||||||
|
XELOGE("FreeTLS: No current thread");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto current_kthread = current_thread->guest_object<X_KTHREAD>();
|
||||||
|
if (!current_kthread) {
|
||||||
|
XELOGE("FreeTLS: Failed to get guest thread object");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto process_ptr = memory()->TranslateVirtual(current_kthread->process);
|
||||||
|
if (!process_ptr) {
|
||||||
|
XELOGE("FreeTLS: Failed to translate process pointer");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto globals =
|
||||||
|
memory()->TranslateVirtual<KernelGuestGlobals*>(GetKernelGuestGlobals());
|
||||||
|
auto tls_lock = &globals->tls_lock;
|
||||||
|
auto old_irql = xboxkrnl::xeKeKfAcquireSpinLock(context, tls_lock);
|
||||||
|
|
||||||
|
uint32_t bitmap_index = slot / 32;
|
||||||
|
uint32_t bit_mask = 1U << (31 - (slot % 32));
|
||||||
|
uint32_t bitmap_value =
|
||||||
|
static_cast<uint32_t>(process_ptr->tls_slot_bitmap[bitmap_index]);
|
||||||
|
|
||||||
|
if (bitmap_value & bit_mask) {
|
||||||
|
XELOGW("FreeTLS: Slot {} is already free", slot);
|
||||||
|
xboxkrnl::xeKeKfReleaseSpinLock(context, tls_lock, old_irql);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Clear TLS values in all threads of this process
|
||||||
const std::vector<object_ref<XThread>> threads =
|
const std::vector<object_ref<XThread>> threads =
|
||||||
object_table()->GetObjectsByType<XThread>();
|
object_table()->GetObjectsByType<XThread>();
|
||||||
|
|
||||||
|
uint32_t current_process_ptr = current_kthread->process.m_ptr;
|
||||||
for (const object_ref<XThread>& thread : threads) {
|
for (const object_ref<XThread>& thread : threads) {
|
||||||
if (thread->is_guest_thread()) {
|
if (!thread || !thread->is_guest_thread()) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto thread_kthread = thread->guest_object<X_KTHREAD>();
|
||||||
|
if (thread_kthread &&
|
||||||
|
thread_kthread->process.m_ptr == current_process_ptr) {
|
||||||
thread->SetTLSValue(slot, 0);
|
thread->SetTLSValue(slot, 0);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
tls_bitmap_.Release(slot);
|
|
||||||
|
// Mark slot as free in bitmap
|
||||||
|
process_ptr->tls_slot_bitmap[bitmap_index] = bitmap_value | bit_mask;
|
||||||
|
|
||||||
|
xboxkrnl::xeKeKfReleaseSpinLock(context, tls_lock, old_irql);
|
||||||
}
|
}
|
||||||
|
|
||||||
void KernelState::RegisterTitleTerminateNotification(uint32_t routine,
|
void KernelState::RegisterTitleTerminateNotification(uint32_t routine,
|
||||||
@@ -805,9 +916,6 @@ void KernelState::TerminateTitle() {
|
|||||||
// Unregister all notify listeners.
|
// Unregister all notify listeners.
|
||||||
notify_listeners_.clear();
|
notify_listeners_.clear();
|
||||||
|
|
||||||
// Clear the TLS map.
|
|
||||||
tls_bitmap_.Reset();
|
|
||||||
|
|
||||||
// Unset the executable module.
|
// Unset the executable module.
|
||||||
executable_module_ = nullptr;
|
executable_module_ = nullptr;
|
||||||
|
|
||||||
@@ -1077,12 +1185,6 @@ bool KernelState::Save(ByteStream* stream) {
|
|||||||
object_table_.Save(stream);
|
object_table_.Save(stream);
|
||||||
|
|
||||||
// Write the TLS allocation bitmap
|
// Write the TLS allocation bitmap
|
||||||
auto tls_bitmap = tls_bitmap_.data();
|
|
||||||
stream->Write(uint32_t(tls_bitmap.size()));
|
|
||||||
for (size_t i = 0; i < tls_bitmap.size(); i++) {
|
|
||||||
stream->Write<uint64_t>(tls_bitmap[i]);
|
|
||||||
}
|
|
||||||
|
|
||||||
// We save XThreads absolutely first, as they will execute code upon save
|
// We save XThreads absolutely first, as they will execute code upon save
|
||||||
// (which could modify the kernel state)
|
// (which could modify the kernel state)
|
||||||
auto threads = object_table_.GetObjectsByType<XThread>();
|
auto threads = object_table_.GetObjectsByType<XThread>();
|
||||||
@@ -1210,12 +1312,11 @@ bool KernelState::Restore(ByteStream* stream) {
|
|||||||
// Restore the object table
|
// Restore the object table
|
||||||
object_table_.Restore(stream);
|
object_table_.Restore(stream);
|
||||||
|
|
||||||
// Read the TLS allocation bitmap
|
// TLS bitmap is now stored per-process in X_KPROCESS structures (in guest
|
||||||
|
// memory) Skip reading old global TLS bitmap if present in old save files
|
||||||
auto num_bitmap_entries = stream->Read<uint32_t>();
|
auto num_bitmap_entries = stream->Read<uint32_t>();
|
||||||
auto& tls_bitmap = tls_bitmap_.data();
|
|
||||||
tls_bitmap.resize(num_bitmap_entries);
|
|
||||||
for (uint32_t i = 0; i < num_bitmap_entries; i++) {
|
for (uint32_t i = 0; i < num_bitmap_entries; i++) {
|
||||||
tls_bitmap[i] = stream->Read<uint64_t>();
|
stream->Read<uint64_t>(); // Discard old data
|
||||||
}
|
}
|
||||||
|
|
||||||
uint32_t num_threads = stream->Read<uint32_t>();
|
uint32_t num_threads = stream->Read<uint32_t>();
|
||||||
@@ -1349,12 +1450,13 @@ void KernelState::SetProcessTLSVars(X_KPROCESS* process, int num_slots,
|
|||||||
process->tls_slot_size = 4 * slots_padded;
|
process->tls_slot_size = 4 * slots_padded;
|
||||||
uint32_t count_div32 = slots_padded / 32;
|
uint32_t count_div32 = slots_padded / 32;
|
||||||
for (unsigned word_index = 0; word_index < count_div32; ++word_index) {
|
for (unsigned word_index = 0; word_index < count_div32; ++word_index) {
|
||||||
process->bitmap[word_index] = -1;
|
process->tls_slot_bitmap[word_index] = -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
// set remainder of bitset
|
// set remainder of bitset
|
||||||
if (((num_slots + 3) & 0x1C) != 0)
|
if (((num_slots + 3) & 0x1C) != 0)
|
||||||
process->bitmap[count_div32] = -1 << (32 - ((num_slots + 3) & 0x1C));
|
process->tls_slot_bitmap[count_div32] = -1
|
||||||
|
<< (32 - ((num_slots + 3) & 0x1C));
|
||||||
}
|
}
|
||||||
void AllocateThread(PPCContext* context) {
|
void AllocateThread(PPCContext* context) {
|
||||||
uint32_t thread_mem_size = static_cast<uint32_t>(context->r[3]);
|
uint32_t thread_mem_size = static_cast<uint32_t>(context->r[3]);
|
||||||
|
|||||||
@@ -80,7 +80,7 @@ struct X_KPROCESS {
|
|||||||
uint8_t is_terminating;
|
uint8_t is_terminating;
|
||||||
// one of X_PROCTYPE_
|
// one of X_PROCTYPE_
|
||||||
uint8_t process_type;
|
uint8_t process_type;
|
||||||
xe::be<uint32_t> bitmap[8];
|
xe::be<uint32_t> tls_slot_bitmap[8];
|
||||||
xe::be<uint32_t> unk_50;
|
xe::be<uint32_t> unk_50;
|
||||||
X_LIST_ENTRY unk_54;
|
X_LIST_ENTRY unk_54;
|
||||||
xe::be<uint32_t> unk_5C;
|
xe::be<uint32_t> unk_5C;
|
||||||
@@ -140,6 +140,7 @@ struct KernelGuestGlobals {
|
|||||||
// this lock is only used in some Ob functions. It's odd that it is used at
|
// this lock is only used in some Ob functions. It's odd that it is used at
|
||||||
// all, as each table already has its own spinlock.
|
// all, as each table already has its own spinlock.
|
||||||
X_KSPINLOCK ob_lock;
|
X_KSPINLOCK ob_lock;
|
||||||
|
X_KSPINLOCK tls_lock; // protects per-process TLS bitmap allocations
|
||||||
|
|
||||||
// if LLE emulating Xam, this is needed or you get an immediate freeze
|
// if LLE emulating Xam, this is needed or you get an immediate freeze
|
||||||
X_KEVENT UsbdBootEnumerationDoneEvent;
|
X_KEVENT UsbdBootEnumerationDoneEvent;
|
||||||
@@ -219,8 +220,8 @@ class KernelState {
|
|||||||
return kernel_guest_globals_ + offsetof(KernelGuestGlobals, idle_process);
|
return kernel_guest_globals_ + offsetof(KernelGuestGlobals, idle_process);
|
||||||
}
|
}
|
||||||
|
|
||||||
uint32_t AllocateTLS();
|
uint32_t AllocateTLS(cpu::ppc::PPCContext* context);
|
||||||
void FreeTLS(uint32_t slot);
|
void FreeTLS(cpu::ppc::PPCContext* context, uint32_t slot);
|
||||||
|
|
||||||
void RegisterTitleTerminateNotification(uint32_t routine, uint32_t priority);
|
void RegisterTitleTerminateNotification(uint32_t routine, uint32_t priority);
|
||||||
void RemoveTitleTerminateNotification(uint32_t routine);
|
void RemoveTitleTerminateNotification(uint32_t routine);
|
||||||
@@ -382,7 +383,6 @@ class KernelState {
|
|||||||
std::condition_variable_any dispatch_cond_;
|
std::condition_variable_any dispatch_cond_;
|
||||||
std::list<std::function<void()>> dispatch_queue_;
|
std::list<std::function<void()>> dispatch_queue_;
|
||||||
|
|
||||||
BitMap tls_bitmap_;
|
|
||||||
uint32_t ke_timestamp_bundle_ptr_ = 0;
|
uint32_t ke_timestamp_bundle_ptr_ = 0;
|
||||||
std::unique_ptr<xe::threading::HighResolutionTimer> timestamp_timer_;
|
std::unique_ptr<xe::threading::HighResolutionTimer> timestamp_timer_;
|
||||||
uint32_t quantum_timer_counter_ = 0;
|
uint32_t quantum_timer_counter_ = 0;
|
||||||
|
|||||||
@@ -469,8 +469,8 @@ void KeQuerySystemTime_entry(lpqword_t time_ptr, const ppc_context_t& ctx) {
|
|||||||
DECLARE_XBOXKRNL_EXPORT1(KeQuerySystemTime, kThreading, kImplemented);
|
DECLARE_XBOXKRNL_EXPORT1(KeQuerySystemTime, kThreading, kImplemented);
|
||||||
|
|
||||||
// https://msdn.microsoft.com/en-us/library/ms686801
|
// https://msdn.microsoft.com/en-us/library/ms686801
|
||||||
dword_result_t KeTlsAlloc_entry() {
|
dword_result_t KeTlsAlloc_entry(const ppc_context_t& context) {
|
||||||
uint32_t slot = kernel_state()->AllocateTLS();
|
uint32_t slot = kernel_state()->AllocateTLS(context);
|
||||||
XThread::GetCurrentThread()->SetTLSValue(slot, 0);
|
XThread::GetCurrentThread()->SetTLSValue(slot, 0);
|
||||||
|
|
||||||
return slot;
|
return slot;
|
||||||
@@ -478,12 +478,13 @@ dword_result_t KeTlsAlloc_entry() {
|
|||||||
DECLARE_XBOXKRNL_EXPORT1(KeTlsAlloc, kThreading, kImplemented);
|
DECLARE_XBOXKRNL_EXPORT1(KeTlsAlloc, kThreading, kImplemented);
|
||||||
|
|
||||||
// https://msdn.microsoft.com/en-us/library/ms686804
|
// https://msdn.microsoft.com/en-us/library/ms686804
|
||||||
dword_result_t KeTlsFree_entry(dword_t tls_index) {
|
dword_result_t KeTlsFree_entry(dword_t tls_index,
|
||||||
|
const ppc_context_t& context) {
|
||||||
if (tls_index == X_TLS_OUT_OF_INDEXES) {
|
if (tls_index == X_TLS_OUT_OF_INDEXES) {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
kernel_state()->FreeTLS(tls_index);
|
kernel_state()->FreeTLS(context, tls_index);
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
DECLARE_XBOXKRNL_EXPORT1(KeTlsFree, kThreading, kImplemented);
|
DECLARE_XBOXKRNL_EXPORT1(KeTlsFree, kThreading, kImplemented);
|
||||||
|
|||||||
Reference in New Issue
Block a user