diff --git a/src/xenia/cpu/mmio_handler.cc b/src/xenia/cpu/mmio_handler.cc index 980ff12f1..e5412d8e7 100644 --- a/src/xenia/cpu/mmio_handler.cc +++ b/src/xenia/cpu/mmio_handler.cc @@ -156,8 +156,29 @@ void MMIOHandler::CancelWriteWatch(uintptr_t watch_handle) { delete entry; } -bool MMIOHandler::CheckWriteWatch(X64Context* thread_context, - uint64_t fault_address) { +void MMIOHandler::InvalidateRange(uint32_t physical_address, size_t length) { + auto lock = global_critical_region_.Acquire(); + + for (auto it = write_watches_.begin(); it != write_watches_.end();) { + auto entry = *it; + if ((entry->address <= physical_address && + entry->address + entry->length > physical_address) || + (entry->address >= physical_address && + entry->address < physical_address + length)) { + // This watch lies within the range. End it. + ClearWriteWatch(entry); + entry->callback(entry->callback_context, entry->callback_data, + entry->address); + + it = write_watches_.erase(it); + continue; + } + + ++it; + } +} + +bool MMIOHandler::CheckWriteWatch(uint64_t fault_address) { uint32_t physical_address = uint32_t(fault_address); if (physical_address > 0x1FFFFFFF) { physical_address &= 0x1FFFFFFF; @@ -395,7 +416,7 @@ bool MMIOHandler::ExceptionCallback(Exception* ex) { if (!range) { // Access is not found within any range, so fail and let the caller handle // it (likely by aborting). - return CheckWriteWatch(ex->thread_context(), ex->fault_address()); + return CheckWriteWatch(ex->fault_address()); } auto rip = ex->pc(); diff --git a/src/xenia/cpu/mmio_handler.h b/src/xenia/cpu/mmio_handler.h index 4151da5bb..70d89ac02 100644 --- a/src/xenia/cpu/mmio_handler.h +++ b/src/xenia/cpu/mmio_handler.h @@ -63,6 +63,7 @@ class MMIOHandler { WriteWatchCallback callback, void* callback_context, void* callback_data); void CancelWriteWatch(uintptr_t watch_handle); + void InvalidateRange(uint32_t physical_address, size_t length); protected: struct WriteWatchEntry { @@ -83,7 +84,7 @@ class MMIOHandler { bool ExceptionCallback(Exception* ex); void ClearWriteWatch(WriteWatchEntry* entry); - bool CheckWriteWatch(X64Context* thread_context, uint64_t fault_address); + bool CheckWriteWatch(uint64_t fault_address); uint8_t* virtual_membase_; uint8_t* physical_membase_; diff --git a/src/xenia/memory.cc b/src/xenia/memory.cc index 7485ece77..6b13ecee6 100644 --- a/src/xenia/memory.cc +++ b/src/xenia/memory.cc @@ -1128,21 +1128,30 @@ bool PhysicalHeap::Decommit(uint32_t address, uint32_t size) { bool PhysicalHeap::Release(uint32_t base_address, uint32_t* out_region_size) { auto global_lock = global_critical_region_.Acquire(); uint32_t parent_base_address = GetPhysicalAddress(base_address); + uint32_t region_size = 0; + if (QuerySize(base_address, ®ion_size)) { + cpu::MMIOHandler::global_handler()->InvalidateRange(parent_base_address, + region_size); + } + if (!parent_heap_->Release(parent_base_address, out_region_size)) { XELOGE("PhysicalHeap::Release failed due to parent heap failure"); return false; } + return BaseHeap::Release(base_address, out_region_size); } bool PhysicalHeap::Protect(uint32_t address, uint32_t size, uint32_t protect) { auto global_lock = global_critical_region_.Acquire(); uint32_t parent_address = GetPhysicalAddress(address); - bool parent_result = parent_heap_->Protect(parent_address, size, protect); - if (!parent_result) { + cpu::MMIOHandler::global_handler()->InvalidateRange(parent_address, size); + + if (!parent_heap_->Protect(parent_address, size, protect)) { XELOGE("PhysicalHeap::Protect failed due to parent heap failure"); return false; } + return BaseHeap::Protect(address, size, protect); }