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) {
|
||||
|
||||
Reference in New Issue
Block a user