aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorFelix Morgner <felix.morgner@gmail.com>2025-03-28 18:35:28 +0100
committerFelix Morgner <felix.morgner@gmail.com>2025-03-28 18:39:15 +0100
commitfbd1ebe4f7c5985554fdca7c7fc05de15d47dd3a (patch)
treeaa693a6f6edc717a1f3e184141d0ee1c150c57d7
parent91c37142dbed40e42fd1a27a2755a79b8ccc329c (diff)
downloadteachos-fbd1ebe4f7c5985554fdca7c7fc05de15d47dd3a.tar.xz
teachos-fbd1ebe4f7c5985554fdca7c7fc05de15d47dd3a.zip
gdt: fix reload of GDT
The core problems were/are the following: - The flags of the segments were not entirely correct. Please recheck them against the spec! - The GDT pointer did not contain the address of the first (null) GTD entry, but the address of the stl::vector containing the GDT entries. - The far pointer must consist of: - the address to jump to - the byte index into the GDT for the desired segement descriptor to be loaded into CS. - The type of the "dummy" function we jump to was wrong (it's a function, we should declare it as such). - We cannot enable interrupts right now, since we die with a triple fault. This is caused by some initia fault which seems to lead to a general protection fault, which then triple faults since we cannot find the IDT. Some FIXMEs have been added to the code. Please look at them carefully and compare things against the specs.
-rw-r--r--arch/x86_64/include/arch/boot/pointers.hpp2
-rw-r--r--arch/x86_64/include/arch/context_switching/segment_descriptor_table/gdt_flags.hpp17
-rw-r--r--arch/x86_64/include/arch/context_switching/segment_descriptor_table/global_descriptor_table_pointer.hpp4
-rw-r--r--arch/x86_64/include/arch/kernel/cpu/jmp.hpp7
-rw-r--r--arch/x86_64/src/boot/boot.s3
-rw-r--r--arch/x86_64/src/context_switching/main.cpp25
-rw-r--r--arch/x86_64/src/context_switching/segment_descriptor_table/global_descriptor_table.cpp8
-rw-r--r--arch/x86_64/src/context_switching/segment_descriptor_table/global_descriptor_table_pointer.cpp2
8 files changed, 39 insertions, 29 deletions
diff --git a/arch/x86_64/include/arch/boot/pointers.hpp b/arch/x86_64/include/arch/boot/pointers.hpp
index c08de52..9bf5cfd 100644
--- a/arch/x86_64/include/arch/boot/pointers.hpp
+++ b/arch/x86_64/include/arch/boot/pointers.hpp
@@ -13,7 +13,7 @@ namespace teachos::arch::boot
/**
* @brief Address pointing to the method that clears all segment registers.
*/
- extern "C" size_t reload_segment_register;
+ extern "C" void reload_segment_register();
} // namespace teachos::arch::boot
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 fdf0044..764aec5 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,24 @@ namespace teachos::arch::context_switching::segment_descriptor_table
*/
enum bitset : uint8_t
{
- LENGTH = 1U << 0U, ///< 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 bis
- ///< is set the DEFAULT_LENGTH bis needs to be 0
+ 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 bis
+ ///< is set the DEFAULT_LENGTH bis needs to be 0
+
+ // FIXME: Where does this come from, and is this value correct?
UPPER_BOUND = 1U << 1U, ///< Specifies the upper bound of the segment for expand down data segment. Enable for 5
///< GiB, 4 KiB otherwise.
+
+ // FIXME: Where does this come from, and is this value correct?
STACK_POINTER_SIZE = 1U << 1U, ///< Specifies the size of the Stack Pointer (SP) for stack segments used for
///< implicit stack operations. Enable for 32 bit, 16 bit otherwise.
+
+ // FIXME: Where does this come from, and is this value correct?
DEFAULT_LENGTH = 1U << 1U, ///< Indicates the default length for code segments with effective addresses and
///< operands. Enable for 32 bit, 16 bit otherwise.
- GRANULARITY = 1U << 2U, ///< Indicates the size the Limit value in the segment descriptor is scaled by 1 Byte
+
+ 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_pointer.hpp b/arch/x86_64/include/arch/context_switching/segment_descriptor_table/global_descriptor_table_pointer.hpp
index 13d6f53..6fac2a0 100644
--- a/arch/x86_64/include/arch/context_switching/segment_descriptor_table/global_descriptor_table_pointer.hpp
+++ b/arch/x86_64/include/arch/context_switching/segment_descriptor_table/global_descriptor_table_pointer.hpp
@@ -26,7 +26,7 @@ namespace teachos::arch::context_switching::segment_descriptor_table
/**
* @brief Constructor.
*/
- global_descriptor_table_pointer(uint16_t table_length, global_descriptor_table * address);
+ global_descriptor_table_pointer(uint16_t table_length, segment_descriptor * address);
/**
* @brief Defaulted three-way comparsion operator.
@@ -35,7 +35,7 @@ namespace teachos::arch::context_switching::segment_descriptor_table
private:
uint16_t table_length = {}; ///< The amount of segment descriptor entries in the global descriptor table - 1.
- global_descriptor_table * address = {}; ///< Non-owning pointer to the GDT base address.
+ segment_descriptor * address = {}; ///< Non-owning pointer to the GDT base address.
};
} // 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 163fac6..0ed38e9 100644
--- a/arch/x86_64/include/arch/kernel/cpu/jmp.hpp
+++ b/arch/x86_64/include/arch/kernel/cpu/jmp.hpp
@@ -10,11 +10,10 @@ namespace teachos::arch::kernel::cpu
/**
* @brief Far jump - A jump to an instruction located in a different segment.
*/
- struct [[gnu::packed]] far_pointer
+ struct far_pointer
{
- std::size_t function; ///< Address of the function we want to jump too. (0- 63)
- context_switching::interrupt_descriptor_table::segment_selector
- selector; ///< Segment selector that shows the segment we want to jump into. (64 - 79)
+ 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)
};
/**
diff --git a/arch/x86_64/src/boot/boot.s b/arch/x86_64/src/boot/boot.s
index bf150a3..108dbe5 100644
--- a/arch/x86_64/src/boot/boot.s
+++ b/arch/x86_64/src/boot/boot.s
@@ -356,6 +356,9 @@ prepare_page_maps:
.global reload_segment_register
reload_segment_register:
+ // FIXME: maybe we should set the actually correct values here. We'd need to communicate them down from C++.
+ // Alternatively, we could probably implement this as a [[gnu::naked]] function in C++, to have easier access to
+ // arguments and symbols. Maybe later.
xor %rax, %rax
mov %rax, %ss
mov %rax, %ds
diff --git a/arch/x86_64/src/context_switching/main.cpp b/arch/x86_64/src/context_switching/main.cpp
index 5d19f23..a5bd3fb 100644
--- a/arch/x86_64/src/context_switching/main.cpp
+++ b/arch/x86_64/src/context_switching/main.cpp
@@ -13,22 +13,21 @@ namespace teachos::arch::context_switching
decltype(auto) global_descriptor_table = segment_descriptor_table::initialize_global_descriptor_table();
decltype(auto) interrupt_descriptor_table = interrupt_descriptor_table::initialize_interrupt_descriptor_table();
- interrupt_descriptor_table::segment_selector segment_selector{
- 1U, interrupt_descriptor_table::segment_selector::REQUEST_LEVEL_KERNEL};
- kernel::cpu::far_pointer pointer{boot::reload_segment_register, segment_selector};
- kernel::cpu::jmp(pointer);
+ 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));
- // 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);
+ // // 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);
- 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.");
+ // 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.");
- kernel::cpu::set_interrupt_flag();
+ // FIXME: We currently cannot enable interrupts, since for some reason, we will later run into what looks like a GP. Maybe because no IDT is loaded? Maybe our boot code segment is not set up correctly?
+ // kernel::cpu::set_interrupt_flag();
descriptor_tables tables = {global_descriptor_table, interrupt_descriptor_table};
return tables;
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 e6a489c..37ee778 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
@@ -20,12 +20,14 @@ namespace teachos::arch::context_switching::segment_descriptor_table
uint8_t gdt_flags_bits = gdt_flags::GRANULARITY;
if (segment_descriptor_type == segment_descriptor_type::CODE_SEGMENT)
{
- gdt_flags_bits |= gdt_flags::LENGTH;
+ gdt_flags_bits |= gdt_flags::LONG_MODE;
access_level_bits |= access_byte::CODE_SEGMENT | access_byte::READABLE;
}
else if (segment_descriptor_type == segment_descriptor_type::DATA_SEGMENT)
{
- gdt_flags_bits |= gdt_flags::UPPER_BOUND;
+ gdt_flags_bits |= 1 << 2;
+ // FIXME: Look at those bit flags, something seems off.
+ // gdt_flags_bits |= gdt_flags::UPPER_BOUND;
access_level_bits |= access_byte::WRITABLE;
}
@@ -78,7 +80,7 @@ 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>((gdt.size() * sizeof(segment_descriptor)) - 1),
- &gdt};
+ gdt.data()};
kernel::cpu::load_global_descriptor_table(gdt_pointer);
auto const stored_gdt_pointer = kernel::cpu::store_global_descriptor_table();
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 132565f..a4a5de8 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
@@ -3,7 +3,7 @@
namespace teachos::arch::context_switching::segment_descriptor_table
{
global_descriptor_table_pointer::global_descriptor_table_pointer(uint16_t table_length,
- global_descriptor_table * address)
+ segment_descriptor * address)
: table_length(table_length)
, address(address)
{