aboutsummaryrefslogtreecommitdiff
path: root/arch/x86_64/src
diff options
context:
space:
mode:
authorMatteo Gmür <matteo.gmuer1@ost.ch>2025-05-02 14:49:06 +0000
committerMatteo Gmür <matteo.gmuer1@ost.ch>2025-05-02 14:49:06 +0000
commit099a7fbbc35a71f98553fa39899f2d17c555242f (patch)
treeb80f9dfef02d223e08af123bf5adff41a98bc2bc /arch/x86_64/src
parent7c045d8ded72017ff11fd4b9b02148987b944caf (diff)
downloadteachos-099a7fbbc35a71f98553fa39899f2d17c555242f.tar.xz
teachos-099a7fbbc35a71f98553fa39899f2d17c555242f.zip
Finish implementing 8-byte GDT entries and syscall arg loads.
Diffstat (limited to 'arch/x86_64/src')
-rw-r--r--arch/x86_64/src/context_switching/interrupt_descriptor_table/segment_selector.cpp2
-rw-r--r--arch/x86_64/src/context_switching/main.cpp58
-rw-r--r--arch/x86_64/src/context_switching/segment_descriptor_table/global_descriptor_table.cpp75
-rw-r--r--arch/x86_64/src/context_switching/segment_descriptor_table/global_descriptor_table_pointer.cpp2
-rw-r--r--arch/x86_64/src/context_switching/segment_descriptor_table/segment_descriptor.cpp46
-rw-r--r--arch/x86_64/src/context_switching/segment_descriptor_table/segment_descriptor_base.cpp35
-rw-r--r--arch/x86_64/src/context_switching/segment_descriptor_table/segment_descriptor_extension.cpp18
-rw-r--r--arch/x86_64/src/kernel/cpu/call.cpp4
-rw-r--r--arch/x86_64/src/kernel/cpu/tr.cpp8
9 files changed, 127 insertions, 121 deletions
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 0e29356..8568447 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
@@ -8,4 +8,6 @@ namespace teachos::arch::context_switching::interrupt_descriptor_table
}
auto segment_selector::operator|=(std::bitset<3U> other) -> void { _flags |= other.to_ulong(); }
+
+ segment_selector::operator uint16_t() const { return *reinterpret_cast<uint16_t const *>(this); }
} // 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 5901998..7449d84 100644
--- a/arch/x86_64/src/context_switching/main.cpp
+++ b/arch/x86_64/src/context_switching/main.cpp
@@ -31,14 +31,14 @@ namespace teachos::arch::context_switching
auto reload_gdtr() -> void
{
- // asm volatile("pushq $0x8\n\t" // Push new CS
- // "lea 1f(%%rip), %%rax\n\t" // Get address of label 1 into RAX
- // "pushq %%rax\n\t" // Push return address
- // "lretq\n" // Far return (loads CS:RIP)
- // "1:\n\t" // Label to return to
- // :
- // :
- // : "rax", "memory");
+ /*asm volatile("pushq $0x8\n\t" // Push new CS
+ "lea 1f(%%rip), %%rax\n\t" // Get address of label 1 into RAX
+ "pushq %%rax\n\t" // Push return address
+ "lretq\n" // Far return (loads CS:RIP)
+ "1:\n\t" // Label to return to
+ :
+ :
+ : "rax", "memory");*/
kernel::cpu::call(KERNEL_CODE_POINTER);
}
@@ -57,21 +57,33 @@ namespace teachos::arch::context_switching
// Actual Source: https://man7.org/linux/man-pages/man2/syscall.2.html More cleare documentation:
// https://sys.readthedocs.io/en/latest/doc/05_calling_system_calls.html
- uint64_t new_value = 60U;
+ uint64_t syscall_number = 60U;
asm volatile("mov %[input], %%rax"
: /* no output from call */
- : [input] "r"(new_value)
+ : [input] "r"(syscall_number)
: "memory");
asm volatile("syscall");
- video::vga::text::write("Successfully made a SYSCALL and returned with SYSRETQ!",
- video::vga::text::common_attributes::green_on_black);
+ // TODO: Reading RAX value does not work because the read itself changes the RAX value?!
+ // asm volatile("mov %%rax, %[output]" : [output] "=r"(syscall_value));
+
+ // TODO: Causes a general protection fault after the sysreturn?
+ // If removed instead it will cause a general protection fault after leaving this main method to return to kernel
+ // mode. But CS and SS are still configured for User mode.
+ /*video::vga::text::write("Successfully made a SYSCALL and returned with SYSRETQ!",
+ video::vga::text::common_attributes::green_on_black);*/
}
auto syscall_handler() -> void
{
- uint64_t syscall_number{};
+ uint64_t syscall_number, arg_0, arg_1, arg_2, arg_3, arg_4, arg_5 = {};
asm volatile("mov %%rax, %[output]" : [output] "=r"(syscall_number));
+ asm volatile("mov %%rdi, %[output]" : [output] "=r"(arg_0));
+ asm volatile("mov %%rsi, %[output]" : [output] "=r"(arg_1));
+ asm volatile("mov %%rdx, %[output]" : [output] "=r"(arg_2));
+ asm volatile("mov %%r10, %[output]" : [output] "=r"(arg_3));
+ asm volatile("mov %%r8, %[output]" : [output] "=r"(arg_4));
+ asm volatile("mov %%r9, %[output]" : [output] "=r"(arg_5));
switch (syscall_number)
{
@@ -85,7 +97,17 @@ namespace teachos::arch::context_switching
break;
}
- asm volatile("sysretq");
+ uint64_t result = 0U;
+ asm volatile("mov %[input], %%rax"
+ : /* no output from call */
+ : [input] "r"(result)
+ : "memory");
+
+ // Use SYSRET instead of SYSRETQ, because the latter would add 0x10 to bits [48:63] of IA32_STAR_ADDRESS for the
+ // Code Segment. But only add 0x8 to bits [48:63] of IA32_STAR_ADDRESS for the Stack Segment, which means either
+ // the Stack Segment or Code Segment is wrong. Whereas the former does not add 0x10 for the Code Segment and
+ // therefore fixes the aformentioned issue.
+ asm volatile("sysret");
}
auto enable_systemcall() -> void
@@ -94,9 +116,9 @@ namespace teachos::arch::context_switching
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;
- uint64_t user_cs = KERNEL_CODE_SEGMENT_SELECTOR;
- uint64_t star_value = (kernel_cs << 32) | (user_cs << 48);
+ uint64_t const kernel_cs = KERNEL_CODE_SEGMENT_SELECTOR;
+ uint64_t const user_cs = USER_CODE_SEGMENT_SELECTOR;
+ uint64_t const star_value = (kernel_cs << 32) | (user_cs << 48);
kernel::cpu::write_msr(IA32_STAR_ADDRESS, star_value);
kernel::cpu::set_efer_bit(kernel::cpu::efer_flags::SCE);
@@ -116,7 +138,7 @@ namespace teachos::arch::context_switching
interrupt_descriptor_table::update_interrupt_descriptor_table_register();
reload_gdtr();
- segment_descriptor_table::update_task_state_segment_register();
+ segment_descriptor_table::update_tss_register();
kernel::cpu::set_interrupt_flag();
initalized = true;
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 dd22603..d692e51 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
@@ -1,8 +1,6 @@
#include "arch/context_switching/segment_descriptor_table/global_descriptor_table.hpp"
-#include "arch/context_switching/segment_descriptor_table/segment_descriptor_base.hpp"
#include "arch/context_switching/segment_descriptor_table/segment_descriptor_extension.hpp"
-#include "arch/context_switching/segment_descriptor_table/task_state_segment.hpp"
#include "arch/exception_handling/assert.hpp"
#include "arch/kernel/cpu/gdtr.hpp"
#include "arch/kernel/cpu/tr.hpp"
@@ -12,7 +10,7 @@ namespace teachos::arch::context_switching::segment_descriptor_table
namespace
{
auto create_segment_descriptor(segment_descriptor_type segment_descriptor_type, access_byte access_level)
- -> segment_descriptor
+ -> segment_descriptor_base
{
uint64_t const base = 0x0;
std::bitset<20U> const limit{0xFFFFF};
@@ -29,57 +27,53 @@ namespace teachos::arch::context_switching::segment_descriptor_table
access_level |= access_byte::WRITABLE;
}
- segment_descriptor const segment_descriptor{access_level, flags, base, limit};
- return segment_descriptor;
+ segment_descriptor_base const segment_descriptor_base{access_level, flags, base, limit};
+ return segment_descriptor_base;
}
- auto create_tss_descriptor(task_state_segment * tss) -> segment_descriptor
+ auto create_tss_descriptor(task_state_segment * tss) -> segment_descriptor_extension
{
constexpr uint64_t TSS_LIMIT = sizeof(task_state_segment) - 1;
access_byte const tss_access_byte{access_byte::PRESENT | access_byte::DESCRIPTOR_LEVEL_KERNEL |
access_byte::TASK_STATE_SEGMENT_AVAILABLE};
gdt_flags const tss_gdt_flags{0U, TSS_LIMIT};
- segment_descriptor const tss_descriptor{tss_access_byte, tss_gdt_flags, reinterpret_cast<uint64_t>(tss),
- TSS_LIMIT};
+ segment_descriptor_extension const tss_descriptor{tss_access_byte, tss_gdt_flags, reinterpret_cast<uint64_t>(tss),
+ TSS_LIMIT};
return tss_descriptor;
}
- auto create_gdt() -> global_descriptor_table *
+ auto create_gdt() -> global_descriptor_table
{
- segment_descriptor_base const null_segment{segment_descriptor{0}};
- segment_descriptor_base const kernel_code_segment{
- create_segment_descriptor(segment_descriptor_type::CODE_SEGMENT, access_byte::DESCRIPTOR_LEVEL_KERNEL)};
- segment_descriptor_base const kernel_data_segment{
- create_segment_descriptor(segment_descriptor_type::DATA_SEGMENT, access_byte::DESCRIPTOR_LEVEL_KERNEL)};
- segment_descriptor_base const user_code_segment{
- create_segment_descriptor(segment_descriptor_type::CODE_SEGMENT, access_byte::DESCRIPTOR_LEVEL_USER)};
- segment_descriptor_base const user_data_segment{
- create_segment_descriptor(segment_descriptor_type::DATA_SEGMENT, access_byte::DESCRIPTOR_LEVEL_USER)};
+ segment_descriptor_base const null_segment{0};
+ segment_descriptor_base const kernel_code_segment =
+ create_segment_descriptor(segment_descriptor_type::CODE_SEGMENT, access_byte::DESCRIPTOR_LEVEL_KERNEL);
+ segment_descriptor_base const kernel_data_segment =
+ create_segment_descriptor(segment_descriptor_type::DATA_SEGMENT, access_byte::DESCRIPTOR_LEVEL_KERNEL);
+ segment_descriptor_base const user_code_segment =
+ create_segment_descriptor(segment_descriptor_type::CODE_SEGMENT, access_byte::DESCRIPTOR_LEVEL_USER);
+ segment_descriptor_base 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_tss_descriptor(tss);
- segment_descriptor_base const tss_descriptor_base{tss_descriptor};
- segment_descriptor_extension const tss_descriptor_extension{tss_descriptor};
-
- decltype(auto) gdt = new global_descriptor_table{};
- gdt->null = null_segment;
- gdt->kernel_code = kernel_code_segment;
- gdt->kernel_data = kernel_data_segment;
- gdt->user_code = user_code_segment;
- gdt->user_data = user_data_segment;
- gdt->tss_low = tss_descriptor_base;
- gdt->tss_high = tss_descriptor_extension;
-
- return gdt;
- };
+ segment_descriptor_extension const tss_descriptor = create_tss_descriptor(tss);
+
+ global_descriptor_table global_descriptor_table{null_segment,
+ kernel_code_segment,
+ kernel_data_segment,
+ user_code_segment,
+ user_data_segment,
+ tss_descriptor.get_first_gdt_entry(),
+ tss_descriptor.get_second_gdt_entry()};
+ return global_descriptor_table;
+ }
} // namespace
auto get_or_create_gdt() -> global_descriptor_table &
{
// Global Descriptor Table needs to be kept alive
- static global_descriptor_table * gdt = create_gdt();
- return *gdt;
+ static global_descriptor_table gdt = create_gdt();
+ return gdt;
}
auto update_gdtr() -> void
@@ -88,7 +82,8 @@ 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>((7 * sizeof(seg_desc)) - 1), &gdt.null};
+ uint16_t gdt_size = static_cast<uint16_t>((gdt.size() * sizeof(segment_descriptor_base)) - 1);
+ global_descriptor_table_pointer gdt_pointer{gdt_size, gdt.data()};
kernel::cpu::load_global_descriptor_table(gdt_pointer);
auto const stored_gdt_pointer = kernel::cpu::store_global_descriptor_table();
@@ -97,13 +92,13 @@ namespace teachos::arch::context_switching::segment_descriptor_table
"[Global Descriptor Table] Loaded GDTR value is not the same as the stored value.");
}
- auto update_task_state_segment_register() -> void
+ auto update_tss_register() -> void
{
- get_or_create_gdt();
+ decltype(auto) gdt = get_or_create_gdt();
// 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 = (7 - 1) * sizeof(segment_descriptor_table::segment_descriptor);
+ // offset in bytes to the start of the segment descriptor (5 * 8) = 40
+ uint16_t tss_selector = (gdt.size() * sizeof(segment_descriptor_base)) - sizeof(segment_descriptor_extension);
kernel::cpu::load_task_register(tss_selector);
auto const stored_task_register = kernel::cpu::store_task_register();
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 b7d2da4..79088b8 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
@@ -2,7 +2,7 @@
namespace teachos::arch::context_switching::segment_descriptor_table
{
- global_descriptor_table_pointer::global_descriptor_table_pointer(uint16_t table_length, seg_desc * address)
+ global_descriptor_table_pointer::global_descriptor_table_pointer(uint16_t table_length, uint64_t * address)
: table_length(table_length)
, address(address)
{
diff --git a/arch/x86_64/src/context_switching/segment_descriptor_table/segment_descriptor.cpp b/arch/x86_64/src/context_switching/segment_descriptor_table/segment_descriptor.cpp
deleted file mode 100644
index e74156b..0000000
--- a/arch/x86_64/src/context_switching/segment_descriptor_table/segment_descriptor.cpp
+++ /dev/null
@@ -1,46 +0,0 @@
-#include "arch/context_switching/segment_descriptor_table/segment_descriptor.hpp"
-
-namespace teachos::arch::context_switching::segment_descriptor_table
-{
- segment_descriptor::segment_descriptor(uint128_t flags)
- : _limit_1(flags)
- , _base_1(flags >> 16U)
- , _access(flags >> 40U)
- , _flag(flags >> 52U, flags >> 48U)
- , _base_2(flags >> 56U)
- {
- // Nothing to do.
- }
-
- segment_descriptor::segment_descriptor(access_byte access_byte, gdt_flags flags, uint64_t base,
- std::bitset<20U> limit)
- : _limit_1(limit.to_ulong())
- , _base_1(base)
- , _access(access_byte)
- , _flag(flags)
- , _base_2(base >> 24U)
- {
- // Nothing to do
- }
-
- auto segment_descriptor::get_segment_type() const -> segment_descriptor_type
- {
- if (!_access.contains_flags(access_byte::CODE_OR_DATA_SEGMENT))
- {
- return segment_descriptor_type::SYSTEM_SEGMENT;
- }
- return _access.contains_flags(access_byte::CODE_SEGMENT) ? segment_descriptor_type::CODE_SEGMENT
- : segment_descriptor_type::DATA_SEGMENT;
- }
-
- auto segment_descriptor::get_limit_1() -> uint16_t { return _limit_1; }
-
- auto segment_descriptor::get_base_1() -> uint32_t { return _base_1; }
-
- auto segment_descriptor::get_access() -> access_byte { return _access; }
-
- auto segment_descriptor::get_flag() -> gdt_flags { return _flag; }
-
- auto segment_descriptor::get_base_2() -> uint64_t { return _base_2; }
-
-} // namespace teachos::arch::context_switching::segment_descriptor_table
diff --git a/arch/x86_64/src/context_switching/segment_descriptor_table/segment_descriptor_base.cpp b/arch/x86_64/src/context_switching/segment_descriptor_table/segment_descriptor_base.cpp
index f82dbb9..04804d9 100644
--- a/arch/x86_64/src/context_switching/segment_descriptor_table/segment_descriptor_base.cpp
+++ b/arch/x86_64/src/context_switching/segment_descriptor_table/segment_descriptor_base.cpp
@@ -2,12 +2,37 @@
namespace teachos::arch::context_switching::segment_descriptor_table
{
- segment_descriptor_base::segment_descriptor_base(segment_descriptor segment_descriptor)
- : _limit_1(segment_descriptor.get_limit_1())
- , _base_1(segment_descriptor.get_base_1())
- , _access(segment_descriptor.get_access())
- , _flag(segment_descriptor.get_flag())
+ segment_descriptor_base::segment_descriptor_base(uint64_t flags)
+ : _limit_1(flags)
+ , _base_1(flags >> 16U)
+ , _access(flags >> 40U)
+ , _flag(flags >> 52U, flags >> 48U)
+ , _base_2(flags >> 56U)
+ {
+ // Nothing to do.
+ }
+
+ segment_descriptor_base::segment_descriptor_base(access_byte access_byte, gdt_flags flags, uint32_t base,
+ std::bitset<20U> limit)
+ : _limit_1(limit.to_ulong())
+ , _base_1(base)
+ , _access(access_byte)
+ , _flag(flags)
+ , _base_2(base >> 24U)
{
// Nothing to do
}
+
+ auto segment_descriptor_base::get_segment_type() const -> segment_descriptor_type
+ {
+ if (!_access.contains_flags(access_byte::CODE_OR_DATA_SEGMENT))
+ {
+ return segment_descriptor_type::SYSTEM_SEGMENT;
+ }
+ return _access.contains_flags(access_byte::CODE_SEGMENT) ? segment_descriptor_type::CODE_SEGMENT
+ : segment_descriptor_type::DATA_SEGMENT;
+ }
+
+ segment_descriptor_base::operator uint64_t() const { return *reinterpret_cast<uint64_t const *>(this); }
+
} // namespace teachos::arch::context_switching::segment_descriptor_table
diff --git a/arch/x86_64/src/context_switching/segment_descriptor_table/segment_descriptor_extension.cpp b/arch/x86_64/src/context_switching/segment_descriptor_table/segment_descriptor_extension.cpp
index b2649d0..a28ec9b 100644
--- a/arch/x86_64/src/context_switching/segment_descriptor_table/segment_descriptor_extension.cpp
+++ b/arch/x86_64/src/context_switching/segment_descriptor_table/segment_descriptor_extension.cpp
@@ -2,9 +2,23 @@
namespace teachos::arch::context_switching::segment_descriptor_table
{
- segment_descriptor_extension::segment_descriptor_extension(segment_descriptor segment_descriptor)
- : _base_2(segment_descriptor.get_base_2())
+ segment_descriptor_extension::segment_descriptor_extension(uint128_t flags)
+ : _base(flags)
+ , _base_3(flags >> 64U)
+ {
+ // Nothing to do.
+ }
+
+ segment_descriptor_extension::segment_descriptor_extension(access_byte access_byte, gdt_flags flags, uint64_t base,
+ std::bitset<20U> limit)
+ : _base(access_byte, flags, base, limit)
+ , _base_3(base >> 32U)
{
// Nothing to do
}
+
+ auto segment_descriptor_extension::get_first_gdt_entry() const -> segment_descriptor_base { return _base; }
+
+ auto segment_descriptor_extension::get_second_gdt_entry() const -> uint64_t { return _base_3; }
+
} // namespace teachos::arch::context_switching::segment_descriptor_table
diff --git a/arch/x86_64/src/kernel/cpu/call.cpp b/arch/x86_64/src/kernel/cpu/call.cpp
index 932d248..6564b76 100644
--- a/arch/x86_64/src/kernel/cpu/call.cpp
+++ b/arch/x86_64/src/kernel/cpu/call.cpp
@@ -4,7 +4,7 @@ namespace teachos::arch::kernel::cpu
{
auto call(far_pointer pointer) -> void
{
- // asm volatile("rex64 lcall *%[input]" : /* no output from call */ : [input] "m"(pointer));
- asm volatile("ljmp *%0" : : "m"(pointer));
+ asm volatile("rex64 lcall *%[input]" : /* no output from call */ : [input] "m"(pointer));
+ // asm volatile("ljmp *%0" : : "m"(pointer));
}
} // namespace teachos::arch::kernel::cpu
diff --git a/arch/x86_64/src/kernel/cpu/tr.cpp b/arch/x86_64/src/kernel/cpu/tr.cpp
index e281189..a435540 100644
--- a/arch/x86_64/src/kernel/cpu/tr.cpp
+++ b/arch/x86_64/src/kernel/cpu/tr.cpp
@@ -11,12 +11,6 @@ namespace teachos::arch::kernel::cpu
auto load_task_register(uint16_t gdt_offset) -> void
{
- // asm volatile("ltr %[input]" : /* no output from call */ : [input] "R"(gdt_offset));
- // https://www.scs.stanford.edu/05au-cs240c/lab/i386/s07_03.htm
- asm volatile("mov %[input], %%ax\n"
- "ltr %%ax\n"
- : /* no output from call */
- : [input] "r"(gdt_offset)
- : "ax");
+ asm volatile("ltr %[input]" : /* no output from call */ : [input] "m"(gdt_offset));
}
} // namespace teachos::arch::kernel::cpu