diff --git a/Kernel/Process.cpp b/Kernel/Process.cpp index 9a519ed4c45..4f3859e5a3b 100644 --- a/Kernel/Process.cpp +++ b/Kernel/Process.cpp @@ -9,14 +9,14 @@ #include #include #include -#include -#include #include -#include +#include #include #include #include +#include #include +#include #include #include #include @@ -108,6 +108,12 @@ static unsigned prot_to_region_access_flags(int prot) return access; } +Region& Process::allocate_split_region(const Region& source_region, const Range& range) +{ + m_regions.append(Region::create_user_accessible(range, source_region.name(), source_region.access())); + return m_regions.last(); +} + Region* Process::allocate_region(VirtualAddress vaddr, size_t size, const String& name, int prot, bool commit) { auto range = allocate_range(vaddr, size); @@ -154,11 +160,20 @@ bool Process::deallocate_region(Region& region) return false; } -Region* Process::region_from_range(VirtualAddress vaddr, size_t size) +Region* Process::region_from_range(const Range& range) { - size = PAGE_ROUND_UP(size); + size_t size = PAGE_ROUND_UP(range.size()); for (auto& region : m_regions) { - if (region.vaddr() == vaddr && region.size() == size) + if (region.vaddr() == range.base() && region.size() == size) + return ®ion; + } + return nullptr; +} + +Region* Process::region_containing(const Range& range) +{ + for (auto& region : m_regions) { + if (region.contains(range)) return ®ion; } return nullptr; @@ -168,7 +183,7 @@ int Process::sys$set_mmap_name(void* addr, size_t size, const char* name) { if (!validate_read_str(name)) return -EFAULT; - auto* region = region_from_range(VirtualAddress((u32)addr), size); + auto* region = region_from_range({ VirtualAddress((u32)addr), size }); if (!region) return -EINVAL; region->set_name(String(name)); @@ -220,17 +235,46 @@ void* Process::sys$mmap(const Syscall::SC_mmap_params* params) int Process::sys$munmap(void* addr, size_t size) { - auto* region = region_from_range(VirtualAddress((u32)addr), size); - if (!region) - return -EINVAL; - if (!deallocate_region(*region)) - return -EINVAL; - return 0; + Range range_to_unmap { VirtualAddress((u32)addr), size }; + if (auto* whole_region = region_from_range(range_to_unmap)) { + bool success = deallocate_region(*whole_region); + ASSERT(success); + return 0; + } + + if (auto* old_region = region_containing(range_to_unmap)) { + Range old_region_range = old_region->range(); + auto remaining_ranges_after_unmap = old_region_range.carve(range_to_unmap); + ASSERT(!remaining_ranges_after_unmap.is_empty()); + auto make_replacement_region = [&](const Range& new_range) -> Region& { + auto& new_region = allocate_split_region(*old_region, new_range); + ASSERT(new_range.base() >= old_region_range.base()); + size_t new_range_offset_in_old_region = new_range.base().get() - old_region_range.base().get(); + size_t first_physical_page_of_new_region_in_old_region = new_range_offset_in_old_region / PAGE_SIZE; + for (size_t i = 0; i < new_region.page_count(); ++i) { + new_region.vmo().physical_pages()[i] = old_region->vmo().physical_pages()[first_physical_page_of_new_region_in_old_region + i]; + } + return new_region; + }; + Vector new_regions; + for (auto& new_range : remaining_ranges_after_unmap) { + new_regions.unchecked_append(&make_replacement_region(new_range)); + } + deallocate_region(*old_region); + for (auto* new_region : new_regions) { + MM.map_region(*this, *new_region); + } + return 0; + } + + // FIXME: We should also support munmap() across multiple regions. (#175) + + return -EINVAL; } int Process::sys$mprotect(void* addr, size_t size, int prot) { - auto* region = region_from_range(VirtualAddress((u32)addr), size); + auto* region = region_from_range({ VirtualAddress((u32)addr), size }); if (!region) return -EINVAL; region->set_writable(prot & PROT_WRITE); @@ -2930,4 +2974,3 @@ int Process::sys$get_process_name(char* buffer, int buffer_size) strncpy(buffer, m_name.characters(), buffer_size); return 0; } - diff --git a/Kernel/Process.h b/Kernel/Process.h index e34e1bc22cf..82d2063c807 100644 --- a/Kernel/Process.h +++ b/Kernel/Process.h @@ -275,6 +275,8 @@ public: Region* allocate_region(VirtualAddress, size_t, const String& name, int prot = PROT_READ | PROT_WRITE, bool commit = true); bool deallocate_region(Region& region); + Region& allocate_split_region(const Region& source_region, const Range&); + void set_being_inspected(bool b) { m_being_inspected = b; } bool is_being_inspected() const { return m_being_inspected; } @@ -348,7 +350,8 @@ private: TTY* m_tty { nullptr }; - Region* region_from_range(VirtualAddress, size_t); + Region* region_from_range(const Range&); + Region* region_containing(const Range&); NonnullRefPtrVector m_regions;