aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--arch/x86_64/include/arch/memory/region_allocator.hpp3
-rw-r--r--arch/x86_64/kapi/memory.cpp54
-rw-r--r--arch/x86_64/src/memory/region_allocator.cpp9
-rw-r--r--kapi/include/kapi/memory.hpp15
-rw-r--r--kapi/include/kapi/memory/buffered_allocator.hpp5
-rw-r--r--kapi/include/kapi/memory/chunk.hpp37
-rw-r--r--kapi/include/kapi/memory/frame.hpp12
-rw-r--r--kapi/include/kapi/memory/frame_allocator.hpp3
-rw-r--r--kapi/include/kapi/memory/page.hpp10
-rw-r--r--kernel/CMakeLists.txt1
-rw-r--r--kernel/include/kernel/memory/bitmap_allocator.hpp56
-rw-r--r--kernel/kapi/memory.cpp56
-rw-r--r--kernel/src/memory/bitmap_allocator.cpp100
13 files changed, 334 insertions, 27 deletions
diff --git a/arch/x86_64/include/arch/memory/region_allocator.hpp b/arch/x86_64/include/arch/memory/region_allocator.hpp
index f391293..1c5885e 100644
--- a/arch/x86_64/include/arch/memory/region_allocator.hpp
+++ b/arch/x86_64/include/arch/memory/region_allocator.hpp
@@ -58,6 +58,9 @@ namespace arch::memory
auto allocate_many(std::size_t count = 1) noexcept
-> std::optional<std::pair<kapi::memory::frame, std::size_t>> override;
+ //! @copydoc kapi::memory::frame_allocator::mark_used
+ auto mark_used(kapi::memory::frame frame) -> void override;
+
//! @copydoc kapi::memory::frame_allocator::release_many
//!
//! @note This implementation will never actually release any frames.
diff --git a/arch/x86_64/kapi/memory.cpp b/arch/x86_64/kapi/memory.cpp
index b71fa17..48aab9f 100644
--- a/arch/x86_64/kapi/memory.cpp
+++ b/arch/x86_64/kapi/memory.cpp
@@ -18,14 +18,17 @@
#include <kstd/print>
+#include <multiboot2/constants.hpp>
#include <multiboot2/information.hpp>
+#include <algorithm>
#include <atomic>
#include <bit>
#include <cstddef>
#include <cstdint>
#include <memory>
#include <optional>
+#include <ranges>
#include <span>
#include <utility>
@@ -37,6 +40,10 @@ namespace kapi::memory
constexpr auto static unused_page_address = linear_address{0x0000'7fff'cafe'faceuz};
constexpr auto static recursive_page_map_index = arch::memory::page_table::entry_count - 2;
+ auto constinit region_based_allocator = std::optional<arch::memory::region_allocator>{};
+ auto constinit allocation_buffer = std::optional<buffered_allocator<4>>{};
+ auto constinit recursive_page_mapper = std::optional<arch::memory::recursive_page_mapper>{};
+
//! Instantiate a basic, memory region based, early frame allocator for remapping.
auto collect_memory_information()
{
@@ -150,9 +157,42 @@ namespace kapi::memory
}
}
- auto constinit region_based_allocator = std::optional<arch::memory::region_allocator>{};
- auto constinit allocation_buffer = std::optional<buffered_allocator<4>>{};
- auto constinit recursive_page_mapper = std::optional<arch::memory::recursive_page_mapper>{};
+ auto handoff_to_kernel_pmm(frame_allocator & new_allocator) -> void
+ {
+ auto memory_map = boot::bootstrap_information.mbi->memory_map();
+
+ for (auto const & region : memory_map.regions() | std::views::filter([](auto const & region) {
+ return region.type == multiboot2::memory_type::available;
+ }))
+ {
+ auto start = frame::containing(physical_address{region.base});
+ auto count = region.size_in_B / page::size;
+ new_allocator.release_many({start, count});
+ }
+
+ auto next_free_frame = region_based_allocator->next_free_frame();
+ if (!next_free_frame)
+ {
+ system::panic("[x86_64:MEM] No more free memory!");
+ }
+
+ std::ranges::for_each(std::views::iota(kapi::memory::frame{}, *next_free_frame),
+ [&](auto frame) { new_allocator.mark_used(frame); });
+
+ auto image_start = frame::containing(physical_address{&arch::boot::_start_physical});
+ auto image_end = frame::containing(physical_address{&arch::boot::_end_physical}) + 1;
+
+ std::ranges::for_each(std::views::iota(image_start, image_end),
+ [&](auto frame) { new_allocator.mark_used(frame); });
+
+ auto mbi_base = std::bit_cast<std::uintptr_t>(boot::bootstrap_information.mbi);
+ auto mbi_size = boot::bootstrap_information.mbi->size_bytes();
+ auto mbi_address = physical_address{mbi_base & ~std::bit_cast<std::uintptr_t>(&arch::boot::TEACHOS_VMA)};
+ auto mbi_start = frame::containing(mbi_address);
+ auto mbi_end = frame::containing(physical_address{mbi_address.raw() + mbi_size}) + 1;
+
+ std::ranges::for_each(std::views::iota(mbi_start, mbi_end), [&](auto frame) { new_allocator.mark_used(frame); });
+ }
} // namespace
@@ -189,6 +229,14 @@ namespace kapi::memory
set_frame_allocator(*allocation_buffer);
set_page_mapper(*recursive_page_mapper);
+
+ auto memory_map = boot::bootstrap_information.mbi->memory_map();
+ auto highest_byte = std::ranges::max(std::views::transform(
+ std::views::filter(memory_map.regions(),
+ [](auto const & region) { return region.type == multiboot2::memory_type::available; }),
+ [](auto const & region) { return region.base + region.size_in_B; }));
+
+ init_pmm(frame::containing(physical_address{highest_byte}).number() + 1, handoff_to_kernel_pmm);
}
} // namespace kapi::memory
diff --git a/arch/x86_64/src/memory/region_allocator.cpp b/arch/x86_64/src/memory/region_allocator.cpp
index a2dfd48..36902ad 100644
--- a/arch/x86_64/src/memory/region_allocator.cpp
+++ b/arch/x86_64/src/memory/region_allocator.cpp
@@ -124,6 +124,15 @@ namespace arch::memory
return std::nullopt;
}
+ auto region_allocator::mark_used(kapi::memory::frame frame) -> void
+ {
+ if (frame < m_next_frame)
+ {
+ m_next_frame = frame;
+ find_next_frame();
+ }
+ }
+
auto region_allocator::release_many(std::pair<kapi::memory::frame, std::size_t>) -> void {}
auto region_allocator::next_free_frame() noexcept -> std::optional<kapi::memory::frame>
diff --git a/kapi/include/kapi/memory.hpp b/kapi/include/kapi/memory.hpp
index 87268ff..3a8f42d 100644
--- a/kapi/include/kapi/memory.hpp
+++ b/kapi/include/kapi/memory.hpp
@@ -25,6 +25,21 @@ namespace kapi::memory
auto init() -> void;
//! @qualifier kernel-defined
+ //! Initialize the physical memory manager.
+ //!
+ //! This function initializes the kernel-wide physical memory manager. The function will invoke the handoff handler to
+ //! transfer the platform-specific frame allocation state to the physical memory manager.
+ //!
+ //! @param frame_count The number of frames present in the system.
+ //! @param handoff_handler A function to be invoked to transfer the platform-specific frame allocation state. The
+ //! allocator to hand off to is passed to the handler.
+ auto init_pmm(std::size_t frame_count, void (&handoff_handler)(frame_allocator &)) -> void;
+
+ //! @qualifier kernel-defined
+ //! Get the currently active frame allocator.
+ auto get_frame_allocator() -> frame_allocator &;
+
+ //! @qualifier kernel-defined
//! Set the currently active frame allocator.
//!
//! @param allocator A new frame allocator.
diff --git a/kapi/include/kapi/memory/buffered_allocator.hpp b/kapi/include/kapi/memory/buffered_allocator.hpp
index 49137c6..286c841 100644
--- a/kapi/include/kapi/memory/buffered_allocator.hpp
+++ b/kapi/include/kapi/memory/buffered_allocator.hpp
@@ -88,6 +88,11 @@ namespace kapi::memory
return m_underlying->allocate_many(count);
}
+ auto mark_used(kapi::memory::frame) -> void override
+ {
+ system::panic("[x86_64:MEM] Not implemented");
+ }
+
auto release_many(std::pair<kapi::memory::frame, std::size_t> frame_set) -> void override
{
if (m_free == BufferSize)
diff --git a/kapi/include/kapi/memory/chunk.hpp b/kapi/include/kapi/memory/chunk.hpp
index dd97a58..3d90326 100644
--- a/kapi/include/kapi/memory/chunk.hpp
+++ b/kapi/include/kapi/memory/chunk.hpp
@@ -12,9 +12,10 @@ namespace kapi::memory
//! @qualifier kernel-defined
//! A fixed-size unit of memory, indexed by a number.
//!
+ //! @tparam ChunkType The CRTP type of the deriving class
//! @tparam AddressType The type of addresses used to index this chunk
//! @tparam Size The size of this chunk.
- template<typename AddressType, std::size_t Size>
+ template<typename ChunkType, typename AddressType, std::size_t Size>
struct chunk
{
//! The type of addresses used to index this chunk
@@ -23,21 +24,13 @@ namespace kapi::memory
//! The size of this chunk
constexpr auto static size = Size;
- //! Construct a handle referencing the first chunk of the respective address space.
- constexpr chunk() noexcept = default;
-
- //! Construct a handle referencing the chunk of memory with the given number.
- explicit constexpr chunk(std::size_t number) noexcept
- : m_number{number}
- {}
-
//! Construct a new chunk handle for the chunk containing the given address.
//!
//! @param address An address contained by the desired chunk.
//! @return A handle to a chunk containing the given address.
- constexpr auto static containing(address_type address) noexcept -> chunk
+ constexpr auto static containing(address_type address) noexcept -> ChunkType
{
- return chunk{address.raw() / size};
+ return ChunkType{address.raw() / size};
}
//! Get the start address of the chunk referenced by this handle.
@@ -60,17 +53,17 @@ namespace kapi::memory
//!
//! @param n The positive offset to this chunk.
//! @return A handle referencing the chunk n chunks after the one referenced by this handle.
- constexpr auto operator+(std::size_t n) const noexcept -> chunk
+ constexpr auto operator+(std::size_t n) const noexcept -> ChunkType
{
- return chunk{m_number + n};
+ return ChunkType{m_number + n};
}
//! Let this handle reference the next chunk after the currently reference one.
//!
//! @return A handle referencing the same chunk as this handle did before the operation.
- constexpr auto operator++(int) noexcept -> chunk
+ constexpr auto operator++(int) noexcept -> ChunkType
{
- auto copy = *this;
+ auto copy = static_cast<ChunkType>(*this);
++*this;
return copy;
}
@@ -78,10 +71,10 @@ namespace kapi::memory
//! Let this handle reference the next chunk after the currently reference one.
//!
//! @return A reference to this handle.
- constexpr auto operator++() noexcept -> chunk &
+ constexpr auto operator++() noexcept -> ChunkType &
{
++m_number;
- return *this;
+ return static_cast<ChunkType &>(*this);
}
//! Check if this chunk handle reference the same chunk as another one.
@@ -95,6 +88,16 @@ namespace kapi::memory
constexpr auto operator<=>(chunk const & other) const noexcept -> std::strong_ordering = default;
private:
+ friend ChunkType;
+
+ //! Construct a handle referencing the first chunk of the respective address space.
+ constexpr chunk() noexcept = default;
+
+ //! Construct a handle referencing the chunk of memory with the given number.
+ explicit constexpr chunk(std::size_t number) noexcept
+ : m_number{number}
+ {}
+
//! The number of the currently referenced chunk.
std::size_t m_number{};
};
diff --git a/kapi/include/kapi/memory/frame.hpp b/kapi/include/kapi/memory/frame.hpp
index e7b0832..deab300 100644
--- a/kapi/include/kapi/memory/frame.hpp
+++ b/kapi/include/kapi/memory/frame.hpp
@@ -6,6 +6,8 @@
#include "kapi/memory/address.hpp"
#include "kapi/memory/chunk.hpp"
+#include <cstddef>
+
namespace kapi::memory
{
@@ -13,9 +15,15 @@ namespace kapi::memory
//! A handle to a frame of physical memory.
//!
//! @note Contrary to the address types, this type is modeled using inheritance to support future extensions.
- struct frame : chunk<physical_address, PLATFORM_FRAME_SIZE>
+ struct frame : chunk<frame, physical_address, PLATFORM_FRAME_SIZE>
{
- using chunk::chunk;
+ frame() = default;
+
+ frame(std::size_t number)
+ : chunk{number}
+ {}
+
+ using difference_type = std::ptrdiff_t;
//! @copydoc chunk::containing
//!
diff --git a/kapi/include/kapi/memory/frame_allocator.hpp b/kapi/include/kapi/memory/frame_allocator.hpp
index 6ed114c..cfa8a1c 100644
--- a/kapi/include/kapi/memory/frame_allocator.hpp
+++ b/kapi/include/kapi/memory/frame_allocator.hpp
@@ -34,6 +34,9 @@ namespace kapi::memory
return allocate_many(1).transform([](auto result) { return result.first; });
}
+ //! Mark the given frame as used
+ virtual auto mark_used(frame frame) -> void = 0;
+
//! Allocate multiple consecutive frames of physical memory.
//!
//! @param count The number of frames to allocate
diff --git a/kapi/include/kapi/memory/page.hpp b/kapi/include/kapi/memory/page.hpp
index 55d6e75..f209278 100644
--- a/kapi/include/kapi/memory/page.hpp
+++ b/kapi/include/kapi/memory/page.hpp
@@ -6,6 +6,8 @@
#include "kapi/memory/address.hpp"
#include "kapi/memory/chunk.hpp"
+#include <cstddef>
+
namespace kapi::memory
{
@@ -13,9 +15,13 @@ namespace kapi::memory
//! A handle to a page of virtual memory.
//!
//! @note Contrary to the address types, this type is modeled using inheritance to support future extensions.
- struct page : chunk<linear_address, PLATFORM_PAGE_SIZE>
+ struct page : chunk<page, linear_address, PLATFORM_PAGE_SIZE>
{
- using chunk::chunk;
+ page() = default;
+
+ page(std::size_t number)
+ : chunk{number}
+ {}
//! @copydoc chunk::containing
//!
diff --git a/kernel/CMakeLists.txt b/kernel/CMakeLists.txt
index 97a0267..ae2325a 100644
--- a/kernel/CMakeLists.txt
+++ b/kernel/CMakeLists.txt
@@ -10,6 +10,7 @@ add_executable("kernel"
# Kernel Implementation
"src/main.cpp"
+ "src/memory/bitmap_allocator.cpp"
)
target_include_directories("kernel" PRIVATE
diff --git a/kernel/include/kernel/memory/bitmap_allocator.hpp b/kernel/include/kernel/memory/bitmap_allocator.hpp
new file mode 100644
index 0000000..fb5bf55
--- /dev/null
+++ b/kernel/include/kernel/memory/bitmap_allocator.hpp
@@ -0,0 +1,56 @@
+#ifndef TEACHOS_KERNEL_MEMORY_BITMAP_ALLOCATOR_HPP
+#define TEACHOS_KERNEL_MEMORY_BITMAP_ALLOCATOR_HPP
+
+#include "kapi/memory.hpp"
+
+#include <cstddef>
+#include <cstdint>
+#include <optional>
+#include <span>
+#include <utility>
+
+namespace kernel::memory
+{
+
+ //! A generic, bitmap base frame allocator.
+ //!
+ //! This frame allocator manages the allocation state of each frame present in the system using a single bit per
+ //! frame. All state information is stored in a contiguous region of memory.
+ struct bitmap_frame_allocator final : kapi::memory::frame_allocator
+ {
+ //! Construct a new, empty bitmap allocator.
+ //!
+ //! @param storage A contiguous region of virtual memory for state storage.
+ //! @param total_frame The total number of frames in the system.
+ bitmap_frame_allocator(std::span<std::uint64_t> storage, std::size_t frame_count) noexcept;
+
+ bitmap_frame_allocator(bitmap_frame_allocator const &) = delete;
+ bitmap_frame_allocator(bitmap_frame_allocator &&) = delete;
+ auto operator=(bitmap_frame_allocator const &) -> bitmap_frame_allocator & = delete;
+ auto operator=(bitmap_frame_allocator &&) -> bitmap_frame_allocator & = delete;
+
+ //! @copydoc kapi::memory::frame_allocator::allocate_many
+ [[nodiscard]] auto allocate_many(std::size_t count = 1) noexcept
+ -> std::optional<std::pair<kapi::memory::frame, std::size_t>> override;
+
+ //! @copydoc kapi::memory::frame_allocator::release_many
+ auto release_many(std::pair<kapi::memory::frame, std::size_t> frame_set) -> void override;
+
+ //! Mark a given frame as being used.
+ //!
+ //! This function is used during bootstrap to hand the platform allocator state over to this allocator.
+ auto mark_used(kapi::memory::frame frame) -> void override;
+
+ private:
+ [[nodiscard]] auto test(std::size_t index) const noexcept -> bool;
+ auto set(std::size_t index) noexcept -> void;
+ auto clear(std::size_t index) noexcept -> void;
+
+ std::span<std::uint64_t> m_bitmap;
+ std::uint64_t m_frame_count;
+ std::uint64_t m_last_index;
+ };
+
+} // namespace kernel::memory
+
+#endif
diff --git a/kernel/kapi/memory.cpp b/kernel/kapi/memory.cpp
index 7c9b1da..1699cd3 100644
--- a/kernel/kapi/memory.cpp
+++ b/kernel/kapi/memory.cpp
@@ -2,8 +2,17 @@
#include "kapi/system.hpp"
+#include "kernel/memory/bitmap_allocator.hpp"
+
+#include <kstd/print>
+
+#include <algorithm>
+#include <bit>
#include <cstddef>
+#include <cstdint>
#include <optional>
+#include <ranges>
+#include <span>
#include <utility>
namespace kapi::memory
@@ -20,6 +29,11 @@ namespace kapi::memory
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.");
@@ -48,11 +62,17 @@ namespace kapi::memory
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>{};
} // 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)
@@ -73,12 +93,12 @@ namespace kapi::memory
auto allocate_frame() -> std::optional<frame>
{
- return active_frame_allocator->allocate();
+ return get_frame_allocator().allocate();
}
auto allocate_many_frames(std::size_t count) -> std::optional<std::pair<frame, std::size_t>>
{
- return active_frame_allocator->allocate_many(count);
+ return get_frame_allocator().allocate_many(count);
}
auto map(page page, frame frame) -> std::byte *
@@ -91,4 +111,34 @@ namespace kapi::memory
return active_page_mapper->unmap(page);
}
-} // namespace kapi::memory \ No newline at end of file
+ auto init_pmm(std::size_t frame_count, void (&handoff_handler)(frame_allocator &)) -> void
+ {
+ auto const bitmap_bytes = (frame_count + 7uz) / 8uz;
+ auto const bitmap_pages = (bitmap_bytes + page::size - 1uz) / 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 base_address = 0xffff'c000'0000'0000uz;
+ auto const flags = page_mapper::flags::writable | page_mapper::flags::supervisor_only;
+
+ std::ranges::for_each(std::views::iota(0uz, bitmap_pages), [&](auto index) {
+ auto page = page::containing(linear_address{base_address + index * page::size});
+ auto frame = memory::frame(bitmap_frames->first.number() + index);
+ active_page_mapper->map(page, frame, flags);
+ });
+
+ auto bitmap_ptr = std::bit_cast<std::uint64_t *>(base_address);
+ auto bitmap = std::span{bitmap_ptr, (bitmap_bytes + sizeof(std::uint64_t) - 1uz) / sizeof(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.");
+ }
+
+} // namespace kapi::memory
diff --git a/kernel/src/memory/bitmap_allocator.cpp b/kernel/src/memory/bitmap_allocator.cpp
new file mode 100644
index 0000000..c010f77
--- /dev/null
+++ b/kernel/src/memory/bitmap_allocator.cpp
@@ -0,0 +1,100 @@
+#include "kernel/memory/bitmap_allocator.hpp"
+
+#include "kapi/memory.hpp"
+
+#include <algorithm>
+#include <cstddef>
+#include <cstdint>
+#include <optional>
+#include <span>
+#include <utility>
+
+namespace kernel::memory
+{
+
+ bitmap_frame_allocator::bitmap_frame_allocator(std::span<std::uint64_t> storage, std::size_t frame_count) noexcept
+ : m_bitmap{storage}
+ , m_frame_count{frame_count}
+ , m_last_index{}
+ {
+ std::ranges::fill(m_bitmap, ~0uz);
+ }
+
+ auto bitmap_frame_allocator::allocate_many(std::size_t count) noexcept
+ -> std::optional<std::pair<kapi::memory::frame, std::size_t>>
+ {
+ if (count == 0)
+ {
+ return std::nullopt;
+ }
+
+ auto free_count = 0uz;
+ auto first_free = 0uz;
+
+ for (auto i = 0uz; i < m_frame_count; ++i)
+ {
+ auto const current = (m_last_index + i) % m_frame_count;
+ if (!test(current))
+ {
+ if (free_count == 0)
+ {
+ first_free = current;
+ }
+
+ ++free_count;
+
+ if (free_count == count)
+ {
+ for (auto j = 0uz; j < count; ++j)
+ {
+ set(first_free + j);
+ }
+ m_last_index = first_free + count;
+ return std::make_pair(kapi::memory::frame{first_free}, count);
+ }
+ }
+ else
+ {
+ free_count = 0;
+ }
+ }
+
+ return std::nullopt;
+ }
+
+ auto bitmap_frame_allocator::release_many(std::pair<kapi::memory::frame, std::size_t> frame_set) -> void
+ {
+ auto const [start, count] = frame_set;
+ for (auto i = 0uz; i < count; ++i)
+ {
+ clear(start.number() + i);
+ }
+ }
+
+ auto bitmap_frame_allocator::mark_used(kapi::memory::frame frame) -> void
+ {
+ set(frame.number());
+ }
+
+ auto bitmap_frame_allocator::test(std::size_t index) const noexcept -> bool
+ {
+ auto entry_entry = index / 64;
+ auto entry_offset = index % 64;
+ return (m_bitmap[entry_entry] & (1uz << entry_offset));
+ }
+
+ auto bitmap_frame_allocator::set(std::size_t index) noexcept -> void
+ {
+ auto entry_entry = index / 64;
+ auto entry_offset = index % 64;
+ m_bitmap[entry_entry] |= (1uz << entry_offset);
+ }
+
+ auto bitmap_frame_allocator::clear(std::size_t index) noexcept -> void
+ {
+ auto entry_entry = index / 64;
+ auto entry_offset = index % 64;
+ m_bitmap[entry_entry] &= ~(1uz << entry_offset);
+ }
+
+} // namespace kernel::memory \ No newline at end of file