diff options
| author | Felix Morgner <felix.morgner@gmail.com> | 2025-03-28 18:35:28 +0100 |
|---|---|---|
| committer | Felix Morgner <felix.morgner@gmail.com> | 2025-03-28 18:39:15 +0100 |
| commit | fbd1ebe4f7c5985554fdca7c7fc05de15d47dd3a (patch) | |
| tree | aa693a6f6edc717a1f3e184141d0ee1c150c57d7 | |
| parent | 91c37142dbed40e42fd1a27a2755a79b8ccc329c (diff) | |
| download | teachos-fbd1ebe4f7c5985554fdca7c7fc05de15d47dd3a.tar.xz teachos-fbd1ebe4f7c5985554fdca7c7fc05de15d47dd3a.zip | |
gdt: fix reload of GDT
The core problems were/are the following:
- The flags of the segments were not entirely correct. Please recheck
them against the spec!
- The GDT pointer did not contain the address of the first (null) GTD
entry, but the address of the stl::vector containing the GDT
entries.
- The far pointer must consist of:
- the address to jump to
- the byte index into the GDT for the desired segement descriptor to
be loaded into CS.
- The type of the "dummy" function we jump to was wrong (it's a
function, we should declare it as such).
- We cannot enable interrupts right now, since we die with a triple
fault. This is caused by some initia fault which seems to lead to a
general protection fault, which then triple faults since we cannot
find the IDT.
Some FIXMEs have been added to the code. Please look at them carefully
and compare things against the specs.
8 files changed, 39 insertions, 29 deletions
diff --git a/arch/x86_64/include/arch/boot/pointers.hpp b/arch/x86_64/include/arch/boot/pointers.hpp index c08de52..9bf5cfd 100644 --- a/arch/x86_64/include/arch/boot/pointers.hpp +++ b/arch/x86_64/include/arch/boot/pointers.hpp @@ -13,7 +13,7 @@ namespace teachos::arch::boot /** * @brief Address pointing to the method that clears all segment registers. */ - extern "C" size_t reload_segment_register; + extern "C" void reload_segment_register(); } // namespace teachos::arch::boot diff --git a/arch/x86_64/include/arch/context_switching/segment_descriptor_table/gdt_flags.hpp b/arch/x86_64/include/arch/context_switching/segment_descriptor_table/gdt_flags.hpp index fdf0044..764aec5 100644 --- a/arch/x86_64/include/arch/context_switching/segment_descriptor_table/gdt_flags.hpp +++ b/arch/x86_64/include/arch/context_switching/segment_descriptor_table/gdt_flags.hpp @@ -18,17 +18,24 @@ namespace teachos::arch::context_switching::segment_descriptor_table */ enum bitset : uint8_t { - LENGTH = 1U << 0U, ///< Defines in IA-32e mode (64-bit code and 32-bit compatability mode) if the segment - ///< contains 64-bit code. Otherwise this bit should always be 0. Enable if instructions are - ///< executed in 64-bit code, otherwise they are executed in compatability 32-bit mode. If bis - ///< is set the DEFAULT_LENGTH bis needs to be 0 + LONG_MODE = 1U << 1U, ///< Defines in IA-32e mode (64-bit code and 32-bit compatability mode) if the segment + ///< contains 64-bit code. Otherwise this bit should always be 0. Enable if instructions are + ///< executed in 64-bit code, otherwise they are executed in compatability 32-bit mode. If bis + ///< is set the DEFAULT_LENGTH bis needs to be 0 + + // FIXME: Where does this come from, and is this value correct? UPPER_BOUND = 1U << 1U, ///< Specifies the upper bound of the segment for expand down data segment. Enable for 5 ///< GiB, 4 KiB otherwise. + + // FIXME: Where does this come from, and is this value correct? STACK_POINTER_SIZE = 1U << 1U, ///< Specifies the size of the Stack Pointer (SP) for stack segments used for ///< implicit stack operations. Enable for 32 bit, 16 bit otherwise. + + // FIXME: Where does this come from, and is this value correct? DEFAULT_LENGTH = 1U << 1U, ///< Indicates the default length for code segments with effective addresses and ///< operands. Enable for 32 bit, 16 bit otherwise. - GRANULARITY = 1U << 2U, ///< Indicates the size the Limit value in the segment descriptor is scaled by 1 Byte + + GRANULARITY = 1U << 3U, ///< Indicates the size the Limit value in the segment descriptor is scaled by 1 Byte ///< blocks if the bit is not set or by 4 KiB blocks if the bit is set. }; diff --git a/arch/x86_64/include/arch/context_switching/segment_descriptor_table/global_descriptor_table_pointer.hpp b/arch/x86_64/include/arch/context_switching/segment_descriptor_table/global_descriptor_table_pointer.hpp index 13d6f53..6fac2a0 100644 --- a/arch/x86_64/include/arch/context_switching/segment_descriptor_table/global_descriptor_table_pointer.hpp +++ b/arch/x86_64/include/arch/context_switching/segment_descriptor_table/global_descriptor_table_pointer.hpp @@ -26,7 +26,7 @@ namespace teachos::arch::context_switching::segment_descriptor_table /** * @brief Constructor. */ - global_descriptor_table_pointer(uint16_t table_length, global_descriptor_table * address); + global_descriptor_table_pointer(uint16_t table_length, segment_descriptor * address); /** * @brief Defaulted three-way comparsion operator. @@ -35,7 +35,7 @@ namespace teachos::arch::context_switching::segment_descriptor_table private: uint16_t table_length = {}; ///< The amount of segment descriptor entries in the global descriptor table - 1. - global_descriptor_table * address = {}; ///< Non-owning pointer to the GDT base address. + segment_descriptor * address = {}; ///< Non-owning pointer to the GDT base address. }; } // namespace teachos::arch::context_switching::segment_descriptor_table diff --git a/arch/x86_64/include/arch/kernel/cpu/jmp.hpp b/arch/x86_64/include/arch/kernel/cpu/jmp.hpp index 163fac6..0ed38e9 100644 --- a/arch/x86_64/include/arch/kernel/cpu/jmp.hpp +++ b/arch/x86_64/include/arch/kernel/cpu/jmp.hpp @@ -10,11 +10,10 @@ namespace teachos::arch::kernel::cpu /** * @brief Far jump - A jump to an instruction located in a different segment. */ - struct [[gnu::packed]] far_pointer + struct far_pointer { - std::size_t function; ///< Address of the function we want to jump too. (0- 63) - context_switching::interrupt_descriptor_table::segment_selector - selector; ///< Segment selector that shows the segment we want to jump into. (64 - 79) + void (*function)(); ///< Address of the function we want to jump too. (0-63) + std::uint16_t index; ///< Index of the GDT entry we want to load into register CS. (64-79) }; /** diff --git a/arch/x86_64/src/boot/boot.s b/arch/x86_64/src/boot/boot.s index bf150a3..108dbe5 100644 --- a/arch/x86_64/src/boot/boot.s +++ b/arch/x86_64/src/boot/boot.s @@ -356,6 +356,9 @@ prepare_page_maps: .global reload_segment_register reload_segment_register: + // FIXME: maybe we should set the actually correct values here. We'd need to communicate them down from C++. + // Alternatively, we could probably implement this as a [[gnu::naked]] function in C++, to have easier access to + // arguments and symbols. Maybe later. xor %rax, %rax mov %rax, %ss mov %rax, %ds diff --git a/arch/x86_64/src/context_switching/main.cpp b/arch/x86_64/src/context_switching/main.cpp index 5d19f23..a5bd3fb 100644 --- a/arch/x86_64/src/context_switching/main.cpp +++ b/arch/x86_64/src/context_switching/main.cpp @@ -13,22 +13,21 @@ namespace teachos::arch::context_switching decltype(auto) global_descriptor_table = segment_descriptor_table::initialize_global_descriptor_table(); decltype(auto) interrupt_descriptor_table = interrupt_descriptor_table::initialize_interrupt_descriptor_table(); - interrupt_descriptor_table::segment_selector segment_selector{ - 1U, interrupt_descriptor_table::segment_selector::REQUEST_LEVEL_KERNEL}; - kernel::cpu::far_pointer pointer{boot::reload_segment_register, segment_selector}; - kernel::cpu::jmp(pointer); + kernel::cpu::far_pointer pointer{&boot::reload_segment_register, 1 * sizeof(segment_descriptor_table::segment_descriptor)}; + asm volatile("rex64 lcall *%[far_function_pointer]" : : [far_function_pointer] "m" (pointer)); - // 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 * 16) = 80 - uint16_t const tss_selector = - (global_descriptor_table.size() - 1) * sizeof(segment_descriptor_table::segment_descriptor); - kernel::cpu::load_task_register(tss_selector); + // // 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 * 16) = 80 + // uint16_t const tss_selector = + // (global_descriptor_table.size() - 1) * sizeof(segment_descriptor_table::segment_descriptor); + // 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."); + // 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."); - kernel::cpu::set_interrupt_flag(); + // FIXME: We currently cannot enable interrupts, since for some reason, we will later run into what looks like a GP. Maybe because no IDT is loaded? Maybe our boot code segment is not set up correctly? + // kernel::cpu::set_interrupt_flag(); descriptor_tables tables = {global_descriptor_table, interrupt_descriptor_table}; return tables; diff --git a/arch/x86_64/src/context_switching/segment_descriptor_table/global_descriptor_table.cpp b/arch/x86_64/src/context_switching/segment_descriptor_table/global_descriptor_table.cpp index e6a489c..37ee778 100644 --- a/arch/x86_64/src/context_switching/segment_descriptor_table/global_descriptor_table.cpp +++ b/arch/x86_64/src/context_switching/segment_descriptor_table/global_descriptor_table.cpp @@ -20,12 +20,14 @@ namespace teachos::arch::context_switching::segment_descriptor_table uint8_t gdt_flags_bits = gdt_flags::GRANULARITY; if (segment_descriptor_type == segment_descriptor_type::CODE_SEGMENT) { - gdt_flags_bits |= gdt_flags::LENGTH; + gdt_flags_bits |= gdt_flags::LONG_MODE; access_level_bits |= access_byte::CODE_SEGMENT | access_byte::READABLE; } else if (segment_descriptor_type == segment_descriptor_type::DATA_SEGMENT) { - gdt_flags_bits |= gdt_flags::UPPER_BOUND; + gdt_flags_bits |= 1 << 2; + // FIXME: Look at those bit flags, something seems off. + // gdt_flags_bits |= gdt_flags::UPPER_BOUND; access_level_bits |= access_byte::WRITABLE; } @@ -78,7 +80,7 @@ namespace teachos::arch::context_switching::segment_descriptor_table // 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. global_descriptor_table_pointer gdt_pointer{static_cast<uint16_t>((gdt.size() * sizeof(segment_descriptor)) - 1), - &gdt}; + gdt.data()}; kernel::cpu::load_global_descriptor_table(gdt_pointer); auto const stored_gdt_pointer = kernel::cpu::store_global_descriptor_table(); diff --git a/arch/x86_64/src/context_switching/segment_descriptor_table/global_descriptor_table_pointer.cpp b/arch/x86_64/src/context_switching/segment_descriptor_table/global_descriptor_table_pointer.cpp index 132565f..a4a5de8 100644 --- a/arch/x86_64/src/context_switching/segment_descriptor_table/global_descriptor_table_pointer.cpp +++ b/arch/x86_64/src/context_switching/segment_descriptor_table/global_descriptor_table_pointer.cpp @@ -3,7 +3,7 @@ namespace teachos::arch::context_switching::segment_descriptor_table { global_descriptor_table_pointer::global_descriptor_table_pointer(uint16_t table_length, - global_descriptor_table * address) + segment_descriptor * address) : table_length(table_length) , address(address) { |
