From 45443f24ec6624a46abf7363c9ee962666dc5e5e Mon Sep 17 00:00:00 2001 From: Ben Wiederhake Date: Sun, 7 Mar 2021 22:37:38 +0100 Subject: [PATCH] UserspaceEmulator: Enable splitting regions at arbitrary points This is not yet useful in and of itself, but enables the feature in the next commit. --- .../DevTools/UserspaceEmulator/MmapRegion.cpp | 13 ++++++++ .../DevTools/UserspaceEmulator/MmapRegion.h | 6 ++++ Userland/DevTools/UserspaceEmulator/Range.cpp | 2 +- Userland/DevTools/UserspaceEmulator/Range.h | 13 +++++++- Userland/DevTools/UserspaceEmulator/Region.h | 1 + .../DevTools/UserspaceEmulator/SoftMMU.cpp | 32 +++++++++++++++++++ Userland/DevTools/UserspaceEmulator/SoftMMU.h | 1 + 7 files changed, 66 insertions(+), 2 deletions(-) diff --git a/Userland/DevTools/UserspaceEmulator/MmapRegion.cpp b/Userland/DevTools/UserspaceEmulator/MmapRegion.cpp index c70c39d064..4661feb656 100644 --- a/Userland/DevTools/UserspaceEmulator/MmapRegion.cpp +++ b/Userland/DevTools/UserspaceEmulator/MmapRegion.cpp @@ -223,6 +223,19 @@ void MmapRegion::write64(u32 offset, ValueWithShadow value) *reinterpret_cast(m_shadow_data + offset) = value.shadow(); } +NonnullOwnPtr MmapRegion::split_at(VirtualAddress offset) +{ + VERIFY(!m_malloc); + VERIFY(!m_malloc_metadata); + Range new_range = range(); + Range other_range = new_range.split_at(offset); + auto other_region = adopt_own(*new MmapRegion(other_range.base().get(), other_range.size(), prot(), data() + new_range.size(), shadow_data() + new_range.size())); + other_region->m_file_backed = m_file_backed; + other_region->m_name = m_name; + set_range(new_range); + return other_region; +} + void MmapRegion::set_prot(int prot) { set_readable(prot & PROT_READ); diff --git a/Userland/DevTools/UserspaceEmulator/MmapRegion.h b/Userland/DevTools/UserspaceEmulator/MmapRegion.h index 7820c94b9d..3c0c76ec7e 100644 --- a/Userland/DevTools/UserspaceEmulator/MmapRegion.h +++ b/Userland/DevTools/UserspaceEmulator/MmapRegion.h @@ -56,6 +56,12 @@ public: bool is_malloc_block() const { return m_malloc; } void set_malloc(bool b) { m_malloc = b; } + NonnullOwnPtr split_at(VirtualAddress); + + int prot() const + { + return (is_readable() ? PROT_READ : 0) | (is_writable() ? PROT_WRITE : 0) | (is_executable() ? PROT_EXEC : 0); + } void set_prot(int prot); MallocRegionMetadata* malloc_metadata() { return m_malloc_metadata; } diff --git a/Userland/DevTools/UserspaceEmulator/Range.cpp b/Userland/DevTools/UserspaceEmulator/Range.cpp index 635e8cf8c6..d5fe81aad8 100644 --- a/Userland/DevTools/UserspaceEmulator/Range.cpp +++ b/Userland/DevTools/UserspaceEmulator/Range.cpp @@ -29,7 +29,7 @@ namespace UserspaceEmulator { -Vector Range::carve(const Range& taken) +Vector Range::carve(const Range& taken) const { VERIFY((taken.size() % PAGE_SIZE) == 0); Vector parts; diff --git a/Userland/DevTools/UserspaceEmulator/Range.h b/Userland/DevTools/UserspaceEmulator/Range.h index 08a8e9caaf..0102e687fe 100644 --- a/Userland/DevTools/UserspaceEmulator/Range.h +++ b/Userland/DevTools/UserspaceEmulator/Range.h @@ -67,7 +67,18 @@ public: return contains(other.base(), other.size()); } - Vector carve(const Range&); + Vector carve(const Range&) const; + + Range split_at(VirtualAddress address) + { + VERIFY(address.is_page_aligned()); + VERIFY(m_base < address); + size_t new_size = (address - m_base).get(); + VERIFY(new_size < m_size); + size_t other_size = m_size - new_size; + m_size = new_size; + return { address, other_size }; + } private: VirtualAddress m_base; diff --git a/Userland/DevTools/UserspaceEmulator/Region.h b/Userland/DevTools/UserspaceEmulator/Region.h index 589f7b77a7..f903b2387f 100644 --- a/Userland/DevTools/UserspaceEmulator/Region.h +++ b/Userland/DevTools/UserspaceEmulator/Region.h @@ -81,6 +81,7 @@ public: protected: Region(u32 base, u32 size); + void set_range(Range r) { m_range = r; }; private: Emulator& m_emulator; diff --git a/Userland/DevTools/UserspaceEmulator/SoftMMU.cpp b/Userland/DevTools/UserspaceEmulator/SoftMMU.cpp index af835347e6..7d42a8eb24 100644 --- a/Userland/DevTools/UserspaceEmulator/SoftMMU.cpp +++ b/Userland/DevTools/UserspaceEmulator/SoftMMU.cpp @@ -61,6 +61,38 @@ void SoftMMU::remove_region(Region& region) m_regions.remove_first_matching([&](auto& entry) { return entry.ptr() == ®ion; }); } +void SoftMMU::ensure_split_at(X86::LogicalAddress address) +{ + // FIXME: If this fails, call Emulator::dump_backtrace + VERIFY(address.selector() != 0x2b); + + u32 offset = address.offset(); + VERIFY((offset & (PAGE_SIZE - 1)) == 0); + size_t page_index = address.offset() / PAGE_SIZE; + + if (!page_index) + return; + if (m_page_to_region_map[page_index - 1] != m_page_to_region_map[page_index]) + return; + if (!m_page_to_region_map[page_index]) + return; + + // If we get here, we know that the page exists and belongs to a region, that there is + // a previous page, and that it belongs to the same region. + VERIFY(is(m_page_to_region_map[page_index])); + MmapRegion* old_region = static_cast(m_page_to_region_map[page_index]); + NonnullOwnPtr new_region = old_region->split_at(VirtualAddress(offset)); + + size_t first_page_in_region = new_region->base() / PAGE_SIZE; + size_t last_page_in_region = (new_region->base() + new_region->size() - 1) / PAGE_SIZE; + for (size_t page = first_page_in_region; page <= last_page_in_region; ++page) { + VERIFY(m_page_to_region_map[page] == old_region); + m_page_to_region_map[page] = new_region.ptr(); + } + + m_regions.append(move(new_region)); +} + void SoftMMU::set_tls_region(NonnullOwnPtr region) { VERIFY(!m_tls_region); diff --git a/Userland/DevTools/UserspaceEmulator/SoftMMU.h b/Userland/DevTools/UserspaceEmulator/SoftMMU.h index 671c3bc99d..5ee14c7a47 100644 --- a/Userland/DevTools/UserspaceEmulator/SoftMMU.h +++ b/Userland/DevTools/UserspaceEmulator/SoftMMU.h @@ -63,6 +63,7 @@ public: void add_region(NonnullOwnPtr); void remove_region(Region&); + void ensure_split_at(X86::LogicalAddress); void set_tls_region(NonnullOwnPtr);