aboutsummaryrefslogtreecommitdiff
path: root/arch
diff options
context:
space:
mode:
authorMatteo Gmür <matteo.gmuer1@ost.ch>2025-06-06 17:15:32 +0200
committerMatteo Gmür <matteo.gmuer1@ost.ch>2025-06-06 17:15:32 +0200
commitc4ced070ab057e4be6552b2f10ec1bf35509e245 (patch)
tree91602a7732d216bff3fbaf2d6158e965460019e5 /arch
parent3fb836101a2032e93f7b82c924ce208d7377a5ea (diff)
parent1031a69ca5e23f2087148ad57e57506735872617 (diff)
downloadkernel-c4ced070ab057e4be6552b2f10ec1bf35509e245.tar.xz
kernel-c4ced070ab057e4be6552b2f10ec1bf35509e245.zip
Merge branch 'feat_inital_context_switching' into 'develop_ba'
Implement Context Switching See merge request teachos/kernel!6
Diffstat (limited to 'arch')
-rw-r--r--arch/x86_64/CMakeLists.txt58
-rw-r--r--arch/x86_64/include/arch/context_switching/interrupt_descriptor_table/gate_descriptor.hpp69
-rw-r--r--arch/x86_64/include/arch/context_switching/interrupt_descriptor_table/idt_flags.hpp81
-rw-r--r--arch/x86_64/include/arch/context_switching/interrupt_descriptor_table/interrupt_descriptor_table.hpp24
-rw-r--r--arch/x86_64/include/arch/context_switching/interrupt_descriptor_table/interrupt_descriptor_table_pointer.hpp40
-rw-r--r--arch/x86_64/include/arch/context_switching/interrupt_descriptor_table/ist_offset.hpp45
-rw-r--r--arch/x86_64/include/arch/context_switching/interrupt_descriptor_table/segment_selector.hpp105
-rw-r--r--arch/x86_64/include/arch/context_switching/main.hpp51
-rw-r--r--arch/x86_64/include/arch/context_switching/segment_descriptor_table/access_byte.hpp104
-rw-r--r--arch/x86_64/include/arch/context_switching/segment_descriptor_table/gdt_flags.hpp93
-rw-r--r--arch/x86_64/include/arch/context_switching/segment_descriptor_table/global_descriptor_table.hpp37
-rw-r--r--arch/x86_64/include/arch/context_switching/segment_descriptor_table/global_descriptor_table_pointer.hpp41
-rw-r--r--arch/x86_64/include/arch/context_switching/segment_descriptor_table/segment_descriptor_base.hpp73
-rw-r--r--arch/x86_64/include/arch/context_switching/segment_descriptor_table/segment_descriptor_extension.hpp74
-rw-r--r--arch/x86_64/include/arch/context_switching/segment_descriptor_table/segment_descriptor_type.hpp27
-rw-r--r--arch/x86_64/include/arch/context_switching/segment_descriptor_table/task_state_segment.hpp32
-rw-r--r--arch/x86_64/include/arch/context_switching/syscall/main.hpp88
-rw-r--r--arch/x86_64/include/arch/context_switching/syscall/syscall_enable.hpp18
-rw-r--r--arch/x86_64/include/arch/context_switching/syscall/syscall_handler.hpp18
-rw-r--r--arch/x86_64/include/arch/interrupt_handling/generic_interrupt_handler.hpp34
-rw-r--r--arch/x86_64/include/arch/kernel/cpu/call.hpp30
-rw-r--r--arch/x86_64/include/arch/kernel/cpu/control_register.hpp (renamed from arch/x86_64/include/arch/memory/cpu/control_register.hpp)10
-rw-r--r--arch/x86_64/include/arch/kernel/cpu/gdtr.hpp27
-rw-r--r--arch/x86_64/include/arch/kernel/cpu/idtr.hpp27
-rw-r--r--arch/x86_64/include/arch/kernel/cpu/if.hpp21
-rw-r--r--arch/x86_64/include/arch/kernel/cpu/msr.hpp (renamed from arch/x86_64/include/arch/memory/cpu/msr.hpp)10
-rw-r--r--arch/x86_64/include/arch/kernel/cpu/segment_register.hpp97
-rw-r--r--arch/x86_64/include/arch/kernel/cpu/tlb.hpp (renamed from arch/x86_64/include/arch/memory/cpu/tlb.hpp)12
-rw-r--r--arch/x86_64/include/arch/kernel/cpu/tr.hpp24
-rw-r--r--arch/x86_64/include/arch/memory/allocator/area_frame_allocator.hpp6
-rw-r--r--arch/x86_64/include/arch/memory/allocator/physical_frame.hpp8
-rw-r--r--arch/x86_64/include/arch/memory/heap/global_heap_allocator.hpp50
-rw-r--r--arch/x86_64/include/arch/memory/heap/heap_allocator.hpp6
-rw-r--r--arch/x86_64/include/arch/memory/heap/linked_list_allocator.hpp11
-rw-r--r--arch/x86_64/include/arch/memory/heap/memory_block.hpp2
-rw-r--r--arch/x86_64/include/arch/memory/heap/user_heap_allocator.hpp148
-rw-r--r--arch/x86_64/include/arch/memory/main.hpp14
-rw-r--r--arch/x86_64/include/arch/memory/multiboot/elf_symbols_section.hpp8
-rw-r--r--arch/x86_64/include/arch/memory/multiboot/memory_map.hpp6
-rw-r--r--arch/x86_64/include/arch/memory/paging/active_page_table.hpp10
-rw-r--r--arch/x86_64/include/arch/memory/paging/kernel_mapper.hpp45
-rw-r--r--arch/x86_64/include/arch/memory/paging/page_entry.hpp8
-rw-r--r--arch/x86_64/include/arch/memory/paging/page_table.hpp11
-rw-r--r--arch/x86_64/include/arch/memory/paging/virtual_page.hpp4
-rw-r--r--arch/x86_64/include/arch/stl/container.hpp (renamed from arch/x86_64/include/arch/shared/container.hpp)34
-rw-r--r--arch/x86_64/include/arch/stl/contiguous_pointer_iterator.hpp (renamed from arch/x86_64/include/arch/shared/contiguous_pointer_iterator.hpp)44
-rw-r--r--arch/x86_64/include/arch/stl/forward_value_iterator.hpp (renamed from arch/x86_64/include/arch/shared/forward_value_iterator.hpp)25
-rw-r--r--arch/x86_64/include/arch/stl/mutex.hpp (renamed from arch/x86_64/include/arch/shared/mutex.hpp)13
-rw-r--r--arch/x86_64/include/arch/stl/shared_pointer.hpp191
-rw-r--r--arch/x86_64/include/arch/stl/stack.hpp212
-rw-r--r--arch/x86_64/include/arch/stl/unique_pointer.hpp158
-rw-r--r--arch/x86_64/include/arch/stl/vector.hpp604
-rw-r--r--arch/x86_64/include/arch/user/main.hpp16
-rw-r--r--arch/x86_64/scripts/kernel.ld18
-rw-r--r--arch/x86_64/src/boot/boot.s4
-rw-r--r--arch/x86_64/src/context_switching/interrupt_descriptor_table/gate_descriptor.cpp24
-rw-r--r--arch/x86_64/src/context_switching/interrupt_descriptor_table/idt_flags.cpp17
-rw-r--r--arch/x86_64/src/context_switching/interrupt_descriptor_table/interrupt_descriptor_table.cpp53
-rw-r--r--arch/x86_64/src/context_switching/interrupt_descriptor_table/interrupt_descriptor_table_pointer.cpp13
-rw-r--r--arch/x86_64/src/context_switching/interrupt_descriptor_table/ist_offset.cpp10
-rw-r--r--arch/x86_64/src/context_switching/interrupt_descriptor_table/segment_selector.cpp15
-rw-r--r--arch/x86_64/src/context_switching/main.cpp63
-rw-r--r--arch/x86_64/src/context_switching/segment_descriptor_table/access_byte.cpp17
-rw-r--r--arch/x86_64/src/context_switching/segment_descriptor_table/gdt_flags.cpp20
-rw-r--r--arch/x86_64/src/context_switching/segment_descriptor_table/global_descriptor_table.cpp109
-rw-r--r--arch/x86_64/src/context_switching/segment_descriptor_table/global_descriptor_table_pointer.cpp11
-rw-r--r--arch/x86_64/src/context_switching/segment_descriptor_table/segment_descriptor_base.cpp38
-rw-r--r--arch/x86_64/src/context_switching/segment_descriptor_table/segment_descriptor_extension.cpp24
-rw-r--r--arch/x86_64/src/context_switching/syscall/main.cpp35
-rw-r--r--arch/x86_64/src/context_switching/syscall/syscall_enable.cpp32
-rw-r--r--arch/x86_64/src/context_switching/syscall/syscall_handler.cpp118
-rw-r--r--arch/x86_64/src/interrupt_handling/generic_interrupt_handler.cpp13
-rw-r--r--arch/x86_64/src/kernel/cpu/call.cpp9
-rw-r--r--arch/x86_64/src/kernel/cpu/control_register.cpp (renamed from arch/x86_64/src/memory/cpu/control_register.cpp)12
-rw-r--r--arch/x86_64/src/kernel/cpu/gdtr.cpp19
-rw-r--r--arch/x86_64/src/kernel/cpu/idtr.cpp18
-rw-r--r--arch/x86_64/src/kernel/cpu/if.cpp7
-rw-r--r--arch/x86_64/src/kernel/cpu/msr.cpp (renamed from arch/x86_64/src/memory/cpu/msr.cpp)8
-rw-r--r--arch/x86_64/src/kernel/cpu/segment_register.cpp98
-rw-r--r--arch/x86_64/src/kernel/cpu/tlb.cpp (renamed from arch/x86_64/src/memory/cpu/tlb.cpp)10
-rw-r--r--arch/x86_64/src/kernel/cpu/tr.cpp16
-rw-r--r--arch/x86_64/src/kernel/main.cpp12
-rw-r--r--arch/x86_64/src/memory/allocator/area_frame_allocator.cpp2
-rw-r--r--arch/x86_64/src/memory/heap/bump_allocator.cpp4
-rw-r--r--arch/x86_64/src/memory/heap/global_heap_allocator.cpp95
-rw-r--r--arch/x86_64/src/memory/heap/linked_list_allocator.cpp6
-rw-r--r--arch/x86_64/src/memory/heap/memory_block.cpp4
-rw-r--r--arch/x86_64/src/memory/heap/user_heap_allocator.cpp200
-rw-r--r--arch/x86_64/src/memory/main.cpp64
-rw-r--r--arch/x86_64/src/memory/paging/page_entry.cpp5
-rw-r--r--arch/x86_64/src/memory/paging/temporary_page.cpp8
-rw-r--r--arch/x86_64/src/stl/mutex.cpp (renamed from arch/x86_64/src/shared/mutex.cpp)6
-rw-r--r--arch/x86_64/src/user/main.cpp35
93 files changed, 3965 insertions, 311 deletions
diff --git a/arch/x86_64/CMakeLists.txt b/arch/x86_64/CMakeLists.txt
index e9f8d5f..57b3a60 100644
--- a/arch/x86_64/CMakeLists.txt
+++ b/arch/x86_64/CMakeLists.txt
@@ -7,6 +7,15 @@ mark_as_advanced(TEACHOS_KERNEL_LINKER_SCRIPT)
target_sources("_kernel" PRIVATE
"src/kernel/main.cpp"
+ "src/kernel/cpu/control_register.cpp"
+ "src/kernel/cpu/gdtr.cpp"
+ "src/kernel/cpu/idtr.cpp"
+ "src/kernel/cpu/if.cpp"
+ "src/kernel/cpu/call.cpp"
+ "src/kernel/cpu/msr.cpp"
+ "src/kernel/cpu/segment_register.cpp"
+ "src/kernel/cpu/tlb.cpp"
+ "src/kernel/cpu/tr.cpp"
)
target_link_options("_kernel" PRIVATE
@@ -53,21 +62,19 @@ target_sources("_memory" PRIVATE
"src/memory/paging/virtual_page.cpp"
"src/memory/paging/active_page_table.cpp"
"src/memory/paging/inactive_page_table.cpp"
- "src/memory/cpu/tlb.cpp"
- "src/memory/cpu/control_register.cpp"
- "src/memory/cpu/msr.cpp"
"src/memory/heap/bump_allocator.cpp"
+ "src/memory/heap/user_heap_allocator.cpp"
"src/memory/heap/memory_block.cpp"
"src/memory/heap/linked_list_allocator.cpp"
"src/memory/heap/global_heap_allocator.cpp"
)
#[============================================================================[
-# The Shared Library
+# The STL Library
#]============================================================================]
-target_sources("_shared" PRIVATE
- "src/shared/mutex.cpp"
+target_sources("_stl" PRIVATE
+ "src/stl/mutex.cpp"
)
#[============================================================================[
@@ -82,6 +89,45 @@ target_sources("_exception" PRIVATE
)
#[============================================================================[
+# The Context switching Library
+#]============================================================================]
+
+target_sources("_context" PRIVATE
+ "src/context_switching/segment_descriptor_table/access_byte.cpp"
+ "src/context_switching/segment_descriptor_table/gdt_flags.cpp"
+ "src/context_switching/segment_descriptor_table/global_descriptor_table_pointer.cpp"
+ "src/context_switching/segment_descriptor_table/global_descriptor_table.cpp"
+ "src/context_switching/segment_descriptor_table/segment_descriptor_base.cpp"
+ "src/context_switching/segment_descriptor_table/segment_descriptor_extension.cpp"
+ "src/context_switching/main.cpp"
+ "src/context_switching/syscall/main.cpp"
+ "src/context_switching/syscall/syscall_enable.cpp"
+ "src/context_switching/syscall/syscall_handler.cpp"
+ "src/context_switching/interrupt_descriptor_table/gate_descriptor.cpp"
+ "src/context_switching/interrupt_descriptor_table/idt_flags.cpp"
+ "src/context_switching/interrupt_descriptor_table/interrupt_descriptor_table_pointer.cpp"
+ "src/context_switching/interrupt_descriptor_table/interrupt_descriptor_table.cpp"
+ "src/context_switching/interrupt_descriptor_table/ist_offset.cpp"
+ "src/context_switching/interrupt_descriptor_table/segment_selector.cpp"
+)
+
+#[============================================================================[
+# The Interrupt Handlers
+#]============================================================================]
+
+target_sources("_interrupt_handling" PRIVATE
+ "src/interrupt_handling/generic_interrupt_handler.cpp"
+)
+
+#[============================================================================[
+# The User code
+#]============================================================================]
+
+target_sources("_context" PRIVATE
+ "src/user/main.cpp"
+)
+
+#[============================================================================[
# The Bootable ISO Image
#]============================================================================]
diff --git a/arch/x86_64/include/arch/context_switching/interrupt_descriptor_table/gate_descriptor.hpp b/arch/x86_64/include/arch/context_switching/interrupt_descriptor_table/gate_descriptor.hpp
new file mode 100644
index 0000000..07110c8
--- /dev/null
+++ b/arch/x86_64/include/arch/context_switching/interrupt_descriptor_table/gate_descriptor.hpp
@@ -0,0 +1,69 @@
+#ifndef TEACHOS_ARCH_X86_64_CONTEXT_SWITCHING_INTERRUPT_DESCRIPTOR_TABLE_GATE_DESCRIPTOR_HPP
+#define TEACHOS_ARCH_X86_64_CONTEXT_SWITCHING_INTERRUPT_DESCRIPTOR_TABLE_GATE_DESCRIPTOR_HPP
+
+#include "arch/context_switching/interrupt_descriptor_table/idt_flags.hpp"
+#include "arch/context_switching/interrupt_descriptor_table/ist_offset.hpp"
+#include "arch/context_switching/interrupt_descriptor_table/segment_selector.hpp"
+
+#include <bitset>
+#include <cstdint>
+
+namespace teachos::arch::context_switching::interrupt_descriptor_table
+{
+ __extension__ typedef __int128 int128_t;
+ __extension__ typedef unsigned __int128 uint128_t;
+
+ /**
+ * @brief Defines helper function for all states and the actual data the gate descriptor can have.
+ */
+ struct [[gnu::packed]] gate_descriptor
+ {
+ /**
+ * @brief Default Constructor.
+ */
+ gate_descriptor() = default;
+
+ /**
+ * @brief Constructor.
+ *
+ * @note Created gate descriptor copies the given bytes into these components ending with a 32 bit reserved
+ * field that has to be used, because the 64-bit gate descriptor needs to be big enough for two 32-bit gate
+ * descriptor.
+ * - 16 bit Segment Selector
+ * - 3 bit Interrupt Stack Table Offset
+ * - 8 bit Type and Flags
+ * - 64 bit Offset
+ *
+ * @param flags Copies the bits set from the given data into the individual components of a gate
+ * descriptor.
+ */
+ explicit gate_descriptor(uint128_t flags);
+
+ /**
+ * @brief Constructor.
+ *
+ * @param selector, ist, flags, offset Copies the bits set from the given data into the individual components of
+ * a gate descriptor.
+ */
+ gate_descriptor(segment_selector selector, ist_offset ist, idt_flags flags, uint64_t offset);
+
+ /**
+ * @brief Allows to compare the underlying bits of two instances.
+ *
+ * @param other Other instance that we want to compare with.
+ * @return Whether the underlying set bits of both types are the same.
+ */
+ auto operator==(gate_descriptor const & other) const -> bool = default;
+
+ private:
+ // The order in private variables starts for the first variable being the rightmost bit.
+ uint16_t _offset_1 = {}; ///< Lower 16 bits of handler function address (0 - 15)
+ segment_selector _selector = {}; ///< Segment selector (16 - 31)
+ ist_offset _ist = {}; ///< Interrupt Stack Table offset (32 - 39)
+ idt_flags _flags = {}; ///< Gate Type and Flags (40 - 47)
+ uint64_t _offset_2 : 48 = {}; ///< Upper 48 bits of handler function address (48 - 95)
+ uint32_t : 32; ///< Reserved field used to ensure this struct is 128 bits big (96 - 127)
+ };
+} // namespace teachos::arch::context_switching::interrupt_descriptor_table
+
+#endif // TEACHOS_ARCH_X86_64_CONTEXT_SWITCHING_INTERRUPT_DESCRIPTOR_TABLE_GATE_DESCRIPTOR_HPP
diff --git a/arch/x86_64/include/arch/context_switching/interrupt_descriptor_table/idt_flags.hpp b/arch/x86_64/include/arch/context_switching/interrupt_descriptor_table/idt_flags.hpp
new file mode 100644
index 0000000..5104c36
--- /dev/null
+++ b/arch/x86_64/include/arch/context_switching/interrupt_descriptor_table/idt_flags.hpp
@@ -0,0 +1,81 @@
+
+#ifndef TEACHOS_ARCH_X86_64_CONTEXT_SWITCHING_INTERRUPT_DESCRIPTOR_TABLE_IDT_FLAGS_HPP
+#define TEACHOS_ARCH_X86_64_CONTEXT_SWITCHING_INTERRUPT_DESCRIPTOR_TABLE_IDT_FLAGS_HPP
+
+#include <bitset>
+#include <cstdint>
+
+namespace teachos::arch::context_switching::interrupt_descriptor_table
+{
+ /**
+ * @brief Defines helper function for all states that the access byte field of a segment descriptor can
+ * have.
+ */
+ struct [[gnu::packed]] idt_flags
+ {
+ /**
+ * @brief Possible set bits in our underlying bits and the meaning when they are set.
+ */
+ enum bitset : uint8_t
+ {
+ INTERRUPT_GATE = 0b01110, ///< The actual type of gate segment is a interrupt gate.
+ TRAP_GATE = 0b01111, ///< The actual type of gate segment is a trap gate.
+ DESCRIPTOR_LEVEL_KERNEL =
+ 0U << 5U, ///< Highest privileged level used by the kernel to allow for full access of resources.
+ DESCRIPTOR_LEVEL_ADMIN =
+ 1U << 5U, ///< Restricts access to own application and thoose of lower privilege. Should only be used if more
+ ///< than two privilege levels are required, otherwise using Level 3 and Level 0 is recommended.
+ DESCRIPTOR_LEVEL_PRIVILEGED_USER =
+ 2U << 5U, ///< Restricts access to own application and thoose of lower privilege. Should only be used if more
+ ///< than two privilege levels are required, otherwise using Level 3 and Level 0 is recommended.
+ DESCRIPTOR_LEVEL_USER = 3U << 5U, ///< Restricts access to only application and their specific memory.
+ PRESENT = 1U << 7U, ///< Present bit; Allows an entry to refer to a valid segment.
+ ///< Must be set (1) for any valid segment.
+ };
+
+ /**
+ * @brief Default Constructor.
+ */
+ idt_flags() = default;
+
+ /**
+ * @brief Constructor.
+ *
+ * @param flags Allows to set flags for the access byte field using the unscoped enum contained in this class, used
+ * to allow for direct integer conversion. This value is saved and can later be used to check whether certain flags
+ * are enabled or not using contains_flags method.
+ */
+ idt_flags(uint8_t flags);
+
+ /**
+ * @brief Checks if the given std::bitset is a subset or equivalent to the underlying data.
+ *
+ * @note Meaning that all bits that are set in the given std::bitset also have to be set in the underlyng
+ * data. Any additional bits that are set are not relevant.
+ *
+ * @param other Flags that we want to compare against and check if the underlying data has the same bits set.
+ * @return Whether the given flags are a subset or equivalent with the underlying data.
+ */
+ auto contains_flags(std::bitset<8U> other) const -> bool;
+
+ /**
+ * @brief Allows to compare the underlying bits of two instances.
+ *
+ * @param other Other instance that we want to compare with.
+ * @return Whether the underlying set bits of both types are the same.
+ */
+ auto operator==(idt_flags const & other) const -> bool = default;
+
+ /**
+ * @brief Combines all bits that are set in the std::bitset flags with the bits already set in the underlying data.
+ *
+ * @param other Additional bits that should be set.
+ */
+ auto operator|=(std::bitset<8U> other) -> void;
+
+ private:
+ uint8_t _flags = {}; ///< Underlying bits used to read the flags from.
+ };
+} // namespace teachos::arch::context_switching::interrupt_descriptor_table
+
+#endif // TEACHOS_ARCH_X86_64_CONTEXT_SWITCHING_INTERRUPT_DESCRIPTOR_TABLE_IDT_FLAGS_HPP \ No newline at end of file
diff --git a/arch/x86_64/include/arch/context_switching/interrupt_descriptor_table/interrupt_descriptor_table.hpp b/arch/x86_64/include/arch/context_switching/interrupt_descriptor_table/interrupt_descriptor_table.hpp
new file mode 100644
index 0000000..b388e0e
--- /dev/null
+++ b/arch/x86_64/include/arch/context_switching/interrupt_descriptor_table/interrupt_descriptor_table.hpp
@@ -0,0 +1,24 @@
+#ifndef TEACHOS_ARCH_X86_64_CONTEXT_SWITCHING_INTERRUPT_DESCRIPTOR_TABLE_INTERRUPT_DESCRIPTOR_TABLE_HPP
+#define TEACHOS_ARCH_X86_64_CONTEXT_SWITCHING_INTERRUPT_DESCRIPTOR_TABLE_INTERRUPT_DESCRIPTOR_TABLE_HPP
+
+#include "arch/context_switching/interrupt_descriptor_table/interrupt_descriptor_table_pointer.hpp"
+
+namespace teachos::arch::context_switching::interrupt_descriptor_table
+{
+ /**
+ * @brief Updates the IDTR with the created interrupt descriptor table. If it has not been created yet this
+ * method will create it.
+ */
+ auto update_interrupt_descriptor_table_register() -> void;
+
+ /**
+ * @brief Creates the interrupt descriptor table, with the minimum required configuration. If this method is called
+ * more than once, the previously created instance is returned instead.
+ *
+ * @return Reference to the created interrupt_descriptor_table.
+ */
+ auto get_or_create_interrupt_descriptor_table() -> interrupt_descriptor_table &;
+
+} // namespace teachos::arch::context_switching::interrupt_descriptor_table
+
+#endif // TEACHOS_ARCH_X86_64_CONTEXT_SWITCHING_INTERRUPT_DESCRIPTOR_TABLE_INTERRUPT_DESCRIPTOR_TABLE_HPP
diff --git a/arch/x86_64/include/arch/context_switching/interrupt_descriptor_table/interrupt_descriptor_table_pointer.hpp b/arch/x86_64/include/arch/context_switching/interrupt_descriptor_table/interrupt_descriptor_table_pointer.hpp
new file mode 100644
index 0000000..7fe933b
--- /dev/null
+++ b/arch/x86_64/include/arch/context_switching/interrupt_descriptor_table/interrupt_descriptor_table_pointer.hpp
@@ -0,0 +1,40 @@
+#ifndef TEACHOS_ARCH_X86_64_CONTEXT_SWITCHING_INTERRUPT_DESCRIPTOR_TABLE_INTERRUPT_DESCRIPTOR_TABLE_POINTER_HPP
+#define TEACHOS_ARCH_X86_64_CONTEXT_SWITCHING_INTERRUPT_DESCRIPTOR_TABLE_INTERRUPT_DESCRIPTOR_TABLE_POINTER_HPP
+
+#include "arch/context_switching/interrupt_descriptor_table/gate_descriptor.hpp"
+#include "arch/stl/vector.hpp"
+
+namespace teachos::arch::context_switching::interrupt_descriptor_table
+{
+ using interrupt_descriptor_table = stl::vector<gate_descriptor>;
+
+ /**
+ * @brief Represents a pointer to the Interrupt Descriptor Table (IDT).
+ *
+ * This structure is used to store the base address and length of the IDT.
+ */
+ struct [[gnu::packed]] interrupt_descriptor_table_pointer
+ {
+ /**
+ * @brief Default constructor.
+ */
+ interrupt_descriptor_table_pointer() = default;
+
+ /**
+ * @brief Constructor.
+ */
+ interrupt_descriptor_table_pointer(uint16_t table_length, gate_descriptor * address);
+
+ /**
+ * @brief Defaulted three-way comparsion operator.
+ */
+ auto operator<=>(interrupt_descriptor_table_pointer const & other) const -> std::strong_ordering = default;
+
+ private:
+ uint16_t table_length = {}; ///< The amount of segment descriptor entries in the global descriptor table - 1.
+ gate_descriptor * address = {}; ///< Non-owning pointer to the IDT base address.
+ };
+
+} // namespace teachos::arch::context_switching::interrupt_descriptor_table
+
+#endif // TEACHOS_ARCH_X86_64_CONTEXT_SWITCHING_INTERRUPT_DESCRIPTOR_TABLE_INTERRUPT_DESCRIPTOR_TABLE_POINTER_HPP
diff --git a/arch/x86_64/include/arch/context_switching/interrupt_descriptor_table/ist_offset.hpp b/arch/x86_64/include/arch/context_switching/interrupt_descriptor_table/ist_offset.hpp
new file mode 100644
index 0000000..e45bcf4
--- /dev/null
+++ b/arch/x86_64/include/arch/context_switching/interrupt_descriptor_table/ist_offset.hpp
@@ -0,0 +1,45 @@
+#ifndef TEACHOS_ARCH_X86_64_CONTEXT_SWITCHING_INTERRUPT_DESCRIPTOR_TABLE_IST_OFFSET_HPP
+#define TEACHOS_ARCH_X86_64_CONTEXT_SWITCHING_INTERRUPT_DESCRIPTOR_TABLE_IST_OFFSET_HPP
+
+#include <bitset>
+#include <cstdint>
+
+namespace teachos::arch::context_switching::interrupt_descriptor_table
+{
+ /**
+ * @brief Defines helper function for all states that the interrupt stack table offset field of a gate descriptor can
+ * have. Is automatically increased to one byte in size, to include the following 5 reserved bits in the gate
+ * descriptor.
+ */
+ struct [[gnu::packed]] ist_offset
+ {
+ /**
+ * @brief Default Constructor.
+ */
+ ist_offset() = default;
+
+ /**
+ * @brief Constructor.
+ *
+ * @param offset Offset into the interrupt stack table. A value of of 0 means we do not switch stacks, whereas 1 - 7
+ * mean we switch to the n-th stack in the Interrupt Stack Table, contained in the TSS if the gate descriptor that
+ * contains this field is called.
+ */
+ ist_offset(uint8_t offset);
+
+ /**
+ * @brief Allows to compare the underlying set bits of two instances.
+ *
+ * @param other Other instance that we want to compare with.
+ * @return Whether the underlying set bits of both types are the same.
+ */
+ auto operator==(ist_offset const & other) const -> bool = default;
+
+ private:
+ uint8_t _ist : 3 = {}; ///< Offset into the interrupt stack table. A value of of 0 means we do not switch stacks,
+ ///< whereas 1 - 7 mean we switch to the n-th stack in the Interrupt Stack Table, contained
+ ///< in the TSS if the gate descriptor that contains this field is called.
+ };
+} // namespace teachos::arch::context_switching::interrupt_descriptor_table
+
+#endif // TEACHOS_ARCH_X86_64_CONTEXT_SWITCHING_INTERRUPT_DESCRIPTOR_TABLE_IST_OFFSET_HPP
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
new file mode 100644
index 0000000..2a7704e
--- /dev/null
+++ b/arch/x86_64/include/arch/context_switching/interrupt_descriptor_table/segment_selector.hpp
@@ -0,0 +1,105 @@
+#ifndef TEACHOS_ARCH_X86_64_CONTEXT_SWITCHING_INTERRUPT_DESCRIPTOR_TABLE_SEGMENT_SELECTOR_HPP
+#define TEACHOS_ARCH_X86_64_CONTEXT_SWITCHING_INTERRUPT_DESCRIPTOR_TABLE_SEGMENT_SELECTOR_HPP
+
+#include <bitset>
+#include <cstdint>
+
+namespace teachos::arch::context_switching::interrupt_descriptor_table
+{
+ /**
+ * @brief Represents a segment selector in the x86_64 architecture, which points to a valid code segment in the global
+ * descriptor table.
+ *
+ * A segment selector is a 16-bit identifier used to select a segment descriptor
+ * from the Global Descriptor Table (GDT) or the Local Descriptor Table (LDT).
+ * It contains an index, a table indicator (TI), and a requested privilege level (RPL).
+ */
+ struct [[gnu::packed]] segment_selector
+ {
+ /**
+ * @brief Possible set bits in our underlying bits and the meaning when they are set.
+ */
+ enum bitset : uint8_t
+ {
+ REQUEST_LEVEL_KERNEL =
+ 0U << 0U, ///< Highest privileged level used by the kernel to allow for full access of resources.
+ REQUEST_LEVEL_ADMIN =
+ 1U << 0U, ///< Restricts access to own application and thoose of lower privilege. Should only be used if more
+ ///< than two privilege levels are required, otherwise using Level 3 and Level 0 is recommended.
+ REQUEST_LEVEL_PRIVILEGED_USER =
+ 2U << 0U, ///< Restricts access to own application and thoose of lower privilege. Should only be used if more
+ ///< than two privilege levels are required, otherwise using Level 3 and Level 0 is recommended.
+ REQUEST_LEVEL_USER = 3U << 0U, ///< Restricts access to only application and their specific memory.
+ LOCAL_DESCRIPTOR_TABLE = 1U << 2U, ///< Wheter the index referes to an entry in the local or global descriptor
+ ///< table. If enabled the index points to a local descriptor table, if it is
+ ///< cleared it referes to a global descriptor table instead.
+ };
+
+ /**
+ * @brief Default constructor.
+ */
+ segment_selector() = default;
+
+ /**
+ * @brief Constructor.
+ *
+ * @param index Index into the local or global descriptor table. Processor multiplies the index value by 8 (number
+ * of bytes in 32-bit segment descriptor) and adds the result to the base GDT or LDT address.
+ * @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.
+ */
+ constexpr segment_selector(uint16_t index, uint8_t flags)
+ : _flags(flags)
+ , _index(index)
+ {
+ // Nothing to do.
+ }
+
+ /**
+ * @brief Checks if the given std::bitset is a subset or equivalent to the underlying data.
+ *
+ * @note Meaning that all bits that are set in the given std::bitset also have to be set in the underlyng
+ * data. Any additional bits that are set are not relevant.
+ *
+ * @param other Flags that we want to compare against and check if the underlying data has the same bits set.
+ * @return Whether the given flags are a subset or equivalent with the underlying data.
+ */
+ auto contains_flags(std::bitset<3U> other) const -> bool;
+
+ /**
+ * @brief Gets the index into the global descriptor table or the local descriptor table this segment selector is
+ * pointing too.
+ *
+ * @return Underlying value of the index field, bit 3 - 16.
+ */
+ [[gnu::section(".user_text")]]
+ auto get_index() const -> uint16_t;
+
+ /**
+ * @brief Defaulted three-way comparsion operator.
+ */
+ auto operator<=>(segment_selector const & other) const -> std::strong_ordering = default;
+
+ /**
+ * @brief Combines all bits that are set in the std::bitset flags with the bits already set in the underlying data.
+ *
+ * @param other Additional bits that should be set.
+ */
+ auto operator|=(std::bitset<3U> other) -> void;
+
+ /**
+ * @brief Cast the underlying data into a combined 16-bit form, that contains all data.
+ *
+ * @return Underlying value combined into it's full size.
+ */
+ operator uint16_t() const;
+
+ private:
+ uint8_t _flags : 3 = {}; ///< Underlying bits used to read the flags from.
+ uint16_t _index : 13 =
+ {}; ///< Index into the local or global descriptor table. Processor multiplies the index value by 16 (number of
+ ///< bytes in segment descriptor) and adds the result to the base address.
+ };
+} // namespace teachos::arch::context_switching::interrupt_descriptor_table
+
+#endif // TEACHOS_ARCH_X86_64_CONTEXT_SWITCHING_INTERRUPT_DESCRIPTOR_TABLE_SEGMENT_SELECTOR_HPP
diff --git a/arch/x86_64/include/arch/context_switching/main.hpp b/arch/x86_64/include/arch/context_switching/main.hpp
new file mode 100644
index 0000000..f8477ea
--- /dev/null
+++ b/arch/x86_64/include/arch/context_switching/main.hpp
@@ -0,0 +1,51 @@
+#ifndef TEACHOS_ARCH_X86_64_CONTEXT_SWITCHING_MAIN_HPP
+#define TEACHOS_ARCH_X86_64_CONTEXT_SWITCHING_MAIN_HPP
+
+#include "arch/context_switching/interrupt_descriptor_table/interrupt_descriptor_table.hpp"
+#include "arch/context_switching/segment_descriptor_table/global_descriptor_table.hpp"
+
+namespace teachos::arch::context_switching
+{
+ /**
+ * @brief Contains the references to the tables required for context switching
+ */
+ struct descriptor_tables
+ {
+ segment_descriptor_table::global_descriptor_table & gdt; ///< Reference to the global descriptor table.
+ interrupt_descriptor_table::interrupt_descriptor_table & idt; ///< Reference to the interrupt descriptor table.
+ };
+
+ /**
+ * @brief Creates the Interrupt Descriptor Table and Global Descriptor Table as a static variable the first time this
+ * method is called and update IDTR and GDTR registers values.
+ *
+ * @note Subsequent calls after the first one, will simply return the previously created tables, but not update the
+ * registers again.
+ *
+ * @return References to the statically created Interrupt Descriptor and Global Descriptor Table.
+ */
+ auto initialize_descriptor_tables() -> descriptor_tables;
+
+ /**
+ * @brief Switches from the current Kernel Mode (Level 0) to User Mode (Level 3). Will simply use predefined Segment
+ * Selectors for the User Data and User Code Segment, which are Index 3 and 4 in the GDT respectively.
+ */
+ auto switch_to_user_mode() -> void;
+
+ /**
+ * @brief Switches from the current Code and Data Segment to the given Code and Data Segment.
+ *
+ * @note This method will additionally call initialize_descriptor_tables, to ensure the GDTR and IDTR have been setup
+ * correctly before attempting to switch the context. This switch is achieved using a far return, which will once
+ * executed call the given void function.
+ *
+ * @param data_segment Data Segment that the SS, DS; ES, FS and GS register will be set too.
+ * @param code_segment Code Segment that the CS register will be set too.
+ * @param return_function Function that will be called once the switch has been achieved.
+ */
+ 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/context_switching/segment_descriptor_table/access_byte.hpp b/arch/x86_64/include/arch/context_switching/segment_descriptor_table/access_byte.hpp
new file mode 100644
index 0000000..7450330
--- /dev/null
+++ b/arch/x86_64/include/arch/context_switching/segment_descriptor_table/access_byte.hpp
@@ -0,0 +1,104 @@
+
+#ifndef TEACHOS_ARCH_X86_64_CONTEXT_SWITCHING_SEGMENT_DESCRIPTOR_TABLE_ACCESS_BYTE_HPP
+#define TEACHOS_ARCH_X86_64_CONTEXT_SWITCHING_SEGMENT_DESCRIPTOR_TABLE_ACCESS_BYTE_HPP
+
+#include <bitset>
+#include <cstdint>
+
+namespace teachos::arch::context_switching::segment_descriptor_table
+{
+ /**
+ * @brief Defines helper function for all states that the access byte field of a segment descriptor can
+ * have.
+ */
+ struct [[gnu::packed]] access_byte
+ {
+ /**
+ * @brief Possible set bits in our underlying bits and the meaning when they are set.
+ */
+ enum bitset : uint8_t
+ {
+ ACCESSED =
+ 1U
+ << 0U, ///< Whether the segment has been accessed since the last time the operating system has cleared the
+ ///< flag. If enabled it has been accessed, otherwise it has not been accessed since the last clear.
+ WRITABLE = 1U << 1U, ///< Indicates if the data segment is writable or not. If enabled the code segment allows
+ ///< read and write access, otherwise only read access is possible.
+ READABLE = 1U << 1U, ///< Indicates if the code segment is readable or not. If enabled the code segment allows
+ ///< read and execute access, otherwise only executable access is possible.
+ CONFORMING =
+ 1U << 2U, ///< Indicates if the code is allowed to be executed by different access levels
+ ///< (higher or lower) in code segments. If enabled the code segment allows access, otherwise
+ ///< access from different privilege levels with throw a General-Protectione exception.
+ EXPAND_DOWN = 1U << 2U, ///< Indicates if the expansion direction is up or down in data segments. If enabled the
+ ///< data segment expands downwards, otherwise it expands upwards.
+ CODE_SEGMENT = 1U << 3U, ///< Further defines the actual type of the segment. If enabled this segment is a code
+ ///< segment, otherwise its a data segment.
+ LOCAL_DESCRIPTOR_TABLE = 2, ///< The actual type of sytem segment is a local descriptor table.
+ TASK_STATE_SEGMENT_AVAILABLE =
+ 9, ///< The actual type of sytem segment is a task state segment that is still available.
+ TASK_STATE_SEGMENT_BUSY = 11, ///< The actual type of sytem segment is a task state segment that is currently in
+ ///< use and therefore busy.
+ CALL_GATE = 11, ///< The actual type of sytem segment is a call gate.
+ INTERRUPT_GATE = 14, ///< The actual type of sytem segment is a interrupt gate.
+ TRAP_GATE = 15, ///< The actual type of sytem segment is a trap gate.
+ CODE_OR_DATA_SEGMENT = 1U << 4U, ///< Defines a system segment (if 0) or a code/data segment (if 1).
+ DESCRIPTOR_LEVEL_KERNEL =
+ 0U << 5U, ///< Highest privileged level used by the kernel to allow for full access of resources.
+ DESCRIPTOR_LEVEL_ADMIN =
+ 1U << 5U, ///< Restricts access to own application and thoose of lower privilege. Should only be used if more
+ ///< than two privilege levels are required, otherwise using Level 3 and Level 0 is recommended.
+ DESCRIPTOR_LEVEL_PRIVILEGED_USER =
+ 2U << 5U, ///< Restricts access to own application and thoose of lower privilege. Should only be used if more
+ ///< than two privilege levels are required, otherwise using Level 3 and Level 0 is recommended.
+ DESCRIPTOR_LEVEL_USER = 3U << 5U, ///< Restricts access to only application and their specific memory.
+ PRESENT = 1U << 7U, ///< Present bit; Allows an entry to refer to a valid segment.
+ ///< Must be set (1) for any valid segment.
+ };
+
+ /**
+ * @brief Default Constructor.
+ */
+ access_byte() = default;
+
+ /**
+ * @brief Constructor.
+ *
+ * @param flags Allows to set flags for the access byte field using the unscoped enum contained in this class, used
+ * to allow for direct integer conversion. This value is saved and can later be used to check whether certain flags
+ * are enabled or not using contains_flags method.
+ */
+ access_byte(uint8_t flags);
+
+ /**
+ * @brief Checks if the given std::bitset is a subset or equivalent to the underlying data.
+ *
+ * @note Meaning that all bits that are set in the given std::bitset also have to be set in the underlyng
+ * data. Any additional bits that are set are not relevant.
+ *
+ * @param other Flags that we want to compare against and check if the underlying data has the same bits set.
+ * @return Whether the given flags are a subset or equivalent with the underlying data.
+ */
+ auto contains_flags(std::bitset<8U> other) const -> bool;
+
+ /**
+ * @brief Allows to compare the underlying data of two instances.
+ *
+ * @param other Other instance that we want to compare with.
+ * @return Whether the underlying data of both types is the same.
+ */
+ auto operator==(access_byte const & other) const -> bool = default;
+
+ /**
+ * @brief Combines all bits that are set in the std::bitset flags with the bits already set in the underlying data.
+ *
+ * @param other Additional bits that should be set.
+ */
+ auto operator|=(std::bitset<8U> other) -> void;
+
+ private:
+ uint8_t _flags = {}; ///< Underlying bits used to read the flags from.
+ };
+} // namespace teachos::arch::context_switching::segment_descriptor_table
+
+#endif // TEACHOS_ARCH_X86_64_CONTEXT_SWITCHING_SEGMENT_DESCRIPTOR_TABLE_ACCESS_BYTE_HPP \ No newline at end of file
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
new file mode 100644
index 0000000..e24b988
--- /dev/null
+++ b/arch/x86_64/include/arch/context_switching/segment_descriptor_table/gdt_flags.hpp
@@ -0,0 +1,93 @@
+#ifndef TEACHOS_ARCH_X86_64_CONTEXT_SWITCHING_SEGMENT_DESCRIPTOR_TABLE_GDT_FLAGS_HPP
+#define TEACHOS_ARCH_X86_64_CONTEXT_SWITCHING_SEGMENT_DESCRIPTOR_TABLE_GDT_FLAGS_HPP
+
+#include "arch/context_switching/segment_descriptor_table/segment_descriptor_type.hpp"
+
+#include <bitset>
+
+namespace teachos::arch::context_switching::segment_descriptor_table
+{
+ /**
+ * @brief Defines helper function for all states that the flags field of a segment descriptor can
+ * have.
+ */
+ struct [[gnu::packed]] gdt_flags
+ {
+ /**
+ * @brief Possible set bits in our underlying bits and the meaning when they are set.
+ */
+ enum bitset : uint8_t
+ {
+ 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 this bit is set the 3rd bit needs to be clear (0).
+ UPPER_BOUND = 1U << 2U, ///< Specifies the upper bound of the segment for expand down data segment. Enable for 4
+ ///< GiB, 4 KiB otherwise.
+ STACK_POINTER_SIZE = 1U << 2U, ///< Specifies the size of the Stack Pointer (SP) for stack segments used for
+ ///< implicit stack operations. Enable for 32 bit, 16 bit otherwise.
+ DEFAULT_LENGTH = 1U << 2U, ///< Indicates the default length for code segments with effective addresses and
+ ///< operands. Enable for 32 bit, 16 bit otherwise.
+ 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.
+ };
+
+ /**
+ * @brief Default Constructor.
+ */
+ gdt_flags() = default;
+
+ /**
+ * @brief Constructor.
+ *
+ * @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. This value is saved and can later be used to check whether certain flags are
+ * enabled or not using contains_flags method.
+ * @param limit Does not necessarily make sense in the gdt flags type, but because the flags alone are only 4 bit
+ * the type would still require the space for a complete bit. Therefore the 4 bit segment limit field before the
+ * flags field is included in this type to ensure we actually contain 8 bit of data.
+ */
+ gdt_flags(uint8_t flags, std::bitset<20U> limit);
+
+ /**
+ * @brief Checks if the given std::bitset is a subset or equivalent to the underlying data.
+ *
+ * @note Meaning that all bits that are set in the given std::bitset also have to be set in the underlyng
+ * data. Any additional bits that are set are not relevant.
+ *
+ * @param other Flags that we want to compare against and check if the underlying data has the same bits set.
+ * @return Whether the given flags are a subset or equivalent with the underlying data.
+ */
+ auto contains_flags(std::bitset<4U> other) const -> bool;
+
+ /**
+ * @brief Get part of the segment limit that is saved in the gdt flags. This does not necessarily make sense in this
+ * object, but it has to be included here because a struct can not be smaller than a full byte. Therefore we include
+ * the 4 bit segment limit field so that it results in a compelte byte with the addtional 4 bit of gdt flags.
+ *
+ * @return 4-bit limit segment
+ */
+ auto get_limit() const -> std::bitset<4U>;
+
+ /**
+ * @brief Allows to compare the underlying set bits of two instances.
+ *
+ * @param other Other instance that we want to compare with.
+ * @return Whether the underlying set bits of both types are the same.
+ */
+ auto operator==(gdt_flags const & other) const -> bool = default;
+
+ /**
+ * @brief Combines all bits that are set in the std::bitset flags with the bits already set in the underlying data.
+ *
+ * @param other Additional bits that should be set.
+ */
+ auto operator|=(std::bitset<4U> other) -> void;
+
+ private:
+ uint8_t _limit_2 : 4 = {}; ///< Second part of the limit field.
+ uint8_t _flags : 4 = {}; ///< Underlying bits used to read the flags from.
+ };
+} // namespace teachos::arch::context_switching::segment_descriptor_table
+
+#endif // TEACHOS_ARCH_X86_64_CONTEXT_SWITCHING_SEGMENT_DESCRIPTOR_TABLE_GDT_FLAGS_HPP
diff --git a/arch/x86_64/include/arch/context_switching/segment_descriptor_table/global_descriptor_table.hpp b/arch/x86_64/include/arch/context_switching/segment_descriptor_table/global_descriptor_table.hpp
new file mode 100644
index 0000000..44f2692
--- /dev/null
+++ b/arch/x86_64/include/arch/context_switching/segment_descriptor_table/global_descriptor_table.hpp
@@ -0,0 +1,37 @@
+#ifndef TEACHOS_ARCH_X86_64_CONTEXT_SWITCHING_SEGMENT_DESCRIPTOR_TABLE_GLOBAL_DESCRIPTOR_TABLE_HPP
+#define TEACHOS_ARCH_X86_64_CONTEXT_SWITCHING_SEGMENT_DESCRIPTOR_TABLE_GLOBAL_DESCRIPTOR_TABLE_HPP
+
+#include "arch/context_switching/segment_descriptor_table/global_descriptor_table_pointer.hpp"
+#include "arch/context_switching/segment_descriptor_table/task_state_segment.hpp"
+
+namespace teachos::arch::context_switching::segment_descriptor_table
+{
+ /**
+ * @brief Creates the global descriptor table, with the minimum required configuration. If this method is called more
+ * than once, the previously created instance is returned instead.
+ *
+ * @return Reference to the created global_descriptor_table.
+ */
+ auto get_or_create_gdt() -> global_descriptor_table &;
+
+ /**
+ * @brief Updates the GDTR with the created global descriptor table. If it has not been created yet this
+ * method will create it.
+ *
+ * @note This method will only set the GDTR, but for the processor to actually register the change a far jump
+ * has to be executed. This also has to be done before updating the TR.
+ */
+ auto update_gdtr() -> void;
+
+ /**
+ * @brief Updates the TR with the created task state segment. If it has not been created yet this
+ * method will create it.
+ *
+ * @note This method should only be called after update_gdtr() and a far jump has been
+ * executed. Because before that trying to access the segment will cause an exception.
+ */
+ auto update_tss_register() -> void;
+
+} // namespace teachos::arch::context_switching::segment_descriptor_table
+
+#endif // TEACHOS_ARCH_X86_64_CONTEXT_SWITCHING_SEGMENT_DESCRIPTOR_TABLE_GLOBAL_DESCRIPTOR_TABLE_HPP
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
new file mode 100644
index 0000000..292ff70
--- /dev/null
+++ b/arch/x86_64/include/arch/context_switching/segment_descriptor_table/global_descriptor_table_pointer.hpp
@@ -0,0 +1,41 @@
+#ifndef TEACHOS_ARCH_X86_64_CONTEXT_SWITCHING_SEGMENT_DESCRIPTOR_TABLE_GLOBAL_DESCRIPTOR_TABLE_POINTER_HPP
+#define TEACHOS_ARCH_X86_64_CONTEXT_SWITCHING_SEGMENT_DESCRIPTOR_TABLE_GLOBAL_DESCRIPTOR_TABLE_POINTER_HPP
+
+#include "arch/stl/vector.hpp"
+
+#include <cstdint>
+
+namespace teachos::arch::context_switching::segment_descriptor_table
+{
+ using global_descriptor_table = stl::vector<uint64_t>;
+
+ /**
+ * @brief Represents a pointer to the Global Descriptor Table (GDT).
+ *
+ * This structure is used to store the base address and length of the GDT.
+ * It is used when loading or modifying the GDT during context switching.
+ */
+ struct [[gnu::packed]] global_descriptor_table_pointer
+ {
+ /**
+ * @brief Default constructor.
+ */
+ global_descriptor_table_pointer() = default;
+
+ /**
+ * @brief Constructor.
+ */
+ global_descriptor_table_pointer(uint16_t table_length, uint64_t * address);
+
+ /**
+ * @brief Defaulted three-way comparsion operator.
+ */
+ auto operator<=>(global_descriptor_table_pointer const & other) const -> std::strong_ordering = default;
+
+ private:
+ uint16_t table_length = {}; ///< The amount of segment descriptor entries in the global descriptor table - 1.
+ uint64_t * address = {}; ///< Non-owning pointer to the GDT base address.
+ };
+} // namespace teachos::arch::context_switching::segment_descriptor_table
+
+#endif // TEACHOS_ARCH_X86_64_CONTEXT_SWITCHING_SEGMENT_DESCRIPTOR_TABLE_GLOBAL_DESCRIPTOR_TABLE_POINTER_HPP
diff --git a/arch/x86_64/include/arch/context_switching/segment_descriptor_table/segment_descriptor_base.hpp b/arch/x86_64/include/arch/context_switching/segment_descriptor_table/segment_descriptor_base.hpp
new file mode 100644
index 0000000..933fb4d
--- /dev/null
+++ b/arch/x86_64/include/arch/context_switching/segment_descriptor_table/segment_descriptor_base.hpp
@@ -0,0 +1,73 @@
+#ifndef TEACHOS_ARCH_X86_64_CONTEXT_SWITCHING_SEGMENT_DESCRIPTOR_TABLE_SEGMENT_DESCRIPTOR_BASE_HPP
+#define TEACHOS_ARCH_X86_64_CONTEXT_SWITCHING_SEGMENT_DESCRIPTOR_TABLE_SEGMENT_DESCRIPTOR_BASE_HPP
+
+#include "arch/context_switching/segment_descriptor_table/access_byte.hpp"
+#include "arch/context_switching/segment_descriptor_table/gdt_flags.hpp"
+#include "arch/context_switching/segment_descriptor_table/segment_descriptor_type.hpp"
+
+namespace teachos::arch::context_switching::segment_descriptor_table
+{
+ /**
+ * @brief Defines helper function for all states and the actual data the segment descriptor can have.
+ */
+ struct [[gnu::packed]] segment_descriptor_base
+ {
+ /**
+ * @brief Default Constructor.
+ */
+ segment_descriptor_base() = default;
+
+ /**
+ * @brief Constructor.
+ *
+ * @note Created segment descriptor copies the given bytes into these components requiring the space of one
+ * segment descriptor entry in the global descriptor table being 64-bit.
+ * - 8 bit Access Type
+ * - 4 bit Flags
+ * - 32 bit Base Address
+ * - 20 bit Limit
+ *
+ * @param flags Copies the bits set from the given data into the individual components of a segment
+ * descriptor.
+ */
+ explicit segment_descriptor_base(uint64_t flags);
+
+ /**
+ * @brief Constructor.
+ *
+ * @param access_byte, flags, base, limit Copies the bits set from the given data into the individual components of
+ * a segment descriptor.
+ */
+ segment_descriptor_base(access_byte access_byte, gdt_flags flags, uint32_t base, std::bitset<20U> limit);
+
+ /**
+ * @brief Calculates the underlying segment type that this segement descriptor is describing.
+ */
+ auto get_segment_type() const -> segment_descriptor_type;
+
+ /**
+ * @brief Cast the underlying data into a combined 64-bit form, that contains all data.
+ *
+ * @return Underlying value combined into it's full size.
+ */
+ operator uint64_t() const;
+
+ /**
+ * @brief Allows to compare the underlying bits of two instances.
+ *
+ * @param other Other instance that we want to compare with.
+ * @return Whether the underlying set bits of both types are the same.
+ */
+ auto operator==(segment_descriptor_base const & other) const -> bool = default;
+
+ private:
+ // The order in private variables starts for the first variable being the rightmost bit.
+ uint16_t _limit_1 = {}; ///< First part of the limit field (0 - 15)
+ uint32_t _base_1 : 24 = {}; ///< First part of the base field (16 - 39)
+ access_byte _access = {}; ///< Access byte field (40 - 47)
+ gdt_flags _flag = {}; ///< Second part of the limit field + Flags field (48 - 55)
+ uint8_t _base_2 = {}; ///< Second part of the base field (56 - 63)
+ };
+} // namespace teachos::arch::context_switching::segment_descriptor_table
+
+#endif // TEACHOS_ARCH_X86_64_CONTEXT_SWITCHING_SEGMENT_DESCRIPTOR_TABLE_SEGMENT_DESCRIPTOR_BASE_HPP
diff --git a/arch/x86_64/include/arch/context_switching/segment_descriptor_table/segment_descriptor_extension.hpp b/arch/x86_64/include/arch/context_switching/segment_descriptor_table/segment_descriptor_extension.hpp
new file mode 100644
index 0000000..40bcc8a
--- /dev/null
+++ b/arch/x86_64/include/arch/context_switching/segment_descriptor_table/segment_descriptor_extension.hpp
@@ -0,0 +1,74 @@
+#ifndef TEACHOS_ARCH_X86_64_CONTEXT_SWITCHING_SEGMENT_DESCRIPTOR_TABLE_SEGMENT_DESCRIPTOR_EXTENSION_HPP
+#define TEACHOS_ARCH_X86_64_CONTEXT_SWITCHING_SEGMENT_DESCRIPTOR_TABLE_SEGMENT_DESCRIPTOR_EXTENSION_HPP
+
+#include "arch/context_switching/segment_descriptor_table/segment_descriptor_base.hpp"
+
+namespace teachos::arch::context_switching::segment_descriptor_table
+{
+ __extension__ typedef __int128 int128_t;
+ __extension__ typedef unsigned __int128 uint128_t;
+
+ /**
+ * @brief Defines helper function for all states and the actual data the segment descriptor can have.
+ */
+ struct [[gnu::packed]] segment_descriptor_extension
+ {
+ /**
+ * @brief Default Constructor.
+ */
+ segment_descriptor_extension() = default;
+
+ /**
+ * @brief Constructor.
+ *
+ * @note Created segment descriptor copies the given bytes into these components requiring the space of two
+ * segment descriptor entry in the global descriptor table being 128-bit. Ending with a 32 bit reserved
+ * field that has to be used, because the segment descriptor needs to be big enough for two segment
+ * descriptor entries.
+ * - 8 bit Access Type
+ * - 4 bit Flags
+ * - 64 bit Base Address
+ * - 20 bit Limit
+ *
+ * @param flags Copies the bits set from the given data into the individual components of a segment
+ * descriptor.
+ */
+ explicit segment_descriptor_extension(uint128_t flags);
+
+ /**
+ * @brief Constructor.
+ *
+ * @param access_byte, flags, base, limit Copies the bits set from the given data into the individual components of
+ * a segment descriptor.
+ */
+ segment_descriptor_extension(access_byte access_byte, gdt_flags flags, uint64_t base, std::bitset<20U> limit);
+
+ /**
+ * @brief Returns the underlying base segment descriptor, being the first part of the segment descriptor consisting
+ * of two entries in the global descriptor table.
+ */
+ auto get_first_gdt_entry() const -> segment_descriptor_base;
+
+ /**
+ * @brief Returns the underlying extension to the segment descriptor, being the second part of the segment
+ * descriptor consiting of two entries in the global descriptor table.
+ */
+ auto get_second_gdt_entry() const -> uint64_t;
+
+ /**
+ * @brief Allows to compare the underlying bits of two instances.
+ *
+ * @param other Other instance that we want to compare with.
+ * @return Whether the underlying set bits of both types are the same.
+ */
+ auto operator==(segment_descriptor_extension const & other) const -> bool = default;
+
+ private:
+ // The order in private variables starts for the first variable being the rightmost bit.
+ segment_descriptor_base _base = {}; ///< Base Segment Descriptor representing single entry in GDT (0 - 63)
+ uint32_t _base_3 = {}; ///< Third part of the base field (63 - 95)
+ uint32_t : 32; ///< Reserved field used to ensure this struct is 128 bits big (96 - 127)
+ };
+} // namespace teachos::arch::context_switching::segment_descriptor_table
+
+#endif // TEACHOS_ARCH_X86_64_CONTEXT_SWITCHING_SEGMENT_DESCRIPTOR_TABLE_SEGMENT_DESCRIPTOR_EXTENSION_HPP
diff --git a/arch/x86_64/include/arch/context_switching/segment_descriptor_table/segment_descriptor_type.hpp b/arch/x86_64/include/arch/context_switching/segment_descriptor_table/segment_descriptor_type.hpp
new file mode 100644
index 0000000..8770b81
--- /dev/null
+++ b/arch/x86_64/include/arch/context_switching/segment_descriptor_table/segment_descriptor_type.hpp
@@ -0,0 +1,27 @@
+#ifndef TEACHOS_ARCH_X86_64_CONTEXT_SWITCHING_SEGMENT_DESCRIPTOR_TABLE_SEGMENT_DESCRIPTOR_TYPES_HPP
+#define TEACHOS_ARCH_X86_64_CONTEXT_SWITCHING_SEGMENT_DESCRIPTOR_TABLE_SEGMENT_DESCRIPTOR_TYPES_HPP
+
+#include <cstdint>
+
+namespace teachos::arch::context_switching::segment_descriptor_table
+{
+ /**
+ * @brief Possible overlying types of the segment descriptor. Allowing to discern between the major types, which
+ * result in different handling of the actual data contained in the descriptor.
+ */
+ enum class segment_descriptor_type : uint8_t
+ {
+ SYSTEM_SEGMENT, ///< The segment is of type system, is distinguished by the Descriptor Type field in the Access
+ ///< Byte. Can be further distinguised to specific system segment types using the Type Field in the
+ ///< Access Byte.
+ DATA_SEGMENT, ///< The segment is of type data, is is distinguished by the Descriptor Type field in the Access
+ ///< Byte and the first bit of the Type Field in the Access Byte. Can be further distinguised to
+ ///< specific data segment types using the the remaining bits in the Type Field in the Access Byte.
+ CODE_SEGMENT, ///< The segment is of type code, is is distinguished by the Descriptor Type field in
+ ///< the Access Byte and the first bit of the Type Field in the Access Byte. Can be
+ ///< further distinguised to specific data segment types using the the remaining bits in
+ ///< the Type Field in the Access Byte.
+ };
+} // namespace teachos::arch::context_switching::segment_descriptor_table
+
+#endif // TEACHOS_ARCH_X86_64_CONTEXT_SWITCHING_SEGMENT_DESCRIPTOR_TABLE_SEGMENT_DESCRIPTOR_TYPES_HPP
diff --git a/arch/x86_64/include/arch/context_switching/segment_descriptor_table/task_state_segment.hpp b/arch/x86_64/include/arch/context_switching/segment_descriptor_table/task_state_segment.hpp
new file mode 100644
index 0000000..d4aa5e8
--- /dev/null
+++ b/arch/x86_64/include/arch/context_switching/segment_descriptor_table/task_state_segment.hpp
@@ -0,0 +1,32 @@
+#ifndef TEACHOS_ARCH_X86_64_CONTEXT_SWITCHING_SEGMENT_DESCRIPTOR_TABLE_TASK_STATE_SEGMENT_HPP
+#define TEACHOS_ARCH_X86_64_CONTEXT_SWITCHING_SEGMENT_DESCRIPTOR_TABLE_TASK_STATE_SEGMENT_HPP
+
+#include <cstdint>
+
+namespace teachos::arch::context_switching::segment_descriptor_table
+{
+ /**
+ * @brief 64-bit task state segment
+ */
+ struct [[gnu::packed]] task_state_segment
+ {
+ private:
+ uint32_t : 32;
+ uint64_t rsp0 = {};
+ uint64_t rsp1 = {};
+ uint64_t rsp2 = {};
+ uint64_t : 64;
+ uint64_t ist1 = {};
+ uint64_t ist2 = {};
+ uint64_t ist3 = {};
+ uint64_t ist4 = {};
+ uint64_t ist5 = {};
+ uint64_t ist6 = {};
+ uint64_t ist7 = {};
+ uint64_t : 64;
+ uint16_t : 16;
+ uint16_t io_map_base_address = {};
+ };
+} // namespace teachos::arch::context_switching::segment_descriptor_table
+
+#endif // TEACHOS_ARCH_X86_64_CONTEXT_SWITCHING_SEGMENT_DESCRIPTOR_TABLE_TASK_STATE_SEGMENT_HPP
diff --git a/arch/x86_64/include/arch/context_switching/syscall/main.hpp b/arch/x86_64/include/arch/context_switching/syscall/main.hpp
new file mode 100644
index 0000000..59adc13
--- /dev/null
+++ b/arch/x86_64/include/arch/context_switching/syscall/main.hpp
@@ -0,0 +1,88 @@
+#ifndef TEACHOS_ARCH_X86_64_CONTEXT_SWITCHING_SYSCALL_MAIN_HPP
+#define TEACHOS_ARCH_X86_64_CONTEXT_SWITCHING_SYSCALL_MAIN_HPP
+
+#include <cstdint>
+
+namespace teachos::arch::context_switching::syscall
+{
+ /**
+ * @brief Possible syscall implementation that should actually be called.
+ *
+ * @note Attempts to reflect the Linux interface partially. See https://filippo.io/linux-syscall-table/ for more
+ * information.
+ */
+ enum class type : uint64_t
+ {
+ WRITE = 1U, ///< Loads the arg_0 parameter as an address pointing to a const char array, which will then be printed
+ ///< onto the VGA buffer screen.
+ EXPAND_HEAP = 2U, /// Expands the User Heap by additonally mapping 100 KiB of virtual page memory. Ignores the
+ /// parameters and uses them as out parameters instead, where arg_0 is the start of the newly
+ /// mapped heap area and arg_1 is the size of the entire area. Can be less than 100 KiB if less
+ /// space remains.
+ ASSERT = 3U, /// Loads the arg_0 parameter as a boolean which needs to be true or it will print the message in
+ /// arg_1 parameter onto the VGA buffer screen, keep it as a nullptr if it shouldn't print anything
+ /// and then it halts the further execution of the application.
+ };
+
+ /**
+ * @brief Possible error codes that can be returned by the different syscall methods called depending on the type
+ * enum.
+ */
+ enum class error : uint8_t
+ {
+ OK = 0U, ///< No error occured in syscall.
+ OUT_OF_MEMORY = 1U, ///< Expanding heap failed because we have run out of mappable virtual address space.
+ };
+
+ /**
+ * @brief Allows to convert the error enum type into a boolean directly. Where any code besides 0 being no error, will
+ * return true. The remaining errors return true.
+ *
+ * @param e Error code that was returned by the syscall.
+ * @return Return true if there was no error and false otherwise.
+ */
+ constexpr bool operator!(error e) { return e == error::OK; }
+
+ /**
+ * @brief Maximum amount of arguments that can be passed to a syscall. Default value is 0 and arguments are only ever
+ * used depending on the actual enum type and if the method requires thoose parameters.
+ */
+ struct arguments
+ {
+ uint64_t arg_0{}; ///< First optional paramter to the syscall representing the RDI register.
+ uint64_t arg_1{}; ///< Second optional paramter to the syscall representing the RSI register.
+ uint64_t arg_2{}; ///< Third optional paramter to the syscall representing the RDX register.
+ uint64_t arg_3{}; ///< Fourth optional paramter to the syscall representing the R10 register.
+ uint64_t arg_4{}; ///< Fifth optional paramter to the syscall representing the R8 register.
+ uint64_t arg_5{}; ///< Sixth optional paramter to the syscall representing the R9 register.
+ };
+
+ /**
+ * @brief Response of a systemcall always containin an error code, signaling if the syscall even succeeded or not.
+ * Additionally it may contain up to 6 return values in the values struct.
+ */
+ struct response
+ {
+ error error_code; ///< Error code returned by the syscall. If it failed all the values will be 0.
+ arguments values = {}; ///< Optional return values of the syscall implementation.
+ };
+
+ /**
+ * @brief Calls the method associated with the given syscall number and passes the given optional arguments to it,
+ * over the RDI, RSI, RDX, R10, R8 and R9 register.
+ *
+ * @param syscall_number Syscall method that should be called. See enum values in type for possible implemented
+ * methods.
+ * @param args Optional arguments passable to the different syscall methods, called depending on the syscall_number.
+ * Not passing the required parameters to the method, will result in passing 0 instead, which might make the fail or
+ * not function correctly.
+ * @return The syscall implementation always returns a bool-convertable error code converting to true if the syscall
+ * failed or false if it didn't. Additionally it might pase additional values besides the error code, they will be set
+ * in the arguments struct. So the value can be read and used for further processing.
+ */
+ [[gnu::section(".user_text")]]
+ auto syscall(type syscall_number, arguments args = {}) -> response;
+
+} // namespace teachos::arch::context_switching::syscall
+
+#endif // TEACHOS_ARCH_X86_64_CONTEXT_SWITCHING_SYSCALL_MAIN_HPP
diff --git a/arch/x86_64/include/arch/context_switching/syscall/syscall_enable.hpp b/arch/x86_64/include/arch/context_switching/syscall/syscall_enable.hpp
new file mode 100644
index 0000000..8cb468a
--- /dev/null
+++ b/arch/x86_64/include/arch/context_switching/syscall/syscall_enable.hpp
@@ -0,0 +1,18 @@
+#ifndef TEACHOS_ARCH_X86_64_CONTEXT_SWITCHING_SYSCALL_SYSCALL_ENABLE_HPP
+#define TEACHOS_ARCH_X86_64_CONTEXT_SWITCHING_SYSCALL_SYSCALL_ENABLE_HPP
+
+namespace teachos::arch::context_switching::syscall
+{
+ /**
+ * @brief Enables and sets up internal CPU registers to allow for syscall to function correctly and switch context
+ * temporarily back to the kernel level.
+ *
+ * @note Configures the Model Specific Register required by syscall (LSTAR, FMASK, STAR) with the correct values so
+ * that the syscall_handler is called and sets the System Call Extension bit on the EFER flags to allow for syscall
+ * to be used.
+ */
+ auto enable_syscall() -> void;
+
+} // namespace teachos::arch::context_switching::syscall
+
+#endif // TEACHOS_ARCH_X86_64_CONTEXT_SWITCHING_SYSCALL_SYSCALL_ENABLE_HPP
diff --git a/arch/x86_64/include/arch/context_switching/syscall/syscall_handler.hpp b/arch/x86_64/include/arch/context_switching/syscall/syscall_handler.hpp
new file mode 100644
index 0000000..2e7bcd1
--- /dev/null
+++ b/arch/x86_64/include/arch/context_switching/syscall/syscall_handler.hpp
@@ -0,0 +1,18 @@
+#ifndef TEACHOS_ARCH_X86_64_CONTEXT_SWITCHING_SYSCALL_SYSCALL_HANDLER_HPP
+#define TEACHOS_ARCH_X86_64_CONTEXT_SWITCHING_SYSCALL_SYSCALL_HANDLER_HPP
+
+#include <cstdint>
+
+namespace teachos::arch::context_switching::syscall
+{
+ /**
+ * @brief Handler for SYSCALL instruction. Calls a specific implementation based
+ * on the register RAX.
+ *
+ * @return Returns with LEAVE, SYSRETQ
+ */
+ auto syscall_handler() -> void;
+
+} // namespace teachos::arch::context_switching::syscall
+
+#endif // TEACHOS_ARCH_X86_64_CONTEXT_SWITCHING_SYSCALL_SYSCALL_HANDLER_HPP
diff --git a/arch/x86_64/include/arch/interrupt_handling/generic_interrupt_handler.hpp b/arch/x86_64/include/arch/interrupt_handling/generic_interrupt_handler.hpp
new file mode 100644
index 0000000..15b35c1
--- /dev/null
+++ b/arch/x86_64/include/arch/interrupt_handling/generic_interrupt_handler.hpp
@@ -0,0 +1,34 @@
+#ifndef TEACHOS_ARCH_X86_64_INTERRUPT_HANDLING_GENERIC_INTERRUPT_HANDLER_HPP
+#define TEACHOS_ARCH_X86_64_INTERRUPT_HANDLING_GENERIC_INTERRUPT_HANDLER_HPP
+
+#include <cstdint>
+
+namespace teachos::arch::interrupt_handling
+{
+ /**
+ * @brief Represents the CPU state during an interrupt.
+ *
+ * Some interrupts push an error code, while others do not. The full list
+ * of which vector number contains the error code can be found here: https://wiki.osdev.org/Exceptions
+ */
+ struct [[gnu::packed]] interrupt_frame
+ {
+ // uint64_t error_code; ///< Error code only pushed by some exceptions, therefore it is commented out.
+ uint64_t ip; ///< Instruction pointer at the time of the interrupt.
+ uint64_t cs; ///< Code segment selector indicating privilege level.
+ uint64_t flags; ///< CPU flags (RFLAGS) storing processor state.
+ uint64_t sp; ///< Stack pointer at the time of the interrupt.
+ uint64_t ss; ///< Stack segment selector, usually unused in 64-bit mode.
+ };
+
+ /**
+ * @brief Generic interrupt handler function.
+ *
+ * @param frame Pointer to the interrupt frame containing CPU state.
+ */
+ [[gnu::interrupt]]
+ auto generic_interrupt_handler(interrupt_frame * frame) -> void;
+
+} // namespace teachos::arch::interrupt_handling
+
+#endif // TEACHOS_ARCH_X86_64_INTERRUPT_HANDLING_GENERIC_INTERRUPT_HANDLER_HPP
diff --git a/arch/x86_64/include/arch/kernel/cpu/call.hpp b/arch/x86_64/include/arch/kernel/cpu/call.hpp
new file mode 100644
index 0000000..3c43304
--- /dev/null
+++ b/arch/x86_64/include/arch/kernel/cpu/call.hpp
@@ -0,0 +1,30 @@
+#ifndef TEACHOS_ARCH_X86_64_KERNEL_CPU_JMP_HPP
+#define TEACHOS_ARCH_X86_64_KERNEL_CPU_JMP_HPP
+
+#include "arch/context_switching/interrupt_descriptor_table/segment_selector.hpp"
+
+#include <cstdint>
+
+namespace teachos::arch::kernel::cpu
+{
+ /**
+ * @brief Far Pointer. Address to function located in another code segment.
+ */
+ struct far_pointer
+ {
+ void (*function)(); ///< Address of the function we want to call. (0-63)
+ context_switching::interrupt_descriptor_table::segment_selector
+ selector; ///< Segment selector pointing to the GDT entry we want to load into register CS. (64-79)
+ };
+
+ /**
+ * @brief Far call - A call to an instruction located in a different segment than the current code segment but at the
+ * same privilege level.
+ *
+ * @param pointer 64-bit operand size far pointer that we want to call.
+ */
+ auto call(far_pointer pointer) -> void;
+
+} // namespace teachos::arch::kernel::cpu
+
+#endif // TEACHOS_ARCH_X86_64_KERNEL_CPU_JMP_HPP
diff --git a/arch/x86_64/include/arch/memory/cpu/control_register.hpp b/arch/x86_64/include/arch/kernel/cpu/control_register.hpp
index e11813d..dcaf02d 100644
--- a/arch/x86_64/include/arch/memory/cpu/control_register.hpp
+++ b/arch/x86_64/include/arch/kernel/cpu/control_register.hpp
@@ -1,9 +1,9 @@
-#ifndef TEACHOS_ARCH_X86_64_MEMORY_CPU_CR3_HPP
-#define TEACHOS_ARCH_X86_64_MEMORY_CPU_CR3_HPP
+#ifndef TEACHOS_ARCH_X86_64_KERNEL_CPU_CR3_HPP
+#define TEACHOS_ARCH_X86_64_KERNEL_CPU_CR3_HPP
#include <cstdint>
-namespace teachos::arch::memory::cpu
+namespace teachos::arch::kernel::cpu
{
/**
* @brief Control registers that can be read and written to.
@@ -66,6 +66,6 @@ namespace teachos::arch::memory::cpu
*/
auto set_cr0_bit(cr0_flags flag) -> void;
-} // namespace teachos::arch::memory::cpu
+} // namespace teachos::arch::kernel::cpu
-#endif // TEACHOS_ARCH_X86_64_MEMORY_CPU_CR3_HPP
+#endif // TEACHOS_ARCH_X86_64_KERNEL_CPU_CR3_HPP
diff --git a/arch/x86_64/include/arch/kernel/cpu/gdtr.hpp b/arch/x86_64/include/arch/kernel/cpu/gdtr.hpp
new file mode 100644
index 0000000..68b950d
--- /dev/null
+++ b/arch/x86_64/include/arch/kernel/cpu/gdtr.hpp
@@ -0,0 +1,27 @@
+#ifndef TEACHOS_ARCH_X86_64_KERNEL_CPU_GDTR_HPP
+#define TEACHOS_ARCH_X86_64_KERNEL_CPU_GDTR_HPP
+
+#include "arch/context_switching/segment_descriptor_table/global_descriptor_table_pointer.hpp"
+
+#include <bitset>
+#include <cstdint>
+
+namespace teachos::arch::kernel::cpu
+{
+
+ /**
+ * @brief Returns the value in the GDTR register.
+ *
+ * @return Value of GDTR register.
+ */
+ auto store_global_descriptor_table() -> context_switching::segment_descriptor_table::global_descriptor_table_pointer;
+
+ /**
+ * @brief Loads the global_descriptor_table_pointer into the global descriptor table register (GDTR).
+ */
+ auto load_global_descriptor_table(
+ context_switching::segment_descriptor_table::global_descriptor_table_pointer const & gdt_pointer) -> void;
+
+} // namespace teachos::arch::kernel::cpu
+
+#endif // TEACHOS_ARCH_X86_64_KERNEL_CPU_GDTR_HPP
diff --git a/arch/x86_64/include/arch/kernel/cpu/idtr.hpp b/arch/x86_64/include/arch/kernel/cpu/idtr.hpp
new file mode 100644
index 0000000..cb800d0
--- /dev/null
+++ b/arch/x86_64/include/arch/kernel/cpu/idtr.hpp
@@ -0,0 +1,27 @@
+#ifndef TEACHOS_ARCH_X86_64_KERNEL_CPU_IDTR_HPP
+#define TEACHOS_ARCH_X86_64_KERNEL_CPU_IDTR_HPP
+
+#include "arch/context_switching/interrupt_descriptor_table/interrupt_descriptor_table_pointer.hpp"
+
+#include <bitset>
+#include <cstdint>
+
+namespace teachos::arch::kernel::cpu
+{
+ /**
+ * @brief Returns the value in the IDTR register.
+ *
+ * @return Value of IDTR register.
+ */
+ auto store_interrupt_descriptor_table()
+ -> context_switching::interrupt_descriptor_table::interrupt_descriptor_table_pointer;
+
+ /**
+ * @brief Loads the interrupt_descriptor_table_pointer into the interrupt descriptor table register (IDTR).
+ */
+ auto load_interrupt_descriptor_table(
+ context_switching::interrupt_descriptor_table::interrupt_descriptor_table_pointer const & idt_pointer) -> void;
+
+} // namespace teachos::arch::kernel::cpu
+
+#endif // TEACHOS_ARCH_X86_64_KERNEL_CPU_IDTR_HPP
diff --git a/arch/x86_64/include/arch/kernel/cpu/if.hpp b/arch/x86_64/include/arch/kernel/cpu/if.hpp
new file mode 100644
index 0000000..48707dc
--- /dev/null
+++ b/arch/x86_64/include/arch/kernel/cpu/if.hpp
@@ -0,0 +1,21 @@
+#ifndef TEACHOS_ARCH_X86_64_KERNEL_CPU_IF_HPP
+#define TEACHOS_ARCH_X86_64_KERNEL_CPU_IF_HPP
+
+namespace teachos::arch::kernel::cpu
+{
+ /**
+ * @brief Sets the interrupt flag (IF) in the EFLAGS register.
+ * This allows the processor to respond to maskable hardware interrupts.
+ */
+ auto set_interrupt_flag() -> void;
+
+ /**
+ * @brief Clears the interrupt flag (IF) in the EFLAGS register.
+ * This will stop the processor to respond to maskable hardware interrupts and needs to be done before changing the
+ * Interrupt Descriptor Table with lidt.
+ */
+ auto clear_interrupt_flag() -> void;
+
+} // namespace teachos::arch::kernel::cpu
+
+#endif // TEACHOS_ARCH_X86_64_KERNEL_CPU_IF_HPP
diff --git a/arch/x86_64/include/arch/memory/cpu/msr.hpp b/arch/x86_64/include/arch/kernel/cpu/msr.hpp
index cda70e2..99d6378 100644
--- a/arch/x86_64/include/arch/memory/cpu/msr.hpp
+++ b/arch/x86_64/include/arch/kernel/cpu/msr.hpp
@@ -1,10 +1,10 @@
-#ifndef TEACHOS_ARCH_X86_64_MEMORY_CPU_NXE_HPP
-#define TEACHOS_ARCH_X86_64_MEMORY_CPU_NXE_HPP
+#ifndef TEACHOS_ARCH_X86_64_KERNEL_CPU_NXE_HPP
+#define TEACHOS_ARCH_X86_64_KERNEL_CPU_NXE_HPP
#include <bitset>
#include <cstdint>
-namespace teachos::arch::memory::cpu
+namespace teachos::arch::kernel::cpu
{
/**
* @brief Important flags that can be writen into the Extended Feature Enable Register (EFER).
@@ -59,6 +59,6 @@ namespace teachos::arch::memory::cpu
*/
auto set_efer_bit(efer_flags flag) -> void;
-} // namespace teachos::arch::memory::cpu
+} // namespace teachos::arch::kernel::cpu
-#endif // TEACHOS_ARCH_X86_64_MEMORY_CPU_NXE_HPP \ No newline at end of file
+#endif // TEACHOS_ARCH_X86_64_KERNEL_CPU_NXE_HPP \ No newline at end of file
diff --git a/arch/x86_64/include/arch/kernel/cpu/segment_register.hpp b/arch/x86_64/include/arch/kernel/cpu/segment_register.hpp
new file mode 100644
index 0000000..a236452
--- /dev/null
+++ b/arch/x86_64/include/arch/kernel/cpu/segment_register.hpp
@@ -0,0 +1,97 @@
+#ifndef TEACHOS_ARCH_X86_64_KERNEL_CPU_SEGMENT_REGISTER_HPP
+#define TEACHOS_ARCH_X86_64_KERNEL_CPU_SEGMENT_REGISTER_HPP
+
+#include "arch/context_switching/interrupt_descriptor_table/segment_selector.hpp"
+
+namespace teachos::arch::kernel::cpu
+{
+ /**
+ * @brief Clear all Data Segment registers (DS / ES / FS / GS).
+ */
+ auto reload_data_segment_registers() -> void;
+
+ /**
+ * @brief Updates the value of the Data Segment Register (DS), Extra Segment Register (ES), Thread-Local Storage
+ * Registers (FS / GS).
+ *
+ * @note The Stack Segment Register (SS) value should also be updated, but the value can not be directly set in
+ * comparsion to the other registers. This is the case because the register is used for stack management and can not
+ * be directly changed, instead this has to be done by a special instruction. Therefore
+ * validate_data_segment_registers should only be called after set_code_segment_register has been called as well.
+ *
+ * @param data_segment Value that should be loaded into the registers.
+ */
+ auto set_data_segment_registers(context_switching::interrupt_descriptor_table::segment_selector data_segment) -> void;
+
+ /**
+ * @brief Returns the Segment Selector pointing to the Code Segment that has been loaded into the Code Segment
+ * Register (CS).
+ *
+ * @note The CS register can not be directly changed, instead a Far Return has to be executed to change it
+ *
+ * @return Segment Selector pointing to the currently loaded Code Segment.
+ */
+ [[gnu::section(".user_text")]]
+ auto read_code_segment_register() -> context_switching::interrupt_descriptor_table::segment_selector;
+
+ /**
+ * @brief Validates that all Data Segment Registers (DS / ES / FS / GS / SS) are the same as the given Data Segment
+ * and asserts and stops the application if they are not.
+ *
+ * @note This is only the case after set_code_segment_register has been executed as well, because it makes a far
+ * return that updates the SS register.
+ *
+ * @param data_segment Value that should be loaded into all Data Segment Registers.
+ */
+ auto validate_data_segment_registers(context_switching::interrupt_descriptor_table::segment_selector data_segment)
+ -> void;
+
+ /**
+ * @brief Validates that the Code Segment Register (CS) is the same as the given Code Segment
+ * and asserts and stops the application if they are not.
+ *
+ * @param code_segment Value that should be loaded into the Code Segment Register.
+ */
+ auto validate_code_segment_register(context_switching::interrupt_descriptor_table::segment_selector code_segment)
+ -> void;
+
+ /**
+ * @brief Simply forwards the call to validate_data_segment_registers and validate_code_segment_register and ensures
+ * that all Segment Registers, have been configured correctly.
+ *
+ * @note If all Segment Register have been set correctly the Context Switch using the set_code_segment_register method
+ * was successfull and the Privilege Level has been changed.
+ *
+ * @param data_segment Value that should be loaded into all Data Segment Registers.
+ * @param code_segment Value that should be loaded into 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;
+
+ /**
+ * @brief Sets the value of the Code Segment Register (CS), this is achieved using a Far Return.
+ *
+ * @note The Far Return used by this method, will cause the context to switch, because we are changing from the
+ * current Code Segment and it's associated Privilege Level to another Code Segment. The given method will then be
+ * called in the new context and it should be possible to call validate_segment_registers, with the same values
+ * without assertions if the switch was successful.
+ *
+ * To achieve this Far Return we call IRETQ, which expects the stack to be defined a certain way to achieve that we:
+ * 1. Push the Data Segment Selector
+ * 2. Push the current Stack Pointer
+ * 3. Push Eflags
+ * 4. Push Code Segment Selector
+ * 5. Push Return Address
+ *
+ * @param data_segment Data Segment that should be loaded into the SS register.
+ * @param code_segment Code Segment that should be loaded into the CS register.
+ * @param address Function that we want to call in the new context created by the given 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;
+
+} // namespace teachos::arch::kernel::cpu
+
+#endif // TEACHOS_ARCH_X86_64_KERNEL_CPU_SEGMENT_REGISTER_HPP
diff --git a/arch/x86_64/include/arch/memory/cpu/tlb.hpp b/arch/x86_64/include/arch/kernel/cpu/tlb.hpp
index 075d7bb..f3e58a6 100644
--- a/arch/x86_64/include/arch/memory/cpu/tlb.hpp
+++ b/arch/x86_64/include/arch/kernel/cpu/tlb.hpp
@@ -1,9 +1,9 @@
-#ifndef TEACHOS_ARCH_X86_64_MEMORY_CPU_TLB_HPP
-#define TEACHOS_ARCH_X86_64_MEMORY_CPU_TLB_HPP
+#ifndef TEACHOS_ARCH_X86_64_KERNEL_CPU_TLB_HPP
+#define TEACHOS_ARCH_X86_64_KERNEL_CPU_TLB_HPP
#include "arch/memory/paging/virtual_page.hpp"
-namespace teachos::arch::memory::cpu
+namespace teachos::arch::kernel::cpu
{
/**
* @brief Invalidates any translation lookaside buffer (TLB) entry for the page table the given address is cotained
@@ -12,7 +12,7 @@ namespace teachos::arch::memory::cpu
* @param address Memory address, which will be used to determine the contained page and flush the TLB entry for
* that page.
*/
- auto tlb_flush(paging::virtual_address address) -> void;
+ auto tlb_flush(memory::paging::virtual_address address) -> void;
/**
* @brief Invalidates the translation lookaside buffer (TLB) entry for all page tables.
@@ -22,6 +22,6 @@ namespace teachos::arch::memory::cpu
*/
auto tlb_flush_all() -> void;
-} // namespace teachos::arch::memory::cpu
+} // namespace teachos::arch::kernel::cpu
-#endif // TEACHOS_ARCH_X86_64_MEMORY_CPU_TLB_HPP
+#endif // TEACHOS_ARCH_X86_64_KERNEL_CPU_TLB_HPP
diff --git a/arch/x86_64/include/arch/kernel/cpu/tr.hpp b/arch/x86_64/include/arch/kernel/cpu/tr.hpp
new file mode 100644
index 0000000..7c856f1
--- /dev/null
+++ b/arch/x86_64/include/arch/kernel/cpu/tr.hpp
@@ -0,0 +1,24 @@
+#ifndef TEACHOS_ARCH_X86_64_KERNEL_CPU_TR_HPP
+#define TEACHOS_ARCH_X86_64_KERNEL_CPU_TR_HPP
+
+#include <bitset>
+#include <cstdint>
+
+namespace teachos::arch::kernel::cpu
+{
+
+ /**
+ * @brief Returns the value in the LTR register.
+ *
+ * @return Value of LTR register.
+ */
+ auto store_task_register() -> uint16_t;
+
+ /**
+ * @brief Loads the gdt offset to the tss segment descriptor into the task register (TR).
+ */
+ auto load_task_register(uint16_t gdt_offset) -> void;
+
+} // namespace teachos::arch::kernel::cpu
+
+#endif // TEACHOS_ARCH_X86_64_KERNEL_CPU_TR_HPP
diff --git a/arch/x86_64/include/arch/memory/allocator/area_frame_allocator.hpp b/arch/x86_64/include/arch/memory/allocator/area_frame_allocator.hpp
index b8370db..6cb5f56 100644
--- a/arch/x86_64/include/arch/memory/allocator/area_frame_allocator.hpp
+++ b/arch/x86_64/include/arch/memory/allocator/area_frame_allocator.hpp
@@ -15,9 +15,9 @@ namespace teachos::arch::memory::allocator
struct area_frame_allocator
{
/**
- * @brief Constructor
+ * @brief Constructor.
*
- * @param mem_info Structure containg all relevant information to map and allocate memory
+ * @param mem_info Structure containg all relevant information to map and allocate memory.
*/
area_frame_allocator(multiboot::memory_information const & mem_info);
@@ -55,7 +55,7 @@ namespace teachos::arch::memory::allocator
physical_frame next_free_frame; ///< The physical_frame after the last allocated one.
std::optional<multiboot::memory_area> current_area; ///< The current memory area.
multiboot::memory_area_container const
- memory_areas; ///< All memory areas in custom container allows to use std::ranges
+ memory_areas; ///< All memory areas in custom container allows to use std::ranges.
physical_frame const kernel_start; ///< The start address of the kernel code in memory.
physical_frame const kernel_end; ///< The end address of the kernel code in memory.
physical_frame const multiboot_start; ///< The start address of the multiboot code in memory.
diff --git a/arch/x86_64/include/arch/memory/allocator/physical_frame.hpp b/arch/x86_64/include/arch/memory/allocator/physical_frame.hpp
index 7f04042..cb6c5b3 100644
--- a/arch/x86_64/include/arch/memory/allocator/physical_frame.hpp
+++ b/arch/x86_64/include/arch/memory/allocator/physical_frame.hpp
@@ -1,8 +1,8 @@
#ifndef TEACHOS_ARCH_X86_64_MEMORY_ALLOCATOR_PHYSICAL_FRAME_HPP
#define TEACHOS_ARCH_X86_64_MEMORY_ALLOCATOR_PHYSICAL_FRAME_HPP
-#include "arch/shared/container.hpp"
-#include "arch/shared/forward_value_iterator.hpp"
+#include "arch/stl/container.hpp"
+#include "arch/stl/forward_value_iterator.hpp"
#include <compare>
#include <cstdint>
@@ -10,7 +10,7 @@
namespace teachos::arch::memory::allocator
{
- typedef std::size_t physical_address;
+ using physical_address = std::size_t;
std::size_t constexpr PAGE_FRAME_SIZE = 4096U; ///< Default page size of x86_84 is always 4KiB.
@@ -79,7 +79,7 @@ namespace teachos::arch::memory::allocator
{}; ///< Index number of the current physical frame, used to distinguish it from other frames.
};
- typedef shared::container<shared::forward_value_iterator<physical_frame>> frame_container;
+ using frame_container = stl::container<stl::forward_value_iterator<physical_frame>>;
} // namespace teachos::arch::memory::allocator
diff --git a/arch/x86_64/include/arch/memory/heap/global_heap_allocator.hpp b/arch/x86_64/include/arch/memory/heap/global_heap_allocator.hpp
index dff837e..c98c130 100644
--- a/arch/x86_64/include/arch/memory/heap/global_heap_allocator.hpp
+++ b/arch/x86_64/include/arch/memory/heap/global_heap_allocator.hpp
@@ -2,6 +2,7 @@
#define TEACHOS_ARCH_X86_64_MEMORY_HEAP_GLOBAL_HEAP_ALLOCATOR_HPP
#include "arch/memory/heap/heap_allocator.hpp"
+#include "arch/memory/heap/user_heap_allocator.hpp"
namespace teachos::arch::memory::heap
{
@@ -16,7 +17,7 @@ namespace teachos::arch::memory::heap
BUMP, ///< Use the bump allocator as the heap allocation implementation, be aware that using this allocator leaks
///< memory, because there is no delete implementation
LINKED_LIST ///< Use the linked list allocator as the heap implementation, recommended because it does not leak
- ///< memory and
+ ///< memory
};
/**
@@ -45,7 +46,7 @@ namespace teachos::arch::memory::heap
* @param size Amount of bytes that should be allocated
* @return void* Pointer to the start of the allocatable memory area
*/
- static auto allocate(std::size_t size) -> void *;
+ static auto kmalloc(std::size_t size) -> void *;
/**
* @brief Deallocated all memory associated with the memory area starting from the given pointer address.
@@ -53,30 +54,65 @@ namespace teachos::arch::memory::heap
*
* @param pointer Previously allocated memory area, that should now be freed
*/
- static auto deallocate(void * pointer) noexcept -> void;
+ static auto kfree(void * pointer) noexcept -> void;
+
+ /**
+ * @brief Allocates the given amount of memory and returns the pointer to the start of the allocatable memory area.
+ * Simply forwards the call to the allocate method of the registered heap_allocation implementation
+ *
+ * @param size Amount of bytes that should be allocated
+ * @return void* Pointer to the start of the allocatable memory area
+ */
+ [[gnu::section(".user_text")]]
+ static auto malloc(std::size_t size) -> void *;
+
+ /**
+ * @brief Deallocated all memory associated with the memory area starting from the given pointer address.
+ * Simply forwards the call to the deallocate method of the registered heap_allocation implementation
+ *
+ * @param pointer Previously allocated memory area, that should now be freed
+ */
+ [[gnu::section(".user_text")]]
+ static auto free(void * pointer) noexcept -> void;
private:
- static heap_allocator * allocator_instance; ///< Instance used to actually allocate and deallocate
+ static heap_allocator * kernel_allocator_instance; ///< Instance used to allocate and deallocate kernel heap memory
+ [[gnu::section(".user_data")]] static user_heap_allocator *
+ user_allocator_instance; ///< Instance used to allocate and deallocate user heap memory
+
+ /**
+ * @brief Either returns the previously registered heap allocated or halts further execution
+ *
+ * @return Reference to the registered kernel heap allocation
+ */
+ static auto kernel() -> heap_allocator &;
/**
- * @brief Either returns the previously registered heap allocated or halts furthere execution
+ * @brief Either returns the previously registered heap allocated or halts further execution
*
- * @return Reference to the registered heap allocation
+ * @return Reference to the registered user heap allocation
*/
- static auto get() -> heap_allocator &;
+ [[gnu::section(".user_text")]]
+ static auto user() -> user_heap_allocator &;
};
} // namespace teachos::arch::memory::heap
+[[gnu::section(".user_text")]]
auto operator new(std::size_t size) -> void *;
+[[gnu::section(".user_text")]]
auto operator delete(void * pointer) noexcept -> void;
+[[gnu::section(".user_text")]]
auto operator delete(void * pointer, std::size_t size) noexcept -> void;
+[[gnu::section(".user_text")]]
auto operator new[](std::size_t size) -> void *;
+[[gnu::section(".user_text")]]
auto operator delete[](void * pointer) noexcept -> void;
+[[gnu::section(".user_text")]]
auto operator delete[](void * pointer, std::size_t size) noexcept -> void;
#endif // TEACHOS_ARCH_X86_64_MEMORY_HEAP_GLOBAL_HEAP_ALLOCATOR_HPP
diff --git a/arch/x86_64/include/arch/memory/heap/heap_allocator.hpp b/arch/x86_64/include/arch/memory/heap/heap_allocator.hpp
index 6aed3d8..420a1d3 100644
--- a/arch/x86_64/include/arch/memory/heap/heap_allocator.hpp
+++ b/arch/x86_64/include/arch/memory/heap/heap_allocator.hpp
@@ -5,8 +5,10 @@
namespace teachos::arch::memory::heap
{
- std::size_t constexpr HEAP_START = 0x100000000;
- std::size_t constexpr HEAP_SIZE = 100 * 1024;
+ std::size_t constexpr KERNEL_HEAP_START = 0x100000000;
+ std::size_t constexpr KERNEL_HEAP_SIZE = 100 * 1024;
+ std::size_t constexpr USER_HEAP_START = 0x100019000; // Starts directly after kernel heap
+ std::size_t constexpr USER_HEAP_SIZE = 100 * 1024;
/**
* @brief Heap allocator interface containing methods required to allocate and deallocate heap memory areas
diff --git a/arch/x86_64/include/arch/memory/heap/linked_list_allocator.hpp b/arch/x86_64/include/arch/memory/heap/linked_list_allocator.hpp
index df9e370..582b4af 100644
--- a/arch/x86_64/include/arch/memory/heap/linked_list_allocator.hpp
+++ b/arch/x86_64/include/arch/memory/heap/linked_list_allocator.hpp
@@ -3,7 +3,7 @@
#include "arch/memory/heap/heap_allocator.hpp"
#include "arch/memory/heap/memory_block.hpp"
-#include "arch/shared/mutex.hpp"
+#include "arch/stl/mutex.hpp"
namespace teachos::arch::memory::heap
{
@@ -111,14 +111,9 @@ namespace teachos::arch::memory::heap
auto coalesce_free_memory_block(memory_block * previous_block, memory_block * current_block, void * pointer,
std::size_t size) -> void;
- std::size_t heap_start; ///< Start of the allocatable heap area.
- std::size_t heap_end; ///< End of the allocatable heap area.
- memory_block * first; ///< First free entry in our memory.
- shared::mutex mutex; ///< Mutex to ensure only one thread calls allocate or deallocate at once.
+ memory_block * first; ///< First free entry in our memory.
+ stl::mutex mutex; ///< Mutex to ensure only one thread calls allocate or deallocate at once.
};
-
- extern linked_list_allocator kernel_heap;
-
} // namespace teachos::arch::memory::heap
#endif // TEACHOS_ARCH_X86_64_MEMORY_HEAP_LINKED_LIST_ALLOCATOR_HPP
diff --git a/arch/x86_64/include/arch/memory/heap/memory_block.hpp b/arch/x86_64/include/arch/memory/heap/memory_block.hpp
index e1cd288..9d1fb02 100644
--- a/arch/x86_64/include/arch/memory/heap/memory_block.hpp
+++ b/arch/x86_64/include/arch/memory/heap/memory_block.hpp
@@ -18,6 +18,7 @@ namespace teachos::arch::memory::heap
* @param size Amount of free memory of this specific hole.
* @param next Optional pointer to the next free memory.
*/
+ [[gnu::section(".user_text")]]
memory_block(std::size_t size, memory_block * next);
/**
@@ -26,6 +27,7 @@ namespace teachos::arch::memory::heap
* @note Used so the memory can be reused to construct other classes into, without having the old values.
* Required because we cannot call delete, because it causes "undefined reference to `sbrk`".
*/
+ [[gnu::section(".user_text")]]
~memory_block();
std::size_t size; ///< Amount of free memory this hole contains, has to always be atleast 16 bytes to hold the
diff --git a/arch/x86_64/include/arch/memory/heap/user_heap_allocator.hpp b/arch/x86_64/include/arch/memory/heap/user_heap_allocator.hpp
new file mode 100644
index 0000000..6b1b7bb
--- /dev/null
+++ b/arch/x86_64/include/arch/memory/heap/user_heap_allocator.hpp
@@ -0,0 +1,148 @@
+#ifndef TEACHOS_ARCH_X86_64_MEMORY_HEAP_USER_HEAP_ALLOCATOR_HPP
+#define TEACHOS_ARCH_X86_64_MEMORY_HEAP_USER_HEAP_ALLOCATOR_HPP
+
+#include "arch/memory/heap/memory_block.hpp"
+#include "arch/stl/mutex.hpp"
+
+#include <optional>
+
+namespace teachos::arch::memory::heap
+{
+ /**
+ * @brief Sorted by address list of memory holes (free memory). Uses free holes itself to save the information,
+ * containing the size and pointer to the next hole. Resulting in a singly linked list.
+ */
+ struct user_heap_allocator
+ {
+ /**
+ * @brief Constructor.
+ */
+ user_heap_allocator() = default;
+
+ /**
+ * @copybrief heap_allocator::allocate
+ *
+ * @note The specified size is used to find a free memory block with the exact same size, meaning we can remove that
+ * free memory block from the free list and simply return its address. Or it has to be big enough to hold the size
+ * and alteast enough memory for another free memory block entry (16 bytes). If the amount of memory of that free
+ * memory block is in between we cannot use it for our allocation, because we could only return it to the user, but
+ * the additional bytes, could not be used to create a free memory block. Additionaly the user couldn't know
+ * they received more memory than wanted. Therefore the memory would simply be unused and because it is neither
+ * allocated nor deallocated would never be indexed by the free memory list. We would therefore permanently loose
+ * that memory, to prevent that allocation into free memory blocks like that are impossible.
+ */
+ [[gnu::section(".user_text")]]
+ auto allocate(std::size_t size) -> void *;
+
+ /**
+ * @copybrief heap_allocator::deallocate
+ */
+ [[gnu::section(".user_text")]]
+ auto deallocate(void * pointer) noexcept -> void;
+
+ private:
+ /**
+ * @brief Returns the smallest allocatable block of heap memory.
+ *
+ * @return Smallest allocatable block of heap memory.
+ */
+ [[gnu::section(".user_text")]] auto constexpr min_allocatable_size() -> std::size_t { return sizeof(memory_block); }
+
+ /**
+ * @brief Checks if the given memory block is big enough and if it is allocates into the current block.
+ *
+ * @note Adjusts the link of the previous memory block to the new smaller remaining block. If the allocation used
+ * the complete block instead the previous block will point to the next block of the current memroy block that was
+ * used for the allocation.
+ *
+ * @return Allocated usable memory area.
+ */
+ [[gnu::section(".user_text")]] auto
+ allocate_into_memory_block_if_big_enough(memory_block * current, memory_block * previous, std::size_t total_size)
+ -> std::optional<void *>;
+
+ /**
+ * @brief Special functionality fo the user heap allocator. Which will result in it being expanded by a syscall with
+ * addtionally 100 KiB, which are mapped into the page table. Will always work until there is no physical memory
+ * left.
+ *
+ * @return Start of the newly with syscall allocated free memory block. Nullptr if the syscall failed.
+ */
+ [[gnu::section(".user_text")]] auto expand_heap_if_full() -> memory_block *;
+
+ /**
+ * @brief Removes a free memory block from the free list and returns its address so the caller can allocate into it.
+ *
+ * @param previous_block Free memory block before the block to allocate in our heap memory. Was to small to
+ * allocate the required size into.
+ * @param current_block Free memory block we want to remove from the free list and return for the allocation.
+ *
+ * @return Previous start address of the memory block we removed, because it can now be used for the allocation.
+ */
+ [[gnu::section(".user_text")]]
+ auto remove_free_memory_block(memory_block * previous_block, memory_block * current_block) -> void *;
+
+ /**
+ * @brief Splits the given free memory block into two, where the latter block keeps being free and the first
+ * part will be used for the allocation.
+ *
+ * @param previous_block Free memory block before the block to allocate in our heap memory. Was to small to
+ * allocate the required size into.
+ * @param current_block Free memory block we want to split into a size part for the allocation and the rest for
+ * future allocations.
+ * @param size Size we want to allocate at the start of the free memory block.
+ *
+ * @return Previous start address of the memory block we just split, because it can now be used for the allocation.
+ */
+ [[gnu::section(".user_text")]]
+ auto split_free_memory_block(memory_block * previous_block, memory_block * current_block, std::size_t size)
+ -> void *;
+
+ /**
+ * @brief Removes a free memory block from the free list and returns its address so the caller can allocate into it.
+ *
+ * @param previous_block Free memory block before the block to allocate in our heap memory. Was to small to
+ * allocate the required size into.
+ * @param current_block Free memory block we want to remove from the free list and return for the allocation.
+ * @param new_block Replaces the current block with the given new block can be nullptr, meaning the free list will
+ * end here.
+ *
+ * @return Previous start address of the memory block we removed, because it can now be used for the allocation.
+ */
+ [[gnu::section(".user_text")]]
+ auto replace_free_memory_block(memory_block * previous_block, memory_block * current_block,
+ memory_block * new_block) -> void *;
+
+ /**
+ * @brief Combines multiple free memory blocks into one if they are adjacent.
+ *
+ * @note The internal algorithm for recombination functions like this:
+ * 1. Check if there is even any memory left, if not the first entry of our linked list should be a nullptr and
+ * we can therefore set the first entry to our newly created entry. This entry is created in the now deallocated
+ * memory area.
+ * 2. If there are more blocks but neither the previous nor the current block are adjacent, we simply create a
+ * new free memory block of the given size and set the previous next to our block and the next of our block to
+ * the current block.
+ * 3. If the current block is adjacent the start address of the newly created block stays the same, but the size
+ * increases by the amount in the current memory block header. After reading it we also clear the header.
+ * 4. If the previous block is adjacent the size of the previous block simply increases to include the given
+ * size as well.
+ * 5. If the previous block is directly in our start address, so they overlap then it has to mean some or all of
+ * the region we are trying to deallocate has been freed before. Which would result in a double free therefore
+ * we halt the execution of the program.
+ *
+ * @param previous_block Free memory block before the block to deallocate in our heap memory.
+ * @param current_block Free memory block after the block to deallocate in our heap memory.
+ * @param pointer Block to deallocate.
+ * @param size Size of the block we want to deallocate.
+ */
+ [[gnu::section(".user_text")]]
+ auto coalesce_free_memory_block(memory_block * previous_block, memory_block * current_block, void * pointer,
+ std::size_t size) -> void;
+
+ memory_block * first = {}; ///< First free entry in our memory.
+ stl::mutex mutex = {}; ///< Mutex to ensure only one thread calls allocate or deallocate at once.
+ };
+} // namespace teachos::arch::memory::heap
+
+#endif // TEACHOS_ARCH_X86_64_MEMORY_HEAP_USER_HEAP_ALLOCATOR_HPP
diff --git a/arch/x86_64/include/arch/memory/main.hpp b/arch/x86_64/include/arch/memory/main.hpp
index 164abbc..d51815f 100644
--- a/arch/x86_64/include/arch/memory/main.hpp
+++ b/arch/x86_64/include/arch/memory/main.hpp
@@ -1,8 +1,22 @@
#ifndef TEACHOS_ARCH_X86_64_MEMORY_MAIN_HPP
#define TEACHOS_ARCH_X86_64_MEMORY_MAIN_HPP
+#include "arch/memory/paging/page_entry.hpp"
+
+#include <cstdint>
+
namespace teachos::arch::memory
{
+
+ /**
+ * @brief Maps a heap section to a page.
+ *
+ * @param heap_start Start-address of the heap.
+ * @param heap_size Size of the heap.
+ * @param additional_flags Additional flags to apply to the page entry.
+ */
+ auto remap_heap(std::size_t heap_start, std::size_t heap_size, paging::entry::bitset additional_flags) -> void;
+
/**
* @brief Initializes memory management.
*
diff --git a/arch/x86_64/include/arch/memory/multiboot/elf_symbols_section.hpp b/arch/x86_64/include/arch/memory/multiboot/elf_symbols_section.hpp
index e8f6b0a..0a25ca9 100644
--- a/arch/x86_64/include/arch/memory/multiboot/elf_symbols_section.hpp
+++ b/arch/x86_64/include/arch/memory/multiboot/elf_symbols_section.hpp
@@ -2,8 +2,8 @@
#define TEACHOS_ARCH_X86_64_MEMORY_MULTIBOOT_ELF_SYBOLS_SECTION_HPP
#include "arch/memory/multiboot/info.hpp"
-#include "arch/shared/container.hpp"
-#include "arch/shared/contiguous_pointer_iterator.hpp"
+#include "arch/stl/container.hpp"
+#include "arch/stl/contiguous_pointer_iterator.hpp"
#include <bitset>
#include <cstdint>
@@ -20,7 +20,7 @@ namespace teachos::arch::memory::multiboot
INACTIVE, ///< (SHT_NULL) Unused, meaning all values are zeroed out.
PROGRAMM, ///< (SHT_PROGBITS) Program data (DATA, CODE).
SYMBOL_TABLE, ///< (SHT_SYMBTAB) Contains actual entries pointed to in symbol hash table.
- STRING_TABLE, ///< (SHT_STRTAB) Contains symbols, section and deubbging null-terminated strings.
+ STRING_TABLE, ///< (SHT_STRTAB) Contains symbols, section and debugging null-terminated strings.
RELOCATION_ENTRY_WITH_ADDENDS, ///< (SHT_RELA) Only used on 64 bit systems.
SYMBOL_HASH_TABLE, ///< (SHT_HASH) Hash table used by dynamic linker to locate symbols.
DYNAMIC, ///< (SHT_DYNAMIC) Contains dynamic linking information.
@@ -162,7 +162,7 @@ namespace teachos::arch::memory::multiboot
///< contained in the section, to ensure byte alignment is actually 4 byte.
};
- typedef shared::container<shared::contiguous_pointer_iterator<elf_section_header>> elf_section_header_container;
+ using elf_section_header_container = stl::container<stl::contiguous_pointer_iterator<elf_section_header>>;
} // namespace teachos::arch::memory::multiboot
diff --git a/arch/x86_64/include/arch/memory/multiboot/memory_map.hpp b/arch/x86_64/include/arch/memory/multiboot/memory_map.hpp
index c28c986..68394c8 100644
--- a/arch/x86_64/include/arch/memory/multiboot/memory_map.hpp
+++ b/arch/x86_64/include/arch/memory/multiboot/memory_map.hpp
@@ -2,8 +2,8 @@
#define TEACHOS_ARCH_X86_64_MEMORY_MULTIBOOT_MEMORY_MAP_HPP
#include "arch/memory/multiboot/info.hpp"
-#include "arch/shared/container.hpp"
-#include "arch/shared/contiguous_pointer_iterator.hpp"
+#include "arch/stl/container.hpp"
+#include "arch/stl/contiguous_pointer_iterator.hpp"
#include <cstdint>
@@ -46,7 +46,7 @@ namespace teachos::arch::memory::multiboot
struct memory_area entries; ///< Specific memory regions.
};
- typedef shared::container<shared::contiguous_pointer_iterator<memory_area>> memory_area_container;
+ using memory_area_container = stl::container<stl::contiguous_pointer_iterator<memory_area>>;
} // namespace teachos::arch::memory::multiboot
diff --git a/arch/x86_64/include/arch/memory/paging/active_page_table.hpp b/arch/x86_64/include/arch/memory/paging/active_page_table.hpp
index 1b2aaed..f68d8b6 100644
--- a/arch/x86_64/include/arch/memory/paging/active_page_table.hpp
+++ b/arch/x86_64/include/arch/memory/paging/active_page_table.hpp
@@ -2,8 +2,8 @@
#define TEACHOS_ARCH_X86_64_MEMORY_PAGING_ACTIVE_PAGE_TABLE_HPP
#include "arch/exception_handling/assert.hpp"
+#include "arch/kernel/cpu/tlb.hpp"
#include "arch/memory/allocator/concept.hpp"
-#include "arch/memory/cpu/tlb.hpp"
#include "arch/memory/paging/virtual_page.hpp"
#include <array>
@@ -75,14 +75,14 @@ namespace teachos::arch::memory::paging
* @param flags A bitset of flags that configure the page table entry for this mapping.
*/
template<allocator::FrameAllocator T>
- auto map_page_to_frame(T & allocator, virtual_page page, allocator::physical_frame frame,
- std::bitset<64U> flags) -> void
+ auto map_page_to_frame(T & allocator, virtual_page page, allocator::physical_frame frame, std::bitset<64U> flags)
+ -> void
{
auto current_handle = active_handle;
for (auto level = page_table_handle::LEVEL4; level != page_table_handle::LEVEL1; --level)
{
- current_handle = current_handle.next_table_or_create(allocator, page.get_level_index(level));
+ current_handle = current_handle.next_table_or_create(allocator, page.get_level_index(level), flags);
}
auto & level1_entry = current_handle[page.get_level_index(page_table_handle::LEVEL1)];
@@ -153,7 +153,7 @@ namespace teachos::arch::memory::paging
}
unmap_page_table_entry(allocator, page, current_handle);
- cpu::tlb_flush(page.start_address());
+ kernel::cpu::tlb_flush(page.start_address());
}
private:
diff --git a/arch/x86_64/include/arch/memory/paging/kernel_mapper.hpp b/arch/x86_64/include/arch/memory/paging/kernel_mapper.hpp
index 74f1c14..81ac0cb 100644
--- a/arch/x86_64/include/arch/memory/paging/kernel_mapper.hpp
+++ b/arch/x86_64/include/arch/memory/paging/kernel_mapper.hpp
@@ -1,12 +1,15 @@
#ifndef TEACHOS_ARCH_X86_64_MEMORY_PAGING_KERNEL_MAPPER_HPP
#define TEACHOS_ARCH_X86_64_MEMORY_PAGING_KERNEL_MAPPER_HPP
-#include "arch/memory/cpu/control_register.hpp"
+#include "arch/kernel/cpu/control_register.hpp"
#include "arch/memory/paging/active_page_table.hpp"
#include "arch/memory/paging/inactive_page_table.hpp"
#include "arch/memory/paging/temporary_page.hpp"
#include "arch/video/vga/text.hpp"
+#include <algorithm>
+#include <array>
+
namespace teachos::arch::memory::paging
{
/**
@@ -38,10 +41,14 @@ namespace teachos::arch::memory::paging
* inactive page table, that is not used by the CPU to ensure we are not changign memory that we are using. Because
* remapping active kernel memory in the kernel wouldn't work.
*/
- auto remap_kernel() -> active_page_table &
+ auto remap_kernel() -> void
{
+ // Set Page Global Enable bit
+ auto cr4 = kernel::cpu::read_control_register(kernel::cpu::control_register::CR4);
+ kernel::cpu::write_control_register(kernel::cpu::control_register::CR4, cr4 | 0x80);
+
temporary_page temporary_page{virtual_page{0xCAFEBABE}, allocator};
- auto & active_table = active_page_table::create_or_get();
+ decltype(auto) active_table = active_page_table::create_or_get();
auto const frame = allocator.allocate_frame();
exception_handling::assert(frame.has_value(),
"[Kernel Mapper] Frame could not be allocated and therefore kernel not mapped");
@@ -53,7 +60,6 @@ namespace teachos::arch::memory::paging
auto const old_level_4_page =
virtual_page::containing_address(old_table.page_table_level_4_frame.start_address());
active_table.unmap_page(allocator, old_level_4_page);
- return active_table;
}
private:
@@ -77,16 +83,16 @@ namespace teachos::arch::memory::paging
auto remap_elf_kernel_sections(inactive_page_table & inactive_table, temporary_page & temporary_page,
active_page_table & active_table) -> void
{
- auto const backup =
- allocator::physical_frame::containing_address(cpu::read_control_register(cpu::control_register::CR3));
+ auto const backup = allocator::physical_frame::containing_address(
+ kernel::cpu::read_control_register(kernel::cpu::control_register::CR3));
auto page_table_level4 = temporary_page.map_table_frame(backup, active_table);
active_table[511].set_entry(inactive_table.page_table_level_4_frame, entry::PRESENT | entry::WRITABLE);
- cpu::tlb_flush_all();
+ kernel::cpu::tlb_flush_all();
map_elf_kernel_sections(active_table);
page_table_level4[511].set_entry(backup, entry::PRESENT | entry::WRITABLE);
- cpu::tlb_flush_all();
+ kernel::cpu::tlb_flush_all();
temporary_page.unmap_page(active_table);
}
@@ -99,12 +105,12 @@ namespace teachos::arch::memory::paging
*/
auto switch_active_page_table(inactive_page_table new_table) -> inactive_page_table
{
- auto const backup =
- allocator::physical_frame::containing_address(cpu::read_control_register(cpu::control_register::CR3));
+ auto const backup = allocator::physical_frame::containing_address(
+ kernel::cpu::read_control_register(kernel::cpu::control_register::CR3));
auto const old_table = inactive_page_table{backup};
auto const new_address = new_table.page_table_level_4_frame.start_address();
- cpu::write_control_register(cpu::control_register::CR3, new_address);
+ kernel::cpu::write_control_register(kernel::cpu::control_register::CR3, new_address);
return old_table;
}
@@ -118,6 +124,16 @@ namespace teachos::arch::memory::paging
auto map_elf_kernel_sections(active_page_table & active_table) -> void
{
exception_handling::assert(!mem_info.sections.empty(), "[Kernel Mapper] Kernel elf sections empty");
+ std::array<uint64_t, 6U> constexpr USER_SECTION_BASES = {
+ 0x102000, // .boot_bss (Contains statically allocated variables)
+ 0x209000, // .stl_text (Contains code for custom std implementations and standard library code)
+ 0x218000, // .user_text (Contains the actual user code executed)
+ 0x21F000, // .user_data (Contains static user variables)
+
+ 0x20A000 // .text (Necessary, because symbols for all template standard library features are placed here if
+ // they were first used in the Kernel Code Section)
+ };
+
for (auto const & section : mem_info.sections)
{
if (!section.flags.contains_flags(multiboot::elf_section_flags::OCCUPIES_MEMORY))
@@ -136,7 +152,12 @@ namespace teachos::arch::memory::paging
allocator::frame_container::iterator const begin{start_frame};
allocator::frame_container::iterator const end{end_frame};
allocator::frame_container const frames{begin, end};
- entry const entry{section.flags};
+ entry entry{section.flags};
+
+ if (std::ranges::find(USER_SECTION_BASES, section.physical_address) != USER_SECTION_BASES.end())
+ {
+ entry.set_user_accessible();
+ }
for (auto const & frame : frames)
{
diff --git a/arch/x86_64/include/arch/memory/paging/page_entry.hpp b/arch/x86_64/include/arch/memory/paging/page_entry.hpp
index 1de31b5..8147c5c 100644
--- a/arch/x86_64/include/arch/memory/paging/page_entry.hpp
+++ b/arch/x86_64/include/arch/memory/paging/page_entry.hpp
@@ -22,7 +22,7 @@ namespace teachos::arch::memory::paging
PRESENT = 1UL << 0UL, ///< Page is in memory and therefore present.
///< is assumed to be READONLY and only that flag is shown in the objdump.
WRITABLE = 1UL << 1UL, ///< It is possible to write to the page.
- USER_ACCESIBLE = 1UL << 2UL, ///< Page can be accessed in user mode instead of only in kernel mode code.
+ USER_ACCESSIBLE = 1UL << 2UL, ///< Page can be accessed in user mode instead of only in kernel mode code.
WRITE_THROUGH_CACHING = 1UL << 3UL, ///< Write to the page go directly to memory instead of the cache.
DISABLED_CACHING = 1UL << 4UL, ///< Page uses caching.
ACCESSED = 1UL << 5UL, ///< Page is currently in use.
@@ -72,6 +72,12 @@ namespace teachos::arch::memory::paging
auto set_unused() -> void;
/**
+ * @brief Marks the page as accessible in User mode, meaning the underlying std::bitset has the 2nd bit aditonally
+ * set.
+ */
+ auto set_user_accessible() -> void;
+
+ /**
* @brief Calculates the physical frame this entry is pointing too, can be null if the page is not present in
* memory.
*
diff --git a/arch/x86_64/include/arch/memory/paging/page_table.hpp b/arch/x86_64/include/arch/memory/paging/page_table.hpp
index 60a0a2f..b972337 100644
--- a/arch/x86_64/include/arch/memory/paging/page_table.hpp
+++ b/arch/x86_64/include/arch/memory/paging/page_table.hpp
@@ -81,9 +81,10 @@ namespace teachos::arch::memory::paging
* @param allocator Reference to an allocator following the FrameAllocator concept, which is used to allocate
* entries when a new page table is required.
* @param table_index Index of this page table in the page table one level lower.
+ * @param flags A bitset of flags that configure the page table entry for this mapping.
*/
template<allocator::FrameAllocator T>
- auto next_table_or_create(T & allocator, std::size_t table_index) -> page_table_handle
+ auto next_table_or_create(T & allocator, std::size_t table_index, std::bitset<64U> flags) -> page_table_handle
{
auto next_handle = next_table(table_index);
// If the next table method failed then it means that the page level of the frame we want allocate has not yet
@@ -100,6 +101,14 @@ namespace teachos::arch::memory::paging
exception_handling::assert(next_handle.has_value(), "[Page mapper] Unable to create new entry into page table");
next_handle.value().zero_entries();
}
+
+ // Check if the now created or previously created level 4, level 3 or level 2 page entry is used by user
+ // accessible code. If it is that page entry needs to be user accesible as well.
+ entry entry{flags.to_ulong()};
+ if (entry.contains_flags(entry::USER_ACCESSIBLE))
+ {
+ this->operator[](table_index).set_user_accessible();
+ }
return next_handle.value();
}
diff --git a/arch/x86_64/include/arch/memory/paging/virtual_page.hpp b/arch/x86_64/include/arch/memory/paging/virtual_page.hpp
index d820e82..a6c8c39 100644
--- a/arch/x86_64/include/arch/memory/paging/virtual_page.hpp
+++ b/arch/x86_64/include/arch/memory/paging/virtual_page.hpp
@@ -10,7 +10,7 @@
namespace teachos::arch::memory::paging
{
- typedef std::size_t virtual_address;
+ using virtual_address = std::size_t;
/**
* @brief Virtual page entry contained in P1 page tables
@@ -84,7 +84,7 @@ namespace teachos::arch::memory::paging
{}; ///< Index number of the current virtual page, used to distinguish it from other pages.
};
- typedef shared::container<shared::forward_value_iterator<virtual_page>> page_container;
+ using page_container = stl::container<stl::forward_value_iterator<virtual_page>>;
} // namespace teachos::arch::memory::paging
diff --git a/arch/x86_64/include/arch/shared/container.hpp b/arch/x86_64/include/arch/stl/container.hpp
index f2fd1dc..4ea08c7 100644
--- a/arch/x86_64/include/arch/shared/container.hpp
+++ b/arch/x86_64/include/arch/stl/container.hpp
@@ -1,9 +1,9 @@
-#ifndef TEACHOS_ARCH_X86_64_SHARED_CONTAINER_HPP
-#define TEACHOS_ARCH_X86_64_SHARED_CONTAINER_HPP
+#ifndef TEACHOS_ARCH_X86_64_STL_CONTAINER_HPP
+#define TEACHOS_ARCH_X86_64_STL_CONTAINER_HPP
#include <iterator>
-namespace teachos::arch::shared
+namespace teachos::arch::stl
{
/**
* @brief Minimal iterator concept required for usage in container
@@ -48,7 +48,11 @@ namespace teachos::arch::shared
*
* @return Iterator pointing to first element of the memory area.
*/
- auto begin() const -> iterator { return begin_itr; }
+ [[gnu::section(".stl_text")]]
+ auto begin() const -> iterator
+ {
+ return begin_itr;
+ }
/**
* @brief Returns the iterator pointing to one past the last element of the memory area.
@@ -56,7 +60,11 @@ namespace teachos::arch::shared
*
* @return Iterator pointing to one past the last element of the memory area.
*/
- auto end() const -> iterator { return end_itr; }
+ [[gnu::section(".stl_text")]]
+ auto end() const -> iterator
+ {
+ return end_itr;
+ }
/**
* @brief Calculates the size of this container, simply subtracts the iterator pointing to the first element by the
@@ -64,20 +72,28 @@ namespace teachos::arch::shared
*
* @return Actual size of this container.
*/
- auto size() const -> size_type { return std::distance(begin(), end()); }
+ [[gnu::section(".stl_text")]]
+ auto size() const -> size_type
+ {
+ return std::distance(begin(), end());
+ }
/**
* @brief Calcualtes the size and returns true if the size is 0 and the container therefore emtpy.
*
* @return Whether the container is empty, size being 0 or not
*/
- auto empty() const -> bool { return size() == 0; }
+ [[gnu::section(".stl_text")]]
+ auto empty() const -> bool
+ {
+ return size() == 0;
+ }
private:
iterator begin_itr = {}; ///< Pointer to the first element of the given template type.
iterator end_itr = {}; ///< Pointer to one pas the last element of the given template type.
};
-} // namespace teachos::arch::shared
+} // namespace teachos::arch::stl
-#endif // TEACHOS_ARCH_X86_64_SHARED_CONTAINER_HPP
+#endif // TEACHOS_ARCH_X86_64_STL_CONTAINER_HPP
diff --git a/arch/x86_64/include/arch/shared/contiguous_pointer_iterator.hpp b/arch/x86_64/include/arch/stl/contiguous_pointer_iterator.hpp
index e2520dc..f2dfb2b 100644
--- a/arch/x86_64/include/arch/shared/contiguous_pointer_iterator.hpp
+++ b/arch/x86_64/include/arch/stl/contiguous_pointer_iterator.hpp
@@ -1,9 +1,9 @@
-#ifndef TEACHOS_ARCH_X86_64_SHARED_CONTIGUOUS_POINTER_ITERATOR_HPP
-#define TEACHOS_ARCH_X86_64_SHARED_CONTIGUOUS_POINTER_ITERATOR_HPP
+#ifndef TEACHOS_ARCH_X86_64_STL_CONTIGUOUS_POINTER_ITERATOR_HPP
+#define TEACHOS_ARCH_X86_64_STL_CONTIGUOUS_POINTER_ITERATOR_HPP
#include <iterator>
-namespace teachos::arch::shared
+namespace teachos::arch::stl
{
/**
* @brief Generic contiguous iterator for given template type. Allows to easily use this iterator instance in
@@ -45,20 +45,29 @@ namespace teachos::arch::shared
*
* @return Reference to the value.
*/
- auto operator*() const -> reference_type { return *ptr; }
+ [[gnu::section(".stl_text")]]
+ auto operator*() const -> reference_type
+ {
+ return *ptr;
+ }
/**
* @brief Get underlying value, which is the intially passed pointer.
*
* @return Pointer to the underlying value passed intially.
*/
- auto operator->() const -> pointer_type { return ptr; }
+ [[gnu::section(".stl_text")]]
+ auto operator->() const -> pointer_type
+ {
+ return ptr;
+ }
/**
* @brief Pre decrement operator. Returns a reference to the changed address.
*
* @return Reference to the decremented underlying address.
*/
+ [[gnu::section(".stl_text")]]
auto operator--() -> contiguous_pointer_iterator &
{
contiguous_pointer_iterator const old_value = *this;
@@ -71,6 +80,7 @@ namespace teachos::arch::shared
*
* @return Reference to the incremented underlying address.
*/
+ [[gnu::section(".stl_text")]]
auto operator++() -> contiguous_pointer_iterator &
{
++ptr;
@@ -82,6 +92,7 @@ namespace teachos::arch::shared
*
* @return Copy of the decremented underlying address.
*/
+ [[gnu::section(".stl_text")]]
auto operator--(int) -> contiguous_pointer_iterator
{
auto const old_value = *this;
@@ -94,6 +105,7 @@ namespace teachos::arch::shared
*
* @return Copy of the incremented underlying address.
*/
+ [[gnu::section(".stl_text")]]
auto operator++(int) -> contiguous_pointer_iterator
{
auto const old_value = *this;
@@ -107,6 +119,7 @@ namespace teachos::arch::shared
* @param value Value we want to add to the underlying address.
* @return Reference to the changed underlying address.
*/
+ [[gnu::section(".stl_text")]]
auto operator+=(difference_type value) -> contiguous_pointer_iterator &
{
ptr += value;
@@ -119,6 +132,7 @@ namespace teachos::arch::shared
* @param value Value we want to subtract from the underlying address.
* @return Reference to the changed underlying address.
*/
+ [[gnu::section(".stl_text")]]
auto operator-=(difference_type value) -> contiguous_pointer_iterator &
{
ptr -= value;
@@ -131,6 +145,7 @@ namespace teachos::arch::shared
* @param value Value we want to add to a copy of the underlying address.
* @return Copy of underlying address incremented by the given value.
*/
+ [[gnu::section(".stl_text")]]
auto operator+(difference_type value) const -> contiguous_pointer_iterator
{
return contiguous_pointer_iterator{ptr + value};
@@ -142,6 +157,7 @@ namespace teachos::arch::shared
* @param value Value we want to subtrcat from a copy of the underlying address.
* @return Copy of underlying address decremented by the given value.
*/
+ [[gnu::section(".stl_text")]]
auto operator-(difference_type value) const -> contiguous_pointer_iterator
{
return contiguous_pointer_iterator{ptr - value};
@@ -153,7 +169,11 @@ namespace teachos::arch::shared
* @param other Other iterator we want to substract the underlying address with ours.
* @return Size difference between the underlying address of this instance and the given iterator.
*/
- auto operator-(const contiguous_pointer_iterator & other) const -> difference_type { return ptr - other.ptr; }
+ [[gnu::section(".stl_text")]]
+ auto operator-(const contiguous_pointer_iterator & other) const -> difference_type
+ {
+ return ptr - other.ptr;
+ }
/**
* @brief Index operator overload. Returns a reference to the value at the given index. Simply returns the
@@ -162,7 +182,11 @@ namespace teachos::arch::shared
* @param index Index we want to access and get the value from.
* @return Reference to the value at the given index.
*/
- auto operator[](difference_type index) const -> value_type & { return *(ptr + index); }
+ [[gnu::section(".stl_text")]]
+ auto operator[](difference_type index) const -> value_type &
+ {
+ return *(ptr + index);
+ }
/**
* @brief Defaulted comparsion operator. Simply compares the memory address of both iterators.
@@ -170,6 +194,7 @@ namespace teachos::arch::shared
* @param other Other iterator to compare to.
* @return Whether both iterators point to the same underlying address in memory.
*/
+ [[gnu::section(".stl_text")]]
auto operator==(contiguous_pointer_iterator const & other) const -> bool = default;
/**
@@ -178,6 +203,7 @@ namespace teachos::arch::shared
* @param other Other iterator to compare to.
* @return Whether the given iterator is smaller or larger than this iterator.
*/
+ [[gnu::section(".stl_text")]]
auto operator<=>(contiguous_pointer_iterator const & other) const -> std::strong_ordering = default;
private:
@@ -185,6 +211,6 @@ namespace teachos::arch::shared
{}; ///< Underlying value the iterator is currently pointing too and should increment or decrement.
};
-} // namespace teachos::arch::shared
+} // namespace teachos::arch::stl
-#endif // TEACHOS_ARCH_X86_64_SHARED_CONTIGUOUS_POINTER_ITERATOR_HPP
+#endif // TEACHOS_ARCH_X86_64_STL_CONTIGUOUS_POINTER_ITERATOR_HPP
diff --git a/arch/x86_64/include/arch/shared/forward_value_iterator.hpp b/arch/x86_64/include/arch/stl/forward_value_iterator.hpp
index c5dfc06..be3d8e6 100644
--- a/arch/x86_64/include/arch/shared/forward_value_iterator.hpp
+++ b/arch/x86_64/include/arch/stl/forward_value_iterator.hpp
@@ -1,9 +1,9 @@
-#ifndef TEACHOS_ARCH_X86_64_SHARED_FORWARD_VALUE_ITERATOR_HPP
-#define TEACHOS_ARCH_X86_64_SHARED_FORWARD_VALUE_ITERATOR_HPP
+#ifndef TEACHOS_ARCH_X86_64_STL_FORWARD_VALUE_ITERATOR_HPP
+#define TEACHOS_ARCH_X86_64_STL_FORWARD_VALUE_ITERATOR_HPP
#include <iterator>
-namespace teachos::arch::shared
+namespace teachos::arch::stl
{
/**
* @brief Concept for a type to have a post and prefix increment operator, that returns the correct type.
@@ -60,20 +60,29 @@ namespace teachos::arch::shared
*
* @return Reference to the value.
*/
- auto operator*() const -> const_reference_type { return value; }
+ [[gnu::section(".stl_text")]]
+ auto operator*() const -> const_reference_type
+ {
+ return value;
+ }
/**
* @brief Gets pointer to the underlying value passed intially.
*
* @return Pointer to the underlying value passed intially.
*/
- auto operator->() const -> const_pointer_type { return &value; }
+ [[gnu::section(".stl_text")]]
+ auto operator->() const -> const_pointer_type
+ {
+ return &value;
+ }
/**
* @brief Pre increment operator. Returns a reference to the changed value.
*
* @return Reference to the incremented underlying value.
*/
+ [[gnu::section(".stl_text")]]
auto operator++() -> forward_value_iterator &
{
++value;
@@ -85,6 +94,7 @@ namespace teachos::arch::shared
*
* @return Copy of the incremented underlying value.
*/
+ [[gnu::section(".stl_text")]]
auto operator++(int) -> forward_value_iterator
{
auto const old_value = *this;
@@ -98,6 +108,7 @@ namespace teachos::arch::shared
* @param other Other iterator to compare to.
* @return Whether both iterators point to the same underlying address in memory.
*/
+ [[gnu::section(".stl_text")]]
auto operator==(forward_value_iterator const & other) const -> bool = default;
private:
@@ -105,6 +116,6 @@ namespace teachos::arch::shared
{}; ///< Underlying value the iterator is currently pointing too and should increment or decrement.
};
-} // namespace teachos::arch::shared
+} // namespace teachos::arch::stl
-#endif // TEACHOS_ARCH_X86_64_SHARED_FORWARD_VALUE_ITERATOR_HPP
+#endif // TEACHOS_ARCH_X86_64_STL_FORWARD_VALUE_ITERATOR_HPP
diff --git a/arch/x86_64/include/arch/shared/mutex.hpp b/arch/x86_64/include/arch/stl/mutex.hpp
index b18a8b3..a7d297d 100644
--- a/arch/x86_64/include/arch/shared/mutex.hpp
+++ b/arch/x86_64/include/arch/stl/mutex.hpp
@@ -1,9 +1,9 @@
-#ifndef TEACHOS_ARCH_X86_64_SHARED_MUTEX_HPP
-#define TEACHOS_ARCH_X86_64_SHARED_MUTEX_HPP
+#ifndef TEACHOS_ARCH_X86_64_STL_MUTEX_HPP
+#define TEACHOS_ARCH_X86_64_STL_MUTEX_HPP
#include <atomic>
-namespace teachos::arch::shared
+namespace teachos::arch::stl
{
/**
* @brief Custom mutex implementation, that simply wraps an atomic boolean to keep track if the mutex is already in
@@ -34,6 +34,7 @@ namespace teachos::arch::shared
/**
* @brief Lock the mutex (blocks for as long as it is not available).
*/
+ [[gnu::section(".stl_text")]]
auto lock() -> void;
/**
@@ -41,17 +42,19 @@ namespace teachos::arch::shared
*
* @return True if lock has been acquired and false otherwise.
*/
+ [[gnu::section(".stl_text")]]
auto try_lock() -> bool;
/**
* @brief Unlock the mutex.
*/
+ [[gnu::section(".stl_text")]]
auto unlock() -> void;
private:
std::atomic<bool> locked = {false}; // Atomic boolean to track if mutex is locked or not.
};
-} // namespace teachos::arch::shared
+} // namespace teachos::arch::stl
-#endif // TEACHOS_ARCH_X86_64_SHARED_MUTEX_HPP
+#endif // TEACHOS_ARCH_X86_64_STL_MUTEX_HPP
diff --git a/arch/x86_64/include/arch/stl/shared_pointer.hpp b/arch/x86_64/include/arch/stl/shared_pointer.hpp
index 79a9f82..c9796a8 100644
--- a/arch/x86_64/include/arch/stl/shared_pointer.hpp
+++ b/arch/x86_64/include/arch/stl/shared_pointer.hpp
@@ -1,17 +1,44 @@
#ifndef TEACHOS_ARCH_X86_64_STL_SHARED_POINTER_HPP
#define TEACHOS_ARCH_X86_64_STL_SHARED_POINTER_HPP
+#include <atomic>
+
namespace teachos::arch::stl
{
+ /**
+ * @brief Shared_pointer is a smart pointer that retains shared ownership of an object through a pointer. Several
+ * shared_pointer objects may own the same object. The object is destroyed and its memory deallocated when either of
+ * the following happens: the last remaining shared_pointer owning the object is destroyed; the last remaining
+ * shared_pointer owning the object is assigned another pointer via operator= or reset(). A
+ * shared_pointer can share ownership of an object while storing a pointer to another object. This feature can be used
+ * to point to member objects while owning the object they belong to. The stored pointer is the one accessed by get(),
+ * the dereference and the comparison operators. The managed pointer is the one passed to the deleter when use count
+ * reaches zero.
+ *
+ * @tparam T The type of the managed object.
+ */
template<typename T>
struct shared_pointer
{
+ /**
+ * @brief Constructor.
+ *
+ * @param pointer A pointer to an object to manage (default is nullptr).
+ */
+ [[gnu::section(".stl_text")]]
explicit shared_pointer(T * pointer = nullptr)
: pointer(pointer)
- , ref_count(new std::atomic<int>(pointer != nullptr ? 1 : 0))
+ , ref_count(new std::atomic<std::size_t>(pointer != nullptr ? 1 : 0))
{
+ // Nothing to do.
}
+ /**
+ * @brief Copy constructor.
+ *
+ * @param other The shared_pointer to copy from.
+ */
+ [[gnu::section(".stl_text")]]
shared_pointer(const shared_pointer & other)
: pointer(other.pointer)
, ref_count(other.ref_count)
@@ -22,6 +49,12 @@ namespace teachos::arch::stl
}
}
+ /**
+ * @brief Move constructor.
+ *
+ * @param other The shared_pointer to move from.
+ */
+ [[gnu::section(".stl_text")]]
shared_pointer(shared_pointer && other) noexcept
: pointer(other.pointer)
, ref_count(other.ref_count)
@@ -30,6 +63,15 @@ namespace teachos::arch::stl
other.ref_count = nullptr;
}
+ /**
+ * @brief Copy assignment operator. Replaces the managed object with the one managed by r. Shares ownership of the
+ * object managed by r. If r manages no object, *this manages no object too. Equivalent to
+ * shared_ptr<T>(r).swap(*this).
+ *
+ * @param other Another smart pointer to share the ownership with.
+ * @return Reference to this shared pointer.
+ */
+ [[gnu::section(".stl_text")]]
shared_pointer & operator=(const shared_pointer & other)
{
if (this != &other)
@@ -47,6 +89,14 @@ namespace teachos::arch::stl
return *this;
}
+ /**
+ * @brief Move assignment operator. Move-assigns a shared_ptr from r. After the assignment, *this contains a copy of
+ * the previous state of r, and r is empty. Equivalent to shared_ptr<T>(std::move(r)).swap(*this).
+ *
+ * @param other Another smart pointer to acquire the ownership from.
+ * @return Reference to this shared pointer.
+ */
+ [[gnu::section(".stl_text")]]
shared_pointer & operator=(shared_pointer && other) noexcept
{
if (this != &other)
@@ -61,37 +111,87 @@ namespace teachos::arch::stl
return *this;
}
- ~shared_pointer() { cleanup(); }
-
- void cleanup()
+ /**
+ * @brief Destructor. Cleans up resources if necessary.
+ */
+ [[gnu::section(".stl_text")]]
+ ~shared_pointer()
{
- if (pointer != nullptr && ref_count != nullptr && --(*ref_count) == 0)
- {
- delete pointer;
- delete ref_count;
- }
+ cleanup();
}
- void reset(T * p = nullptr)
+ /**
+ * @brief Replaces the managed object.
+ *
+ * @param ptr Pointer to a new object to manage (default = nullptr).
+ */
+ [[gnu::section(".stl_text")]]
+ void reset(T * ptr = nullptr)
{
cleanup();
- pointer = p;
- ref_count = new std::atomic<int>(p != nullptr ? 1 : 0);
+ pointer = ptr;
+ ref_count = new std::atomic<std::size_t>(ptr != nullptr ? 1 : 0);
}
+ /**
+ * @brief Exchanges the stored pointer values and the ownerships of *this and r. Reference counts, if any, are not
+ * adjusted.
+ *
+ * @param other The shared_pointer to swap with.
+ */
+ [[gnu::section(".stl_text")]]
void swap(shared_pointer & other)
{
std::swap(pointer, other.pointer);
std::swap(ref_count, other.ref_count);
}
- T & operator*() const { return *pointer; }
+ /**
+ * @brief Dereference operator. If get() is a null pointer, the behavior is undefined.
+ *
+ * @return Returns the object owned by *this, equivalent to *get().
+ */
+ [[gnu::section(".stl_text")]]
+ auto operator*() const -> T &
+ {
+ return *pointer;
+ }
- T * operator->() const { return pointer; }
+ /**
+ * @brief Member access operator.
+ *
+ * @return Returns a pointer to the object owned by *this, i.e. get().
+ */
+ [[gnu::section(".stl_text")]]
+ auto operator->() const -> T *
+ {
+ return pointer;
+ }
- T * get() const { return pointer; }
+ /**
+ * @brief Returns a pointer to the managed object or nullptr if no object is owned.
+ *
+ * @return Pointer to the managed object or nullptr if no object is owned.
+ */
+ [[gnu::section(".stl_text")]]
+ auto get() const -> T *
+ {
+ return pointer;
+ }
- int use_count() const
+ /**
+ * @brief Returns the number of different shared_pointer instances (*this included) managing the current object. If
+ * there is no managed object, ​0​ is returned.
+ *
+ * @note Common use cases include comparison with ​0​. If use_count returns zero, the shared pointer is empty
+ * and manages no objects (whether or not its stored pointer is nullptr). Comparison with 1. If use_count returns 1,
+ * there are no other owners.
+ *
+ * @return The number of Shared_pointer instances managing the current object or ​0​ if there is no managed
+ * object.
+ */
+ [[gnu::section(".stl_text")]]
+ auto use_count() const -> std::size_t
{
if (pointer != nullptr)
{
@@ -101,17 +201,66 @@ namespace teachos::arch::stl
return 0;
}
- bool unique() const { return use_count() == 1; }
+ /**
+ * @brief Checks whether *this owns an object, i.e. whether get() != nullptr.
+ *
+ * @return true if *this owns an object, false otherwise.
+ */
+ [[gnu::section(".stl_text")]]
+ explicit operator bool() const
+ {
+ return pointer != nullptr;
+ }
- explicit operator bool() const { return pointer != nullptr; }
+ /**
+ * @brief Defaulted three-way comparator operator.
+ */
+ [[gnu::section(".stl_text")]]
+ auto operator<=>(const shared_pointer & other) const = default;
private:
- T * pointer;
- std::atomic<int> * ref_count;
+ /**
+ * @brief Releases ownership and deletes the object if this was the last ereference to the owned managed object.
+ */
+ [[gnu::section(".stl_text")]]
+ auto cleanup() -> void
+ {
+ if (pointer != nullptr && ref_count != nullptr && --(*ref_count) == 0)
+ {
+ delete pointer;
+ delete ref_count;
+ }
+ }
+
+ T * pointer; ///< The managed object.
+ std::atomic<std::size_t> * ref_count; ///< Reference count.
};
+ /**
+ * @brief Specializes the std::swap algorithm for stl::unique_ptr. Swaps the contents of lhs and rhs. Calls
+ * lhs.swap(rhs).
+ *
+ * @tparam T Type of the managed object.
+ * @param lhs, rhs Smart pointers whose contents to swap.
+ */
+ template<typename T>
+ auto swap(shared_pointer<T> & lhs, shared_pointer<T> & rhs) -> void
+ {
+ lhs.swap(rhs);
+ }
+
+ /**
+ * @brief Constructs an object of type T and wraps it in a shared_pointer. Constructs a non-array type T. The
+ * arguments args are passed to the constructor of T. This overload participates in overload resolution only if T is
+ * not an array type. The function is equivalent to: shared_pointer<T>(new T(std::forward<Args>(args)...)).
+ *
+ * @tparam T Type of the managed object.
+ * @tparam Args Argument types for T's constructor.
+ * @param args List of arguments with which an instance of T will be constructed.
+ * @returns Shared_pointer of an instance of type T.
+ */
template<typename T, typename... Args>
- shared_pointer<T> make_shared(Args &&... args)
+ auto make_shared(Args &&... args) -> shared_pointer<T>
{
return shared_pointer<T>(new T(std::forward<Args>(args)...));
}
diff --git a/arch/x86_64/include/arch/stl/stack.hpp b/arch/x86_64/include/arch/stl/stack.hpp
new file mode 100644
index 0000000..48bcf10
--- /dev/null
+++ b/arch/x86_64/include/arch/stl/stack.hpp
@@ -0,0 +1,212 @@
+#ifndef TEACHOS_ARCH_X86_64_STL_STACK_HPP
+#define TEACHOS_ARCH_X86_64_STL_STACK_HPP
+
+#include "arch/exception_handling/panic.hpp"
+#include "arch/stl/vector.hpp"
+
+namespace teachos::arch::stl
+{
+ /**
+ * @brief Custom stack implementation mirroring the std::stack to allow for the usage of STL functionality with our
+ * custom memory management.
+ *
+ * @tparam T Element the stack instance should contain.
+ * @tparam Container Actual underlying container that should be wrapped to provide stack functionality. Requires
+ * access to pop_back(), push_back(), back(), size(), empty() and emplace_back()
+ */
+ template<typename T, typename Container = stl::vector<T>>
+ struct stack
+ {
+ using container_type = Container; ///< Type of the underlying container used to implement stack-like interface.
+ using value_type = Container::value_type; ///< Type of the elements contained in the underlying container.
+ using size_type = Container::size_type; ///< Type of the size in the underlying container.
+ using reference = Container::reference; ///< Type of reference to the elements.
+ using const_reference = Container::const_reference; ///< Type of constant reference to the elements.
+
+ /**
+ * @brief Default Constructor.
+ */
+ stack() = default;
+
+ /**
+ * @brief Constructs data with the given amount of elements containg the given value or alterantively the default
+ * constructed value.
+ *
+ * @param n Amount of elements we want to create and set the given value for.
+ * @param initial Inital value of all elements in the underlying data array.
+ */
+ [[gnu::section(".stl_text")]]
+ explicit stack(size_type n, value_type initial = value_type{})
+ : _container(n, initial)
+ {
+ // Nothing to do.
+ }
+
+ /**
+ * @brief Constructs data by copying all element from the given exclusive range.
+ *
+ * @tparam InputIterator Template that should have atleast input iterator characteristics.
+ * @param first Input iterator to the first element in the range we want to copy from.
+ * @param last Input iterator to one past the last element in the range we want to copy from.
+ */
+ template<typename InputIterator>
+ [[gnu::section(".stl_text")]]
+ explicit stack(InputIterator first, InputIterator last)
+ : _container(first, last)
+ {
+ // Nothing to do.
+ }
+
+ /**
+ * @brief Construct data by copying all elements from the initializer list.
+ *
+ * @param initalizer_list List we want to copy all elements from.
+ */
+ [[gnu::section(".stl_text")]]
+ explicit stack(std::initializer_list<T> initalizer_list)
+ : _container(initalizer_list)
+ {
+ // Nothing to do.
+ }
+
+ /**
+ * @brief Copy constructor.
+ *
+ * @note Allocates underlying data container with the same capacity as stack we are copying from and copies all
+ * elements from it.
+ *
+ * @param other Other instance of stack we want to copy the data from.
+ */
+ [[gnu::section(".stl_text")]]
+ stack(stack<T> const & other)
+ : _container(other)
+ {
+ // Nothing to do.
+ }
+
+ /**
+ * @brief Copy assignment operator.
+ *
+ * @note Allocates underlying data container with the same capacity as vector we are copying from and copies all
+ * elements from it.
+ *
+ * @param other Other instance of vector we want to copy the data from.
+ * @return Newly created copy.
+ */
+ [[gnu::section(".stl_text")]]
+ stack<T> & operator=(stack<T> const & other)
+ {
+ _container = other;
+ }
+
+ /**
+ * @brief Destructor.
+ */
+ ~stack() = default;
+
+ /**
+ * @brief Amount of elements currently contained in this vector, will fill up until we have reached the capacity. If
+ * that is the case the capacity is increased automatically.
+ *
+ * @return Current amount of elements.
+ */
+ [[gnu::section(".stl_text")]]
+ auto size() const -> size_type
+ {
+ return _container.size();
+ }
+
+ /**
+ * @brief Returns a reference to the last element in the container. Calling back on an empty container causes
+ * undefined behavior.
+ *
+ * @return Reference to the last element.
+ */
+ [[gnu::section(".stl_text")]]
+ auto top() -> reference
+ {
+ return _container.back();
+ }
+
+ /**
+ * @brief Returns a reference to the last element in the container. Calling back on an empty container causes
+ * undefined behavior.
+ *
+ * @return Reference to the last element.
+ */
+ [[gnu::section(".stl_text")]]
+ auto top() const -> const_reference
+ {
+ return _container.back();
+ }
+
+ /**
+ * @brief Appends the given element value to the end of the container. The element is assigned through the
+ * assignment operator of the template type. The value is forwarded to the constructor as
+ * std::forward<U>(value), meaning it is either moved (rvalue) or copied (lvalue).
+ *
+ * @note If after the operation the new size() is greater than old capacity() a reallocation takes place,
+ * in which case all iterators (including the end() iterator) and all references to the elements are invalidated.
+ * Otherwise only the end() iterator is invalidated. Uses a forward reference for the actual value passed, which
+ * allows the template method to be used by both lvalue and rvalues and compile a different implementation.
+ *
+ * @param value The value of the element to append.
+ */
+ template<class U>
+ [[gnu::section(".stl_text")]]
+ auto push(U && value) -> void
+ {
+ _container.push_back(std::forward<U>(value));
+ }
+
+ /**
+ * @brief Appends a new element to the end of the container. The element is constructed through a constructor of the
+ * template type. The arguments args... are forwarded to the constructor as std::forward<Args>(args)....
+ *
+ * If after the operation the new size() is greater than old capacity() a reallocation takes place, in which case
+ * all iterators (including the end() iterator) and all references to the elements are invalidated. Otherwise only
+ * the end() iterator is invalidated. Uses a forward reference for the actual value passed, which
+ * allows the template method to be used by both lvalue and rvalues and compile a different implementation.
+ *
+ * @tparam Args
+ * @param args Arguments to forward to the constructor of the element
+ * @return value_type&
+ */
+ template<class... Args>
+ [[gnu::section(".stl_text")]]
+ auto emplace(Args &&... args) -> reference
+ {
+ _container.emplace_back(std::forward<Args>(args)...);
+ }
+
+ /**
+ * @brief Removes the last element of the container.
+ *
+ * @note Calling pop_back on an empty container results in halting the
+ * further execution. Iterators and references to the last element are invalidated. The end()
+ * iterator is also invalidated.
+ */
+ [[gnu::section(".stl_text")]]
+ auto pop() -> void
+ {
+ _container.pop_back();
+ }
+
+ /**
+ * @brief Wheter there are currently any items this container or not.
+ *
+ * @return True if there are no elements, false if there are.
+ */
+ [[gnu::section(".stl_text")]]
+ auto empty() const -> bool
+ {
+ return _container.empty();
+ }
+
+ private:
+ container_type _container = {}; ///< Underlying container used by the stack to actually save the data.
+ };
+
+} // namespace teachos::arch::stl
+
+#endif // TEACHOS_ARCH_X86_64_STL_STACK_HPP
diff --git a/arch/x86_64/include/arch/stl/unique_pointer.hpp b/arch/x86_64/include/arch/stl/unique_pointer.hpp
index 0ec3c38..03b4ef3 100644
--- a/arch/x86_64/include/arch/stl/unique_pointer.hpp
+++ b/arch/x86_64/include/arch/stl/unique_pointer.hpp
@@ -3,26 +3,66 @@
namespace teachos::arch::stl
{
+ /**
+ * @brief Unique_pointer is a smart pointer that owns (is responsible for) and manages another object via a pointer
+ * and subsequently disposes of that object when the unique_pointer goes out of scope.
+ *
+ * @tparam T Type of the managed object.
+ */
template<typename T>
struct unique_pointer
{
+ /**
+ * @brief Constructor.
+ *
+ * @param ptr A pointer to an object to manage (default is nullptr).
+ */
+ [[gnu::section(".stl_text")]]
explicit unique_pointer(T * ptr = nullptr)
: pointer(ptr)
{
+ // Nothing to do.
}
- ~unique_pointer() { delete pointer; }
+ /**
+ * @brief Destructor that deletes the managed object.
+ */
+ [[gnu::section(".stl_text")]]
+ ~unique_pointer()
+ {
+ delete pointer;
+ }
+ /**
+ * @brief Deleted copy constructor to enforce unique ownership.
+ */
unique_pointer(const unique_pointer &) = delete;
- unique_pointer & operator=(const unique_pointer &) = delete;
+ /**
+ * @brief Deleted copy assignment operator to enforce unique ownership.
+ */
+ auto operator=(const unique_pointer &) -> unique_pointer & = delete;
+
+ /**
+ * @brief Move constructor.
+ *
+ * @param other Unique pointer to move from.
+ */
+ [[gnu::section(".stl_text")]]
unique_pointer(unique_pointer && other) noexcept
: pointer(other.pointer)
{
other.pointer = nullptr;
}
- unique_pointer & operator=(unique_pointer && other) noexcept
+ /**
+ * @brief Move assignment operator. Transfers ownership from other to *this as if by calling reset(r.release()).
+ *
+ * @param other Smart pointer from which ownership will be transferred.
+ * @return Reference to this unique pointer.
+ */
+ [[gnu::section(".stl_text")]]
+ auto operator=(unique_pointer && other) noexcept -> unique_pointer &
{
if (this != &other)
{
@@ -33,33 +73,129 @@ namespace teachos::arch::stl
return *this;
}
- T & operator*() const { return *pointer; }
+ /**
+ * @brief Dereference operator. If get() is a null pointer, the behavior is undefined.
+ *
+ * @return Returns the object owned by *this, equivalent to *get().
+ */
+ [[gnu::section(".stl_text")]]
+ auto operator*() const -> T &
+ {
+ return *pointer;
+ }
+
+ /**
+ * @brief Member access operator.
+ *
+ * @return Returns a pointer to the object owned by *this, i.e. get().
+ */
+ [[gnu::section(".stl_text")]]
+ auto operator->() const -> T *
+ {
+ return pointer;
+ }
- T * operator->() const { return pointer; }
+ /**
+ * @brief Returns a pointer to the managed object or nullptr if no object is owned.
+ *
+ * @return Pointer to the managed object or nullptr if no object is owned.
+ */
+ [[gnu::section(".stl_text")]]
+ auto get() const -> T *
+ {
+ return pointer;
+ }
- T * get() const { return pointer; }
+ /**
+ * @brief Checks whether *this owns an object, i.e. whether get() != nullptr.
+ *
+ * @return true if *this owns an object, false otherwise.
+ */
+ [[gnu::section(".stl_text")]]
+ explicit operator bool() const noexcept
+ {
+ return pointer != nullptr;
+ }
- T * release()
+ /**
+ * @brief Releases the ownership of the managed object, if any.
+ * get() returns nullptr after the call.
+ * The caller is responsible for cleaning up the object (e.g. by use of get_deleter()).
+ *
+ * @return Pointer to the managed object or nullptr if there was no managed object, i.e. the value which would be
+ * returned by get() before the call.
+ */
+ [[gnu::section(".stl_text")]]
+ auto release() -> T *
{
T * temp = pointer;
pointer = nullptr;
return temp;
}
- void reset(T * ptr = nullptr)
+ /**
+ * @brief Replaces the managed object.
+ *
+ * @note A test for self-reset, i.e. whether ptr points to an object already managed by *this, is not performed,
+ * except where provided as a compiler extension or as a debugging assert. Note that code such as
+ * p.reset(p.release()) does not involve self-reset, only code like p.reset(p.get()) does.
+ *
+ * @param ptr Pointer to a new object to manage (default = nullptr).
+ */
+ [[gnu::section(".stl_text")]]
+ auto reset(T * ptr = nullptr) -> void
{
delete pointer;
pointer = ptr;
}
- void swap(unique_pointer & other) { std::swap(pointer, other.pointer); }
+ /**
+ * @brief Swaps the managed objects and associated deleters of *this and another unique_ptr object other.
+ *
+ * @param other Another unique_ptr object to swap the managed object and the deleter with.
+ */
+ [[gnu::section(".stl_text")]]
+ auto swap(unique_pointer & other) -> void
+ {
+ using std::swap;
+ swap(pointer, other.pointer);
+ }
+
+ /**
+ * @brief Defaulted three-way comparator operator.
+ */
+ [[gnu::section(".stl_text")]]
+ auto operator<=>(const unique_pointer & other) const = default;
private:
- T * pointer;
+ T * pointer; ///< The managed pointer.
};
+ /**
+ * @brief Specializes the std::swap algorithm for stl::unique_ptr. Swaps the contents of lhs and rhs. Calls
+ * lhs.swap(rhs).
+ *
+ * @tparam T Type of the managed object.
+ * @param lhs, rhs Smart pointers whose contents to swap.
+ */
+ template<typename T>
+ auto swap(unique_pointer<T> & lhs, unique_pointer<T> & rhs) -> void
+ {
+ lhs.swap(rhs);
+ }
+
+ /**
+ * @brief Constructs an object of type T and wraps it in a unique_pointer. Constructs a non-array type T. The
+ * arguments args are passed to the constructor of T. This overload participates in overload resolution only if T is
+ * not an array type. The function is equivalent to: unique_pointer<T>(new T(std::forward<Args>(args)...)).
+ *
+ * @tparam T Type of the managed object.
+ * @tparam Args Argument types for T's constructor.
+ * @param args List of arguments with which an instance of T will be constructed.
+ * @returns Unique_pointer of an instance of type T.
+ */
template<typename T, typename... Args>
- unique_pointer<T> make_unique(Args &&... args)
+ auto make_unique(Args &&... args) -> unique_pointer<T>
{
return unique_pointer<T>(new T(std::forward<Args>(args)...));
}
diff --git a/arch/x86_64/include/arch/stl/vector.hpp b/arch/x86_64/include/arch/stl/vector.hpp
index 62be704..5314029 100644
--- a/arch/x86_64/include/arch/stl/vector.hpp
+++ b/arch/x86_64/include/arch/stl/vector.hpp
@@ -2,8 +2,8 @@
#define TEACHOS_ARCH_X86_64_STL_VECTOR_HPP
#include "arch/exception_handling/panic.hpp"
-#include "arch/shared/container.hpp"
-#include "arch/shared/contiguous_pointer_iterator.hpp"
+#include "arch/stl/container.hpp"
+#include "arch/stl/contiguous_pointer_iterator.hpp"
#include <algorithm>
@@ -11,181 +11,589 @@ namespace teachos::arch::stl
{
/**
* @brief Custom vector implementation mirroring the std::vector to allow for the usage of STL functionality with our
- * custom memory management
+ * custom memory management.
*
- * @tparam T Element the vector instance should contain
+ * @tparam T Element the vector instance should contain.
*/
template<typename T>
struct vector
{
+ using value_type = T; ///< Type of the elements contained in the container.
+ using size_type = std::size_t; ///< Type of the size in the container.
+ using reference = value_type &; ///< Type of reference to the elements.
+ using const_reference = value_type const &; ///< Type of constant reference to the elements.
+ using pointer = value_type *; ///< Type of pointer to the elements.
+ using const_pointer = value_type const *; ///< Type of constant pointer to the elements.
+
/**
- * @brief Defaulted constructor. Initalizes empty vector
+ * @brief Default Constructor.
*/
vector() = default;
/**
* @brief Constructs data with the given amount of elements containg the given value or alterantively the default
- * constructed value
+ * constructed value.
*
- * @param capacity Amount of elements we want to create and set the given value for
- * @param initial Inital value of all elements in the underlying data array
+ * @param n Amount of elements we want to create and set the given value for.
+ * @param initial Inital value of all elements in the underlying data array.
*/
- vector(size_t capacity, T initial = T{})
- : capacity(capacity)
- , size(capacity)
- , data(new T[capacity]{})
+ explicit vector(size_type n, value_type initial = value_type{})
+ : _size(n)
+ , _capacity(n)
+ , _data(new value_type[_capacity]{})
{
- auto const begin = data;
- auto const end = data + size;
- vector_container container{begin, end};
- std::ranges::fill(container, inital);
+ std::ranges::fill(*this, initial);
}
/**
- * @brief Copy constructor
- *
- * @note Allocates underlying data container with the same capacity as vector we are copying from and copies all
- * elements from it
+ * @brief Constructs data by copying all element from the given exclusive range.
*
- * @param other Other instance of vector we want to copy the data from
+ * @tparam InputIterator Template that should have atleast input iterator characteristics.
+ * @param first Input iterator to the first element in the range we want to copy from.
+ * @param last Input iterator to one past the last element in the range we want to copy from.
*/
- vector(vector<T> const & other)
- : size(size)
- , capacity(capacity)
+ template<typename InputIterator>
+ explicit vector(InputIterator first, InputIterator last)
+ : _size(std::distance(first, last))
+ , _capacity(std::distance(first, last))
+ , _data(new value_type[_capacity]{})
{
- delete[] data;
- data = new T[capacity]{};
+ stl::container<InputIterator> container{first, last};
+ std::ranges::copy(container, _data);
+ }
- auto const begin = other.data;
- auto const end = other.data + size;
- vector_container container{begin, end};
- std::ranges::copy(container, data);
+ /**
+ * @brief Construct data by copying all elements from the initializer list.
+ *
+ * @param initalizer_list List we want to copy all elements from.
+ */
+ explicit vector(std::initializer_list<value_type> initalizer_list)
+ : _size(initalizer_list.size())
+ , _capacity(initalizer_list.size())
+ , _data(new value_type[_capacity]{})
+ {
+ std::ranges::copy(initalizer_list, _data);
}
/**
- * @brief Copy assignment operator
+ * @brief Copy constructor.
*
* @note Allocates underlying data container with the same capacity as vector we are copying from and copies all
- * elements from it
+ * elements from it.
*
- * @param other Other instance of vector we want to copy the data from
- * @return Newly created copy
+ * @param other Other instance of vector we want to copy the data from.
*/
- vector<T> & operator=(vector<T> const & other)
+ vector(vector<value_type> const & other)
+ : _size(other._size)
+ , _capacity(other._capacity)
{
- delete[] data;
- size = other.size();
- capacity = other.capacity();
- data = new T[capacity]{};
+ delete[] _data;
+ _data = new value_type[_capacity]{};
+ std::ranges::copy(other, _data);
+ }
- auto const begin = other.data;
- auto const end = other.data + size;
- vector_container container{begin, end};
- std::ranges::copy(container, data);
+ /**
+ * @brief Copy assignment operator.
+ *
+ * @note Allocates underlying data container with the same capacity as vector we are copying from and copies all
+ * elements from it.
+ *
+ * @param other Other instance of vector we want to copy the data from.
+ * @return Newly created copy.
+ */
+ [[gnu::section(".stl_text")]]
+ vector<value_type> & operator=(vector<value_type> const & other)
+ {
+ delete[] _data;
+ _size = other._size;
+ _capacity = other._capacity;
+ _data = new value_type[_capacity]{};
+ std::ranges::copy(other, _data);
return *this;
}
/**
- * @brief Destructor
+ * @brief Destructor.
*/
- ~vector() { delete[] data; }
+ ~vector() { delete[] _data; }
/**
* @brief Amount of elements currently contained in this vector, will fill up until we have reached the capacity. If
- * that is the case the capacity is increased automatically
+ * that is the case the capacity is increased automatically.
*
- * @return Current amount of elements
+ * @return Current amount of elements.
*/
- size_t size() const { return size; }
+ [[gnu::section(".stl_text")]]
+ auto size() const -> size_type
+ {
+ return _size;
+ }
/**
* @brief Amount of space the vector currently has, can be different than the size, because we allocate more than we
- * exactly require to decrease the amount of allocations and deallocation to improve speed
+ * exactly require to decrease the amount of allocations and deallocation to improve speed.
*
- * @return Current amount of space the vector has for elements
+ * @return Current amount of space the vector has for elements.
*/
- size_t capacity() const { return capacity; }
+ [[gnu::section(".stl_text")]]
+ auto capacity() const -> size_type
+ {
+ return _capacity;
+ }
/**
- * @brief Array indexing operator. Allowing to access element at the given index
+ * @brief Array indexing operator. Allowing to access element at the given index.
*
- * @note Does not do any bounds checks use at() for that
+ * @note Does not do any bounds checks use at() for that.
*
- * @param index Index we want to access elements at
- * @return Reference to the underlying element
+ * @param index Index we want to access elements at.
+ * @return Reference to the underlying element.
*/
- T & operator[](size_t index) { return data[index]; }
+ [[gnu::section(".stl_text")]]
+ auto operator[](size_type index) -> reference
+ {
+ return _data[index];
+ }
/**
- * @brief Array indexing operator. Allowing to access element at the given index
+ * @brief Array indexing operator. Allowing to access element at the given index.
*
- * @note Ensures we do not access element outside of the bounds of the array, if we do further execution is halted
+ * @note Does not do any bounds checks use at() for that.
*
- * @param index Index we want to access elements at
- * @return Reference to the underlying element
+ * @param index Index we want to access elements at.
+ * @return Reference to the underlying element.
*/
- T & at(size_t index)
+ [[gnu::section(".stl_text")]]
+ auto operator[](size_type index) const -> const_reference
{
- if (index >= size)
- {
- exception_handling::panic("[Vector] Attempted to read element at invalid index");
- }
- return this->operator[](size);
+ return _data[index];
}
- void push_back(T & const element) {}
+ /**
+ * @brief Array indexing operator. Allowing to access element at the given index.
+ *
+ * @note Ensures we do not access element outside of the bounds of the array, if we do further execution is halted.
+ *
+ * @param index Index we want to access elements at.
+ * @return Reference to the underlying element.
+ */
+ [[gnu::section(".stl_text")]]
+ auto at(size_type index) -> reference
+ {
+ throw_if_out_of_range(index);
+ return this->operator[](index);
+ }
- void emplace_back(T && const element)
+ /**
+ * @brief Array indexing operator. Allowing to access element at the given index.
+ *
+ * @note Ensures we do not access element outside of the bounds of the array, if we do further execution is halted.
+ *
+ * @param index Index we want to access elements at.
+ * @return Reference to the underlying element.
+ */
+ [[gnu::section(".stl_text")]]
+ auto at(size_type index) const -> const_reference
{
- // If no cacacity, increase capacity
- if (size == capacity)
- {
- reserve(capacity * 2);
- }
+ throw_if_out_of_range(index);
+ return this->operator[](index);
+ }
- data[size] = element;
- size++;
+ /**
+ * @brief Appends the given element value to the end of the container. The element is assigned through the
+ * assignment operator of the template type. The value is forwarded to the constructor as
+ * std::forward<U>(value), meaning it is either moved (rvalue) or copied (lvalue).
+ *
+ * @note If after the operation the new size() is greater than old capacity() a reallocation takes place,
+ * in which case all iterators (including the end() iterator) and all references to the elements are invalidated.
+ * Otherwise only the end() iterator is invalidated. Uses a forward reference for the actual value passed, which
+ * allows the template method to be used by both lvalue and rvalues and compile a different implementation.
+ *
+ * @param value The value of the element to append.
+ */
+ template<class U>
+ [[gnu::section(".stl_text")]]
+ auto push_back(U && value) -> void
+ {
+ increase_capacity_if_full();
+ _data[_size] = std::forward<U>(value);
+ (void)_size++;
}
- void pop_back()
+ /**
+ * @brief Appends a new element to the end of the container. The element is constructed through a constructor of the
+ * template type. The arguments args... are forwarded to the constructor as std::forward<Args>(args)....
+ *
+ * If after the operation the new size() is greater than old capacity() a reallocation takes place, in which case
+ * all iterators (including the end() iterator) and all references to the elements are invalidated. Otherwise only
+ * the end() iterator is invalidated. Uses a forward reference for the actual value passed, which
+ * allows the template method to be used by both lvalue and rvalues and compile a different implementation.
+ *
+ * @tparam Args
+ * @param args Arguments to forward to the constructor of the element
+ * @return value_type&
+ */
+ template<class... Args>
+ [[gnu::section(".stl_text")]]
+ auto emplace_back(Args &&... args) -> value_type &
{
- if (size <= 0)
- {
- exception_handling::panic("[Vector] Attempted to pop back last element of already empty vector");
- }
- size--;
+ increase_capacity_if_full();
+ _data[_size] = value_type{std::forward<Args>(args)...};
+ auto const index = _size++;
+ return _data[index];
+ }
+
+ /**
+ * @brief Removes the last element of the container. Calling pop_back on an empty container results in halting the
+ * further execution. Iterators and references to the last element are invalidated. The end()
+ * iterator is also invalidated.
+ */
+ [[gnu::section(".stl_text")]]
+ auto pop_back() -> void
+ {
+ throw_if_empty();
+ (void)_size--;
+ }
+
+ /**
+ * @brief Returns an iterator to the first element of the vector.
+ * If the vector is empty, the returned iterator will be equal to end().
+ *
+ * @return Iterator to the first element.
+ */
+ [[gnu::section(".stl_text")]]
+ auto begin() noexcept -> pointer
+ {
+ return _data;
+ }
+
+ /**
+ * @brief Returns an iterator to the first element of the vector.
+ * If the vector is empty, the returned iterator will be equal to end().
+ *
+ * @return Iterator to the first element.
+ */
+ [[gnu::section(".stl_text")]]
+ auto begin() const noexcept -> const_pointer
+ {
+ return _data;
}
- T & front() { return *data; }
+ /**
+ * @brief Returns an iterator to the first element of the vector.
+ * If the vector is empty, the returned iterator will be equal to end().
+ *
+ * @return Iterator to the first element.
+ */
+ [[gnu::section(".stl_text")]]
+ auto cbegin() const noexcept -> const_pointer
+ {
+ return begin();
+ }
- T & back() { return *(data + size); }
+ /**
+ * @brief Returns a reverse iterator to the first element of the reversed vector. It corresponds to the last element
+ * of the non-reversed vector. If the vector is empty, the returned iterator will be equal to rend().
+ *
+ * @return Reverse iterator to the first element.
+ */
+ [[gnu::section(".stl_text")]]
+ auto rbegin() noexcept -> pointer
+ {
+ return _data + _size - 1;
+ }
- void reserve(size_t new_capacity)
+ /**
+ * @brief Returns a reverse iterator to the first element of the reversed vector. It corresponds to the last element
+ * of the non-reversed vector. If the vector is empty, the returned iterator will be equal to rend().
+ *
+ * @return Reverse iterator to the first element.
+ */
+ [[gnu::section(".stl_text")]]
+ auto rbegin() const noexcept -> const_pointer
+ {
+ return _data + _size - 1;
+ }
+
+ /**
+ * @brief Returns a reverse iterator to the first element of the reversed vector. It corresponds to the last element
+ * of the non-reversed vector. If the vector is empty, the returned iterator will be equal to rend().
+ *
+ * @return Reverse iterator to the first element.
+ */
+ [[gnu::section(".stl_text")]]
+ auto crbegin() const noexcept -> const_pointer
+ {
+ return rbegin();
+ }
+
+ /**
+ * @brief Returns an iterator to the element following the last element of the vector. This element acts as a
+ * placeholder, attempting to access it results in undefined behavior.
+ *
+ * @return Iterator to the element following the last element.
+ */
+ [[gnu::section(".stl_text")]]
+ auto end() noexcept -> pointer
+ {
+ return _data + _size;
+ }
+
+ /**
+ * @brief Returns an iterator to the element following the last element of the vector. This element acts as a
+ * placeholder, attempting to access it results in undefined behavior.
+ *
+ * @return Iterator to the element following the last element.
+ */
+ [[gnu::section(".stl_text")]]
+ auto end() const noexcept -> const_pointer
+ {
+ return _data + _size;
+ }
+
+ /**
+ * @brief Returns an iterator to the element following the last element of the vector. This element acts as a
+ * placeholder, attempting to access it results in undefined behavior.
+ *
+ * @return Iterator to the element following the last element.
+ */
+ [[gnu::section(".stl_text")]]
+ auto cend() const noexcept -> const_pointer
{
- if (new_capacity < size)
+ return end();
+ }
+
+ /**
+ * @brief Returns a reverse iterator to the element following the last element of the reversed vector. It
+ * corresponds to the element preceding the first element of the non-reversed vector. This element acts as a
+ * placeholder, attempting to access it results in undefined behavior.
+ *
+ * @return Reverse iterator to the element following the last element.
+ */
+ [[gnu::section(".stl_text")]]
+ auto rend() noexcept -> pointer
+ {
+ return _data + size - 1;
+ }
+
+ /**
+ * @brief Returns a reverse iterator to the element following the last element of the reversed vector. It
+ * corresponds to the element preceding the first element of the non-reversed vector. This element acts as a
+ * placeholder, attempting to access it results in undefined behavior.
+ *
+ * @return Reverse iterator to the element following the last element.
+ */
+ [[gnu::section(".stl_text")]]
+ auto rend() const noexcept -> const_pointer
+ {
+ return _data + size - 1;
+ }
+
+ /**
+ * @brief Returns a reverse iterator to the element following the last element of the reversed vector. It
+ * corresponds to the element preceding the first element of the non-reversed vector. This element acts as a
+ * placeholder, attempting to access it results in undefined behavior.
+ *
+ * @return Reverse iterator to the element following the last element.
+ */
+ [[gnu::section(".stl_text")]]
+ auto crend() const noexcept -> const_pointer
+ {
+ return rbegin();
+ }
+
+ /**
+ * @brief Returns a pointer to the underlying array serving as element storage. The pointer is such that range
+ * [data(), data() + size()) is always a valid range, even if the container is empty (data() is not dereferenceable
+ * in that case).
+ *
+ * @return Pointer to the underlying element storage. For non-empty containers, the returned pointer compares equal
+ * to the address of the first element.
+ */
+ [[gnu::section(".stl_text")]]
+ auto data() -> pointer
+ {
+ return _data;
+ }
+
+ /**
+ * @brief Returns a pointer to the underlying array serving as element storage. The pointer is such that range
+ * [data(), data() + size()) is always a valid range, even if the container is empty (data() is not dereferenceable
+ * in that case).
+ *
+ * @return Pointer to the underlying element storage. For non-empty containers, the returned pointer compares equal
+ * to the address of the first element.
+ */
+ [[gnu::section(".stl_text")]]
+ auto data() const -> const_pointer
+ {
+ return _data;
+ }
+
+ /**
+ * @brief Returns a reference to the first element in the container. Calling front on an empty container causes
+ * undefined behavior.
+ *
+ * @return Reference to the first element.
+ */
+ [[gnu::section(".stl_text")]]
+ auto front() -> reference
+ {
+ throw_if_empty();
+ return *begin();
+ }
+
+ /**
+ * @brief Returns a reference to the first element in the container. Calling front on an empty container causes
+ * undefined behavior.
+ *
+ * @return Reference to the first element.
+ */
+ [[gnu::section(".stl_text")]]
+ auto front() const -> const_reference
+ {
+ throw_if_empty();
+ return *begin();
+ }
+
+ /**
+ * @brief Returns a reference to the last element in the container. Calling back on an empty container causes
+ * undefined behavior.
+ *
+ * @return Reference to the last element.
+ */
+ [[gnu::section(".stl_text")]]
+ auto back() -> reference
+ {
+ throw_if_empty();
+ return *rbegin();
+ }
+
+ /**
+ * @brief Returns a reference to the last element in the container. Calling back on an empty container causes
+ * undefined behavior.
+ *
+ * @return Reference to the last element.
+ */
+ [[gnu::section(".stl_text")]]
+ auto back() const -> const_reference
+ {
+ throw_if_empty();
+ return *rbegin();
+ }
+
+ /**
+ * @brief Increase the capacity of the vector (the total number of elements that the vector can hold without
+ * requiring reallocation) to a value that's greater or equal to new_cap. If new_cap is greater than the current
+ * capacity(), new storage is allocated, otherwise the function does nothing.
+ *
+ * reserve() does not change the size of the vector.
+ *
+ * If new_cap is greater than capacity(), all iterators (including the end() iterator) and all references to the
+ * elements are invalidated. Otherwise, no iterators or references are invalidated.
+ *
+ * After a call to reserve(), insertions will not trigger reallocation unless the insertion would make the size of
+ * the vector greater than the value of capacity().
+ *
+ * @note Correctly using reserve() can prevent unnecessary reallocations, but inappropriate uses of reserve() (for
+ * instance, calling it before every push_back() call) may actually increase the number of reallocations (by causing
+ * the capacity to grow linearly rather than exponentially) and result in increased computational complexity and
+ * decreased performance. For example, a function that receives an arbitrary vector by reference and appends
+ * elements to it should usually not call reserve() on the vector, since it does not know of the vector's usage
+ * characteristics.
+ *
+ * When inserting a range, the range version of insert() is generally preferable as it preserves the correct
+ * capacity growth behavior, unlike reserve() followed by a series of push_back()s.
+ *
+ * reserve() cannot be used to reduce the capacity of the container; to that end shrink_to_fit() is provided.
+ *
+ * @param new_capacity New capacity of the vector, in number of elements
+ */
+ [[gnu::section(".stl_text")]]
+ auto reserve(size_type new_capacity) -> void
+ {
+ if (new_capacity <= _capacity)
{
- // Creating new array with less capacity than is required to keep all current elements makes no sense
return;
}
- T * temp = new T[new_capacity];
-
- auto const begin = other.data;
- auto const end = other.data + capacity;
- vector_container container{begin, end};
+ _capacity = new_capacity;
+ value_type * temp = new value_type[_capacity]{};
+ stl::container<value_type *> container{begin(), end()};
std::ranges::copy(container, temp);
+ delete[] _data;
+ _data = temp;
+ }
+
+ /**
+ * @brief Requests the removal of unused capacity. Meaning it requests to reduce capacity() to size().
+ *
+ * If reallocation occurs, all iterators (including the end() iterator) and all references to the elements are
+ * invalidated. If no reallocation occurs, no iterators or references are invalidated.
+ */
+ [[gnu::section(".stl_text")]]
+ auto shrink_to_fit() -> void
+ {
+ if (_size == _capacity)
+ {
+ return;
+ }
- delete[] data;
- capacity = new_capacity;
- data = temp;
+ _capacity = _size;
+ value_type * temp = new value_type[_capacity]{};
+ stl::container<value_type *> container{begin(), end()};
+ std::copy(container, temp);
+ delete[] _data;
+ _data = temp;
+ }
+
+ /**
+ * @brief Wheter there are currently any items this container or not.
+ *
+ * @return True if there are no elements, false if there are.
+ */
+ [[gnu::section(".stl_text")]]
+ auto empty() const -> bool
+ {
+ return _size <= 0;
}
private:
- T * data = {}; ///< Pointer to the first element in the underlying data container
- size_t capacity = {}; ///< Amount of space for elements in the underlying data container
- size_t size = {}; ///< Amount of elements in the underlying data container
+ /**
+ * @brief Halts the execution of the application if the data container is currently empty.
+ */
+ auto throw_if_empty() const -> void
+ {
+ if (empty())
+ {
+ exception_handling::panic("[Vector] Attempted to access element of currently empty vector");
+ }
+ }
+
+ auto throw_if_out_of_range(size_type index) const -> void
+ {
+ if (index >= _size)
+ {
+ exception_handling::panic("[Vector] Attempted to read element at invalid index");
+ }
+ }
+
+ /**
+ * @brief Increases the internal capacity to 1 if it was previously 0 and to * 2 after that, meaning exponential
+ * growth. This is done to decrease the amount of single allocations done and because a power of 2 in memory size is
+ * normally perferable for the cache.
+ */
+ auto increase_capacity_if_full() -> void
+ {
+ if (_size == _capacity)
+ {
+ reserve(_capacity == 0U ? 1U : _capacity * 2U);
+ }
+ }
- typedef shared::container<shared::contiguous_pointer_iterator<T>> vector_container;
+ size_type _size = {}; ///< Amount of elements in the underlying data container
+ size_type _capacity = {}; ///< Amount of space for elements in the underlying data container
+ value_type * _data = {}; ///< Pointer to the first element in the underlying data container
};
} // namespace teachos::arch::stl
diff --git a/arch/x86_64/include/arch/user/main.hpp b/arch/x86_64/include/arch/user/main.hpp
new file mode 100644
index 0000000..c168a1f
--- /dev/null
+++ b/arch/x86_64/include/arch/user/main.hpp
@@ -0,0 +1,16 @@
+#ifndef TEACHOS_ARCH_X86_64_USER_MAIN_HPP
+#define TEACHOS_ARCH_X86_64_USER_MAIN_HPP
+
+namespace teachos::arch::user
+{
+ /**
+ * @brief User Main method. If this method finishes there is no code left to run and the whole OS will shut down.
+ * Additionally this main method is executed at Ring 3 and accessing CPU Registers or Kernel level functionality can
+ * only be done over syscalls and not directly anymore.
+ */
+ [[gnu::section(".user_text")]]
+ auto main() -> void;
+
+} // namespace teachos::arch::user
+
+#endif // TEACHOS_ARCH_X86_64_USER_MAIN_HPP
diff --git a/arch/x86_64/scripts/kernel.ld b/arch/x86_64/scripts/kernel.ld
index cc07896..3d9a7ae 100644
--- a/arch/x86_64/scripts/kernel.ld
+++ b/arch/x86_64/scripts/kernel.ld
@@ -85,9 +85,20 @@ SECTIONS
KEEP(*crtn.s.o*(.fini))
}
+ .stl_text ALIGN(4K) : AT(ADDR (.stl_text))
+ {
+ *(.stl_text .stl_text*)
+ KEEP(*libstdc++.a:*(.text .text.*))
+ }
+
.text ALIGN(4K) : AT(ADDR (.text))
{
- *(.text*)
+ *(.text .text.*)
+ }
+
+ .user_text ALIGN(4K) : AT(ADDR (.user_text))
+ {
+ *(.user_text .user_text.*)
}
.rodata ALIGN(4K) : AT (ADDR (.rodata))
@@ -123,6 +134,11 @@ SECTIONS
*(.data*)
}
+ .user_data ALIGN(4K) : AT (ADDR (.user_data))
+ {
+ *(.user_data .user_data.*)
+ }
+
/***************************************************************************
* In accordance with the symbol definitions at the start, we generate some
* symbols to mark the end of our loaded image.
diff --git a/arch/x86_64/src/boot/boot.s b/arch/x86_64/src/boot/boot.s
index 2197dce..7932045 100644
--- a/arch/x86_64/src/boot/boot.s
+++ b/arch/x86_64/src/boot/boot.s
@@ -197,10 +197,8 @@ _start:
call enable_paging
call enable_sse
- cli // Clears the interrupt flag during the GDT setup
lgdt (global_descriptor_table_pointer)
- jmp $global_descriptor_table_code,$_transition_to_long_mode
- // The interrupt flag is set in cpp after setting up the GDT
+ jmp $global_descriptor_table_code, $_transition_to_long_mode
call halt
diff --git a/arch/x86_64/src/context_switching/interrupt_descriptor_table/gate_descriptor.cpp b/arch/x86_64/src/context_switching/interrupt_descriptor_table/gate_descriptor.cpp
new file mode 100644
index 0000000..28f289c
--- /dev/null
+++ b/arch/x86_64/src/context_switching/interrupt_descriptor_table/gate_descriptor.cpp
@@ -0,0 +1,24 @@
+#include "arch/context_switching/interrupt_descriptor_table/gate_descriptor.hpp"
+
+namespace teachos::arch::context_switching::interrupt_descriptor_table
+{
+ gate_descriptor::gate_descriptor(uint128_t flags)
+ : _offset_1(flags)
+ , _selector(flags >> 19U, flags >> 16U)
+ , _ist(flags >> 32U)
+ , _flags(flags >> 40U)
+ , _offset_2(flags >> 48U)
+ {
+ // Nothing to do.
+ }
+
+ gate_descriptor::gate_descriptor(segment_selector selector, ist_offset ist, idt_flags flags, uint64_t offset)
+ : _offset_1(offset)
+ , _selector(selector)
+ , _ist(ist)
+ , _flags(flags)
+ , _offset_2(offset >> 16U)
+ {
+ // Nothing to do.
+ }
+} // namespace teachos::arch::context_switching::interrupt_descriptor_table
diff --git a/arch/x86_64/src/context_switching/interrupt_descriptor_table/idt_flags.cpp b/arch/x86_64/src/context_switching/interrupt_descriptor_table/idt_flags.cpp
new file mode 100644
index 0000000..d36a4c1
--- /dev/null
+++ b/arch/x86_64/src/context_switching/interrupt_descriptor_table/idt_flags.cpp
@@ -0,0 +1,17 @@
+#include "arch/context_switching/interrupt_descriptor_table/idt_flags.hpp"
+
+namespace teachos::arch::context_switching::interrupt_descriptor_table
+{
+ idt_flags::idt_flags(uint8_t flags)
+ : _flags(flags)
+ {
+ // Nothing to do.
+ }
+
+ auto idt_flags::contains_flags(std::bitset<8U> other) const -> bool
+ {
+ return (std::bitset<8U>{_flags} & other) == other;
+ }
+
+ auto idt_flags::operator|=(std::bitset<8U> other) -> void { _flags |= other.to_ulong(); }
+} // namespace teachos::arch::context_switching::interrupt_descriptor_table
diff --git a/arch/x86_64/src/context_switching/interrupt_descriptor_table/interrupt_descriptor_table.cpp b/arch/x86_64/src/context_switching/interrupt_descriptor_table/interrupt_descriptor_table.cpp
new file mode 100644
index 0000000..7aa0859
--- /dev/null
+++ b/arch/x86_64/src/context_switching/interrupt_descriptor_table/interrupt_descriptor_table.cpp
@@ -0,0 +1,53 @@
+#include "arch/context_switching/interrupt_descriptor_table/interrupt_descriptor_table.hpp"
+
+#include "arch/exception_handling/assert.hpp"
+#include "arch/interrupt_handling/generic_interrupt_handler.hpp"
+#include "arch/kernel/cpu/idtr.hpp"
+
+namespace teachos::arch::context_switching::interrupt_descriptor_table
+{
+ namespace
+ {
+ /// @brief Amount of currently reserved interrupt indicies.
+ /// See https://wiki.osdev.org/Interrupt_Descriptor_Table#IDT_items for more information.
+ constexpr uint16_t RESERVED_INTERRUPT_COUNT = 256U;
+
+ auto create_interrupt_descriptor_table() -> interrupt_descriptor_table
+ {
+ interrupt_descriptor_table interrupt_descriptor_table{RESERVED_INTERRUPT_COUNT};
+
+ uint64_t offset = reinterpret_cast<uint64_t>(interrupt_handling::generic_interrupt_handler);
+ segment_selector selector{1U, segment_selector::REQUEST_LEVEL_KERNEL};
+ ist_offset ist{0U};
+ idt_flags flags{idt_flags::DESCRIPTOR_LEVEL_KERNEL | idt_flags::INTERRUPT_GATE | idt_flags::PRESENT};
+
+ for (std::size_t i = 0; i < interrupt_descriptor_table.size(); i++)
+ {
+ interrupt_descriptor_table.at(i) = {selector, ist, flags, offset};
+ }
+
+ return interrupt_descriptor_table;
+ }
+ } // namespace
+
+ auto get_or_create_interrupt_descriptor_table() -> interrupt_descriptor_table &
+ {
+ // Interrupt Descriptor Table needs to be kept alive
+ static interrupt_descriptor_table idt = create_interrupt_descriptor_table();
+ return idt;
+ }
+
+ auto update_interrupt_descriptor_table_register() -> void
+ {
+ decltype(auto) idt = get_or_create_interrupt_descriptor_table();
+
+ interrupt_descriptor_table_pointer idt_pointer{static_cast<uint16_t>((idt.size() * sizeof(gate_descriptor)) - 1),
+ idt.data()};
+ kernel::cpu::load_interrupt_descriptor_table(idt_pointer);
+
+ auto const stored_gdt_pointer = kernel::cpu::store_interrupt_descriptor_table();
+ arch::exception_handling::assert(
+ idt_pointer == stored_gdt_pointer,
+ "[Interrupt Descriptor Table] Loaded IDTR value is not the same as the stored value.");
+ }
+} // namespace teachos::arch::context_switching::interrupt_descriptor_table
diff --git a/arch/x86_64/src/context_switching/interrupt_descriptor_table/interrupt_descriptor_table_pointer.cpp b/arch/x86_64/src/context_switching/interrupt_descriptor_table/interrupt_descriptor_table_pointer.cpp
new file mode 100644
index 0000000..7bcbae6
--- /dev/null
+++ b/arch/x86_64/src/context_switching/interrupt_descriptor_table/interrupt_descriptor_table_pointer.cpp
@@ -0,0 +1,13 @@
+#include "arch/context_switching/interrupt_descriptor_table/interrupt_descriptor_table_pointer.hpp"
+
+namespace teachos::arch::context_switching::interrupt_descriptor_table
+{
+ interrupt_descriptor_table_pointer::interrupt_descriptor_table_pointer(uint16_t table_length,
+ gate_descriptor * address)
+ : table_length(table_length)
+ , address(address)
+ {
+ // Nothing to do.
+ }
+
+} // namespace teachos::arch::context_switching::interrupt_descriptor_table
diff --git a/arch/x86_64/src/context_switching/interrupt_descriptor_table/ist_offset.cpp b/arch/x86_64/src/context_switching/interrupt_descriptor_table/ist_offset.cpp
new file mode 100644
index 0000000..a70e75d
--- /dev/null
+++ b/arch/x86_64/src/context_switching/interrupt_descriptor_table/ist_offset.cpp
@@ -0,0 +1,10 @@
+#include "arch/context_switching/interrupt_descriptor_table/ist_offset.hpp"
+
+namespace teachos::arch::context_switching::interrupt_descriptor_table
+{
+ ist_offset::ist_offset(uint8_t index)
+ : _ist(index)
+ {
+ // Nothing to do.
+ }
+} // namespace teachos::arch::context_switching::interrupt_descriptor_table
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
new file mode 100644
index 0000000..27f0a3b
--- /dev/null
+++ b/arch/x86_64/src/context_switching/interrupt_descriptor_table/segment_selector.cpp
@@ -0,0 +1,15 @@
+#include "arch/context_switching/interrupt_descriptor_table/segment_selector.hpp"
+
+namespace teachos::arch::context_switching::interrupt_descriptor_table
+{
+ auto segment_selector::contains_flags(std::bitset<3U> other) const -> bool
+ {
+ return (std::bitset<3U>{_flags} & other) == other;
+ }
+
+ auto segment_selector::get_index() const -> uint16_t { return _index; }
+
+ 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
new file mode 100644
index 0000000..9539428
--- /dev/null
+++ b/arch/x86_64/src/context_switching/main.cpp
@@ -0,0 +1,63 @@
+#include "arch/context_switching/main.hpp"
+
+#include "arch/boot/pointers.hpp"
+#include "arch/context_switching/syscall/syscall_enable.hpp"
+#include "arch/kernel/cpu/call.hpp"
+#include "arch/kernel/cpu/if.hpp"
+#include "arch/kernel/cpu/segment_register.hpp"
+#include "arch/kernel/cpu/tr.hpp"
+#include "arch/user/main.hpp"
+
+namespace teachos::arch::context_switching
+{
+ namespace
+ {
+ 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_gdtr() -> void { kernel::cpu::call(KERNEL_CODE_POINTER); }
+ } // namespace
+
+ auto initialize_descriptor_tables() -> descriptor_tables
+ {
+ static bool initalized = false;
+
+ if (!initalized)
+ {
+ kernel::cpu::clear_interrupt_flag();
+
+ segment_descriptor_table::update_gdtr();
+ interrupt_descriptor_table::update_interrupt_descriptor_table_register();
+
+ reload_gdtr();
+ segment_descriptor_table::update_tss_register();
+
+ kernel::cpu::set_interrupt_flag();
+ initalized = true;
+ }
+
+ descriptor_tables tables = {segment_descriptor_table::get_or_create_gdt(),
+ interrupt_descriptor_table::get_or_create_interrupt_descriptor_table()};
+ return tables;
+ }
+
+ auto switch_to_user_mode() -> void
+ {
+ syscall::enable_syscall();
+ switch_context(USER_DATA_SEGMENT_SELECTOR, USER_CODE_SEGMENT_SELECTOR, user::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
diff --git a/arch/x86_64/src/context_switching/segment_descriptor_table/access_byte.cpp b/arch/x86_64/src/context_switching/segment_descriptor_table/access_byte.cpp
new file mode 100644
index 0000000..e31e021
--- /dev/null
+++ b/arch/x86_64/src/context_switching/segment_descriptor_table/access_byte.cpp
@@ -0,0 +1,17 @@
+#include "arch/context_switching/segment_descriptor_table/access_byte.hpp"
+
+namespace teachos::arch::context_switching::segment_descriptor_table
+{
+ access_byte::access_byte(uint8_t flags)
+ : _flags(flags)
+ {
+ // Nothing to do.
+ }
+
+ auto access_byte::contains_flags(std::bitset<8U> other) const -> bool
+ {
+ return (std::bitset<8U>{_flags} & other) == other;
+ }
+
+ auto access_byte::operator|=(std::bitset<8U> other) -> void { _flags |= other.to_ulong(); }
+} // namespace teachos::arch::context_switching::segment_descriptor_table
diff --git a/arch/x86_64/src/context_switching/segment_descriptor_table/gdt_flags.cpp b/arch/x86_64/src/context_switching/segment_descriptor_table/gdt_flags.cpp
new file mode 100644
index 0000000..e444a24
--- /dev/null
+++ b/arch/x86_64/src/context_switching/segment_descriptor_table/gdt_flags.cpp
@@ -0,0 +1,20 @@
+#include "arch/context_switching/segment_descriptor_table/gdt_flags.hpp"
+
+namespace teachos::arch::context_switching::segment_descriptor_table
+{
+ gdt_flags::gdt_flags(uint8_t flags, std::bitset<20U> limit)
+ : _limit_2(limit.to_ulong() >> 16U)
+ , _flags(flags)
+ {
+ // Nothing to do.
+ }
+
+ auto gdt_flags::contains_flags(std::bitset<4U> other) const -> bool
+ {
+ return (std::bitset<4U>{_flags} & other) == other;
+ }
+
+ auto gdt_flags::get_limit() const -> std::bitset<4U> { return std::bitset<4U>{_limit_2}; }
+
+ auto gdt_flags::operator|=(std::bitset<4U> other) -> void { _flags |= other.to_ulong(); }
+} // namespace teachos::arch::context_switching::segment_descriptor_table
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
new file mode 100644
index 0000000..d692e51
--- /dev/null
+++ b/arch/x86_64/src/context_switching/segment_descriptor_table/global_descriptor_table.cpp
@@ -0,0 +1,109 @@
+#include "arch/context_switching/segment_descriptor_table/global_descriptor_table.hpp"
+
+#include "arch/context_switching/segment_descriptor_table/segment_descriptor_extension.hpp"
+#include "arch/exception_handling/assert.hpp"
+#include "arch/kernel/cpu/gdtr.hpp"
+#include "arch/kernel/cpu/tr.hpp"
+
+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_base
+ {
+ uint64_t const base = 0x0;
+ std::bitset<20U> const limit{0xFFFFF};
+ gdt_flags flags{gdt_flags::GRANULARITY, limit};
+
+ access_level |= access_byte::PRESENT | access_byte::CODE_OR_DATA_SEGMENT;
+ if (segment_descriptor_type == segment_descriptor_type::CODE_SEGMENT)
+ {
+ flags |= gdt_flags::LONG_MODE;
+ access_level |= access_byte::CODE_SEGMENT | access_byte::READABLE;
+ }
+ else if (segment_descriptor_type == segment_descriptor_type::DATA_SEGMENT)
+ {
+ access_level |= access_byte::WRITABLE;
+ }
+
+ 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_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_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
+ {
+ 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_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;
+ }
+
+ auto update_gdtr() -> void
+ {
+ decltype(auto) gdt = get_or_create_gdt();
+
+ // 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.
+ 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();
+ arch::exception_handling::assert(
+ gdt_pointer == stored_gdt_pointer,
+ "[Global Descriptor Table] Loaded GDTR value is not the same as the stored value.");
+ }
+
+ auto update_tss_register() -> void
+ {
+ 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 * 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();
+ arch::exception_handling::assert(tss_selector == stored_task_register,
+ "[Global Descriptor Table] Loaded TR value is not the same as the stored value.");
+ }
+
+} // namespace teachos::arch::context_switching::segment_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
new file mode 100644
index 0000000..79088b8
--- /dev/null
+++ b/arch/x86_64/src/context_switching/segment_descriptor_table/global_descriptor_table_pointer.cpp
@@ -0,0 +1,11 @@
+#include "arch/context_switching/segment_descriptor_table/global_descriptor_table_pointer.hpp"
+
+namespace teachos::arch::context_switching::segment_descriptor_table
+{
+ global_descriptor_table_pointer::global_descriptor_table_pointer(uint16_t table_length, uint64_t * address)
+ : table_length(table_length)
+ , address(address)
+ {
+ // Nothing to do.
+ }
+} // 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
new file mode 100644
index 0000000..04804d9
--- /dev/null
+++ b/arch/x86_64/src/context_switching/segment_descriptor_table/segment_descriptor_base.cpp
@@ -0,0 +1,38 @@
+#include "arch/context_switching/segment_descriptor_table/segment_descriptor_base.hpp"
+
+namespace teachos::arch::context_switching::segment_descriptor_table
+{
+ 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
new file mode 100644
index 0000000..a28ec9b
--- /dev/null
+++ b/arch/x86_64/src/context_switching/segment_descriptor_table/segment_descriptor_extension.cpp
@@ -0,0 +1,24 @@
+#include "arch/context_switching/segment_descriptor_table/segment_descriptor_extension.hpp"
+
+namespace teachos::arch::context_switching::segment_descriptor_table
+{
+ 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/context_switching/syscall/main.cpp b/arch/x86_64/src/context_switching/syscall/main.cpp
new file mode 100644
index 0000000..e291c10
--- /dev/null
+++ b/arch/x86_64/src/context_switching/syscall/main.cpp
@@ -0,0 +1,35 @@
+#include "arch/context_switching/syscall/main.hpp"
+
+namespace teachos::arch::context_switching::syscall
+{
+ auto syscall(type syscall_number, arguments args) -> response
+ {
+ asm volatile("mov %[input], %%rax"
+ : /* no output from call */
+ : [input] "m"(syscall_number)
+ : "memory");
+
+ asm volatile("mov %[input], %%rdi " : /* no output from call */ : [input] "m"(args.arg_0) : "memory");
+ asm volatile("mov %[input], %%rsi" : /* no output from call */ : [input] "m"(args.arg_1) : "memory");
+ asm volatile("mov %[input], %%rdx" : /* no output from call */ : [input] "m"(args.arg_2) : "memory");
+ asm volatile("mov %[input], %%r10" : /* no output from call */ : [input] "m"(args.arg_3) : "memory");
+ asm volatile("mov %[input], %%r8" : /* no output from call */ : [input] "m"(args.arg_4) : "memory");
+ asm volatile("mov %[input], %%r9" : /* no output from call */ : [input] "m"(args.arg_5) : "memory");
+
+ asm volatile("syscall");
+
+ arguments values{};
+ asm volatile("mov %%rdi, %[output]" : [output] "=m"(values.arg_0));
+ asm volatile("mov %%rsi, %[output]" : [output] "=m"(values.arg_1));
+ asm volatile("mov %%rdx, %[output]" : [output] "=m"(values.arg_2));
+ asm volatile("mov %%r10, %[output]" : [output] "=m"(values.arg_3));
+ asm volatile("mov %%r8, %[output]" : [output] "=m"(values.arg_4));
+ asm volatile("mov %%r9, %[output]" : [output] "=m"(values.arg_5));
+
+ error error_code{};
+ asm volatile("mov %%rax, %[output]" : [output] "=m"(error_code));
+
+ return {error_code, values};
+ }
+
+} // namespace teachos::arch::context_switching::syscall
diff --git a/arch/x86_64/src/context_switching/syscall/syscall_enable.cpp b/arch/x86_64/src/context_switching/syscall/syscall_enable.cpp
new file mode 100644
index 0000000..3c43336
--- /dev/null
+++ b/arch/x86_64/src/context_switching/syscall/syscall_enable.cpp
@@ -0,0 +1,32 @@
+#include "arch/context_switching/syscall/syscall_enable.hpp"
+
+#include "arch/context_switching/interrupt_descriptor_table/segment_selector.hpp"
+#include "arch/context_switching/syscall/syscall_handler.hpp"
+#include "arch/kernel/cpu/msr.hpp"
+
+namespace teachos::arch::context_switching::syscall
+{
+ namespace
+ {
+ interrupt_descriptor_table::segment_selector constexpr KERNEL_CODE_SEGMENT_SELECTOR{
+ 1U, interrupt_descriptor_table::segment_selector::REQUEST_LEVEL_KERNEL};
+
+ auto constexpr IA32_STAR_ADDRESS = 0xC0000081;
+ auto constexpr IA32_LSTAR_ADDRESS = 0xC0000082;
+ auto constexpr IA32_FMASK_ADDRESS = 0xC0000084;
+
+ } // namespace
+
+ auto enable_syscall() -> 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 const kernel_cs = KERNEL_CODE_SEGMENT_SELECTOR;
+ uint64_t const star_value = (kernel_cs << 32) | (kernel_cs << 48);
+ kernel::cpu::write_msr(IA32_STAR_ADDRESS, star_value);
+
+ kernel::cpu::set_efer_bit(kernel::cpu::efer_flags::SCE);
+ }
+} // namespace teachos::arch::context_switching::syscall
diff --git a/arch/x86_64/src/context_switching/syscall/syscall_handler.cpp b/arch/x86_64/src/context_switching/syscall/syscall_handler.cpp
new file mode 100644
index 0000000..84dbe5f
--- /dev/null
+++ b/arch/x86_64/src/context_switching/syscall/syscall_handler.cpp
@@ -0,0 +1,118 @@
+#include "arch/context_switching/syscall/syscall_handler.hpp"
+
+#include "arch/context_switching/syscall/main.hpp"
+#include "arch/exception_handling/assert.hpp"
+#include "arch/exception_handling/panic.hpp"
+#include "arch/memory/heap/global_heap_allocator.hpp"
+#include "arch/memory/main.hpp"
+#include "arch/video/vga/text.hpp"
+
+namespace teachos::arch::context_switching::syscall
+{
+
+ namespace
+ {
+ auto write_to_vga_buffer(uint64_t buffer) -> response
+ {
+ video::vga::text::write(reinterpret_cast<const char *>(buffer),
+ video::vga::text::common_attributes::green_on_black);
+ video::vga::text::newline();
+ return {error::OK};
+ }
+
+ auto expand_user_heap() -> response
+ {
+ static auto current_heap_end = memory::heap::USER_HEAP_START;
+ uint64_t const heap_start = current_heap_end;
+ memory::remap_heap(heap_start, memory::heap::USER_HEAP_SIZE, memory::paging::entry::USER_ACCESSIBLE);
+ current_heap_end += memory::heap::USER_HEAP_SIZE;
+ return {error::OK, {heap_start, memory::heap::USER_HEAP_SIZE}};
+ }
+ } // namespace
+
+ auto syscall_handler() -> void
+ {
+ // Saving state of rcx and r11 because it is required by sysretq to function.
+ // Calls to other functions potentially overwrite these registers, because of
+ // callee saved calling convention.
+ uint64_t return_instruction_pointer, rflags = {};
+ asm volatile("mov %%rcx, %[output]" : [output] "=m"(return_instruction_pointer));
+ asm volatile("mov %%r11, %[output]" : [output] "=m"(rflags));
+
+ uint64_t syscall_number, arg_0, arg_1, arg_2, arg_3, arg_4, arg_5 = {};
+ asm volatile("mov %%rdi, %[output]" : [output] "=m"(arg_0));
+ asm volatile("mov %%rsi, %[output]" : [output] "=m"(arg_1));
+ asm volatile("mov %%rdx, %[output]" : [output] "=m"(arg_2));
+ asm volatile("mov %%r10, %[output]" : [output] "=m"(arg_3));
+ asm volatile("mov %%r8, %[output]" : [output] "=m"(arg_4));
+ asm volatile("mov %%r9, %[output]" : [output] "=m"(arg_5));
+
+ // RAX is read last, because paired with our type enum, we can use it to check
+ // if the register has been written by the compiled code between executing the syscall
+ // and now.
+ asm volatile("mov %%rax, %[output]" : [output] "=m"(syscall_number));
+
+ response result;
+ switch (static_cast<type>(syscall_number))
+ {
+ case type::WRITE:
+ result = write_to_vga_buffer(arg_0);
+ break;
+ case type::EXPAND_HEAP:
+ result = expand_user_heap();
+ break;
+ case type::ASSERT:
+ teachos::arch::exception_handling::assert(arg_0, reinterpret_cast<const char *>(arg_1));
+ break;
+ default:
+ teachos::arch::exception_handling::panic("[Syscall Handler] Invalid syscall number");
+ break;
+ }
+
+ asm volatile("mov %[input], %%rax"
+ : /* no output from call */
+ : [input] "m"(result.error_code)
+ : "memory");
+
+ asm volatile("mov %[input], %%rdi"
+ : /* no output from call */
+ : [input] "m"(result.values.arg_0)
+ : "memory");
+ asm volatile("mov %[input], %%rsi"
+ : /* no output from call */
+ : [input] "m"(result.values.arg_1)
+ : "memory");
+ asm volatile("mov %[input], %%rdx"
+ : /* no output from call */
+ : [input] "m"(result.values.arg_2)
+ : "memory");
+ asm volatile("mov %[input], %%r10"
+ : /* no output from call */
+ : [input] "m"(result.values.arg_3)
+ : "memory");
+ asm volatile("mov %[input], %%r8"
+ : /* no output from call */
+ : [input] "m"(result.values.arg_4)
+ : "memory");
+ asm volatile("mov %[input], %%r9"
+ : /* no output from call */
+ : [input] "m"(result.values.arg_5)
+ : "memory");
+
+ asm volatile("mov %[input], %%rcx"
+ : /* no output from call */
+ : [input] "m"(return_instruction_pointer)
+ : "memory");
+ asm volatile("mov %[input], %%r11"
+ : /* no output from call */
+ : [input] "m"(rflags)
+ : "memory");
+
+ // Additionally call leave, because x86 allocates stack space for the internal variables. If we do not clean up this
+ // newly created stack frame the syscall instruction that landed in this syscall_handler, will never return to the
+ // method that originally called it, because the RIP has not been restored from the previous stack frame.
+ asm volatile("leave\n"
+ "sysretq");
+ }
+
+} // namespace teachos::arch::context_switching::syscall
diff --git a/arch/x86_64/src/interrupt_handling/generic_interrupt_handler.cpp b/arch/x86_64/src/interrupt_handling/generic_interrupt_handler.cpp
new file mode 100644
index 0000000..9d061a8
--- /dev/null
+++ b/arch/x86_64/src/interrupt_handling/generic_interrupt_handler.cpp
@@ -0,0 +1,13 @@
+#include "arch/interrupt_handling/generic_interrupt_handler.hpp"
+
+#include "arch/video/vga/text.hpp"
+
+namespace teachos::arch::interrupt_handling
+{
+ auto generic_interrupt_handler(interrupt_frame * frame) -> void
+ {
+ (void)frame;
+ video::vga::text::write("An Interrupt occurred.", video::vga::text::common_attributes::green_on_black);
+ video::vga::text::newline();
+ }
+} // namespace teachos::arch::interrupt_handling
diff --git a/arch/x86_64/src/kernel/cpu/call.cpp b/arch/x86_64/src/kernel/cpu/call.cpp
new file mode 100644
index 0000000..98fa248
--- /dev/null
+++ b/arch/x86_64/src/kernel/cpu/call.cpp
@@ -0,0 +1,9 @@
+#include "arch/kernel/cpu/call.hpp"
+
+namespace teachos::arch::kernel::cpu
+{
+ auto call(far_pointer pointer) -> void
+ {
+ asm volatile("rex64 lcall *%[input]" : /* no output from call */ : [input] "m"(pointer));
+ }
+} // namespace teachos::arch::kernel::cpu
diff --git a/arch/x86_64/src/memory/cpu/control_register.cpp b/arch/x86_64/src/kernel/cpu/control_register.cpp
index 7ee88b5..41b8cd7 100644
--- a/arch/x86_64/src/memory/cpu/control_register.cpp
+++ b/arch/x86_64/src/kernel/cpu/control_register.cpp
@@ -1,10 +1,10 @@
-#include "arch/memory/cpu/control_register.hpp"
+#include "arch/kernel/cpu/control_register.hpp"
#include "arch/exception_handling/panic.hpp"
#include <type_traits>
-namespace teachos::arch::memory::cpu
+namespace teachos::arch::kernel::cpu
{
auto read_control_register(control_register cr) -> uint64_t
{
@@ -23,9 +23,6 @@ namespace teachos::arch::memory::cpu
case control_register::CR4:
asm volatile("mov %%cr4, %[output]" : [output] "=r"(current_value));
break;
- default:
- exception_handling::panic("[Control Register] Attempted to read non-existent or reserved control register");
- break;
}
return current_value;
}
@@ -58,9 +55,6 @@ namespace teachos::arch::memory::cpu
: [input] "r"(new_value)
: "memory");
break;
- default:
- exception_handling::panic("[Control Register] Attempted to write non-existent or reserved control register");
- break;
}
}
@@ -69,4 +63,4 @@ namespace teachos::arch::memory::cpu
auto const cr0 = read_control_register(control_register::CR0);
write_control_register(control_register::CR0, static_cast<std::underlying_type<cr0_flags>::type>(flag) | cr0);
}
-} // namespace teachos::arch::memory::cpu
+} // namespace teachos::arch::kernel::cpu
diff --git a/arch/x86_64/src/kernel/cpu/gdtr.cpp b/arch/x86_64/src/kernel/cpu/gdtr.cpp
new file mode 100644
index 0000000..74a4e1c
--- /dev/null
+++ b/arch/x86_64/src/kernel/cpu/gdtr.cpp
@@ -0,0 +1,19 @@
+#include "arch/kernel/cpu/gdtr.hpp"
+
+#include "arch/context_switching/segment_descriptor_table/global_descriptor_table_pointer.hpp"
+
+namespace teachos::arch::kernel::cpu
+{
+ auto store_global_descriptor_table() -> context_switching::segment_descriptor_table::global_descriptor_table_pointer
+ {
+ context_switching::segment_descriptor_table::global_descriptor_table_pointer current_value{};
+ asm("sgdt %[output]" : [output] "=m"(current_value));
+ return current_value;
+ }
+
+ auto load_global_descriptor_table(
+ context_switching::segment_descriptor_table::global_descriptor_table_pointer const & gdt_pointer) -> void
+ {
+ asm volatile("lgdt %[input]" : /* no output from call */ : [input] "m"(gdt_pointer));
+ }
+} // namespace teachos::arch::kernel::cpu
diff --git a/arch/x86_64/src/kernel/cpu/idtr.cpp b/arch/x86_64/src/kernel/cpu/idtr.cpp
new file mode 100644
index 0000000..7aa20c1
--- /dev/null
+++ b/arch/x86_64/src/kernel/cpu/idtr.cpp
@@ -0,0 +1,18 @@
+#include "arch/kernel/cpu/idtr.hpp"
+
+namespace teachos::arch::kernel::cpu
+{
+ auto store_interrupt_descriptor_table()
+ -> context_switching::interrupt_descriptor_table::interrupt_descriptor_table_pointer
+ {
+ context_switching::interrupt_descriptor_table::interrupt_descriptor_table_pointer current_value{};
+ asm("sidt %[output]" : [output] "=m"(current_value));
+ return current_value;
+ }
+
+ auto load_interrupt_descriptor_table(
+ context_switching::interrupt_descriptor_table::interrupt_descriptor_table_pointer const & idt_pointer) -> void
+ {
+ asm volatile("lidt %[input]" : /* no output from call */ : [input] "m"(idt_pointer));
+ }
+} // namespace teachos::arch::kernel::cpu
diff --git a/arch/x86_64/src/kernel/cpu/if.cpp b/arch/x86_64/src/kernel/cpu/if.cpp
new file mode 100644
index 0000000..60a90a3
--- /dev/null
+++ b/arch/x86_64/src/kernel/cpu/if.cpp
@@ -0,0 +1,7 @@
+namespace teachos::arch::kernel::cpu
+{
+ auto set_interrupt_flag() -> void { asm volatile("sti"); }
+
+ auto clear_interrupt_flag() -> void { asm volatile("cli"); }
+
+} // namespace teachos::arch::kernel::cpu
diff --git a/arch/x86_64/src/memory/cpu/msr.cpp b/arch/x86_64/src/kernel/cpu/msr.cpp
index b83f902..9c474a1 100644
--- a/arch/x86_64/src/memory/cpu/msr.cpp
+++ b/arch/x86_64/src/kernel/cpu/msr.cpp
@@ -1,6 +1,6 @@
-#include "arch/memory/cpu/msr.hpp"
+#include "arch/kernel/cpu/msr.hpp"
-namespace teachos::arch::memory::cpu
+namespace teachos::arch::kernel::cpu
{
namespace
{
@@ -16,7 +16,7 @@ namespace teachos::arch::memory::cpu
auto write_msr(uint32_t msr, uint64_t value) -> void
{
- uint32_t low = value & 0xFFFFFFFF;
+ uint32_t low = value;
uint32_t high = value >> 32;
asm volatile("wrmsr"
: /* no output from call */
@@ -28,4 +28,4 @@ namespace teachos::arch::memory::cpu
auto const efer = read_msr(IA32_EFER_ADDRESS);
write_msr(IA32_EFER_ADDRESS, static_cast<std::underlying_type<efer_flags>::type>(flag) | efer);
}
-} // namespace teachos::arch::memory::cpu
+} // namespace teachos::arch::kernel::cpu
diff --git a/arch/x86_64/src/kernel/cpu/segment_register.cpp b/arch/x86_64/src/kernel/cpu/segment_register.cpp
new file mode 100644
index 0000000..b08c9c4
--- /dev/null
+++ b/arch/x86_64/src/kernel/cpu/segment_register.cpp
@@ -0,0 +1,98 @@
+#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);
+ }
+
+ 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
diff --git a/arch/x86_64/src/memory/cpu/tlb.cpp b/arch/x86_64/src/kernel/cpu/tlb.cpp
index 591d9fc..a09001c 100644
--- a/arch/x86_64/src/memory/cpu/tlb.cpp
+++ b/arch/x86_64/src/kernel/cpu/tlb.cpp
@@ -1,10 +1,10 @@
-#include "arch/memory/cpu/tlb.hpp"
+#include "arch/kernel/cpu/tlb.hpp"
-#include "arch/memory/cpu/control_register.hpp"
+#include "arch/kernel/cpu/control_register.hpp"
-namespace teachos::arch::memory::cpu
+namespace teachos::arch::kernel::cpu
{
- auto tlb_flush(paging::virtual_address address) -> void
+ auto tlb_flush(memory::paging::virtual_address address) -> void
{
asm volatile("invlpg (%[input])" : /* no output from call */ : [input] "r"(address) : "memory");
}
@@ -13,4 +13,4 @@ namespace teachos::arch::memory::cpu
{
write_control_register(cpu::control_register::CR3, read_control_register(cpu::control_register::CR3));
}
-} // namespace teachos::arch::memory::cpu
+} // 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
new file mode 100644
index 0000000..a435540
--- /dev/null
+++ b/arch/x86_64/src/kernel/cpu/tr.cpp
@@ -0,0 +1,16 @@
+#include "arch/kernel/cpu/tr.hpp"
+
+namespace teachos::arch::kernel::cpu
+{
+ auto store_task_register() -> uint16_t
+ {
+ uint16_t current_value{};
+ asm("str %[output]" : [output] "=r"(current_value));
+ return current_value;
+ }
+
+ auto load_task_register(uint16_t gdt_offset) -> void
+ {
+ asm volatile("ltr %[input]" : /* no output from call */ : [input] "m"(gdt_offset));
+ }
+} // namespace teachos::arch::kernel::cpu
diff --git a/arch/x86_64/src/kernel/main.cpp b/arch/x86_64/src/kernel/main.cpp
index 472aed5..43b5f90 100644
--- a/arch/x86_64/src/kernel/main.cpp
+++ b/arch/x86_64/src/kernel/main.cpp
@@ -1,9 +1,15 @@
#include "arch/kernel/main.hpp"
+#include "arch/boot/pointers.hpp"
+#include "arch/context_switching/interrupt_descriptor_table/segment_selector.hpp"
+#include "arch/context_switching/main.hpp"
+#include "arch/kernel/cpu/if.hpp"
+#include "arch/kernel/cpu/segment_register.hpp"
#include "arch/memory/heap/bump_allocator.hpp"
#include "arch/memory/heap/global_heap_allocator.hpp"
#include "arch/memory/main.hpp"
#include "arch/memory/multiboot/reader.hpp"
+#include "arch/stl/vector.hpp"
#include "arch/video/vga/text.hpp"
namespace teachos::arch::kernel
@@ -55,7 +61,11 @@ namespace teachos::arch::kernel
// stack_overflow_test(0);
memory::heap::global_heap_allocator::register_heap_allocator(memory::heap::heap_allocator_type::LINKED_LIST);
+ // heap_test();
- heap_test();
+ auto address = memory::heap::global_heap_allocator::kmalloc(8U);
+ (void)address;
+
+ context_switching::switch_to_user_mode();
}
} // namespace teachos::arch::kernel
diff --git a/arch/x86_64/src/memory/allocator/area_frame_allocator.cpp b/arch/x86_64/src/memory/allocator/area_frame_allocator.cpp
index cb4fefa..a5a1b49 100644
--- a/arch/x86_64/src/memory/allocator/area_frame_allocator.cpp
+++ b/arch/x86_64/src/memory/allocator/area_frame_allocator.cpp
@@ -9,7 +9,7 @@
namespace teachos::arch::memory::allocator
{
area_frame_allocator::area_frame_allocator(multiboot::memory_information const & mem_info)
- : next_free_frame(0U)
+ : next_free_frame()
, current_area(std::nullopt)
, memory_areas(mem_info.areas)
, kernel_start(physical_frame::containing_address(mem_info.kernel_start))
diff --git a/arch/x86_64/src/memory/heap/bump_allocator.cpp b/arch/x86_64/src/memory/heap/bump_allocator.cpp
index df95346..525f45c 100644
--- a/arch/x86_64/src/memory/heap/bump_allocator.cpp
+++ b/arch/x86_64/src/memory/heap/bump_allocator.cpp
@@ -24,11 +24,13 @@ namespace teachos::arch::memory::heap
auto bump_allocator::allocate(std::size_t size) -> void *
{
+ // Reading the value only has to be done once, because compare_exchange_weak updates the value as well if the
+ // exchange failed, becuase the value was not the expected one.
+ auto alloc_start = next.load(std::memory_order::relaxed);
// Repeat allocation until it succeeds, has to be done, because another allocator could overtake it at any time
// causing the value to differ and the calculation to have to be redone.
for (;;)
{
- auto alloc_start = next.load(std::memory_order::relaxed);
auto const alloc_end = saturating_add(alloc_start, size);
arch::exception_handling::assert(alloc_end <= heap_end, "[Heap Allocator] Out of memory");
// Check if the atomic value is still the one initally loaded, if it isn't we have been overtaken by another
diff --git a/arch/x86_64/src/memory/heap/global_heap_allocator.cpp b/arch/x86_64/src/memory/heap/global_heap_allocator.cpp
index c1ca160..35cd623 100644
--- a/arch/x86_64/src/memory/heap/global_heap_allocator.cpp
+++ b/arch/x86_64/src/memory/heap/global_heap_allocator.cpp
@@ -1,20 +1,43 @@
#include "arch/memory/heap/global_heap_allocator.hpp"
+#include "arch/context_switching/syscall/main.hpp"
#include "arch/exception_handling/assert.hpp"
+#include "arch/kernel/cpu/segment_register.hpp"
#include "arch/memory/heap/bump_allocator.hpp"
#include "arch/memory/heap/linked_list_allocator.hpp"
+#include "arch/memory/heap/user_heap_allocator.hpp"
namespace teachos::arch::memory::heap
{
- heap_allocator * global_heap_allocator::allocator_instance = nullptr;
+ namespace
+ {
+ constexpr char NOT_REGISTRED_ERROR_MESSAGE[] =
+ "Attempted to allocate or deallocate using the global_heap_allocator before "
+ "register_heap_allocation_type was called.";
+ constexpr uint16_t KERNEL_CODE_INDEX = 1U;
+
+ [[gnu::section(".user_text")]]
+ auto os_in_kernel_mode() -> bool
+ {
+ auto const cs = teachos::arch::kernel::cpu::read_code_segment_register();
+ return cs.get_index() == KERNEL_CODE_INDEX;
+ }
+ } // namespace
+
+ heap_allocator * global_heap_allocator::kernel_allocator_instance = nullptr;
+ user_heap_allocator * global_heap_allocator::user_allocator_instance = nullptr;
+
+ auto global_heap_allocator::kmalloc(std::size_t size) -> void * { return kernel().allocate(size); }
- auto global_heap_allocator::allocate(std::size_t size) -> void * { return get().allocate(size); }
+ auto global_heap_allocator::kfree(void * pointer) noexcept -> void { kernel().deallocate(pointer); }
- auto global_heap_allocator::deallocate(void * pointer) noexcept -> void { get().deallocate(pointer); }
+ auto global_heap_allocator::malloc(std::size_t size) -> void * { return user().allocate(size); }
+
+ auto global_heap_allocator::free(void * pointer) noexcept -> void { user().deallocate(pointer); }
auto global_heap_allocator::register_heap_allocator(heap_allocator_type new_type) -> void
{
- exception_handling::assert(allocator_instance == nullptr,
+ exception_handling::assert(kernel_allocator_instance == nullptr,
"Calling register_heap_allocator_type can only be done once.");
switch (new_type)
@@ -23,56 +46,90 @@ namespace teachos::arch::memory::heap
// Nothing to do
break;
case heap_allocator_type::BUMP: {
- static bump_allocator allocator{HEAP_START, HEAP_START + HEAP_SIZE};
- allocator_instance = &allocator;
+ static bump_allocator kernel_allocator{KERNEL_HEAP_START, KERNEL_HEAP_START + KERNEL_HEAP_SIZE};
+ kernel_allocator_instance = &kernel_allocator;
break;
}
case heap_allocator_type::LINKED_LIST: {
- static linked_list_allocator allocator{HEAP_START, HEAP_START + HEAP_SIZE};
- allocator_instance = &allocator;
+ static linked_list_allocator kernel_allocator{KERNEL_HEAP_START, KERNEL_HEAP_START + KERNEL_HEAP_SIZE};
+ kernel_allocator_instance = &kernel_allocator;
break;
}
}
+
+ [[gnu::section(".user_data")]]
+ static user_heap_allocator user_allocator{};
+ user_allocator_instance = &user_allocator;
}
- auto global_heap_allocator::get() -> heap_allocator &
+ auto global_heap_allocator::kernel() -> heap_allocator &
{
- exception_handling::assert(allocator_instance != nullptr,
- "Attempted to allocate or deallocate using the global_heap_allocator before "
- "register_heap_allocation_type was called.");
+ exception_handling::assert(kernel_allocator_instance != nullptr, NOT_REGISTRED_ERROR_MESSAGE);
+
+ return *kernel_allocator_instance;
+ }
- return *allocator_instance;
+ auto global_heap_allocator::user() -> user_heap_allocator &
+ {
+ context_switching::syscall::syscall(
+ context_switching::syscall::type::ASSERT,
+ {user_allocator_instance != nullptr, reinterpret_cast<uint64_t>(&NOT_REGISTRED_ERROR_MESSAGE)});
+ return *user_allocator_instance;
}
} // namespace teachos::arch::memory::heap
auto operator new(std::size_t size) -> void *
{
- return teachos::arch::memory::heap::global_heap_allocator::allocate(size);
+ if (teachos::arch::memory::heap::os_in_kernel_mode())
+ {
+ return teachos::arch::memory::heap::global_heap_allocator::kmalloc(size);
+ }
+ return teachos::arch::memory::heap::global_heap_allocator::malloc(size);
}
auto operator delete(void * pointer) noexcept -> void
{
- teachos::arch::memory::heap::global_heap_allocator::deallocate(pointer);
+ if (teachos::arch::memory::heap::os_in_kernel_mode())
+ {
+ teachos::arch::memory::heap::global_heap_allocator::kfree(pointer);
+ }
+ teachos::arch::memory::heap::global_heap_allocator::free(pointer);
}
auto operator delete(void * pointer, std::size_t size) noexcept -> void
{
(void)size;
- teachos::arch::memory::heap::global_heap_allocator::deallocate(pointer);
+ if (teachos::arch::memory::heap::os_in_kernel_mode())
+ {
+ teachos::arch::memory::heap::global_heap_allocator::kfree(pointer);
+ }
+ teachos::arch::memory::heap::global_heap_allocator::free(pointer);
}
auto operator new[](std::size_t size) -> void *
{
- return teachos::arch::memory::heap::global_heap_allocator::allocate(size);
+ if (teachos::arch::memory::heap::os_in_kernel_mode())
+ {
+ return teachos::arch::memory::heap::global_heap_allocator::kmalloc(size);
+ }
+ return teachos::arch::memory::heap::global_heap_allocator::malloc(size);
}
auto operator delete[](void * pointer) noexcept -> void
{
- teachos::arch::memory::heap::global_heap_allocator::deallocate(pointer);
+ if (teachos::arch::memory::heap::os_in_kernel_mode())
+ {
+ teachos::arch::memory::heap::global_heap_allocator::kfree(pointer);
+ }
+ teachos::arch::memory::heap::global_heap_allocator::free(pointer);
}
auto operator delete[](void * pointer, std::size_t size) noexcept -> void
{
(void)size;
- teachos::arch::memory::heap::global_heap_allocator::deallocate(pointer);
+ if (teachos::arch::memory::heap::os_in_kernel_mode())
+ {
+ teachos::arch::memory::heap::global_heap_allocator::kfree(pointer);
+ }
+ teachos::arch::memory::heap::global_heap_allocator::free(pointer);
}
diff --git a/arch/x86_64/src/memory/heap/linked_list_allocator.cpp b/arch/x86_64/src/memory/heap/linked_list_allocator.cpp
index a824c8a..63a6111 100644
--- a/arch/x86_64/src/memory/heap/linked_list_allocator.cpp
+++ b/arch/x86_64/src/memory/heap/linked_list_allocator.cpp
@@ -8,10 +8,8 @@
namespace teachos::arch::memory::heap
{
linked_list_allocator::linked_list_allocator(std::size_t heap_start, std::size_t heap_end)
- : heap_start(heap_start)
- , heap_end(heap_end)
- , first(nullptr)
- , mutex{shared::mutex{}}
+ : first(nullptr)
+ , mutex{stl::mutex{}}
{
auto const heap_size = heap_end - heap_start;
exception_handling::assert(
diff --git a/arch/x86_64/src/memory/heap/memory_block.cpp b/arch/x86_64/src/memory/heap/memory_block.cpp
index 446cd96..bc97bd6 100644
--- a/arch/x86_64/src/memory/heap/memory_block.cpp
+++ b/arch/x86_64/src/memory/heap/memory_block.cpp
@@ -6,10 +6,10 @@ namespace teachos::arch::memory::heap
{
memory_block::memory_block(std::size_t size, memory_block * next)
{
- memset(static_cast<void *>(this), 0, size);
+ memset(static_cast<void *>(this), 0U, size);
this->size = size;
this->next = next;
}
- memory_block::~memory_block() { memset(static_cast<void *>(this), 0, sizeof(memory_block)); }
+ memory_block::~memory_block() { memset(static_cast<void *>(this), 0U, sizeof(memory_block)); }
} // namespace teachos::arch::memory::heap
diff --git a/arch/x86_64/src/memory/heap/user_heap_allocator.cpp b/arch/x86_64/src/memory/heap/user_heap_allocator.cpp
new file mode 100644
index 0000000..427a68a
--- /dev/null
+++ b/arch/x86_64/src/memory/heap/user_heap_allocator.cpp
@@ -0,0 +1,200 @@
+#include "arch/memory/heap/user_heap_allocator.hpp"
+
+#include "arch/context_switching/syscall/main.hpp"
+
+#include <algorithm>
+
+namespace teachos::arch::memory::heap
+{
+ auto user_heap_allocator::allocate(std::size_t size) -> void *
+ {
+ // Add size of size_t to the total allocated size, because we add a header that includes the size of the allocated
+ // block, to allow for deallocation without the need to call with the corresponding size
+ auto const total_size = size + sizeof(std::size_t);
+ mutex.lock();
+
+ memory_block * previous = nullptr;
+ auto current = first;
+
+ while (current != nullptr)
+ {
+ auto memory = allocate_into_memory_block_if_big_enough(current, previous, total_size);
+ if (memory.has_value())
+ {
+ return memory.value();
+ }
+
+ previous = current;
+ current = current->next;
+ }
+
+ current = expand_heap_if_full();
+
+ if (current != nullptr)
+ {
+ auto memory = allocate_into_memory_block_if_big_enough(current, previous, total_size);
+ if (memory.has_value())
+ {
+ return memory.value();
+ }
+ }
+
+ char constexpr OUT_OF_MEMORY_ERROR_MESSAGE[] = "[Linked List Allocator] Out of memory";
+ context_switching::syscall::syscall(context_switching::syscall::type::ASSERT,
+ {false, reinterpret_cast<uint64_t>(&OUT_OF_MEMORY_ERROR_MESSAGE)});
+ return nullptr;
+ }
+
+ auto user_heap_allocator::deallocate(void * pointer) noexcept -> void
+ {
+ mutex.lock();
+
+ // Read configured header size of the complete allocated block
+ auto const header_pointer = reinterpret_cast<void *>(reinterpret_cast<std::size_t>(pointer) - sizeof(std::size_t));
+ auto const total_size = *reinterpret_cast<std::size_t *>(header_pointer);
+
+ auto const start_address = reinterpret_cast<std::size_t>(header_pointer);
+ auto const end_address = start_address + total_size;
+
+ memory_block * previous = nullptr;
+ auto current = first;
+
+ while (current != nullptr)
+ {
+ // Current address of the free memory block now points to an address that is after our block to deallocate in heap
+ // memory space.
+ if (reinterpret_cast<std::size_t>(current) >= end_address)
+ {
+ break;
+ }
+
+ previous = current;
+ current = current->next;
+ }
+
+ coalesce_free_memory_block(previous, current, header_pointer, total_size);
+ mutex.unlock();
+ }
+
+ auto user_heap_allocator::allocate_into_memory_block_if_big_enough(memory_block * current, memory_block * previous,
+ std::size_t total_size) -> std::optional<void *>
+ {
+ if (current->size == total_size)
+ {
+ auto const memory_address = remove_free_memory_block(previous, current);
+ new (memory_address) std::size_t(total_size);
+ mutex.unlock();
+ return reinterpret_cast<void *>(reinterpret_cast<std::size_t>(memory_address) + sizeof(std::size_t));
+ }
+ else if (current->size >= total_size + min_allocatable_size())
+ {
+ // Ensure that the allocated size block is atleast 16 bytes (required because if we free the hole afterwards
+ // there needs to be enough space for a memory block). Therefore we allocate more than is actually required if
+ // the total size was less and simply deallocate it as well
+ auto const max_size = std::max(total_size, min_allocatable_size());
+ auto const memory_address = split_free_memory_block(previous, current, max_size);
+ new (memory_address) std::size_t(max_size);
+ mutex.unlock();
+ return reinterpret_cast<void *>(reinterpret_cast<std::size_t>(memory_address) + sizeof(std::size_t));
+ }
+ return std::nullopt;
+ }
+
+ auto user_heap_allocator::expand_heap_if_full() -> memory_block *
+ {
+ auto const result = context_switching::syscall::syscall(context_switching::syscall::type::EXPAND_HEAP);
+
+ uint64_t const heap_start = result.values.arg_0;
+ uint64_t const heap_size = result.values.arg_1;
+ return !result.error_code ? new (reinterpret_cast<void *>(heap_start)) memory_block(heap_size, nullptr) : nullptr;
+ }
+
+ auto user_heap_allocator::remove_free_memory_block(memory_block * previous_block, memory_block * current_block)
+ -> void *
+ {
+ return replace_free_memory_block(previous_block, current_block, current_block->next);
+ }
+
+ auto user_heap_allocator::split_free_memory_block(memory_block * previous_block, memory_block * current_block,
+ std::size_t size) -> void *
+ {
+ auto const end_address = reinterpret_cast<std::size_t>(current_block) + size;
+ auto const new_block =
+ new (reinterpret_cast<void *>(end_address)) memory_block(current_block->size - size, current_block->next);
+ return replace_free_memory_block(previous_block, current_block, new_block);
+ }
+
+ auto user_heap_allocator::replace_free_memory_block(memory_block * previous_block, memory_block * current_block,
+ memory_block * new_block) -> void *
+ {
+ auto const start_address = reinterpret_cast<std::size_t>(current_block);
+ // If we want to allocate into the first block that is before any other free block, then there exists no previous
+ // free block (nullptr). Therefore we have to overwrite the first block instead of overwriting its next value.
+ if (previous_block == nullptr)
+ {
+ first = new_block;
+ }
+ else
+ {
+ previous_block->next = new_block;
+ }
+ current_block->~memory_block();
+ return reinterpret_cast<void *>(start_address);
+ }
+
+ auto user_heap_allocator::coalesce_free_memory_block(memory_block * previous_block, memory_block * current_block,
+ void * pointer, std::size_t size) -> void
+ {
+ auto const start_address = reinterpret_cast<std::size_t>(pointer);
+ auto const end_address = start_address + size;
+
+ // Inital values if there are no adjacent blocks either before or after, meaning we have to simply create a free
+ // memory block that is placed in between the previous and next block.
+ auto block_size = size;
+ auto next_block = current_block;
+
+ // If the block we want to deallocate is before another free block and we can therefore combine both into one.
+ // This is done by deleting the current free block and creating a new block at the start address of the block to
+ // deallocate with both the size of the block to deallcoate and the free block next to it.
+ if (end_address == reinterpret_cast<std::size_t>(current_block))
+ {
+ block_size += current_block->size;
+ next_block = current_block->next;
+ current_block->~memory_block();
+ }
+
+ // If the block we want to deallocate is behind another free block and we can therefore combine both into one.
+ // This is done by simply changin the size of the previous block to include the size of the block to deallocate.
+ // This is done, because the previous block might still be referencered by the next field of other memory blocks.
+ if (previous_block != nullptr &&
+ start_address == (reinterpret_cast<std::size_t>(previous_block) + previous_block->size))
+ {
+ block_size += previous_block->size;
+
+ previous_block->size = block_size;
+ previous_block->next = next_block;
+ return;
+ }
+
+ // Check if the block we want to deallocate is contained in the previous block, because if it is it can only mean
+ // that the block has already been deallocated and we therefore attempted a double free.
+ char constexpr DOUBLE_FREE_ERROR_MESSAGE[] = "[Linked List Allocator] Attempted double free detected";
+ context_switching::syscall::syscall(
+ context_switching::syscall::type::ASSERT,
+ {previous_block == nullptr ||
+ start_address >= (reinterpret_cast<std::size_t>(previous_block) + previous_block->size),
+ reinterpret_cast<uint64_t>(&DOUBLE_FREE_ERROR_MESSAGE)});
+
+ auto const new_block = new (pointer) memory_block(block_size, next_block);
+ // If we want to deallocate the first block that is before any other free block, then there exists no previous free
+ // block (nullptr). Therefore we have to overwrite the first block instead of overwriting its
+ // next value.
+ if (previous_block == nullptr)
+ {
+ first = new_block;
+ return;
+ }
+ previous_block->next = new_block;
+ }
+
+} // namespace teachos::arch::memory::heap
diff --git a/arch/x86_64/src/memory/main.cpp b/arch/x86_64/src/memory/main.cpp
index a6f91d9..2746a71 100644
--- a/arch/x86_64/src/memory/main.cpp
+++ b/arch/x86_64/src/memory/main.cpp
@@ -1,33 +1,57 @@
#include "arch/memory/main.hpp"
#include "arch/exception_handling/assert.hpp"
+#include "arch/kernel/cpu/control_register.hpp"
+#include "arch/kernel/cpu/msr.hpp"
#include "arch/memory/allocator/area_frame_allocator.hpp"
-#include "arch/memory/cpu/control_register.hpp"
-#include "arch/memory/cpu/msr.hpp"
-#include "arch/memory/heap/heap_allocator.hpp"
+#include "arch/memory/allocator/concept.hpp"
+#include "arch/memory/heap/global_heap_allocator.hpp"
#include "arch/memory/paging/active_page_table.hpp"
#include "arch/memory/paging/kernel_mapper.hpp"
+#include <optional>
+
namespace teachos::arch::memory
{
namespace
{
- auto remap_heap(allocator::area_frame_allocator allocator, paging::active_page_table & active_table) -> void
+ static std::optional<allocator::area_frame_allocator> frame_allocator;
+
+ auto create_frame_allocator(multiboot::memory_information const & memory_information)
+ -> allocator::area_frame_allocator &
{
- auto const start_page = paging::virtual_page::containing_address(memory::heap::HEAP_START);
- auto const end_page =
- ++(paging::virtual_page::containing_address(memory::heap::HEAP_START + memory::heap::HEAP_SIZE - 1));
- paging::page_container::iterator const begin{start_page};
- paging::page_container::iterator const end{end_page};
- paging::page_container const pages{begin, end};
-
- for (auto const & page : pages)
- {
- active_table.map_page_to_next_free_frame(allocator, page, paging::entry::WRITABLE);
- }
+ frame_allocator.emplace(memory_information);
+ return frame_allocator.value();
+ }
+
+ auto get_frame_allocator() -> allocator::area_frame_allocator &
+ {
+ exception_handling::assert(frame_allocator.has_value(),
+ "[Initialization] Frame allocator has not been created yet");
+ return frame_allocator.value();
}
} // namespace
+ auto remap_heap(std::size_t heap_start, std::size_t heap_size, paging::entry::bitset additional_flags = {}) -> void
+ {
+ decltype(auto) allocator = get_frame_allocator();
+ decltype(auto) active_table = paging::active_page_table::create_or_get();
+ auto const start_page = paging::virtual_page::containing_address(heap_start);
+ auto const end_page = ++(paging::virtual_page::containing_address(heap_start + heap_size - 1));
+
+ paging::page_container::iterator const begin{start_page};
+ paging::page_container::iterator const end{end_page};
+ paging::page_container const pages{begin, end};
+
+ constexpr auto base_flags = paging::entry::WRITABLE;
+ auto const flags = base_flags | additional_flags;
+
+ for (auto const & page : pages)
+ {
+ active_table.map_page_to_next_free_frame(allocator, page, flags);
+ }
+ }
+
auto initialize_memory_management() -> void
{
static bool has_been_called = false;
@@ -36,17 +60,17 @@ namespace teachos::arch::memory
has_been_called = true;
auto const memory_information = multiboot::read_multiboot2();
- allocator::area_frame_allocator allocator(memory_information);
+ decltype(auto) allocator = create_frame_allocator(memory_information);
- cpu::set_cr0_bit(memory::cpu::cr0_flags::WRITE_PROTECT);
- cpu::set_efer_bit(memory::cpu::efer_flags::NXE);
+ kernel::cpu::set_cr0_bit(kernel::cpu::cr0_flags::WRITE_PROTECT);
+ kernel::cpu::set_efer_bit(kernel::cpu::efer_flags::NXE);
paging::kernel_mapper kernel(allocator, memory_information);
- auto & active_table = kernel.remap_kernel();
+ kernel.remap_kernel();
video::vga::text::write("Kernel remapping successful", video::vga::text::common_attributes::green_on_black);
video::vga::text::newline();
- remap_heap(allocator, active_table);
+ remap_heap(heap::KERNEL_HEAP_START, heap::KERNEL_HEAP_SIZE);
video::vga::text::write("Heap remapping successful", video::vga::text::common_attributes::green_on_black);
video::vga::text::newline();
}
diff --git a/arch/x86_64/src/memory/paging/page_entry.cpp b/arch/x86_64/src/memory/paging/page_entry.cpp
index 5aa0982..57045ca 100644
--- a/arch/x86_64/src/memory/paging/page_entry.cpp
+++ b/arch/x86_64/src/memory/paging/page_entry.cpp
@@ -21,10 +21,12 @@ namespace teachos::arch::memory::paging
{
flags |= entry::PRESENT;
}
+
if (elf_flags.contains_flags(multiboot::elf_section_flags::WRITABLE))
{
flags |= entry::WRITABLE;
}
+
if (!elf_flags.contains_flags(multiboot::elf_section_flags::EXECUTABLE_CODE))
{
flags |= entry::EXECUTING_CODE_FORBIDDEN;
@@ -35,6 +37,8 @@ namespace teachos::arch::memory::paging
auto entry::set_unused() -> void { flags = 0U; }
+ auto entry::set_user_accessible() -> void { flags |= entry::USER_ACCESSIBLE; }
+
auto entry::calculate_pointed_to_frame() const -> std::optional<allocator::physical_frame>
{
if (contains_flags(PRESENT))
@@ -51,6 +55,7 @@ namespace teachos::arch::memory::paging
{
exception_handling::assert((frame.start_address() & ~PHYSICAL_ADDRESS_MASK) == 0,
"[Paging Entry] Start address is not aligned with page");
+
flags = frame.start_address() | additional_flags.to_ulong();
}
diff --git a/arch/x86_64/src/memory/paging/temporary_page.cpp b/arch/x86_64/src/memory/paging/temporary_page.cpp
index 152241d..8e73523 100644
--- a/arch/x86_64/src/memory/paging/temporary_page.cpp
+++ b/arch/x86_64/src/memory/paging/temporary_page.cpp
@@ -4,16 +4,16 @@
namespace teachos::arch::memory::paging
{
- auto temporary_page::map_table_frame(allocator::physical_frame frame,
- active_page_table & active_table) -> page_table_handle
+ auto temporary_page::map_table_frame(allocator::physical_frame frame, active_page_table & active_table)
+ -> page_table_handle
{
page_table_handle handle{reinterpret_cast<page_table *>(map_to_frame(frame, active_table)),
page_table_handle::LEVEL1};
return handle;
}
- auto temporary_page::map_to_frame(allocator::physical_frame frame,
- active_page_table & active_table) -> virtual_address
+ auto temporary_page::map_to_frame(allocator::physical_frame frame, active_page_table & active_table)
+ -> virtual_address
{
exception_handling::assert(!active_table.translate_page(page).has_value(),
"[Temporary page] Page is already mapped");
diff --git a/arch/x86_64/src/shared/mutex.cpp b/arch/x86_64/src/stl/mutex.cpp
index 6598255..232a11c 100644
--- a/arch/x86_64/src/shared/mutex.cpp
+++ b/arch/x86_64/src/stl/mutex.cpp
@@ -1,6 +1,6 @@
-#include "arch/shared/mutex.hpp"
+#include "arch/stl/mutex.hpp"
-namespace teachos::arch::shared
+namespace teachos::arch::stl
{
auto mutex::lock() -> void
{
@@ -13,4 +13,4 @@ namespace teachos::arch::shared
auto mutex::try_lock() -> bool { return !locked.exchange(true, std::memory_order_acquire); }
auto mutex::unlock() -> void { locked.store(false, std::memory_order_release); }
-} // namespace teachos::arch::shared
+} // namespace teachos::arch::stl
diff --git a/arch/x86_64/src/user/main.cpp b/arch/x86_64/src/user/main.cpp
new file mode 100644
index 0000000..8b07e4a
--- /dev/null
+++ b/arch/x86_64/src/user/main.cpp
@@ -0,0 +1,35 @@
+#include "arch/user/main.hpp"
+
+#include "arch/context_switching/syscall/main.hpp"
+#include "arch/memory/heap/global_heap_allocator.hpp"
+
+#include <algorithm>
+#include <array>
+#include <atomic>
+#include <ranges>
+
+namespace teachos::arch::user
+{
+ auto main() -> void
+ {
+ constexpr char syscall_message[] = "Successfully entered user mode and wrote to VGA buffer via syscall!";
+ context_switching::syscall::syscall(context_switching::syscall::type::WRITE,
+ {reinterpret_cast<uint64_t>(&syscall_message)});
+
+ // Test C++ standard library
+ std::array<std::atomic<uint8_t>, 4> array_test = {std::atomic<uint8_t>{5}, std::atomic<uint8_t>{10},
+ std::atomic<uint8_t>{15}, std::atomic<uint8_t>{20}};
+ std::ranges::for_each(array_test, [](auto & item) {
+ auto value = item.load();
+ uint8_t max_value = std::max(value, uint8_t{10});
+ item.exchange(max_value + 2);
+ });
+
+ auto address = new uint64_t{10U};
+ (void)address;
+
+ for (;;)
+ {
+ }
+ }
+} // namespace teachos::arch::user