From 36758071881088b27a52cee4e5653f6cf6a79a78 Mon Sep 17 00:00:00 2001 From: Fabian Imhof Date: Sun, 16 Mar 2025 12:41:09 +0000 Subject: start implementing TSS --- arch/x86_64/CMakeLists.txt | 3 +- .../descriptor_table/global_descriptor_table.hpp | 42 ++++++++ .../descriptor_table/task_state_segment.hpp | 39 ++++++++ arch/x86_64/include/arch/kernel/cpu/gdtr.hpp | 28 ++++++ arch/x86_64/include/arch/kernel/cpu/lgdt.hpp | 28 ------ arch/x86_64/include/arch/kernel/cpu/tr.hpp | 26 +++++ .../descriptor_table/global_descriptor_table.cpp | 108 ++++++++++++++------- arch/x86_64/src/kernel/cpu/gdtr.cpp | 20 ++++ arch/x86_64/src/kernel/cpu/lgdt.cpp | 20 ---- arch/x86_64/src/kernel/cpu/tr.cpp | 17 ++++ arch/x86_64/src/kernel/main.cpp | 1 - 11 files changed, 245 insertions(+), 87 deletions(-) create mode 100644 arch/x86_64/include/arch/context_switching/descriptor_table/task_state_segment.hpp create mode 100644 arch/x86_64/include/arch/kernel/cpu/gdtr.hpp delete mode 100644 arch/x86_64/include/arch/kernel/cpu/lgdt.hpp create mode 100644 arch/x86_64/include/arch/kernel/cpu/tr.hpp create mode 100644 arch/x86_64/src/kernel/cpu/gdtr.cpp delete mode 100644 arch/x86_64/src/kernel/cpu/lgdt.cpp create mode 100644 arch/x86_64/src/kernel/cpu/tr.cpp (limited to 'arch') diff --git a/arch/x86_64/CMakeLists.txt b/arch/x86_64/CMakeLists.txt index 912daf6..aad7951 100644 --- a/arch/x86_64/CMakeLists.txt +++ b/arch/x86_64/CMakeLists.txt @@ -8,10 +8,11 @@ mark_as_advanced(TEACHOS_KERNEL_LINKER_SCRIPT) target_sources("_kernel" PRIVATE "src/kernel/main.cpp" "src/kernel/cpu/control_register.cpp" - "src/kernel/cpu/lgdt.cpp" + "src/kernel/cpu/gdtr.cpp" "src/kernel/cpu/msr.cpp" "src/kernel/cpu/ss.cpp" "src/kernel/cpu/tlb.cpp" + "src/kernel/cpu/tr.cpp" ) target_link_options("_kernel" PRIVATE diff --git a/arch/x86_64/include/arch/context_switching/descriptor_table/global_descriptor_table.hpp b/arch/x86_64/include/arch/context_switching/descriptor_table/global_descriptor_table.hpp index c4d0e30..ab8f1ac 100644 --- a/arch/x86_64/include/arch/context_switching/descriptor_table/global_descriptor_table.hpp +++ b/arch/x86_64/include/arch/context_switching/descriptor_table/global_descriptor_table.hpp @@ -2,12 +2,54 @@ #define TEACHOS_ARCH_X86_64_CONTEXT_SWITCHING_DESCRIPTOR_TABLE_GLOBAL_DESCRIPTOR_TABLE_HPP #include "arch/context_switching/descriptor_table/global_descriptor_table_pointer.hpp" +#include "arch/context_switching/descriptor_table/task_state_segment.hpp" namespace teachos::arch::context_switching::descriptor_table { + enum class access_level + { + KERNEL, + USER + }; + + /** + * @brief Creates a generic segment_descriptor from the passed arguments. + * + * @param segment_descriptor_type Defines the type of the segment descriptor. + * @param access_level Defines the segment descriptor access level (KERNEL or USER). + * @return Created segment_descriptor. + */ + auto create_segment_descriptor(segment_descriptor_type segment_descriptor_type, access_level access_level) + -> segment_descriptor; + + /** + * @brief Creates a global_descriptor_table on the heap and fills it with the following segment registers: + * + * - Kernel Code Segment + * - Kernel Data Segment + * - User Code Segment + * - User Data Segment + * + * @return Created global_descriptor_table. + */ auto create_global_descriptor_table() -> global_descriptor_table; + /** + * @brief Creates a task_state_segment segment_descriptor on the heap. + * + * @param tss task_state_segment whose pointer is used in the segment_descriptor + * @return Created segment_descriptor. + */ + auto create_task_state_segment_descriptor(task_state_segment * tss) -> segment_descriptor; + + /** + * @brief Initializes the global_descriptor_table and task_state_segment by loading them + * in the GDTR and TR registers respectively. + * + * @return Created global_descriptor_table. + */ auto initialize_global_descriptor_table() -> global_descriptor_table; + } // namespace teachos::arch::context_switching::descriptor_table #endif // TEACHOS_ARCH_X86_64_CONTEXT_SWITCHING_DESCRIPTOR_TABLE_GLOBAL_DESCRIPTOR_TABLE_HPP diff --git a/arch/x86_64/include/arch/context_switching/descriptor_table/task_state_segment.hpp b/arch/x86_64/include/arch/context_switching/descriptor_table/task_state_segment.hpp new file mode 100644 index 0000000..af7ae7c --- /dev/null +++ b/arch/x86_64/include/arch/context_switching/descriptor_table/task_state_segment.hpp @@ -0,0 +1,39 @@ +#ifndef TEACHOS_ARCH_X86_64_CONTEXT_SWITCHING_DESCRIPTOR_TABLE_TASK_STATE_SEGMENT_HPP +#define TEACHOS_ARCH_X86_64_CONTEXT_SWITCHING_DESCRIPTOR_TABLE_TASK_STATE_SEGMENT_HPP + +#include + +namespace teachos::arch::context_switching::descriptor_table +{ + /** + * @brief 64-bit task state segment + */ + struct [[gnu::packed]] task_state_segment + { + private: + uint16_t io_map_base_address = {}; + + uint16_t reserved_1 = {}; + uint32_t reserved_2 = {}; + uint32_t reserved_3 = {}; + + uint64_t ist7 = {}; + uint64_t ist6 = {}; + uint64_t ist5 = {}; + uint64_t ist4 = {}; + uint64_t ist3 = {}; + uint64_t ist2 = {}; + uint64_t ist1 = {}; + + uint32_t reserved_4 = {}; + uint32_t reserved_5 = {}; + + uint64_t rsp2 = {}; + uint64_t rsp1 = {}; + uint64_t rsp0 = {}; + + uint32_t reserved_6 = {}; + }; +} // namespace teachos::arch::context_switching::descriptor_table + +#endif // TEACHOS_ARCH_X86_64_CONTEXT_SWITCHING_DESCRIPTOR_TABLE_TASK_STATE_SEGMENT_HPP \ No newline at end of file diff --git a/arch/x86_64/include/arch/kernel/cpu/gdtr.hpp b/arch/x86_64/include/arch/kernel/cpu/gdtr.hpp new file mode 100644 index 0000000..f9220b9 --- /dev/null +++ b/arch/x86_64/include/arch/kernel/cpu/gdtr.hpp @@ -0,0 +1,28 @@ +#ifndef TEACHOS_ARCH_X86_64_KERNEL_CPU_GDTR_HPP +#define TEACHOS_ARCH_X86_64_KERNEL_CPU_GDTR_HPP + +#include "arch/context_switching/descriptor_table/global_descriptor_table_pointer.hpp" + +#include +#include + +namespace teachos::arch::kernel::cpu +{ + + /** + * @brief Returns the value in the GDTR register. + * + * @return Value of GDTR register. + */ + auto store_global_descriptor_table() -> context_switching::descriptor_table::global_descriptor_table_pointer; + + /** + * @brief Loads the global_descriptor_table_pointer into the global descriptor table register (GDTR). + */ + auto + load_global_descriptor_table(context_switching::descriptor_table::global_descriptor_table_pointer const & gdt_pointer) + -> void; + +} // namespace teachos::arch::kernel::cpu + +#endif // TEACHOS_ARCH_X86_64_KERNEL_CPU_GDTR_HPP diff --git a/arch/x86_64/include/arch/kernel/cpu/lgdt.hpp b/arch/x86_64/include/arch/kernel/cpu/lgdt.hpp deleted file mode 100644 index 85e2949..0000000 --- a/arch/x86_64/include/arch/kernel/cpu/lgdt.hpp +++ /dev/null @@ -1,28 +0,0 @@ -#ifndef TEACHOS_ARCH_X86_64_KERNEL_CPU_LGDT_HPP -#define TEACHOS_ARCH_X86_64_KERNEL_CPU_LGDT_HPP - -#include "arch/context_switching/descriptor_table/global_descriptor_table_pointer.hpp" - -#include -#include - -namespace teachos::arch::kernel::cpu -{ - - /** - * @brief Returns the value in the GDTR register. - * - * @return Value of GDTR register. - */ - auto store_global_descriptor_table() -> context_switching::descriptor_table::global_descriptor_table_pointer; - - /** - * @brief Loads the global_descriptor_table_pointer into the global descriptor table register (GDTR). - */ - auto - load_global_descriptor_table(context_switching::descriptor_table::global_descriptor_table_pointer const & gdt_pointer) - -> void; - -} // namespace teachos::arch::kernel::cpu - -#endif // TEACHOS_ARCH_X86_64_KERNEL_CPU_LGDT_HPP diff --git a/arch/x86_64/include/arch/kernel/cpu/tr.hpp b/arch/x86_64/include/arch/kernel/cpu/tr.hpp new file mode 100644 index 0000000..5f99472 --- /dev/null +++ b/arch/x86_64/include/arch/kernel/cpu/tr.hpp @@ -0,0 +1,26 @@ +#ifndef TEACHOS_ARCH_X86_64_KERNEL_CPU_TR_HPP +#define TEACHOS_ARCH_X86_64_KERNEL_CPU_TR_HPP + +#include "arch/context_switching/descriptor_table/task_state_segment.hpp" + +#include +#include + +namespace teachos::arch::kernel::cpu +{ + + /** + * @brief Returns the value in the LTR register. + * + * @return Value of LTR register. + */ + auto store_task_register() -> uint16_t; + + /** + * @brief Loads the tss_pointer into the task register (TR). + */ + auto load_task_register(uint16_t gdt_offset) -> void; + +} // namespace teachos::arch::kernel::cpu + +#endif // TEACHOS_ARCH_X86_64_KERNEL_CPU_TR_HPP diff --git a/arch/x86_64/src/context_switching/descriptor_table/global_descriptor_table.cpp b/arch/x86_64/src/context_switching/descriptor_table/global_descriptor_table.cpp index 6474739..2b399f8 100644 --- a/arch/x86_64/src/context_switching/descriptor_table/global_descriptor_table.cpp +++ b/arch/x86_64/src/context_switching/descriptor_table/global_descriptor_table.cpp @@ -1,61 +1,87 @@ #include "arch/context_switching/descriptor_table/global_descriptor_table.hpp" #include "arch/context_switching/descriptor_table/segment_descriptor.hpp" +#include "arch/context_switching/descriptor_table/task_state_segment.hpp" #include "arch/exception_handling/assert.hpp" -#include "arch/kernel/cpu/lgdt.hpp" +#include "arch/kernel/cpu/gdtr.hpp" +#include "arch/kernel/cpu/tr.hpp" #include "arch/stl/vector.hpp" namespace teachos::arch::context_switching::descriptor_table { + auto create_segment_descriptor(segment_descriptor_type segment_descriptor_type, access_level access_level) + -> segment_descriptor + { + uint8_t access_level_bitset = access_byte::PRESENT | access_byte::CODE_OR_DATA_SEGMENT; + if (access_level == access_level::KERNEL) + { + access_level_bitset |= access_byte::ACCESS_LEVEL_KERNEL; + } + else if (access_level == access_level::USER) + { + access_level_bitset = access_byte::ACCESS_LEVEL_USER; + } + + uint8_t gdt_flags_bitset = gdt_flags::GRANULARITY; + if (segment_descriptor_type == segment_descriptor_type::CODE_SEGMENT) + { + gdt_flags_bitset |= gdt_flags::LENGTH; + access_level_bitset |= access_byte::CODE_SEGMENT | access_byte::READABLE; + } + else if (segment_descriptor_type == segment_descriptor_type::DATA_SEGMENT) + { + gdt_flags_bitset |= gdt_flags::UPPER_BOUND; + access_level_bitset |= access_byte::WRITABLE; + } + + std::bitset<20U> limit{0xFFFFF}; + access_byte access_byte{access_level_bitset}; + gdt_flags gdt_flags{gdt_flags_bitset, limit}; + segment_descriptor code_segment{access_byte, gdt_flags, 0, limit}; + + return code_segment; + } + auto create_global_descriptor_table() -> global_descriptor_table { segment_descriptor null_segment{0}; + segment_descriptor kernel_code_segment = + create_segment_descriptor(segment_descriptor_type::CODE_SEGMENT, access_level::KERNEL); + segment_descriptor kernel_data_segment = + create_segment_descriptor(segment_descriptor_type::DATA_SEGMENT, access_level::KERNEL); + segment_descriptor user_code_segment = + create_segment_descriptor(segment_descriptor_type::CODE_SEGMENT, access_level::USER); + segment_descriptor user_data_segment = + create_segment_descriptor(segment_descriptor_type::DATA_SEGMENT, access_level::USER); - std::bitset<20U> limit{0xFFFFF}; + stl::vector global_descriptor_table{null_segment, kernel_code_segment, kernel_data_segment, + user_code_segment, user_data_segment}; + return global_descriptor_table; + } - // Kernel space code segment - access_byte kernel_code_access_byte{access_byte::PRESENT | access_byte::ACCESS_LEVEL_KERNEL | - access_byte::CODE_OR_DATA_SEGMENT | access_byte::CODE_SEGMENT | - access_byte::READABLE}; - gdt_flags kernel_code_gdt_flags{gdt_flags::GRANULARITY | gdt_flags::LENGTH, limit}; - segment_descriptor kernel_code_segment{kernel_code_access_byte, kernel_code_gdt_flags, 0, limit}; - - // Kernel space data segment - access_byte kernel_data_access_byte{access_byte::PRESENT | access_byte::ACCESS_LEVEL_KERNEL | - access_byte::CODE_OR_DATA_SEGMENT | access_byte::WRITABLE}; - gdt_flags kernel_data_gdt_flags{gdt_flags::GRANULARITY | gdt_flags::UPPER_BOUND, limit}; - segment_descriptor kernel_data_segment{kernel_data_access_byte, kernel_data_gdt_flags, 0, limit}; - - // User space code segment - access_byte user_code_access_byte{access_byte::PRESENT | access_byte::ACCESS_LEVEL_USER | - access_byte::CODE_OR_DATA_SEGMENT | access_byte::CODE_SEGMENT | - access_byte::READABLE}; - gdt_flags user_code_gdt_flags{gdt_flags::GRANULARITY | gdt_flags::LENGTH, limit}; - segment_descriptor user_code_segment{user_code_access_byte, user_code_gdt_flags, 0, limit}; - - // User space data segment - access_byte user_data_access_byte{access_byte::PRESENT | access_byte::ACCESS_LEVEL_USER | - access_byte::CODE_OR_DATA_SEGMENT | access_byte::WRITABLE}; - gdt_flags user_data_gdt_flags{gdt_flags::GRANULARITY | gdt_flags::UPPER_BOUND, limit}; - segment_descriptor user_data_segment{user_data_access_byte, user_data_gdt_flags, 0, limit}; - - // Task state segment - // TODO: Create TSS + auto create_task_state_segment_descriptor(task_state_segment * tss) -> segment_descriptor + { + std::bitset<20U> limit{0xFFFFF}; access_byte tss_access_byte{access_byte::PRESENT | access_byte::ACCESS_LEVEL_KERNEL | access_byte::TASK_STATE_SEGMENT_AVAILABLE}; gdt_flags tss_gdt_flags{0U, limit}; - uint64_t tss_address = 0; // &TSS - uint64_t tss_limit = 0U; // sizeof(TSS) - 1 - segment_descriptor task_state_segment{tss_access_byte, tss_gdt_flags, tss_address, tss_limit}; + uint64_t tss_address = reinterpret_cast(tss); + constexpr uint64_t tss_limit = sizeof(task_state_segment) - 1; + segment_descriptor tss_descriptor{tss_access_byte, tss_gdt_flags, tss_address, tss_limit}; - stl::vector global_descriptor_table{null_segment, kernel_code_segment, kernel_data_segment, - user_code_segment, user_data_segment}; - return global_descriptor_table; + return tss_descriptor; } auto initialize_global_descriptor_table() -> global_descriptor_table { - decltype(auto) gdt = create_global_descriptor_table(); + auto gdt = create_global_descriptor_table(); + + // Add TSS segment descriptor to GDT + auto tss = new task_state_segment(); + auto tss_descriptor = create_task_state_segment_descriptor(tss); + gdt.push_back(tss_descriptor); + + // Load GDT into GDTR global_descriptor_table_pointer gdt_pointer{static_cast(gdt.size() - 1), &gdt}; kernel::cpu::load_global_descriptor_table(gdt_pointer); @@ -64,6 +90,14 @@ namespace teachos::arch::context_switching::descriptor_table gdt_pointer == stored_gdt_pointer, "[Global Descriptor Table] Loaded GDTR value is not the same as the stored value."); + // Load Task Register (LTR) using the index of TSS descriptor + uint16_t tss_selector = (gdt.size() - 1) * sizeof(segment_descriptor); + kernel::cpu::load_task_register(tss_selector); + + auto 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."); + return gdt; } } // namespace teachos::arch::context_switching::descriptor_table diff --git a/arch/x86_64/src/kernel/cpu/gdtr.cpp b/arch/x86_64/src/kernel/cpu/gdtr.cpp new file mode 100644 index 0000000..2fe3a99 --- /dev/null +++ b/arch/x86_64/src/kernel/cpu/gdtr.cpp @@ -0,0 +1,20 @@ +#include "arch/kernel/cpu/gdtr.hpp" + +#include "arch/context_switching/descriptor_table/global_descriptor_table_pointer.hpp" + +namespace teachos::arch::kernel::cpu +{ + auto store_global_descriptor_table() -> context_switching::descriptor_table::global_descriptor_table_pointer + { + context_switching::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::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/src/kernel/cpu/lgdt.cpp b/arch/x86_64/src/kernel/cpu/lgdt.cpp deleted file mode 100644 index 386914f..0000000 --- a/arch/x86_64/src/kernel/cpu/lgdt.cpp +++ /dev/null @@ -1,20 +0,0 @@ -#include "arch/kernel/cpu/lgdt.hpp" - -#include "arch/context_switching/descriptor_table/global_descriptor_table_pointer.hpp" - -namespace teachos::arch::kernel::cpu -{ - auto store_global_descriptor_table() -> context_switching::descriptor_table::global_descriptor_table_pointer - { - context_switching::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::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/src/kernel/cpu/tr.cpp b/arch/x86_64/src/kernel/cpu/tr.cpp new file mode 100644 index 0000000..a28b9fc --- /dev/null +++ b/arch/x86_64/src/kernel/cpu/tr.cpp @@ -0,0 +1,17 @@ +#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] "=m"(current_value)); + return current_value; + } + + // TODO: Is this really correct? + 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/src/kernel/main.cpp b/arch/x86_64/src/kernel/main.cpp index 2c0b6c8..c1e134a 100644 --- a/arch/x86_64/src/kernel/main.cpp +++ b/arch/x86_64/src/kernel/main.cpp @@ -1,7 +1,6 @@ #include "arch/kernel/main.hpp" #include "arch/context_switching/descriptor_table/global_descriptor_table.hpp" -#include "arch/kernel/cpu/lgdt.hpp" #include "arch/memory/heap/bump_allocator.hpp" #include "arch/memory/heap/global_heap_allocator.hpp" #include "arch/memory/main.hpp" -- cgit v1.2.3