aboutsummaryrefslogtreecommitdiff
path: root/arch/x86_64/include
diff options
context:
space:
mode:
authorFelix Morgner <felix.morgner@ost.ch>2026-01-16 17:39:23 +0100
committerFelix Morgner <felix.morgner@ost.ch>2026-01-16 17:39:23 +0100
commit7ba274d0838e5cd4e48e85f81557bbb837ed4349 (patch)
treeb792f550634b856e3401ea42dd0ee7089494be28 /arch/x86_64/include
parentf7fc4cb67c8c7c6ec1833f6ac6f0ad3a2837251d (diff)
downloadteachos-7ba274d0838e5cd4e48e85f81557bbb837ed4349.tar.xz
teachos-7ba274d0838e5cd4e48e85f81557bbb837ed4349.zip
x86_64/cpu: port GDT reload procedure
Diffstat (limited to 'arch/x86_64/include')
-rw-r--r--arch/x86_64/include/arch/cpu/global_descriptor_table.hpp91
-rw-r--r--arch/x86_64/include/arch/cpu/segment_descriptor.hpp61
-rw-r--r--arch/x86_64/include/arch/cpu/task_state_segment.hpp30
3 files changed, 182 insertions, 0 deletions
diff --git a/arch/x86_64/include/arch/cpu/global_descriptor_table.hpp b/arch/x86_64/include/arch/cpu/global_descriptor_table.hpp
new file mode 100644
index 0000000..402c87d
--- /dev/null
+++ b/arch/x86_64/include/arch/cpu/global_descriptor_table.hpp
@@ -0,0 +1,91 @@
+#ifndef TEACHOS_X86_64_GLOBAL_DESCRIPTOR_TABLE_HPP
+#define TEACHOS_X86_64_GLOBAL_DESCRIPTOR_TABLE_HPP
+
+#include "kapi/memory.hpp"
+
+#include "arch/cpu/segment_descriptor.hpp"
+
+#include <algorithm>
+#include <array>
+#include <bit>
+#include <concepts>
+#include <cstddef>
+#include <cstdint>
+#include <utility>
+
+namespace arch::cpu
+{
+
+ template<std::size_t Size>
+ struct global_descriptor_table;
+
+ struct [[gnu::packed]] global_descriptor_table_pointer
+ {
+ template<std::size_t GdtSize>
+ global_descriptor_table_pointer(global_descriptor_table<GdtSize> const & gdt)
+ : size{GdtSize * sizeof(segment_descriptor) - 1}
+ , address{kapi::memory::physical_address{std::bit_cast<std::uintptr_t>(&gdt)}.raw()}
+ {}
+
+ auto load() -> void
+ {
+ asm volatile("lgdt %0" : : "m"(*this));
+ }
+
+ std::uint16_t size{};
+ std::uint64_t address{};
+ };
+
+ static_assert(sizeof(global_descriptor_table_pointer) == sizeof(std::uint16_t) + sizeof(std::uint64_t));
+
+ template<std::size_t SizeInBytes>
+ struct global_descriptor_table
+ {
+ template<std::derived_from<segment_descriptor>... SegmentDescriptors>
+ constexpr global_descriptor_table(SegmentDescriptors const &... descriptors)
+ : m_descriptors{}
+ {
+ auto descriptor_data = std::array{
+ std::pair{std::bit_cast<std::byte const *>(&descriptors), sizeof(descriptors)}
+ ...
+ };
+ auto written_size = 0uz;
+ std::ranges::for_each(descriptor_data, [&written_size, this](auto entry) {
+ std::ranges::copy(entry.first, entry.first + entry.second, m_descriptors.begin() + written_size);
+ written_size += entry.second;
+ });
+ }
+
+ auto load(std::size_t code_segment_index, std::size_t data_segment_index) const -> void
+ {
+ auto pointer = global_descriptor_table_pointer{*this};
+ pointer.load();
+
+ asm volatile("push %0\n"
+ "lea 1f(%%rip), %%rax\n"
+ "push %%rax\n"
+ "lretq\n"
+ "1:\n"
+ "mov %1, %%rax\n"
+ "mov %%rax, %%ss\n"
+ "mov %%rax, %%ds\n"
+ "mov %%rax, %%es\n"
+ "mov %%rax, %%fs\n"
+ "mov %%rax, %%gs\n"
+ :
+ : "X"(code_segment_index * sizeof(segment_descriptor)),
+ "X"(data_segment_index * sizeof(segment_descriptor))
+ : "rax");
+ }
+
+ private:
+ std::array<std::byte, SizeInBytes> m_descriptors;
+ };
+
+ template<std::derived_from<segment_descriptor>... SegmentDescriptors>
+ global_descriptor_table(SegmentDescriptors const &... descriptors)
+ -> global_descriptor_table<(sizeof(SegmentDescriptors) + ...)>;
+
+} // namespace arch::cpu
+
+#endif \ No newline at end of file
diff --git a/arch/x86_64/include/arch/cpu/segment_descriptor.hpp b/arch/x86_64/include/arch/cpu/segment_descriptor.hpp
new file mode 100644
index 0000000..9570670
--- /dev/null
+++ b/arch/x86_64/include/arch/cpu/segment_descriptor.hpp
@@ -0,0 +1,61 @@
+#ifndef TEACHOS_X86_64_SEGMENT_DESCRIPTOR_HPP
+#define TEACHOS_X86_64_SEGMENT_DESCRIPTOR_HPP
+
+#include <cstdint>
+
+namespace arch::cpu
+{
+
+ //! The type of segment described by a segment_descriptor.
+ enum struct segment_type : std::uint8_t
+ {
+ //! A system (TSS or LDT) segment
+ system = 0,
+ //! A code or data segment
+ code_or_data = 1,
+ };
+
+ //! The granularity of a segment described by a segment_descriptor
+ enum struct segment_granularity : std::uint8_t
+ {
+ //! The limit of the segment is defined in bytes.
+ byte = 0,
+ //! The limit of the segment is defined in pages (4KiB)
+ page = 1,
+ };
+
+ //! An entry in a segment descriptor table
+ struct segment_descriptor
+ {
+ std::uint64_t limit_low : 16;
+ std::uint64_t base_low : 24;
+ bool accessed : 1;
+ bool read_write : 1;
+ bool direction_or_conforming : 1;
+ bool executable : 1;
+ segment_type type : 1;
+ std::uint64_t privilege_level : 2;
+ bool present : 1;
+ std::uint64_t limit_high : 4;
+ std::uint64_t : 1;
+ bool long_mode : 1;
+ bool protected_mode : 1;
+ segment_granularity granularity : 1;
+ std::uint64_t base_high : 8;
+ };
+
+ static_assert(sizeof(segment_descriptor) == sizeof(std::uint64_t));
+ static_assert(alignof(segment_descriptor) == alignof(std::uint64_t));
+
+ struct system_segment_descriptor : segment_descriptor
+ {
+ std::uint64_t base_extended : 32;
+ std::uint64_t : 32;
+ };
+
+ static_assert(sizeof(system_segment_descriptor) == 2 * sizeof(std::uint64_t));
+ static_assert(alignof(system_segment_descriptor) == alignof(segment_descriptor));
+
+} // namespace arch::cpu
+
+#endif \ No newline at end of file
diff --git a/arch/x86_64/include/arch/cpu/task_state_segment.hpp b/arch/x86_64/include/arch/cpu/task_state_segment.hpp
new file mode 100644
index 0000000..57729dd
--- /dev/null
+++ b/arch/x86_64/include/arch/cpu/task_state_segment.hpp
@@ -0,0 +1,30 @@
+#ifndef TEACHOS_X86_64_TASK_STATE_SEGMENT_HPP
+#define TEACHOS_X86_64_TASK_STATE_SEGMENT_HPP
+
+#include <cstdint>
+
+namespace arch::cpu
+{
+
+ struct [[gnu::packed]] task_state_segment
+ {
+ uint32_t : 32;
+ uint64_t rsp0 = {};
+ uint64_t rsp1 = {};
+ uint64_t rsp2 = {};
+ uint64_t : 64;
+ uint64_t ist1 = {};
+ uint64_t ist2 = {};
+ uint64_t ist3 = {};
+ uint64_t ist4 = {};
+ uint64_t ist5 = {};
+ uint64_t ist6 = {};
+ uint64_t ist7 = {};
+ uint64_t : 64;
+ uint16_t : 16;
+ uint16_t io_map_base_address = {};
+ };
+
+} // namespace arch::cpu
+
+#endif \ No newline at end of file