diff options
| author | Matteo Gmür <matteo.gmuer1@ost.ch> | 2025-03-31 10:38:53 +0000 |
|---|---|---|
| committer | Matteo Gmür <matteo.gmuer1@ost.ch> | 2025-03-31 10:38:53 +0000 |
| commit | abe7bd7480c8f4e1e30b9f0f3b98966222817f3e (patch) | |
| tree | 37a27a84eadcd4694843b8d5e1e24bf498106dd5 | |
| parent | 8d16dcb672c4b5f4b0a12ef2eac3486f1b2bb316 (diff) | |
| download | teachos-abe7bd7480c8f4e1e30b9f0f3b98966222817f3e.tar.xz teachos-abe7bd7480c8f4e1e30b9f0f3b98966222817f3e.zip | |
Clean up global descriptor table initalization
11 files changed, 100 insertions, 77 deletions
diff --git a/arch/x86_64/include/arch/context_switching/interrupt_descriptor_table/gate_descriptor.hpp b/arch/x86_64/include/arch/context_switching/interrupt_descriptor_table/gate_descriptor.hpp index e677cbb..22cd0f0 100644 --- a/arch/x86_64/include/arch/context_switching/interrupt_descriptor_table/gate_descriptor.hpp +++ b/arch/x86_64/include/arch/context_switching/interrupt_descriptor_table/gate_descriptor.hpp @@ -61,8 +61,7 @@ namespace teachos::arch::context_switching::interrupt_descriptor_table segment_selector _selector = {}; ///< Segment selector (16 - 31) ist_offset _ist = {}; ///< Interrupt Stack Table offset (32 - 39) idt_flags _flags = {}; ///< Gate Type and Flags (40 - 47) - uint16_t _offset_2 = {}; ///< Middle 16 bits of handler function address (48 - 63) - uint32_t _offset_3 = {}; ///< Upper 32 bits of handler function address (for x86_64) (64 - 95) + uint64_t _offset_2 : 48 = {}; ///< Upper 48 bits of handler function address (48 - 95) uint32_t : 32; ///< Reserved field used to ensure this struct is 128 bits big (96 - 127) }; } // namespace teachos::arch::context_switching::interrupt_descriptor_table diff --git a/arch/x86_64/include/arch/context_switching/interrupt_descriptor_table/interrupt_descriptor_table.hpp b/arch/x86_64/include/arch/context_switching/interrupt_descriptor_table/interrupt_descriptor_table.hpp index e2ec4c5..b388e0e 100644 --- a/arch/x86_64/include/arch/context_switching/interrupt_descriptor_table/interrupt_descriptor_table.hpp +++ b/arch/x86_64/include/arch/context_switching/interrupt_descriptor_table/interrupt_descriptor_table.hpp @@ -6,12 +6,18 @@ namespace teachos::arch::context_switching::interrupt_descriptor_table { /** - * @brief Initializes the interrupt_descriptor_table by loading it - * in the IDTR register. + * @brief Updates the IDTR with the created interrupt descriptor table. If it has not been created yet this + * method will create it. + */ + auto update_interrupt_descriptor_table_register() -> void; + + /** + * @brief Creates the interrupt descriptor table, with the minimum required configuration. If this method is called + * more than once, the previously created instance is returned instead. * * @return Reference to the created interrupt_descriptor_table. */ - auto initialize_interrupt_descriptor_table() -> interrupt_descriptor_table &; + auto get_or_create_interrupt_descriptor_table() -> interrupt_descriptor_table &; } // namespace teachos::arch::context_switching::interrupt_descriptor_table 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 8217bcb..d8c3cd1 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,16 @@ namespace teachos::arch::context_switching::segment_descriptor_table */ enum bitset : uint8_t { - AVAILABLE = 1U << 0U, ///< Available for use by System software. For our purposes this is basically reserved. 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 this is set the DEFAULT_OPERAND_SIZE/BIG bit needs to be clear (0). - - DEFAULT_OPERATION_SIZE = - 1U << 2U, ///< If clear, this is a 16-bit code segment; if set, this is a 32-bit segment. - BIG = 1U << 2U, ///< If set, the maximum offset size for a data segment is increased to 32-bit - ///< 0xffffffff. Otherwise it's the 16-bit max 0x0000ffff. Essentially the same meaning as "D". - + ///< If this bit is set the 3rd bit needs to be clear (0). + UPPER_BOUND = 1U << 2U, ///< Specifies the upper bound of the segment for expand down data segment. Enable for 4 + ///< GiB, 4 KiB otherwise. + STACK_POINTER_SIZE = 1U << 2U, ///< Specifies the size of the Stack Pointer (SP) for stack segments used for + ///< implicit stack operations. Enable for 32 bit, 16 bit otherwise. + DEFAULT_LENGTH = 1U << 2U, ///< Indicates the default length for code segments with effective addresses and + ///< operands. Enable for 32 bit, 16 bit otherwise. 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.hpp b/arch/x86_64/include/arch/context_switching/segment_descriptor_table/global_descriptor_table.hpp index f3067ba..bd69a46 100644 --- a/arch/x86_64/include/arch/context_switching/segment_descriptor_table/global_descriptor_table.hpp +++ b/arch/x86_64/include/arch/context_switching/segment_descriptor_table/global_descriptor_table.hpp @@ -7,12 +7,30 @@ namespace teachos::arch::context_switching::segment_descriptor_table { /** - * @brief Initializes the global_descriptor_table and task_state_segment by loading them - * in the GDTR and TR registers respectively. + * @brief Creates the global descriptor table, with the minimum required configuration. If this method is called more + * than once, the previously created instance is returned instead. * * @return Reference to the created global_descriptor_table. */ - auto initialize_global_descriptor_table() -> global_descriptor_table &; + auto get_or_create_global_descriptor_table() -> global_descriptor_table &; + + /** + * @brief Updates the GDTR with the created global descriptor table. If it has not been created yet this + * method will create it. + * + * @note This method will only set the GDTR, but for the processor to actually register the change a far jump + * has to be executed. This also has to be done before updating the TR. + */ + auto update_global_descriptor_table_register() -> void; + + /** + * @brief Updates the TR with the created task state segment. If it has not been created yet this + * method will create it. + * + * @note This method should only be called after update_global_descriptor_table_register() and a far jump has been + * executed. Because before that trying to access the segment will cause an exception. + */ + auto update_task_state_segment_register() -> void; } // 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 0ed38e9..1657c18 100644 --- a/arch/x86_64/include/arch/kernel/cpu/jmp.hpp +++ b/arch/x86_64/include/arch/kernel/cpu/jmp.hpp @@ -13,7 +13,8 @@ namespace teachos::arch::kernel::cpu struct far_pointer { 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) + context_switching::interrupt_descriptor_table::segment_selector + selector; ///< Segment selector pointing to the GDT entry we want to load into register CS. (64-79) }; /** diff --git a/arch/x86_64/src/context_switching/interrupt_descriptor_table/gate_descriptor.cpp b/arch/x86_64/src/context_switching/interrupt_descriptor_table/gate_descriptor.cpp index d86c459..28f289c 100644 --- a/arch/x86_64/src/context_switching/interrupt_descriptor_table/gate_descriptor.cpp +++ b/arch/x86_64/src/context_switching/interrupt_descriptor_table/gate_descriptor.cpp @@ -8,7 +8,6 @@ namespace teachos::arch::context_switching::interrupt_descriptor_table , _ist(flags >> 32U) , _flags(flags >> 40U) , _offset_2(flags >> 48U) - , _offset_3(flags >> 64U) { // Nothing to do. } @@ -19,7 +18,6 @@ namespace teachos::arch::context_switching::interrupt_descriptor_table , _ist(ist) , _flags(flags) , _offset_2(offset >> 16U) - , _offset_3(offset >> 48U) { // Nothing to do. } diff --git a/arch/x86_64/src/context_switching/interrupt_descriptor_table/interrupt_descriptor_table.cpp b/arch/x86_64/src/context_switching/interrupt_descriptor_table/interrupt_descriptor_table.cpp index 1c1de68..62b116b 100644 --- a/arch/x86_64/src/context_switching/interrupt_descriptor_table/interrupt_descriptor_table.cpp +++ b/arch/x86_64/src/context_switching/interrupt_descriptor_table/interrupt_descriptor_table.cpp @@ -6,26 +6,23 @@ namespace teachos::arch::context_switching::interrupt_descriptor_table { - namespace + auto get_or_create_interrupt_descriptor_table() -> interrupt_descriptor_table & { + // @MTO: This address resolution is most certainly wrong -> numbers in dbg seem off (offset_3 = 0) + uint64_t offset = reinterpret_cast<uint64_t>(&interrupt_handling::generic_interrupt_handler); + segment_selector selector{0U, segment_selector::REQUEST_LEVEL_KERNEL}; + ist_offset ist{0U}; + idt_flags flags{idt_flags::DESCRIPTOR_LEVEL_KERNEL | idt_flags::PRESENT}; + gate_descriptor descriptor{selector, ist, flags, offset}; - auto create_interrupt_descriptor_table() -> interrupt_descriptor_table - { - // @MTO: This address resolution is most certainly wrong -> numbers in dbg seem off (offset_3 = 0) - uint64_t offset = reinterpret_cast<uint64_t>(&interrupt_handling::generic_interrupt_handler); - segment_selector selector{0U, segment_selector::REQUEST_LEVEL_KERNEL}; - ist_offset ist{0U}; - idt_flags flags{idt_flags::DESCRIPTOR_LEVEL_KERNEL | idt_flags::PRESENT}; - gate_descriptor descriptor{selector, ist, flags, offset}; - - return interrupt_descriptor_table{descriptor}; - } - } // namespace + // Interrupt Descriptor Table needs to be kept alive + static interrupt_descriptor_table interrupt_descriptor_table{descriptor}; + return interrupt_descriptor_table; + } - auto initialize_interrupt_descriptor_table() -> interrupt_descriptor_table & + auto update_interrupt_descriptor_table_register() -> void { - // Interrupt Descriptor Table needs to be kept alive - static auto idt = create_interrupt_descriptor_table(); + static 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()}; @@ -35,7 +32,5 @@ namespace teachos::arch::context_switching::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."); - - return idt; } } // namespace teachos::arch::context_switching::interrupt_descriptor_table diff --git a/arch/x86_64/src/context_switching/main.cpp b/arch/x86_64/src/context_switching/main.cpp index 008da2f..db04b52 100644 --- a/arch/x86_64/src/context_switching/main.cpp +++ b/arch/x86_64/src/context_switching/main.cpp @@ -11,22 +11,16 @@ namespace teachos::arch::context_switching auto initialize_descriptor_tables() -> descriptor_tables { kernel::cpu::clear_interrupt_flag(); - decltype(auto) global_descriptor_table = segment_descriptor_table::initialize_global_descriptor_table(); - decltype(auto) interrupt_descriptor_table = interrupt_descriptor_table::initialize_interrupt_descriptor_table(); - 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)); + segment_descriptor_table::update_global_descriptor_table_register(); + interrupt_descriptor_table::update_interrupt_descriptor_table_register(); - // 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); + interrupt_descriptor_table::segment_selector segment_selector{ + 2U, interrupt_descriptor_table::segment_selector::REQUEST_LEVEL_KERNEL}; + kernel::cpu::far_pointer pointer{&boot::reload_segment_register, segment_selector}; + kernel::cpu::jmp(pointer); - 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."); + segment_descriptor_table::update_task_state_segment_register(); // FIXME: We currently cannot enable interrupts, since for some reason, we will later run into what looks like a GP // and triple fault. @@ -34,7 +28,8 @@ namespace teachos::arch::context_switching // idt). NEVER happened when stepping through quickly. Can you reproduce this? kernel::cpu::set_interrupt_flag(); - descriptor_tables tables = {global_descriptor_table, interrupt_descriptor_table}; + descriptor_tables tables = {segment_descriptor_table::get_or_create_global_descriptor_table(), + interrupt_descriptor_table::get_or_create_interrupt_descriptor_table()}; return tables; } } // namespace teachos::arch::context_switching 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 d9ad91c..346db9e 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 @@ -24,7 +24,7 @@ namespace teachos::arch::context_switching::segment_descriptor_table } else if (segment_descriptor_type == segment_descriptor_type::DATA_SEGMENT) { - gdt_flags_bits |= 1 << 2; + gdt_flags_bits |= gdt_flags::UPPER_BOUND; access_level_bits |= access_byte::WRITABLE; } @@ -46,33 +46,33 @@ namespace teachos::arch::context_switching::segment_descriptor_table TSS_LIMIT}; return tss_descriptor; } + } // namespace - auto create_global_descriptor_table() -> global_descriptor_table - { - segment_descriptor const null_segment{0}; - segment_descriptor const kernel_code_segment = - create_segment_descriptor(segment_descriptor_type::CODE_SEGMENT, access_byte::DESCRIPTOR_LEVEL_KERNEL); - segment_descriptor const kernel_data_segment = - create_segment_descriptor(segment_descriptor_type::DATA_SEGMENT, access_byte::DESCRIPTOR_LEVEL_KERNEL); - segment_descriptor const user_code_segment = - create_segment_descriptor(segment_descriptor_type::CODE_SEGMENT, access_byte::DESCRIPTOR_LEVEL_USER); - segment_descriptor const user_data_segment = - create_segment_descriptor(segment_descriptor_type::DATA_SEGMENT, access_byte::DESCRIPTOR_LEVEL_USER); + auto get_or_create_global_descriptor_table() -> global_descriptor_table & + { + segment_descriptor const null_segment{0}; + segment_descriptor const kernel_code_segment = + create_segment_descriptor(segment_descriptor_type::CODE_SEGMENT, access_byte::DESCRIPTOR_LEVEL_KERNEL); + segment_descriptor const kernel_data_segment = + create_segment_descriptor(segment_descriptor_type::DATA_SEGMENT, access_byte::DESCRIPTOR_LEVEL_KERNEL); + segment_descriptor const user_code_segment = + create_segment_descriptor(segment_descriptor_type::CODE_SEGMENT, access_byte::DESCRIPTOR_LEVEL_USER); + segment_descriptor 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 const tss_descriptor = create_task_state_segment_descriptor(tss); + // Task State Segment needs to be kept alive + static auto tss = new task_state_segment(); + segment_descriptor const tss_descriptor = create_task_state_segment_descriptor(tss); - global_descriptor_table const global_descriptor_table{null_segment, kernel_code_segment, kernel_data_segment, - user_code_segment, user_data_segment, tss_descriptor}; - return global_descriptor_table; - } - } // namespace + // Global Descriptor Table needs to be kept alive + static global_descriptor_table global_descriptor_table{null_segment, kernel_code_segment, kernel_data_segment, + user_code_segment, user_data_segment, tss_descriptor}; + return global_descriptor_table; + } - auto initialize_global_descriptor_table() -> global_descriptor_table & + auto update_global_descriptor_table_register() -> void { - // Global Descriptor Table needs to be kept alive - static auto gdt = create_global_descriptor_table(); + decltype(auto) gdt = get_or_create_global_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. @@ -84,8 +84,20 @@ namespace teachos::arch::context_switching::segment_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_task_state_segment_register() -> void + { + decltype(auto) gdt = get_or_create_global_descriptor_table(); + + // 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 = (gdt.size() - 1) * sizeof(segment_descriptor_table::segment_descriptor); + kernel::cpu::load_task_register(tss_selector); - return gdt; + 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/src/interrupt_handling/generic_interrupt_handler.cpp b/arch/x86_64/src/interrupt_handling/generic_interrupt_handler.cpp index 68b4568..2f599e5 100644 --- a/arch/x86_64/src/interrupt_handling/generic_interrupt_handler.cpp +++ b/arch/x86_64/src/interrupt_handling/generic_interrupt_handler.cpp @@ -11,4 +11,4 @@ namespace teachos::arch::interrupt_handling (void)frame; video::vga::text::write("An Interrupt occurred.", video::vga::text::common_attributes::green_on_black); } -} // namespace teachos::arch::interrupt_handling
\ No newline at end of file +} // namespace teachos::arch::interrupt_handling diff --git a/arch/x86_64/src/kernel/cpu/jmp.cpp b/arch/x86_64/src/kernel/cpu/jmp.cpp index 0c94693..2833219 100644 --- a/arch/x86_64/src/kernel/cpu/jmp.cpp +++ b/arch/x86_64/src/kernel/cpu/jmp.cpp @@ -9,6 +9,6 @@ namespace teachos::arch::kernel::cpu auto jmp(far_pointer pointer) -> void { - asm volatile("rex64 ljmp *%[input]" : /* no output from call */ : [input] "m"(pointer)); + asm volatile("rex64 lcall *%[input]" : /* no output from call */ : [input] "m"(pointer)); } } // namespace teachos::arch::kernel::cpu |
