Rewriting memory manager.
This commit is contained in:
@@ -23,9 +23,11 @@ namespace cpu {
|
||||
MMIOHandler* MMIOHandler::global_handler_ = nullptr;
|
||||
|
||||
// Implemented in the platform cc file.
|
||||
std::unique_ptr<MMIOHandler> CreateMMIOHandler(uint8_t* mapping_base);
|
||||
std::unique_ptr<MMIOHandler> CreateMMIOHandler(uint8_t* virtual_membase,
|
||||
uint8_t* physical_membase);
|
||||
|
||||
std::unique_ptr<MMIOHandler> MMIOHandler::Install(uint8_t* mapping_base) {
|
||||
std::unique_ptr<MMIOHandler> MMIOHandler::Install(uint8_t* virtual_membase,
|
||||
uint8_t* physical_membase) {
|
||||
// There can be only one handler at a time.
|
||||
assert_null(global_handler_);
|
||||
if (global_handler_) {
|
||||
@@ -33,7 +35,7 @@ std::unique_ptr<MMIOHandler> MMIOHandler::Install(uint8_t* mapping_base) {
|
||||
}
|
||||
|
||||
// Create the platform-specific handler.
|
||||
auto handler = CreateMMIOHandler(mapping_base);
|
||||
auto handler = CreateMMIOHandler(virtual_membase, physical_membase);
|
||||
|
||||
// Platform-specific initialization for the handler.
|
||||
if (!handler->Initialize()) {
|
||||
@@ -49,45 +51,44 @@ MMIOHandler::~MMIOHandler() {
|
||||
global_handler_ = nullptr;
|
||||
}
|
||||
|
||||
bool MMIOHandler::RegisterRange(uint64_t address, uint64_t mask, uint64_t size,
|
||||
void* context, MMIOReadCallback read_callback,
|
||||
bool MMIOHandler::RegisterRange(uint32_t virtual_address, uint32_t mask,
|
||||
uint32_t size, void* context,
|
||||
MMIOReadCallback read_callback,
|
||||
MMIOWriteCallback write_callback) {
|
||||
mapped_ranges_.push_back({
|
||||
reinterpret_cast<uint64_t>(mapping_base_) | address,
|
||||
0xFFFFFFFF00000000ull | mask, size, context, read_callback,
|
||||
write_callback,
|
||||
virtual_address, mask, size, context, read_callback, write_callback,
|
||||
});
|
||||
return true;
|
||||
}
|
||||
|
||||
bool MMIOHandler::CheckLoad(uint64_t address, uint64_t* out_value) {
|
||||
bool MMIOHandler::CheckLoad(uint32_t virtual_address, uint64_t* out_value) {
|
||||
for (const auto& range : mapped_ranges_) {
|
||||
if (((address | (uint64_t)mapping_base_) & range.mask) == range.address) {
|
||||
*out_value = static_cast<uint32_t>(range.read(range.context, address));
|
||||
if ((virtual_address & range.mask) == range.address) {
|
||||
*out_value =
|
||||
static_cast<uint32_t>(range.read(range.context, virtual_address));
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool MMIOHandler::CheckStore(uint64_t address, uint64_t value) {
|
||||
bool MMIOHandler::CheckStore(uint32_t virtual_address, uint64_t value) {
|
||||
for (const auto& range : mapped_ranges_) {
|
||||
if (((address | (uint64_t)mapping_base_) & range.mask) == range.address) {
|
||||
range.write(range.context, address, value);
|
||||
if ((virtual_address & range.mask) == range.address) {
|
||||
range.write(range.context, virtual_address, value);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
uintptr_t MMIOHandler::AddWriteWatch(uint32_t guest_address, size_t length,
|
||||
WriteWatchCallback callback,
|
||||
void* callback_context,
|
||||
void* callback_data) {
|
||||
uintptr_t MMIOHandler::AddPhysicalWriteWatch(uint32_t guest_address,
|
||||
size_t length,
|
||||
WriteWatchCallback callback,
|
||||
void* callback_context,
|
||||
void* callback_data) {
|
||||
uint32_t base_address = guest_address;
|
||||
if (base_address > 0xA0000000) {
|
||||
base_address -= 0xA0000000;
|
||||
}
|
||||
assert_true(base_address < 0x1FFFFFFF);
|
||||
|
||||
// Add to table. The slot reservation may evict a previous watch, which
|
||||
// could include our target, so we do it first.
|
||||
@@ -102,29 +103,33 @@ uintptr_t MMIOHandler::AddWriteWatch(uint32_t guest_address, size_t length,
|
||||
write_watch_mutex_.unlock();
|
||||
|
||||
// Make the desired range read only under all address spaces.
|
||||
auto host_address = mapping_base_ + base_address;
|
||||
DWORD old_protect;
|
||||
VirtualProtect(host_address, length, PAGE_READONLY, &old_protect);
|
||||
VirtualProtect(host_address + 0xA0000000, length, PAGE_READONLY,
|
||||
&old_protect);
|
||||
VirtualProtect(host_address + 0xC0000000, length, PAGE_READONLY,
|
||||
&old_protect);
|
||||
VirtualProtect(host_address + 0xE0000000, length, PAGE_READONLY,
|
||||
&old_protect);
|
||||
VirtualProtect(physical_membase_ + entry->address, entry->length,
|
||||
PAGE_READONLY, &old_protect);
|
||||
VirtualProtect(virtual_membase_ + entry->address, entry->length,
|
||||
PAGE_READONLY, &old_protect);
|
||||
VirtualProtect(virtual_membase_ + 0xA0000000 + entry->address, entry->length,
|
||||
PAGE_READONLY, &old_protect);
|
||||
VirtualProtect(virtual_membase_ + 0xC0000000 + entry->address, entry->length,
|
||||
PAGE_READONLY, &old_protect);
|
||||
VirtualProtect(virtual_membase_ + 0xE0000000 + entry->address, entry->length,
|
||||
PAGE_READONLY, &old_protect);
|
||||
|
||||
return reinterpret_cast<uintptr_t>(entry);
|
||||
}
|
||||
|
||||
void MMIOHandler::ClearWriteWatch(WriteWatchEntry* entry) {
|
||||
auto host_address = mapping_base_ + entry->address;
|
||||
DWORD old_protect;
|
||||
VirtualProtect(host_address, entry->length, PAGE_READWRITE, &old_protect);
|
||||
VirtualProtect(host_address + 0xA0000000, entry->length, PAGE_READWRITE,
|
||||
&old_protect);
|
||||
VirtualProtect(host_address + 0xC0000000, entry->length, PAGE_READWRITE,
|
||||
&old_protect);
|
||||
VirtualProtect(host_address + 0xE0000000, entry->length, PAGE_READWRITE,
|
||||
&old_protect);
|
||||
VirtualProtect(physical_membase_ + entry->address, entry->length,
|
||||
PAGE_READWRITE, &old_protect);
|
||||
VirtualProtect(virtual_membase_ + entry->address, entry->length,
|
||||
PAGE_READWRITE, &old_protect);
|
||||
VirtualProtect(virtual_membase_ + 0xA0000000 + entry->address, entry->length,
|
||||
PAGE_READWRITE, &old_protect);
|
||||
VirtualProtect(virtual_membase_ + 0xC0000000 + entry->address, entry->length,
|
||||
PAGE_READWRITE, &old_protect);
|
||||
VirtualProtect(virtual_membase_ + 0xE0000000 + entry->address, entry->length,
|
||||
PAGE_READWRITE, &old_protect);
|
||||
}
|
||||
|
||||
void MMIOHandler::CancelWriteWatch(uintptr_t watch_handle) {
|
||||
@@ -145,17 +150,16 @@ void MMIOHandler::CancelWriteWatch(uintptr_t watch_handle) {
|
||||
}
|
||||
|
||||
bool MMIOHandler::CheckWriteWatch(void* thread_state, uint64_t fault_address) {
|
||||
uint32_t guest_address = uint32_t(fault_address - uintptr_t(mapping_base_));
|
||||
uint32_t base_address = guest_address;
|
||||
if (base_address > 0xA0000000) {
|
||||
base_address -= 0xA0000000;
|
||||
uint32_t physical_address = uint32_t(fault_address);
|
||||
if (physical_address > 0x1FFFFFFF) {
|
||||
physical_address &= 0x1FFFFFFF;
|
||||
}
|
||||
std::list<WriteWatchEntry*> pending_invalidates;
|
||||
write_watch_mutex_.lock();
|
||||
for (auto it = write_watches_.begin(); it != write_watches_.end();) {
|
||||
auto entry = *it;
|
||||
if (entry->address <= base_address &&
|
||||
entry->address + entry->length > base_address) {
|
||||
if (entry->address <= physical_address &&
|
||||
entry->address + entry->length > physical_address) {
|
||||
// Hit!
|
||||
pending_invalidates.push_back(entry);
|
||||
// TODO(benvanik): outside of lock?
|
||||
@@ -176,7 +180,7 @@ bool MMIOHandler::CheckWriteWatch(void* thread_state, uint64_t fault_address) {
|
||||
auto entry = pending_invalidates.back();
|
||||
pending_invalidates.pop_back();
|
||||
entry->callback(entry->callback_context, entry->callback_data,
|
||||
guest_address);
|
||||
physical_address);
|
||||
delete entry;
|
||||
}
|
||||
// Range was watched, so lets eat this access violation.
|
||||
@@ -185,18 +189,21 @@ bool MMIOHandler::CheckWriteWatch(void* thread_state, uint64_t fault_address) {
|
||||
|
||||
bool MMIOHandler::HandleAccessFault(void* thread_state,
|
||||
uint64_t fault_address) {
|
||||
if (fault_address < uint64_t(mapping_base_)) {
|
||||
if (fault_address < uint64_t(virtual_membase_)) {
|
||||
// Quick kill anything below our mapping base.
|
||||
return false;
|
||||
}
|
||||
|
||||
// Access violations are pretty rare, so we can do a linear search here.
|
||||
// Only check if in the virtual range, as we only support virtual ranges.
|
||||
const MMIORange* range = nullptr;
|
||||
for (const auto& test_range : mapped_ranges_) {
|
||||
if ((fault_address & test_range.mask) == test_range.address) {
|
||||
// Address is within the range of this mapping.
|
||||
range = &test_range;
|
||||
break;
|
||||
if (fault_address < uint64_t(physical_membase_)) {
|
||||
for (const auto& test_range : mapped_ranges_) {
|
||||
if ((uint32_t(fault_address) & test_range.mask) == test_range.address) {
|
||||
// Address is within the range of this mapping.
|
||||
range = &test_range;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!range) {
|
||||
|
||||
@@ -18,8 +18,8 @@
|
||||
namespace xe {
|
||||
namespace cpu {
|
||||
|
||||
typedef uint64_t (*MMIOReadCallback)(void* context, uint64_t addr);
|
||||
typedef void (*MMIOWriteCallback)(void* context, uint64_t addr, uint64_t value);
|
||||
typedef uint64_t (*MMIOReadCallback)(void* context, uint32_t addr);
|
||||
typedef void (*MMIOWriteCallback)(void* context, uint32_t addr, uint64_t value);
|
||||
|
||||
typedef void (*WriteWatchCallback)(void* context_ptr, void* data_ptr,
|
||||
uint32_t address);
|
||||
@@ -29,19 +29,20 @@ class MMIOHandler {
|
||||
public:
|
||||
virtual ~MMIOHandler();
|
||||
|
||||
static std::unique_ptr<MMIOHandler> Install(uint8_t* mapping_base);
|
||||
static std::unique_ptr<MMIOHandler> Install(uint8_t* virtual_membase,
|
||||
uint8_t* physical_membase);
|
||||
static MMIOHandler* global_handler() { return global_handler_; }
|
||||
|
||||
bool RegisterRange(uint64_t address, uint64_t mask, uint64_t size,
|
||||
bool RegisterRange(uint32_t virtual_address, uint32_t mask, uint32_t size,
|
||||
void* context, MMIOReadCallback read_callback,
|
||||
MMIOWriteCallback write_callback);
|
||||
|
||||
bool CheckLoad(uint64_t address, uint64_t* out_value);
|
||||
bool CheckStore(uint64_t address, uint64_t value);
|
||||
bool CheckLoad(uint32_t virtual_address, uint64_t* out_value);
|
||||
bool CheckStore(uint32_t virtual_address, uint64_t value);
|
||||
|
||||
uintptr_t AddWriteWatch(uint32_t guest_address, size_t length,
|
||||
WriteWatchCallback callback, void* callback_context,
|
||||
void* callback_data);
|
||||
uintptr_t AddPhysicalWriteWatch(uint32_t guest_address, size_t length,
|
||||
WriteWatchCallback callback,
|
||||
void* callback_context, void* callback_data);
|
||||
void CancelWriteWatch(uintptr_t watch_handle);
|
||||
|
||||
public:
|
||||
@@ -56,7 +57,9 @@ class MMIOHandler {
|
||||
void* callback_data;
|
||||
};
|
||||
|
||||
MMIOHandler(uint8_t* mapping_base) : mapping_base_(mapping_base) {}
|
||||
MMIOHandler(uint8_t* virtual_membase, uint8_t* physical_membase)
|
||||
: virtual_membase_(virtual_membase),
|
||||
physical_membase_(physical_membase) {}
|
||||
|
||||
virtual bool Initialize() = 0;
|
||||
|
||||
@@ -68,12 +71,13 @@ class MMIOHandler {
|
||||
virtual uint64_t* GetThreadStateRegPtr(void* thread_state_ptr,
|
||||
int32_t be_reg_index) = 0;
|
||||
|
||||
uint8_t* mapping_base_;
|
||||
uint8_t* virtual_membase_;
|
||||
uint8_t* physical_membase_;
|
||||
|
||||
struct MMIORange {
|
||||
uint64_t address;
|
||||
uint64_t mask;
|
||||
uint64_t size;
|
||||
uint32_t address;
|
||||
uint32_t mask;
|
||||
uint32_t size;
|
||||
void* context;
|
||||
MMIOReadCallback read;
|
||||
MMIOWriteCallback write;
|
||||
|
||||
@@ -11,6 +11,10 @@
|
||||
|
||||
#include <Windows.h>
|
||||
|
||||
namespace xe {
|
||||
void CrashDump();
|
||||
} // namespace xe
|
||||
|
||||
namespace xe {
|
||||
namespace cpu {
|
||||
|
||||
@@ -18,7 +22,8 @@ LONG CALLBACK MMIOExceptionHandler(PEXCEPTION_POINTERS ex_info);
|
||||
|
||||
class WinMMIOHandler : public MMIOHandler {
|
||||
public:
|
||||
WinMMIOHandler(uint8_t* mapping_base) : MMIOHandler(mapping_base) {}
|
||||
WinMMIOHandler(uint8_t* virtual_membase, uint8_t* physical_membase)
|
||||
: MMIOHandler(virtual_membase, physical_membase) {}
|
||||
~WinMMIOHandler() override;
|
||||
|
||||
protected:
|
||||
@@ -30,8 +35,9 @@ class WinMMIOHandler : public MMIOHandler {
|
||||
int32_t be_reg_index) override;
|
||||
};
|
||||
|
||||
std::unique_ptr<MMIOHandler> CreateMMIOHandler(uint8_t* mapping_base) {
|
||||
return std::make_unique<WinMMIOHandler>(mapping_base);
|
||||
std::unique_ptr<MMIOHandler> CreateMMIOHandler(uint8_t* virtual_membase,
|
||||
uint8_t* physical_membase) {
|
||||
return std::make_unique<WinMMIOHandler>(virtual_membase, physical_membase);
|
||||
}
|
||||
|
||||
bool WinMMIOHandler::Initialize() {
|
||||
@@ -67,6 +73,7 @@ LONG CALLBACK MMIOExceptionHandler(PEXCEPTION_POINTERS ex_info) {
|
||||
} else {
|
||||
// Failed to handle; continue search for a handler (and die if no other
|
||||
// handler is found).
|
||||
xe::CrashDump();
|
||||
return EXCEPTION_CONTINUE_SEARCH;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -152,6 +152,11 @@ bool Processor::Setup() {
|
||||
interrupt_thread_state_->set_name("Interrupt");
|
||||
interrupt_thread_block_ = memory_->SystemHeapAlloc(2048);
|
||||
interrupt_thread_state_->context()->r[13] = interrupt_thread_block_;
|
||||
XELOGI("Interrupt Thread %X Stack: %.8X-%.8X",
|
||||
interrupt_thread_state_->thread_id(),
|
||||
interrupt_thread_state_->stack_address(),
|
||||
interrupt_thread_state_->stack_address() +
|
||||
interrupt_thread_state_->stack_size());
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -30,8 +30,11 @@ bool RawModule::LoadFile(uint32_t base_address, const std::wstring& path) {
|
||||
// Allocate memory.
|
||||
// Since we have no real heap just load it wherever.
|
||||
base_address_ = base_address;
|
||||
memory_->LookupHeap(base_address_)
|
||||
->AllocFixed(base_address_, file_length, 0,
|
||||
kMemoryAllocationReserve | kMemoryAllocationCommit,
|
||||
kMemoryProtectRead | kMemoryProtectWrite);
|
||||
uint8_t* p = memory_->TranslateVirtual(base_address_);
|
||||
std::memset(p, 0, file_length);
|
||||
|
||||
// Read into memory.
|
||||
fread(p, file_length, 1, file);
|
||||
|
||||
@@ -64,8 +64,6 @@ class TestFunction {
|
||||
void Run(std::function<void(PPCContext*)> pre_call,
|
||||
std::function<void(PPCContext*)> post_call) {
|
||||
for (auto& processor : processors) {
|
||||
memory->Zero(0, memory_size);
|
||||
|
||||
xe::cpu::Function* fn;
|
||||
processor->ResolveFunction(0x1000, &fn);
|
||||
|
||||
|
||||
@@ -10,6 +10,7 @@
|
||||
#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"
|
||||
@@ -49,12 +50,19 @@ ThreadState::ThreadState(Processor* processor, uint32_t thread_id,
|
||||
uint32_t stack_alignment = (stack_size & 0xF000) ? 0x1000 : 0x10000;
|
||||
uint32_t stack_padding = stack_alignment * 1;
|
||||
uint32_t actual_stack_size = stack_padding + stack_size;
|
||||
stack_address_ = memory()->SystemHeapAlloc(actual_stack_size, stack_alignment);
|
||||
assert_true(!(stack_address & 0xFFF)); // just to be safe
|
||||
memory()
|
||||
->LookupHeapByType(false, 0x10000)
|
||||
->Alloc(actual_stack_size, stack_alignment,
|
||||
kMemoryAllocationReserve | kMemoryAllocationCommit,
|
||||
kMemoryProtectRead | kMemoryProtectWrite, true,
|
||||
&stack_address_);
|
||||
assert_true(!(stack_address_ & 0xFFF)); // just to be safe
|
||||
stack_position = stack_address_ + actual_stack_size;
|
||||
stack_allocated_ = true;
|
||||
memset(memory()->TranslateVirtual(stack_address_), 0xBE, actual_stack_size);
|
||||
memory()->Protect(stack_address_, stack_padding, X_PAGE_NOACCESS);
|
||||
memory()
|
||||
->LookupHeap(stack_address_)
|
||||
->Protect(stack_address_, stack_padding, kMemoryProtectNoAccess);
|
||||
} else {
|
||||
stack_address_ = stack_address;
|
||||
stack_position = stack_address_ + stack_size;
|
||||
@@ -100,7 +108,7 @@ ThreadState::~ThreadState() {
|
||||
|
||||
_aligned_free(context_);
|
||||
if (stack_allocated_) {
|
||||
memory()->SystemHeapFree(stack_address_);
|
||||
memory()->LookupHeap(stack_address_)->Decommit(stack_address_, stack_size_);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user