diff options
7 files changed, 121 insertions, 63 deletions
diff --git a/arch/x86_64/include/arch/context_switching/interrupt_descriptor_table/segment_selector.hpp b/arch/x86_64/include/arch/context_switching/interrupt_descriptor_table/segment_selector.hpp index b31f9e8..e8854f9 100644 --- a/arch/x86_64/include/arch/context_switching/interrupt_descriptor_table/segment_selector.hpp +++ b/arch/x86_64/include/arch/context_switching/interrupt_descriptor_table/segment_selector.hpp @@ -52,7 +52,12 @@ namespace teachos::arch::context_switching::interrupt_descriptor_table * @param flags Allows to set flags for the flags field using the unscoped enum contained in this class, used to * allow for direct integer conversion. */ - segment_selector(uint16_t index, uint8_t flags); + constexpr segment_selector(uint16_t index, uint8_t flags) + : _flags(flags) + , _index(index * 2U) + { + // Nothing to do. + } /** * @brief Checks if the given std::bitset is a subset or equivalent to the underlying data. diff --git a/arch/x86_64/include/arch/context_switching/main.hpp b/arch/x86_64/include/arch/context_switching/main.hpp index d2243ed..e296457 100644 --- a/arch/x86_64/include/arch/context_switching/main.hpp +++ b/arch/x86_64/include/arch/context_switching/main.hpp @@ -15,8 +15,32 @@ namespace teachos::arch::context_switching interrupt_descriptor_table::interrupt_descriptor_table & idt; ///< Reference to the interrupt descriptor table. }; + /** + * @brief Setup GDT and IDT TODO + * + * @return descriptor_tables + */ auto initialize_descriptor_tables() -> descriptor_tables; + /** + * @brief TODO + * + * @param data_segment + * @param code_segment + * @param return_function + */ + auto switch_to_user_mode() -> void; + + /** + * @brief TODO + * + * @param data_segment + * @param code_segment + * @param return_function + */ + auto switch_context(interrupt_descriptor_table::segment_selector data_segment, + interrupt_descriptor_table::segment_selector code_segment, void (*return_function)()) -> void; + } // namespace teachos::arch::context_switching #endif // TEACHOS_ARCH_X86_64_CONTEXT_SWITCHING_MAIN_HPP diff --git a/arch/x86_64/include/arch/kernel/cpu/segment_register.hpp b/arch/x86_64/include/arch/kernel/cpu/segment_register.hpp index bd244fd..fd5a972 100644 --- a/arch/x86_64/include/arch/kernel/cpu/segment_register.hpp +++ b/arch/x86_64/include/arch/kernel/cpu/segment_register.hpp @@ -12,12 +12,19 @@ namespace teachos::arch::kernel::cpu auto reload_segment_registers() -> void; /** - * @brief Set the value of all segment registers. + * @brief Set the value of all segment registers. TODO * * @param segment_selector */ auto set_segment_registers(context_switching::interrupt_descriptor_table::segment_selector segment_selector) -> void; + /** + * @brief Returns the segment_selector in the code segment (cs) register. TODO + * + * @return segment_selector in the cs register + */ + auto read_code_segment_register() -> context_switching::interrupt_descriptor_table::segment_selector; + } // namespace teachos::arch::kernel::cpu #endif // TEACHOS_ARCH_X86_64_KERNEL_CPU_SEGMENT_REGISTER_HPP diff --git a/arch/x86_64/src/context_switching/interrupt_descriptor_table/segment_selector.cpp b/arch/x86_64/src/context_switching/interrupt_descriptor_table/segment_selector.cpp index b1b316d..0e29356 100644 --- a/arch/x86_64/src/context_switching/interrupt_descriptor_table/segment_selector.cpp +++ b/arch/x86_64/src/context_switching/interrupt_descriptor_table/segment_selector.cpp @@ -2,13 +2,6 @@ namespace teachos::arch::context_switching::interrupt_descriptor_table { - segment_selector::segment_selector(uint16_t index, uint8_t flags) - : _flags(flags) - , _index(index * 2U) - { - // Nothing to do. - } - auto segment_selector::contains_flags(std::bitset<3U> other) const -> bool { return (std::bitset<3U>{_flags} & other) == other; diff --git a/arch/x86_64/src/context_switching/main.cpp b/arch/x86_64/src/context_switching/main.cpp index 762445f..3efba45 100644 --- a/arch/x86_64/src/context_switching/main.cpp +++ b/arch/x86_64/src/context_switching/main.cpp @@ -5,9 +5,59 @@ #include "arch/kernel/cpu/if.hpp" #include "arch/kernel/cpu/segment_register.hpp" #include "arch/kernel/cpu/tr.hpp" +#include "arch/video/vga/text.hpp" namespace teachos::arch::context_switching { + namespace + { + + /** + * @brief Switch context into the mode defined in the segment selectors. + * + * Setup the stack IRETQ expects to switch the mode: + * 1. push data selector + * 2. push current stack pointer + * 3. push eflags + * 4. push code segment selector + * 5. push return address + * + * @param data_segment + * @param code_segment + * @param address + */ + [[gnu::naked]] + auto far_return(context_switching::interrupt_descriptor_table::segment_selector data_segment, + context_switching::interrupt_descriptor_table::segment_selector code_segment, uint64_t address) + -> void + { + asm volatile("mov %[data_segment], %%rax\n" + "mov %%rax, %%ds\n" + "mov %%rax, %%es\n" + "mov %%rax, %%fs\n" + "mov %%rax, %%gs\n" + "mov %%rsp, %%rax\n" + + "push %[data_segment]\n" + "push %%rax\n" + "pushfq\n" + "push %[code_segment]\n" + "mov %[return_function], %%rax\n" + "push %%rax\n" + + "iretq\n" + : + : [data_segment] "m"(data_segment), [code_segment] "m"(code_segment), [return_function] "r"(address) + : "rax"); + } + + constexpr context_switching::interrupt_descriptor_table::segment_selector USER_CODE_SEGMENT_SELECTOR{ + 3U, context_switching::interrupt_descriptor_table::segment_selector::REQUEST_LEVEL_USER}; + constexpr context_switching::interrupt_descriptor_table::segment_selector USER_DATA_SEGMENT_SELECTOR{ + 4U, context_switching::interrupt_descriptor_table::segment_selector::REQUEST_LEVEL_USER}; + + } // namespace + auto initialize_descriptor_tables() -> descriptor_tables { kernel::cpu::clear_interrupt_flag(); @@ -28,4 +78,26 @@ namespace teachos::arch::context_switching interrupt_descriptor_table::get_or_create_interrupt_descriptor_table()}; return tables; } + + auto user_mode_main() -> void + { + auto current_segment = kernel::cpu::read_code_segment_register(); + arch::exception_handling::assert(USER_CODE_SEGMENT_SELECTOR == current_segment, + "[Context Switching] Context switch into user mode not successful"); + + video::vga::text::write("User Mode!!!", video::vga::text::common_attributes::green_on_black); + } + + auto switch_to_user_mode() -> void + { + switch_context(USER_DATA_SEGMENT_SELECTOR, USER_CODE_SEGMENT_SELECTOR, user_mode_main); + } + + auto switch_context(interrupt_descriptor_table::segment_selector data_segment, + interrupt_descriptor_table::segment_selector code_segment, void (*return_function)()) -> void + { + kernel::cpu::set_segment_registers(data_segment); + far_return(data_segment, code_segment, reinterpret_cast<uint64_t>(return_function)); + } + } // namespace teachos::arch::context_switching diff --git a/arch/x86_64/src/kernel/cpu/segment_register.cpp b/arch/x86_64/src/kernel/cpu/segment_register.cpp index d7857dd..9fb7433 100644 --- a/arch/x86_64/src/kernel/cpu/segment_register.cpp +++ b/arch/x86_64/src/kernel/cpu/segment_register.cpp @@ -30,4 +30,13 @@ namespace teachos::arch::kernel::cpu : [input] "m"(segment_selector)); } + auto read_code_segment_register() -> context_switching::interrupt_descriptor_table::segment_selector + { + context_switching::interrupt_descriptor_table::segment_selector current_value; + asm volatile("mov %%cs, %[output]" : [output] "=r"(current_value)); + return current_value; + } + + auto validate_segment_registers() -> context_switching::interrupt_descriptor_table::segment_selector {} + } // namespace teachos::arch::kernel::cpu diff --git a/arch/x86_64/src/kernel/main.cpp b/arch/x86_64/src/kernel/main.cpp index ac2591e..b69064d 100644 --- a/arch/x86_64/src/kernel/main.cpp +++ b/arch/x86_64/src/kernel/main.cpp @@ -50,50 +50,6 @@ namespace teachos::arch::kernel delete test9; } - auto return_function() -> void - { - video::vga::text::write("User Mode!!!", video::vga::text::common_attributes::green_on_black); - } - - /** - * @brief Switch context into the mode defined in the segment selectors. - * - * Setup the stack IRETQ expects to switch the mode: - * 1. push data selector - * 2. push current stack pointer - * 3. push eflags - * 4. push code segment selector - * 5. push return address - * - * @param data_segment - * @param code_segment - * @param address - */ - [[gnu::naked]] - auto switch_context(context_switching::interrupt_descriptor_table::segment_selector data_segment, - context_switching::interrupt_descriptor_table::segment_selector code_segment, uint64_t address) - -> void - { - asm volatile("mov %[data_segment], %%rax\n" - "mov %%rax, %%ds\n" - "mov %%rax, %%es\n" - "mov %%rax, %%fs\n" - "mov %%rax, %%gs\n" - "mov %%rsp, %%rax\n" - - "push %[data_segment]\n" - "push %%rax\n" - "pushfq\n" - "push %[code_segment]\n" - "mov %[return_function], %%rax\n" - "push %%rax\n" - - "iretq\n" - : - : [data_segment] "m"(data_segment), [code_segment] "m"(code_segment), [return_function] "r"(address) - : "rax"); - } - auto main() -> void { video::vga::text::clear(); @@ -109,16 +65,8 @@ namespace teachos::arch::kernel heap_test(); decltype(auto) descriptor_tables = context_switching::initialize_descriptor_tables(); - - context_switching::interrupt_descriptor_table::segment_selector user_code_segment_selector{ - 3U, context_switching::interrupt_descriptor_table::segment_selector::REQUEST_LEVEL_USER}; - context_switching::interrupt_descriptor_table::segment_selector user_data_segment_selector{ - 4U, context_switching::interrupt_descriptor_table::segment_selector::REQUEST_LEVEL_USER}; - - cpu::set_segment_registers(user_data_segment_selector); - switch_context(user_data_segment_selector, user_code_segment_selector, - reinterpret_cast<uint64_t>(&return_function)); - (void)descriptor_tables; + + context_switching::switch_to_user_mode(); } } // namespace teachos::arch::kernel |
