aboutsummaryrefslogtreecommitdiff
path: root/arch/x86_64/src/kernel/cpu/segment_register.cpp
blob: b59cd1b81e4f8c266d8dd8620ea254d75101e424 (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
#include "arch/kernel/cpu/segment_register.hpp"

#include "arch/context_switching/interrupt_descriptor_table/segment_selector.hpp"
#include "arch/exception_handling/assert.hpp"

namespace teachos::arch::kernel::cpu
{
  auto reload_data_segment_registers() -> void
  {
    asm volatile("xor %%rax, %%rax\n"
                 "mov %%rax, %%ss\n"
                 "mov %%rax, %%ds\n"
                 "mov %%rax, %%es\n"
                 "mov %%rax, %%fs\n"
                 "mov %%rax, %%gs\n"
                 : /* no output from call */
                 : /* no input to call */
                 : "rax");
  }

  auto set_data_segment_registers(context_switching::interrupt_descriptor_table::segment_selector data_segment) -> void
  {
    asm volatile("xor %%rax, %%rax\n"
                 "mov %[input], %%ax\n"
                 "mov %%rax, %%ds\n"
                 "mov %%rax, %%es\n"
                 "mov %%rax, %%fs\n"
                 "mov %%rax, %%gs\n"
                 : /* no output from call */
                 : [input] "m"(data_segment)
                 : "rax");
  }

  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_data_segment_registers(context_switching::interrupt_descriptor_table::segment_selector data_segment)
      -> void
  {
    context_switching::interrupt_descriptor_table::segment_selector ss{};
    context_switching::interrupt_descriptor_table::segment_selector ds{};
    context_switching::interrupt_descriptor_table::segment_selector es{};
    context_switching::interrupt_descriptor_table::segment_selector fs{};
    context_switching::interrupt_descriptor_table::segment_selector gs{};

    asm volatile(
        "mov %%ss, %[ss_output]\n"
        "mov %%ds, %[ds_output]\n"
        "mov %%es, %[es_output]\n"
        "mov %%fs, %[fs_output]\n"
        "mov %%gs, %[gs_output]\n"
        : [ss_output] "=r"(ss), [ds_output] "=r"(ds), [es_output] "=r"(es), [fs_output] "=r"(fs), [gs_output] "=r"(gs));

    auto result = (ss == ds && ss == es && ss == fs && ss == gs);
    exception_handling::assert(result, "[Segment Register] Values in data register are not the same.");
    result = (ss == data_segment);
    exception_handling::assert(
        result, "[Segment Register] Expected Data Segment is not the same as the value in the Stack Segment register.");
  }

  auto validate_code_segment_register(context_switching::interrupt_descriptor_table::segment_selector code_segment)
      -> void
  {
    auto const cs = read_code_segment_register();
    exception_handling::assert(
        cs == code_segment,
        "[Segment Register] Expected Code Segment is not the same as the value in the Code Segment register.");
  }

  auto validate_segment_registers(context_switching::interrupt_descriptor_table::segment_selector data_segment,
                                  context_switching::interrupt_descriptor_table::segment_selector code_segment) -> void
  {
    validate_data_segment_registers(data_segment);
    validate_code_segment_register(code_segment);
  }

  [[gnu::naked]]
  auto set_code_segment_register(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 %%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"
                 : /* no output from call */
                 : [data_segment] "m"(data_segment), [code_segment] "m"(code_segment), [return_function] "r"(address)
                 : "rax");
  }

}  // namespace teachos::arch::kernel::cpu