diff options
| author | Felix Morgner <felix.morgner@ost.ch> | 2026-01-16 17:39:23 +0100 |
|---|---|---|
| committer | Felix Morgner <felix.morgner@ost.ch> | 2026-01-16 17:39:23 +0100 |
| commit | 7ba274d0838e5cd4e48e85f81557bbb837ed4349 (patch) | |
| tree | b792f550634b856e3401ea42dd0ee7089494be28 /arch/x86_64/include | |
| parent | f7fc4cb67c8c7c6ec1833f6ac6f0ad3a2837251d (diff) | |
| download | teachos-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.hpp | 91 | ||||
| -rw-r--r-- | arch/x86_64/include/arch/cpu/segment_descriptor.hpp | 61 | ||||
| -rw-r--r-- | arch/x86_64/include/arch/cpu/task_state_segment.hpp | 30 |
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 |
