2020-01-18 11:38:21 +03:00
|
|
|
/*
|
|
|
|
* Copyright (c) 2018-2020, Andreas Kling <kling@serenityos.org>
|
|
|
|
*
|
2021-04-22 11:24:48 +03:00
|
|
|
* SPDX-License-Identifier: BSD-2-Clause
|
2020-01-18 11:38:21 +03:00
|
|
|
*/
|
|
|
|
|
2021-12-20 00:46:55 +03:00
|
|
|
#include <AK/BuiltinWrappers.h>
|
2023-02-24 21:10:59 +03:00
|
|
|
#include <Kernel/Library/Assertions.h>
|
2021-08-06 11:45:34 +03:00
|
|
|
#include <Kernel/Memory/MemoryManager.h>
|
|
|
|
#include <Kernel/Memory/PhysicalRegion.h>
|
|
|
|
#include <Kernel/Memory/PhysicalZone.h>
|
2023-02-24 20:49:37 +03:00
|
|
|
#include <Kernel/Security/Random.h>
|
2019-06-11 14:13:02 +03:00
|
|
|
|
2021-08-06 14:49:36 +03:00
|
|
|
namespace Kernel::Memory {
|
2020-02-16 03:27:42 +03:00
|
|
|
|
2021-07-12 23:52:17 +03:00
|
|
|
static constexpr u32 next_power_of_two(u32 value)
|
|
|
|
{
|
|
|
|
value--;
|
|
|
|
value |= value >> 1;
|
|
|
|
value |= value >> 2;
|
|
|
|
value |= value >> 4;
|
|
|
|
value |= value >> 8;
|
|
|
|
value |= value >> 16;
|
|
|
|
value++;
|
|
|
|
return value;
|
|
|
|
}
|
|
|
|
|
2022-03-16 22:15:15 +03:00
|
|
|
PhysicalRegion::~PhysicalRegion() = default;
|
2021-07-12 23:52:17 +03:00
|
|
|
|
2019-06-11 14:13:02 +03:00
|
|
|
PhysicalRegion::PhysicalRegion(PhysicalAddress lower, PhysicalAddress upper)
|
|
|
|
: m_lower(lower)
|
|
|
|
, m_upper(upper)
|
|
|
|
{
|
2021-07-13 19:46:09 +03:00
|
|
|
m_pages = (m_upper.get() - m_lower.get()) / PAGE_SIZE;
|
2019-06-11 14:13:02 +03:00
|
|
|
}
|
|
|
|
|
2021-07-12 23:52:17 +03:00
|
|
|
void PhysicalRegion::initialize_zones()
|
|
|
|
{
|
|
|
|
size_t remaining_pages = m_pages;
|
|
|
|
auto base_address = m_lower;
|
|
|
|
|
2021-12-13 00:00:29 +03:00
|
|
|
auto make_zones = [&](size_t zone_size) -> size_t {
|
2021-12-11 21:00:47 +03:00
|
|
|
size_t pages_per_zone = zone_size / PAGE_SIZE;
|
2021-07-13 23:38:23 +03:00
|
|
|
size_t zone_count = 0;
|
|
|
|
auto first_address = base_address;
|
|
|
|
while (remaining_pages >= pages_per_zone) {
|
2022-02-03 17:33:46 +03:00
|
|
|
m_zones.append(adopt_nonnull_own_or_enomem(new (nothrow) PhysicalZone(base_address, pages_per_zone)).release_value_but_fixme_should_propagate_errors());
|
2021-07-13 23:38:23 +03:00
|
|
|
base_address = base_address.offset(pages_per_zone * PAGE_SIZE);
|
2023-03-06 19:16:25 +03:00
|
|
|
m_usable_zones.append(*m_zones.last());
|
2021-07-13 23:38:23 +03:00
|
|
|
remaining_pages -= pages_per_zone;
|
|
|
|
++zone_count;
|
2021-07-12 23:52:17 +03:00
|
|
|
}
|
2021-07-13 23:38:23 +03:00
|
|
|
if (zone_count)
|
|
|
|
dmesgln(" * {}x PhysicalZone ({} MiB) @ {:016x}-{:016x}", zone_count, pages_per_zone / 256, first_address.get(), base_address.get() - pages_per_zone * PAGE_SIZE - 1);
|
2021-12-13 00:00:29 +03:00
|
|
|
return zone_count;
|
2021-07-12 23:52:17 +03:00
|
|
|
};
|
|
|
|
|
2021-07-13 23:38:23 +03:00
|
|
|
// First make 16 MiB zones (with 4096 pages each)
|
2021-12-13 00:00:29 +03:00
|
|
|
m_large_zones = make_zones(large_zone_size);
|
2021-07-13 23:38:23 +03:00
|
|
|
|
|
|
|
// Then divide any remaining space into 1 MiB zones (with 256 pages each)
|
2021-12-22 12:26:35 +03:00
|
|
|
make_zones(small_zone_size);
|
2021-07-12 23:52:17 +03:00
|
|
|
}
|
|
|
|
|
2022-12-07 05:03:38 +03:00
|
|
|
OwnPtr<PhysicalRegion> PhysicalRegion::try_take_pages_from_beginning(size_t page_count)
|
2021-07-08 04:50:05 +03:00
|
|
|
{
|
|
|
|
VERIFY(page_count > 0);
|
|
|
|
VERIFY(page_count < m_pages);
|
|
|
|
auto taken_lower = m_lower;
|
|
|
|
auto taken_upper = taken_lower.offset((PhysicalPtr)page_count * PAGE_SIZE);
|
|
|
|
m_lower = m_lower.offset((PhysicalPtr)page_count * PAGE_SIZE);
|
2021-07-13 19:46:09 +03:00
|
|
|
m_pages = (m_upper.get() - m_lower.get()) / PAGE_SIZE;
|
2021-07-08 04:50:05 +03:00
|
|
|
|
2021-07-13 19:46:09 +03:00
|
|
|
return try_create(taken_lower, taken_upper);
|
2021-07-08 04:50:05 +03:00
|
|
|
}
|
|
|
|
|
2023-03-06 16:17:01 +03:00
|
|
|
Vector<NonnullRefPtr<PhysicalPage>> PhysicalRegion::take_contiguous_free_pages(size_t count)
|
2019-06-11 14:13:02 +03:00
|
|
|
{
|
2021-07-12 23:52:17 +03:00
|
|
|
auto rounded_page_count = next_power_of_two(count);
|
2021-12-20 00:46:55 +03:00
|
|
|
auto order = count_trailing_zeroes(rounded_page_count);
|
2019-06-11 14:13:02 +03:00
|
|
|
|
2021-07-12 23:52:17 +03:00
|
|
|
Optional<PhysicalAddress> page_base;
|
2021-07-13 20:55:39 +03:00
|
|
|
for (auto& zone : m_usable_zones) {
|
2021-07-12 23:52:17 +03:00
|
|
|
page_base = zone.allocate_block(order);
|
2021-07-13 20:52:42 +03:00
|
|
|
if (page_base.has_value()) {
|
|
|
|
if (zone.is_empty()) {
|
|
|
|
// We've exhausted this zone, move it to the full zones list.
|
|
|
|
m_full_zones.append(zone);
|
|
|
|
}
|
2021-07-12 23:52:17 +03:00
|
|
|
break;
|
2021-07-13 20:52:42 +03:00
|
|
|
}
|
2020-09-08 08:18:38 +03:00
|
|
|
}
|
2020-09-18 10:49:51 +03:00
|
|
|
|
2021-07-12 23:52:17 +03:00
|
|
|
if (!page_base.has_value())
|
2020-03-07 20:24:41 +03:00
|
|
|
return {};
|
|
|
|
|
2023-03-06 16:17:01 +03:00
|
|
|
Vector<NonnullRefPtr<PhysicalPage>> physical_pages;
|
2021-07-12 23:52:17 +03:00
|
|
|
physical_pages.ensure_capacity(count);
|
2019-06-11 14:13:02 +03:00
|
|
|
|
2021-07-12 23:52:17 +03:00
|
|
|
for (size_t i = 0; i < count; ++i)
|
|
|
|
physical_pages.append(PhysicalPage::create(page_base.value().offset(i * PAGE_SIZE)));
|
|
|
|
return physical_pages;
|
2019-06-11 14:13:02 +03:00
|
|
|
}
|
|
|
|
|
2022-08-24 16:56:26 +03:00
|
|
|
RefPtr<PhysicalPage> PhysicalRegion::take_free_page()
|
2019-06-11 14:13:02 +03:00
|
|
|
{
|
2021-07-14 02:32:33 +03:00
|
|
|
if (m_usable_zones.is_empty())
|
2021-07-13 20:52:42 +03:00
|
|
|
return nullptr;
|
|
|
|
|
|
|
|
auto& zone = *m_usable_zones.first();
|
|
|
|
auto page = zone.allocate_block(0);
|
|
|
|
VERIFY(page.has_value());
|
|
|
|
|
|
|
|
if (zone.is_empty()) {
|
|
|
|
// We've exhausted this zone, move it to the full zones list.
|
|
|
|
m_full_zones.append(zone);
|
2019-06-11 14:13:02 +03:00
|
|
|
}
|
|
|
|
|
2021-07-13 20:52:42 +03:00
|
|
|
return PhysicalPage::create(page.value());
|
2019-06-11 14:13:02 +03:00
|
|
|
}
|
2020-02-16 03:27:42 +03:00
|
|
|
|
2021-07-08 05:28:51 +03:00
|
|
|
void PhysicalRegion::return_page(PhysicalAddress paddr)
|
2020-09-08 08:18:38 +03:00
|
|
|
{
|
2021-12-13 00:00:29 +03:00
|
|
|
auto large_zone_base = lower().get();
|
|
|
|
auto small_zone_base = lower().get() + (m_large_zones * large_zone_size);
|
|
|
|
|
|
|
|
size_t zone_index;
|
|
|
|
if (paddr.get() < small_zone_base)
|
|
|
|
zone_index = (paddr.get() - large_zone_base) / large_zone_size;
|
|
|
|
else
|
|
|
|
zone_index = m_large_zones + (paddr.get() - small_zone_base) / small_zone_size;
|
|
|
|
|
|
|
|
auto& zone = m_zones[zone_index];
|
2023-03-06 19:16:25 +03:00
|
|
|
VERIFY(zone->contains(paddr));
|
|
|
|
zone->deallocate_block(paddr, 0);
|
|
|
|
if (m_full_zones.contains(*zone))
|
|
|
|
m_usable_zones.append(*zone);
|
2020-09-08 08:18:38 +03:00
|
|
|
}
|
|
|
|
|
2020-02-16 03:27:42 +03:00
|
|
|
}
|