MMIO Handler: Don't allow more than one watch to occupy the same region (fire old watches if a new one overlaps)

This commit is contained in:
Dr. Chat 2017-03-11 20:55:01 -06:00
parent 257fbfc408
commit c4b728b121
2 changed files with 43 additions and 8 deletions

View File

@ -104,11 +104,45 @@ uintptr_t MMIOHandler::AddPhysicalAccessWatch(uint32_t guest_address,
auto lock = global_critical_region_.Acquire();
// Fire any access watches that overlap this region.
for (auto it = access_watches_.begin(); it != access_watches_.end();) {
// Case 1: 2222222|222|11111111
// Case 2: 1111111|222|22222222
// Case 3: 1111111|222|11111111 (fragmentation)
// Case 4: 2222222|222|22222222 (complete overlap)
bool hit = false;
auto entry = *it;
if (base_address < (*it)->address &&
base_address + length > (*it)->address) {
hit = true;
} else if ((*it)->address < base_address &&
(*it)->address + (*it)->length > base_address) {
hit = true;
} else if ((*it)->address < base_address &&
(*it)->address + (*it)->length > base_address + length) {
hit = true;
} else if ((*it)->address > base_address &&
(*it)->address + (*it)->length < base_address + length) {
hit = true;
}
if (hit) {
FireAccessWatch(*it);
it = access_watches_.erase(it);
delete entry;
continue;
}
++it;
}
// Add to table. The slot reservation may evict a previous watch, which
// could include our target, so we do it first.
auto entry = new AccessWatchEntry();
entry->address = base_address;
entry->length = uint32_t(length);
entry->type = type;
entry->callback = callback;
entry->callback_context = callback_context;
entry->callback_data = callback_data;
@ -140,6 +174,12 @@ uintptr_t MMIOHandler::AddPhysicalAccessWatch(uint32_t guest_address,
return reinterpret_cast<uintptr_t>(entry);
}
void MMIOHandler::FireAccessWatch(AccessWatchEntry* entry) {
ClearAccessWatch(entry);
entry->callback(entry->callback_context, entry->callback_data,
entry->address);
}
void MMIOHandler::ClearAccessWatch(AccessWatchEntry* entry) {
memory::Protect(physical_membase_ + entry->address, entry->length,
xe::memory::PageAccess::kReadWrite, nullptr);
@ -179,10 +219,7 @@ void MMIOHandler::InvalidateRange(uint32_t physical_address, size_t length) {
(entry->address >= physical_address &&
entry->address < physical_address + length)) {
// This watch lies within the range. End it.
ClearAccessWatch(entry);
entry->callback(entry->callback_context, entry->callback_data,
entry->address);
FireAccessWatch(entry);
it = access_watches_.erase(it);
delete entry;
continue;
@ -219,10 +256,7 @@ bool MMIOHandler::CheckAccessWatch(uint32_t physical_address) {
entry->address + entry->length > physical_address) {
// Hit! Remove the watch.
hit = true;
ClearAccessWatch(entry);
entry->callback(entry->callback_context, entry->callback_data,
physical_address);
FireAccessWatch(entry);
it = access_watches_.erase(it);
delete entry;
continue;

View File

@ -95,6 +95,7 @@ class MMIOHandler {
static bool ExceptionCallbackThunk(Exception* ex, void* data);
bool ExceptionCallback(Exception* ex);
void FireAccessWatch(AccessWatchEntry* entry);
void ClearAccessWatch(AccessWatchEntry* entry);
bool CheckAccessWatch(uint32_t guest_address);