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:
parent
257fbfc408
commit
c4b728b121
|
@ -104,11 +104,45 @@ uintptr_t MMIOHandler::AddPhysicalAccessWatch(uint32_t guest_address,
|
||||||
|
|
||||||
auto lock = global_critical_region_.Acquire();
|
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
|
// Add to table. The slot reservation may evict a previous watch, which
|
||||||
// could include our target, so we do it first.
|
// could include our target, so we do it first.
|
||||||
auto entry = new AccessWatchEntry();
|
auto entry = new AccessWatchEntry();
|
||||||
entry->address = base_address;
|
entry->address = base_address;
|
||||||
entry->length = uint32_t(length);
|
entry->length = uint32_t(length);
|
||||||
|
entry->type = type;
|
||||||
entry->callback = callback;
|
entry->callback = callback;
|
||||||
entry->callback_context = callback_context;
|
entry->callback_context = callback_context;
|
||||||
entry->callback_data = callback_data;
|
entry->callback_data = callback_data;
|
||||||
|
@ -140,6 +174,12 @@ uintptr_t MMIOHandler::AddPhysicalAccessWatch(uint32_t guest_address,
|
||||||
return reinterpret_cast<uintptr_t>(entry);
|
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) {
|
void MMIOHandler::ClearAccessWatch(AccessWatchEntry* entry) {
|
||||||
memory::Protect(physical_membase_ + entry->address, entry->length,
|
memory::Protect(physical_membase_ + entry->address, entry->length,
|
||||||
xe::memory::PageAccess::kReadWrite, nullptr);
|
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 &&
|
||||||
entry->address < physical_address + length)) {
|
entry->address < physical_address + length)) {
|
||||||
// This watch lies within the range. End it.
|
// This watch lies within the range. End it.
|
||||||
ClearAccessWatch(entry);
|
FireAccessWatch(entry);
|
||||||
entry->callback(entry->callback_context, entry->callback_data,
|
|
||||||
entry->address);
|
|
||||||
|
|
||||||
it = access_watches_.erase(it);
|
it = access_watches_.erase(it);
|
||||||
delete entry;
|
delete entry;
|
||||||
continue;
|
continue;
|
||||||
|
@ -219,10 +256,7 @@ bool MMIOHandler::CheckAccessWatch(uint32_t physical_address) {
|
||||||
entry->address + entry->length > physical_address) {
|
entry->address + entry->length > physical_address) {
|
||||||
// Hit! Remove the watch.
|
// Hit! Remove the watch.
|
||||||
hit = true;
|
hit = true;
|
||||||
ClearAccessWatch(entry);
|
FireAccessWatch(entry);
|
||||||
entry->callback(entry->callback_context, entry->callback_data,
|
|
||||||
physical_address);
|
|
||||||
|
|
||||||
it = access_watches_.erase(it);
|
it = access_watches_.erase(it);
|
||||||
delete entry;
|
delete entry;
|
||||||
continue;
|
continue;
|
||||||
|
|
|
@ -95,6 +95,7 @@ class MMIOHandler {
|
||||||
static bool ExceptionCallbackThunk(Exception* ex, void* data);
|
static bool ExceptionCallbackThunk(Exception* ex, void* data);
|
||||||
bool ExceptionCallback(Exception* ex);
|
bool ExceptionCallback(Exception* ex);
|
||||||
|
|
||||||
|
void FireAccessWatch(AccessWatchEntry* entry);
|
||||||
void ClearAccessWatch(AccessWatchEntry* entry);
|
void ClearAccessWatch(AccessWatchEntry* entry);
|
||||||
bool CheckAccessWatch(uint32_t guest_address);
|
bool CheckAccessWatch(uint32_t guest_address);
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue