Files
Xenia-Canary/src/xenia/cpu/thread_state.cc
2015-05-18 20:47:40 -07:00

137 lines
4.5 KiB
C++

/**
******************************************************************************
* Xenia : Xbox 360 Emulator Research Project *
******************************************************************************
* Copyright 2013 Ben Vanik. All rights reserved. *
* Released under the BSD license - see LICENSE in the root for more details. *
******************************************************************************
*/
#include "xenia/cpu/thread_state.h"
#include "xenia/base/assert.h"
#include "xenia/base/logging.h"
#include "xenia/base/threading.h"
#include "xenia/cpu/processor.h"
#include "xenia/debug/debugger.h"
#include "xenia/xbox.h"
namespace xe {
namespace cpu {
using namespace xe::cpu;
using PPCContext = xe::cpu::frontend::PPCContext;
thread_local ThreadState* thread_state_ = nullptr;
ThreadState::ThreadState(Processor* processor, uint32_t thread_id,
ThreadStackType stack_type, uint32_t stack_address,
uint32_t stack_size, uint32_t pcr_address)
: processor_(processor),
memory_(processor->memory()),
thread_id_(thread_id),
stack_type_(stack_type),
name_(""),
backend_data_(0),
stack_size_(stack_size),
pcr_address_(pcr_address) {
if (thread_id_ == UINT_MAX) {
// System thread. Assign the system thread ID with a high bit
// set so people know what's up.
uint32_t system_thread_handle = xe::threading::current_thread_id();
thread_id_ = 0x80000000 | system_thread_handle;
}
backend_data_ = processor->backend()->AllocThreadData();
if (!stack_address) {
// We must always allocate 64K as a guard region before stacks, as we can
// only Protect() on system page granularity.
stack_size = (stack_size + 0xFFF) & 0xFFFFF000;
uint32_t stack_alignment = (stack_size & 0xF000) ? 0x1000 : 0x10000;
uint32_t stack_padding = uint32_t(xe::page_size()); // Host page size.
uint32_t actual_stack_size = stack_padding + stack_size;
bool top_down;
switch (stack_type) {
case ThreadStackType::kKernelStack:
top_down = true;
break;
case ThreadStackType::kUserStack:
top_down = false;
break;
default:
assert_unhandled_case(stack_type);
break;
}
memory()
->LookupHeap(0x70000000)
->AllocRange(0x70000000, 0x7FFFFFFF, actual_stack_size, stack_alignment,
kMemoryAllocationReserve | kMemoryAllocationCommit,
kMemoryProtectRead | kMemoryProtectWrite, top_down,
&stack_address_);
assert_true(!(stack_address_ & 0xFFF)); // just to be safe
stack_allocated_ = true;
stack_base_ = stack_address_ + actual_stack_size;
stack_limit_ = stack_address_ + stack_padding;
memory()->Fill(stack_address_, actual_stack_size, 0xBE);
memory()
->LookupHeap(stack_address_)
->Protect(stack_address_, stack_padding, kMemoryProtectNoAccess);
} else {
stack_address_ = stack_address;
stack_allocated_ = false;
stack_base_ = stack_address_ + stack_size;
stack_limit_ = stack_address_;
}
assert_not_zero(stack_address_);
// Allocate with 64b alignment.
context_ =
reinterpret_cast<PPCContext*>(_aligned_malloc(sizeof(PPCContext), 64));
assert_true(((uint64_t)context_ & 0x3F) == 0);
std::memset(context_, 0, sizeof(PPCContext));
// Stash pointers to common structures that callbacks may need.
context_->reserve_address = memory_->reserve_address();
context_->reserve_value = memory_->reserve_value();
context_->virtual_membase = memory_->virtual_membase();
context_->physical_membase = memory_->physical_membase();
context_->processor = processor_;
context_->thread_state = this;
context_->thread_id = thread_id_;
// Set initial registers.
context_->r[1] = stack_base_;
context_->r[13] = pcr_address_;
processor_->debugger()->OnThreadCreated(this);
}
ThreadState::~ThreadState() {
processor_->debugger()->OnThreadDestroyed(this);
if (backend_data_) {
processor_->backend()->FreeThreadData(backend_data_);
}
if (thread_state_ == this) {
thread_state_ = nullptr;
}
_aligned_free(context_);
if (stack_allocated_) {
memory()->LookupHeap(stack_address_)->Decommit(stack_address_, stack_size_);
}
}
void ThreadState::Bind(ThreadState* thread_state) {
thread_state_ = thread_state;
}
ThreadState* ThreadState::Get() { return thread_state_; }
uint32_t ThreadState::GetThreadID() { return thread_state_->thread_id_; }
} // namespace cpu
} // namespace xe