aboutsummaryrefslogtreecommitdiff
path: root/arch/x86_64/src
diff options
context:
space:
mode:
Diffstat (limited to 'arch/x86_64/src')
-rw-r--r--arch/x86_64/src/exception_handling/pure_virtual.cpp6
-rw-r--r--arch/x86_64/src/kernel/main.cpp1
-rw-r--r--arch/x86_64/src/memory/allocator/area_frame_allocator.cpp2
-rw-r--r--arch/x86_64/src/memory/allocator/tiny_frame_allocator.cpp4
-rw-r--r--arch/x86_64/src/memory/paging/active_page_table.cpp67
-rw-r--r--arch/x86_64/src/memory/paging/kernel_mapper.cpp94
-rw-r--r--arch/x86_64/src/memory/paging/page_table.cpp21
-rw-r--r--arch/x86_64/src/memory/paging/temporary_page.cpp9
8 files changed, 200 insertions, 4 deletions
diff --git a/arch/x86_64/src/exception_handling/pure_virtual.cpp b/arch/x86_64/src/exception_handling/pure_virtual.cpp
new file mode 100644
index 0000000..67772f7
--- /dev/null
+++ b/arch/x86_64/src/exception_handling/pure_virtual.cpp
@@ -0,0 +1,6 @@
+#include "arch/exception_handling/panic.hpp"
+
+extern "C" auto __cxa_pure_virtual() -> void
+{
+ teachos::arch::exception_handling::panic("Runtime", "Tried to call a pure virtual function!");
+}
diff --git a/arch/x86_64/src/kernel/main.cpp b/arch/x86_64/src/kernel/main.cpp
index 5b0f2c3..8c68a49 100644
--- a/arch/x86_64/src/kernel/main.cpp
+++ b/arch/x86_64/src/kernel/main.cpp
@@ -2,6 +2,7 @@
#include "arch/exception_handling/assert.hpp"
#include "arch/memory/allocator/area_frame_allocator.hpp"
+#include "arch/memory/cpu/control_register.hpp"
#include "arch/memory/cpu/msr.hpp"
#include "arch/memory/multiboot/reader.hpp"
#include "arch/memory/paging/kernel_mapper.hpp"
diff --git a/arch/x86_64/src/memory/allocator/area_frame_allocator.cpp b/arch/x86_64/src/memory/allocator/area_frame_allocator.cpp
index 3bc9676..cb4fefa 100644
--- a/arch/x86_64/src/memory/allocator/area_frame_allocator.cpp
+++ b/arch/x86_64/src/memory/allocator/area_frame_allocator.cpp
@@ -81,5 +81,5 @@ namespace teachos::arch::memory::allocator
return allocate_frame();
}
- auto area_frame_allocator::deallocate_frame(physical_frame physical_frame) -> void { (void)physical_frame; }
+ auto area_frame_allocator::deallocate_frame(physical_frame const & physical_frame) -> void { (void)physical_frame; }
} // namespace teachos::arch::memory::allocator
diff --git a/arch/x86_64/src/memory/allocator/tiny_frame_allocator.cpp b/arch/x86_64/src/memory/allocator/tiny_frame_allocator.cpp
index d37864e..ed000a0 100644
--- a/arch/x86_64/src/memory/allocator/tiny_frame_allocator.cpp
+++ b/arch/x86_64/src/memory/allocator/tiny_frame_allocator.cpp
@@ -4,7 +4,7 @@
namespace teachos::arch::memory::allocator
{
- tiny_frame_allocator::tiny_frame_allocator(area_frame_allocator & allocator)
+ tiny_frame_allocator::tiny_frame_allocator(frame_allocator & allocator)
: frames{}
{
// Has to be done this way, because constructing the constructor with the data from allocator.allocate_frames(),
@@ -34,7 +34,7 @@ namespace teachos::arch::memory::allocator
return std::nullopt;
}
- auto tiny_frame_allocator::deallocate_frame(physical_frame physical_frame) -> void
+ auto tiny_frame_allocator::deallocate_frame(physical_frame const & physical_frame) -> void
{
for (auto & frame_option : frames)
{
diff --git a/arch/x86_64/src/memory/paging/active_page_table.cpp b/arch/x86_64/src/memory/paging/active_page_table.cpp
index 0113869..d2bf683 100644
--- a/arch/x86_64/src/memory/paging/active_page_table.cpp
+++ b/arch/x86_64/src/memory/paging/active_page_table.cpp
@@ -1,5 +1,7 @@
#include "arch/memory/paging/active_page_table.hpp"
+#include "arch/memory/cpu/tlb.hpp"
+
namespace teachos::arch::memory::paging
{
namespace
@@ -15,6 +17,71 @@ namespace teachos::arch::memory::paging
return active_page;
}
+ auto active_page_table::map_page_to_frame(allocator::frame_allocator & allocator, virtual_page page,
+ allocator::physical_frame frame, std::bitset<64U> flags) -> void
+ {
+ auto current_handle = active_handle;
+
+ for (auto level = page_table_handle::LEVEL4; level != page_table_handle::LEVEL1; --level)
+ {
+ current_handle = current_handle.next_table_or_create(allocator, page.get_level_index(level));
+ }
+
+ auto & level1_entry = current_handle[page.get_level_index(page_table_handle::LEVEL1)];
+ arch::exception_handling::assert(!level1_entry.contains_flags(entry::HUGE_PAGE),
+ "[Page Mapper] Unable to map huge pages");
+ arch::exception_handling::assert(level1_entry.is_unused(), "[Page Mapper] Page table entry is already used");
+ level1_entry.set_entry(frame, flags.to_ulong() | entry::PRESENT);
+ }
+
+ auto active_page_table::identity_map(allocator::frame_allocator & allocator, allocator::physical_frame frame,
+ std::bitset<64U> flags) -> void
+ {
+ auto const page = virtual_page::containing_address(frame.start_address());
+ map_page_to_frame(allocator, page, frame, flags);
+ }
+
+ auto active_page_table::map_next_free_page_to_frame(allocator::frame_allocator & allocator, virtual_page page,
+ std::bitset<64U> flags) -> void
+ {
+ auto const frame = allocator.allocate_frame();
+ exception_handling::assert(frame.has_value(), "[Page mapper] Out of memory exception");
+ map_page_to_frame(allocator, page, frame.value(), flags);
+ }
+
+ auto active_page_table::unmap_page(allocator::frame_allocator & allocator, virtual_page page) -> void
+ {
+ exception_handling::assert(translate_page(page).has_value(),
+ "[Page Mapper] Attempted to unmap page, which has not been mapped previously");
+
+ auto current_handle = active_handle;
+
+ for (auto level = page_table_handle::LEVEL4; level != page_table_handle::LEVEL1; --level)
+ {
+ auto const level_index = page.get_level_index(level);
+ auto const next_handle = current_handle.next_table(level_index);
+ // The next table method failed even tough the page has to be mapped already, because translate_page did not
+ // fail. This can only mean that we attempted to unmap a huge page, which is not supported in the first place.
+ exception_handling::assert(next_handle.has_value(), "[Page Mapper] Unable to unmap huge pages");
+ current_handle = next_handle.value();
+ }
+
+ unmap_page_table_entry(allocator, page, current_handle);
+ cpu::tlb_flush(page.start_address());
+ }
+
+ auto active_page_table::unmap_page_table_entry(allocator::frame_allocator & allocator, virtual_page page,
+ page_table_handle & handle) -> void
+ {
+ auto level_index = page.get_level_index(handle.get_level());
+ auto & entry = handle[level_index];
+ auto const frame = entry.calculate_pointed_to_frame();
+ exception_handling::assert(frame.has_value(),
+ "[Page Mapper] Attempted to unmap page, which has not been mapped previously");
+ entry.set_unused();
+ allocator.deallocate_frame(frame.value());
+ }
+
auto active_page_table::operator[](std::size_t index) -> entry & { return active_handle[index]; }
auto active_page_table::translate_address(virtual_address address) -> std::optional<allocator::physical_address>
diff --git a/arch/x86_64/src/memory/paging/kernel_mapper.cpp b/arch/x86_64/src/memory/paging/kernel_mapper.cpp
new file mode 100644
index 0000000..f938cb3
--- /dev/null
+++ b/arch/x86_64/src/memory/paging/kernel_mapper.cpp
@@ -0,0 +1,94 @@
+#include "arch/memory/paging/kernel_mapper.hpp"
+
+#include "arch/memory/cpu/control_register.hpp"
+#include "arch/memory/cpu/tlb.hpp"
+#include "arch/memory/paging/temporary_page.hpp"
+#include "arch/video/vga/text.hpp"
+
+namespace teachos::arch::memory::paging
+{
+
+ kernel_mapper::kernel_mapper(allocator::frame_allocator & allocator, multiboot::memory_information const & mem_info)
+ : allocator(allocator)
+ , mem_info(mem_info)
+ {
+ // Nothing to do
+ }
+
+ auto kernel_mapper::remap_kernel() -> active_page_table &
+ {
+ temporary_page temporary_page{virtual_page{0xCAFEBABE}, allocator};
+ auto & active_table = active_page_table::create_or_get();
+ auto const frame = allocator.allocate_frame();
+ exception_handling::assert(frame.has_value(),
+ "[Kernel Mapper] Frame could not be allocated and therefore kernel not mapped");
+ inactive_page_table new_table{frame.value(), active_table, temporary_page};
+ remap_elf_kernel_sections(new_table, temporary_page, active_table);
+ auto const old_table = switch_active_page_table(new_table);
+ auto const old_level_4_page = virtual_page::containing_address(old_table.page_table_level_4_frame.start_address());
+ active_table.unmap_page(allocator, old_level_4_page);
+ return active_table;
+ }
+
+ auto kernel_mapper::remap_elf_kernel_sections(inactive_page_table & inactive_table, temporary_page & temporary_page,
+ active_page_table & active_table) -> void
+ {
+ auto const backup =
+ allocator::physical_frame::containing_address(cpu::read_control_register(cpu::control_register::CR3));
+ auto page_table_level4 = temporary_page.map_table_frame(backup, active_table);
+
+ active_table[511].set_entry(inactive_table.page_table_level_4_frame, entry::PRESENT | entry::WRITABLE);
+ cpu::tlb_flush_all();
+ map_elf_kernel_sections(active_table);
+
+ page_table_level4[511].set_entry(backup, entry::PRESENT | entry::WRITABLE);
+ cpu::tlb_flush_all();
+ temporary_page.unmap_page(active_table);
+ }
+
+ auto kernel_mapper::switch_active_page_table(inactive_page_table new_table) -> inactive_page_table
+ {
+ auto const backup =
+ allocator::physical_frame::containing_address(cpu::read_control_register(cpu::control_register::CR3));
+ auto const old_table = inactive_page_table{backup};
+
+ auto const new_address = new_table.page_table_level_4_frame.start_address();
+ cpu::write_control_register(cpu::control_register::CR3, new_address);
+ return old_table;
+ }
+
+ auto kernel_mapper::map_elf_kernel_sections(active_page_table & active_table) -> void
+ {
+ exception_handling::assert(!mem_info.sections.empty(), "[Kernel Mapper] Kernel elf sections empty");
+ for (auto const & section : mem_info.sections)
+ {
+ if (!section.flags.contains_flags(multiboot::elf_section_flags::OCCUPIES_MEMORY))
+ {
+ continue;
+ }
+ exception_handling::assert(section.physical_address % allocator::PAGE_FRAME_SIZE == 0U,
+ "[Kernel Mapper] Section must be page aligned");
+ auto const start_frame = allocator::physical_frame::containing_address(section.physical_address);
+ // End address is exclusive, so that it is not part of the section anymore (one past the last frame of this
+ // section). But end frame would now point to the actual last frame and not one past the last frame, therefore
+ // we increment by one to get one past the last frame of this section.
+ auto const end_frame =
+ ++(allocator::physical_frame::containing_address(section.physical_address + section.section_size - 1));
+
+ allocator::frame_container::iterator const begin{start_frame};
+ allocator::frame_container::iterator const end{end_frame};
+ allocator::frame_container const frames{begin, end};
+ entry const entry{section.flags};
+
+ for (auto const & frame : frames)
+ {
+ active_table.identity_map(allocator, frame, entry.get_flags());
+ }
+ }
+
+ auto const vga_buffer_frame =
+ allocator::physical_frame::containing_address(video::vga::text::DEFAULT_VGA_TEXT_BUFFER_ADDRESS);
+ active_table.identity_map(allocator, vga_buffer_frame, entry::WRITABLE);
+ }
+
+} // namespace teachos::arch::memory::paging \ No newline at end of file
diff --git a/arch/x86_64/src/memory/paging/page_table.cpp b/arch/x86_64/src/memory/paging/page_table.cpp
index eb11810..48e6d4f 100644
--- a/arch/x86_64/src/memory/paging/page_table.cpp
+++ b/arch/x86_64/src/memory/paging/page_table.cpp
@@ -96,6 +96,27 @@ namespace teachos::arch::memory::paging
"[Page Table] Attempted to pass nullptr as table to page table table method");
}
+ auto page_table_handle::next_table_or_create(allocator::frame_allocator & allocator,
+ std::size_t table_index) -> page_table_handle
+ {
+ auto next_handle = next_table(table_index);
+ // If the next table method failed then it means that the page level of the frame we want allocate has not yet
+ // been created itself. So we have to do that before we are able to allocate the wanted frame. This has to be done
+ // for every level, meaning we potenitally create a level 4, level 3 and level 2 page entry, each pointing to a
+ // page table one level below.
+ if (!next_handle.has_value())
+ {
+ auto const allocated_frame = allocator.allocate_frame();
+ exception_handling::assert(allocated_frame.has_value(), "[Page mapper] Unable to allocate frame");
+ this->operator[](table_index).set_entry(allocated_frame.value(), entry::PRESENT | entry::WRITABLE);
+ // There should now be an entry at the previously not existent index, therefore we can simply access it again.
+ next_handle = next_table(table_index);
+ exception_handling::assert(next_handle.has_value(), "[Page mapper] Unable to create new entry into page table");
+ next_handle.value().zero_entries();
+ }
+ return next_handle.value();
+ }
+
auto page_table_handle::zero_entries() -> void { table->zero_entries(); }
auto page_table_handle::is_empty() const -> bool { return table->is_empty(); }
diff --git a/arch/x86_64/src/memory/paging/temporary_page.cpp b/arch/x86_64/src/memory/paging/temporary_page.cpp
index 152241d..8439864 100644
--- a/arch/x86_64/src/memory/paging/temporary_page.cpp
+++ b/arch/x86_64/src/memory/paging/temporary_page.cpp
@@ -4,6 +4,13 @@
namespace teachos::arch::memory::paging
{
+ temporary_page::temporary_page(virtual_page page, allocator::frame_allocator & allocator)
+ : page{page}
+ , allocator{allocator}
+ {
+ // Nothing to do
+ }
+
auto temporary_page::map_table_frame(allocator::physical_frame frame,
active_page_table & active_table) -> page_table_handle
{
@@ -24,6 +31,6 @@ namespace teachos::arch::memory::paging
auto temporary_page::unmap_page(active_page_table & active_table) -> void
{
- active_table.unmap_page(allocator, page);
+ active_table.unmap_page(this->allocator, page);
}
} // namespace teachos::arch::memory::paging