aboutsummaryrefslogtreecommitdiff
path: root/arch/x86_64/pre/src
diff options
context:
space:
mode:
Diffstat (limited to 'arch/x86_64/pre/src')
-rw-r--r--arch/x86_64/pre/src/context_switching/interrupt_descriptor_table/gate_descriptor.cpp24
-rw-r--r--arch/x86_64/pre/src/context_switching/interrupt_descriptor_table/idt_flags.cpp17
-rw-r--r--arch/x86_64/pre/src/context_switching/interrupt_descriptor_table/interrupt_descriptor_table.cpp53
-rw-r--r--arch/x86_64/pre/src/context_switching/interrupt_descriptor_table/interrupt_descriptor_table_pointer.cpp13
-rw-r--r--arch/x86_64/pre/src/context_switching/interrupt_descriptor_table/ist_offset.cpp10
-rw-r--r--arch/x86_64/pre/src/context_switching/interrupt_descriptor_table/segment_selector.cpp15
-rw-r--r--arch/x86_64/pre/src/context_switching/main.cpp63
-rw-r--r--arch/x86_64/pre/src/context_switching/segment_descriptor_table/access_byte.cpp17
-rw-r--r--arch/x86_64/pre/src/context_switching/segment_descriptor_table/gdt_flags.cpp20
-rw-r--r--arch/x86_64/pre/src/context_switching/segment_descriptor_table/global_descriptor_table.cpp109
-rw-r--r--arch/x86_64/pre/src/context_switching/segment_descriptor_table/global_descriptor_table_pointer.cpp11
-rw-r--r--arch/x86_64/pre/src/context_switching/segment_descriptor_table/segment_descriptor_base.cpp38
-rw-r--r--arch/x86_64/pre/src/context_switching/segment_descriptor_table/segment_descriptor_extension.cpp24
-rw-r--r--arch/x86_64/pre/src/context_switching/syscall/main.cpp35
-rw-r--r--arch/x86_64/pre/src/context_switching/syscall/syscall_enable.cpp32
-rw-r--r--arch/x86_64/pre/src/context_switching/syscall/syscall_handler.cpp118
-rw-r--r--arch/x86_64/pre/src/exception_handling/abort.cpp15
-rw-r--r--arch/x86_64/pre/src/exception_handling/assert.cpp15
-rw-r--r--arch/x86_64/pre/src/exception_handling/panic.cpp22
-rw-r--r--arch/x86_64/pre/src/exception_handling/pure_virtual.cpp6
-rw-r--r--arch/x86_64/pre/src/interrupt_handling/generic_interrupt_handler.cpp13
-rw-r--r--arch/x86_64/pre/src/kernel/cpu/call.cpp9
-rw-r--r--arch/x86_64/pre/src/kernel/cpu/gdtr.cpp19
-rw-r--r--arch/x86_64/pre/src/kernel/cpu/idtr.cpp18
-rw-r--r--arch/x86_64/pre/src/kernel/cpu/if.cpp7
-rw-r--r--arch/x86_64/pre/src/kernel/cpu/msr.cpp31
-rw-r--r--arch/x86_64/pre/src/kernel/cpu/segment_register.cpp98
-rw-r--r--arch/x86_64/pre/src/kernel/cpu/tr.cpp16
-rw-r--r--arch/x86_64/pre/src/kernel/main.cpp71
-rw-r--r--arch/x86_64/pre/src/memory/allocator/area_frame_allocator.cpp85
-rw-r--r--arch/x86_64/pre/src/memory/allocator/tiny_frame_allocator.cpp34
-rw-r--r--arch/x86_64/pre/src/memory/heap/bump_allocator.cpp54
-rw-r--r--arch/x86_64/pre/src/memory/heap/global_heap_allocator.cpp135
-rw-r--r--arch/x86_64/pre/src/memory/heap/linked_list_allocator.cpp177
-rw-r--r--arch/x86_64/pre/src/memory/heap/memory_block.cpp15
-rw-r--r--arch/x86_64/pre/src/memory/heap/user_heap_allocator.cpp200
-rw-r--r--arch/x86_64/pre/src/memory/main.cpp77
-rw-r--r--arch/x86_64/pre/src/memory/multiboot/elf_symbols_section.cpp13
-rw-r--r--arch/x86_64/pre/src/memory/multiboot/reader.cpp135
-rw-r--r--arch/x86_64/pre/src/memory/paging/active_page_table.cpp98
-rw-r--r--arch/x86_64/pre/src/memory/paging/inactive_page_table.cpp20
-rw-r--r--arch/x86_64/pre/src/memory/paging/page_entry.cpp63
-rw-r--r--arch/x86_64/pre/src/memory/paging/page_table.cpp128
-rw-r--r--arch/x86_64/pre/src/memory/paging/temporary_page.cpp29
-rw-r--r--arch/x86_64/pre/src/memory/paging/virtual_page.cpp33
-rw-r--r--arch/x86_64/pre/src/user/main.cpp35
46 files changed, 2270 insertions, 0 deletions
diff --git a/arch/x86_64/pre/src/context_switching/interrupt_descriptor_table/gate_descriptor.cpp b/arch/x86_64/pre/src/context_switching/interrupt_descriptor_table/gate_descriptor.cpp
new file mode 100644
index 0000000..28f289c
--- /dev/null
+++ b/arch/x86_64/pre/src/context_switching/interrupt_descriptor_table/gate_descriptor.cpp
@@ -0,0 +1,24 @@
+#include "arch/context_switching/interrupt_descriptor_table/gate_descriptor.hpp"
+
+namespace teachos::arch::context_switching::interrupt_descriptor_table
+{
+ gate_descriptor::gate_descriptor(uint128_t flags)
+ : _offset_1(flags)
+ , _selector(flags >> 19U, flags >> 16U)
+ , _ist(flags >> 32U)
+ , _flags(flags >> 40U)
+ , _offset_2(flags >> 48U)
+ {
+ // Nothing to do.
+ }
+
+ gate_descriptor::gate_descriptor(segment_selector selector, ist_offset ist, idt_flags flags, uint64_t offset)
+ : _offset_1(offset)
+ , _selector(selector)
+ , _ist(ist)
+ , _flags(flags)
+ , _offset_2(offset >> 16U)
+ {
+ // Nothing to do.
+ }
+} // namespace teachos::arch::context_switching::interrupt_descriptor_table
diff --git a/arch/x86_64/pre/src/context_switching/interrupt_descriptor_table/idt_flags.cpp b/arch/x86_64/pre/src/context_switching/interrupt_descriptor_table/idt_flags.cpp
new file mode 100644
index 0000000..d36a4c1
--- /dev/null
+++ b/arch/x86_64/pre/src/context_switching/interrupt_descriptor_table/idt_flags.cpp
@@ -0,0 +1,17 @@
+#include "arch/context_switching/interrupt_descriptor_table/idt_flags.hpp"
+
+namespace teachos::arch::context_switching::interrupt_descriptor_table
+{
+ idt_flags::idt_flags(uint8_t flags)
+ : _flags(flags)
+ {
+ // Nothing to do.
+ }
+
+ auto idt_flags::contains_flags(std::bitset<8U> other) const -> bool
+ {
+ return (std::bitset<8U>{_flags} & other) == other;
+ }
+
+ auto idt_flags::operator|=(std::bitset<8U> other) -> void { _flags |= other.to_ulong(); }
+} // namespace teachos::arch::context_switching::interrupt_descriptor_table
diff --git a/arch/x86_64/pre/src/context_switching/interrupt_descriptor_table/interrupt_descriptor_table.cpp b/arch/x86_64/pre/src/context_switching/interrupt_descriptor_table/interrupt_descriptor_table.cpp
new file mode 100644
index 0000000..7aa0859
--- /dev/null
+++ b/arch/x86_64/pre/src/context_switching/interrupt_descriptor_table/interrupt_descriptor_table.cpp
@@ -0,0 +1,53 @@
+#include "arch/context_switching/interrupt_descriptor_table/interrupt_descriptor_table.hpp"
+
+#include "arch/exception_handling/assert.hpp"
+#include "arch/interrupt_handling/generic_interrupt_handler.hpp"
+#include "arch/kernel/cpu/idtr.hpp"
+
+namespace teachos::arch::context_switching::interrupt_descriptor_table
+{
+ namespace
+ {
+ /// @brief Amount of currently reserved interrupt indicies.
+ /// See https://wiki.osdev.org/Interrupt_Descriptor_Table#IDT_items for more information.
+ constexpr uint16_t RESERVED_INTERRUPT_COUNT = 256U;
+
+ auto create_interrupt_descriptor_table() -> interrupt_descriptor_table
+ {
+ interrupt_descriptor_table interrupt_descriptor_table{RESERVED_INTERRUPT_COUNT};
+
+ uint64_t offset = reinterpret_cast<uint64_t>(interrupt_handling::generic_interrupt_handler);
+ segment_selector selector{1U, segment_selector::REQUEST_LEVEL_KERNEL};
+ ist_offset ist{0U};
+ idt_flags flags{idt_flags::DESCRIPTOR_LEVEL_KERNEL | idt_flags::INTERRUPT_GATE | idt_flags::PRESENT};
+
+ for (std::size_t i = 0; i < interrupt_descriptor_table.size(); i++)
+ {
+ interrupt_descriptor_table.at(i) = {selector, ist, flags, offset};
+ }
+
+ return interrupt_descriptor_table;
+ }
+ } // namespace
+
+ auto get_or_create_interrupt_descriptor_table() -> interrupt_descriptor_table &
+ {
+ // Interrupt Descriptor Table needs to be kept alive
+ static interrupt_descriptor_table idt = create_interrupt_descriptor_table();
+ return idt;
+ }
+
+ auto update_interrupt_descriptor_table_register() -> void
+ {
+ decltype(auto) idt = get_or_create_interrupt_descriptor_table();
+
+ interrupt_descriptor_table_pointer idt_pointer{static_cast<uint16_t>((idt.size() * sizeof(gate_descriptor)) - 1),
+ idt.data()};
+ kernel::cpu::load_interrupt_descriptor_table(idt_pointer);
+
+ auto const stored_gdt_pointer = kernel::cpu::store_interrupt_descriptor_table();
+ arch::exception_handling::assert(
+ idt_pointer == stored_gdt_pointer,
+ "[Interrupt Descriptor Table] Loaded IDTR value is not the same as the stored value.");
+ }
+} // namespace teachos::arch::context_switching::interrupt_descriptor_table
diff --git a/arch/x86_64/pre/src/context_switching/interrupt_descriptor_table/interrupt_descriptor_table_pointer.cpp b/arch/x86_64/pre/src/context_switching/interrupt_descriptor_table/interrupt_descriptor_table_pointer.cpp
new file mode 100644
index 0000000..7bcbae6
--- /dev/null
+++ b/arch/x86_64/pre/src/context_switching/interrupt_descriptor_table/interrupt_descriptor_table_pointer.cpp
@@ -0,0 +1,13 @@
+#include "arch/context_switching/interrupt_descriptor_table/interrupt_descriptor_table_pointer.hpp"
+
+namespace teachos::arch::context_switching::interrupt_descriptor_table
+{
+ interrupt_descriptor_table_pointer::interrupt_descriptor_table_pointer(uint16_t table_length,
+ gate_descriptor * address)
+ : table_length(table_length)
+ , address(address)
+ {
+ // Nothing to do.
+ }
+
+} // namespace teachos::arch::context_switching::interrupt_descriptor_table
diff --git a/arch/x86_64/pre/src/context_switching/interrupt_descriptor_table/ist_offset.cpp b/arch/x86_64/pre/src/context_switching/interrupt_descriptor_table/ist_offset.cpp
new file mode 100644
index 0000000..a70e75d
--- /dev/null
+++ b/arch/x86_64/pre/src/context_switching/interrupt_descriptor_table/ist_offset.cpp
@@ -0,0 +1,10 @@
+#include "arch/context_switching/interrupt_descriptor_table/ist_offset.hpp"
+
+namespace teachos::arch::context_switching::interrupt_descriptor_table
+{
+ ist_offset::ist_offset(uint8_t index)
+ : _ist(index)
+ {
+ // Nothing to do.
+ }
+} // namespace teachos::arch::context_switching::interrupt_descriptor_table
diff --git a/arch/x86_64/pre/src/context_switching/interrupt_descriptor_table/segment_selector.cpp b/arch/x86_64/pre/src/context_switching/interrupt_descriptor_table/segment_selector.cpp
new file mode 100644
index 0000000..27f0a3b
--- /dev/null
+++ b/arch/x86_64/pre/src/context_switching/interrupt_descriptor_table/segment_selector.cpp
@@ -0,0 +1,15 @@
+#include "arch/context_switching/interrupt_descriptor_table/segment_selector.hpp"
+
+namespace teachos::arch::context_switching::interrupt_descriptor_table
+{
+ auto segment_selector::contains_flags(std::bitset<3U> other) const -> bool
+ {
+ return (std::bitset<3U>{_flags} & other) == other;
+ }
+
+ auto segment_selector::get_index() const -> uint16_t { return _index; }
+
+ auto segment_selector::operator|=(std::bitset<3U> other) -> void { _flags |= other.to_ulong(); }
+
+ segment_selector::operator uint16_t() const { return *reinterpret_cast<uint16_t const *>(this); }
+} // namespace teachos::arch::context_switching::interrupt_descriptor_table
diff --git a/arch/x86_64/pre/src/context_switching/main.cpp b/arch/x86_64/pre/src/context_switching/main.cpp
new file mode 100644
index 0000000..9539428
--- /dev/null
+++ b/arch/x86_64/pre/src/context_switching/main.cpp
@@ -0,0 +1,63 @@
+#include "arch/context_switching/main.hpp"
+
+#include "arch/boot/pointers.hpp"
+#include "arch/context_switching/syscall/syscall_enable.hpp"
+#include "arch/kernel/cpu/call.hpp"
+#include "arch/kernel/cpu/if.hpp"
+#include "arch/kernel/cpu/segment_register.hpp"
+#include "arch/kernel/cpu/tr.hpp"
+#include "arch/user/main.hpp"
+
+namespace teachos::arch::context_switching
+{
+ namespace
+ {
+ constexpr interrupt_descriptor_table::segment_selector KERNEL_CODE_SEGMENT_SELECTOR{
+ 1U, interrupt_descriptor_table::segment_selector::REQUEST_LEVEL_KERNEL};
+ constexpr kernel::cpu::far_pointer KERNEL_CODE_POINTER{&kernel::cpu::reload_data_segment_registers,
+ KERNEL_CODE_SEGMENT_SELECTOR};
+ constexpr context_switching::interrupt_descriptor_table::segment_selector USER_CODE_SEGMENT_SELECTOR{
+ 3U, context_switching::interrupt_descriptor_table::segment_selector::REQUEST_LEVEL_USER};
+ constexpr context_switching::interrupt_descriptor_table::segment_selector USER_DATA_SEGMENT_SELECTOR{
+ 4U, context_switching::interrupt_descriptor_table::segment_selector::REQUEST_LEVEL_USER};
+
+ auto reload_gdtr() -> void { kernel::cpu::call(KERNEL_CODE_POINTER); }
+ } // namespace
+
+ auto initialize_descriptor_tables() -> descriptor_tables
+ {
+ static bool initalized = false;
+
+ if (!initalized)
+ {
+ kernel::cpu::clear_interrupt_flag();
+
+ segment_descriptor_table::update_gdtr();
+ interrupt_descriptor_table::update_interrupt_descriptor_table_register();
+
+ reload_gdtr();
+ segment_descriptor_table::update_tss_register();
+
+ kernel::cpu::set_interrupt_flag();
+ initalized = true;
+ }
+
+ descriptor_tables tables = {segment_descriptor_table::get_or_create_gdt(),
+ interrupt_descriptor_table::get_or_create_interrupt_descriptor_table()};
+ return tables;
+ }
+
+ auto switch_to_user_mode() -> void
+ {
+ syscall::enable_syscall();
+ switch_context(USER_DATA_SEGMENT_SELECTOR, USER_CODE_SEGMENT_SELECTOR, user::main);
+ }
+
+ auto switch_context(interrupt_descriptor_table::segment_selector data_segment,
+ interrupt_descriptor_table::segment_selector code_segment, void (*return_function)()) -> void
+ {
+ (void)initialize_descriptor_tables();
+ kernel::cpu::set_data_segment_registers(data_segment);
+ kernel::cpu::set_code_segment_register(data_segment, code_segment, reinterpret_cast<uint64_t>(return_function));
+ }
+} // namespace teachos::arch::context_switching
diff --git a/arch/x86_64/pre/src/context_switching/segment_descriptor_table/access_byte.cpp b/arch/x86_64/pre/src/context_switching/segment_descriptor_table/access_byte.cpp
new file mode 100644
index 0000000..e31e021
--- /dev/null
+++ b/arch/x86_64/pre/src/context_switching/segment_descriptor_table/access_byte.cpp
@@ -0,0 +1,17 @@
+#include "arch/context_switching/segment_descriptor_table/access_byte.hpp"
+
+namespace teachos::arch::context_switching::segment_descriptor_table
+{
+ access_byte::access_byte(uint8_t flags)
+ : _flags(flags)
+ {
+ // Nothing to do.
+ }
+
+ auto access_byte::contains_flags(std::bitset<8U> other) const -> bool
+ {
+ return (std::bitset<8U>{_flags} & other) == other;
+ }
+
+ auto access_byte::operator|=(std::bitset<8U> other) -> void { _flags |= other.to_ulong(); }
+} // namespace teachos::arch::context_switching::segment_descriptor_table
diff --git a/arch/x86_64/pre/src/context_switching/segment_descriptor_table/gdt_flags.cpp b/arch/x86_64/pre/src/context_switching/segment_descriptor_table/gdt_flags.cpp
new file mode 100644
index 0000000..e444a24
--- /dev/null
+++ b/arch/x86_64/pre/src/context_switching/segment_descriptor_table/gdt_flags.cpp
@@ -0,0 +1,20 @@
+#include "arch/context_switching/segment_descriptor_table/gdt_flags.hpp"
+
+namespace teachos::arch::context_switching::segment_descriptor_table
+{
+ gdt_flags::gdt_flags(uint8_t flags, std::bitset<20U> limit)
+ : _limit_2(limit.to_ulong() >> 16U)
+ , _flags(flags)
+ {
+ // Nothing to do.
+ }
+
+ auto gdt_flags::contains_flags(std::bitset<4U> other) const -> bool
+ {
+ return (std::bitset<4U>{_flags} & other) == other;
+ }
+
+ auto gdt_flags::get_limit() const -> std::bitset<4U> { return std::bitset<4U>{_limit_2}; }
+
+ auto gdt_flags::operator|=(std::bitset<4U> other) -> void { _flags |= other.to_ulong(); }
+} // namespace teachos::arch::context_switching::segment_descriptor_table
diff --git a/arch/x86_64/pre/src/context_switching/segment_descriptor_table/global_descriptor_table.cpp b/arch/x86_64/pre/src/context_switching/segment_descriptor_table/global_descriptor_table.cpp
new file mode 100644
index 0000000..bbcee31
--- /dev/null
+++ b/arch/x86_64/pre/src/context_switching/segment_descriptor_table/global_descriptor_table.cpp
@@ -0,0 +1,109 @@
+#include "arch/context_switching/segment_descriptor_table/global_descriptor_table.hpp"
+
+#include "arch/context_switching/segment_descriptor_table/segment_descriptor_extension.hpp"
+#include "arch/exception_handling/assert.hpp"
+#include "arch/kernel/cpu/gdtr.hpp"
+#include "arch/kernel/cpu/tr.hpp"
+
+namespace teachos::arch::context_switching::segment_descriptor_table
+{
+ namespace
+ {
+ auto create_segment_descriptor(segment_descriptor_type segment_descriptor_type, access_byte access_level)
+ -> segment_descriptor_base
+ {
+ uint64_t constexpr BASE = 0x0;
+ std::bitset<20U> constexpr LIMIT{0xFFFFF};
+ gdt_flags flags{gdt_flags::GRANULARITY, LIMIT};
+
+ access_level |= access_byte::PRESENT | access_byte::CODE_OR_DATA_SEGMENT;
+ if (segment_descriptor_type == segment_descriptor_type::CODE_SEGMENT)
+ {
+ flags |= gdt_flags::LONG_MODE;
+ access_level |= access_byte::CODE_SEGMENT | access_byte::READABLE;
+ }
+ else if (segment_descriptor_type == segment_descriptor_type::DATA_SEGMENT)
+ {
+ access_level |= access_byte::WRITABLE;
+ }
+
+ segment_descriptor_base const segment_descriptor_base{access_level, flags, BASE, LIMIT};
+ return segment_descriptor_base;
+ }
+
+ auto create_tss_descriptor(task_state_segment * tss) -> segment_descriptor_extension
+ {
+ uint64_t constexpr TSS_LIMIT = sizeof(task_state_segment) - 1;
+ access_byte const tss_access_byte{access_byte::PRESENT | access_byte::DESCRIPTOR_LEVEL_KERNEL |
+ access_byte::TASK_STATE_SEGMENT_AVAILABLE};
+ gdt_flags const tss_gdt_flags{0U, TSS_LIMIT};
+ segment_descriptor_extension const tss_descriptor{tss_access_byte, tss_gdt_flags, reinterpret_cast<uint64_t>(tss),
+ TSS_LIMIT};
+ return tss_descriptor;
+ }
+
+ auto create_gdt() -> global_descriptor_table
+ {
+ segment_descriptor_base const null_segment{0};
+ segment_descriptor_base const kernel_code_segment =
+ create_segment_descriptor(segment_descriptor_type::CODE_SEGMENT, access_byte::DESCRIPTOR_LEVEL_KERNEL);
+ segment_descriptor_base const kernel_data_segment =
+ create_segment_descriptor(segment_descriptor_type::DATA_SEGMENT, access_byte::DESCRIPTOR_LEVEL_KERNEL);
+ segment_descriptor_base const user_code_segment =
+ create_segment_descriptor(segment_descriptor_type::CODE_SEGMENT, access_byte::DESCRIPTOR_LEVEL_USER);
+ segment_descriptor_base const user_data_segment =
+ create_segment_descriptor(segment_descriptor_type::DATA_SEGMENT, access_byte::DESCRIPTOR_LEVEL_USER);
+
+ // Task State Segment needs to be kept alive
+ static auto tss = new task_state_segment();
+ segment_descriptor_extension const tss_descriptor = create_tss_descriptor(tss);
+
+ global_descriptor_table global_descriptor_table{null_segment,
+ kernel_code_segment,
+ kernel_data_segment,
+ user_code_segment,
+ user_data_segment,
+ tss_descriptor.get_first_gdt_entry(),
+ tss_descriptor.get_second_gdt_entry()};
+ return global_descriptor_table;
+ }
+ } // namespace
+
+ auto get_or_create_gdt() -> global_descriptor_table &
+ {
+ // Global Descriptor Table needs to be kept alive
+ static global_descriptor_table gdt = create_gdt();
+ return gdt;
+ }
+
+ auto update_gdtr() -> void
+ {
+ decltype(auto) gdt = get_or_create_gdt();
+
+ // Calculate the size of the gdt in bytes - 1. This subtraction occurs because the maximum value of Size is 65535,
+ // while the GDT can be up to 65536 bytes in length (8192 entries). Further, no GDT can have a size of 0 bytes.
+ uint16_t gdt_size = static_cast<uint16_t>((gdt.size() * sizeof(segment_descriptor_base)) - 1);
+ global_descriptor_table_pointer gdt_pointer{gdt_size, gdt.data()};
+ kernel::cpu::load_global_descriptor_table(gdt_pointer);
+
+ auto const stored_gdt_pointer = kernel::cpu::store_global_descriptor_table();
+ arch::exception_handling::assert(
+ gdt_pointer == stored_gdt_pointer,
+ "[Global Descriptor Table] Loaded GDTR value is not the same as the stored value.");
+ }
+
+ auto update_tss_register() -> void
+ {
+ decltype(auto) gdt = get_or_create_gdt();
+
+ // Load task state segment descriptor from the last element in the global descriptor table, done by calculating
+ // offset in bytes to the start of the segment descriptor (5 * 8) = 40
+ uint16_t tss_selector = (gdt.size() * sizeof(segment_descriptor_base)) - sizeof(segment_descriptor_extension);
+ kernel::cpu::load_task_register(tss_selector);
+
+ auto const stored_task_register = kernel::cpu::store_task_register();
+ arch::exception_handling::assert(tss_selector == stored_task_register,
+ "[Global Descriptor Table] Loaded TR value is not the same as the stored value.");
+ }
+
+} // namespace teachos::arch::context_switching::segment_descriptor_table
diff --git a/arch/x86_64/pre/src/context_switching/segment_descriptor_table/global_descriptor_table_pointer.cpp b/arch/x86_64/pre/src/context_switching/segment_descriptor_table/global_descriptor_table_pointer.cpp
new file mode 100644
index 0000000..79088b8
--- /dev/null
+++ b/arch/x86_64/pre/src/context_switching/segment_descriptor_table/global_descriptor_table_pointer.cpp
@@ -0,0 +1,11 @@
+#include "arch/context_switching/segment_descriptor_table/global_descriptor_table_pointer.hpp"
+
+namespace teachos::arch::context_switching::segment_descriptor_table
+{
+ global_descriptor_table_pointer::global_descriptor_table_pointer(uint16_t table_length, uint64_t * address)
+ : table_length(table_length)
+ , address(address)
+ {
+ // Nothing to do.
+ }
+} // namespace teachos::arch::context_switching::segment_descriptor_table
diff --git a/arch/x86_64/pre/src/context_switching/segment_descriptor_table/segment_descriptor_base.cpp b/arch/x86_64/pre/src/context_switching/segment_descriptor_table/segment_descriptor_base.cpp
new file mode 100644
index 0000000..04804d9
--- /dev/null
+++ b/arch/x86_64/pre/src/context_switching/segment_descriptor_table/segment_descriptor_base.cpp
@@ -0,0 +1,38 @@
+#include "arch/context_switching/segment_descriptor_table/segment_descriptor_base.hpp"
+
+namespace teachos::arch::context_switching::segment_descriptor_table
+{
+ segment_descriptor_base::segment_descriptor_base(uint64_t flags)
+ : _limit_1(flags)
+ , _base_1(flags >> 16U)
+ , _access(flags >> 40U)
+ , _flag(flags >> 52U, flags >> 48U)
+ , _base_2(flags >> 56U)
+ {
+ // Nothing to do.
+ }
+
+ segment_descriptor_base::segment_descriptor_base(access_byte access_byte, gdt_flags flags, uint32_t base,
+ std::bitset<20U> limit)
+ : _limit_1(limit.to_ulong())
+ , _base_1(base)
+ , _access(access_byte)
+ , _flag(flags)
+ , _base_2(base >> 24U)
+ {
+ // Nothing to do
+ }
+
+ auto segment_descriptor_base::get_segment_type() const -> segment_descriptor_type
+ {
+ if (!_access.contains_flags(access_byte::CODE_OR_DATA_SEGMENT))
+ {
+ return segment_descriptor_type::SYSTEM_SEGMENT;
+ }
+ return _access.contains_flags(access_byte::CODE_SEGMENT) ? segment_descriptor_type::CODE_SEGMENT
+ : segment_descriptor_type::DATA_SEGMENT;
+ }
+
+ segment_descriptor_base::operator uint64_t() const { return *reinterpret_cast<uint64_t const *>(this); }
+
+} // namespace teachos::arch::context_switching::segment_descriptor_table
diff --git a/arch/x86_64/pre/src/context_switching/segment_descriptor_table/segment_descriptor_extension.cpp b/arch/x86_64/pre/src/context_switching/segment_descriptor_table/segment_descriptor_extension.cpp
new file mode 100644
index 0000000..a28ec9b
--- /dev/null
+++ b/arch/x86_64/pre/src/context_switching/segment_descriptor_table/segment_descriptor_extension.cpp
@@ -0,0 +1,24 @@
+#include "arch/context_switching/segment_descriptor_table/segment_descriptor_extension.hpp"
+
+namespace teachos::arch::context_switching::segment_descriptor_table
+{
+ segment_descriptor_extension::segment_descriptor_extension(uint128_t flags)
+ : _base(flags)
+ , _base_3(flags >> 64U)
+ {
+ // Nothing to do.
+ }
+
+ segment_descriptor_extension::segment_descriptor_extension(access_byte access_byte, gdt_flags flags, uint64_t base,
+ std::bitset<20U> limit)
+ : _base(access_byte, flags, base, limit)
+ , _base_3(base >> 32U)
+ {
+ // Nothing to do
+ }
+
+ auto segment_descriptor_extension::get_first_gdt_entry() const -> segment_descriptor_base { return _base; }
+
+ auto segment_descriptor_extension::get_second_gdt_entry() const -> uint64_t { return _base_3; }
+
+} // namespace teachos::arch::context_switching::segment_descriptor_table
diff --git a/arch/x86_64/pre/src/context_switching/syscall/main.cpp b/arch/x86_64/pre/src/context_switching/syscall/main.cpp
new file mode 100644
index 0000000..b4ab468
--- /dev/null
+++ b/arch/x86_64/pre/src/context_switching/syscall/main.cpp
@@ -0,0 +1,35 @@
+#include "arch/context_switching/syscall/main.hpp"
+
+namespace teachos::arch::context_switching::syscall
+{
+ auto syscall(type syscall_number, arguments args) -> response
+ {
+ asm volatile("mov %[input], %%rax"
+ : /* no output from call */
+ : [input] "m"(syscall_number)
+ : "memory");
+
+ asm volatile("mov %[input], %%rdi " : /* no output from call */ : [input] "m"(args.arg_0) : "memory");
+ asm volatile("mov %[input], %%rsi" : /* no output from call */ : [input] "m"(args.arg_1) : "memory");
+ asm volatile("mov %[input], %%rdx" : /* no output from call */ : [input] "m"(args.arg_2) : "memory");
+ asm volatile("mov %[input], %%r10" : /* no output from call */ : [input] "m"(args.arg_3) : "memory");
+ asm volatile("mov %[input], %%r8" : /* no output from call */ : [input] "m"(args.arg_4) : "memory");
+ asm volatile("mov %[input], %%r9" : /* no output from call */ : [input] "m"(args.arg_5) : "memory");
+
+ asm volatile("syscall");
+
+ arguments values{};
+ asm volatile("mov %%rdi, %[output]" : [output] "=m"(values.arg_0));
+ asm volatile("mov %%rsi, %[output]" : [output] "=m"(values.arg_1));
+ asm volatile("mov %%rdx, %[output]" : [output] "=m"(values.arg_2));
+ asm volatile("mov %%r10, %[output]" : [output] "=m"(values.arg_3));
+ asm volatile("mov %%r8, %[output]" : [output] "=m"(values.arg_4));
+ asm volatile("mov %%r9, %[output]" : [output] "=m"(values.arg_5));
+
+ error error_code{};
+ asm volatile("mov %%al, %[output]" : [output] "=m"(error_code));
+
+ return {error_code, values};
+ }
+
+} // namespace teachos::arch::context_switching::syscall
diff --git a/arch/x86_64/pre/src/context_switching/syscall/syscall_enable.cpp b/arch/x86_64/pre/src/context_switching/syscall/syscall_enable.cpp
new file mode 100644
index 0000000..3c43336
--- /dev/null
+++ b/arch/x86_64/pre/src/context_switching/syscall/syscall_enable.cpp
@@ -0,0 +1,32 @@
+#include "arch/context_switching/syscall/syscall_enable.hpp"
+
+#include "arch/context_switching/interrupt_descriptor_table/segment_selector.hpp"
+#include "arch/context_switching/syscall/syscall_handler.hpp"
+#include "arch/kernel/cpu/msr.hpp"
+
+namespace teachos::arch::context_switching::syscall
+{
+ namespace
+ {
+ interrupt_descriptor_table::segment_selector constexpr KERNEL_CODE_SEGMENT_SELECTOR{
+ 1U, interrupt_descriptor_table::segment_selector::REQUEST_LEVEL_KERNEL};
+
+ auto constexpr IA32_STAR_ADDRESS = 0xC0000081;
+ auto constexpr IA32_LSTAR_ADDRESS = 0xC0000082;
+ auto constexpr IA32_FMASK_ADDRESS = 0xC0000084;
+
+ } // namespace
+
+ auto enable_syscall() -> void
+ {
+ uint64_t const syscall_function = reinterpret_cast<uint64_t>(syscall_handler);
+ kernel::cpu::write_msr(IA32_LSTAR_ADDRESS, syscall_function);
+ kernel::cpu::write_msr(IA32_FMASK_ADDRESS, 0U);
+
+ uint64_t const kernel_cs = KERNEL_CODE_SEGMENT_SELECTOR;
+ uint64_t const star_value = (kernel_cs << 32) | (kernel_cs << 48);
+ kernel::cpu::write_msr(IA32_STAR_ADDRESS, star_value);
+
+ kernel::cpu::set_efer_bit(kernel::cpu::efer_flags::SCE);
+ }
+} // namespace teachos::arch::context_switching::syscall
diff --git a/arch/x86_64/pre/src/context_switching/syscall/syscall_handler.cpp b/arch/x86_64/pre/src/context_switching/syscall/syscall_handler.cpp
new file mode 100644
index 0000000..84dbe5f
--- /dev/null
+++ b/arch/x86_64/pre/src/context_switching/syscall/syscall_handler.cpp
@@ -0,0 +1,118 @@
+#include "arch/context_switching/syscall/syscall_handler.hpp"
+
+#include "arch/context_switching/syscall/main.hpp"
+#include "arch/exception_handling/assert.hpp"
+#include "arch/exception_handling/panic.hpp"
+#include "arch/memory/heap/global_heap_allocator.hpp"
+#include "arch/memory/main.hpp"
+#include "arch/video/vga/text.hpp"
+
+namespace teachos::arch::context_switching::syscall
+{
+
+ namespace
+ {
+ auto write_to_vga_buffer(uint64_t buffer) -> response
+ {
+ video::vga::text::write(reinterpret_cast<const char *>(buffer),
+ video::vga::text::common_attributes::green_on_black);
+ video::vga::text::newline();
+ return {error::OK};
+ }
+
+ auto expand_user_heap() -> response
+ {
+ static auto current_heap_end = memory::heap::USER_HEAP_START;
+ uint64_t const heap_start = current_heap_end;
+ memory::remap_heap(heap_start, memory::heap::USER_HEAP_SIZE, memory::paging::entry::USER_ACCESSIBLE);
+ current_heap_end += memory::heap::USER_HEAP_SIZE;
+ return {error::OK, {heap_start, memory::heap::USER_HEAP_SIZE}};
+ }
+ } // namespace
+
+ auto syscall_handler() -> void
+ {
+ // Saving state of rcx and r11 because it is required by sysretq to function.
+ // Calls to other functions potentially overwrite these registers, because of
+ // callee saved calling convention.
+ uint64_t return_instruction_pointer, rflags = {};
+ asm volatile("mov %%rcx, %[output]" : [output] "=m"(return_instruction_pointer));
+ asm volatile("mov %%r11, %[output]" : [output] "=m"(rflags));
+
+ uint64_t syscall_number, arg_0, arg_1, arg_2, arg_3, arg_4, arg_5 = {};
+ asm volatile("mov %%rdi, %[output]" : [output] "=m"(arg_0));
+ asm volatile("mov %%rsi, %[output]" : [output] "=m"(arg_1));
+ asm volatile("mov %%rdx, %[output]" : [output] "=m"(arg_2));
+ asm volatile("mov %%r10, %[output]" : [output] "=m"(arg_3));
+ asm volatile("mov %%r8, %[output]" : [output] "=m"(arg_4));
+ asm volatile("mov %%r9, %[output]" : [output] "=m"(arg_5));
+
+ // RAX is read last, because paired with our type enum, we can use it to check
+ // if the register has been written by the compiled code between executing the syscall
+ // and now.
+ asm volatile("mov %%rax, %[output]" : [output] "=m"(syscall_number));
+
+ response result;
+ switch (static_cast<type>(syscall_number))
+ {
+ case type::WRITE:
+ result = write_to_vga_buffer(arg_0);
+ break;
+ case type::EXPAND_HEAP:
+ result = expand_user_heap();
+ break;
+ case type::ASSERT:
+ teachos::arch::exception_handling::assert(arg_0, reinterpret_cast<const char *>(arg_1));
+ break;
+ default:
+ teachos::arch::exception_handling::panic("[Syscall Handler] Invalid syscall number");
+ break;
+ }
+
+ asm volatile("mov %[input], %%rax"
+ : /* no output from call */
+ : [input] "m"(result.error_code)
+ : "memory");
+
+ asm volatile("mov %[input], %%rdi"
+ : /* no output from call */
+ : [input] "m"(result.values.arg_0)
+ : "memory");
+ asm volatile("mov %[input], %%rsi"
+ : /* no output from call */
+ : [input] "m"(result.values.arg_1)
+ : "memory");
+ asm volatile("mov %[input], %%rdx"
+ : /* no output from call */
+ : [input] "m"(result.values.arg_2)
+ : "memory");
+ asm volatile("mov %[input], %%r10"
+ : /* no output from call */
+ : [input] "m"(result.values.arg_3)
+ : "memory");
+ asm volatile("mov %[input], %%r8"
+ : /* no output from call */
+ : [input] "m"(result.values.arg_4)
+ : "memory");
+ asm volatile("mov %[input], %%r9"
+ : /* no output from call */
+ : [input] "m"(result.values.arg_5)
+ : "memory");
+
+ asm volatile("mov %[input], %%rcx"
+ : /* no output from call */
+ : [input] "m"(return_instruction_pointer)
+ : "memory");
+ asm volatile("mov %[input], %%r11"
+ : /* no output from call */
+ : [input] "m"(rflags)
+ : "memory");
+
+ // Additionally call leave, because x86 allocates stack space for the internal variables. If we do not clean up this
+ // newly created stack frame the syscall instruction that landed in this syscall_handler, will never return to the
+ // method that originally called it, because the RIP has not been restored from the previous stack frame.
+ asm volatile("leave\n"
+ "sysretq");
+ }
+
+} // namespace teachos::arch::context_switching::syscall
diff --git a/arch/x86_64/pre/src/exception_handling/abort.cpp b/arch/x86_64/pre/src/exception_handling/abort.cpp
new file mode 100644
index 0000000..e12e4cb
--- /dev/null
+++ b/arch/x86_64/pre/src/exception_handling/abort.cpp
@@ -0,0 +1,15 @@
+#include "arch/exception_handling/panic.hpp"
+
+#include <cstdlib>
+
+namespace teachos::arch::exception_handling
+{
+ /**
+ * @brief Override for the newlib abort function.
+ *
+ * @note newlib defines @p ::abort as a weak symbol, thus allowing implementations to override it by simply providing
+ * a matching implementation. Since the default implemenatation calls a number of functions the kernel does not
+ * currently implement, @p ::abort gets overridden to simply panic.
+ */
+ extern "C" auto abort() -> void { panic("Terminate was called, possibly due to an unhandled exception"); }
+} // namespace teachos::arch::exception_handling
diff --git a/arch/x86_64/pre/src/exception_handling/assert.cpp b/arch/x86_64/pre/src/exception_handling/assert.cpp
new file mode 100644
index 0000000..b2963de
--- /dev/null
+++ b/arch/x86_64/pre/src/exception_handling/assert.cpp
@@ -0,0 +1,15 @@
+#include "arch/exception_handling/assert.hpp"
+
+#include "arch/exception_handling/panic.hpp"
+
+namespace teachos::arch::exception_handling
+{
+ auto assert(bool condition, char const * message) -> void
+ {
+ if (condition)
+ {
+ return;
+ }
+ panic("Assertion Violation: ", message);
+ }
+} // namespace teachos::arch::exception_handling
diff --git a/arch/x86_64/pre/src/exception_handling/panic.cpp b/arch/x86_64/pre/src/exception_handling/panic.cpp
new file mode 100644
index 0000000..8e3802a
--- /dev/null
+++ b/arch/x86_64/pre/src/exception_handling/panic.cpp
@@ -0,0 +1,22 @@
+#include "arch/exception_handling/panic.hpp"
+
+#include "arch/kernel/halt.hpp"
+#include "arch/video/vga/text.hpp"
+
+namespace teachos::arch::exception_handling
+{
+ extern "C" char const message_prefix_panic[];
+
+ auto panic(char const * reason) -> void { panic(message_prefix_panic, reason); }
+
+ auto panic(char const * prefix, char const * reason) -> void
+ {
+ using video::vga::text::common_attributes::white_on_red;
+
+ video::vga::text::newline();
+ video::vga::text::write(prefix, white_on_red);
+ video::vga::text::write(reason, white_on_red);
+
+ kernel::halt();
+ };
+} // namespace teachos::arch::exception_handling
diff --git a/arch/x86_64/pre/src/exception_handling/pure_virtual.cpp b/arch/x86_64/pre/src/exception_handling/pure_virtual.cpp
new file mode 100644
index 0000000..67772f7
--- /dev/null
+++ b/arch/x86_64/pre/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/pre/src/interrupt_handling/generic_interrupt_handler.cpp b/arch/x86_64/pre/src/interrupt_handling/generic_interrupt_handler.cpp
new file mode 100644
index 0000000..9d061a8
--- /dev/null
+++ b/arch/x86_64/pre/src/interrupt_handling/generic_interrupt_handler.cpp
@@ -0,0 +1,13 @@
+#include "arch/interrupt_handling/generic_interrupt_handler.hpp"
+
+#include "arch/video/vga/text.hpp"
+
+namespace teachos::arch::interrupt_handling
+{
+ auto generic_interrupt_handler(interrupt_frame * frame) -> void
+ {
+ (void)frame;
+ video::vga::text::write("An Interrupt occurred.", video::vga::text::common_attributes::green_on_black);
+ video::vga::text::newline();
+ }
+} // namespace teachos::arch::interrupt_handling
diff --git a/arch/x86_64/pre/src/kernel/cpu/call.cpp b/arch/x86_64/pre/src/kernel/cpu/call.cpp
new file mode 100644
index 0000000..98fa248
--- /dev/null
+++ b/arch/x86_64/pre/src/kernel/cpu/call.cpp
@@ -0,0 +1,9 @@
+#include "arch/kernel/cpu/call.hpp"
+
+namespace teachos::arch::kernel::cpu
+{
+ auto call(far_pointer pointer) -> void
+ {
+ asm volatile("rex64 lcall *%[input]" : /* no output from call */ : [input] "m"(pointer));
+ }
+} // namespace teachos::arch::kernel::cpu
diff --git a/arch/x86_64/pre/src/kernel/cpu/gdtr.cpp b/arch/x86_64/pre/src/kernel/cpu/gdtr.cpp
new file mode 100644
index 0000000..74a4e1c
--- /dev/null
+++ b/arch/x86_64/pre/src/kernel/cpu/gdtr.cpp
@@ -0,0 +1,19 @@
+#include "arch/kernel/cpu/gdtr.hpp"
+
+#include "arch/context_switching/segment_descriptor_table/global_descriptor_table_pointer.hpp"
+
+namespace teachos::arch::kernel::cpu
+{
+ auto store_global_descriptor_table() -> context_switching::segment_descriptor_table::global_descriptor_table_pointer
+ {
+ context_switching::segment_descriptor_table::global_descriptor_table_pointer current_value{};
+ asm("sgdt %[output]" : [output] "=m"(current_value));
+ return current_value;
+ }
+
+ auto load_global_descriptor_table(
+ context_switching::segment_descriptor_table::global_descriptor_table_pointer const & gdt_pointer) -> void
+ {
+ asm volatile("lgdt %[input]" : /* no output from call */ : [input] "m"(gdt_pointer));
+ }
+} // namespace teachos::arch::kernel::cpu
diff --git a/arch/x86_64/pre/src/kernel/cpu/idtr.cpp b/arch/x86_64/pre/src/kernel/cpu/idtr.cpp
new file mode 100644
index 0000000..7aa20c1
--- /dev/null
+++ b/arch/x86_64/pre/src/kernel/cpu/idtr.cpp
@@ -0,0 +1,18 @@
+#include "arch/kernel/cpu/idtr.hpp"
+
+namespace teachos::arch::kernel::cpu
+{
+ auto store_interrupt_descriptor_table()
+ -> context_switching::interrupt_descriptor_table::interrupt_descriptor_table_pointer
+ {
+ context_switching::interrupt_descriptor_table::interrupt_descriptor_table_pointer current_value{};
+ asm("sidt %[output]" : [output] "=m"(current_value));
+ return current_value;
+ }
+
+ auto load_interrupt_descriptor_table(
+ context_switching::interrupt_descriptor_table::interrupt_descriptor_table_pointer const & idt_pointer) -> void
+ {
+ asm volatile("lidt %[input]" : /* no output from call */ : [input] "m"(idt_pointer));
+ }
+} // namespace teachos::arch::kernel::cpu
diff --git a/arch/x86_64/pre/src/kernel/cpu/if.cpp b/arch/x86_64/pre/src/kernel/cpu/if.cpp
new file mode 100644
index 0000000..60a90a3
--- /dev/null
+++ b/arch/x86_64/pre/src/kernel/cpu/if.cpp
@@ -0,0 +1,7 @@
+namespace teachos::arch::kernel::cpu
+{
+ auto set_interrupt_flag() -> void { asm volatile("sti"); }
+
+ auto clear_interrupt_flag() -> void { asm volatile("cli"); }
+
+} // namespace teachos::arch::kernel::cpu
diff --git a/arch/x86_64/pre/src/kernel/cpu/msr.cpp b/arch/x86_64/pre/src/kernel/cpu/msr.cpp
new file mode 100644
index 0000000..9c474a1
--- /dev/null
+++ b/arch/x86_64/pre/src/kernel/cpu/msr.cpp
@@ -0,0 +1,31 @@
+#include "arch/kernel/cpu/msr.hpp"
+
+namespace teachos::arch::kernel::cpu
+{
+ namespace
+ {
+ auto constexpr IA32_EFER_ADDRESS = 0xC0000080;
+ }
+
+ auto read_msr(uint32_t msr) -> uint64_t
+ {
+ uint32_t low, high;
+ asm volatile("rdmsr" : "=a"(low), "=d"(high) : "c"(msr));
+ return (static_cast<uint64_t>(high) << 32) | low;
+ }
+
+ auto write_msr(uint32_t msr, uint64_t value) -> void
+ {
+ uint32_t low = value;
+ uint32_t high = value >> 32;
+ asm volatile("wrmsr"
+ : /* no output from call */
+ : "c"(msr), "a"(low), "d"(high));
+ }
+
+ auto set_efer_bit(efer_flags flag) -> void
+ {
+ auto const efer = read_msr(IA32_EFER_ADDRESS);
+ write_msr(IA32_EFER_ADDRESS, static_cast<std::underlying_type<efer_flags>::type>(flag) | efer);
+ }
+} // namespace teachos::arch::kernel::cpu
diff --git a/arch/x86_64/pre/src/kernel/cpu/segment_register.cpp b/arch/x86_64/pre/src/kernel/cpu/segment_register.cpp
new file mode 100644
index 0000000..b08c9c4
--- /dev/null
+++ b/arch/x86_64/pre/src/kernel/cpu/segment_register.cpp
@@ -0,0 +1,98 @@
+#include "arch/kernel/cpu/segment_register.hpp"
+
+#include "arch/context_switching/interrupt_descriptor_table/segment_selector.hpp"
+#include "arch/exception_handling/assert.hpp"
+
+namespace teachos::arch::kernel::cpu
+{
+ auto reload_data_segment_registers() -> void
+ {
+ asm volatile("xor %%rax, %%rax\n"
+ "mov %%rax, %%ss\n"
+ "mov %%rax, %%ds\n"
+ "mov %%rax, %%es\n"
+ "mov %%rax, %%fs\n"
+ "mov %%rax, %%gs\n"
+ : /* no output from call */
+ : /* no input to call */
+ : "rax");
+ }
+
+ auto set_data_segment_registers(context_switching::interrupt_descriptor_table::segment_selector data_segment) -> void
+ {
+ asm volatile("xor %%rax, %%rax\n"
+ "mov %[input], %%ax\n"
+ "mov %%rax, %%ds\n"
+ "mov %%rax, %%es\n"
+ "mov %%rax, %%fs\n"
+ "mov %%rax, %%gs\n"
+ : /* no output from call */
+ : [input] "m"(data_segment)
+ : "rax");
+ }
+
+ auto read_code_segment_register() -> context_switching::interrupt_descriptor_table::segment_selector
+ {
+ context_switching::interrupt_descriptor_table::segment_selector current_value{};
+ asm volatile("mov %%cs, %[output]" : [output] "=r"(current_value));
+ return current_value;
+ }
+
+ auto validate_data_segment_registers(context_switching::interrupt_descriptor_table::segment_selector data_segment)
+ -> void
+ {
+ context_switching::interrupt_descriptor_table::segment_selector ss{};
+ context_switching::interrupt_descriptor_table::segment_selector ds{};
+ context_switching::interrupt_descriptor_table::segment_selector es{};
+ context_switching::interrupt_descriptor_table::segment_selector fs{};
+ context_switching::interrupt_descriptor_table::segment_selector gs{};
+
+ asm volatile(
+ "mov %%ss, %[ss_output]\n"
+ "mov %%ds, %[ds_output]\n"
+ "mov %%es, %[es_output]\n"
+ "mov %%fs, %[fs_output]\n"
+ "mov %%gs, %[gs_output]\n"
+ : [ss_output] "=r"(ss), [ds_output] "=r"(ds), [es_output] "=r"(es), [fs_output] "=r"(fs), [gs_output] "=r"(gs));
+
+ auto result = (ss == ds && ss == es && ss == fs && ss == gs);
+ exception_handling::assert(result, "[Segment Register] Values in data register are not the same.");
+ result = (ss == data_segment);
+ exception_handling::assert(
+ result, "[Segment Register] Expected Data Segment is not the same as the value in the Stack Segment register.");
+ }
+
+ auto validate_code_segment_register(context_switching::interrupt_descriptor_table::segment_selector code_segment)
+ -> void
+ {
+ auto const cs = read_code_segment_register();
+ exception_handling::assert(
+ cs == code_segment,
+ "[Segment Register] Expected Code Segment is not the same as the value in the Code Segment register.");
+ }
+
+ auto validate_segment_registers(context_switching::interrupt_descriptor_table::segment_selector data_segment,
+ context_switching::interrupt_descriptor_table::segment_selector code_segment) -> void
+ {
+ validate_data_segment_registers(data_segment);
+ validate_code_segment_register(code_segment);
+ }
+
+ auto set_code_segment_register(context_switching::interrupt_descriptor_table::segment_selector data_segment,
+ context_switching::interrupt_descriptor_table::segment_selector code_segment,
+ uint64_t address) -> void
+ {
+ asm volatile("mov %%rsp, %%rax\n"
+ "push %[data_segment]\n"
+ "push %%rax\n"
+ "pushfq\n"
+ "push %[code_segment]\n"
+ "mov %[return_function], %%rax\n"
+ "push %%rax\n"
+ "iretq\n"
+ : /* no output from call */
+ : [data_segment] "m"(data_segment), [code_segment] "m"(code_segment), [return_function] "r"(address)
+ : "rax");
+ }
+
+} // namespace teachos::arch::kernel::cpu
diff --git a/arch/x86_64/pre/src/kernel/cpu/tr.cpp b/arch/x86_64/pre/src/kernel/cpu/tr.cpp
new file mode 100644
index 0000000..a435540
--- /dev/null
+++ b/arch/x86_64/pre/src/kernel/cpu/tr.cpp
@@ -0,0 +1,16 @@
+#include "arch/kernel/cpu/tr.hpp"
+
+namespace teachos::arch::kernel::cpu
+{
+ auto store_task_register() -> uint16_t
+ {
+ uint16_t current_value{};
+ asm("str %[output]" : [output] "=r"(current_value));
+ return current_value;
+ }
+
+ auto load_task_register(uint16_t gdt_offset) -> void
+ {
+ asm volatile("ltr %[input]" : /* no output from call */ : [input] "m"(gdt_offset));
+ }
+} // namespace teachos::arch::kernel::cpu
diff --git a/arch/x86_64/pre/src/kernel/main.cpp b/arch/x86_64/pre/src/kernel/main.cpp
new file mode 100644
index 0000000..43b5f90
--- /dev/null
+++ b/arch/x86_64/pre/src/kernel/main.cpp
@@ -0,0 +1,71 @@
+#include "arch/kernel/main.hpp"
+
+#include "arch/boot/pointers.hpp"
+#include "arch/context_switching/interrupt_descriptor_table/segment_selector.hpp"
+#include "arch/context_switching/main.hpp"
+#include "arch/kernel/cpu/if.hpp"
+#include "arch/kernel/cpu/segment_register.hpp"
+#include "arch/memory/heap/bump_allocator.hpp"
+#include "arch/memory/heap/global_heap_allocator.hpp"
+#include "arch/memory/main.hpp"
+#include "arch/memory/multiboot/reader.hpp"
+#include "arch/stl/vector.hpp"
+#include "arch/video/vga/text.hpp"
+
+namespace teachos::arch::kernel
+{
+ auto stack_overflow_test(int count) -> int
+ {
+ int test[5000] = {};
+ if (test[0] == 0xFFFF)
+ {
+ return count;
+ }
+ count = stack_overflow_test(count);
+ return count++;
+ }
+
+ auto heap_test() -> void
+ {
+ auto test2 = new memory::multiboot::memory_information{};
+ auto test3 = new memory::multiboot::memory_information{};
+ auto test4 = *test2;
+ auto test5 = *test3;
+ test4.kernel_end = 5000;
+ test5.kernel_end = 3000;
+ auto test6 = test4.kernel_end;
+ auto test7 = test5.kernel_end;
+ auto test8 = memory::multiboot::read_multiboot2();
+ if (test6 && test7 && test8.kernel_end)
+ {
+ video::vga::text::write("Heap test successful", video::vga::text::common_attributes::green_on_black);
+ }
+ test2->kernel_end = 2000;
+ test2->kernel_start = 1000;
+ test2->multiboot_start = 2000;
+ delete test2;
+ delete test3;
+
+ auto test9 = new int(50);
+ delete test9;
+ }
+
+ auto main() -> void
+ {
+ video::vga::text::clear();
+ video::vga::text::cursor(false);
+ video::vga::text::write("TeachOS is starting up...", video::vga::text::common_attributes::green_on_black);
+ video::vga::text::newline();
+
+ memory::initialize_memory_management();
+ // stack_overflow_test(0);
+
+ memory::heap::global_heap_allocator::register_heap_allocator(memory::heap::heap_allocator_type::LINKED_LIST);
+ // heap_test();
+
+ auto address = memory::heap::global_heap_allocator::kmalloc(8U);
+ (void)address;
+
+ context_switching::switch_to_user_mode();
+ }
+} // namespace teachos::arch::kernel
diff --git a/arch/x86_64/pre/src/memory/allocator/area_frame_allocator.cpp b/arch/x86_64/pre/src/memory/allocator/area_frame_allocator.cpp
new file mode 100644
index 0000000..a5a1b49
--- /dev/null
+++ b/arch/x86_64/pre/src/memory/allocator/area_frame_allocator.cpp
@@ -0,0 +1,85 @@
+#include "arch/memory/allocator/area_frame_allocator.hpp"
+
+#include "arch/exception_handling/assert.hpp"
+
+#include <algorithm>
+#include <array>
+#include <ranges>
+
+namespace teachos::arch::memory::allocator
+{
+ area_frame_allocator::area_frame_allocator(multiboot::memory_information const & mem_info)
+ : next_free_frame()
+ , current_area(std::nullopt)
+ , memory_areas(mem_info.areas)
+ , kernel_start(physical_frame::containing_address(mem_info.kernel_start))
+ , kernel_end(physical_frame::containing_address(mem_info.kernel_end))
+ , multiboot_start(physical_frame::containing_address(mem_info.multiboot_start))
+ , multiboot_end(physical_frame::containing_address(mem_info.multiboot_end))
+ {
+ choose_next_area();
+ }
+
+ auto area_frame_allocator::choose_next_area() -> void
+ {
+ current_area = std::nullopt;
+ auto next_area_with_free_frames = memory_areas | std::views::filter([this](auto const & area) {
+ auto address = area.base_address + area.area_length - 1;
+ return physical_frame::containing_address(address) >= next_free_frame;
+ });
+
+ auto const lowest_area_with_free_frames = std::ranges::min_element(
+ next_area_with_free_frames, [](auto const & a, auto const & b) { return a.base_address < b.base_address; });
+
+ if (lowest_area_with_free_frames != next_area_with_free_frames.end())
+ {
+ current_area = *lowest_area_with_free_frames;
+ // Update the `next_free_frame` according to the new memory area
+ auto const start_frame = physical_frame::containing_address(current_area.value().base_address);
+ if (next_free_frame < start_frame)
+ {
+ next_free_frame = start_frame;
+ }
+ }
+ }
+
+ auto area_frame_allocator::allocate_frame() -> std::optional<physical_frame>
+ {
+ // Only try to allocate memory if current_area is not null, because
+ // the current_area is null if there is no more available memory.
+ if (!current_area.has_value())
+ {
+ return std::nullopt;
+ }
+
+ auto const address = current_area.value().base_address + current_area.value().area_length - 1;
+ physical_frame current_area_last_frame = physical_frame::containing_address(address);
+
+ if (next_free_frame > current_area_last_frame)
+ {
+ // All frames of current area are used, switch to next area.
+ choose_next_area();
+ }
+ else if (next_free_frame >= kernel_start && next_free_frame <= kernel_end)
+ {
+ // `physical_frame` is used by the kernel or multiboot information structure.
+ next_free_frame = allocator::physical_frame{kernel_end.frame_number + 1};
+ }
+ else if (next_free_frame >= multiboot_start && next_free_frame <= multiboot_end)
+ {
+ // `physical_frame` is used by the kernel or multiboot information structure.
+ next_free_frame = allocator::physical_frame{multiboot_end.frame_number + 1};
+ }
+ else
+ {
+ // Frame is unused, increment `next_free_frame` and return it.
+ next_free_frame.frame_number += 1;
+ return next_free_frame;
+ }
+
+ // `physical_frame` was not valid, try it again with the updated `next_free_frame`.
+ return allocate_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/pre/src/memory/allocator/tiny_frame_allocator.cpp b/arch/x86_64/pre/src/memory/allocator/tiny_frame_allocator.cpp
new file mode 100644
index 0000000..3cdf9c7
--- /dev/null
+++ b/arch/x86_64/pre/src/memory/allocator/tiny_frame_allocator.cpp
@@ -0,0 +1,34 @@
+#include "arch/memory/allocator/tiny_frame_allocator.hpp"
+
+#include "arch/exception_handling/panic.hpp"
+
+namespace teachos::arch::memory::allocator
+{
+ auto tiny_frame_allocator::allocate_frame() -> std::optional<physical_frame>
+ {
+ for (auto & frame_option : frames)
+ {
+ if (frame_option.has_value())
+ {
+ auto value = frame_option;
+ frame_option.reset();
+ return value;
+ }
+ }
+ return std::nullopt;
+ }
+
+ auto tiny_frame_allocator::deallocate_frame(physical_frame const & physical_frame) -> void
+ {
+ for (auto & frame_option : frames)
+ {
+ if (!frame_option.has_value())
+ {
+ frame_option.emplace(physical_frame);
+ return;
+ }
+ }
+ exception_handling::panic(
+ "[Tiny Frame Allocator] Attempted to deallocate more than the 3 frames, that can be held");
+ }
+} // namespace teachos::arch::memory::allocator
diff --git a/arch/x86_64/pre/src/memory/heap/bump_allocator.cpp b/arch/x86_64/pre/src/memory/heap/bump_allocator.cpp
new file mode 100644
index 0000000..525f45c
--- /dev/null
+++ b/arch/x86_64/pre/src/memory/heap/bump_allocator.cpp
@@ -0,0 +1,54 @@
+#include "arch/memory/heap/bump_allocator.hpp"
+
+#include "arch/exception_handling/assert.hpp"
+
+#include <limits>
+#include <type_traits>
+
+namespace teachos::arch::memory::heap
+{
+ namespace
+ {
+ template<typename T>
+ auto saturating_add(T x, T y) -> T
+ requires std::is_unsigned_v<T>
+ {
+ if (x > std::numeric_limits<T>::max() - y)
+ {
+ return std::numeric_limits<T>::max();
+ }
+ T result = x + y;
+ return result;
+ }
+ } // namespace
+
+ auto bump_allocator::allocate(std::size_t size) -> void *
+ {
+ // Reading the value only has to be done once, because compare_exchange_weak updates the value as well if the
+ // exchange failed, becuase the value was not the expected one.
+ auto alloc_start = next.load(std::memory_order::relaxed);
+ // Repeat allocation until it succeeds, has to be done, because another allocator could overtake it at any time
+ // causing the value to differ and the calculation to have to be redone.
+ for (;;)
+ {
+ auto const alloc_end = saturating_add(alloc_start, size);
+ arch::exception_handling::assert(alloc_end <= heap_end, "[Heap Allocator] Out of memory");
+ // Check if the atomic value is still the one initally loaded, if it isn't we have been overtaken by another
+ // thread and need to redo the calculation. Spurious failure by weak can be ignored, because the whole allocation
+ // is wrapped in an infinite for loop so a failure that wasn't actually one will simply be retried until it works.
+ auto const updated = next.compare_exchange_weak(alloc_start, alloc_end, std::memory_order::relaxed);
+ if (updated)
+ {
+ return reinterpret_cast<void *>(alloc_start);
+ }
+ }
+ }
+
+ auto bump_allocator::deallocate(void * pointer) noexcept -> void
+ {
+ if (pointer)
+ {
+ }
+ }
+
+} // namespace teachos::arch::memory::heap
diff --git a/arch/x86_64/pre/src/memory/heap/global_heap_allocator.cpp b/arch/x86_64/pre/src/memory/heap/global_heap_allocator.cpp
new file mode 100644
index 0000000..35cd623
--- /dev/null
+++ b/arch/x86_64/pre/src/memory/heap/global_heap_allocator.cpp
@@ -0,0 +1,135 @@
+#include "arch/memory/heap/global_heap_allocator.hpp"
+
+#include "arch/context_switching/syscall/main.hpp"
+#include "arch/exception_handling/assert.hpp"
+#include "arch/kernel/cpu/segment_register.hpp"
+#include "arch/memory/heap/bump_allocator.hpp"
+#include "arch/memory/heap/linked_list_allocator.hpp"
+#include "arch/memory/heap/user_heap_allocator.hpp"
+
+namespace teachos::arch::memory::heap
+{
+ namespace
+ {
+ constexpr char NOT_REGISTRED_ERROR_MESSAGE[] =
+ "Attempted to allocate or deallocate using the global_heap_allocator before "
+ "register_heap_allocation_type was called.";
+ constexpr uint16_t KERNEL_CODE_INDEX = 1U;
+
+ [[gnu::section(".user_text")]]
+ auto os_in_kernel_mode() -> bool
+ {
+ auto const cs = teachos::arch::kernel::cpu::read_code_segment_register();
+ return cs.get_index() == KERNEL_CODE_INDEX;
+ }
+ } // namespace
+
+ heap_allocator * global_heap_allocator::kernel_allocator_instance = nullptr;
+ user_heap_allocator * global_heap_allocator::user_allocator_instance = nullptr;
+
+ auto global_heap_allocator::kmalloc(std::size_t size) -> void * { return kernel().allocate(size); }
+
+ auto global_heap_allocator::kfree(void * pointer) noexcept -> void { kernel().deallocate(pointer); }
+
+ auto global_heap_allocator::malloc(std::size_t size) -> void * { return user().allocate(size); }
+
+ auto global_heap_allocator::free(void * pointer) noexcept -> void { user().deallocate(pointer); }
+
+ auto global_heap_allocator::register_heap_allocator(heap_allocator_type new_type) -> void
+ {
+ exception_handling::assert(kernel_allocator_instance == nullptr,
+ "Calling register_heap_allocator_type can only be done once.");
+
+ switch (new_type)
+ {
+ case heap_allocator_type::NONE:
+ // Nothing to do
+ break;
+ case heap_allocator_type::BUMP: {
+ static bump_allocator kernel_allocator{KERNEL_HEAP_START, KERNEL_HEAP_START + KERNEL_HEAP_SIZE};
+ kernel_allocator_instance = &kernel_allocator;
+ break;
+ }
+ case heap_allocator_type::LINKED_LIST: {
+ static linked_list_allocator kernel_allocator{KERNEL_HEAP_START, KERNEL_HEAP_START + KERNEL_HEAP_SIZE};
+ kernel_allocator_instance = &kernel_allocator;
+ break;
+ }
+ }
+
+ [[gnu::section(".user_data")]]
+ static user_heap_allocator user_allocator{};
+ user_allocator_instance = &user_allocator;
+ }
+
+ auto global_heap_allocator::kernel() -> heap_allocator &
+ {
+ exception_handling::assert(kernel_allocator_instance != nullptr, NOT_REGISTRED_ERROR_MESSAGE);
+
+ return *kernel_allocator_instance;
+ }
+
+ auto global_heap_allocator::user() -> user_heap_allocator &
+ {
+ context_switching::syscall::syscall(
+ context_switching::syscall::type::ASSERT,
+ {user_allocator_instance != nullptr, reinterpret_cast<uint64_t>(&NOT_REGISTRED_ERROR_MESSAGE)});
+ return *user_allocator_instance;
+ }
+} // namespace teachos::arch::memory::heap
+
+auto operator new(std::size_t size) -> void *
+{
+ if (teachos::arch::memory::heap::os_in_kernel_mode())
+ {
+ return teachos::arch::memory::heap::global_heap_allocator::kmalloc(size);
+ }
+ return teachos::arch::memory::heap::global_heap_allocator::malloc(size);
+}
+
+auto operator delete(void * pointer) noexcept -> void
+{
+ if (teachos::arch::memory::heap::os_in_kernel_mode())
+ {
+ teachos::arch::memory::heap::global_heap_allocator::kfree(pointer);
+ }
+ teachos::arch::memory::heap::global_heap_allocator::free(pointer);
+}
+
+auto operator delete(void * pointer, std::size_t size) noexcept -> void
+{
+ (void)size;
+ if (teachos::arch::memory::heap::os_in_kernel_mode())
+ {
+ teachos::arch::memory::heap::global_heap_allocator::kfree(pointer);
+ }
+ teachos::arch::memory::heap::global_heap_allocator::free(pointer);
+}
+
+auto operator new[](std::size_t size) -> void *
+{
+ if (teachos::arch::memory::heap::os_in_kernel_mode())
+ {
+ return teachos::arch::memory::heap::global_heap_allocator::kmalloc(size);
+ }
+ return teachos::arch::memory::heap::global_heap_allocator::malloc(size);
+}
+
+auto operator delete[](void * pointer) noexcept -> void
+{
+ if (teachos::arch::memory::heap::os_in_kernel_mode())
+ {
+ teachos::arch::memory::heap::global_heap_allocator::kfree(pointer);
+ }
+ teachos::arch::memory::heap::global_heap_allocator::free(pointer);
+}
+
+auto operator delete[](void * pointer, std::size_t size) noexcept -> void
+{
+ (void)size;
+ if (teachos::arch::memory::heap::os_in_kernel_mode())
+ {
+ teachos::arch::memory::heap::global_heap_allocator::kfree(pointer);
+ }
+ teachos::arch::memory::heap::global_heap_allocator::free(pointer);
+}
diff --git a/arch/x86_64/pre/src/memory/heap/linked_list_allocator.cpp b/arch/x86_64/pre/src/memory/heap/linked_list_allocator.cpp
new file mode 100644
index 0000000..00ca366
--- /dev/null
+++ b/arch/x86_64/pre/src/memory/heap/linked_list_allocator.cpp
@@ -0,0 +1,177 @@
+#include "arch/memory/heap/linked_list_allocator.hpp"
+
+#include "arch/exception_handling/assert.hpp"
+#include "arch/exception_handling/panic.hpp"
+
+#include <algorithm>
+
+namespace teachos::arch::memory::heap
+{
+ linked_list_allocator::linked_list_allocator(std::size_t heap_start, std::size_t heap_end)
+ : first(nullptr)
+ , mutex{kstd::mutex{}}
+ {
+ auto const heap_size = heap_end - heap_start;
+ exception_handling::assert(
+ heap_size > min_allocatable_size(),
+ "[Linked List Allocator] Total heap size can not be smaller than minimum of 16 bytes to hold "
+ "atleast one memory hole entry");
+ first = new (reinterpret_cast<void *>(heap_start)) memory_block(heap_size, nullptr);
+ }
+
+ auto linked_list_allocator::allocate(std::size_t size) -> void *
+ {
+ // Add size of size_t to the total allocated size, because we add a header that includes the size of the allocated
+ // block, to allow for deallocation without the need to call with the corresponding size
+ auto const total_size = size + sizeof(std::size_t);
+ mutex.lock();
+
+ memory_block * previous = nullptr;
+ auto current = first;
+
+ while (current != nullptr)
+ {
+ if (current->size == total_size)
+ {
+ auto const memory_address = remove_free_memory_block(previous, current);
+ new (memory_address) std::size_t(total_size);
+ mutex.unlock();
+ return reinterpret_cast<void *>(reinterpret_cast<std::size_t>(memory_address) + sizeof(std::size_t));
+ }
+ else if (current->size >= total_size + min_allocatable_size())
+ {
+ // Ensure that the allocated size block is atleast 16 bytes (required because if we free the hole afterwards
+ // there needs to be enough space for a memory block). Therefore we allocate more than is actually required if
+ // the total size was less and simply deallocate it as well
+ auto const max_size = std::max(total_size, min_allocatable_size());
+ auto const memory_address = split_free_memory_block(previous, current, max_size);
+ new (memory_address) std::size_t(max_size);
+ mutex.unlock();
+ return reinterpret_cast<void *>(reinterpret_cast<std::size_t>(memory_address) + sizeof(std::size_t));
+ }
+
+ previous = current;
+ current = current->next;
+ }
+
+ exception_handling::panic("[Linked List Allocator] Out of memory");
+ }
+
+ auto linked_list_allocator::deallocate(void * pointer) noexcept -> void
+ {
+ mutex.lock();
+
+ // Read configured header size of the complete allocated block
+ auto const header_pointer = reinterpret_cast<void *>(reinterpret_cast<std::size_t>(pointer) - sizeof(std::size_t));
+ auto const total_size = *reinterpret_cast<std::size_t *>(header_pointer);
+
+ auto const start_address = reinterpret_cast<std::size_t>(header_pointer);
+ auto const end_address = start_address + total_size;
+
+ memory_block * previous = nullptr;
+ auto current = first;
+
+ while (current != nullptr)
+ {
+ // Current address of the free memory block now points to an address that is after our block to deallocate in heap
+ // memory space.
+ if (reinterpret_cast<std::size_t>(current) >= end_address)
+ {
+ break;
+ }
+
+ previous = current;
+ current = current->next;
+ }
+
+ coalesce_free_memory_block(previous, current, header_pointer, total_size);
+ mutex.unlock();
+ }
+
+ auto linked_list_allocator::remove_free_memory_block(memory_block * previous_block, memory_block * current_block)
+ -> void *
+ {
+ return replace_free_memory_block(previous_block, current_block, current_block->next);
+ }
+
+ auto linked_list_allocator::split_free_memory_block(memory_block * previous_block, memory_block * current_block,
+ std::size_t size) -> void *
+ {
+ auto const end_address = reinterpret_cast<std::size_t>(current_block) + size;
+ auto const new_block =
+ new (reinterpret_cast<void *>(end_address)) memory_block(current_block->size - size, current_block->next);
+ return replace_free_memory_block(previous_block, current_block, new_block);
+ }
+
+ auto linked_list_allocator::replace_free_memory_block(memory_block * previous_block, memory_block * current_block,
+ memory_block * new_block) -> void *
+ {
+ auto const start_address = reinterpret_cast<std::size_t>(current_block);
+ // If we want to allocate into the first block that is before any other free block, then there exists no previous
+ // free block (nullptr). Therefore we have to overwrite the first block instead of overwriting its next value.
+ if (previous_block == nullptr)
+ {
+ first = new_block;
+ }
+ else
+ {
+ previous_block->next = new_block;
+ }
+ current_block->~memory_block();
+ return reinterpret_cast<void *>(start_address);
+ }
+
+ auto linked_list_allocator::coalesce_free_memory_block(memory_block * previous_block, memory_block * current_block,
+ void * pointer, std::size_t size) -> void
+ {
+ auto const start_address = reinterpret_cast<std::size_t>(pointer);
+ auto const end_address = start_address + size;
+
+ // Inital values if there are no adjacent blocks either before or after, meaning we have to simply create a free
+ // memory block that is placed in between the previous and next block.
+ auto block_size = size;
+ auto next_block = current_block;
+
+ // If the block we want to deallocate is before another free block and we can therefore combine both into one.
+ // This is done by deleting the current free block and creating a new block at the start address of the block to
+ // deallocate with both the size of the block to deallcoate and the free block next to it.
+ if (end_address == reinterpret_cast<std::size_t>(current_block))
+ {
+ block_size += current_block->size;
+ next_block = current_block->next;
+ current_block->~memory_block();
+ }
+
+ // If the block we want to deallocate is behind another free block and we can therefore combine both into one.
+ // This is done by simply changin the size of the previous block to include the size of the block to deallocate.
+ // This is done, because the previous block might still be referencered by the next field of other memory blocks.
+ if (previous_block != nullptr &&
+ start_address == (reinterpret_cast<std::size_t>(previous_block) + previous_block->size))
+ {
+ block_size += previous_block->size;
+
+ previous_block->size = block_size;
+ previous_block->next = next_block;
+ return;
+ }
+
+ // Check if the block we want to deallocate is contained in the previous block, because if it is it can only mean
+ // that the block has already been deallocated and we therefore attempted a double free.
+ exception_handling::assert(previous_block == nullptr ||
+ start_address >=
+ (reinterpret_cast<std::size_t>(previous_block) + previous_block->size),
+ "[Linked List Allocator] Attempted double free detected");
+
+ auto const new_block = new (pointer) memory_block(block_size, next_block);
+ // If we want to deallocate the first block that is before any other free block, then there exists no previous free
+ // block (nullptr). Therefore we have to overwrite the first block instead of overwriting its
+ // next value.
+ if (previous_block == nullptr)
+ {
+ first = new_block;
+ return;
+ }
+ previous_block->next = new_block;
+ }
+
+} // namespace teachos::arch::memory::heap
diff --git a/arch/x86_64/pre/src/memory/heap/memory_block.cpp b/arch/x86_64/pre/src/memory/heap/memory_block.cpp
new file mode 100644
index 0000000..bc97bd6
--- /dev/null
+++ b/arch/x86_64/pre/src/memory/heap/memory_block.cpp
@@ -0,0 +1,15 @@
+#include "arch/memory/heap/memory_block.hpp"
+
+#include <string.h>
+
+namespace teachos::arch::memory::heap
+{
+ memory_block::memory_block(std::size_t size, memory_block * next)
+ {
+ memset(static_cast<void *>(this), 0U, size);
+ this->size = size;
+ this->next = next;
+ }
+
+ memory_block::~memory_block() { memset(static_cast<void *>(this), 0U, sizeof(memory_block)); }
+} // namespace teachos::arch::memory::heap
diff --git a/arch/x86_64/pre/src/memory/heap/user_heap_allocator.cpp b/arch/x86_64/pre/src/memory/heap/user_heap_allocator.cpp
new file mode 100644
index 0000000..427a68a
--- /dev/null
+++ b/arch/x86_64/pre/src/memory/heap/user_heap_allocator.cpp
@@ -0,0 +1,200 @@
+#include "arch/memory/heap/user_heap_allocator.hpp"
+
+#include "arch/context_switching/syscall/main.hpp"
+
+#include <algorithm>
+
+namespace teachos::arch::memory::heap
+{
+ auto user_heap_allocator::allocate(std::size_t size) -> void *
+ {
+ // Add size of size_t to the total allocated size, because we add a header that includes the size of the allocated
+ // block, to allow for deallocation without the need to call with the corresponding size
+ auto const total_size = size + sizeof(std::size_t);
+ mutex.lock();
+
+ memory_block * previous = nullptr;
+ auto current = first;
+
+ while (current != nullptr)
+ {
+ auto memory = allocate_into_memory_block_if_big_enough(current, previous, total_size);
+ if (memory.has_value())
+ {
+ return memory.value();
+ }
+
+ previous = current;
+ current = current->next;
+ }
+
+ current = expand_heap_if_full();
+
+ if (current != nullptr)
+ {
+ auto memory = allocate_into_memory_block_if_big_enough(current, previous, total_size);
+ if (memory.has_value())
+ {
+ return memory.value();
+ }
+ }
+
+ char constexpr OUT_OF_MEMORY_ERROR_MESSAGE[] = "[Linked List Allocator] Out of memory";
+ context_switching::syscall::syscall(context_switching::syscall::type::ASSERT,
+ {false, reinterpret_cast<uint64_t>(&OUT_OF_MEMORY_ERROR_MESSAGE)});
+ return nullptr;
+ }
+
+ auto user_heap_allocator::deallocate(void * pointer) noexcept -> void
+ {
+ mutex.lock();
+
+ // Read configured header size of the complete allocated block
+ auto const header_pointer = reinterpret_cast<void *>(reinterpret_cast<std::size_t>(pointer) - sizeof(std::size_t));
+ auto const total_size = *reinterpret_cast<std::size_t *>(header_pointer);
+
+ auto const start_address = reinterpret_cast<std::size_t>(header_pointer);
+ auto const end_address = start_address + total_size;
+
+ memory_block * previous = nullptr;
+ auto current = first;
+
+ while (current != nullptr)
+ {
+ // Current address of the free memory block now points to an address that is after our block to deallocate in heap
+ // memory space.
+ if (reinterpret_cast<std::size_t>(current) >= end_address)
+ {
+ break;
+ }
+
+ previous = current;
+ current = current->next;
+ }
+
+ coalesce_free_memory_block(previous, current, header_pointer, total_size);
+ mutex.unlock();
+ }
+
+ auto user_heap_allocator::allocate_into_memory_block_if_big_enough(memory_block * current, memory_block * previous,
+ std::size_t total_size) -> std::optional<void *>
+ {
+ if (current->size == total_size)
+ {
+ auto const memory_address = remove_free_memory_block(previous, current);
+ new (memory_address) std::size_t(total_size);
+ mutex.unlock();
+ return reinterpret_cast<void *>(reinterpret_cast<std::size_t>(memory_address) + sizeof(std::size_t));
+ }
+ else if (current->size >= total_size + min_allocatable_size())
+ {
+ // Ensure that the allocated size block is atleast 16 bytes (required because if we free the hole afterwards
+ // there needs to be enough space for a memory block). Therefore we allocate more than is actually required if
+ // the total size was less and simply deallocate it as well
+ auto const max_size = std::max(total_size, min_allocatable_size());
+ auto const memory_address = split_free_memory_block(previous, current, max_size);
+ new (memory_address) std::size_t(max_size);
+ mutex.unlock();
+ return reinterpret_cast<void *>(reinterpret_cast<std::size_t>(memory_address) + sizeof(std::size_t));
+ }
+ return std::nullopt;
+ }
+
+ auto user_heap_allocator::expand_heap_if_full() -> memory_block *
+ {
+ auto const result = context_switching::syscall::syscall(context_switching::syscall::type::EXPAND_HEAP);
+
+ uint64_t const heap_start = result.values.arg_0;
+ uint64_t const heap_size = result.values.arg_1;
+ return !result.error_code ? new (reinterpret_cast<void *>(heap_start)) memory_block(heap_size, nullptr) : nullptr;
+ }
+
+ auto user_heap_allocator::remove_free_memory_block(memory_block * previous_block, memory_block * current_block)
+ -> void *
+ {
+ return replace_free_memory_block(previous_block, current_block, current_block->next);
+ }
+
+ auto user_heap_allocator::split_free_memory_block(memory_block * previous_block, memory_block * current_block,
+ std::size_t size) -> void *
+ {
+ auto const end_address = reinterpret_cast<std::size_t>(current_block) + size;
+ auto const new_block =
+ new (reinterpret_cast<void *>(end_address)) memory_block(current_block->size - size, current_block->next);
+ return replace_free_memory_block(previous_block, current_block, new_block);
+ }
+
+ auto user_heap_allocator::replace_free_memory_block(memory_block * previous_block, memory_block * current_block,
+ memory_block * new_block) -> void *
+ {
+ auto const start_address = reinterpret_cast<std::size_t>(current_block);
+ // If we want to allocate into the first block that is before any other free block, then there exists no previous
+ // free block (nullptr). Therefore we have to overwrite the first block instead of overwriting its next value.
+ if (previous_block == nullptr)
+ {
+ first = new_block;
+ }
+ else
+ {
+ previous_block->next = new_block;
+ }
+ current_block->~memory_block();
+ return reinterpret_cast<void *>(start_address);
+ }
+
+ auto user_heap_allocator::coalesce_free_memory_block(memory_block * previous_block, memory_block * current_block,
+ void * pointer, std::size_t size) -> void
+ {
+ auto const start_address = reinterpret_cast<std::size_t>(pointer);
+ auto const end_address = start_address + size;
+
+ // Inital values if there are no adjacent blocks either before or after, meaning we have to simply create a free
+ // memory block that is placed in between the previous and next block.
+ auto block_size = size;
+ auto next_block = current_block;
+
+ // If the block we want to deallocate is before another free block and we can therefore combine both into one.
+ // This is done by deleting the current free block and creating a new block at the start address of the block to
+ // deallocate with both the size of the block to deallcoate and the free block next to it.
+ if (end_address == reinterpret_cast<std::size_t>(current_block))
+ {
+ block_size += current_block->size;
+ next_block = current_block->next;
+ current_block->~memory_block();
+ }
+
+ // If the block we want to deallocate is behind another free block and we can therefore combine both into one.
+ // This is done by simply changin the size of the previous block to include the size of the block to deallocate.
+ // This is done, because the previous block might still be referencered by the next field of other memory blocks.
+ if (previous_block != nullptr &&
+ start_address == (reinterpret_cast<std::size_t>(previous_block) + previous_block->size))
+ {
+ block_size += previous_block->size;
+
+ previous_block->size = block_size;
+ previous_block->next = next_block;
+ return;
+ }
+
+ // Check if the block we want to deallocate is contained in the previous block, because if it is it can only mean
+ // that the block has already been deallocated and we therefore attempted a double free.
+ char constexpr DOUBLE_FREE_ERROR_MESSAGE[] = "[Linked List Allocator] Attempted double free detected";
+ context_switching::syscall::syscall(
+ context_switching::syscall::type::ASSERT,
+ {previous_block == nullptr ||
+ start_address >= (reinterpret_cast<std::size_t>(previous_block) + previous_block->size),
+ reinterpret_cast<uint64_t>(&DOUBLE_FREE_ERROR_MESSAGE)});
+
+ auto const new_block = new (pointer) memory_block(block_size, next_block);
+ // If we want to deallocate the first block that is before any other free block, then there exists no previous free
+ // block (nullptr). Therefore we have to overwrite the first block instead of overwriting its
+ // next value.
+ if (previous_block == nullptr)
+ {
+ first = new_block;
+ return;
+ }
+ previous_block->next = new_block;
+ }
+
+} // namespace teachos::arch::memory::heap
diff --git a/arch/x86_64/pre/src/memory/main.cpp b/arch/x86_64/pre/src/memory/main.cpp
new file mode 100644
index 0000000..2746a71
--- /dev/null
+++ b/arch/x86_64/pre/src/memory/main.cpp
@@ -0,0 +1,77 @@
+#include "arch/memory/main.hpp"
+
+#include "arch/exception_handling/assert.hpp"
+#include "arch/kernel/cpu/control_register.hpp"
+#include "arch/kernel/cpu/msr.hpp"
+#include "arch/memory/allocator/area_frame_allocator.hpp"
+#include "arch/memory/allocator/concept.hpp"
+#include "arch/memory/heap/global_heap_allocator.hpp"
+#include "arch/memory/paging/active_page_table.hpp"
+#include "arch/memory/paging/kernel_mapper.hpp"
+
+#include <optional>
+
+namespace teachos::arch::memory
+{
+ namespace
+ {
+ static std::optional<allocator::area_frame_allocator> frame_allocator;
+
+ auto create_frame_allocator(multiboot::memory_information const & memory_information)
+ -> allocator::area_frame_allocator &
+ {
+ frame_allocator.emplace(memory_information);
+ return frame_allocator.value();
+ }
+
+ auto get_frame_allocator() -> allocator::area_frame_allocator &
+ {
+ exception_handling::assert(frame_allocator.has_value(),
+ "[Initialization] Frame allocator has not been created yet");
+ return frame_allocator.value();
+ }
+ } // namespace
+
+ auto remap_heap(std::size_t heap_start, std::size_t heap_size, paging::entry::bitset additional_flags = {}) -> void
+ {
+ decltype(auto) allocator = get_frame_allocator();
+ decltype(auto) active_table = paging::active_page_table::create_or_get();
+ auto const start_page = paging::virtual_page::containing_address(heap_start);
+ auto const end_page = ++(paging::virtual_page::containing_address(heap_start + heap_size - 1));
+
+ paging::page_container::iterator const begin{start_page};
+ paging::page_container::iterator const end{end_page};
+ paging::page_container const pages{begin, end};
+
+ constexpr auto base_flags = paging::entry::WRITABLE;
+ auto const flags = base_flags | additional_flags;
+
+ for (auto const & page : pages)
+ {
+ active_table.map_page_to_next_free_frame(allocator, page, flags);
+ }
+ }
+
+ auto initialize_memory_management() -> void
+ {
+ static bool has_been_called = false;
+ arch::exception_handling::assert(!has_been_called,
+ "[Initialization] Memory management has already been initialized");
+ has_been_called = true;
+
+ auto const memory_information = multiboot::read_multiboot2();
+ decltype(auto) allocator = create_frame_allocator(memory_information);
+
+ kernel::cpu::set_cr0_bit(kernel::cpu::cr0_flags::WRITE_PROTECT);
+ kernel::cpu::set_efer_bit(kernel::cpu::efer_flags::NXE);
+
+ paging::kernel_mapper kernel(allocator, memory_information);
+ kernel.remap_kernel();
+ video::vga::text::write("Kernel remapping successful", video::vga::text::common_attributes::green_on_black);
+ video::vga::text::newline();
+
+ remap_heap(heap::KERNEL_HEAP_START, heap::KERNEL_HEAP_SIZE);
+ video::vga::text::write("Heap remapping successful", video::vga::text::common_attributes::green_on_black);
+ video::vga::text::newline();
+ }
+} // namespace teachos::arch::memory
diff --git a/arch/x86_64/pre/src/memory/multiboot/elf_symbols_section.cpp b/arch/x86_64/pre/src/memory/multiboot/elf_symbols_section.cpp
new file mode 100644
index 0000000..f5d126b
--- /dev/null
+++ b/arch/x86_64/pre/src/memory/multiboot/elf_symbols_section.cpp
@@ -0,0 +1,13 @@
+#include "arch/memory/multiboot/elf_symbols_section.hpp"
+
+namespace teachos::arch::memory::multiboot
+{
+ auto elf_section_flags::contains_flags(std::bitset<64U> other) const -> bool { return (flags & other) == other; }
+
+ auto elf_section_header::is_null() const -> bool
+ {
+ return name_table_index == 0U && type == elf_section_type::INACTIVE && flags == elf_section_flags(0U) &&
+ physical_address == 0U && file_offset == 0U && additional_information == 0U && address_alignment == 0U &&
+ fixed_table_entry_size == 0U;
+ }
+} // namespace teachos::arch::memory::multiboot
diff --git a/arch/x86_64/pre/src/memory/multiboot/reader.cpp b/arch/x86_64/pre/src/memory/multiboot/reader.cpp
new file mode 100644
index 0000000..b05e6b3
--- /dev/null
+++ b/arch/x86_64/pre/src/memory/multiboot/reader.cpp
@@ -0,0 +1,135 @@
+#include "arch/memory/multiboot/reader.hpp"
+
+#include "arch/boot/pointers.hpp"
+#include "arch/exception_handling/assert.hpp"
+#include "multiboot2/information.hpp"
+// #include "arch/memory/multiboot/elf_symbols_section.hpp"
+// #include "arch/memory/multiboot/info.hpp"
+
+#include <algorithm>
+#include <ranges>
+
+// namespace teachos::arch::memory::multiboot
+// {
+// namespace
+// {
+// template<typename T>
+// requires std::is_pointer<T>::value
+// auto align_to_8_byte_boundary(T ptr, uint32_t size) -> T
+// {
+// return reinterpret_cast<T>(reinterpret_cast<uint8_t *>(ptr) + ((size + 7) & ~7));
+// }
+
+// auto process_memory_map(memory_map_header * mminfo) -> memory_area_container
+// {
+// auto const expected_entry_size = mminfo->entry_size;
+// auto constexpr actual_entry_size = sizeof(memory_area);
+// exception_handling::assert(expected_entry_size == actual_entry_size,
+// "[Multiboot Reader] Unexpected memory area entry size");
+
+// auto const total_size = mminfo->info.size;
+// auto const total_entries_size = total_size - sizeof(memory_map_header) + actual_entry_size;
+// auto const number_of_entries = total_entries_size / actual_entry_size;
+
+// auto const begin = memory_area_container::iterator{&mminfo->entries};
+// auto const end = begin + number_of_entries;
+// return memory_area_container{begin, end};
+// }
+
+// auto process_elf_sections(elf_symbols_section_header * symbol, std::size_t & kernel_start, std::size_t &
+// kernel_end)
+// -> elf_section_header_container
+// {
+// auto const expected_entry_size = symbol->entry_size;
+// auto constexpr actual_entry_size = sizeof(elf_section_header);
+// exception_handling::assert(expected_entry_size == actual_entry_size,
+// "[Multiboot Reader] Unexpected elf section header entry size");
+
+// auto const expected_total_size = symbol->info.size;
+// auto const actual_total_entry_size = actual_entry_size * symbol->number_of_sections;
+// auto constexpr actual_total_section_size = sizeof(elf_symbols_section_header) - sizeof(uint32_t);
+// auto const actual_total_size = actual_total_entry_size + actual_total_section_size;
+// exception_handling::assert(expected_total_size == actual_total_size,
+// "[Multiboot Reader] Unexpected elf symbols section header total size");
+
+// auto const begin = elf_section_header_container::iterator{reinterpret_cast<elf_section_header
+// *>(&symbol->end)}; auto const end = begin + symbol->number_of_sections;
+// exception_handling::assert(begin->is_null(),
+// "[Multiboot Reader] Elf symbols section not starting with SHT_NULL section");
+
+// elf_section_header_container sections{begin, end};
+
+// auto allocated_sections = sections | std::views::filter([](auto const & section) {
+// return section.flags.contains_flags(elf_section_flags::OCCUPIES_MEMORY);
+// });
+
+// auto const elf_section_with_lowest_physical_address = std::ranges::min_element(
+// allocated_sections, [](auto const & a, auto const & b) { return a.physical_address < b.physical_address;
+// });
+
+// auto const elf_section_with_highest_physical_address =
+// std::ranges::max_element(allocated_sections, [](auto const & a, auto const & b) {
+// auto a_physical_address_end = a.physical_address + a.section_size;
+// auto b_physical_address_end = b.physical_address + b.section_size;
+// return a_physical_address_end < b_physical_address_end;
+// });
+
+// auto const symbol_table_section_count = std::ranges::count_if(sections, [](auto const & section) {
+// return section.type == elf_section_type::DYNAMIC_SYMBOL_TABLE || section.type ==
+// elf_section_type::SYMBOL_TABLE;
+// });
+// auto const dynamic_section_count = std::ranges::count_if(
+// sections, [](auto const & section) { return section.type == elf_section_type::DYNAMIC; });
+
+// exception_handling::assert(
+// symbol_table_section_count == 1U,
+// "[Multiboot Reader] ELF Specifications allows only (1) symbol table section, but got more");
+// exception_handling::assert(
+// dynamic_section_count <= 1U,
+// "[Multiboot Reader] ELF Specifications allows only (1) or less dynamic sections, but got more");
+
+// auto const lowest_elf_section = *elf_section_with_lowest_physical_address;
+// kernel_start = lowest_elf_section.physical_address;
+
+// auto const highest_elf_section = *elf_section_with_highest_physical_address;
+// kernel_end = highest_elf_section.physical_address + highest_elf_section.section_size;
+
+// return sections;
+// }
+// } // namespace
+
+// auto read_multiboot2() -> memory_information
+// {
+// memory_information mem_info{UINT64_MAX,
+// 0U,
+// elf_section_header_container{},
+// boot::multiboot_information_pointer,
+// 0U,
+// memory_area_container{}};
+
+// auto const multiboot_information_pointer = reinterpret_cast<info_header *>(boot::multiboot_information_pointer);
+// auto const multiboot_tag = &multiboot_information_pointer->tags;
+// mem_info.multiboot_end = mem_info.multiboot_start + multiboot_information_pointer->total_size;
+
+// for (auto tag = multiboot_tag; tag->type != tag_type::END; tag = align_to_8_byte_boundary(tag, tag->size))
+// {
+// switch (tag->type)
+// {
+// case tag_type::ELF_SECTIONS: {
+// auto const symbol = reinterpret_cast<elf_symbols_section_header *>(tag);
+// mem_info.sections = process_elf_sections(symbol, mem_info.kernel_start, mem_info.kernel_end);
+// break;
+// }
+// case tag_type::MEMORY_MAP: {
+// auto const mminfo = reinterpret_cast<memory_map_header *>(tag);
+// mem_info.areas = process_memory_map(mminfo);
+// break;
+// }
+// default:
+// // All other cases are not important and can be ignored.
+// break;
+// }
+// }
+// return mem_info;
+// }
+// } // namespace teachos::arch::memory::multiboot
diff --git a/arch/x86_64/pre/src/memory/paging/active_page_table.cpp b/arch/x86_64/pre/src/memory/paging/active_page_table.cpp
new file mode 100644
index 0000000..0113869
--- /dev/null
+++ b/arch/x86_64/pre/src/memory/paging/active_page_table.cpp
@@ -0,0 +1,98 @@
+#include "arch/memory/paging/active_page_table.hpp"
+
+namespace teachos::arch::memory::paging
+{
+ namespace
+ {
+ paging::virtual_address constexpr PAGE_TABLE_LEVEL_4_ADDRESS = 0xffffffff'fffff000;
+ }
+
+ auto active_page_table::create_or_get() -> active_page_table &
+ {
+ static page_table_handle active_handle{reinterpret_cast<page_table *>(PAGE_TABLE_LEVEL_4_ADDRESS),
+ page_table_handle::LEVEL4};
+ static active_page_table active_page{active_handle};
+ return active_page;
+ }
+
+ 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>
+ {
+ auto const offset = address % allocator::PAGE_FRAME_SIZE;
+ auto const page = virtual_page::containing_address(address);
+ auto const frame = translate_page(page);
+
+ if (frame.has_value())
+ {
+ return frame.value().frame_number * allocator::PAGE_FRAME_SIZE + offset;
+ }
+
+ return std::nullopt;
+ }
+
+ auto active_page_table::translate_page(virtual_page page) -> std::optional<allocator::physical_frame>
+ {
+ auto current_handle = active_handle;
+
+ for (auto level = page_table_handle::LEVEL4; level != page_table_handle::LEVEL1; --level)
+ {
+ auto const next_handle = current_handle.next_table(page.get_level_index(level));
+ // If the next table method failed then it is highly likely that it was a huge page and we therefore have to
+ // parse the table differently. Therefore, we attempt to parse it using the method required by huge pages.
+ if (!next_handle.has_value())
+ {
+ return translate_huge_page(page);
+ }
+ current_handle = next_handle.value();
+ }
+
+ auto const level1_index = page.get_level_index(page_table_handle::LEVEL1);
+ auto const level1_entry = current_handle[level1_index];
+ return level1_entry.calculate_pointed_to_frame();
+ }
+
+ auto active_page_table::translate_huge_page(virtual_page page) -> std::optional<allocator::physical_frame>
+ {
+ auto current_handle = active_handle;
+ auto level3_handle = current_handle.next_table(page.get_level_index(page_table_handle::LEVEL4));
+
+ if (!level3_handle.has_value())
+ {
+ return std::nullopt;
+ }
+
+ auto const level3_entry = level3_handle.value()[page.get_level_index(page_table_handle::LEVEL3)];
+ auto const level3_frame = level3_entry.calculate_pointed_to_frame();
+ if (level3_frame.has_value() && level3_entry.contains_flags(entry::HUGE_PAGE))
+ {
+ exception_handling::assert(
+ level3_frame.value().frame_number % (PAGE_TABLE_ENTRY_COUNT * PAGE_TABLE_ENTRY_COUNT) == 0U,
+ "[Page Mapper] Physical address must be 1 GiB aligned");
+ return allocator::physical_frame{level3_frame.value().frame_number +
+ page.get_level_index(page_table_handle::LEVEL2) * PAGE_TABLE_ENTRY_COUNT +
+ page.get_level_index(page_table_handle::LEVEL1)};
+ }
+
+ auto level2_handle = level3_handle.value().next_table(page.get_level_index(page_table_handle::LEVEL3));
+ if (level2_handle.has_value())
+ {
+ auto const level2_entry = level2_handle.value()[page.get_level_index(page_table_handle::LEVEL2)];
+ auto const level2_frame = level2_entry.calculate_pointed_to_frame();
+ if (level2_frame.has_value() && level2_entry.contains_flags(entry::HUGE_PAGE))
+ {
+ exception_handling::assert(level2_frame.value().frame_number % PAGE_TABLE_ENTRY_COUNT == 0U,
+ "[Page Mapper] Physical address must be 2 MiB aligned");
+ return allocator::physical_frame{level2_frame.value().frame_number +
+ page.get_level_index(page_table_handle::LEVEL1)};
+ }
+ }
+ return std::nullopt;
+ }
+
+ active_page_table::active_page_table(page_table_handle active_handle)
+ : active_handle(active_handle)
+ {
+ // Nothing to do
+ }
+} // namespace teachos::arch::memory::paging
diff --git a/arch/x86_64/pre/src/memory/paging/inactive_page_table.cpp b/arch/x86_64/pre/src/memory/paging/inactive_page_table.cpp
new file mode 100644
index 0000000..4e0610e
--- /dev/null
+++ b/arch/x86_64/pre/src/memory/paging/inactive_page_table.cpp
@@ -0,0 +1,20 @@
+#include "arch/memory/paging/inactive_page_table.hpp"
+
+namespace teachos::arch::memory::paging
+{
+ inactive_page_table::inactive_page_table(allocator::physical_frame frame)
+ : page_table_level_4_frame{frame}
+ {
+ // Nothing to do
+ }
+
+ inactive_page_table::inactive_page_table(allocator::physical_frame frame, active_page_table & active_page_table,
+ temporary_page & temporary_page)
+ : page_table_level_4_frame{frame}
+ {
+ auto table = temporary_page.map_table_frame(page_table_level_4_frame, active_page_table);
+ table.zero_entries();
+ table[511].set_entry(page_table_level_4_frame, entry::PRESENT | entry::WRITABLE);
+ temporary_page.unmap_page(active_page_table);
+ }
+} // namespace teachos::arch::memory::paging
diff --git a/arch/x86_64/pre/src/memory/paging/page_entry.cpp b/arch/x86_64/pre/src/memory/paging/page_entry.cpp
new file mode 100644
index 0000000..57045ca
--- /dev/null
+++ b/arch/x86_64/pre/src/memory/paging/page_entry.cpp
@@ -0,0 +1,63 @@
+#include "arch/memory/paging/page_entry.hpp"
+
+#include "arch/exception_handling/assert.hpp"
+
+namespace teachos::arch::memory::paging
+{
+ namespace
+ {
+ std::size_t constexpr PHYSICAL_ADDRESS_MASK = 0x000fffff'fffff000;
+ } // namespace
+
+ entry::entry(uint64_t flags)
+ : flags(flags)
+ {
+ // Nothing to do.
+ }
+
+ entry::entry(multiboot::elf_section_flags elf_flags)
+ {
+ if (elf_flags.contains_flags(multiboot::elf_section_flags::OCCUPIES_MEMORY))
+ {
+ flags |= entry::PRESENT;
+ }
+
+ if (elf_flags.contains_flags(multiboot::elf_section_flags::WRITABLE))
+ {
+ flags |= entry::WRITABLE;
+ }
+
+ if (!elf_flags.contains_flags(multiboot::elf_section_flags::EXECUTABLE_CODE))
+ {
+ flags |= entry::EXECUTING_CODE_FORBIDDEN;
+ }
+ }
+
+ auto entry::is_unused() const -> bool { return flags == 0U; }
+
+ auto entry::set_unused() -> void { flags = 0U; }
+
+ auto entry::set_user_accessible() -> void { flags |= entry::USER_ACCESSIBLE; }
+
+ auto entry::calculate_pointed_to_frame() const -> std::optional<allocator::physical_frame>
+ {
+ if (contains_flags(PRESENT))
+ {
+ auto const address = flags.to_ulong() & PHYSICAL_ADDRESS_MASK;
+ return allocator::physical_frame::containing_address(address);
+ }
+ return std::nullopt;
+ }
+
+ auto entry::contains_flags(std::bitset<64U> other) const -> bool { return (flags & other) == other; }
+
+ auto entry::set_entry(allocator::physical_frame frame, std::bitset<64U> additional_flags) -> void
+ {
+ exception_handling::assert((frame.start_address() & ~PHYSICAL_ADDRESS_MASK) == 0,
+ "[Paging Entry] Start address is not aligned with page");
+
+ flags = frame.start_address() | additional_flags.to_ulong();
+ }
+
+ auto entry::get_flags() const -> std::bitset<64U> { return flags.to_ulong() & ~PHYSICAL_ADDRESS_MASK; }
+} // namespace teachos::arch::memory::paging
diff --git a/arch/x86_64/pre/src/memory/paging/page_table.cpp b/arch/x86_64/pre/src/memory/paging/page_table.cpp
new file mode 100644
index 0000000..eb11810
--- /dev/null
+++ b/arch/x86_64/pre/src/memory/paging/page_table.cpp
@@ -0,0 +1,128 @@
+#include "arch/memory/paging/page_table.hpp"
+
+#include <algorithm>
+#include <array>
+#include <memory>
+
+/*
+ * This is a linker variable reference. This referenc cannot reside inside a namespace, because in
+ * that case the compiler would try to find arch::memory::paging::_end_of_image inside the ELF file.
+ */
+extern char _end_of_image;
+
+namespace teachos::arch::memory::paging
+{
+ /**
+ * @brief A Page table containing 512 entries.
+ */
+ struct page_table
+ {
+ auto zero_entries() -> void;
+
+ auto is_empty() const -> bool;
+
+ auto next_table(std::size_t table_index) const -> std::optional<page_table *>;
+
+ auto operator[](std::size_t index) -> entry &;
+
+ auto operator[](std::size_t index) const -> entry const &;
+
+ private:
+ /**
+ * @brief Calculates the address of the next page table level for the given table index.
+ *
+ * @note The next page table address is only valid if the corresponding entry is present and not a huge page.
+ * Meaning we use an index into a Level 4 page table to get the according Level 3 page table address.
+ *
+ * @param table_index Index of this page table in the page table one level higher.
+ * @return An optional of the address of the next page table or null.
+ */
+ auto next_table_address(std::size_t table_index) const -> std::optional<std::size_t>;
+
+ std::array<entry, PAGE_TABLE_ENTRY_COUNT> entries =
+ {}; ///< Entries containing addresses to page tables of a level below or
+ ///< actual virtual addresses for the level 1 page table.
+ };
+
+ auto page_table::zero_entries() -> void
+ {
+ std::ranges::for_each(entries, [](auto & entry) { entry.set_unused(); });
+ }
+
+ auto page_table::is_empty() const -> bool
+ {
+ return std::all_of(entries.begin(), entries.end(), [](entry const & entry) { return entry.is_unused(); });
+ }
+
+ auto page_table::next_table(std::size_t table_index) const -> std::optional<page_table *>
+ {
+ auto const address = next_table_address(table_index);
+ if (address.has_value())
+ {
+ return reinterpret_cast<page_table *>(address.value());
+ }
+ return std::nullopt;
+ }
+
+ auto page_table::operator[](std::size_t index) -> entry &
+ {
+ exception_handling::assert(index < PAGE_TABLE_ENTRY_COUNT, "[Page Table] Index out of bounds");
+ return entries[index];
+ }
+
+ auto page_table::operator[](std::size_t index) const -> entry const &
+ {
+ exception_handling::assert(index < PAGE_TABLE_ENTRY_COUNT, "[Page Table] Index out of bounds");
+ return entries[index];
+ }
+
+ auto page_table::next_table_address(std::size_t table_index) const -> std::optional<std::size_t>
+ {
+ auto const entry = this->operator[](table_index);
+
+ if (entry.contains_flags(entry::PRESENT) && !entry.contains_flags(entry::HUGE_PAGE))
+ {
+ auto const table_address = reinterpret_cast<std::size_t>(this);
+ return ((table_address << 9) | (table_index << 12));
+ }
+ return std::nullopt;
+ }
+
+ page_table_handle::page_table_handle(page_table * table, page_table_handle::level table_level)
+ : table(table)
+ , table_level(table_level)
+ {
+ exception_handling::assert(table != nullptr,
+ "[Page Table] Attempted to pass nullptr as table to page table table method");
+ }
+
+ auto page_table_handle::zero_entries() -> void { table->zero_entries(); }
+
+ auto page_table_handle::is_empty() const -> bool { return table->is_empty(); }
+
+ auto page_table_handle::next_table(std::size_t table_index) const -> std::optional<page_table_handle>
+ {
+ exception_handling::assert(table_level != page_table_handle::LEVEL1,
+ "[Page Table] Attempted to call next_table on level 1 page table");
+ auto const next_table = table->next_table(table_index);
+ if (next_table.has_value())
+ {
+ auto const new_level = static_cast<page_table_handle::level>(table_level - 1);
+ return page_table_handle{next_table.value(), new_level};
+ }
+ return std::nullopt;
+ }
+
+ auto page_table_handle::get_level() const -> page_table_handle::level { return table_level; }
+
+ auto page_table_handle::operator[](std::size_t index) -> entry & { return table->operator[](index); }
+
+ auto operator--(page_table_handle::level & value) -> page_table_handle::level &
+ {
+ exception_handling::assert(value != page_table_handle::LEVEL1,
+ "[Page table] Attempted to decrement enum to value outside of range");
+ auto new_value = static_cast<std::underlying_type<page_table_handle::level>::type>(value);
+ value = static_cast<page_table_handle::level>(--new_value);
+ return value;
+ }
+} // namespace teachos::arch::memory::paging
diff --git a/arch/x86_64/pre/src/memory/paging/temporary_page.cpp b/arch/x86_64/pre/src/memory/paging/temporary_page.cpp
new file mode 100644
index 0000000..8e73523
--- /dev/null
+++ b/arch/x86_64/pre/src/memory/paging/temporary_page.cpp
@@ -0,0 +1,29 @@
+#include "arch/memory/paging/temporary_page.hpp"
+
+#include "arch/memory/paging/page_entry.hpp"
+
+namespace teachos::arch::memory::paging
+{
+ auto temporary_page::map_table_frame(allocator::physical_frame frame, active_page_table & active_table)
+ -> page_table_handle
+ {
+ page_table_handle handle{reinterpret_cast<page_table *>(map_to_frame(frame, active_table)),
+ page_table_handle::LEVEL1};
+ return handle;
+ }
+
+ auto temporary_page::map_to_frame(allocator::physical_frame frame, active_page_table & active_table)
+ -> virtual_address
+ {
+ exception_handling::assert(!active_table.translate_page(page).has_value(),
+ "[Temporary page] Page is already mapped");
+
+ active_table.map_page_to_frame(allocator, page, frame, entry::WRITABLE);
+ return page.start_address();
+ }
+
+ auto temporary_page::unmap_page(active_page_table & active_table) -> void
+ {
+ active_table.unmap_page(allocator, page);
+ }
+} // namespace teachos::arch::memory::paging
diff --git a/arch/x86_64/pre/src/memory/paging/virtual_page.cpp b/arch/x86_64/pre/src/memory/paging/virtual_page.cpp
new file mode 100644
index 0000000..d374156
--- /dev/null
+++ b/arch/x86_64/pre/src/memory/paging/virtual_page.cpp
@@ -0,0 +1,33 @@
+#include "arch/memory/paging/virtual_page.hpp"
+
+#include "arch/exception_handling/assert.hpp"
+
+namespace teachos::arch::memory::paging
+{
+ auto virtual_page::containing_address(virtual_address address) -> virtual_page
+ {
+ exception_handling::assert(address < 0x00008000'00000000 || address >= 0xffff8000'00000000,
+ "[Virtual Page] Attempted to create virtual page from invalid address");
+ return virtual_page{address / allocator::PAGE_FRAME_SIZE};
+ }
+
+ auto virtual_page::start_address() const -> virtual_address { return page_number * allocator::PAGE_FRAME_SIZE; }
+
+ auto virtual_page::get_level_index(page_table_handle::level level) const -> size_t
+ {
+ return (page_number >> (level * 9U)) & 0x1FF;
+ }
+
+ auto virtual_page::operator++(int) -> virtual_page
+ {
+ virtual_page const old_value = *this;
+ ++page_number;
+ return old_value;
+ }
+
+ auto virtual_page::operator++() -> virtual_page &
+ {
+ ++page_number;
+ return *this;
+ }
+} // namespace teachos::arch::memory::paging
diff --git a/arch/x86_64/pre/src/user/main.cpp b/arch/x86_64/pre/src/user/main.cpp
new file mode 100644
index 0000000..8b07e4a
--- /dev/null
+++ b/arch/x86_64/pre/src/user/main.cpp
@@ -0,0 +1,35 @@
+#include "arch/user/main.hpp"
+
+#include "arch/context_switching/syscall/main.hpp"
+#include "arch/memory/heap/global_heap_allocator.hpp"
+
+#include <algorithm>
+#include <array>
+#include <atomic>
+#include <ranges>
+
+namespace teachos::arch::user
+{
+ auto main() -> void
+ {
+ constexpr char syscall_message[] = "Successfully entered user mode and wrote to VGA buffer via syscall!";
+ context_switching::syscall::syscall(context_switching::syscall::type::WRITE,
+ {reinterpret_cast<uint64_t>(&syscall_message)});
+
+ // Test C++ standard library
+ std::array<std::atomic<uint8_t>, 4> array_test = {std::atomic<uint8_t>{5}, std::atomic<uint8_t>{10},
+ std::atomic<uint8_t>{15}, std::atomic<uint8_t>{20}};
+ std::ranges::for_each(array_test, [](auto & item) {
+ auto value = item.load();
+ uint8_t max_value = std::max(value, uint8_t{10});
+ item.exchange(max_value + 2);
+ });
+
+ auto address = new uint64_t{10U};
+ (void)address;
+
+ for (;;)
+ {
+ }
+ }
+} // namespace teachos::arch::user