aboutsummaryrefslogtreecommitdiff
path: root/arch/x86_64/pre/src/context_switching
diff options
context:
space:
mode:
Diffstat (limited to 'arch/x86_64/pre/src/context_switching')
-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
16 files changed, 599 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