Serenity Operating System
at master 169 lines 5.3 kB view raw
1/* 2 * Copyright (c) 2022, Andreas Kling <kling@serenityos.org> 3 * 4 * SPDX-License-Identifier: BSD-2-Clause 5 */ 6 7#include <AK/Format.h> 8#include <Kernel/Memory/AnonymousVMObject.h> 9#include <Kernel/Memory/MemoryManager.h> 10#include <Kernel/Memory/RegionTree.h> 11#include <Kernel/Random.h> 12 13namespace Kernel::Memory { 14 15RegionTree::~RegionTree() 16{ 17 delete_all_regions_assuming_they_are_unmapped(); 18} 19 20void RegionTree::delete_all_regions_assuming_they_are_unmapped() 21{ 22 // FIXME: This could definitely be done in a more efficient manner. 23 while (!m_regions.is_empty()) { 24 auto& region = *m_regions.begin(); 25 m_regions.remove(region.vaddr().get()); 26 delete &region; 27 } 28} 29 30ErrorOr<VirtualRange> RegionTree::allocate_range_anywhere(size_t size, size_t alignment) 31{ 32 if (!size) 33 return EINVAL; 34 35 VERIFY((size % PAGE_SIZE) == 0); 36 VERIFY((alignment % PAGE_SIZE) == 0); 37 38 if (Checked<size_t>::addition_would_overflow(size, alignment)) 39 return EOVERFLOW; 40 41 VirtualAddress window_start = m_total_range.base(); 42 43 auto allocate_from_window = [&](VirtualRange const& window) -> Optional<VirtualRange> { 44 // FIXME: This check is probably excluding some valid candidates when using a large alignment. 45 if (window.size() < (size + alignment)) 46 return {}; 47 48 FlatPtr initial_base = window.base().get(); 49 FlatPtr aligned_base = round_up_to_power_of_two(initial_base, alignment); 50 51 VERIFY(size); 52 53 return VirtualRange { VirtualAddress(aligned_base), size }; 54 }; 55 56 for (auto it = m_regions.begin(); !it.is_end(); ++it) { 57 auto& region = *it; 58 59 if (window_start == region.vaddr()) { 60 window_start = region.range().end(); 61 continue; 62 } 63 64 VirtualRange window { window_start, region.vaddr().get() - window_start.get() }; 65 window_start = region.range().end(); 66 67 if (auto maybe_range = allocate_from_window(window); maybe_range.has_value()) 68 return maybe_range.release_value(); 69 } 70 71 VirtualRange window { window_start, m_total_range.end().get() - window_start.get() }; 72 if (m_total_range.contains(window)) { 73 if (auto maybe_range = allocate_from_window(window); maybe_range.has_value()) 74 return maybe_range.release_value(); 75 } 76 77 dmesgln("RegionTree: Failed to allocate anywhere: size={}, alignment={}", size, alignment); 78 return ENOMEM; 79} 80 81ErrorOr<VirtualRange> RegionTree::allocate_range_specific(VirtualAddress base, size_t size) 82{ 83 if (!size) 84 return EINVAL; 85 86 VERIFY(base.is_page_aligned()); 87 VERIFY((size % PAGE_SIZE) == 0); 88 89 VirtualRange const range { base, size }; 90 if (!m_total_range.contains(range)) 91 return ENOMEM; 92 93 auto* region = m_regions.find_largest_not_above(base.offset(size - 1).get()); 94 if (!region) { 95 // The range can be accommodated below the current lowest range. 96 return range; 97 } 98 99 if (region->range().intersects(range)) { 100 // Requested range overlaps an existing range. 101 return ENOMEM; 102 } 103 104 // Requested range fits between first region and its next neighbor. 105 return range; 106} 107 108ErrorOr<VirtualRange> RegionTree::allocate_range_randomized(size_t size, size_t alignment) 109{ 110 if (!size) 111 return EINVAL; 112 113 VERIFY((size % PAGE_SIZE) == 0); 114 VERIFY((alignment % PAGE_SIZE) == 0); 115 116 // FIXME: I'm sure there's a smarter way to do this. 117 constexpr size_t maximum_randomization_attempts = 1000; 118 for (size_t i = 0; i < maximum_randomization_attempts; ++i) { 119 VirtualAddress random_address { round_up_to_power_of_two(get_fast_random<FlatPtr>() % m_total_range.end().get(), alignment) }; 120 121 if (!m_total_range.contains(random_address, size)) 122 continue; 123 124 auto range_or_error = allocate_range_specific(random_address, size); 125 if (!range_or_error.is_error()) 126 return range_or_error.release_value(); 127 } 128 129 return allocate_range_anywhere(size, alignment); 130} 131 132ErrorOr<void> RegionTree::place_anywhere(Region& region, RandomizeVirtualAddress randomize_virtual_address, size_t size, size_t alignment) 133{ 134 auto range = TRY(randomize_virtual_address == RandomizeVirtualAddress::Yes ? allocate_range_randomized(size, alignment) : allocate_range_anywhere(size, alignment)); 135 region.m_range = range; 136 m_regions.insert(region.vaddr().get(), region); 137 return {}; 138} 139 140ErrorOr<void> RegionTree::place_specifically(Region& region, VirtualRange const& range) 141{ 142 auto allocated_range = TRY(allocate_range_specific(range.base(), range.size())); 143 region.m_range = allocated_range; 144 m_regions.insert(region.vaddr().get(), region); 145 return {}; 146} 147 148bool RegionTree::remove(Region& region) 149{ 150 return m_regions.remove(region.range().base().get()); 151} 152 153Region* RegionTree::find_region_containing(VirtualAddress address) 154{ 155 auto* region = m_regions.find_largest_not_above(address.get()); 156 if (!region || !region->contains(address)) 157 return nullptr; 158 return region; 159} 160 161Region* RegionTree::find_region_containing(VirtualRange range) 162{ 163 auto* region = m_regions.find_largest_not_above(range.base().get()); 164 if (!region || !region->contains(range)) 165 return nullptr; 166 return region; 167} 168 169}