aboutsummaryrefslogtreecommitdiff
path: root/arch/x86_64/src/context_switching/main.cpp
blob: 4e13b1c5a351568793003ce00ada74117bb937cd (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
#include "arch/context_switching/main.hpp"

#include "arch/boot/pointers.hpp"
#include "arch/exception_handling/assert.hpp"
#include "arch/kernel/cpu/call.hpp"
#include "arch/kernel/cpu/control_register.hpp"
#include "arch/kernel/cpu/if.hpp"
#include "arch/kernel/cpu/msr.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
  {
    auto constexpr IA32_STAR_ADDRESS = 0xC0000081;
    auto constexpr IA32_LSTAR_ADDRESS = 0xC0000082;
    auto constexpr IA32_FMASK_ADDRESS = 0xC0000084;

    constexpr interrupt_descriptor_table::segment_selector KERNEL_CODE_SEGMENT_SELECTOR{
        1U, interrupt_descriptor_table::segment_selector::REQUEST_LEVEL_KERNEL};
    constexpr kernel::cpu::far_pointer KERNEL_CODE_POINTER{&kernel::cpu::reload_data_segment_registers,
                                                           KERNEL_CODE_SEGMENT_SELECTOR};
    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};

    auto reload_global_descriptor_table_register() -> void { kernel::cpu::call(KERNEL_CODE_POINTER); }

    auto user_mode_main() -> void
    {
      kernel::cpu::validate_segment_registers(USER_DATA_SEGMENT_SELECTOR, USER_CODE_SEGMENT_SELECTOR);

      video::vga::text::write("Successfully entered user mode!", video::vga::text::common_attributes::green_on_black);

      asm volatile("syscall");

      kernel::cpu::validate_segment_registers(USER_DATA_SEGMENT_SELECTOR, USER_CODE_SEGMENT_SELECTOR);

      video::vga::text::write("Successfully made a SYSCALL and returned back with SYSRET!",
                              video::vga::text::common_attributes::green_on_black);
    }

    auto syscall_handler() -> void
    {
      uint64_t dummy{};
      switch (dummy)
      {
        case 0:
          break;
        default:
          break;
      }

      asm volatile("sysretq");
    }

    auto enable_systemcall() -> void
    {
      uint64_t const syscall_function = reinterpret_cast<uint64_t>(syscall_handler);
      kernel::cpu::write_msr(IA32_LSTAR_ADDRESS, syscall_function);
      kernel::cpu::write_msr(IA32_FMASK_ADDRESS, 0U);

      uint64_t kernel_cs = KERNEL_CODE_SEGMENT_SELECTOR;    // Your 64-bit kernel code segment selector
      uint64_t user_cs = USER_CODE_SEGMENT_SELECTOR + 0x3;  // User mode code segment selector (RPL=3)

      uint64_t star_value = (user_cs << 48) | (kernel_cs << 32);

      kernel::cpu::write_msr(IA32_STAR_ADDRESS, star_value);
      kernel::cpu::write_msr(IA32_STAR_ADDRESS, KERNEL_CODE_SEGMENT_SELECTOR);

      // kernel::cpu::write_msr(IA32_STAR_ADDRESS, KERNEL_CODE_SEGMENT_SELECTOR);
      kernel::cpu::set_efer_bit(kernel::cpu::efer_flags::SCE);
    }

  }  // namespace

  auto initialize_descriptor_tables() -> descriptor_tables
  {
    static bool initalized = false;

    if (!initalized)
    {
      kernel::cpu::clear_interrupt_flag();

      segment_descriptor_table::update_global_descriptor_table_register();
      interrupt_descriptor_table::update_interrupt_descriptor_table_register();

      reload_global_descriptor_table_register();
      segment_descriptor_table::update_task_state_segment_register();

      kernel::cpu::set_interrupt_flag();
      initalized = true;
    }

    descriptor_tables tables = {segment_descriptor_table::get_or_create_global_descriptor_table(),
                                interrupt_descriptor_table::get_or_create_interrupt_descriptor_table()};
    return tables;
  }

  auto switch_to_user_mode() -> void
  {
    enable_systemcall();
    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
  {
    (void)initialize_descriptor_tables();
    kernel::cpu::set_data_segment_registers(data_segment);
    kernel::cpu::set_code_segment_register(data_segment, code_segment, reinterpret_cast<uint64_t>(return_function));
  }
}  // namespace teachos::arch::context_switching