#include <kapi/memory.hpp>
#include <kernel/memory/bitmap_allocator.hpp>
#include <kernel/memory/mmio_allocator.hpp>
#include <kapi/system.hpp>
#include <kstd/print.hpp>
#include <kstd/units.hpp>
#include <algorithm>
#include <cstddef>
#include <cstdint>
#include <optional>
#include <ranges>
#include <span>
#include <utility>
namespace kapi::memory
{
namespace
{
struct bad_frame_allocator final : public frame_allocator
{
bad_frame_allocator static instance;
auto allocate_many(std::size_t) noexcept -> std::optional<std::pair<frame, std::size_t>> override
{
system::panic("Tried to allocate frames without an active allocator.");
}
auto mark_used(frame) -> void override
{
system::panic("Tried to mark frame as used without an active allocator.");
}
auto release_many(std::pair<frame, std::size_t>) -> void override
{
system::panic("Tried to release frames without an active allocator.");
}
};
struct bad_page_mapper final : public page_mapper
{
bad_page_mapper static instance;
auto map(page, frame, flags) -> std::byte * override
{
system::panic("Tried to map a page without an active mapper.");
}
auto unmap(page) -> void override
{
system::panic("Tried to unmap a page without an active mapper.");
}
auto try_unmap(page) noexcept -> bool override
{
return false;
}
};
constinit bad_frame_allocator bad_frame_allocator::instance{};
constinit bad_page_mapper bad_page_mapper::instance{};
auto constinit allocator = std::optional<kernel::memory::bitmap_frame_allocator>{};
auto constinit mmio_allocator = std::optional<kernel::memory::mmio_allocator>{};
} // namespace
constinit auto static active_frame_allocator = static_cast<frame_allocator *>(&bad_frame_allocator::instance);
constinit auto static active_page_mapper = static_cast<page_mapper *>(&bad_page_mapper::instance);
auto get_frame_allocator() -> frame_allocator &
{
return *active_frame_allocator;
}
auto set_frame_allocator(frame_allocator & allocator) -> std::optional<frame_allocator *>
{
if (&allocator == active_frame_allocator)
{
return {};
}
return std::exchange(active_frame_allocator, &allocator);
}
auto set_page_mapper(page_mapper & mapper) -> std::optional<page_mapper *>
{
if (&mapper == active_page_mapper)
{
return {};
}
return std::exchange(active_page_mapper, &mapper);
}
auto allocate_frame() -> std::optional<frame>
{
return get_frame_allocator().allocate();
}
auto allocate_many_frames(std::size_t count) -> std::optional<std::pair<frame, std::size_t>>
{
return get_frame_allocator().allocate_many(count);
}
auto map(page page, frame frame, page_mapper::flags flags) -> std::byte *
{
return active_page_mapper->map(page, frame, flags);
}
auto unmap(page page) -> void
{
return active_page_mapper->unmap(page);
}
auto init_pmm(std::size_t frame_count, void (&handoff_handler)(frame_allocator &)) -> void
{
using namespace kstd::units_literals;
auto const bitmap_bytes = kstd::units::bytes{(frame_count + 7uz) / 8uz};
auto const bitmap_pages = (bitmap_bytes + page::size - 1_B) / page::size;
auto const bitmap_frames = allocate_many_frames(bitmap_pages);
if (!bitmap_frames)
{
system::panic("[OS:MEM] Not enough memory for bitmap allocator!");
}
auto const flags = page_mapper::flags::writable | page_mapper::flags::supervisor_only | page_mapper::flags::global;
auto bitmap_ptr = static_cast<std::uint64_t *>(nullptr);
std::ranges::for_each(std::views::iota(0uz, bitmap_pages), [&](auto index) {
auto page = page::containing(pmm_metadata_base + index * page::size);
auto frame = memory::frame(bitmap_frames->first.number() + index);
auto mapped = active_page_mapper->map(page, frame, flags);
if (!bitmap_ptr)
{
bitmap_ptr = reinterpret_cast<std::uint64_t *>(mapped);
}
});
auto bitmap =
std::span{bitmap_ptr, (bitmap_bytes + kstd::type_size<std::uint64_t> - 1_B) / kstd::type_size<std::uint64_t>};
allocator.emplace(bitmap, frame_count);
handoff_handler(allocator.value());
set_frame_allocator(allocator.value());
kstd::println("[OS:MEM] Physical memory manager initialized.");
}
auto init_mmio(linear_address base, std::size_t page_count) -> void
{
mmio_allocator.emplace(base, page_count);
}
auto allocate_mmio_region(std::size_t page_count) -> mmio_region
{
auto region = mmio_allocator->allocate(page_count);
return {region, page_count};
}
auto map_mmio_region(mmio_region region, physical_address hw_base, page_mapper::flags flags) -> std::byte *
{
auto start_page = page::containing(region.first);
auto start_frame = frame::containing(hw_base);
flags |= page_mapper::flags::uncached;
auto start = map(start_page, start_frame, flags);
std::ranges::for_each(std::views::iota(1uz, region.second), [&](auto index) {
auto page = page::containing(region.first + index * page::size);
auto frame = frame::containing(hw_base + index * page::size);
map(page, frame, flags);
});
return start;
}
auto release_mmio_region(mmio_region region) -> void
{
mmio_allocator->release(region.first);
}
} // namespace kapi::memory