Serenity Operating System
at master 140 lines 4.4 kB view raw
1/* 2 * Copyright (c) 2018-2020, Andreas Kling <kling@serenityos.org> 3 * 4 * SPDX-License-Identifier: BSD-2-Clause 5 */ 6 7#include <AK/BuiltinWrappers.h> 8#include <Kernel/Assertions.h> 9#include <Kernel/Memory/MemoryManager.h> 10#include <Kernel/Memory/PhysicalRegion.h> 11#include <Kernel/Memory/PhysicalZone.h> 12#include <Kernel/Random.h> 13 14namespace Kernel::Memory { 15 16static constexpr u32 next_power_of_two(u32 value) 17{ 18 value--; 19 value |= value >> 1; 20 value |= value >> 2; 21 value |= value >> 4; 22 value |= value >> 8; 23 value |= value >> 16; 24 value++; 25 return value; 26} 27 28PhysicalRegion::~PhysicalRegion() = default; 29 30PhysicalRegion::PhysicalRegion(PhysicalAddress lower, PhysicalAddress upper) 31 : m_lower(lower) 32 , m_upper(upper) 33{ 34 m_pages = (m_upper.get() - m_lower.get()) / PAGE_SIZE; 35} 36 37void PhysicalRegion::initialize_zones() 38{ 39 size_t remaining_pages = m_pages; 40 auto base_address = m_lower; 41 42 auto make_zones = [&](size_t zone_size) -> size_t { 43 size_t pages_per_zone = zone_size / PAGE_SIZE; 44 size_t zone_count = 0; 45 auto first_address = base_address; 46 while (remaining_pages >= pages_per_zone) { 47 m_zones.append(adopt_nonnull_own_or_enomem(new (nothrow) PhysicalZone(base_address, pages_per_zone)).release_value_but_fixme_should_propagate_errors()); 48 base_address = base_address.offset(pages_per_zone * PAGE_SIZE); 49 m_usable_zones.append(*m_zones.last()); 50 remaining_pages -= pages_per_zone; 51 ++zone_count; 52 } 53 if (zone_count) 54 dmesgln(" * {}x PhysicalZone ({} MiB) @ {:016x}-{:016x}", zone_count, pages_per_zone / 256, first_address.get(), base_address.get() - pages_per_zone * PAGE_SIZE - 1); 55 return zone_count; 56 }; 57 58 // First make 16 MiB zones (with 4096 pages each) 59 m_large_zones = make_zones(large_zone_size); 60 61 // Then divide any remaining space into 1 MiB zones (with 256 pages each) 62 make_zones(small_zone_size); 63} 64 65OwnPtr<PhysicalRegion> PhysicalRegion::try_take_pages_from_beginning(size_t page_count) 66{ 67 VERIFY(page_count > 0); 68 VERIFY(page_count < m_pages); 69 auto taken_lower = m_lower; 70 auto taken_upper = taken_lower.offset((PhysicalPtr)page_count * PAGE_SIZE); 71 m_lower = m_lower.offset((PhysicalPtr)page_count * PAGE_SIZE); 72 m_pages = (m_upper.get() - m_lower.get()) / PAGE_SIZE; 73 74 return try_create(taken_lower, taken_upper); 75} 76 77Vector<NonnullRefPtr<PhysicalPage>> PhysicalRegion::take_contiguous_free_pages(size_t count) 78{ 79 auto rounded_page_count = next_power_of_two(count); 80 auto order = count_trailing_zeroes(rounded_page_count); 81 82 Optional<PhysicalAddress> page_base; 83 for (auto& zone : m_usable_zones) { 84 page_base = zone.allocate_block(order); 85 if (page_base.has_value()) { 86 if (zone.is_empty()) { 87 // We've exhausted this zone, move it to the full zones list. 88 m_full_zones.append(zone); 89 } 90 break; 91 } 92 } 93 94 if (!page_base.has_value()) 95 return {}; 96 97 Vector<NonnullRefPtr<PhysicalPage>> physical_pages; 98 physical_pages.ensure_capacity(count); 99 100 for (size_t i = 0; i < count; ++i) 101 physical_pages.append(PhysicalPage::create(page_base.value().offset(i * PAGE_SIZE))); 102 return physical_pages; 103} 104 105RefPtr<PhysicalPage> PhysicalRegion::take_free_page() 106{ 107 if (m_usable_zones.is_empty()) 108 return nullptr; 109 110 auto& zone = *m_usable_zones.first(); 111 auto page = zone.allocate_block(0); 112 VERIFY(page.has_value()); 113 114 if (zone.is_empty()) { 115 // We've exhausted this zone, move it to the full zones list. 116 m_full_zones.append(zone); 117 } 118 119 return PhysicalPage::create(page.value()); 120} 121 122void PhysicalRegion::return_page(PhysicalAddress paddr) 123{ 124 auto large_zone_base = lower().get(); 125 auto small_zone_base = lower().get() + (m_large_zones * large_zone_size); 126 127 size_t zone_index; 128 if (paddr.get() < small_zone_base) 129 zone_index = (paddr.get() - large_zone_base) / large_zone_size; 130 else 131 zone_index = m_large_zones + (paddr.get() - small_zone_base) / small_zone_size; 132 133 auto& zone = m_zones[zone_index]; 134 VERIFY(zone->contains(paddr)); 135 zone->deallocate_block(paddr, 0); 136 if (m_full_zones.contains(*zone)) 137 m_usable_zones.append(*zone); 138} 139 140}