From 2d3399ab6072acd85811a54fce8eff50628888b6 Mon Sep 17 00:00:00 2001 From: Felix Morgner Date: Thu, 24 Jul 2025 16:35:34 +0000 Subject: x86_64: move files out of the way --- .../interrupt_descriptor_table/gate_descriptor.hpp | 69 +++++++ .../interrupt_descriptor_table/idt_flags.hpp | 81 ++++++++ .../interrupt_descriptor_table.hpp | 24 +++ .../interrupt_descriptor_table_pointer.hpp | 40 ++++ .../interrupt_descriptor_table/ist_offset.hpp | 45 +++++ .../segment_selector.hpp | 105 +++++++++++ .../pre/include/arch/context_switching/main.hpp | 51 +++++ .../segment_descriptor_table/access_byte.hpp | 104 +++++++++++ .../segment_descriptor_table/gdt_flags.hpp | 93 ++++++++++ .../global_descriptor_table.hpp | 37 ++++ .../global_descriptor_table_pointer.hpp | 41 ++++ .../segment_descriptor_base.hpp | 73 ++++++++ .../segment_descriptor_extension.hpp | 74 ++++++++ .../segment_descriptor_type.hpp | 27 +++ .../task_state_segment.hpp | 32 ++++ .../arch/context_switching/syscall/main.hpp | 88 +++++++++ .../context_switching/syscall/syscall_enable.hpp | 18 ++ .../context_switching/syscall/syscall_handler.hpp | 18 ++ .../pre/include/arch/exception_handling/assert.hpp | 17 ++ .../pre/include/arch/exception_handling/panic.hpp | 23 +++ .../generic_interrupt_handler.hpp | 34 ++++ arch/x86_64/pre/include/arch/kernel/cpu/call.hpp | 30 +++ arch/x86_64/pre/include/arch/kernel/cpu/gdtr.hpp | 27 +++ arch/x86_64/pre/include/arch/kernel/cpu/idtr.hpp | 27 +++ arch/x86_64/pre/include/arch/kernel/cpu/if.hpp | 21 +++ arch/x86_64/pre/include/arch/kernel/cpu/msr.hpp | 64 +++++++ .../include/arch/kernel/cpu/segment_register.hpp | 97 ++++++++++ arch/x86_64/pre/include/arch/kernel/cpu/tr.hpp | 24 +++ arch/x86_64/pre/include/arch/kernel/halt.hpp | 13 ++ arch/x86_64/pre/include/arch/kernel/main.hpp | 13 ++ .../arch/memory/allocator/area_frame_allocator.hpp | 67 +++++++ .../pre/include/arch/memory/allocator/concept.hpp | 21 +++ .../arch/memory/allocator/tiny_frame_allocator.hpp | 74 ++++++++ .../include/arch/memory/heap/bump_allocator.hpp | 48 +++++ .../arch/memory/heap/global_heap_allocator.hpp | 118 ++++++++++++ .../include/arch/memory/heap/heap_allocator.hpp | 45 +++++ .../arch/memory/heap/linked_list_allocator.hpp | 120 ++++++++++++ .../pre/include/arch/memory/heap/memory_block.hpp | 39 ++++ .../arch/memory/heap/user_heap_allocator.hpp | 149 +++++++++++++++ arch/x86_64/pre/include/arch/memory/main.hpp | 30 +++ .../arch/memory/multiboot/elf_symbols_section.hpp | 170 +++++++++++++++++ .../pre/include/arch/memory/multiboot/reader.hpp | 58 ++++++ .../arch/memory/paging/active_page_table.hpp | 206 +++++++++++++++++++++ .../arch/memory/paging/inactive_page_table.hpp | 39 ++++ .../include/arch/memory/paging/kernel_mapper.hpp | 180 ++++++++++++++++++ .../pre/include/arch/memory/paging/page_entry.hpp | 121 ++++++++++++ .../pre/include/arch/memory/paging/page_table.hpp | 157 ++++++++++++++++ .../include/arch/memory/paging/temporary_page.hpp | 64 +++++++ .../include/arch/memory/paging/virtual_page.hpp | 91 +++++++++ arch/x86_64/pre/include/arch/user/main.hpp | 16 ++ .../interrupt_descriptor_table/gate_descriptor.cpp | 24 +++ .../interrupt_descriptor_table/idt_flags.cpp | 17 ++ .../interrupt_descriptor_table.cpp | 53 ++++++ .../interrupt_descriptor_table_pointer.cpp | 13 ++ .../interrupt_descriptor_table/ist_offset.cpp | 10 + .../segment_selector.cpp | 15 ++ arch/x86_64/pre/src/context_switching/main.cpp | 63 +++++++ .../segment_descriptor_table/access_byte.cpp | 17 ++ .../segment_descriptor_table/gdt_flags.cpp | 20 ++ .../global_descriptor_table.cpp | 109 +++++++++++ .../global_descriptor_table_pointer.cpp | 11 ++ .../segment_descriptor_base.cpp | 38 ++++ .../segment_descriptor_extension.cpp | 24 +++ .../pre/src/context_switching/syscall/main.cpp | 35 ++++ .../context_switching/syscall/syscall_enable.cpp | 32 ++++ .../context_switching/syscall/syscall_handler.cpp | 118 ++++++++++++ arch/x86_64/pre/src/exception_handling/abort.cpp | 15 ++ arch/x86_64/pre/src/exception_handling/assert.cpp | 15 ++ arch/x86_64/pre/src/exception_handling/panic.cpp | 22 +++ .../pre/src/exception_handling/pure_virtual.cpp | 6 + .../generic_interrupt_handler.cpp | 13 ++ arch/x86_64/pre/src/kernel/cpu/call.cpp | 9 + arch/x86_64/pre/src/kernel/cpu/gdtr.cpp | 19 ++ arch/x86_64/pre/src/kernel/cpu/idtr.cpp | 18 ++ arch/x86_64/pre/src/kernel/cpu/if.cpp | 7 + arch/x86_64/pre/src/kernel/cpu/msr.cpp | 31 ++++ .../x86_64/pre/src/kernel/cpu/segment_register.cpp | 98 ++++++++++ arch/x86_64/pre/src/kernel/cpu/tr.cpp | 16 ++ arch/x86_64/pre/src/kernel/main.cpp | 71 +++++++ .../src/memory/allocator/area_frame_allocator.cpp | 85 +++++++++ .../src/memory/allocator/tiny_frame_allocator.cpp | 34 ++++ arch/x86_64/pre/src/memory/heap/bump_allocator.cpp | 54 ++++++ .../pre/src/memory/heap/global_heap_allocator.cpp | 135 ++++++++++++++ .../pre/src/memory/heap/linked_list_allocator.cpp | 177 ++++++++++++++++++ arch/x86_64/pre/src/memory/heap/memory_block.cpp | 15 ++ .../pre/src/memory/heap/user_heap_allocator.cpp | 200 ++++++++++++++++++++ arch/x86_64/pre/src/memory/main.cpp | 77 ++++++++ .../src/memory/multiboot/elf_symbols_section.cpp | 13 ++ arch/x86_64/pre/src/memory/multiboot/reader.cpp | 135 ++++++++++++++ .../pre/src/memory/paging/active_page_table.cpp | 98 ++++++++++ .../pre/src/memory/paging/inactive_page_table.cpp | 20 ++ arch/x86_64/pre/src/memory/paging/page_entry.cpp | 63 +++++++ arch/x86_64/pre/src/memory/paging/page_table.cpp | 128 +++++++++++++ .../pre/src/memory/paging/temporary_page.cpp | 29 +++ arch/x86_64/pre/src/memory/paging/virtual_page.cpp | 33 ++++ arch/x86_64/pre/src/user/main.cpp | 35 ++++ 96 files changed, 5493 insertions(+) create mode 100644 arch/x86_64/pre/include/arch/context_switching/interrupt_descriptor_table/gate_descriptor.hpp create mode 100644 arch/x86_64/pre/include/arch/context_switching/interrupt_descriptor_table/idt_flags.hpp create mode 100644 arch/x86_64/pre/include/arch/context_switching/interrupt_descriptor_table/interrupt_descriptor_table.hpp create mode 100644 arch/x86_64/pre/include/arch/context_switching/interrupt_descriptor_table/interrupt_descriptor_table_pointer.hpp create mode 100644 arch/x86_64/pre/include/arch/context_switching/interrupt_descriptor_table/ist_offset.hpp create mode 100644 arch/x86_64/pre/include/arch/context_switching/interrupt_descriptor_table/segment_selector.hpp create mode 100644 arch/x86_64/pre/include/arch/context_switching/main.hpp create mode 100644 arch/x86_64/pre/include/arch/context_switching/segment_descriptor_table/access_byte.hpp create mode 100644 arch/x86_64/pre/include/arch/context_switching/segment_descriptor_table/gdt_flags.hpp create mode 100644 arch/x86_64/pre/include/arch/context_switching/segment_descriptor_table/global_descriptor_table.hpp create mode 100644 arch/x86_64/pre/include/arch/context_switching/segment_descriptor_table/global_descriptor_table_pointer.hpp create mode 100644 arch/x86_64/pre/include/arch/context_switching/segment_descriptor_table/segment_descriptor_base.hpp create mode 100644 arch/x86_64/pre/include/arch/context_switching/segment_descriptor_table/segment_descriptor_extension.hpp create mode 100644 arch/x86_64/pre/include/arch/context_switching/segment_descriptor_table/segment_descriptor_type.hpp create mode 100644 arch/x86_64/pre/include/arch/context_switching/segment_descriptor_table/task_state_segment.hpp create mode 100644 arch/x86_64/pre/include/arch/context_switching/syscall/main.hpp create mode 100644 arch/x86_64/pre/include/arch/context_switching/syscall/syscall_enable.hpp create mode 100644 arch/x86_64/pre/include/arch/context_switching/syscall/syscall_handler.hpp create mode 100644 arch/x86_64/pre/include/arch/exception_handling/assert.hpp create mode 100644 arch/x86_64/pre/include/arch/exception_handling/panic.hpp create mode 100644 arch/x86_64/pre/include/arch/interrupt_handling/generic_interrupt_handler.hpp create mode 100644 arch/x86_64/pre/include/arch/kernel/cpu/call.hpp create mode 100644 arch/x86_64/pre/include/arch/kernel/cpu/gdtr.hpp create mode 100644 arch/x86_64/pre/include/arch/kernel/cpu/idtr.hpp create mode 100644 arch/x86_64/pre/include/arch/kernel/cpu/if.hpp create mode 100644 arch/x86_64/pre/include/arch/kernel/cpu/msr.hpp create mode 100644 arch/x86_64/pre/include/arch/kernel/cpu/segment_register.hpp create mode 100644 arch/x86_64/pre/include/arch/kernel/cpu/tr.hpp create mode 100644 arch/x86_64/pre/include/arch/kernel/halt.hpp create mode 100644 arch/x86_64/pre/include/arch/kernel/main.hpp create mode 100644 arch/x86_64/pre/include/arch/memory/allocator/area_frame_allocator.hpp create mode 100644 arch/x86_64/pre/include/arch/memory/allocator/concept.hpp create mode 100644 arch/x86_64/pre/include/arch/memory/allocator/tiny_frame_allocator.hpp create mode 100644 arch/x86_64/pre/include/arch/memory/heap/bump_allocator.hpp create mode 100644 arch/x86_64/pre/include/arch/memory/heap/global_heap_allocator.hpp create mode 100644 arch/x86_64/pre/include/arch/memory/heap/heap_allocator.hpp create mode 100644 arch/x86_64/pre/include/arch/memory/heap/linked_list_allocator.hpp create mode 100644 arch/x86_64/pre/include/arch/memory/heap/memory_block.hpp create mode 100644 arch/x86_64/pre/include/arch/memory/heap/user_heap_allocator.hpp create mode 100644 arch/x86_64/pre/include/arch/memory/main.hpp create mode 100644 arch/x86_64/pre/include/arch/memory/multiboot/elf_symbols_section.hpp create mode 100644 arch/x86_64/pre/include/arch/memory/multiboot/reader.hpp create mode 100644 arch/x86_64/pre/include/arch/memory/paging/active_page_table.hpp create mode 100644 arch/x86_64/pre/include/arch/memory/paging/inactive_page_table.hpp create mode 100644 arch/x86_64/pre/include/arch/memory/paging/kernel_mapper.hpp create mode 100644 arch/x86_64/pre/include/arch/memory/paging/page_entry.hpp create mode 100644 arch/x86_64/pre/include/arch/memory/paging/page_table.hpp create mode 100644 arch/x86_64/pre/include/arch/memory/paging/temporary_page.hpp create mode 100644 arch/x86_64/pre/include/arch/memory/paging/virtual_page.hpp create mode 100644 arch/x86_64/pre/include/arch/user/main.hpp create mode 100644 arch/x86_64/pre/src/context_switching/interrupt_descriptor_table/gate_descriptor.cpp create mode 100644 arch/x86_64/pre/src/context_switching/interrupt_descriptor_table/idt_flags.cpp create mode 100644 arch/x86_64/pre/src/context_switching/interrupt_descriptor_table/interrupt_descriptor_table.cpp create mode 100644 arch/x86_64/pre/src/context_switching/interrupt_descriptor_table/interrupt_descriptor_table_pointer.cpp create mode 100644 arch/x86_64/pre/src/context_switching/interrupt_descriptor_table/ist_offset.cpp create mode 100644 arch/x86_64/pre/src/context_switching/interrupt_descriptor_table/segment_selector.cpp create mode 100644 arch/x86_64/pre/src/context_switching/main.cpp create mode 100644 arch/x86_64/pre/src/context_switching/segment_descriptor_table/access_byte.cpp create mode 100644 arch/x86_64/pre/src/context_switching/segment_descriptor_table/gdt_flags.cpp create mode 100644 arch/x86_64/pre/src/context_switching/segment_descriptor_table/global_descriptor_table.cpp create mode 100644 arch/x86_64/pre/src/context_switching/segment_descriptor_table/global_descriptor_table_pointer.cpp create mode 100644 arch/x86_64/pre/src/context_switching/segment_descriptor_table/segment_descriptor_base.cpp create mode 100644 arch/x86_64/pre/src/context_switching/segment_descriptor_table/segment_descriptor_extension.cpp create mode 100644 arch/x86_64/pre/src/context_switching/syscall/main.cpp create mode 100644 arch/x86_64/pre/src/context_switching/syscall/syscall_enable.cpp create mode 100644 arch/x86_64/pre/src/context_switching/syscall/syscall_handler.cpp create mode 100644 arch/x86_64/pre/src/exception_handling/abort.cpp create mode 100644 arch/x86_64/pre/src/exception_handling/assert.cpp create mode 100644 arch/x86_64/pre/src/exception_handling/panic.cpp create mode 100644 arch/x86_64/pre/src/exception_handling/pure_virtual.cpp create mode 100644 arch/x86_64/pre/src/interrupt_handling/generic_interrupt_handler.cpp create mode 100644 arch/x86_64/pre/src/kernel/cpu/call.cpp create mode 100644 arch/x86_64/pre/src/kernel/cpu/gdtr.cpp create mode 100644 arch/x86_64/pre/src/kernel/cpu/idtr.cpp create mode 100644 arch/x86_64/pre/src/kernel/cpu/if.cpp create mode 100644 arch/x86_64/pre/src/kernel/cpu/msr.cpp create mode 100644 arch/x86_64/pre/src/kernel/cpu/segment_register.cpp create mode 100644 arch/x86_64/pre/src/kernel/cpu/tr.cpp create mode 100644 arch/x86_64/pre/src/kernel/main.cpp create mode 100644 arch/x86_64/pre/src/memory/allocator/area_frame_allocator.cpp create mode 100644 arch/x86_64/pre/src/memory/allocator/tiny_frame_allocator.cpp create mode 100644 arch/x86_64/pre/src/memory/heap/bump_allocator.cpp create mode 100644 arch/x86_64/pre/src/memory/heap/global_heap_allocator.cpp create mode 100644 arch/x86_64/pre/src/memory/heap/linked_list_allocator.cpp create mode 100644 arch/x86_64/pre/src/memory/heap/memory_block.cpp create mode 100644 arch/x86_64/pre/src/memory/heap/user_heap_allocator.cpp create mode 100644 arch/x86_64/pre/src/memory/main.cpp create mode 100644 arch/x86_64/pre/src/memory/multiboot/elf_symbols_section.cpp create mode 100644 arch/x86_64/pre/src/memory/multiboot/reader.cpp create mode 100644 arch/x86_64/pre/src/memory/paging/active_page_table.cpp create mode 100644 arch/x86_64/pre/src/memory/paging/inactive_page_table.cpp create mode 100644 arch/x86_64/pre/src/memory/paging/page_entry.cpp create mode 100644 arch/x86_64/pre/src/memory/paging/page_table.cpp create mode 100644 arch/x86_64/pre/src/memory/paging/temporary_page.cpp create mode 100644 arch/x86_64/pre/src/memory/paging/virtual_page.cpp create mode 100644 arch/x86_64/pre/src/user/main.cpp (limited to 'arch/x86_64/pre') diff --git a/arch/x86_64/pre/include/arch/context_switching/interrupt_descriptor_table/gate_descriptor.hpp b/arch/x86_64/pre/include/arch/context_switching/interrupt_descriptor_table/gate_descriptor.hpp new file mode 100644 index 0000000..07110c8 --- /dev/null +++ b/arch/x86_64/pre/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 +#include + +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/pre/include/arch/context_switching/interrupt_descriptor_table/idt_flags.hpp b/arch/x86_64/pre/include/arch/context_switching/interrupt_descriptor_table/idt_flags.hpp new file mode 100644 index 0000000..5104c36 --- /dev/null +++ b/arch/x86_64/pre/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 +#include + +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/pre/include/arch/context_switching/interrupt_descriptor_table/interrupt_descriptor_table.hpp b/arch/x86_64/pre/include/arch/context_switching/interrupt_descriptor_table/interrupt_descriptor_table.hpp new file mode 100644 index 0000000..b388e0e --- /dev/null +++ b/arch/x86_64/pre/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/pre/include/arch/context_switching/interrupt_descriptor_table/interrupt_descriptor_table_pointer.hpp b/arch/x86_64/pre/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/pre/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; + + /** + * @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/pre/include/arch/context_switching/interrupt_descriptor_table/ist_offset.hpp b/arch/x86_64/pre/include/arch/context_switching/interrupt_descriptor_table/ist_offset.hpp new file mode 100644 index 0000000..e45bcf4 --- /dev/null +++ b/arch/x86_64/pre/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 +#include + +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/pre/include/arch/context_switching/interrupt_descriptor_table/segment_selector.hpp b/arch/x86_64/pre/include/arch/context_switching/interrupt_descriptor_table/segment_selector.hpp new file mode 100644 index 0000000..2a7704e --- /dev/null +++ b/arch/x86_64/pre/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 +#include + +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/pre/include/arch/context_switching/main.hpp b/arch/x86_64/pre/include/arch/context_switching/main.hpp new file mode 100644 index 0000000..f8477ea --- /dev/null +++ b/arch/x86_64/pre/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/pre/include/arch/context_switching/segment_descriptor_table/access_byte.hpp b/arch/x86_64/pre/include/arch/context_switching/segment_descriptor_table/access_byte.hpp new file mode 100644 index 0000000..7450330 --- /dev/null +++ b/arch/x86_64/pre/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 +#include + +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/pre/include/arch/context_switching/segment_descriptor_table/gdt_flags.hpp b/arch/x86_64/pre/include/arch/context_switching/segment_descriptor_table/gdt_flags.hpp new file mode 100644 index 0000000..e24b988 --- /dev/null +++ b/arch/x86_64/pre/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 + +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/pre/include/arch/context_switching/segment_descriptor_table/global_descriptor_table.hpp b/arch/x86_64/pre/include/arch/context_switching/segment_descriptor_table/global_descriptor_table.hpp new file mode 100644 index 0000000..44f2692 --- /dev/null +++ b/arch/x86_64/pre/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/pre/include/arch/context_switching/segment_descriptor_table/global_descriptor_table_pointer.hpp b/arch/x86_64/pre/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/pre/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 + +namespace teachos::arch::context_switching::segment_descriptor_table +{ + using global_descriptor_table = stl::vector; + + /** + * @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/pre/include/arch/context_switching/segment_descriptor_table/segment_descriptor_base.hpp b/arch/x86_64/pre/include/arch/context_switching/segment_descriptor_table/segment_descriptor_base.hpp new file mode 100644 index 0000000..933fb4d --- /dev/null +++ b/arch/x86_64/pre/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/pre/include/arch/context_switching/segment_descriptor_table/segment_descriptor_extension.hpp b/arch/x86_64/pre/include/arch/context_switching/segment_descriptor_table/segment_descriptor_extension.hpp new file mode 100644 index 0000000..40bcc8a --- /dev/null +++ b/arch/x86_64/pre/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/pre/include/arch/context_switching/segment_descriptor_table/segment_descriptor_type.hpp b/arch/x86_64/pre/include/arch/context_switching/segment_descriptor_table/segment_descriptor_type.hpp new file mode 100644 index 0000000..8770b81 --- /dev/null +++ b/arch/x86_64/pre/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 + +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/pre/include/arch/context_switching/segment_descriptor_table/task_state_segment.hpp b/arch/x86_64/pre/include/arch/context_switching/segment_descriptor_table/task_state_segment.hpp new file mode 100644 index 0000000..d4aa5e8 --- /dev/null +++ b/arch/x86_64/pre/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 + +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/pre/include/arch/context_switching/syscall/main.hpp b/arch/x86_64/pre/include/arch/context_switching/syscall/main.hpp new file mode 100644 index 0000000..59adc13 --- /dev/null +++ b/arch/x86_64/pre/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 + +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/pre/include/arch/context_switching/syscall/syscall_enable.hpp b/arch/x86_64/pre/include/arch/context_switching/syscall/syscall_enable.hpp new file mode 100644 index 0000000..8cb468a --- /dev/null +++ b/arch/x86_64/pre/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/pre/include/arch/context_switching/syscall/syscall_handler.hpp b/arch/x86_64/pre/include/arch/context_switching/syscall/syscall_handler.hpp new file mode 100644 index 0000000..2e7bcd1 --- /dev/null +++ b/arch/x86_64/pre/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 + +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/pre/include/arch/exception_handling/assert.hpp b/arch/x86_64/pre/include/arch/exception_handling/assert.hpp new file mode 100644 index 0000000..1286768 --- /dev/null +++ b/arch/x86_64/pre/include/arch/exception_handling/assert.hpp @@ -0,0 +1,17 @@ +#ifndef TEACHOS_ARCH_X86_64_EXCEPTION_HANDLING_ASSERT_HPP +#define TEACHOS_ARCH_X86_64_EXCEPTION_HANDLING_ASSERT_HPP + +namespace teachos::arch::exception_handling +{ + /** + * @brief Assert a condition to be true, if not do not continue + * execution of the code and print the given message to screen. + * + * @param condition Condition we want to be true or else halt execution. + * @param message Message that should be printed before halting the execution if the condition is not met. + */ + auto assert(bool condition, char const * message) -> void; + +} // namespace teachos::arch::exception_handling + +#endif // TEACHOS_ARCH_X86_64_EXCEPTION_HANDLING_ASSERT_HPP diff --git a/arch/x86_64/pre/include/arch/exception_handling/panic.hpp b/arch/x86_64/pre/include/arch/exception_handling/panic.hpp new file mode 100644 index 0000000..6a2404c --- /dev/null +++ b/arch/x86_64/pre/include/arch/exception_handling/panic.hpp @@ -0,0 +1,23 @@ +#ifndef TEACHOS_ARCH_X86_64_EXCEPTION_HANDLING_PANIC_HPP +#define TEACHOS_ARCH_X86_64_EXCEPTION_HANDLING_PANIC_HPP + +namespace teachos::arch::exception_handling +{ + /** + * @brief Print the given kernel panic message and then halt the system. + * + * @param reason Reason to print before halting the system. + */ + [[noreturn]] auto panic(char const * reason) -> void; + + /** + * @brief Print the given kernel panic message started by a given prefix and then halt the system. + * + * @param prefix Prefix to print before printing the reason. + * @param reason Reason to print before halting the system. + */ + [[noreturn]] auto panic(char const * prefix, char const * reason) -> void; + +} // namespace teachos::arch::exception_handling + +#endif // TEACHOS_ARCH_X86_64_EXCEPTION_HANDLING_PANIC_HPP diff --git a/arch/x86_64/pre/include/arch/interrupt_handling/generic_interrupt_handler.hpp b/arch/x86_64/pre/include/arch/interrupt_handling/generic_interrupt_handler.hpp new file mode 100644 index 0000000..15b35c1 --- /dev/null +++ b/arch/x86_64/pre/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 + +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/pre/include/arch/kernel/cpu/call.hpp b/arch/x86_64/pre/include/arch/kernel/cpu/call.hpp new file mode 100644 index 0000000..3c43304 --- /dev/null +++ b/arch/x86_64/pre/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 + +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/pre/include/arch/kernel/cpu/gdtr.hpp b/arch/x86_64/pre/include/arch/kernel/cpu/gdtr.hpp new file mode 100644 index 0000000..68b950d --- /dev/null +++ b/arch/x86_64/pre/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 +#include + +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/pre/include/arch/kernel/cpu/idtr.hpp b/arch/x86_64/pre/include/arch/kernel/cpu/idtr.hpp new file mode 100644 index 0000000..cb800d0 --- /dev/null +++ b/arch/x86_64/pre/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 +#include + +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/pre/include/arch/kernel/cpu/if.hpp b/arch/x86_64/pre/include/arch/kernel/cpu/if.hpp new file mode 100644 index 0000000..48707dc --- /dev/null +++ b/arch/x86_64/pre/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/pre/include/arch/kernel/cpu/msr.hpp b/arch/x86_64/pre/include/arch/kernel/cpu/msr.hpp new file mode 100644 index 0000000..99d6378 --- /dev/null +++ b/arch/x86_64/pre/include/arch/kernel/cpu/msr.hpp @@ -0,0 +1,64 @@ +#ifndef TEACHOS_ARCH_X86_64_KERNEL_CPU_NXE_HPP +#define TEACHOS_ARCH_X86_64_KERNEL_CPU_NXE_HPP + +#include +#include + +namespace teachos::arch::kernel::cpu +{ + /** + * @brief Important flags that can be writen into the Extended Feature Enable Register (EFER). + * + * @note EFER is a model-specific register allowing to configure CPU extensions. Only the most important extensions + * are listed below, the rest are excluded for brevity. See https://en.wikipedia.org/wiki/Control_register#EFER for + * more information. + */ + enum class efer_flags : uint64_t + { + SCE = 1UL << 0UL, ///< System Call Extensions. + LME = 1UL << 8UL, ///< Long Mode Enabled. + LMA = 1UL << 10UL, ///< Long Mode Active. + NXE = 1UL << 11UL, ///< No-Execute Enable. + SVME = 1UL << 12UL, ///< Secure Virtual Machine Enable. + LMSLE = 1UL << 13UL, ///< Long Mode Segment Limit Enable. + FFXSR = 1UL << 14UL, ///< Fast FXSAVE/FXSTOR. + TCE = 1UL << 15UL, ///< Translation Cache Extension. + }; + + /** + * @brief Reads a 64-bit from the Model-Specific Register (MSR). + * + * @note This function reads the value of an MSR specified by the given address. It combines the lower and upper + * 32-bits of the MSR value read using the 'rdmsr' instruction and returns it as a 64-bit unsigned integer. + * + * @param msr The address of the MSR to read. + * @return The 64-bit value read from the MSR. + */ + auto read_msr(uint32_t msr) -> uint64_t; + + /** + * @brief Writes a 64-bit value to a Model-Specific Register (MSR). + * + * @note This function writes a 64-bit value to the MSR specified by the given address. + * It splits the 64-bit value into two 32-bit parts and writes them using the + * `wrmsr` instruction. + * + * @param msr The address of the MSR to write to. + * @param new_value The 64-bit value to write to the MSR. + */ + auto write_msr(uint32_t msr, uint64_t new_value) -> void; + + /** + * @brief Sets a specific bit in the Extended Feature Enable Register (EFER), which is a Model-Specific Register + * (MSR). + * + * @note This function reads the current value of the EFER register, ORs the specified + * bit with the current value, and writes the updated value back to the EFER register. + * + * @param flag The flag to set in the EFER register. + */ + auto set_efer_bit(efer_flags flag) -> void; + +} // namespace teachos::arch::kernel::cpu + +#endif // TEACHOS_ARCH_X86_64_KERNEL_CPU_NXE_HPP \ No newline at end of file diff --git a/arch/x86_64/pre/include/arch/kernel/cpu/segment_register.hpp b/arch/x86_64/pre/include/arch/kernel/cpu/segment_register.hpp new file mode 100644 index 0000000..a236452 --- /dev/null +++ b/arch/x86_64/pre/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/pre/include/arch/kernel/cpu/tr.hpp b/arch/x86_64/pre/include/arch/kernel/cpu/tr.hpp new file mode 100644 index 0000000..7c856f1 --- /dev/null +++ b/arch/x86_64/pre/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 +#include + +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/pre/include/arch/kernel/halt.hpp b/arch/x86_64/pre/include/arch/kernel/halt.hpp new file mode 100644 index 0000000..377acc0 --- /dev/null +++ b/arch/x86_64/pre/include/arch/kernel/halt.hpp @@ -0,0 +1,13 @@ +#ifndef TEACHOS_ARCH_X86_64_KERNEL_HALT_HPP +#define TEACHOS_ARCH_X86_64_KERNEL_HALT_HPP + +namespace teachos::arch::kernel +{ + /** + * @brief Halts the kernel execution, meaning any code after a call to this will not run anymore. + */ + extern "C" [[noreturn]] auto halt() -> void; + +} // namespace teachos::arch::kernel + +#endif // TEACHOS_ARCH_X86_64_KERNEL_HALT_HPP diff --git a/arch/x86_64/pre/include/arch/kernel/main.hpp b/arch/x86_64/pre/include/arch/kernel/main.hpp new file mode 100644 index 0000000..a13e5f4 --- /dev/null +++ b/arch/x86_64/pre/include/arch/kernel/main.hpp @@ -0,0 +1,13 @@ +#ifndef TEACHOS_ARCH_X86_64_KERNEL_MAIN_HPP +#define TEACHOS_ARCH_X86_64_KERNEL_MAIN_HPP + +namespace teachos::arch::kernel +{ + /** + * @brief Initalizes the kernel system. + */ + auto main() -> void; + +} // namespace teachos::arch::kernel + +#endif // TEACHOS_ARCH_X86_64_KERNEL_MAIN_HPP diff --git a/arch/x86_64/pre/include/arch/memory/allocator/area_frame_allocator.hpp b/arch/x86_64/pre/include/arch/memory/allocator/area_frame_allocator.hpp new file mode 100644 index 0000000..a86c9b7 --- /dev/null +++ b/arch/x86_64/pre/include/arch/memory/allocator/area_frame_allocator.hpp @@ -0,0 +1,67 @@ +#ifndef TEACHOS_X86_64_MEMORY_AREA_ALLOCATOR_HPP +#define TEACHOS_X86_64_MEMORY_AREA_ALLOCATOR_HPP + +// #include "arch/memory/allocator/physical_frame.hpp" +// #include "arch/memory/multiboot/reader.hpp" + +#include + +namespace x86_64::memory +{ + /** + * @brief Allocates memory linearly using memory areas read from the multiboot2 information pointer and leaks any + * deallocated frames. + */ + struct area_frame_allocator + { + /** + * @brief Constructor. + * + * @param mem_info Structure containg all relevant information to map and allocate memory. + */ + area_frame_allocator(multiboot::memory_information const & mem_info); + + /** + * @brief Allocate memory by finding and returning a free physical frame. + * + * @note The physical_frame allocation executes multiple checks before returning + * the physical_frame that is available to allocate. It must at least + * do the following: + * - check if the next_free_frame is within the current_area + * - check if the next_free_frame is actually free + * - update the next_free_frame after finding a free physical_frame + * + * @return next free physical frame or nullopt if none was found. + */ + auto allocate_frame() -> std::optional; + + /** + * @brief Deallocates a previously allocated physical frame. + * + * @note Simply does nothing, because the simply area frame + * allocator implementation does not keep track of free or used frames and can therefore not deallocate, because it + * does not know which frames have been alocated in the first place. + * + * @param physical_frame Previously allocated physical_frame that should be deallocated. + */ + auto deallocate_frame(physical_frame const & physical_frame) -> void; + + private: + /** + * @brief Find the next memory area and write it into current_area. + */ + auto choose_next_area() -> void; + + physical_frame next_free_frame; ///< The physical_frame after the last allocated one. + std::optional current_area; ///< The current memory area. + multiboot::memory_area_container const + 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. + physical_frame const multiboot_end; ///< The end address of the multiboot code in memory. + }; + +} // namespace x86_64::memory + +#endif // TEACHOS_ARCH_X86_64_MEMORY_ALLOCATOR_AREA_FRAME_ALLOCATOR_HPP diff --git a/arch/x86_64/pre/include/arch/memory/allocator/concept.hpp b/arch/x86_64/pre/include/arch/memory/allocator/concept.hpp new file mode 100644 index 0000000..2d3f4ae --- /dev/null +++ b/arch/x86_64/pre/include/arch/memory/allocator/concept.hpp @@ -0,0 +1,21 @@ +#ifndef TEACHOS_ARCH_X86_64_MEMORY_ALLOCATOR_CONCEPT_HPP +#define TEACHOS_ARCH_X86_64_MEMORY_ALLOCATOR_CONCEPT_HPP + +#include "arch/memory/allocator/physical_frame.hpp" + +#include + +namespace teachos::arch::memory::allocator +{ + /** + * @brief Frame allocator concept required for allocating and deallocating physical frames in memory. + */ + template + concept FrameAllocator = requires(T t, physical_frame const & a) { + { t.allocate_frame() } -> std::same_as>; + { t.deallocate_frame(a) } -> std::same_as; + }; + +} // namespace teachos::arch::memory::allocator + +#endif // TEACHOS_ARCH_X86_64_MEMORY_ALLOCATOR_CONCEPT_HPP diff --git a/arch/x86_64/pre/include/arch/memory/allocator/tiny_frame_allocator.hpp b/arch/x86_64/pre/include/arch/memory/allocator/tiny_frame_allocator.hpp new file mode 100644 index 0000000..1ceb74d --- /dev/null +++ b/arch/x86_64/pre/include/arch/memory/allocator/tiny_frame_allocator.hpp @@ -0,0 +1,74 @@ +#ifndef TEACHOS_ARCH_X86_64_MEMORY_ALLOCATOR_TINY_FRAME_ALLOCATOR_HPP +#define TEACHOS_ARCH_X86_64_MEMORY_ALLOCATOR_TINY_FRAME_ALLOCATOR_HPP + +#include "arch/memory/allocator/concept.hpp" +#include "arch/memory/allocator/physical_frame.hpp" + +#include + +namespace teachos::arch::memory::allocator +{ + namespace + { + uint8_t constexpr TINY_ALLOCATOR_FRAMES_COUNT = 3U; + } + + /** + * @brief Allocates memory using memory areas read from the multiboot2 information pointer. Does not allocate its own + * frames, but uses the necessary three frames provided by another allocator to map one virtual level 1 page entry and + * the necessary upper layers. + */ + struct tiny_frame_allocator + { + /** + * @brief Constructor. + * + * @tparam T Contract the allocator that should be used to actually allocate and deallocate, the underlying three + * frames has to follow. + * + * @param allocator Reference to an allocator following the FrameAllocator concept, which is used to allocate + * entries when the underlying frames are created. + */ + template + tiny_frame_allocator(T & allocator) + : frames{} + { + // Has to be done this way, because constructing the constructor with the data from allocator.allocate_frames(), + // does not work because it would set the value correctly but because we pass it as an std::optional it would not + // set the engaged flag. Meaning the has_value() method would still return false. + for (auto & frame : frames) + { + auto allocated = allocator.allocate_frame(); + if (allocated.has_value()) + { + frame.emplace(allocated.value()); + } + } + } + + /** + * @brief Allocate memory by finding and returning one of the three free physical frames. + * + * @return First free physical frames of the three frames held by this allocator or nullopt if we used up all three + * frames already. + */ + auto allocate_frame() -> std::optional; + + /** + * @brief Deallocates one of the three previously allocated physical frames. + * + * @note If more than the three frames are deallocated the method will halt execution, because it can only hold 3 + * frames. + * + * @param physical_frame Previously allocated physical_frame that should be deallocated. + */ + auto deallocate_frame(physical_frame const & physical_frame) -> void; + + private: + std::array, TINY_ALLOCATOR_FRAMES_COUNT> frames = + {}; ///< Container that holds the frames allocated by another allocator. + }; + +} // namespace teachos::arch::memory::allocator + +#endif // TEACHOS_ARCH_X86_64_MEMORY_ALLOCATOR_TINY_FRAME_ALLOCATOR_HPP diff --git a/arch/x86_64/pre/include/arch/memory/heap/bump_allocator.hpp b/arch/x86_64/pre/include/arch/memory/heap/bump_allocator.hpp new file mode 100644 index 0000000..011f45c --- /dev/null +++ b/arch/x86_64/pre/include/arch/memory/heap/bump_allocator.hpp @@ -0,0 +1,48 @@ +#ifndef TEACHOS_ARCH_X86_64_MEMORY_HEAP_BUMP_ALLOCATOR_HPP +#define TEACHOS_ARCH_X86_64_MEMORY_HEAP_BUMP_ALLOCATOR_HPP + +#include "arch/memory/heap/heap_allocator.hpp" + +#include +#include + +namespace teachos::arch::memory::heap +{ + /** + * @brief Simple heap allocator, which allocates linearly and leaks all allocated memory, because it does not really + * deallocate anything. + */ + struct bump_allocator : heap_allocator + { + /** + * @brief Constructor. + * + * @param heap_start Start of the allocatable heap area + * @param heap_end End of the allocatable heap area (Start + Size) + */ + bump_allocator(std::size_t heap_start, std::size_t heap_end) + : heap_start{heap_start} + , heap_end{heap_end} + , next{heap_start} + { + // Nothing to do + } + + auto allocate(std::size_t size) -> void * override; + + /** + * @copybrief heap_allocator::deallocate + * + * @note Simply does nothing, because this allocator leaks all memory + */ + auto deallocate(void * pointer) noexcept -> void override; + + private: + std::size_t heap_start; ///< Start of the allocatable heap area + std::size_t heap_end; ///< End of the allocatable heap area + std::atomic_uint64_t next; ///< Current address, which is the start of still unused allocatable heap area + }; + +} // namespace teachos::arch::memory::heap + +#endif // TEACHOS_ARCH_X86_64_MEMORY_HEAP_BUMP_ALLOCATOR_HPP diff --git a/arch/x86_64/pre/include/arch/memory/heap/global_heap_allocator.hpp b/arch/x86_64/pre/include/arch/memory/heap/global_heap_allocator.hpp new file mode 100644 index 0000000..c98c130 --- /dev/null +++ b/arch/x86_64/pre/include/arch/memory/heap/global_heap_allocator.hpp @@ -0,0 +1,118 @@ +#ifndef TEACHOS_ARCH_X86_64_MEMORY_HEAP_GLOBAL_HEAP_ALLOCATOR_HPP +#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 +{ + /** + * @brief Possible types that should be constructed by the register_heap_allocator factory method. + * Creates the underlying heap allocator instance that is then used by all global allocations using new and delete + */ + enum class heap_allocator_type : uint8_t + { + NONE, ///< Don't use any heap allocation implementation, this will result in all calls of new and delte halting + ///< further execution of the kernel + 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 + }; + + /** + * @brief Global instance of a heap allocator implementation created by the factory method pattern @see + * https://refactoring.guru/design-patterns/factory-method for more information. + * + * @note Can only be registered once and only once the kernel and the heap part of the kernel has been remapped + * successfully. If the instance is created before than the device will abort, because it acceses unmapped memory + * areas. + */ + struct global_heap_allocator + { + /** + * @brief Registers the heap allocation implementation that should be used by the global heap allocator. + * Meaning all future calls to the global new or delete will be forwarded to the allocate and deallocate calls of + * the underlying heap allocation implementation + * + * @param new_type Type of the heap allocation implementation we want to instantiate + */ + static auto register_heap_allocator(heap_allocator_type new_type) -> 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 + */ + static auto kmalloc(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 + */ + 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 * 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 further execution + * + * @return Reference to the registered user heap allocation + */ + [[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/pre/include/arch/memory/heap/heap_allocator.hpp b/arch/x86_64/pre/include/arch/memory/heap/heap_allocator.hpp new file mode 100644 index 0000000..420a1d3 --- /dev/null +++ b/arch/x86_64/pre/include/arch/memory/heap/heap_allocator.hpp @@ -0,0 +1,45 @@ +#ifndef TEACHOS_ARCH_X86_64_MEMORY_HEAP_HEAP_ALLOCATOR_HPP +#define TEACHOS_ARCH_X86_64_MEMORY_HEAP_HEAP_ALLOCATOR_HPP + +#include + +namespace teachos::arch::memory::heap +{ + 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 + */ + struct heap_allocator + { + /** + * @brief Virtual default destructor, created to ensure that if a pointer to this class is used and deleted, we will + * also call the derived base class destructor. Deleting a base class destructor that does not have a virtual + * destructor is undefined behaviour, because the derived class destructor originally instantiated with new is never + * called. This can cause potential memory leaks, because derived classes can not clean up their internal members as + * expected and instead simply leak them + */ + virtual ~heap_allocator() {} + + /** + * @brief Allocates the given amount of memory and returns the pointer to the start of the allocatable memory area + * + * @param size Amount of bytes that should be allocated + * @return void* Pointer to the start of the allocatable memory area + */ + virtual auto allocate(std::size_t size) -> void * = 0; + + /** + * @brief Deallocates all memory associated with the given pointer address. + * Simply deallocates the amount of memory created with the corresponding call to allocate + * + * @param pointer Previously allocated memory area, that should now be freed + */ + virtual auto deallocate(void * pointer) noexcept -> void = 0; + }; +} // namespace teachos::arch::memory::heap + +#endif // TEACHOS_ARCH_X86_64_MEMORY_HEAP_HEAP_ALLOCATOR_HPP diff --git a/arch/x86_64/pre/include/arch/memory/heap/linked_list_allocator.hpp b/arch/x86_64/pre/include/arch/memory/heap/linked_list_allocator.hpp new file mode 100644 index 0000000..bbbad19 --- /dev/null +++ b/arch/x86_64/pre/include/arch/memory/heap/linked_list_allocator.hpp @@ -0,0 +1,120 @@ +#ifndef TEACHOS_ARCH_X86_64_MEMORY_HEAP_LINKED_LIST_ALLOCATOR_HPP +#define TEACHOS_ARCH_X86_64_MEMORY_HEAP_LINKED_LIST_ALLOCATOR_HPP + +#include "arch/memory/heap/heap_allocator.hpp" +#include "arch/memory/heap/memory_block.hpp" + +#include + +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 linked_list_allocator : heap_allocator + { + /** + * @brief Constructor. + * + * @param heap_start Start of the allocatable heap area + * @param heap_end End of the allocatable heap area (Start + Size) + */ + linked_list_allocator(std::size_t heap_start, std::size_t heap_end); + + /** + * @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. + */ + auto allocate(std::size_t size) -> void * override; + + auto deallocate(void * pointer) noexcept -> void override; + + private: + /** + * @brief Returns the smallest allocatable block of heap memory. + * + * @return Smallest allocatable block of heap memory. + */ + auto constexpr min_allocatable_size() -> std::size_t { return sizeof(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. + */ + 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. + */ + 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. + */ + 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. + */ + 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. + kstd::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_LINKED_LIST_ALLOCATOR_HPP diff --git a/arch/x86_64/pre/include/arch/memory/heap/memory_block.hpp b/arch/x86_64/pre/include/arch/memory/heap/memory_block.hpp new file mode 100644 index 0000000..9d1fb02 --- /dev/null +++ b/arch/x86_64/pre/include/arch/memory/heap/memory_block.hpp @@ -0,0 +1,39 @@ +#ifndef TEACHOS_ARCH_X86_64_MEMORY_HEAP_MEMORY_BLOCK_HPP +#define TEACHOS_ARCH_X86_64_MEMORY_HEAP_MEMORY_BLOCK_HPP + +#include + +namespace teachos::arch::memory::heap +{ + /** + * @brief Block containing free memory, pointing to the next free hole (nullptr) if there is none. + * Forms a singly linked list of free memory blocks that we can callocate memory into. + */ + struct memory_block + { + /** + * @brief Constructor. Clears all memory from the place it was allocated until the end (address + + * size). + * + * @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); + + /** + * @brief Destructor. Clears all internal memory. + * + * @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 + ///< size variable and the pointer to the next hole. + memory_block * next; ///< Optional pointer to the next free memory, holds nullptr if there is none. + }; +} // namespace teachos::arch::memory::heap + +#endif // TEACHOS_ARCH_X86_64_MEMORY_HEAP_MEMORY_BLOCK_HPP diff --git a/arch/x86_64/pre/include/arch/memory/heap/user_heap_allocator.hpp b/arch/x86_64/pre/include/arch/memory/heap/user_heap_allocator.hpp new file mode 100644 index 0000000..3b47f15 --- /dev/null +++ b/arch/x86_64/pre/include/arch/memory/heap/user_heap_allocator.hpp @@ -0,0 +1,149 @@ +#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 +#include +#include + +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; + + /** + * @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. + kstd::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/pre/include/arch/memory/main.hpp b/arch/x86_64/pre/include/arch/memory/main.hpp new file mode 100644 index 0000000..d51815f --- /dev/null +++ b/arch/x86_64/pre/include/arch/memory/main.hpp @@ -0,0 +1,30 @@ +#ifndef TEACHOS_ARCH_X86_64_MEMORY_MAIN_HPP +#define TEACHOS_ARCH_X86_64_MEMORY_MAIN_HPP + +#include "arch/memory/paging/page_entry.hpp" + +#include + +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. + * + * @note Enables the necessary register flags and remaps the kernel, + * elf_sections, vga_text and the heap. + */ + auto initialize_memory_management() -> void; + +} // namespace teachos::arch::memory + +#endif // TEACHOS_ARCH_X86_64_MEMORY_MAIN_HPP diff --git a/arch/x86_64/pre/include/arch/memory/multiboot/elf_symbols_section.hpp b/arch/x86_64/pre/include/arch/memory/multiboot/elf_symbols_section.hpp new file mode 100644 index 0000000..348c159 --- /dev/null +++ b/arch/x86_64/pre/include/arch/memory/multiboot/elf_symbols_section.hpp @@ -0,0 +1,170 @@ +#ifndef TEACHOS_ARCH_X86_64_MEMORY_MULTIBOOT_ELF_SYBOLS_SECTION_HPP +#define TEACHOS_ARCH_X86_64_MEMORY_MULTIBOOT_ELF_SYBOLS_SECTION_HPP + +// #include "arch/memory/multiboot/info.hpp" +// #include "arch/stl/container.hpp" +// #include "arch/stl/contiguous_pointer_iterator.hpp" + +#include +#include + +namespace teachos::arch::memory::multiboot +{ + /** + * @brief Defines all elf section types an elf section header can have. + * + * @note See https://docs.oracle.com/cd/E19683-01/816-1386/chapter6-94076/index.html for more information. + */ + enum struct elf_section_type : uint32_t + { + 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 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. + NOTE, ///< (SHT_NOTE) Stores information that marks files in some way. + EMPTY, ///< (SHT_NOBITS) Program data section, that occupies no space in the file (.bss). + RELOCATION_ENTRY_WITHOUT_ADDENDS, ///< (SHT_REL) Only used on 32 bit systems. + UNSPECIFIED, ///< (SHT_SHLIB) Reserved but has unspecified semantics. + DYNAMIC_SYMBOL_TABLE, ///< (SHT_DYNSYM) Holds minimal set of symbols adequate for dynamic linking. + INITALIZATION_FUNCTION_ARRAY = 14, ///< (SHT_INIT_ARRAY) Array of pointers to intialization functions () -> void. + TERMINATION_FUNCTION_ARRAY, ///< (SHT_FINI_ARRAY) Array of pointers to termination functions () -> void. + PRE_INITALIZATION_FUNCTION_ARRAY ///< (SHT_PRE_INIT_ARRAY) Array of pointers to functions invoked before other + ///< initalization functions () -> void. + }; + + /** + * @brief Defines helper function for all states that the elf section flags of an elf section header can + * have. + * + * @note See https://docs.oracle.com/cd/E19683-01/816-1386/chapter6-94076/index.html for more information. + */ + struct elf_section_flags + { + /** + * @brief Possible set bits in our underlying std::bitset and the meaning when they are set. + */ + enum bitset : uint32_t + { + WRITABLE = 1U << 0U, ///< (SHF_WRITE) Section is writable at runtime. If it isn't then the section + ///< is assumed to be READONLY and only that flag is shown in the objdump. + OCCUPIES_MEMORY = 1U << 1U, ///< (SHF_ALLOC) Section occupies memory during execution. + ///< ALLOC flag is shown in the objdump. + EXECUTABLE_CODE = 1U << 2U, ///< (SHF_EXECINSTR) Section is executable. CODE flag is shown in the object dump. + DUPLICATE_DATA = 1U << 4U, ///< (SHF_MERGE) Section might be merged with another section. + CONTAINS_STRING = 1U << 5U, ///< (SHF_STRINGS) Section contains null-terminated strings. + SECTION_HEADER_INFO_IS_SECTION_HEADER_TABLE_INDEX = + 1U << 6U, ///< (SHF_INFO_LINK) Section contains the section header table index in the (sh_info) + ///< additional_information variable. + PRESERVE_ORDERING_AFTER_COMBINATION = + 1U << 7U, ///< (SHF_LINK_ORDER) Section preserves order after combining with another section. + REQUIRES_SPECIAL_OS_PROCESSING = + 1U << 8U, ///< (SHF_OS_NONCONFORMING) Section requires non-standard OS specific handling of its code or + ///< data, which does not confirm to standard ELF specifications. + SECTION_GROUP_MEMBER = 1U << 9U, ///< (SHF_GROUP) Section is a member of a section group. + HOLDS_THREAD_LOCAL_DATA = 1U << 10U, ///< (SHF_TLS) Section holds thread-local data. + COMPRESSED = 1U << 11U, ///< (SHF_COMPRESSED) Section contains compressed data. + SPECIAL_ORDERING_REQUIREMENTS = + 1U << 30U, ///< (SHF_ORDERED) Section has special ordering requirements, meaning it + ///< should be ordered in relation to other sections of the same type. + EXCLUDED_UNLESS_REFERENCED_OR_ALLOCATED = 1U << 31U, ///< (SHF_EXCLUDE)Section is excluded unless referenced or + ///< allocated, used for LTO (Link-Time Optimizations). + }; + + /** + * @brief Constructor. + * + * @param flags Actual value read from the elf section header, which should be converted into a std::bitset, to + * allow reading the state of single bits more easily. + */ + explicit elf_section_flags(uint64_t flags) + : flags(flags) + { + // Nothing to do + } + + /** + * @brief Checks if the given std::bitset is a subset or equivalent to the underlying std::bitset. + * + * @note Meaning that all bits that are set in the given std::bitset also have to be set in the underlyng + * std::bitset. Any additional bits that are set are not relevant. + * + * @param other Flags that we want to compare against and check if the underlying std::bitset has the same bits set. + * @return Whether the given flags are a subset or equivalent with the underlying std::bitset. + */ + auto contains_flags(std::bitset<64U> other) const -> bool; + + /** + * @brief Allows to compare the underlying std::bitset of two instances. + * + * @param other Other instance that we want to compare with. + * @return Whether the underlying std::bitset of both types is the same. + */ + auto operator==(elf_section_flags const & other) const -> bool = default; + + private: + std::bitset<64U> flags; ///< Underlying bitset used to read the flags from. Bits 21 - 28 are reserved for operating + ///< system specific semantics and bits 29 - 32 are reserved for processor specific + ///< semantics. Bits 33 - 64 are unused for compatability with ELF32. + }; + + /** + * @brief Defines the data included in a section header, where each section has exactly one section header. + * + * @note See https://refspecs.linuxbase.org/elf/gabi4+/ch4.sheader.html for more information. + */ + struct elf_section_header + { + uint32_t name_table_index; ///< Index into the section header string table, specifies the name of the section. + elf_section_type type; ///< Categorizes the sections content and semantics. + elf_section_flags flags; ///< 1-bit flgas that describe section attributes. + uint64_t physical_address; ///< If section appears in memory image of a process, gives address at which the + ///< sections first byte should reside, otherwise 0. + uint64_t file_offset; ///< Offset from the beginning of the file to the first byte in the section. SHT_NOBITS + ///< contains the conceptual placement instead (because it occupies no space in the file). + uint64_t section_size; ///< Complete section size in bytes, SHT_NOBITS may have non-zero value but will always + ///< occupy no space in the file. + uint32_t other_section; ///< Section header table index link, behaviour varies on type + ///< https://refspecs.linuxbase.org/elf/gabi4+/ch4.sheader.html#sh_link. + uint32_t additional_information; ///< Extra information, behaviour varies on type + ///< https://refspecs.linuxbase.org/elf/gabi4+/ch4.sheader.html#sh_link. + uint64_t address_alignment; ///< Possible address alignment constraints. Value of virutal_address must be 0 % value + ///< of address_alignment. Value 0 or 1 mean no alignment constraints. + uint64_t fixed_table_entry_size; ///< If sections holds table with fixed-sized entries, this gives the size in + ///< bytes of each entry. + + /** + * @brief Detect whether a section header is inactive or not, should always be the case for the first entry in the + * sections table. + * + * @return Whether the current section header is actually null or not, requires all fields besides section_size and + * other_section to contain 0. + */ + auto is_null() const -> bool; + }; + + // /** + // * @brief Defines an entry in the multi_boot_tag array of the multi_boot_info struct, of type + // * multi_boot_tag_type::ELF_SECTIONS. + // * + // * @note The first section in the sections array will always be INACTIVE, there can only ever be one DYNAMIC + // section + // * and only either one DYNAMIC_SYMBOL_TABLE or SYMBOL_TABLE. + // */ + // struct elf_symbols_section_header + // { + // tag info; ///< Basic multi_boot_tag information. + // uint32_t number_of_sections; ///< Number of sections in the sections array. + // uint32_t entry_size; ///< Size of each entry in the sections array. + // uint32_t section_index; ///< Index to the string table used for symbol names. + // std::byte end; ///< Marks the end of the tag, used to mark the beginning of any additional data. + // ///< contained in the section, to ensure byte alignment is actually 4 byte. + // }; + + // using elf_section_header_container = stl::container>; + +} // namespace teachos::arch::memory::multiboot + +#endif // TEACHOS_ARCH_X86_64_MEMORY_MULTIBOOT_ELF_SYBOLS_SECTION_HPP diff --git a/arch/x86_64/pre/include/arch/memory/multiboot/reader.hpp b/arch/x86_64/pre/include/arch/memory/multiboot/reader.hpp new file mode 100644 index 0000000..c5464cb --- /dev/null +++ b/arch/x86_64/pre/include/arch/memory/multiboot/reader.hpp @@ -0,0 +1,58 @@ +#ifndef TEACHOS_ARCH_X86_64_MEMORY_MULTIBOOT_READER_HPP +#define TEACHOS_ARCH_X86_64_MEMORY_MULTIBOOT_READER_HPP + +// #include "arch/memory/multiboot/elf_symbols_section.hpp" +// #include "arch/memory/multiboot/memory_map.hpp" + +#include "arch/memory/multiboot/elf_symbols_section.hpp" + +#include +#include +#include + +namespace teachos::arch::memory::multiboot +{ + /** + * @brief Contains all relevant information to map and allocate memory that is read from the multiboot2 information + * structure. + */ + struct memory_information + { + std::size_t kernel_start; ///< Start address of the kernel code in memory. + std::size_t kernel_end; ///< End address of the kernel code in memory. + multiboot2::elf_symbols; ///< Contains non-owning pointers to all kernel sections. + std::size_t multiboot_start; ///< Start address of the multiboot code in memory. + std::size_t multiboot_end; ///< End address of the multiboot code in memory. + // std::sp + // memory_area_container areas; ///< Contains non-owning pointers to all memory areas. + }; + + /** + * @brief Reads the relevant multiboot2 information data from memory. + * + * @note This is done using the multiboot_information_pointer, which marks the start of the multiboot2 data. The + * indivdual headers we have to read are 8 byte aligned, whereas the data contained in those headers does not have to + * be. All sections that are read additionaly receive some sanity to ensure the read address is actually pointing to + * the expected structure, if they are not this method will assert. + * + * The memory_information variables are calcualted like this: + * - kernel_start: Calculated by getting the lowest address specified in the elf symbols headers. + * - kernel_end: Calculated by getting the highest address specified in the elf symbols headers and adding the length + * of that section. + * - multiboot_start: Calcualted by simply getting the value of the multiboot information pointer, because it already + * contains the address pointint to the start of the multiboot2 data. + * - multiboot_end: Calcualted by getting the value of the multiboot information pointer and adding the total size of + * the complete multiboot2 data + * - memory_areas: Calculated by simply accessing the address of the entries variable in the memory map header + * structure. + * - area_count: Calculated by subtracing the memory map header size from the total tag size, which results in the + * remaining size (size of the entries array), this size is then divided by the size of one entry in that array, which + * should be 24 bytes. + * + * @return Relevant data read from multiboot2. + */ + auto read_multiboot2() -> memory_information; + +} // namespace teachos::arch::memory::multiboot + +#endif // TEACHOS_ARCH_X86_64_MEMORY_MULTIBOOT_READER_HPP diff --git a/arch/x86_64/pre/include/arch/memory/paging/active_page_table.hpp b/arch/x86_64/pre/include/arch/memory/paging/active_page_table.hpp new file mode 100644 index 0000000..f68d8b6 --- /dev/null +++ b/arch/x86_64/pre/include/arch/memory/paging/active_page_table.hpp @@ -0,0 +1,206 @@ +#ifndef TEACHOS_ARCH_X86_64_MEMORY_PAGING_ACTIVE_PAGE_TABLE_HPP +#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/paging/virtual_page.hpp" + +#include +#include +#include + +namespace teachos::arch::memory::paging +{ + /** + * @brief Currently actively by the CPU used level 4 page table, is used to ensure there is only ever one valid + * instance and it cannot be copied or constructed again. + */ + struct active_page_table + { + /** + * @brief Creates a single instance of an active level 4 page table table and returns the created instance or + * alternatively returns the previously created instance instead. The instance is owned by this method and is + * static, meaning it lives on for the complete lifetime of the program. + * + * @return Active single unique instance of the level 4 page table. + */ + static auto create_or_get() -> active_page_table &; + + /** + * @brief Index operator overload to access specific mutable entry directy of the level 4 page table. + * + * @param index Index of the entry we want to access and only read. + * @return Entry at the given table index. + */ + auto operator[](std::size_t index) -> entry &; + + /** + * @brief Translates virtual address into corresponding physical address. Calls translate_page under the hood. + * + * @param address Virtual address we want to translate into physical one. + * @return Physical address corresponding to the provided virtual address. + */ + auto translate_address(virtual_address address) -> std::optional; + + /** + * @brief Translates page into physical frame, will first attempt to parse normally using default page size and if + * it failed attempt to parse using huge pages. + * + * @param page Page to translate into physical frame. + * @return Physical frame corresponding to the provided virtual page. + */ + auto translate_page(virtual_page page) -> std::optional; + + /** + * @brief Translates huge page into actual physical frame. + * + * @param page Page to translate into physical frame. + * @return Physical frame corresponding to the provided virtual page. + */ + auto translate_huge_page(virtual_page page) -> std::optional; + + /** + * @brief Maps a virtual page to a physical frame in the page table with the specified flags. + * + * @note Allocates and maps an entry in every page level if it does not exists yet down to level 1. If the level 1 + * page table already exists it halts execution instead. + * + * @tparam T Type constraint of the allocator, being that is follows the given concept and contains an allocate and + * deallocate method. + * @param allocator Reference to an allocator following the FrameAllocator concept, which is used to allocate + * entries when a new page table is required. + * @param page Virtual page that is being mapped. + * @param frame Physical frame that the virtual page will be mapped to. + * @param flags A bitset of flags that configure the page table entry for this mapping. + */ + template + 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), flags); + } + + auto & level1_entry = current_handle[page.get_level_index(page_table_handle::LEVEL1)]; + arch::exception_handling::assert(!level1_entry.contains_flags(entry::HUGE_PAGE), + "[Page Mapper] Unable to map huge pages"); + arch::exception_handling::assert(level1_entry.is_unused(), "[Page Mapper] Page table entry is already used"); + level1_entry.set_entry(frame, flags.to_ulong() | entry::PRESENT); + } + + /** + * @brief Allocates the next free frame and then uses that frame to call map_page_to_frame. + * + * @see map_page_to_frame + */ + template + auto map_page_to_next_free_frame(T & allocator, virtual_page page, std::bitset<64U> flags) -> void + { + auto const frame = allocator.allocate_frame(); + exception_handling::assert(frame.has_value(), "[Page mapper] Out of memory exception"); + map_page_to_frame(allocator, page, frame.value(), flags); + } + + /** + * @brief Gets the corresponding page the given frame has to be contained in and uses that to call + * map_page_to_frame. + * + * @see map_page_to_frame + */ + template + auto identity_map(T & allocator, allocator::physical_frame frame, std::bitset<64U> flags) -> void + { + auto const page = virtual_page::containing_address(frame.start_address()); + map_page_to_frame(allocator, page, frame, flags); + } + + /** + * @brief Unmaps the virtual page from the previously mapped to physical frame and resets the flags. + * + * @note For the unmap function to deallocates and unmaps correctly, the entry in every page level if this page was + * the last one up to level 4 should be unmapped and ensured to clear the Translation Lookaside Buffer, so that the + * unmapped value is removed from cache as well. This is currently not done and instead we only dallocate and unmap + * the level 1 page table entry, this is the case because it conflicts with our recursive mapping for the temporary + * page, which requires the other page table entries to walk to the actual level 4 page table. If we remove all page + * table entries beforehand, we therefore can not remap the kernel anymore. + * + * @tparam T Type constraint of the allocator, being that is follows the given concept and contains an allocate and + * deallocate method. + * @param allocator Reference to an allocator following the FrameAllocator concept, which is used to allocate + * entries when a new page table is required. + * @param page Virtual page that is being unmapped. + */ + template + auto unmap_page(T & allocator, virtual_page page) -> void + { + exception_handling::assert(translate_page(page).has_value(), + "[Page Mapper] Attempted to unmap page, which has not been mapped previously"); + + auto current_handle = active_handle; + + for (auto level = page_table_handle::LEVEL4; level != page_table_handle::LEVEL1; --level) + { + auto const level_index = page.get_level_index(level); + auto const next_handle = current_handle.next_table(level_index); + // The next table method failed even tough the page has to be mapped already, because translate_page did not + // fail. This can only mean that we attempted to unmap a huge page, which is not supported in the first place. + exception_handling::assert(next_handle.has_value(), "[Page Mapper] Unable to unmap huge pages"); + current_handle = next_handle.value(); + } + + unmap_page_table_entry(allocator, page, current_handle); + kernel::cpu::tlb_flush(page.start_address()); + } + + private: + /** + * @brief Private constructor should only be used by create or get method, which ensures to create only ever one + * instance. + * + * @param active_handle Handle to the underlying currently active level 4 page table. + */ + active_page_table(page_table_handle active_handle); + + /** + * @brief Deleted copy constructor. + */ + active_page_table(active_page_table const &) = delete; + + /** + * @brief Deleted copy assignment operator. + */ + active_page_table & operator=(active_page_table const &) = delete; + + /** + * @brief Unmaps specific page at the current internal handle level. + * + * @tparam T Type constraint of the allocator, being that is follows the given concept and contains an allocate and + * deallocate method. + * @param allocator Reference to an allocator following the FrameAllocator concept, which is used to allocate + * entries *when a new page table is required. + * @param page Virtual page that is being unmapped. + * @param handle Page Table handle we want to access the entry that should be cleared on. + */ + template + static auto unmap_page_table_entry(T & allocator, virtual_page page, page_table_handle & handle) -> void + { + auto level_index = page.get_level_index(handle.get_level()); + auto & entry = handle[level_index]; + auto const frame = entry.calculate_pointed_to_frame(); + exception_handling::assert(frame.has_value(), + "[Page Mapper] Attempted to unmap page, which has not been mapped previously"); + entry.set_unused(); + allocator.deallocate_frame(frame.value()); + } + + public: + page_table_handle active_handle; ///< Underlying active level 4 page table + }; + +} // namespace teachos::arch::memory::paging + +#endif // TEACHOS_ARCH_X86_64_MEMORY_PAGING_ACTIVE_PAGE_TABLE_HPP diff --git a/arch/x86_64/pre/include/arch/memory/paging/inactive_page_table.hpp b/arch/x86_64/pre/include/arch/memory/paging/inactive_page_table.hpp new file mode 100644 index 0000000..8d96740 --- /dev/null +++ b/arch/x86_64/pre/include/arch/memory/paging/inactive_page_table.hpp @@ -0,0 +1,39 @@ +#ifndef TEACHOS_ARCH_X86_64_MEMORY_PAGING_INACTIVE_PAGE_TABLE_HPP +#define TEACHOS_ARCH_X86_64_MEMORY_PAGING_INACTIVE_PAGE_TABLE_HPP + +#include "arch/memory/allocator/physical_frame.hpp" +#include "arch/memory/paging/active_page_table.hpp" +#include "arch/memory/paging/temporary_page.hpp" + +namespace teachos::arch::memory::paging +{ + /** + * @brief By the CPU used level 4 page table. + */ + struct inactive_page_table + { + /** + * @brief Constructor. + * + * @param frame Frame that should be mapped as the level 4 page table. + */ + inactive_page_table(allocator::physical_frame frame); + + /** + * @brief Constructor. + * + * @param frame Frame that should be mapped as the level 4 page table. + * @param active_page_table Actual active page table that should be unmapped so we can map a new level 4 + * page table. + * @param temporary_page Temporary page that should be used to map the given frame as the new level 4 page + * table. + */ + inactive_page_table(allocator::physical_frame frame, active_page_table & active_page_table, + temporary_page & temporary_page); + + allocator::physical_frame page_table_level_4_frame; ///< Temporary level 4 page table + }; + +} // namespace teachos::arch::memory::paging + +#endif // TEACHOS_ARCH_X86_64_MEMORY_PAGING_INACTIVE_PAGE_TABLE_HPP diff --git a/arch/x86_64/pre/include/arch/memory/paging/kernel_mapper.hpp b/arch/x86_64/pre/include/arch/memory/paging/kernel_mapper.hpp new file mode 100644 index 0000000..3afb54b --- /dev/null +++ b/arch/x86_64/pre/include/arch/memory/paging/kernel_mapper.hpp @@ -0,0 +1,180 @@ +#ifndef TEACHOS_ARCH_X86_64_MEMORY_PAGING_KERNEL_MAPPER_HPP +#define TEACHOS_ARCH_X86_64_MEMORY_PAGING_KERNEL_MAPPER_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 +#include + +namespace teachos::arch::memory::paging +{ + /** + * @brief Kernel mapper that allows to remap the kernel elf sections in C++. + * + * @tparam T Contract the allocator that should be used to allocate frames for the remapping process has to fulfill. + */ + template + struct kernel_mapper + { + /** + * @brief Constructor. + * + * @param allocator Allocator that should be used to allocate frames for the remapping process. + * @param mem_info Information about elf kernel sections required for remapping process. + */ + kernel_mapper(T & allocator, multiboot::memory_information const & mem_info) + : allocator(allocator) + , mem_info(mem_info) + { + // Nothing to do + } + + /** + * @brief Remap the kernel, meaning we map the entire kernel and all of it's elf sections with the correct flags + * into memory and then replace the created mapping with the current one. + * + * @note We have to use a workaround with an + * 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() -> 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}; + 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"); + inactive_page_table new_table{frame.value(), active_table, temporary_page}; + remap_elf_kernel_sections(new_table, temporary_page, active_table); + auto const old_table = switch_active_page_table(new_table); + // Turn old level 4 page table, mapped by assembler code into stack guard page. + // Only works if the identity mapped page tables by assembler are right above the stack. + 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); + } + + private: + /** + * @brief Remaps the kernel elf sections. + * + * This is done with switching the current level 4 page table recursive + * mapping to any unmapped address in memory and then actually mapping the level 4 page table on that address. + * Once the remapping process is done we can restore the original recursive mapping with the complete remapped + * kernel. + * + * @note Because we change the entries we also have to ensure we flush the translation lookaside buffer, before we + * map the entries. + * + * @param inactive_table Level 4 page table we temporarily map the kernel into. + * @param temporary_page Temporary page that should be used for the mapping process and then + * unmapped once finished. + * @param active_table Active level 4 page table that has its recursive mapping overwritten temporarily and then + * restored once the process is finished. + */ + 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( + 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); + kernel::cpu::tlb_flush_all(); + map_elf_kernel_sections(active_table); + + page_table_level4[511].set_entry(backup, entry::PRESENT | entry::WRITABLE); + kernel::cpu::tlb_flush_all(); + temporary_page.unmap_page(active_table); + } + + /** + * @brief Switches the current active table pointed to by the CR3 register with another page table that is currently + * inactive. + * + * @param new_table Inactive page table that should now be made active and replace the current active one. + * @return The previous active page table. + */ + auto switch_active_page_table(inactive_page_table new_table) -> inactive_page_table + { + 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(); + kernel::cpu::write_control_register(kernel::cpu::control_register::CR3, new_address); + return old_table; + } + + /** + * @brief Maps the required entries according to every elf section and it's contained frames. Additionally each of + * thoose frames gets the correct entry flags according to elf section flags. + * + * @param active_table Active level 4 page table that should be used to map the required elf sections into entries. + * Has had its recursive mapping temporarily replaced and points to unmapped place in memory. + */ + 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 constexpr USER_SECTION_BASES = { + 0x102000, // .boot_bss (Contains statically allocated variables) + 0x209000, // .stl_text (Contains code for custom std implementations and standard library code) + 0x217000, // .user_text (Contains the actual user code executed) + 0x21E000, // .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)) + { + continue; + } + exception_handling::assert(section.physical_address % allocator::PAGE_FRAME_SIZE == 0U, + "[Kernel Mapper] Section must be page aligned"); + auto const start_frame = allocator::physical_frame::containing_address(section.physical_address); + // End address is exclusive, so that it is not part of the section anymore (one past the last frame of this + // section). But end frame would now point to the actual last frame and not one past the last frame, therefore + // we increment by one to get one past the last frame of this section. + auto const end_frame = + ++(allocator::physical_frame::containing_address(section.physical_address + section.section_size - 1)); + + allocator::frame_container::iterator const begin{start_frame}; + allocator::frame_container::iterator const end{end_frame}; + allocator::frame_container const frames{begin, end}; + 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) + { + active_table.identity_map(allocator, frame, entry.get_flags()); + } + } + + auto const vga_buffer_frame = + allocator::physical_frame::containing_address(video::vga::text::DEFAULT_VGA_TEXT_BUFFER_ADDRESS); + active_table.identity_map(allocator, vga_buffer_frame, entry::WRITABLE); + } + + T & allocator; + multiboot::memory_information const & + mem_info; ///< Information about elf kernel sections required for remapping process. + }; + +} // namespace teachos::arch::memory::paging + +#endif // TEACHOS_ARCH_X86_64_MEMORY_PAGING_KERNEL_MAPPER_HPP diff --git a/arch/x86_64/pre/include/arch/memory/paging/page_entry.hpp b/arch/x86_64/pre/include/arch/memory/paging/page_entry.hpp new file mode 100644 index 0000000..8147c5c --- /dev/null +++ b/arch/x86_64/pre/include/arch/memory/paging/page_entry.hpp @@ -0,0 +1,121 @@ +#ifndef TEACHOS_ARCH_X86_64_MEMORY_PAGING_PAGE_ENTRY_HPP +#define TEACHOS_ARCH_X86_64_MEMORY_PAGING_PAGE_ENTRY_HPP + +#include "arch/memory/allocator/physical_frame.hpp" +#include "arch/memory/multiboot/elf_symbols_section.hpp" + +#include +#include + +namespace teachos::arch::memory::paging +{ + /** + * @brief Marks a specific entry in an actual page table. + */ + struct entry + { + /** + * @brief Possible set bits in our underlying std::bitset and the meaning when they are set. + */ + enum bitset : uint64_t + { + 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_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. + DIRTY = 1UL << 6UL, ///< Page has been writen too. + HUGE_PAGE = 1UL << 7UL, ///< Page is huge (2 MiB page size in P2 page table and 1 GiB in P3 page table, + ///< instead of 4 KiB). Has to be false for P1 and P4 page tables. + GLOBAL = 1UL << 8UL, ///< Page is not flushed from caches on address space switches (PGE bit of CR4 register + ///< has to be set) + EXECUTING_CODE_FORBIDDEN = + 1UL << 63UL, ///< Page is forbidden from executing code (NXE bit in the EFER register has to be set) + }; + + /** + * @brief Defaulted constructor. + */ + entry() = default; + + /** + * @brief Creates a new entry object from a 64bit address. Should follow the scheme where bit index 12 - 51 are the + * actual address and the other bits are flags. + * + * @param flags Flags that will be passed to underlying std::bitset. + */ + explicit entry(uint64_t flags); + + /** + * @brief Creates a new entry converting the given elf section flags into the corresponding correct entry flags. + * + * @note Enables us to set the correct flags on a entry depending on which elf section it is contained in. For + * example entries of .text sections should be executable and read only or entries of .data sections should be + * writable but not executable. + * + * @param elf_flags Elf section flags we want to convert into entry flags. + */ + explicit entry(multiboot::elf_section_flags elf_flags); + + /** + * @brief Whether the current page is unused, meaning the underlying std::bitset is 0. + * + * @return Current page is in memory. + */ + auto is_unused() const -> bool; + + /** + * @brief Marks the page as unused, meaning the underlying std::bitset is set to 0. + */ + 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. + * + * @return Calculated physical frame entry is pointing too. + */ + auto calculate_pointed_to_frame() const -> std::optional; + + /** + * @brief Copies the address and flags from the given physical frame into the underlying std::bitset + * + * @param frame Physical frame that contains the address we want to copy into our underlying std::bitset. + * @param additional_flags Entry flags which will be copied into our underlying std::bitset. + */ + auto set_entry(allocator::physical_frame frame, std::bitset<64U> additional_flags) -> void; + + /** + * @brief Checks if the given std::bitset is a subset or equivalent to the underlying std::bitset. + * + * @note Meaning that all bits that are set in the given std::bitset also have to be set in the underlyng + * std::bitset. Any additional bits that are set are not relevant. + * + * @param other Flags that we want to compare against and check if the underlying std::bitset has the same bits set. + * @return Whether the given flags are a subset or equivalent with the underlying std::bitset. + */ + auto contains_flags(std::bitset<64U> other) const -> bool; + + /** + * @brief Extracts only the flags from the underlying entry and ignores all bits that contain the physical address. + * + * @return Extracted entry flags, without the physical address. + */ + auto get_flags() const -> std::bitset<64U>; + + private: + std::bitset<64U> flags; ///< Underlying bitset used to read the flags from. Bits 9 - 11 and 52 - 62 can be + ///< freely used for additional flags by the operating system. + }; + +} // namespace teachos::arch::memory::paging + +#endif // TEACHOS_ARCH_X86_64_MEMORY_PAGING_PAGE_ENTRY_HPP diff --git a/arch/x86_64/pre/include/arch/memory/paging/page_table.hpp b/arch/x86_64/pre/include/arch/memory/paging/page_table.hpp new file mode 100644 index 0000000..b972337 --- /dev/null +++ b/arch/x86_64/pre/include/arch/memory/paging/page_table.hpp @@ -0,0 +1,157 @@ +#ifndef TEACHOS_ARCH_X86_64_MEMORY_PAGING_PAGE_TABLE_HPP +#define TEACHOS_ARCH_X86_64_MEMORY_PAGING_PAGE_TABLE_HPP + +#include "arch/exception_handling/assert.hpp" +#include "arch/memory/allocator/concept.hpp" +#include "arch/memory/paging/page_entry.hpp" + +namespace teachos::arch::memory::paging +{ + std::size_t constexpr PAGE_TABLE_ENTRY_COUNT = 512U; ///< Default entry count of a page table in x86_84 is 512. + + /** + * @brief Forward delcaration of the page_table, because it should only be accessible over the handle. + * + * @note The actual methods or constructor are not defined meaning they are not callable from outside. Instead the + * struct is only fully defined in the implementation (.cpp) file of the page table, and therefore the memthods are + * only accesible in that file. + */ + struct page_table; + + /** + * @brief Handle that ensures accessing the page table is safe because it adds additional checks to the next_table + * method and ensures it can only be called if the table level is not LEVEL1. + */ + struct page_table_handle + { + /** + * @brief Level of the page table. + * + * Level 1 will not be able to call next_table anymore, because it would result in + * attempting to access memory that it should not. + */ + enum level : uint8_t + { + LEVEL1, + LEVEL2, + LEVEL3, + LEVEL4 + }; + + /** + * @brief Constructor. + * + * @param table Underlying page table the handle should point to. + * @param table_level Level the underlying page table is on, used to ensure safety. + */ + page_table_handle(page_table * table, level table_level); + + /** + * @brief Set every entry of the page to unused. + */ + auto zero_entries() -> void; + + /** + * @brief Checks if all entries of this page are unused. + */ + auto is_empty() const -> bool; + + /** + * @brief Get the current table level. + * + * @return Current table level. + */ + auto get_level() const -> level; + + /** + * @brief Returns the next page table level from the given page table index. Meaning we + * use an index into a Level 4 page table to get the according Level 3 page table. + * + * @note If this method is called with a Level 1 page table it will instead assert and halt execution, because there + * is no furthere page table and mangeling up and returning the physical address would cause hard to debug issues. + * + * @param table_index Index of this page table in the page table one level lower. + */ + auto next_table(std::size_t table_index) const -> std::optional; + + /** + * @brief Call next_table and then checks if the table already exists, if it does not it will use the given + * allocator to get the next free frame and set the entry to that instead. + * + * @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 + 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 + // been created itself. So we have to do that before we are able to allocate the wanted frame. This has to be done + // for every level, meaning we potenitally create a level 4, level 3 and level 2 page entry, each pointing to a + // page table one level below. + if (!next_handle.has_value()) + { + auto const allocated_frame = allocator.allocate_frame(); + exception_handling::assert(allocated_frame.has_value(), "[Page mapper] Unable to allocate frame"); + this->operator[](table_index).set_entry(allocated_frame.value(), entry::PRESENT | entry::WRITABLE); + // There should now be an entry at the previously not existent index, therefore we can simply access it again. + next_handle = next_table(table_index); + 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(); + } + + /** + * @brief Index operator overload to access specific mutable entry directy. + * + * @param index Index of the entry we want to access and only read. + * @return Entry at the given table index. + */ + auto operator[](std::size_t index) -> entry &; + + /** + * @brief Index operator overload to access specific immutable entry directy. + * + * @param index Index of the entry we want to access and read or write. + * @return Entry at the given table index. + */ + auto operator[](std::size_t index) const -> entry const &; + + /** + * @brief Pre decrement operator on the page table level enum, is defined so we can use it as a replacement + * for an int index in a range based for loop. + * + * @note Will halt execution if called with page_table_handle::LEVEL1, because there is no level below. Has to be + * defined as either a friend function or inline header method, because we define an operator of another type. In + * this instance friend function was choosen, because the struct itself also requires the operator, but declaring + * before the struct is not possible, because the enum is in the struct. This is inpossible because the struct + * requires the operator declared before itself to work, and the operator requires the struct declared before itself + * to work. Furthermore this allows the defintion of the method to be done in the cpp, avoiding includes in the + * header file. + * + * @param value Value we want to decrement on + * @return New level value decrement by one, meaning the level is also decrement by one Level4 --> Level3, ... + */ + friend auto operator--(level & value) -> level &; + + private: + page_table * table; ///< Handle to underlying page table, can never be null (invariant ensured by + ///< constructor) + level table_level; ///< Level page table is currently on, depends on how often next_level was + ///< called successfuly. + }; + +} // namespace teachos::arch::memory::paging + +#endif // TEACHOS_ARCH_X86_64_MEMORY_PAGING_PAGE_TABLE_HPP diff --git a/arch/x86_64/pre/include/arch/memory/paging/temporary_page.hpp b/arch/x86_64/pre/include/arch/memory/paging/temporary_page.hpp new file mode 100644 index 0000000..d0d7781 --- /dev/null +++ b/arch/x86_64/pre/include/arch/memory/paging/temporary_page.hpp @@ -0,0 +1,64 @@ +#ifndef TEACHOS_ARCH_X86_64_MEMORY_PAGING_TEMPORARY_PAGE_HPP +#define TEACHOS_ARCH_X86_64_MEMORY_PAGING_TEMPORARY_PAGE_HPP + +#include "arch/memory/allocator/physical_frame.hpp" +#include "arch/memory/allocator/tiny_frame_allocator.hpp" +#include "arch/memory/paging/active_page_table.hpp" +#include "arch/memory/paging/virtual_page.hpp" + +namespace teachos::arch::memory::paging +{ + /** + * @brief A temporary page used to remap the kernel. + */ + struct temporary_page + { + /** + * @brief Construct a new temporary page object. + * + * @tparam Type constraint of the allocator, being that is follows the given concept and contains an allocate and + * deallocate method. + * @param page Page to turn into temporary page. + * @param allocator Frame allocator used to fill page. + */ + template + temporary_page(virtual_page page, T & allocator) + : page{page} + , allocator{allocator} + { + // Nothing to do + } + + /** + * @brief Unmap the current page. + * + * @param active_table The current active page table. + */ + auto unmap_page(active_page_table & active_table) -> void; + + /** + * @brief Map the temporary page to a frame. + * + * @param frame The frame to which the page is mapped. + * @param active_table The current active page table. + * @return level1 page table handle containing the mapped page. + */ + auto map_table_frame(allocator::physical_frame frame, active_page_table & active_table) -> page_table_handle; + + private: + /** + * @brief Map the temporary page to a frame. + * + * @param frame The frame to which the page is mapped. + * @param active_table The current active page table. + * @return The virtual address of the page. + */ + auto map_to_frame(allocator::physical_frame frame, active_page_table & active_table) -> virtual_address; + + virtual_page page; ///< Underlying virtual page we want to temporarily map. + allocator::tiny_frame_allocator allocator; ///< Allocator that should be used to map the temporary page. + }; + +} // namespace teachos::arch::memory::paging + +#endif // TEACHOS_ARCH_X86_64_MEMORY_PAGING_TEMPORARY_PAGE_HPP \ No newline at end of file diff --git a/arch/x86_64/pre/include/arch/memory/paging/virtual_page.hpp b/arch/x86_64/pre/include/arch/memory/paging/virtual_page.hpp new file mode 100644 index 0000000..a6c8c39 --- /dev/null +++ b/arch/x86_64/pre/include/arch/memory/paging/virtual_page.hpp @@ -0,0 +1,91 @@ +#ifndef TEACHOS_ARCH_X86_64_MEMORY_PAGING_VIRTUAL_PAGE_HPP +#define TEACHOS_ARCH_X86_64_MEMORY_PAGING_VIRTUAL_PAGE_HPP + +#include "arch/memory/allocator/physical_frame.hpp" +#include "arch/memory/paging/page_table.hpp" + +#include +#include +#include + +namespace teachos::arch::memory::paging +{ + using virtual_address = std::size_t; + + /** + * @brief Virtual page entry contained in P1 page tables + */ + struct virtual_page + { + /** + * @brief Defaulted constructor. + */ + constexpr virtual_page() = default; + + /** + * @brief Constructor. + * + * @param page_number Index number of the current virtual page, used to distinguish it from other pages. + */ + explicit constexpr virtual_page(std::size_t page_number) + : page_number(page_number) + { + // Nothing to do + } + + /** + * @brief Returns the virtual page the given address is contained in. + * + * @param address Virtual address we want to get the corresponding virtual page for. + * @return Frame the given address is contained in. + */ + auto static containing_address(virtual_address address) -> virtual_page; + + /** + * @brief Evaluates the start address of the virtual page. + * + * @return Start address of the virtual page. + */ + auto start_address() const -> virtual_address; + + /** + * @brief Calculates the index into the page table with the given level, which leads to this virtual page. + * + * @param level Level of the page table we want to calculate the index for. + * @return Index into the page table with the given level. + */ + auto get_level_index(page_table_handle::level level) const -> size_t; + + /** + * @brief Post increment operator. Returns a copy of the value. + * + * @return Copy of the incremented underlying page number. + */ + auto operator++(int) -> virtual_page; + + /** + * @brief Pre increment operator. Returns a reference to the changed value. + * + * @return Reference to the incremented underlying page number. + */ + auto operator++() -> virtual_page &; + + /** + * @brief Defaulted equals operator. + */ + auto operator==(const virtual_page & other) const -> bool = default; + + /** + * @brief Defaulted three-way comparsion operator. + */ + auto operator<=>(const virtual_page & other) const -> std::partial_ordering = default; + + std::size_t page_number = + {}; ///< Index number of the current virtual page, used to distinguish it from other pages. + }; + + using page_container = stl::container>; + +} // namespace teachos::arch::memory::paging + +#endif // TEACHOS_ARCH_X86_64_MEMORY_PAGING_VIRTUAL_PAGE_HPP diff --git a/arch/x86_64/pre/include/arch/user/main.hpp b/arch/x86_64/pre/include/arch/user/main.hpp new file mode 100644 index 0000000..c168a1f --- /dev/null +++ b/arch/x86_64/pre/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/pre/src/context_switching/interrupt_descriptor_table/gate_descriptor.cpp b/arch/x86_64/pre/src/context_switching/interrupt_descriptor_table/gate_descriptor.cpp new file mode 100644 index 0000000..28f289c --- /dev/null +++ b/arch/x86_64/pre/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/pre/src/context_switching/interrupt_descriptor_table/idt_flags.cpp b/arch/x86_64/pre/src/context_switching/interrupt_descriptor_table/idt_flags.cpp new file mode 100644 index 0000000..d36a4c1 --- /dev/null +++ b/arch/x86_64/pre/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/pre/src/context_switching/interrupt_descriptor_table/interrupt_descriptor_table.cpp b/arch/x86_64/pre/src/context_switching/interrupt_descriptor_table/interrupt_descriptor_table.cpp new file mode 100644 index 0000000..7aa0859 --- /dev/null +++ b/arch/x86_64/pre/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(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((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/pre/src/context_switching/interrupt_descriptor_table/interrupt_descriptor_table_pointer.cpp b/arch/x86_64/pre/src/context_switching/interrupt_descriptor_table/interrupt_descriptor_table_pointer.cpp new file mode 100644 index 0000000..7bcbae6 --- /dev/null +++ b/arch/x86_64/pre/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/pre/src/context_switching/interrupt_descriptor_table/ist_offset.cpp b/arch/x86_64/pre/src/context_switching/interrupt_descriptor_table/ist_offset.cpp new file mode 100644 index 0000000..a70e75d --- /dev/null +++ b/arch/x86_64/pre/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/pre/src/context_switching/interrupt_descriptor_table/segment_selector.cpp b/arch/x86_64/pre/src/context_switching/interrupt_descriptor_table/segment_selector.cpp new file mode 100644 index 0000000..27f0a3b --- /dev/null +++ b/arch/x86_64/pre/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(this); } +} // namespace teachos::arch::context_switching::interrupt_descriptor_table diff --git a/arch/x86_64/pre/src/context_switching/main.cpp b/arch/x86_64/pre/src/context_switching/main.cpp new file mode 100644 index 0000000..9539428 --- /dev/null +++ b/arch/x86_64/pre/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(return_function)); + } +} // namespace teachos::arch::context_switching diff --git a/arch/x86_64/pre/src/context_switching/segment_descriptor_table/access_byte.cpp b/arch/x86_64/pre/src/context_switching/segment_descriptor_table/access_byte.cpp new file mode 100644 index 0000000..e31e021 --- /dev/null +++ b/arch/x86_64/pre/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/pre/src/context_switching/segment_descriptor_table/gdt_flags.cpp b/arch/x86_64/pre/src/context_switching/segment_descriptor_table/gdt_flags.cpp new file mode 100644 index 0000000..e444a24 --- /dev/null +++ b/arch/x86_64/pre/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/pre/src/context_switching/segment_descriptor_table/global_descriptor_table.cpp b/arch/x86_64/pre/src/context_switching/segment_descriptor_table/global_descriptor_table.cpp new file mode 100644 index 0000000..bbcee31 --- /dev/null +++ b/arch/x86_64/pre/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 constexpr BASE = 0x0; + std::bitset<20U> constexpr 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 + { + uint64_t constexpr 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(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((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/pre/src/context_switching/segment_descriptor_table/global_descriptor_table_pointer.cpp b/arch/x86_64/pre/src/context_switching/segment_descriptor_table/global_descriptor_table_pointer.cpp new file mode 100644 index 0000000..79088b8 --- /dev/null +++ b/arch/x86_64/pre/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/pre/src/context_switching/segment_descriptor_table/segment_descriptor_base.cpp b/arch/x86_64/pre/src/context_switching/segment_descriptor_table/segment_descriptor_base.cpp new file mode 100644 index 0000000..04804d9 --- /dev/null +++ b/arch/x86_64/pre/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(this); } + +} // namespace teachos::arch::context_switching::segment_descriptor_table diff --git a/arch/x86_64/pre/src/context_switching/segment_descriptor_table/segment_descriptor_extension.cpp b/arch/x86_64/pre/src/context_switching/segment_descriptor_table/segment_descriptor_extension.cpp new file mode 100644 index 0000000..a28ec9b --- /dev/null +++ b/arch/x86_64/pre/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/pre/src/context_switching/syscall/main.cpp b/arch/x86_64/pre/src/context_switching/syscall/main.cpp new file mode 100644 index 0000000..b4ab468 --- /dev/null +++ b/arch/x86_64/pre/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 %%al, %[output]" : [output] "=m"(error_code)); + + return {error_code, values}; + } + +} // namespace teachos::arch::context_switching::syscall diff --git a/arch/x86_64/pre/src/context_switching/syscall/syscall_enable.cpp b/arch/x86_64/pre/src/context_switching/syscall/syscall_enable.cpp new file mode 100644 index 0000000..3c43336 --- /dev/null +++ b/arch/x86_64/pre/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(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/pre/src/context_switching/syscall/syscall_handler.cpp b/arch/x86_64/pre/src/context_switching/syscall/syscall_handler.cpp new file mode 100644 index 0000000..84dbe5f --- /dev/null +++ b/arch/x86_64/pre/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(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(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(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/pre/src/exception_handling/abort.cpp b/arch/x86_64/pre/src/exception_handling/abort.cpp new file mode 100644 index 0000000..e12e4cb --- /dev/null +++ b/arch/x86_64/pre/src/exception_handling/abort.cpp @@ -0,0 +1,15 @@ +#include "arch/exception_handling/panic.hpp" + +#include + +namespace teachos::arch::exception_handling +{ + /** + * @brief Override for the newlib abort function. + * + * @note newlib defines @p ::abort as a weak symbol, thus allowing implementations to override it by simply providing + * a matching implementation. Since the default implemenatation calls a number of functions the kernel does not + * currently implement, @p ::abort gets overridden to simply panic. + */ + extern "C" auto abort() -> void { panic("Terminate was called, possibly due to an unhandled exception"); } +} // namespace teachos::arch::exception_handling diff --git a/arch/x86_64/pre/src/exception_handling/assert.cpp b/arch/x86_64/pre/src/exception_handling/assert.cpp new file mode 100644 index 0000000..b2963de --- /dev/null +++ b/arch/x86_64/pre/src/exception_handling/assert.cpp @@ -0,0 +1,15 @@ +#include "arch/exception_handling/assert.hpp" + +#include "arch/exception_handling/panic.hpp" + +namespace teachos::arch::exception_handling +{ + auto assert(bool condition, char const * message) -> void + { + if (condition) + { + return; + } + panic("Assertion Violation: ", message); + } +} // namespace teachos::arch::exception_handling diff --git a/arch/x86_64/pre/src/exception_handling/panic.cpp b/arch/x86_64/pre/src/exception_handling/panic.cpp new file mode 100644 index 0000000..8e3802a --- /dev/null +++ b/arch/x86_64/pre/src/exception_handling/panic.cpp @@ -0,0 +1,22 @@ +#include "arch/exception_handling/panic.hpp" + +#include "arch/kernel/halt.hpp" +#include "arch/video/vga/text.hpp" + +namespace teachos::arch::exception_handling +{ + extern "C" char const message_prefix_panic[]; + + auto panic(char const * reason) -> void { panic(message_prefix_panic, reason); } + + auto panic(char const * prefix, char const * reason) -> void + { + using video::vga::text::common_attributes::white_on_red; + + video::vga::text::newline(); + video::vga::text::write(prefix, white_on_red); + video::vga::text::write(reason, white_on_red); + + kernel::halt(); + }; +} // namespace teachos::arch::exception_handling diff --git a/arch/x86_64/pre/src/exception_handling/pure_virtual.cpp b/arch/x86_64/pre/src/exception_handling/pure_virtual.cpp new file mode 100644 index 0000000..67772f7 --- /dev/null +++ b/arch/x86_64/pre/src/exception_handling/pure_virtual.cpp @@ -0,0 +1,6 @@ +#include "arch/exception_handling/panic.hpp" + +extern "C" auto __cxa_pure_virtual() -> void +{ + teachos::arch::exception_handling::panic("Runtime", "Tried to call a pure virtual function!"); +} diff --git a/arch/x86_64/pre/src/interrupt_handling/generic_interrupt_handler.cpp b/arch/x86_64/pre/src/interrupt_handling/generic_interrupt_handler.cpp new file mode 100644 index 0000000..9d061a8 --- /dev/null +++ b/arch/x86_64/pre/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/pre/src/kernel/cpu/call.cpp b/arch/x86_64/pre/src/kernel/cpu/call.cpp new file mode 100644 index 0000000..98fa248 --- /dev/null +++ b/arch/x86_64/pre/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/pre/src/kernel/cpu/gdtr.cpp b/arch/x86_64/pre/src/kernel/cpu/gdtr.cpp new file mode 100644 index 0000000..74a4e1c --- /dev/null +++ b/arch/x86_64/pre/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/pre/src/kernel/cpu/idtr.cpp b/arch/x86_64/pre/src/kernel/cpu/idtr.cpp new file mode 100644 index 0000000..7aa20c1 --- /dev/null +++ b/arch/x86_64/pre/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/pre/src/kernel/cpu/if.cpp b/arch/x86_64/pre/src/kernel/cpu/if.cpp new file mode 100644 index 0000000..60a90a3 --- /dev/null +++ b/arch/x86_64/pre/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/pre/src/kernel/cpu/msr.cpp b/arch/x86_64/pre/src/kernel/cpu/msr.cpp new file mode 100644 index 0000000..9c474a1 --- /dev/null +++ b/arch/x86_64/pre/src/kernel/cpu/msr.cpp @@ -0,0 +1,31 @@ +#include "arch/kernel/cpu/msr.hpp" + +namespace teachos::arch::kernel::cpu +{ + namespace + { + auto constexpr IA32_EFER_ADDRESS = 0xC0000080; + } + + auto read_msr(uint32_t msr) -> uint64_t + { + uint32_t low, high; + asm volatile("rdmsr" : "=a"(low), "=d"(high) : "c"(msr)); + return (static_cast(high) << 32) | low; + } + + auto write_msr(uint32_t msr, uint64_t value) -> void + { + uint32_t low = value; + uint32_t high = value >> 32; + asm volatile("wrmsr" + : /* no output from call */ + : "c"(msr), "a"(low), "d"(high)); + } + + auto set_efer_bit(efer_flags flag) -> void + { + auto const efer = read_msr(IA32_EFER_ADDRESS); + write_msr(IA32_EFER_ADDRESS, static_cast::type>(flag) | efer); + } +} // namespace teachos::arch::kernel::cpu diff --git a/arch/x86_64/pre/src/kernel/cpu/segment_register.cpp b/arch/x86_64/pre/src/kernel/cpu/segment_register.cpp new file mode 100644 index 0000000..b08c9c4 --- /dev/null +++ b/arch/x86_64/pre/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/pre/src/kernel/cpu/tr.cpp b/arch/x86_64/pre/src/kernel/cpu/tr.cpp new file mode 100644 index 0000000..a435540 --- /dev/null +++ b/arch/x86_64/pre/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/pre/src/kernel/main.cpp b/arch/x86_64/pre/src/kernel/main.cpp new file mode 100644 index 0000000..43b5f90 --- /dev/null +++ b/arch/x86_64/pre/src/kernel/main.cpp @@ -0,0 +1,71 @@ +#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 +{ + auto stack_overflow_test(int count) -> int + { + int test[5000] = {}; + if (test[0] == 0xFFFF) + { + return count; + } + count = stack_overflow_test(count); + return count++; + } + + auto heap_test() -> void + { + auto test2 = new memory::multiboot::memory_information{}; + auto test3 = new memory::multiboot::memory_information{}; + auto test4 = *test2; + auto test5 = *test3; + test4.kernel_end = 5000; + test5.kernel_end = 3000; + auto test6 = test4.kernel_end; + auto test7 = test5.kernel_end; + auto test8 = memory::multiboot::read_multiboot2(); + if (test6 && test7 && test8.kernel_end) + { + video::vga::text::write("Heap test successful", video::vga::text::common_attributes::green_on_black); + } + test2->kernel_end = 2000; + test2->kernel_start = 1000; + test2->multiboot_start = 2000; + delete test2; + delete test3; + + auto test9 = new int(50); + delete test9; + } + + auto main() -> void + { + video::vga::text::clear(); + video::vga::text::cursor(false); + video::vga::text::write("TeachOS is starting up...", video::vga::text::common_attributes::green_on_black); + video::vga::text::newline(); + + memory::initialize_memory_management(); + // stack_overflow_test(0); + + memory::heap::global_heap_allocator::register_heap_allocator(memory::heap::heap_allocator_type::LINKED_LIST); + // 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/pre/src/memory/allocator/area_frame_allocator.cpp b/arch/x86_64/pre/src/memory/allocator/area_frame_allocator.cpp new file mode 100644 index 0000000..a5a1b49 --- /dev/null +++ b/arch/x86_64/pre/src/memory/allocator/area_frame_allocator.cpp @@ -0,0 +1,85 @@ +#include "arch/memory/allocator/area_frame_allocator.hpp" + +#include "arch/exception_handling/assert.hpp" + +#include +#include +#include + +namespace teachos::arch::memory::allocator +{ + area_frame_allocator::area_frame_allocator(multiboot::memory_information const & mem_info) + : next_free_frame() + , current_area(std::nullopt) + , memory_areas(mem_info.areas) + , kernel_start(physical_frame::containing_address(mem_info.kernel_start)) + , kernel_end(physical_frame::containing_address(mem_info.kernel_end)) + , multiboot_start(physical_frame::containing_address(mem_info.multiboot_start)) + , multiboot_end(physical_frame::containing_address(mem_info.multiboot_end)) + { + choose_next_area(); + } + + auto area_frame_allocator::choose_next_area() -> void + { + current_area = std::nullopt; + auto next_area_with_free_frames = memory_areas | std::views::filter([this](auto const & area) { + auto address = area.base_address + area.area_length - 1; + return physical_frame::containing_address(address) >= next_free_frame; + }); + + auto const lowest_area_with_free_frames = std::ranges::min_element( + next_area_with_free_frames, [](auto const & a, auto const & b) { return a.base_address < b.base_address; }); + + if (lowest_area_with_free_frames != next_area_with_free_frames.end()) + { + current_area = *lowest_area_with_free_frames; + // Update the `next_free_frame` according to the new memory area + auto const start_frame = physical_frame::containing_address(current_area.value().base_address); + if (next_free_frame < start_frame) + { + next_free_frame = start_frame; + } + } + } + + auto area_frame_allocator::allocate_frame() -> std::optional + { + // Only try to allocate memory if current_area is not null, because + // the current_area is null if there is no more available memory. + if (!current_area.has_value()) + { + return std::nullopt; + } + + auto const address = current_area.value().base_address + current_area.value().area_length - 1; + physical_frame current_area_last_frame = physical_frame::containing_address(address); + + if (next_free_frame > current_area_last_frame) + { + // All frames of current area are used, switch to next area. + choose_next_area(); + } + else if (next_free_frame >= kernel_start && next_free_frame <= kernel_end) + { + // `physical_frame` is used by the kernel or multiboot information structure. + next_free_frame = allocator::physical_frame{kernel_end.frame_number + 1}; + } + else if (next_free_frame >= multiboot_start && next_free_frame <= multiboot_end) + { + // `physical_frame` is used by the kernel or multiboot information structure. + next_free_frame = allocator::physical_frame{multiboot_end.frame_number + 1}; + } + else + { + // Frame is unused, increment `next_free_frame` and return it. + next_free_frame.frame_number += 1; + return next_free_frame; + } + + // `physical_frame` was not valid, try it again with the updated `next_free_frame`. + return allocate_frame(); + } + + auto area_frame_allocator::deallocate_frame(physical_frame const & physical_frame) -> void { (void)physical_frame; } +} // namespace teachos::arch::memory::allocator diff --git a/arch/x86_64/pre/src/memory/allocator/tiny_frame_allocator.cpp b/arch/x86_64/pre/src/memory/allocator/tiny_frame_allocator.cpp new file mode 100644 index 0000000..3cdf9c7 --- /dev/null +++ b/arch/x86_64/pre/src/memory/allocator/tiny_frame_allocator.cpp @@ -0,0 +1,34 @@ +#include "arch/memory/allocator/tiny_frame_allocator.hpp" + +#include "arch/exception_handling/panic.hpp" + +namespace teachos::arch::memory::allocator +{ + auto tiny_frame_allocator::allocate_frame() -> std::optional + { + for (auto & frame_option : frames) + { + if (frame_option.has_value()) + { + auto value = frame_option; + frame_option.reset(); + return value; + } + } + return std::nullopt; + } + + auto tiny_frame_allocator::deallocate_frame(physical_frame const & physical_frame) -> void + { + for (auto & frame_option : frames) + { + if (!frame_option.has_value()) + { + frame_option.emplace(physical_frame); + return; + } + } + exception_handling::panic( + "[Tiny Frame Allocator] Attempted to deallocate more than the 3 frames, that can be held"); + } +} // namespace teachos::arch::memory::allocator diff --git a/arch/x86_64/pre/src/memory/heap/bump_allocator.cpp b/arch/x86_64/pre/src/memory/heap/bump_allocator.cpp new file mode 100644 index 0000000..525f45c --- /dev/null +++ b/arch/x86_64/pre/src/memory/heap/bump_allocator.cpp @@ -0,0 +1,54 @@ +#include "arch/memory/heap/bump_allocator.hpp" + +#include "arch/exception_handling/assert.hpp" + +#include +#include + +namespace teachos::arch::memory::heap +{ + namespace + { + template + auto saturating_add(T x, T y) -> T + requires std::is_unsigned_v + { + if (x > std::numeric_limits::max() - y) + { + return std::numeric_limits::max(); + } + T result = x + y; + return result; + } + } // namespace + + 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 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 + // thread and need to redo the calculation. Spurious failure by weak can be ignored, because the whole allocation + // is wrapped in an infinite for loop so a failure that wasn't actually one will simply be retried until it works. + auto const updated = next.compare_exchange_weak(alloc_start, alloc_end, std::memory_order::relaxed); + if (updated) + { + return reinterpret_cast(alloc_start); + } + } + } + + auto bump_allocator::deallocate(void * pointer) noexcept -> void + { + if (pointer) + { + } + } + +} // namespace teachos::arch::memory::heap diff --git a/arch/x86_64/pre/src/memory/heap/global_heap_allocator.cpp b/arch/x86_64/pre/src/memory/heap/global_heap_allocator.cpp new file mode 100644 index 0000000..35cd623 --- /dev/null +++ b/arch/x86_64/pre/src/memory/heap/global_heap_allocator.cpp @@ -0,0 +1,135 @@ +#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 +{ + 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::kfree(void * pointer) noexcept -> void { kernel().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(kernel_allocator_instance == nullptr, + "Calling register_heap_allocator_type can only be done once."); + + switch (new_type) + { + case heap_allocator_type::NONE: + // Nothing to do + break; + case heap_allocator_type::BUMP: { + 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 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::kernel() -> heap_allocator & + { + exception_handling::assert(kernel_allocator_instance != nullptr, NOT_REGISTRED_ERROR_MESSAGE); + + return *kernel_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(&NOT_REGISTRED_ERROR_MESSAGE)}); + return *user_allocator_instance; + } +} // namespace teachos::arch::memory::heap + +auto operator new(std::size_t size) -> void * +{ + 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 +{ + 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; + 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 * +{ + 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 +{ + 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; + 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/pre/src/memory/heap/linked_list_allocator.cpp b/arch/x86_64/pre/src/memory/heap/linked_list_allocator.cpp new file mode 100644 index 0000000..00ca366 --- /dev/null +++ b/arch/x86_64/pre/src/memory/heap/linked_list_allocator.cpp @@ -0,0 +1,177 @@ +#include "arch/memory/heap/linked_list_allocator.hpp" + +#include "arch/exception_handling/assert.hpp" +#include "arch/exception_handling/panic.hpp" + +#include + +namespace teachos::arch::memory::heap +{ + linked_list_allocator::linked_list_allocator(std::size_t heap_start, std::size_t heap_end) + : first(nullptr) + , mutex{kstd::mutex{}} + { + auto const heap_size = heap_end - heap_start; + exception_handling::assert( + heap_size > min_allocatable_size(), + "[Linked List Allocator] Total heap size can not be smaller than minimum of 16 bytes to hold " + "atleast one memory hole entry"); + first = new (reinterpret_cast(heap_start)) memory_block(heap_size, nullptr); + } + + auto linked_list_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) + { + 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(reinterpret_cast(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(reinterpret_cast(memory_address) + sizeof(std::size_t)); + } + + previous = current; + current = current->next; + } + + exception_handling::panic("[Linked List Allocator] Out of memory"); + } + + auto linked_list_allocator::deallocate(void * pointer) noexcept -> void + { + mutex.lock(); + + // Read configured header size of the complete allocated block + auto const header_pointer = reinterpret_cast(reinterpret_cast(pointer) - sizeof(std::size_t)); + auto const total_size = *reinterpret_cast(header_pointer); + + auto const start_address = reinterpret_cast(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(current) >= end_address) + { + break; + } + + previous = current; + current = current->next; + } + + coalesce_free_memory_block(previous, current, header_pointer, total_size); + mutex.unlock(); + } + + auto linked_list_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 linked_list_allocator::split_free_memory_block(memory_block * previous_block, memory_block * current_block, + std::size_t size) -> void * + { + auto const end_address = reinterpret_cast(current_block) + size; + auto const new_block = + new (reinterpret_cast(end_address)) memory_block(current_block->size - size, current_block->next); + return replace_free_memory_block(previous_block, current_block, new_block); + } + + auto linked_list_allocator::replace_free_memory_block(memory_block * previous_block, memory_block * current_block, + memory_block * new_block) -> void * + { + auto const start_address = reinterpret_cast(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(start_address); + } + + auto linked_list_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(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(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(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. + exception_handling::assert(previous_block == nullptr || + start_address >= + (reinterpret_cast(previous_block) + previous_block->size), + "[Linked List Allocator] Attempted double free detected"); + + 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/pre/src/memory/heap/memory_block.cpp b/arch/x86_64/pre/src/memory/heap/memory_block.cpp new file mode 100644 index 0000000..bc97bd6 --- /dev/null +++ b/arch/x86_64/pre/src/memory/heap/memory_block.cpp @@ -0,0 +1,15 @@ +#include "arch/memory/heap/memory_block.hpp" + +#include + +namespace teachos::arch::memory::heap +{ + memory_block::memory_block(std::size_t size, memory_block * next) + { + memset(static_cast(this), 0U, size); + this->size = size; + this->next = next; + } + + memory_block::~memory_block() { memset(static_cast(this), 0U, sizeof(memory_block)); } +} // namespace teachos::arch::memory::heap diff --git a/arch/x86_64/pre/src/memory/heap/user_heap_allocator.cpp b/arch/x86_64/pre/src/memory/heap/user_heap_allocator.cpp new file mode 100644 index 0000000..427a68a --- /dev/null +++ b/arch/x86_64/pre/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 + +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(&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(reinterpret_cast(pointer) - sizeof(std::size_t)); + auto const total_size = *reinterpret_cast(header_pointer); + + auto const start_address = reinterpret_cast(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(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 + { + 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(reinterpret_cast(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(reinterpret_cast(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(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(current_block) + size; + auto const new_block = + new (reinterpret_cast(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(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(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(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(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(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(previous_block) + previous_block->size), + reinterpret_cast(&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/pre/src/memory/main.cpp b/arch/x86_64/pre/src/memory/main.cpp new file mode 100644 index 0000000..2746a71 --- /dev/null +++ b/arch/x86_64/pre/src/memory/main.cpp @@ -0,0 +1,77 @@ +#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/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 + +namespace teachos::arch::memory +{ + namespace + { + static std::optional frame_allocator; + + auto create_frame_allocator(multiboot::memory_information const & memory_information) + -> allocator::area_frame_allocator & + { + 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; + arch::exception_handling::assert(!has_been_called, + "[Initialization] Memory management has already been initialized"); + has_been_called = true; + + auto const memory_information = multiboot::read_multiboot2(); + decltype(auto) allocator = create_frame_allocator(memory_information); + + 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); + kernel.remap_kernel(); + video::vga::text::write("Kernel remapping successful", video::vga::text::common_attributes::green_on_black); + video::vga::text::newline(); + + 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(); + } +} // namespace teachos::arch::memory diff --git a/arch/x86_64/pre/src/memory/multiboot/elf_symbols_section.cpp b/arch/x86_64/pre/src/memory/multiboot/elf_symbols_section.cpp new file mode 100644 index 0000000..f5d126b --- /dev/null +++ b/arch/x86_64/pre/src/memory/multiboot/elf_symbols_section.cpp @@ -0,0 +1,13 @@ +#include "arch/memory/multiboot/elf_symbols_section.hpp" + +namespace teachos::arch::memory::multiboot +{ + auto elf_section_flags::contains_flags(std::bitset<64U> other) const -> bool { return (flags & other) == other; } + + auto elf_section_header::is_null() const -> bool + { + return name_table_index == 0U && type == elf_section_type::INACTIVE && flags == elf_section_flags(0U) && + physical_address == 0U && file_offset == 0U && additional_information == 0U && address_alignment == 0U && + fixed_table_entry_size == 0U; + } +} // namespace teachos::arch::memory::multiboot diff --git a/arch/x86_64/pre/src/memory/multiboot/reader.cpp b/arch/x86_64/pre/src/memory/multiboot/reader.cpp new file mode 100644 index 0000000..b05e6b3 --- /dev/null +++ b/arch/x86_64/pre/src/memory/multiboot/reader.cpp @@ -0,0 +1,135 @@ +#include "arch/memory/multiboot/reader.hpp" + +#include "arch/boot/pointers.hpp" +#include "arch/exception_handling/assert.hpp" +#include "multiboot2/information.hpp" +// #include "arch/memory/multiboot/elf_symbols_section.hpp" +// #include "arch/memory/multiboot/info.hpp" + +#include +#include + +// namespace teachos::arch::memory::multiboot +// { +// namespace +// { +// template +// requires std::is_pointer::value +// auto align_to_8_byte_boundary(T ptr, uint32_t size) -> T +// { +// return reinterpret_cast(reinterpret_cast(ptr) + ((size + 7) & ~7)); +// } + +// auto process_memory_map(memory_map_header * mminfo) -> memory_area_container +// { +// auto const expected_entry_size = mminfo->entry_size; +// auto constexpr actual_entry_size = sizeof(memory_area); +// exception_handling::assert(expected_entry_size == actual_entry_size, +// "[Multiboot Reader] Unexpected memory area entry size"); + +// auto const total_size = mminfo->info.size; +// auto const total_entries_size = total_size - sizeof(memory_map_header) + actual_entry_size; +// auto const number_of_entries = total_entries_size / actual_entry_size; + +// auto const begin = memory_area_container::iterator{&mminfo->entries}; +// auto const end = begin + number_of_entries; +// return memory_area_container{begin, end}; +// } + +// auto process_elf_sections(elf_symbols_section_header * symbol, std::size_t & kernel_start, std::size_t & +// kernel_end) +// -> elf_section_header_container +// { +// auto const expected_entry_size = symbol->entry_size; +// auto constexpr actual_entry_size = sizeof(elf_section_header); +// exception_handling::assert(expected_entry_size == actual_entry_size, +// "[Multiboot Reader] Unexpected elf section header entry size"); + +// auto const expected_total_size = symbol->info.size; +// auto const actual_total_entry_size = actual_entry_size * symbol->number_of_sections; +// auto constexpr actual_total_section_size = sizeof(elf_symbols_section_header) - sizeof(uint32_t); +// auto const actual_total_size = actual_total_entry_size + actual_total_section_size; +// exception_handling::assert(expected_total_size == actual_total_size, +// "[Multiboot Reader] Unexpected elf symbols section header total size"); + +// auto const begin = elf_section_header_container::iterator{reinterpret_cast(&symbol->end)}; auto const end = begin + symbol->number_of_sections; +// exception_handling::assert(begin->is_null(), +// "[Multiboot Reader] Elf symbols section not starting with SHT_NULL section"); + +// elf_section_header_container sections{begin, end}; + +// auto allocated_sections = sections | std::views::filter([](auto const & section) { +// return section.flags.contains_flags(elf_section_flags::OCCUPIES_MEMORY); +// }); + +// auto const elf_section_with_lowest_physical_address = std::ranges::min_element( +// allocated_sections, [](auto const & a, auto const & b) { return a.physical_address < b.physical_address; +// }); + +// auto const elf_section_with_highest_physical_address = +// std::ranges::max_element(allocated_sections, [](auto const & a, auto const & b) { +// auto a_physical_address_end = a.physical_address + a.section_size; +// auto b_physical_address_end = b.physical_address + b.section_size; +// return a_physical_address_end < b_physical_address_end; +// }); + +// auto const symbol_table_section_count = std::ranges::count_if(sections, [](auto const & section) { +// return section.type == elf_section_type::DYNAMIC_SYMBOL_TABLE || section.type == +// elf_section_type::SYMBOL_TABLE; +// }); +// auto const dynamic_section_count = std::ranges::count_if( +// sections, [](auto const & section) { return section.type == elf_section_type::DYNAMIC; }); + +// exception_handling::assert( +// symbol_table_section_count == 1U, +// "[Multiboot Reader] ELF Specifications allows only (1) symbol table section, but got more"); +// exception_handling::assert( +// dynamic_section_count <= 1U, +// "[Multiboot Reader] ELF Specifications allows only (1) or less dynamic sections, but got more"); + +// auto const lowest_elf_section = *elf_section_with_lowest_physical_address; +// kernel_start = lowest_elf_section.physical_address; + +// auto const highest_elf_section = *elf_section_with_highest_physical_address; +// kernel_end = highest_elf_section.physical_address + highest_elf_section.section_size; + +// return sections; +// } +// } // namespace + +// auto read_multiboot2() -> memory_information +// { +// memory_information mem_info{UINT64_MAX, +// 0U, +// elf_section_header_container{}, +// boot::multiboot_information_pointer, +// 0U, +// memory_area_container{}}; + +// auto const multiboot_information_pointer = reinterpret_cast(boot::multiboot_information_pointer); +// auto const multiboot_tag = &multiboot_information_pointer->tags; +// mem_info.multiboot_end = mem_info.multiboot_start + multiboot_information_pointer->total_size; + +// for (auto tag = multiboot_tag; tag->type != tag_type::END; tag = align_to_8_byte_boundary(tag, tag->size)) +// { +// switch (tag->type) +// { +// case tag_type::ELF_SECTIONS: { +// auto const symbol = reinterpret_cast(tag); +// mem_info.sections = process_elf_sections(symbol, mem_info.kernel_start, mem_info.kernel_end); +// break; +// } +// case tag_type::MEMORY_MAP: { +// auto const mminfo = reinterpret_cast(tag); +// mem_info.areas = process_memory_map(mminfo); +// break; +// } +// default: +// // All other cases are not important and can be ignored. +// break; +// } +// } +// return mem_info; +// } +// } // namespace teachos::arch::memory::multiboot diff --git a/arch/x86_64/pre/src/memory/paging/active_page_table.cpp b/arch/x86_64/pre/src/memory/paging/active_page_table.cpp new file mode 100644 index 0000000..0113869 --- /dev/null +++ b/arch/x86_64/pre/src/memory/paging/active_page_table.cpp @@ -0,0 +1,98 @@ +#include "arch/memory/paging/active_page_table.hpp" + +namespace teachos::arch::memory::paging +{ + namespace + { + paging::virtual_address constexpr PAGE_TABLE_LEVEL_4_ADDRESS = 0xffffffff'fffff000; + } + + auto active_page_table::create_or_get() -> active_page_table & + { + static page_table_handle active_handle{reinterpret_cast(PAGE_TABLE_LEVEL_4_ADDRESS), + page_table_handle::LEVEL4}; + static active_page_table active_page{active_handle}; + return active_page; + } + + auto active_page_table::operator[](std::size_t index) -> entry & { return active_handle[index]; } + + auto active_page_table::translate_address(virtual_address address) -> std::optional + { + auto const offset = address % allocator::PAGE_FRAME_SIZE; + auto const page = virtual_page::containing_address(address); + auto const frame = translate_page(page); + + if (frame.has_value()) + { + return frame.value().frame_number * allocator::PAGE_FRAME_SIZE + offset; + } + + return std::nullopt; + } + + auto active_page_table::translate_page(virtual_page page) -> std::optional + { + auto current_handle = active_handle; + + for (auto level = page_table_handle::LEVEL4; level != page_table_handle::LEVEL1; --level) + { + auto const next_handle = current_handle.next_table(page.get_level_index(level)); + // If the next table method failed then it is highly likely that it was a huge page and we therefore have to + // parse the table differently. Therefore, we attempt to parse it using the method required by huge pages. + if (!next_handle.has_value()) + { + return translate_huge_page(page); + } + current_handle = next_handle.value(); + } + + auto const level1_index = page.get_level_index(page_table_handle::LEVEL1); + auto const level1_entry = current_handle[level1_index]; + return level1_entry.calculate_pointed_to_frame(); + } + + auto active_page_table::translate_huge_page(virtual_page page) -> std::optional + { + auto current_handle = active_handle; + auto level3_handle = current_handle.next_table(page.get_level_index(page_table_handle::LEVEL4)); + + if (!level3_handle.has_value()) + { + return std::nullopt; + } + + auto const level3_entry = level3_handle.value()[page.get_level_index(page_table_handle::LEVEL3)]; + auto const level3_frame = level3_entry.calculate_pointed_to_frame(); + if (level3_frame.has_value() && level3_entry.contains_flags(entry::HUGE_PAGE)) + { + exception_handling::assert( + level3_frame.value().frame_number % (PAGE_TABLE_ENTRY_COUNT * PAGE_TABLE_ENTRY_COUNT) == 0U, + "[Page Mapper] Physical address must be 1 GiB aligned"); + return allocator::physical_frame{level3_frame.value().frame_number + + page.get_level_index(page_table_handle::LEVEL2) * PAGE_TABLE_ENTRY_COUNT + + page.get_level_index(page_table_handle::LEVEL1)}; + } + + auto level2_handle = level3_handle.value().next_table(page.get_level_index(page_table_handle::LEVEL3)); + if (level2_handle.has_value()) + { + auto const level2_entry = level2_handle.value()[page.get_level_index(page_table_handle::LEVEL2)]; + auto const level2_frame = level2_entry.calculate_pointed_to_frame(); + if (level2_frame.has_value() && level2_entry.contains_flags(entry::HUGE_PAGE)) + { + exception_handling::assert(level2_frame.value().frame_number % PAGE_TABLE_ENTRY_COUNT == 0U, + "[Page Mapper] Physical address must be 2 MiB aligned"); + return allocator::physical_frame{level2_frame.value().frame_number + + page.get_level_index(page_table_handle::LEVEL1)}; + } + } + return std::nullopt; + } + + active_page_table::active_page_table(page_table_handle active_handle) + : active_handle(active_handle) + { + // Nothing to do + } +} // namespace teachos::arch::memory::paging diff --git a/arch/x86_64/pre/src/memory/paging/inactive_page_table.cpp b/arch/x86_64/pre/src/memory/paging/inactive_page_table.cpp new file mode 100644 index 0000000..4e0610e --- /dev/null +++ b/arch/x86_64/pre/src/memory/paging/inactive_page_table.cpp @@ -0,0 +1,20 @@ +#include "arch/memory/paging/inactive_page_table.hpp" + +namespace teachos::arch::memory::paging +{ + inactive_page_table::inactive_page_table(allocator::physical_frame frame) + : page_table_level_4_frame{frame} + { + // Nothing to do + } + + inactive_page_table::inactive_page_table(allocator::physical_frame frame, active_page_table & active_page_table, + temporary_page & temporary_page) + : page_table_level_4_frame{frame} + { + auto table = temporary_page.map_table_frame(page_table_level_4_frame, active_page_table); + table.zero_entries(); + table[511].set_entry(page_table_level_4_frame, entry::PRESENT | entry::WRITABLE); + temporary_page.unmap_page(active_page_table); + } +} // namespace teachos::arch::memory::paging diff --git a/arch/x86_64/pre/src/memory/paging/page_entry.cpp b/arch/x86_64/pre/src/memory/paging/page_entry.cpp new file mode 100644 index 0000000..57045ca --- /dev/null +++ b/arch/x86_64/pre/src/memory/paging/page_entry.cpp @@ -0,0 +1,63 @@ +#include "arch/memory/paging/page_entry.hpp" + +#include "arch/exception_handling/assert.hpp" + +namespace teachos::arch::memory::paging +{ + namespace + { + std::size_t constexpr PHYSICAL_ADDRESS_MASK = 0x000fffff'fffff000; + } // namespace + + entry::entry(uint64_t flags) + : flags(flags) + { + // Nothing to do. + } + + entry::entry(multiboot::elf_section_flags elf_flags) + { + if (elf_flags.contains_flags(multiboot::elf_section_flags::OCCUPIES_MEMORY)) + { + 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; + } + } + + auto entry::is_unused() const -> bool { return flags == 0U; } + + 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 + { + if (contains_flags(PRESENT)) + { + auto const address = flags.to_ulong() & PHYSICAL_ADDRESS_MASK; + return allocator::physical_frame::containing_address(address); + } + return std::nullopt; + } + + auto entry::contains_flags(std::bitset<64U> other) const -> bool { return (flags & other) == other; } + + auto entry::set_entry(allocator::physical_frame frame, std::bitset<64U> additional_flags) -> void + { + 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(); + } + + auto entry::get_flags() const -> std::bitset<64U> { return flags.to_ulong() & ~PHYSICAL_ADDRESS_MASK; } +} // namespace teachos::arch::memory::paging diff --git a/arch/x86_64/pre/src/memory/paging/page_table.cpp b/arch/x86_64/pre/src/memory/paging/page_table.cpp new file mode 100644 index 0000000..eb11810 --- /dev/null +++ b/arch/x86_64/pre/src/memory/paging/page_table.cpp @@ -0,0 +1,128 @@ +#include "arch/memory/paging/page_table.hpp" + +#include +#include +#include + +/* + * This is a linker variable reference. This referenc cannot reside inside a namespace, because in + * that case the compiler would try to find arch::memory::paging::_end_of_image inside the ELF file. + */ +extern char _end_of_image; + +namespace teachos::arch::memory::paging +{ + /** + * @brief A Page table containing 512 entries. + */ + struct page_table + { + auto zero_entries() -> void; + + auto is_empty() const -> bool; + + auto next_table(std::size_t table_index) const -> std::optional; + + auto operator[](std::size_t index) -> entry &; + + auto operator[](std::size_t index) const -> entry const &; + + private: + /** + * @brief Calculates the address of the next page table level for the given table index. + * + * @note The next page table address is only valid if the corresponding entry is present and not a huge page. + * Meaning we use an index into a Level 4 page table to get the according Level 3 page table address. + * + * @param table_index Index of this page table in the page table one level higher. + * @return An optional of the address of the next page table or null. + */ + auto next_table_address(std::size_t table_index) const -> std::optional; + + std::array entries = + {}; ///< Entries containing addresses to page tables of a level below or + ///< actual virtual addresses for the level 1 page table. + }; + + auto page_table::zero_entries() -> void + { + std::ranges::for_each(entries, [](auto & entry) { entry.set_unused(); }); + } + + auto page_table::is_empty() const -> bool + { + return std::all_of(entries.begin(), entries.end(), [](entry const & entry) { return entry.is_unused(); }); + } + + auto page_table::next_table(std::size_t table_index) const -> std::optional + { + auto const address = next_table_address(table_index); + if (address.has_value()) + { + return reinterpret_cast(address.value()); + } + return std::nullopt; + } + + auto page_table::operator[](std::size_t index) -> entry & + { + exception_handling::assert(index < PAGE_TABLE_ENTRY_COUNT, "[Page Table] Index out of bounds"); + return entries[index]; + } + + auto page_table::operator[](std::size_t index) const -> entry const & + { + exception_handling::assert(index < PAGE_TABLE_ENTRY_COUNT, "[Page Table] Index out of bounds"); + return entries[index]; + } + + auto page_table::next_table_address(std::size_t table_index) const -> std::optional + { + auto const entry = this->operator[](table_index); + + if (entry.contains_flags(entry::PRESENT) && !entry.contains_flags(entry::HUGE_PAGE)) + { + auto const table_address = reinterpret_cast(this); + return ((table_address << 9) | (table_index << 12)); + } + return std::nullopt; + } + + page_table_handle::page_table_handle(page_table * table, page_table_handle::level table_level) + : table(table) + , table_level(table_level) + { + exception_handling::assert(table != nullptr, + "[Page Table] Attempted to pass nullptr as table to page table table method"); + } + + auto page_table_handle::zero_entries() -> void { table->zero_entries(); } + + auto page_table_handle::is_empty() const -> bool { return table->is_empty(); } + + auto page_table_handle::next_table(std::size_t table_index) const -> std::optional + { + exception_handling::assert(table_level != page_table_handle::LEVEL1, + "[Page Table] Attempted to call next_table on level 1 page table"); + auto const next_table = table->next_table(table_index); + if (next_table.has_value()) + { + auto const new_level = static_cast(table_level - 1); + return page_table_handle{next_table.value(), new_level}; + } + return std::nullopt; + } + + auto page_table_handle::get_level() const -> page_table_handle::level { return table_level; } + + auto page_table_handle::operator[](std::size_t index) -> entry & { return table->operator[](index); } + + auto operator--(page_table_handle::level & value) -> page_table_handle::level & + { + exception_handling::assert(value != page_table_handle::LEVEL1, + "[Page table] Attempted to decrement enum to value outside of range"); + auto new_value = static_cast::type>(value); + value = static_cast(--new_value); + return value; + } +} // namespace teachos::arch::memory::paging diff --git a/arch/x86_64/pre/src/memory/paging/temporary_page.cpp b/arch/x86_64/pre/src/memory/paging/temporary_page.cpp new file mode 100644 index 0000000..8e73523 --- /dev/null +++ b/arch/x86_64/pre/src/memory/paging/temporary_page.cpp @@ -0,0 +1,29 @@ +#include "arch/memory/paging/temporary_page.hpp" + +#include "arch/memory/paging/page_entry.hpp" + +namespace teachos::arch::memory::paging +{ + auto temporary_page::map_table_frame(allocator::physical_frame frame, active_page_table & active_table) + -> page_table_handle + { + page_table_handle handle{reinterpret_cast(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 + { + exception_handling::assert(!active_table.translate_page(page).has_value(), + "[Temporary page] Page is already mapped"); + + active_table.map_page_to_frame(allocator, page, frame, entry::WRITABLE); + return page.start_address(); + } + + auto temporary_page::unmap_page(active_page_table & active_table) -> void + { + active_table.unmap_page(allocator, page); + } +} // namespace teachos::arch::memory::paging diff --git a/arch/x86_64/pre/src/memory/paging/virtual_page.cpp b/arch/x86_64/pre/src/memory/paging/virtual_page.cpp new file mode 100644 index 0000000..d374156 --- /dev/null +++ b/arch/x86_64/pre/src/memory/paging/virtual_page.cpp @@ -0,0 +1,33 @@ +#include "arch/memory/paging/virtual_page.hpp" + +#include "arch/exception_handling/assert.hpp" + +namespace teachos::arch::memory::paging +{ + auto virtual_page::containing_address(virtual_address address) -> virtual_page + { + exception_handling::assert(address < 0x00008000'00000000 || address >= 0xffff8000'00000000, + "[Virtual Page] Attempted to create virtual page from invalid address"); + return virtual_page{address / allocator::PAGE_FRAME_SIZE}; + } + + auto virtual_page::start_address() const -> virtual_address { return page_number * allocator::PAGE_FRAME_SIZE; } + + auto virtual_page::get_level_index(page_table_handle::level level) const -> size_t + { + return (page_number >> (level * 9U)) & 0x1FF; + } + + auto virtual_page::operator++(int) -> virtual_page + { + virtual_page const old_value = *this; + ++page_number; + return old_value; + } + + auto virtual_page::operator++() -> virtual_page & + { + ++page_number; + return *this; + } +} // namespace teachos::arch::memory::paging diff --git a/arch/x86_64/pre/src/user/main.cpp b/arch/x86_64/pre/src/user/main.cpp new file mode 100644 index 0000000..8b07e4a --- /dev/null +++ b/arch/x86_64/pre/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 +#include +#include +#include + +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(&syscall_message)}); + + // Test C++ standard library + std::array, 4> array_test = {std::atomic{5}, std::atomic{10}, + std::atomic{15}, std::atomic{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 -- cgit v1.2.3 From b157e2c472d8bd67ac1656404a6a6ee821260f4b Mon Sep 17 00:00:00 2001 From: Felix Morgner Date: Wed, 29 Oct 2025 15:01:43 +0100 Subject: chore: reformat source code --- .../segment_selector.hpp | 6 ++-- .../arch/context_switching/syscall/main.hpp | 5 +++- .../arch/memory/allocator/tiny_frame_allocator.hpp | 2 +- .../arch/memory/heap/global_heap_allocator.hpp | 19 ++++++------- .../include/arch/memory/heap/heap_allocator.hpp | 8 +++--- .../arch/memory/heap/linked_list_allocator.hpp | 6 ++-- .../arch/memory/heap/user_heap_allocator.hpp | 6 +++- .../pre/include/arch/memory/multiboot/reader.hpp | 2 +- .../arch/memory/paging/active_page_table.hpp | 4 +-- .../include/arch/memory/paging/kernel_mapper.hpp | 18 ++++++------ .../pre/include/arch/memory/paging/page_table.hpp | 2 +- .../include/arch/memory/paging/virtual_page.hpp | 4 +-- .../interrupt_descriptor_table/idt_flags.cpp | 5 +++- .../interrupt_descriptor_table.cpp | 2 +- .../segment_selector.cpp | 15 ++++++++-- arch/x86_64/pre/src/context_switching/main.cpp | 13 +++++---- .../segment_descriptor_table/access_byte.cpp | 5 +++- .../segment_descriptor_table/gdt_flags.cpp | 10 +++++-- .../global_descriptor_table.cpp | 10 +++---- .../segment_descriptor_base.cpp | 5 +++- .../segment_descriptor_extension.cpp | 10 +++++-- .../context_switching/syscall/syscall_enable.cpp | 10 +++---- .../context_switching/syscall/syscall_handler.cpp | 11 +++++--- arch/x86_64/pre/src/exception_handling/abort.cpp | 5 +++- arch/x86_64/pre/src/exception_handling/panic.cpp | 5 +++- arch/x86_64/pre/src/kernel/cpu/if.cpp | 10 +++++-- arch/x86_64/pre/src/kernel/cpu/msr.cpp | 2 +- .../src/memory/allocator/area_frame_allocator.cpp | 5 +++- .../pre/src/memory/heap/global_heap_allocator.cpp | 33 +++++++++++++++------- arch/x86_64/pre/src/memory/heap/memory_block.cpp | 5 +++- .../pre/src/memory/heap/user_heap_allocator.cpp | 4 +-- arch/x86_64/pre/src/memory/main.cpp | 4 +-- .../src/memory/multiboot/elf_symbols_section.cpp | 5 +++- .../pre/src/memory/paging/active_page_table.cpp | 11 +++++--- arch/x86_64/pre/src/memory/paging/page_entry.cpp | 27 ++++++++++++++---- arch/x86_64/pre/src/memory/paging/page_table.cpp | 20 ++++++++++--- arch/x86_64/pre/src/memory/paging/virtual_page.cpp | 7 +++-- 37 files changed, 216 insertions(+), 105 deletions(-) (limited to 'arch/x86_64/pre') diff --git a/arch/x86_64/pre/include/arch/context_switching/interrupt_descriptor_table/segment_selector.hpp b/arch/x86_64/pre/include/arch/context_switching/interrupt_descriptor_table/segment_selector.hpp index 2a7704e..ea8c145 100644 --- a/arch/x86_64/pre/include/arch/context_switching/interrupt_descriptor_table/segment_selector.hpp +++ b/arch/x86_64/pre/include/arch/context_switching/interrupt_descriptor_table/segment_selector.hpp @@ -96,9 +96,9 @@ namespace teachos::arch::context_switching::interrupt_descriptor_table 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. + 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 diff --git a/arch/x86_64/pre/include/arch/context_switching/syscall/main.hpp b/arch/x86_64/pre/include/arch/context_switching/syscall/main.hpp index 59adc13..f507c61 100644 --- a/arch/x86_64/pre/include/arch/context_switching/syscall/main.hpp +++ b/arch/x86_64/pre/include/arch/context_switching/syscall/main.hpp @@ -41,7 +41,10 @@ namespace teachos::arch::context_switching::syscall * @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; } + 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 diff --git a/arch/x86_64/pre/include/arch/memory/allocator/tiny_frame_allocator.hpp b/arch/x86_64/pre/include/arch/memory/allocator/tiny_frame_allocator.hpp index 1ceb74d..1e4746d 100644 --- a/arch/x86_64/pre/include/arch/memory/allocator/tiny_frame_allocator.hpp +++ b/arch/x86_64/pre/include/arch/memory/allocator/tiny_frame_allocator.hpp @@ -10,7 +10,7 @@ namespace teachos::arch::memory::allocator { namespace { - uint8_t constexpr TINY_ALLOCATOR_FRAMES_COUNT = 3U; + constexpr uint8_t TINY_ALLOCATOR_FRAMES_COUNT = 3U; } /** diff --git a/arch/x86_64/pre/include/arch/memory/heap/global_heap_allocator.hpp b/arch/x86_64/pre/include/arch/memory/heap/global_heap_allocator.hpp index c98c130..480b1d0 100644 --- a/arch/x86_64/pre/include/arch/memory/heap/global_heap_allocator.hpp +++ b/arch/x86_64/pre/include/arch/memory/heap/global_heap_allocator.hpp @@ -37,7 +37,7 @@ namespace teachos::arch::memory::heap * * @param new_type Type of the heap allocation implementation we want to instantiate */ - static auto register_heap_allocator(heap_allocator_type new_type) -> void; + auto static register_heap_allocator(heap_allocator_type new_type) -> void; /** * @brief Allocates the given amount of memory and returns the pointer to the start of the allocatable memory area. @@ -46,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 kmalloc(std::size_t size) -> void *; + auto static kmalloc(std::size_t size) -> void *; /** * @brief Deallocated all memory associated with the memory area starting from the given pointer address. @@ -54,7 +54,7 @@ namespace teachos::arch::memory::heap * * @param pointer Previously allocated memory area, that should now be freed */ - static auto kfree(void * pointer) noexcept -> void; + auto static kfree(void * pointer) noexcept -> void; /** * @brief Allocates the given amount of memory and returns the pointer to the start of the allocatable memory area. @@ -64,7 +64,7 @@ namespace teachos::arch::memory::heap * @return void* Pointer to the start of the allocatable memory area */ [[gnu::section(".user_text")]] - static auto malloc(std::size_t size) -> void *; + auto static malloc(std::size_t size) -> void *; /** * @brief Deallocated all memory associated with the memory area starting from the given pointer address. @@ -73,11 +73,11 @@ namespace teachos::arch::memory::heap * @param pointer Previously allocated memory area, that should now be freed */ [[gnu::section(".user_text")]] - static auto free(void * pointer) noexcept -> void; + auto static free(void * pointer) noexcept -> void; private: - static heap_allocator * kernel_allocator_instance; ///< Instance used to allocate and deallocate kernel heap memory - [[gnu::section(".user_data")]] static user_heap_allocator * + heap_allocator static * kernel_allocator_instance; ///< Instance used to allocate and deallocate kernel heap memory + [[gnu::section(".user_data")]] user_heap_allocator static * user_allocator_instance; ///< Instance used to allocate and deallocate user heap memory /** @@ -85,15 +85,14 @@ namespace teachos::arch::memory::heap * * @return Reference to the registered kernel heap allocation */ - static auto kernel() -> heap_allocator &; + auto static kernel() -> heap_allocator &; /** * @brief Either returns the previously registered heap allocated or halts further execution * * @return Reference to the registered user heap allocation */ - [[gnu::section(".user_text")]] - static auto user() -> user_heap_allocator &; + [[gnu::section(".user_text")]] auto static user() -> user_heap_allocator &; }; } // namespace teachos::arch::memory::heap diff --git a/arch/x86_64/pre/include/arch/memory/heap/heap_allocator.hpp b/arch/x86_64/pre/include/arch/memory/heap/heap_allocator.hpp index 420a1d3..6c25532 100644 --- a/arch/x86_64/pre/include/arch/memory/heap/heap_allocator.hpp +++ b/arch/x86_64/pre/include/arch/memory/heap/heap_allocator.hpp @@ -5,10 +5,10 @@ namespace teachos::arch::memory::heap { - 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; + constexpr std::size_t KERNEL_HEAP_START = 0x1'0000'0000; + constexpr std::size_t KERNEL_HEAP_SIZE = 100 * 1024; + constexpr std::size_t USER_HEAP_START = 0x1'0001'9000; // Starts directly after kernel heap + constexpr std::size_t 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/pre/include/arch/memory/heap/linked_list_allocator.hpp b/arch/x86_64/pre/include/arch/memory/heap/linked_list_allocator.hpp index bbbad19..540ff5c 100644 --- a/arch/x86_64/pre/include/arch/memory/heap/linked_list_allocator.hpp +++ b/arch/x86_64/pre/include/arch/memory/heap/linked_list_allocator.hpp @@ -3,7 +3,6 @@ #include "arch/memory/heap/heap_allocator.hpp" #include "arch/memory/heap/memory_block.hpp" - #include namespace teachos::arch::memory::heap @@ -44,7 +43,10 @@ namespace teachos::arch::memory::heap * * @return Smallest allocatable block of heap memory. */ - auto constexpr min_allocatable_size() -> std::size_t { return sizeof(memory_block); } + constexpr auto min_allocatable_size() -> std::size_t + { + return sizeof(memory_block); + } /** * @brief Removes a free memory block from the free list and returns its address so the caller can allocate into it. diff --git a/arch/x86_64/pre/include/arch/memory/heap/user_heap_allocator.hpp b/arch/x86_64/pre/include/arch/memory/heap/user_heap_allocator.hpp index 3b47f15..15d8574 100644 --- a/arch/x86_64/pre/include/arch/memory/heap/user_heap_allocator.hpp +++ b/arch/x86_64/pre/include/arch/memory/heap/user_heap_allocator.hpp @@ -5,6 +5,7 @@ // #include #include + #include namespace teachos::arch::memory::heap @@ -47,7 +48,10 @@ namespace teachos::arch::memory::heap * * @return Smallest allocatable block of heap memory. */ - [[gnu::section(".user_text")]] auto constexpr min_allocatable_size() -> std::size_t { return sizeof(memory_block); } + [[gnu::section(".user_text")]] constexpr auto 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. diff --git a/arch/x86_64/pre/include/arch/memory/multiboot/reader.hpp b/arch/x86_64/pre/include/arch/memory/multiboot/reader.hpp index c5464cb..ba80918 100644 --- a/arch/x86_64/pre/include/arch/memory/multiboot/reader.hpp +++ b/arch/x86_64/pre/include/arch/memory/multiboot/reader.hpp @@ -5,9 +5,9 @@ // #include "arch/memory/multiboot/memory_map.hpp" #include "arch/memory/multiboot/elf_symbols_section.hpp" +#include #include -#include #include namespace teachos::arch::memory::multiboot diff --git a/arch/x86_64/pre/include/arch/memory/paging/active_page_table.hpp b/arch/x86_64/pre/include/arch/memory/paging/active_page_table.hpp index f68d8b6..abefd61 100644 --- a/arch/x86_64/pre/include/arch/memory/paging/active_page_table.hpp +++ b/arch/x86_64/pre/include/arch/memory/paging/active_page_table.hpp @@ -25,7 +25,7 @@ namespace teachos::arch::memory::paging * * @return Active single unique instance of the level 4 page table. */ - static auto create_or_get() -> active_page_table &; + auto static create_or_get() -> active_page_table &; /** * @brief Index operator overload to access specific mutable entry directy of the level 4 page table. @@ -186,7 +186,7 @@ namespace teachos::arch::memory::paging * @param handle Page Table handle we want to access the entry that should be cleared on. */ template - static auto unmap_page_table_entry(T & allocator, virtual_page page, page_table_handle & handle) -> void + auto static unmap_page_table_entry(T & allocator, virtual_page page, page_table_handle & handle) -> void { auto level_index = page.get_level_index(handle.get_level()); auto & entry = handle[level_index]; diff --git a/arch/x86_64/pre/include/arch/memory/paging/kernel_mapper.hpp b/arch/x86_64/pre/include/arch/memory/paging/kernel_mapper.hpp index 3afb54b..581f142 100644 --- a/arch/x86_64/pre/include/arch/memory/paging/kernel_mapper.hpp +++ b/arch/x86_64/pre/include/arch/memory/paging/kernel_mapper.hpp @@ -47,7 +47,7 @@ namespace teachos::arch::memory::paging 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}; + temporary_page temporary_page{virtual_page{0xCAFE'BABE}, allocator}; decltype(auto) active_table = active_page_table::create_or_get(); auto const frame = allocator.allocate_frame(); exception_handling::assert(frame.has_value(), @@ -124,14 +124,14 @@ 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 constexpr USER_SECTION_BASES = { - 0x102000, // .boot_bss (Contains statically allocated variables) - 0x209000, // .stl_text (Contains code for custom std implementations and standard library code) - 0x217000, // .user_text (Contains the actual user code executed) - 0x21E000, // .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) + constexpr std::array USER_SECTION_BASES = { + 0x102000, // .boot_bss (Contains statically allocated variables) + 0x209000, // .stl_text (Contains code for custom std implementations and standard library code) + 0x217000, // .user_text (Contains the actual user code executed) + 0x21E000, // .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) diff --git a/arch/x86_64/pre/include/arch/memory/paging/page_table.hpp b/arch/x86_64/pre/include/arch/memory/paging/page_table.hpp index b972337..247086c 100644 --- a/arch/x86_64/pre/include/arch/memory/paging/page_table.hpp +++ b/arch/x86_64/pre/include/arch/memory/paging/page_table.hpp @@ -7,7 +7,7 @@ namespace teachos::arch::memory::paging { - std::size_t constexpr PAGE_TABLE_ENTRY_COUNT = 512U; ///< Default entry count of a page table in x86_84 is 512. + constexpr std::size_t PAGE_TABLE_ENTRY_COUNT = 512U; ///< Default entry count of a page table in x86_84 is 512. /** * @brief Forward delcaration of the page_table, because it should only be accessible over the handle. diff --git a/arch/x86_64/pre/include/arch/memory/paging/virtual_page.hpp b/arch/x86_64/pre/include/arch/memory/paging/virtual_page.hpp index a6c8c39..1a20eae 100644 --- a/arch/x86_64/pre/include/arch/memory/paging/virtual_page.hpp +++ b/arch/x86_64/pre/include/arch/memory/paging/virtual_page.hpp @@ -73,12 +73,12 @@ namespace teachos::arch::memory::paging /** * @brief Defaulted equals operator. */ - auto operator==(const virtual_page & other) const -> bool = default; + auto operator==(virtual_page const & other) const -> bool = default; /** * @brief Defaulted three-way comparsion operator. */ - auto operator<=>(const virtual_page & other) const -> std::partial_ordering = default; + auto operator<=>(virtual_page const & other) const -> std::partial_ordering = default; std::size_t page_number = {}; ///< Index number of the current virtual page, used to distinguish it from other pages. diff --git a/arch/x86_64/pre/src/context_switching/interrupt_descriptor_table/idt_flags.cpp b/arch/x86_64/pre/src/context_switching/interrupt_descriptor_table/idt_flags.cpp index d36a4c1..f3b9d5e 100644 --- a/arch/x86_64/pre/src/context_switching/interrupt_descriptor_table/idt_flags.cpp +++ b/arch/x86_64/pre/src/context_switching/interrupt_descriptor_table/idt_flags.cpp @@ -13,5 +13,8 @@ namespace teachos::arch::context_switching::interrupt_descriptor_table return (std::bitset<8U>{_flags} & other) == other; } - auto idt_flags::operator|=(std::bitset<8U> other) -> void { _flags |= other.to_ulong(); } + 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/pre/src/context_switching/interrupt_descriptor_table/interrupt_descriptor_table.cpp b/arch/x86_64/pre/src/context_switching/interrupt_descriptor_table/interrupt_descriptor_table.cpp index 7aa0859..8640385 100644 --- a/arch/x86_64/pre/src/context_switching/interrupt_descriptor_table/interrupt_descriptor_table.cpp +++ b/arch/x86_64/pre/src/context_switching/interrupt_descriptor_table/interrupt_descriptor_table.cpp @@ -33,7 +33,7 @@ namespace teachos::arch::context_switching::interrupt_descriptor_table 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(); + interrupt_descriptor_table static idt = create_interrupt_descriptor_table(); return idt; } diff --git a/arch/x86_64/pre/src/context_switching/interrupt_descriptor_table/segment_selector.cpp b/arch/x86_64/pre/src/context_switching/interrupt_descriptor_table/segment_selector.cpp index 27f0a3b..25ba859 100644 --- a/arch/x86_64/pre/src/context_switching/interrupt_descriptor_table/segment_selector.cpp +++ b/arch/x86_64/pre/src/context_switching/interrupt_descriptor_table/segment_selector.cpp @@ -7,9 +7,18 @@ namespace teachos::arch::context_switching::interrupt_descriptor_table return (std::bitset<3U>{_flags} & other) == other; } - auto segment_selector::get_index() const -> uint16_t { return _index; } + auto segment_selector::get_index() const -> uint16_t + { + return _index; + } - auto segment_selector::operator|=(std::bitset<3U> other) -> void { _flags |= other.to_ulong(); } + auto segment_selector::operator|=(std::bitset<3U> other) -> void + { + _flags |= other.to_ulong(); + } - segment_selector::operator uint16_t() const { return *reinterpret_cast(this); } + segment_selector::operator uint16_t() const + { + return *reinterpret_cast(this); + } } // namespace teachos::arch::context_switching::interrupt_descriptor_table diff --git a/arch/x86_64/pre/src/context_switching/main.cpp b/arch/x86_64/pre/src/context_switching/main.cpp index 9539428..3eb6dae 100644 --- a/arch/x86_64/pre/src/context_switching/main.cpp +++ b/arch/x86_64/pre/src/context_switching/main.cpp @@ -13,20 +13,23 @@ 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}; + 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}; + 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}; + 4U, context_switching::interrupt_descriptor_table::segment_selector::REQUEST_LEVEL_USER}; - auto reload_gdtr() -> void { kernel::cpu::call(KERNEL_CODE_POINTER); } + auto reload_gdtr() -> void + { + kernel::cpu::call(KERNEL_CODE_POINTER); + } } // namespace auto initialize_descriptor_tables() -> descriptor_tables { - static bool initalized = false; + bool static initalized = false; if (!initalized) { diff --git a/arch/x86_64/pre/src/context_switching/segment_descriptor_table/access_byte.cpp b/arch/x86_64/pre/src/context_switching/segment_descriptor_table/access_byte.cpp index e31e021..fcc72cf 100644 --- a/arch/x86_64/pre/src/context_switching/segment_descriptor_table/access_byte.cpp +++ b/arch/x86_64/pre/src/context_switching/segment_descriptor_table/access_byte.cpp @@ -13,5 +13,8 @@ namespace teachos::arch::context_switching::segment_descriptor_table return (std::bitset<8U>{_flags} & other) == other; } - auto access_byte::operator|=(std::bitset<8U> other) -> void { _flags |= other.to_ulong(); } + 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/pre/src/context_switching/segment_descriptor_table/gdt_flags.cpp b/arch/x86_64/pre/src/context_switching/segment_descriptor_table/gdt_flags.cpp index e444a24..ad1366a 100644 --- a/arch/x86_64/pre/src/context_switching/segment_descriptor_table/gdt_flags.cpp +++ b/arch/x86_64/pre/src/context_switching/segment_descriptor_table/gdt_flags.cpp @@ -14,7 +14,13 @@ namespace teachos::arch::context_switching::segment_descriptor_table 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::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(); } + 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/pre/src/context_switching/segment_descriptor_table/global_descriptor_table.cpp b/arch/x86_64/pre/src/context_switching/segment_descriptor_table/global_descriptor_table.cpp index bbcee31..1c4729f 100644 --- a/arch/x86_64/pre/src/context_switching/segment_descriptor_table/global_descriptor_table.cpp +++ b/arch/x86_64/pre/src/context_switching/segment_descriptor_table/global_descriptor_table.cpp @@ -12,8 +12,8 @@ namespace teachos::arch::context_switching::segment_descriptor_table auto create_segment_descriptor(segment_descriptor_type segment_descriptor_type, access_byte access_level) -> segment_descriptor_base { - uint64_t constexpr BASE = 0x0; - std::bitset<20U> constexpr LIMIT{0xFFFFF}; + constexpr uint64_t BASE = 0x0; + constexpr std::bitset<20U> LIMIT{0xFFFFF}; gdt_flags flags{gdt_flags::GRANULARITY, LIMIT}; access_level |= access_byte::PRESENT | access_byte::CODE_OR_DATA_SEGMENT; @@ -33,7 +33,7 @@ namespace teachos::arch::context_switching::segment_descriptor_table auto create_tss_descriptor(task_state_segment * tss) -> segment_descriptor_extension { - uint64_t constexpr TSS_LIMIT = sizeof(task_state_segment) - 1; + 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}; @@ -55,7 +55,7 @@ namespace teachos::arch::context_switching::segment_descriptor_table 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(); + auto static tss = new task_state_segment(); segment_descriptor_extension const tss_descriptor = create_tss_descriptor(tss); global_descriptor_table global_descriptor_table{null_segment, @@ -72,7 +72,7 @@ namespace teachos::arch::context_switching::segment_descriptor_table auto get_or_create_gdt() -> global_descriptor_table & { // Global Descriptor Table needs to be kept alive - static global_descriptor_table gdt = create_gdt(); + global_descriptor_table static gdt = create_gdt(); return gdt; } diff --git a/arch/x86_64/pre/src/context_switching/segment_descriptor_table/segment_descriptor_base.cpp b/arch/x86_64/pre/src/context_switching/segment_descriptor_table/segment_descriptor_base.cpp index 04804d9..c3a03fc 100644 --- a/arch/x86_64/pre/src/context_switching/segment_descriptor_table/segment_descriptor_base.cpp +++ b/arch/x86_64/pre/src/context_switching/segment_descriptor_table/segment_descriptor_base.cpp @@ -33,6 +33,9 @@ namespace teachos::arch::context_switching::segment_descriptor_table : segment_descriptor_type::DATA_SEGMENT; } - segment_descriptor_base::operator uint64_t() const { return *reinterpret_cast(this); } + segment_descriptor_base::operator uint64_t() const + { + return *reinterpret_cast(this); + } } // namespace teachos::arch::context_switching::segment_descriptor_table diff --git a/arch/x86_64/pre/src/context_switching/segment_descriptor_table/segment_descriptor_extension.cpp b/arch/x86_64/pre/src/context_switching/segment_descriptor_table/segment_descriptor_extension.cpp index a28ec9b..5ea0d8a 100644 --- a/arch/x86_64/pre/src/context_switching/segment_descriptor_table/segment_descriptor_extension.cpp +++ b/arch/x86_64/pre/src/context_switching/segment_descriptor_table/segment_descriptor_extension.cpp @@ -17,8 +17,14 @@ namespace teachos::arch::context_switching::segment_descriptor_table // Nothing to do } - auto segment_descriptor_extension::get_first_gdt_entry() const -> segment_descriptor_base { return _base; } + 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; } + 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/pre/src/context_switching/syscall/syscall_enable.cpp b/arch/x86_64/pre/src/context_switching/syscall/syscall_enable.cpp index 3c43336..dbb3ed9 100644 --- a/arch/x86_64/pre/src/context_switching/syscall/syscall_enable.cpp +++ b/arch/x86_64/pre/src/context_switching/syscall/syscall_enable.cpp @@ -8,12 +8,12 @@ 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}; + constexpr interrupt_descriptor_table::segment_selector 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; + constexpr auto IA32_STAR_ADDRESS = 0xC000'0081; + constexpr auto IA32_LSTAR_ADDRESS = 0xC000'0082; + constexpr auto IA32_FMASK_ADDRESS = 0xC000'0084; } // namespace diff --git a/arch/x86_64/pre/src/context_switching/syscall/syscall_handler.cpp b/arch/x86_64/pre/src/context_switching/syscall/syscall_handler.cpp index 84dbe5f..c120f77 100644 --- a/arch/x86_64/pre/src/context_switching/syscall/syscall_handler.cpp +++ b/arch/x86_64/pre/src/context_switching/syscall/syscall_handler.cpp @@ -14,7 +14,7 @@ namespace teachos::arch::context_switching::syscall { auto write_to_vga_buffer(uint64_t buffer) -> response { - video::vga::text::write(reinterpret_cast(buffer), + video::vga::text::write(reinterpret_cast(buffer), video::vga::text::common_attributes::green_on_black); video::vga::text::newline(); return {error::OK}; @@ -22,11 +22,14 @@ namespace teachos::arch::context_switching::syscall auto expand_user_heap() -> response { - static auto current_heap_end = memory::heap::USER_HEAP_START; + auto static 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}}; + return { + error::OK, + {heap_start, memory::heap::USER_HEAP_SIZE} + }; } } // namespace @@ -62,7 +65,7 @@ namespace teachos::arch::context_switching::syscall result = expand_user_heap(); break; case type::ASSERT: - teachos::arch::exception_handling::assert(arg_0, reinterpret_cast(arg_1)); + teachos::arch::exception_handling::assert(arg_0, reinterpret_cast(arg_1)); break; default: teachos::arch::exception_handling::panic("[Syscall Handler] Invalid syscall number"); diff --git a/arch/x86_64/pre/src/exception_handling/abort.cpp b/arch/x86_64/pre/src/exception_handling/abort.cpp index e12e4cb..5dc6869 100644 --- a/arch/x86_64/pre/src/exception_handling/abort.cpp +++ b/arch/x86_64/pre/src/exception_handling/abort.cpp @@ -11,5 +11,8 @@ namespace teachos::arch::exception_handling * a matching implementation. Since the default implemenatation calls a number of functions the kernel does not * currently implement, @p ::abort gets overridden to simply panic. */ - extern "C" auto abort() -> void { panic("Terminate was called, possibly due to an unhandled exception"); } + extern "C" auto abort() -> void + { + panic("Terminate was called, possibly due to an unhandled exception"); + } } // namespace teachos::arch::exception_handling diff --git a/arch/x86_64/pre/src/exception_handling/panic.cpp b/arch/x86_64/pre/src/exception_handling/panic.cpp index 8e3802a..9511a9a 100644 --- a/arch/x86_64/pre/src/exception_handling/panic.cpp +++ b/arch/x86_64/pre/src/exception_handling/panic.cpp @@ -7,7 +7,10 @@ namespace teachos::arch::exception_handling { extern "C" char const message_prefix_panic[]; - auto panic(char const * reason) -> void { panic(message_prefix_panic, reason); } + auto panic(char const * reason) -> void + { + panic(message_prefix_panic, reason); + } auto panic(char const * prefix, char const * reason) -> void { diff --git a/arch/x86_64/pre/src/kernel/cpu/if.cpp b/arch/x86_64/pre/src/kernel/cpu/if.cpp index 60a90a3..5d056fc 100644 --- a/arch/x86_64/pre/src/kernel/cpu/if.cpp +++ b/arch/x86_64/pre/src/kernel/cpu/if.cpp @@ -1,7 +1,13 @@ namespace teachos::arch::kernel::cpu { - auto set_interrupt_flag() -> void { asm volatile("sti"); } + auto set_interrupt_flag() -> void + { + asm volatile("sti"); + } - auto clear_interrupt_flag() -> void { asm volatile("cli"); } + auto clear_interrupt_flag() -> void + { + asm volatile("cli"); + } } // namespace teachos::arch::kernel::cpu diff --git a/arch/x86_64/pre/src/kernel/cpu/msr.cpp b/arch/x86_64/pre/src/kernel/cpu/msr.cpp index 9c474a1..9d6a318 100644 --- a/arch/x86_64/pre/src/kernel/cpu/msr.cpp +++ b/arch/x86_64/pre/src/kernel/cpu/msr.cpp @@ -4,7 +4,7 @@ namespace teachos::arch::kernel::cpu { namespace { - auto constexpr IA32_EFER_ADDRESS = 0xC0000080; + constexpr auto IA32_EFER_ADDRESS = 0xC000'0080; } auto read_msr(uint32_t msr) -> uint64_t diff --git a/arch/x86_64/pre/src/memory/allocator/area_frame_allocator.cpp b/arch/x86_64/pre/src/memory/allocator/area_frame_allocator.cpp index a5a1b49..3105023 100644 --- a/arch/x86_64/pre/src/memory/allocator/area_frame_allocator.cpp +++ b/arch/x86_64/pre/src/memory/allocator/area_frame_allocator.cpp @@ -81,5 +81,8 @@ namespace teachos::arch::memory::allocator return allocate_frame(); } - auto area_frame_allocator::deallocate_frame(physical_frame const & physical_frame) -> void { (void)physical_frame; } + auto area_frame_allocator::deallocate_frame(physical_frame const & physical_frame) -> void + { + (void)physical_frame; + } } // namespace teachos::arch::memory::allocator diff --git a/arch/x86_64/pre/src/memory/heap/global_heap_allocator.cpp b/arch/x86_64/pre/src/memory/heap/global_heap_allocator.cpp index 35cd623..709cda1 100644 --- a/arch/x86_64/pre/src/memory/heap/global_heap_allocator.cpp +++ b/arch/x86_64/pre/src/memory/heap/global_heap_allocator.cpp @@ -27,13 +27,25 @@ namespace teachos::arch::memory::heap 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::kmalloc(std::size_t size) -> void * + { + return kernel().allocate(size); + } - auto global_heap_allocator::kfree(void * pointer) noexcept -> void { kernel().deallocate(pointer); } + auto global_heap_allocator::kfree(void * pointer) noexcept -> void + { + kernel().deallocate(pointer); + } - auto global_heap_allocator::malloc(std::size_t size) -> void * { return user().allocate(size); } + 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::free(void * pointer) noexcept -> void + { + user().deallocate(pointer); + } auto global_heap_allocator::register_heap_allocator(heap_allocator_type new_type) -> void { @@ -45,20 +57,21 @@ namespace teachos::arch::memory::heap case heap_allocator_type::NONE: // Nothing to do break; - case heap_allocator_type::BUMP: { - static bump_allocator kernel_allocator{KERNEL_HEAP_START, KERNEL_HEAP_START + KERNEL_HEAP_SIZE}; + case heap_allocator_type::BUMP: + { + bump_allocator static 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 kernel_allocator{KERNEL_HEAP_START, KERNEL_HEAP_START + KERNEL_HEAP_SIZE}; + case heap_allocator_type::LINKED_LIST: + { + linked_list_allocator static 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{}; + [[gnu::section(".user_data")]] user_heap_allocator static user_allocator{}; user_allocator_instance = &user_allocator; } diff --git a/arch/x86_64/pre/src/memory/heap/memory_block.cpp b/arch/x86_64/pre/src/memory/heap/memory_block.cpp index bc97bd6..4c07454 100644 --- a/arch/x86_64/pre/src/memory/heap/memory_block.cpp +++ b/arch/x86_64/pre/src/memory/heap/memory_block.cpp @@ -11,5 +11,8 @@ namespace teachos::arch::memory::heap this->next = next; } - memory_block::~memory_block() { memset(static_cast(this), 0U, sizeof(memory_block)); } + memory_block::~memory_block() + { + memset(static_cast(this), 0U, sizeof(memory_block)); + } } // namespace teachos::arch::memory::heap diff --git a/arch/x86_64/pre/src/memory/heap/user_heap_allocator.cpp b/arch/x86_64/pre/src/memory/heap/user_heap_allocator.cpp index 427a68a..96de005 100644 --- a/arch/x86_64/pre/src/memory/heap/user_heap_allocator.cpp +++ b/arch/x86_64/pre/src/memory/heap/user_heap_allocator.cpp @@ -39,7 +39,7 @@ namespace teachos::arch::memory::heap } } - char constexpr OUT_OF_MEMORY_ERROR_MESSAGE[] = "[Linked List Allocator] Out of memory"; + constexpr char OUT_OF_MEMORY_ERROR_MESSAGE[] = "[Linked List Allocator] Out of memory"; context_switching::syscall::syscall(context_switching::syscall::type::ASSERT, {false, reinterpret_cast(&OUT_OF_MEMORY_ERROR_MESSAGE)}); return nullptr; @@ -178,7 +178,7 @@ namespace teachos::arch::memory::heap // 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"; + constexpr char DOUBLE_FREE_ERROR_MESSAGE[] = "[Linked List Allocator] Attempted double free detected"; context_switching::syscall::syscall( context_switching::syscall::type::ASSERT, {previous_block == nullptr || diff --git a/arch/x86_64/pre/src/memory/main.cpp b/arch/x86_64/pre/src/memory/main.cpp index 2746a71..b5980db 100644 --- a/arch/x86_64/pre/src/memory/main.cpp +++ b/arch/x86_64/pre/src/memory/main.cpp @@ -15,7 +15,7 @@ namespace teachos::arch::memory { namespace { - static std::optional frame_allocator; + std::optional static frame_allocator; auto create_frame_allocator(multiboot::memory_information const & memory_information) -> allocator::area_frame_allocator & @@ -54,7 +54,7 @@ namespace teachos::arch::memory auto initialize_memory_management() -> void { - static bool has_been_called = false; + bool static has_been_called = false; arch::exception_handling::assert(!has_been_called, "[Initialization] Memory management has already been initialized"); has_been_called = true; diff --git a/arch/x86_64/pre/src/memory/multiboot/elf_symbols_section.cpp b/arch/x86_64/pre/src/memory/multiboot/elf_symbols_section.cpp index f5d126b..3105120 100644 --- a/arch/x86_64/pre/src/memory/multiboot/elf_symbols_section.cpp +++ b/arch/x86_64/pre/src/memory/multiboot/elf_symbols_section.cpp @@ -2,7 +2,10 @@ namespace teachos::arch::memory::multiboot { - auto elf_section_flags::contains_flags(std::bitset<64U> other) const -> bool { return (flags & other) == other; } + auto elf_section_flags::contains_flags(std::bitset<64U> other) const -> bool + { + return (flags & other) == other; + } auto elf_section_header::is_null() const -> bool { diff --git a/arch/x86_64/pre/src/memory/paging/active_page_table.cpp b/arch/x86_64/pre/src/memory/paging/active_page_table.cpp index 0113869..930588d 100644 --- a/arch/x86_64/pre/src/memory/paging/active_page_table.cpp +++ b/arch/x86_64/pre/src/memory/paging/active_page_table.cpp @@ -4,18 +4,21 @@ namespace teachos::arch::memory::paging { namespace { - paging::virtual_address constexpr PAGE_TABLE_LEVEL_4_ADDRESS = 0xffffffff'fffff000; + constexpr paging::virtual_address PAGE_TABLE_LEVEL_4_ADDRESS = 0xffff'ffff'ffff'f000; } auto active_page_table::create_or_get() -> active_page_table & { - static page_table_handle active_handle{reinterpret_cast(PAGE_TABLE_LEVEL_4_ADDRESS), + page_table_handle static active_handle{reinterpret_cast(PAGE_TABLE_LEVEL_4_ADDRESS), page_table_handle::LEVEL4}; - static active_page_table active_page{active_handle}; + active_page_table static active_page{active_handle}; return active_page; } - auto active_page_table::operator[](std::size_t index) -> entry & { return active_handle[index]; } + auto active_page_table::operator[](std::size_t index) -> entry & + { + return active_handle[index]; + } auto active_page_table::translate_address(virtual_address address) -> std::optional { diff --git a/arch/x86_64/pre/src/memory/paging/page_entry.cpp b/arch/x86_64/pre/src/memory/paging/page_entry.cpp index 57045ca..ec45068 100644 --- a/arch/x86_64/pre/src/memory/paging/page_entry.cpp +++ b/arch/x86_64/pre/src/memory/paging/page_entry.cpp @@ -6,7 +6,7 @@ namespace teachos::arch::memory::paging { namespace { - std::size_t constexpr PHYSICAL_ADDRESS_MASK = 0x000fffff'fffff000; + constexpr std::size_t PHYSICAL_ADDRESS_MASK = 0x000f'ffff'ffff'f000; } // namespace entry::entry(uint64_t flags) @@ -33,11 +33,20 @@ namespace teachos::arch::memory::paging } } - auto entry::is_unused() const -> bool { return flags == 0U; } + auto entry::is_unused() const -> bool + { + return flags == 0U; + } - auto entry::set_unused() -> void { flags = 0U; } + auto entry::set_unused() -> void + { + flags = 0U; + } - auto entry::set_user_accessible() -> void { flags |= entry::USER_ACCESSIBLE; } + auto entry::set_user_accessible() -> void + { + flags |= entry::USER_ACCESSIBLE; + } auto entry::calculate_pointed_to_frame() const -> std::optional { @@ -49,7 +58,10 @@ namespace teachos::arch::memory::paging return std::nullopt; } - auto entry::contains_flags(std::bitset<64U> other) const -> bool { return (flags & other) == other; } + auto entry::contains_flags(std::bitset<64U> other) const -> bool + { + return (flags & other) == other; + } auto entry::set_entry(allocator::physical_frame frame, std::bitset<64U> additional_flags) -> void { @@ -59,5 +71,8 @@ namespace teachos::arch::memory::paging flags = frame.start_address() | additional_flags.to_ulong(); } - auto entry::get_flags() const -> std::bitset<64U> { return flags.to_ulong() & ~PHYSICAL_ADDRESS_MASK; } + auto entry::get_flags() const -> std::bitset<64U> + { + return flags.to_ulong() & ~PHYSICAL_ADDRESS_MASK; + } } // namespace teachos::arch::memory::paging diff --git a/arch/x86_64/pre/src/memory/paging/page_table.cpp b/arch/x86_64/pre/src/memory/paging/page_table.cpp index eb11810..e79c3e5 100644 --- a/arch/x86_64/pre/src/memory/paging/page_table.cpp +++ b/arch/x86_64/pre/src/memory/paging/page_table.cpp @@ -96,9 +96,15 @@ namespace teachos::arch::memory::paging "[Page Table] Attempted to pass nullptr as table to page table table method"); } - auto page_table_handle::zero_entries() -> void { table->zero_entries(); } + auto page_table_handle::zero_entries() -> void + { + table->zero_entries(); + } - auto page_table_handle::is_empty() const -> bool { return table->is_empty(); } + auto page_table_handle::is_empty() const -> bool + { + return table->is_empty(); + } auto page_table_handle::next_table(std::size_t table_index) const -> std::optional { @@ -113,9 +119,15 @@ namespace teachos::arch::memory::paging return std::nullopt; } - auto page_table_handle::get_level() const -> page_table_handle::level { return table_level; } + auto page_table_handle::get_level() const -> page_table_handle::level + { + return table_level; + } - auto page_table_handle::operator[](std::size_t index) -> entry & { return table->operator[](index); } + auto page_table_handle::operator[](std::size_t index) -> entry & + { + return table->operator[](index); + } auto operator--(page_table_handle::level & value) -> page_table_handle::level & { diff --git a/arch/x86_64/pre/src/memory/paging/virtual_page.cpp b/arch/x86_64/pre/src/memory/paging/virtual_page.cpp index d374156..8d34918 100644 --- a/arch/x86_64/pre/src/memory/paging/virtual_page.cpp +++ b/arch/x86_64/pre/src/memory/paging/virtual_page.cpp @@ -6,12 +6,15 @@ namespace teachos::arch::memory::paging { auto virtual_page::containing_address(virtual_address address) -> virtual_page { - exception_handling::assert(address < 0x00008000'00000000 || address >= 0xffff8000'00000000, + exception_handling::assert(address < 0x0000'8000'0000'0000 || address >= 0xffff'8000'0000'0000, "[Virtual Page] Attempted to create virtual page from invalid address"); return virtual_page{address / allocator::PAGE_FRAME_SIZE}; } - auto virtual_page::start_address() const -> virtual_address { return page_number * allocator::PAGE_FRAME_SIZE; } + auto virtual_page::start_address() const -> virtual_address + { + return page_number * allocator::PAGE_FRAME_SIZE; + } auto virtual_page::get_level_index(page_table_handle::level level) const -> size_t { -- cgit v1.2.3 From c5ff471e253b27c0d58a78a07322f63522b3e487 Mon Sep 17 00:00:00 2001 From: Felix Morgner Date: Thu, 30 Oct 2025 16:01:26 +0100 Subject: chore: fix header grouping --- arch/x86_64/pre/include/arch/memory/heap/linked_list_allocator.hpp | 1 + arch/x86_64/pre/include/arch/memory/multiboot/reader.hpp | 1 + 2 files changed, 2 insertions(+) (limited to 'arch/x86_64/pre') diff --git a/arch/x86_64/pre/include/arch/memory/heap/linked_list_allocator.hpp b/arch/x86_64/pre/include/arch/memory/heap/linked_list_allocator.hpp index 540ff5c..bb8b526 100644 --- a/arch/x86_64/pre/include/arch/memory/heap/linked_list_allocator.hpp +++ b/arch/x86_64/pre/include/arch/memory/heap/linked_list_allocator.hpp @@ -3,6 +3,7 @@ #include "arch/memory/heap/heap_allocator.hpp" #include "arch/memory/heap/memory_block.hpp" + #include namespace teachos::arch::memory::heap diff --git a/arch/x86_64/pre/include/arch/memory/multiboot/reader.hpp b/arch/x86_64/pre/include/arch/memory/multiboot/reader.hpp index ba80918..275e5e2 100644 --- a/arch/x86_64/pre/include/arch/memory/multiboot/reader.hpp +++ b/arch/x86_64/pre/include/arch/memory/multiboot/reader.hpp @@ -5,6 +5,7 @@ // #include "arch/memory/multiboot/memory_map.hpp" #include "arch/memory/multiboot/elf_symbols_section.hpp" + #include #include -- cgit v1.2.3 From 682f28543be1ef7a315284c932075be1976e2930 Mon Sep 17 00:00:00 2001 From: Felix Morgner Date: Fri, 12 Dec 2025 14:11:07 +0100 Subject: x86_64/pre: remove ported implementation --- .../arch/memory/allocator/area_frame_allocator.hpp | 67 ------- .../pre/include/arch/memory/allocator/concept.hpp | 21 --- .../arch/memory/allocator/tiny_frame_allocator.hpp | 74 -------- arch/x86_64/pre/include/arch/memory/main.hpp | 30 --- .../arch/memory/multiboot/elf_symbols_section.hpp | 170 ----------------- .../pre/include/arch/memory/multiboot/reader.hpp | 59 ------ .../arch/memory/paging/active_page_table.hpp | 206 --------------------- .../arch/memory/paging/inactive_page_table.hpp | 39 ---- .../include/arch/memory/paging/kernel_mapper.hpp | 180 ------------------ .../pre/include/arch/memory/paging/page_entry.hpp | 121 ------------ .../pre/include/arch/memory/paging/page_table.hpp | 157 ---------------- .../include/arch/memory/paging/temporary_page.hpp | 64 ------- .../include/arch/memory/paging/virtual_page.hpp | 91 --------- .../src/memory/allocator/area_frame_allocator.cpp | 88 --------- .../src/memory/allocator/tiny_frame_allocator.cpp | 34 ---- arch/x86_64/pre/src/memory/main.cpp | 77 -------- .../src/memory/multiboot/elf_symbols_section.cpp | 16 -- arch/x86_64/pre/src/memory/multiboot/reader.cpp | 135 -------------- .../pre/src/memory/paging/active_page_table.cpp | 101 ---------- .../pre/src/memory/paging/inactive_page_table.cpp | 20 -- arch/x86_64/pre/src/memory/paging/page_entry.cpp | 78 -------- arch/x86_64/pre/src/memory/paging/page_table.cpp | 140 -------------- .../pre/src/memory/paging/temporary_page.cpp | 29 --- arch/x86_64/pre/src/memory/paging/virtual_page.cpp | 36 ---- 24 files changed, 2033 deletions(-) delete mode 100644 arch/x86_64/pre/include/arch/memory/allocator/area_frame_allocator.hpp delete mode 100644 arch/x86_64/pre/include/arch/memory/allocator/concept.hpp delete mode 100644 arch/x86_64/pre/include/arch/memory/allocator/tiny_frame_allocator.hpp delete mode 100644 arch/x86_64/pre/include/arch/memory/main.hpp delete mode 100644 arch/x86_64/pre/include/arch/memory/multiboot/elf_symbols_section.hpp delete mode 100644 arch/x86_64/pre/include/arch/memory/multiboot/reader.hpp delete mode 100644 arch/x86_64/pre/include/arch/memory/paging/active_page_table.hpp delete mode 100644 arch/x86_64/pre/include/arch/memory/paging/inactive_page_table.hpp delete mode 100644 arch/x86_64/pre/include/arch/memory/paging/kernel_mapper.hpp delete mode 100644 arch/x86_64/pre/include/arch/memory/paging/page_entry.hpp delete mode 100644 arch/x86_64/pre/include/arch/memory/paging/page_table.hpp delete mode 100644 arch/x86_64/pre/include/arch/memory/paging/temporary_page.hpp delete mode 100644 arch/x86_64/pre/include/arch/memory/paging/virtual_page.hpp delete mode 100644 arch/x86_64/pre/src/memory/allocator/area_frame_allocator.cpp delete mode 100644 arch/x86_64/pre/src/memory/allocator/tiny_frame_allocator.cpp delete mode 100644 arch/x86_64/pre/src/memory/main.cpp delete mode 100644 arch/x86_64/pre/src/memory/multiboot/elf_symbols_section.cpp delete mode 100644 arch/x86_64/pre/src/memory/multiboot/reader.cpp delete mode 100644 arch/x86_64/pre/src/memory/paging/active_page_table.cpp delete mode 100644 arch/x86_64/pre/src/memory/paging/inactive_page_table.cpp delete mode 100644 arch/x86_64/pre/src/memory/paging/page_entry.cpp delete mode 100644 arch/x86_64/pre/src/memory/paging/page_table.cpp delete mode 100644 arch/x86_64/pre/src/memory/paging/temporary_page.cpp delete mode 100644 arch/x86_64/pre/src/memory/paging/virtual_page.cpp (limited to 'arch/x86_64/pre') diff --git a/arch/x86_64/pre/include/arch/memory/allocator/area_frame_allocator.hpp b/arch/x86_64/pre/include/arch/memory/allocator/area_frame_allocator.hpp deleted file mode 100644 index a86c9b7..0000000 --- a/arch/x86_64/pre/include/arch/memory/allocator/area_frame_allocator.hpp +++ /dev/null @@ -1,67 +0,0 @@ -#ifndef TEACHOS_X86_64_MEMORY_AREA_ALLOCATOR_HPP -#define TEACHOS_X86_64_MEMORY_AREA_ALLOCATOR_HPP - -// #include "arch/memory/allocator/physical_frame.hpp" -// #include "arch/memory/multiboot/reader.hpp" - -#include - -namespace x86_64::memory -{ - /** - * @brief Allocates memory linearly using memory areas read from the multiboot2 information pointer and leaks any - * deallocated frames. - */ - struct area_frame_allocator - { - /** - * @brief Constructor. - * - * @param mem_info Structure containg all relevant information to map and allocate memory. - */ - area_frame_allocator(multiboot::memory_information const & mem_info); - - /** - * @brief Allocate memory by finding and returning a free physical frame. - * - * @note The physical_frame allocation executes multiple checks before returning - * the physical_frame that is available to allocate. It must at least - * do the following: - * - check if the next_free_frame is within the current_area - * - check if the next_free_frame is actually free - * - update the next_free_frame after finding a free physical_frame - * - * @return next free physical frame or nullopt if none was found. - */ - auto allocate_frame() -> std::optional; - - /** - * @brief Deallocates a previously allocated physical frame. - * - * @note Simply does nothing, because the simply area frame - * allocator implementation does not keep track of free or used frames and can therefore not deallocate, because it - * does not know which frames have been alocated in the first place. - * - * @param physical_frame Previously allocated physical_frame that should be deallocated. - */ - auto deallocate_frame(physical_frame const & physical_frame) -> void; - - private: - /** - * @brief Find the next memory area and write it into current_area. - */ - auto choose_next_area() -> void; - - physical_frame next_free_frame; ///< The physical_frame after the last allocated one. - std::optional current_area; ///< The current memory area. - multiboot::memory_area_container const - 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. - physical_frame const multiboot_end; ///< The end address of the multiboot code in memory. - }; - -} // namespace x86_64::memory - -#endif // TEACHOS_ARCH_X86_64_MEMORY_ALLOCATOR_AREA_FRAME_ALLOCATOR_HPP diff --git a/arch/x86_64/pre/include/arch/memory/allocator/concept.hpp b/arch/x86_64/pre/include/arch/memory/allocator/concept.hpp deleted file mode 100644 index 2d3f4ae..0000000 --- a/arch/x86_64/pre/include/arch/memory/allocator/concept.hpp +++ /dev/null @@ -1,21 +0,0 @@ -#ifndef TEACHOS_ARCH_X86_64_MEMORY_ALLOCATOR_CONCEPT_HPP -#define TEACHOS_ARCH_X86_64_MEMORY_ALLOCATOR_CONCEPT_HPP - -#include "arch/memory/allocator/physical_frame.hpp" - -#include - -namespace teachos::arch::memory::allocator -{ - /** - * @brief Frame allocator concept required for allocating and deallocating physical frames in memory. - */ - template - concept FrameAllocator = requires(T t, physical_frame const & a) { - { t.allocate_frame() } -> std::same_as>; - { t.deallocate_frame(a) } -> std::same_as; - }; - -} // namespace teachos::arch::memory::allocator - -#endif // TEACHOS_ARCH_X86_64_MEMORY_ALLOCATOR_CONCEPT_HPP diff --git a/arch/x86_64/pre/include/arch/memory/allocator/tiny_frame_allocator.hpp b/arch/x86_64/pre/include/arch/memory/allocator/tiny_frame_allocator.hpp deleted file mode 100644 index 1e4746d..0000000 --- a/arch/x86_64/pre/include/arch/memory/allocator/tiny_frame_allocator.hpp +++ /dev/null @@ -1,74 +0,0 @@ -#ifndef TEACHOS_ARCH_X86_64_MEMORY_ALLOCATOR_TINY_FRAME_ALLOCATOR_HPP -#define TEACHOS_ARCH_X86_64_MEMORY_ALLOCATOR_TINY_FRAME_ALLOCATOR_HPP - -#include "arch/memory/allocator/concept.hpp" -#include "arch/memory/allocator/physical_frame.hpp" - -#include - -namespace teachos::arch::memory::allocator -{ - namespace - { - constexpr uint8_t TINY_ALLOCATOR_FRAMES_COUNT = 3U; - } - - /** - * @brief Allocates memory using memory areas read from the multiboot2 information pointer. Does not allocate its own - * frames, but uses the necessary three frames provided by another allocator to map one virtual level 1 page entry and - * the necessary upper layers. - */ - struct tiny_frame_allocator - { - /** - * @brief Constructor. - * - * @tparam T Contract the allocator that should be used to actually allocate and deallocate, the underlying three - * frames has to follow. - * - * @param allocator Reference to an allocator following the FrameAllocator concept, which is used to allocate - * entries when the underlying frames are created. - */ - template - tiny_frame_allocator(T & allocator) - : frames{} - { - // Has to be done this way, because constructing the constructor with the data from allocator.allocate_frames(), - // does not work because it would set the value correctly but because we pass it as an std::optional it would not - // set the engaged flag. Meaning the has_value() method would still return false. - for (auto & frame : frames) - { - auto allocated = allocator.allocate_frame(); - if (allocated.has_value()) - { - frame.emplace(allocated.value()); - } - } - } - - /** - * @brief Allocate memory by finding and returning one of the three free physical frames. - * - * @return First free physical frames of the three frames held by this allocator or nullopt if we used up all three - * frames already. - */ - auto allocate_frame() -> std::optional; - - /** - * @brief Deallocates one of the three previously allocated physical frames. - * - * @note If more than the three frames are deallocated the method will halt execution, because it can only hold 3 - * frames. - * - * @param physical_frame Previously allocated physical_frame that should be deallocated. - */ - auto deallocate_frame(physical_frame const & physical_frame) -> void; - - private: - std::array, TINY_ALLOCATOR_FRAMES_COUNT> frames = - {}; ///< Container that holds the frames allocated by another allocator. - }; - -} // namespace teachos::arch::memory::allocator - -#endif // TEACHOS_ARCH_X86_64_MEMORY_ALLOCATOR_TINY_FRAME_ALLOCATOR_HPP diff --git a/arch/x86_64/pre/include/arch/memory/main.hpp b/arch/x86_64/pre/include/arch/memory/main.hpp deleted file mode 100644 index d51815f..0000000 --- a/arch/x86_64/pre/include/arch/memory/main.hpp +++ /dev/null @@ -1,30 +0,0 @@ -#ifndef TEACHOS_ARCH_X86_64_MEMORY_MAIN_HPP -#define TEACHOS_ARCH_X86_64_MEMORY_MAIN_HPP - -#include "arch/memory/paging/page_entry.hpp" - -#include - -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. - * - * @note Enables the necessary register flags and remaps the kernel, - * elf_sections, vga_text and the heap. - */ - auto initialize_memory_management() -> void; - -} // namespace teachos::arch::memory - -#endif // TEACHOS_ARCH_X86_64_MEMORY_MAIN_HPP diff --git a/arch/x86_64/pre/include/arch/memory/multiboot/elf_symbols_section.hpp b/arch/x86_64/pre/include/arch/memory/multiboot/elf_symbols_section.hpp deleted file mode 100644 index 348c159..0000000 --- a/arch/x86_64/pre/include/arch/memory/multiboot/elf_symbols_section.hpp +++ /dev/null @@ -1,170 +0,0 @@ -#ifndef TEACHOS_ARCH_X86_64_MEMORY_MULTIBOOT_ELF_SYBOLS_SECTION_HPP -#define TEACHOS_ARCH_X86_64_MEMORY_MULTIBOOT_ELF_SYBOLS_SECTION_HPP - -// #include "arch/memory/multiboot/info.hpp" -// #include "arch/stl/container.hpp" -// #include "arch/stl/contiguous_pointer_iterator.hpp" - -#include -#include - -namespace teachos::arch::memory::multiboot -{ - /** - * @brief Defines all elf section types an elf section header can have. - * - * @note See https://docs.oracle.com/cd/E19683-01/816-1386/chapter6-94076/index.html for more information. - */ - enum struct elf_section_type : uint32_t - { - 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 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. - NOTE, ///< (SHT_NOTE) Stores information that marks files in some way. - EMPTY, ///< (SHT_NOBITS) Program data section, that occupies no space in the file (.bss). - RELOCATION_ENTRY_WITHOUT_ADDENDS, ///< (SHT_REL) Only used on 32 bit systems. - UNSPECIFIED, ///< (SHT_SHLIB) Reserved but has unspecified semantics. - DYNAMIC_SYMBOL_TABLE, ///< (SHT_DYNSYM) Holds minimal set of symbols adequate for dynamic linking. - INITALIZATION_FUNCTION_ARRAY = 14, ///< (SHT_INIT_ARRAY) Array of pointers to intialization functions () -> void. - TERMINATION_FUNCTION_ARRAY, ///< (SHT_FINI_ARRAY) Array of pointers to termination functions () -> void. - PRE_INITALIZATION_FUNCTION_ARRAY ///< (SHT_PRE_INIT_ARRAY) Array of pointers to functions invoked before other - ///< initalization functions () -> void. - }; - - /** - * @brief Defines helper function for all states that the elf section flags of an elf section header can - * have. - * - * @note See https://docs.oracle.com/cd/E19683-01/816-1386/chapter6-94076/index.html for more information. - */ - struct elf_section_flags - { - /** - * @brief Possible set bits in our underlying std::bitset and the meaning when they are set. - */ - enum bitset : uint32_t - { - WRITABLE = 1U << 0U, ///< (SHF_WRITE) Section is writable at runtime. If it isn't then the section - ///< is assumed to be READONLY and only that flag is shown in the objdump. - OCCUPIES_MEMORY = 1U << 1U, ///< (SHF_ALLOC) Section occupies memory during execution. - ///< ALLOC flag is shown in the objdump. - EXECUTABLE_CODE = 1U << 2U, ///< (SHF_EXECINSTR) Section is executable. CODE flag is shown in the object dump. - DUPLICATE_DATA = 1U << 4U, ///< (SHF_MERGE) Section might be merged with another section. - CONTAINS_STRING = 1U << 5U, ///< (SHF_STRINGS) Section contains null-terminated strings. - SECTION_HEADER_INFO_IS_SECTION_HEADER_TABLE_INDEX = - 1U << 6U, ///< (SHF_INFO_LINK) Section contains the section header table index in the (sh_info) - ///< additional_information variable. - PRESERVE_ORDERING_AFTER_COMBINATION = - 1U << 7U, ///< (SHF_LINK_ORDER) Section preserves order after combining with another section. - REQUIRES_SPECIAL_OS_PROCESSING = - 1U << 8U, ///< (SHF_OS_NONCONFORMING) Section requires non-standard OS specific handling of its code or - ///< data, which does not confirm to standard ELF specifications. - SECTION_GROUP_MEMBER = 1U << 9U, ///< (SHF_GROUP) Section is a member of a section group. - HOLDS_THREAD_LOCAL_DATA = 1U << 10U, ///< (SHF_TLS) Section holds thread-local data. - COMPRESSED = 1U << 11U, ///< (SHF_COMPRESSED) Section contains compressed data. - SPECIAL_ORDERING_REQUIREMENTS = - 1U << 30U, ///< (SHF_ORDERED) Section has special ordering requirements, meaning it - ///< should be ordered in relation to other sections of the same type. - EXCLUDED_UNLESS_REFERENCED_OR_ALLOCATED = 1U << 31U, ///< (SHF_EXCLUDE)Section is excluded unless referenced or - ///< allocated, used for LTO (Link-Time Optimizations). - }; - - /** - * @brief Constructor. - * - * @param flags Actual value read from the elf section header, which should be converted into a std::bitset, to - * allow reading the state of single bits more easily. - */ - explicit elf_section_flags(uint64_t flags) - : flags(flags) - { - // Nothing to do - } - - /** - * @brief Checks if the given std::bitset is a subset or equivalent to the underlying std::bitset. - * - * @note Meaning that all bits that are set in the given std::bitset also have to be set in the underlyng - * std::bitset. Any additional bits that are set are not relevant. - * - * @param other Flags that we want to compare against and check if the underlying std::bitset has the same bits set. - * @return Whether the given flags are a subset or equivalent with the underlying std::bitset. - */ - auto contains_flags(std::bitset<64U> other) const -> bool; - - /** - * @brief Allows to compare the underlying std::bitset of two instances. - * - * @param other Other instance that we want to compare with. - * @return Whether the underlying std::bitset of both types is the same. - */ - auto operator==(elf_section_flags const & other) const -> bool = default; - - private: - std::bitset<64U> flags; ///< Underlying bitset used to read the flags from. Bits 21 - 28 are reserved for operating - ///< system specific semantics and bits 29 - 32 are reserved for processor specific - ///< semantics. Bits 33 - 64 are unused for compatability with ELF32. - }; - - /** - * @brief Defines the data included in a section header, where each section has exactly one section header. - * - * @note See https://refspecs.linuxbase.org/elf/gabi4+/ch4.sheader.html for more information. - */ - struct elf_section_header - { - uint32_t name_table_index; ///< Index into the section header string table, specifies the name of the section. - elf_section_type type; ///< Categorizes the sections content and semantics. - elf_section_flags flags; ///< 1-bit flgas that describe section attributes. - uint64_t physical_address; ///< If section appears in memory image of a process, gives address at which the - ///< sections first byte should reside, otherwise 0. - uint64_t file_offset; ///< Offset from the beginning of the file to the first byte in the section. SHT_NOBITS - ///< contains the conceptual placement instead (because it occupies no space in the file). - uint64_t section_size; ///< Complete section size in bytes, SHT_NOBITS may have non-zero value but will always - ///< occupy no space in the file. - uint32_t other_section; ///< Section header table index link, behaviour varies on type - ///< https://refspecs.linuxbase.org/elf/gabi4+/ch4.sheader.html#sh_link. - uint32_t additional_information; ///< Extra information, behaviour varies on type - ///< https://refspecs.linuxbase.org/elf/gabi4+/ch4.sheader.html#sh_link. - uint64_t address_alignment; ///< Possible address alignment constraints. Value of virutal_address must be 0 % value - ///< of address_alignment. Value 0 or 1 mean no alignment constraints. - uint64_t fixed_table_entry_size; ///< If sections holds table with fixed-sized entries, this gives the size in - ///< bytes of each entry. - - /** - * @brief Detect whether a section header is inactive or not, should always be the case for the first entry in the - * sections table. - * - * @return Whether the current section header is actually null or not, requires all fields besides section_size and - * other_section to contain 0. - */ - auto is_null() const -> bool; - }; - - // /** - // * @brief Defines an entry in the multi_boot_tag array of the multi_boot_info struct, of type - // * multi_boot_tag_type::ELF_SECTIONS. - // * - // * @note The first section in the sections array will always be INACTIVE, there can only ever be one DYNAMIC - // section - // * and only either one DYNAMIC_SYMBOL_TABLE or SYMBOL_TABLE. - // */ - // struct elf_symbols_section_header - // { - // tag info; ///< Basic multi_boot_tag information. - // uint32_t number_of_sections; ///< Number of sections in the sections array. - // uint32_t entry_size; ///< Size of each entry in the sections array. - // uint32_t section_index; ///< Index to the string table used for symbol names. - // std::byte end; ///< Marks the end of the tag, used to mark the beginning of any additional data. - // ///< contained in the section, to ensure byte alignment is actually 4 byte. - // }; - - // using elf_section_header_container = stl::container>; - -} // namespace teachos::arch::memory::multiboot - -#endif // TEACHOS_ARCH_X86_64_MEMORY_MULTIBOOT_ELF_SYBOLS_SECTION_HPP diff --git a/arch/x86_64/pre/include/arch/memory/multiboot/reader.hpp b/arch/x86_64/pre/include/arch/memory/multiboot/reader.hpp deleted file mode 100644 index 275e5e2..0000000 --- a/arch/x86_64/pre/include/arch/memory/multiboot/reader.hpp +++ /dev/null @@ -1,59 +0,0 @@ -#ifndef TEACHOS_ARCH_X86_64_MEMORY_MULTIBOOT_READER_HPP -#define TEACHOS_ARCH_X86_64_MEMORY_MULTIBOOT_READER_HPP - -// #include "arch/memory/multiboot/elf_symbols_section.hpp" -// #include "arch/memory/multiboot/memory_map.hpp" - -#include "arch/memory/multiboot/elf_symbols_section.hpp" - -#include - -#include -#include - -namespace teachos::arch::memory::multiboot -{ - /** - * @brief Contains all relevant information to map and allocate memory that is read from the multiboot2 information - * structure. - */ - struct memory_information - { - std::size_t kernel_start; ///< Start address of the kernel code in memory. - std::size_t kernel_end; ///< End address of the kernel code in memory. - multiboot2::elf_symbols; ///< Contains non-owning pointers to all kernel sections. - std::size_t multiboot_start; ///< Start address of the multiboot code in memory. - std::size_t multiboot_end; ///< End address of the multiboot code in memory. - // std::sp - // memory_area_container areas; ///< Contains non-owning pointers to all memory areas. - }; - - /** - * @brief Reads the relevant multiboot2 information data from memory. - * - * @note This is done using the multiboot_information_pointer, which marks the start of the multiboot2 data. The - * indivdual headers we have to read are 8 byte aligned, whereas the data contained in those headers does not have to - * be. All sections that are read additionaly receive some sanity to ensure the read address is actually pointing to - * the expected structure, if they are not this method will assert. - * - * The memory_information variables are calcualted like this: - * - kernel_start: Calculated by getting the lowest address specified in the elf symbols headers. - * - kernel_end: Calculated by getting the highest address specified in the elf symbols headers and adding the length - * of that section. - * - multiboot_start: Calcualted by simply getting the value of the multiboot information pointer, because it already - * contains the address pointint to the start of the multiboot2 data. - * - multiboot_end: Calcualted by getting the value of the multiboot information pointer and adding the total size of - * the complete multiboot2 data - * - memory_areas: Calculated by simply accessing the address of the entries variable in the memory map header - * structure. - * - area_count: Calculated by subtracing the memory map header size from the total tag size, which results in the - * remaining size (size of the entries array), this size is then divided by the size of one entry in that array, which - * should be 24 bytes. - * - * @return Relevant data read from multiboot2. - */ - auto read_multiboot2() -> memory_information; - -} // namespace teachos::arch::memory::multiboot - -#endif // TEACHOS_ARCH_X86_64_MEMORY_MULTIBOOT_READER_HPP diff --git a/arch/x86_64/pre/include/arch/memory/paging/active_page_table.hpp b/arch/x86_64/pre/include/arch/memory/paging/active_page_table.hpp deleted file mode 100644 index abefd61..0000000 --- a/arch/x86_64/pre/include/arch/memory/paging/active_page_table.hpp +++ /dev/null @@ -1,206 +0,0 @@ -#ifndef TEACHOS_ARCH_X86_64_MEMORY_PAGING_ACTIVE_PAGE_TABLE_HPP -#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/paging/virtual_page.hpp" - -#include -#include -#include - -namespace teachos::arch::memory::paging -{ - /** - * @brief Currently actively by the CPU used level 4 page table, is used to ensure there is only ever one valid - * instance and it cannot be copied or constructed again. - */ - struct active_page_table - { - /** - * @brief Creates a single instance of an active level 4 page table table and returns the created instance or - * alternatively returns the previously created instance instead. The instance is owned by this method and is - * static, meaning it lives on for the complete lifetime of the program. - * - * @return Active single unique instance of the level 4 page table. - */ - auto static create_or_get() -> active_page_table &; - - /** - * @brief Index operator overload to access specific mutable entry directy of the level 4 page table. - * - * @param index Index of the entry we want to access and only read. - * @return Entry at the given table index. - */ - auto operator[](std::size_t index) -> entry &; - - /** - * @brief Translates virtual address into corresponding physical address. Calls translate_page under the hood. - * - * @param address Virtual address we want to translate into physical one. - * @return Physical address corresponding to the provided virtual address. - */ - auto translate_address(virtual_address address) -> std::optional; - - /** - * @brief Translates page into physical frame, will first attempt to parse normally using default page size and if - * it failed attempt to parse using huge pages. - * - * @param page Page to translate into physical frame. - * @return Physical frame corresponding to the provided virtual page. - */ - auto translate_page(virtual_page page) -> std::optional; - - /** - * @brief Translates huge page into actual physical frame. - * - * @param page Page to translate into physical frame. - * @return Physical frame corresponding to the provided virtual page. - */ - auto translate_huge_page(virtual_page page) -> std::optional; - - /** - * @brief Maps a virtual page to a physical frame in the page table with the specified flags. - * - * @note Allocates and maps an entry in every page level if it does not exists yet down to level 1. If the level 1 - * page table already exists it halts execution instead. - * - * @tparam T Type constraint of the allocator, being that is follows the given concept and contains an allocate and - * deallocate method. - * @param allocator Reference to an allocator following the FrameAllocator concept, which is used to allocate - * entries when a new page table is required. - * @param page Virtual page that is being mapped. - * @param frame Physical frame that the virtual page will be mapped to. - * @param flags A bitset of flags that configure the page table entry for this mapping. - */ - template - 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), flags); - } - - auto & level1_entry = current_handle[page.get_level_index(page_table_handle::LEVEL1)]; - arch::exception_handling::assert(!level1_entry.contains_flags(entry::HUGE_PAGE), - "[Page Mapper] Unable to map huge pages"); - arch::exception_handling::assert(level1_entry.is_unused(), "[Page Mapper] Page table entry is already used"); - level1_entry.set_entry(frame, flags.to_ulong() | entry::PRESENT); - } - - /** - * @brief Allocates the next free frame and then uses that frame to call map_page_to_frame. - * - * @see map_page_to_frame - */ - template - auto map_page_to_next_free_frame(T & allocator, virtual_page page, std::bitset<64U> flags) -> void - { - auto const frame = allocator.allocate_frame(); - exception_handling::assert(frame.has_value(), "[Page mapper] Out of memory exception"); - map_page_to_frame(allocator, page, frame.value(), flags); - } - - /** - * @brief Gets the corresponding page the given frame has to be contained in and uses that to call - * map_page_to_frame. - * - * @see map_page_to_frame - */ - template - auto identity_map(T & allocator, allocator::physical_frame frame, std::bitset<64U> flags) -> void - { - auto const page = virtual_page::containing_address(frame.start_address()); - map_page_to_frame(allocator, page, frame, flags); - } - - /** - * @brief Unmaps the virtual page from the previously mapped to physical frame and resets the flags. - * - * @note For the unmap function to deallocates and unmaps correctly, the entry in every page level if this page was - * the last one up to level 4 should be unmapped and ensured to clear the Translation Lookaside Buffer, so that the - * unmapped value is removed from cache as well. This is currently not done and instead we only dallocate and unmap - * the level 1 page table entry, this is the case because it conflicts with our recursive mapping for the temporary - * page, which requires the other page table entries to walk to the actual level 4 page table. If we remove all page - * table entries beforehand, we therefore can not remap the kernel anymore. - * - * @tparam T Type constraint of the allocator, being that is follows the given concept and contains an allocate and - * deallocate method. - * @param allocator Reference to an allocator following the FrameAllocator concept, which is used to allocate - * entries when a new page table is required. - * @param page Virtual page that is being unmapped. - */ - template - auto unmap_page(T & allocator, virtual_page page) -> void - { - exception_handling::assert(translate_page(page).has_value(), - "[Page Mapper] Attempted to unmap page, which has not been mapped previously"); - - auto current_handle = active_handle; - - for (auto level = page_table_handle::LEVEL4; level != page_table_handle::LEVEL1; --level) - { - auto const level_index = page.get_level_index(level); - auto const next_handle = current_handle.next_table(level_index); - // The next table method failed even tough the page has to be mapped already, because translate_page did not - // fail. This can only mean that we attempted to unmap a huge page, which is not supported in the first place. - exception_handling::assert(next_handle.has_value(), "[Page Mapper] Unable to unmap huge pages"); - current_handle = next_handle.value(); - } - - unmap_page_table_entry(allocator, page, current_handle); - kernel::cpu::tlb_flush(page.start_address()); - } - - private: - /** - * @brief Private constructor should only be used by create or get method, which ensures to create only ever one - * instance. - * - * @param active_handle Handle to the underlying currently active level 4 page table. - */ - active_page_table(page_table_handle active_handle); - - /** - * @brief Deleted copy constructor. - */ - active_page_table(active_page_table const &) = delete; - - /** - * @brief Deleted copy assignment operator. - */ - active_page_table & operator=(active_page_table const &) = delete; - - /** - * @brief Unmaps specific page at the current internal handle level. - * - * @tparam T Type constraint of the allocator, being that is follows the given concept and contains an allocate and - * deallocate method. - * @param allocator Reference to an allocator following the FrameAllocator concept, which is used to allocate - * entries *when a new page table is required. - * @param page Virtual page that is being unmapped. - * @param handle Page Table handle we want to access the entry that should be cleared on. - */ - template - auto static unmap_page_table_entry(T & allocator, virtual_page page, page_table_handle & handle) -> void - { - auto level_index = page.get_level_index(handle.get_level()); - auto & entry = handle[level_index]; - auto const frame = entry.calculate_pointed_to_frame(); - exception_handling::assert(frame.has_value(), - "[Page Mapper] Attempted to unmap page, which has not been mapped previously"); - entry.set_unused(); - allocator.deallocate_frame(frame.value()); - } - - public: - page_table_handle active_handle; ///< Underlying active level 4 page table - }; - -} // namespace teachos::arch::memory::paging - -#endif // TEACHOS_ARCH_X86_64_MEMORY_PAGING_ACTIVE_PAGE_TABLE_HPP diff --git a/arch/x86_64/pre/include/arch/memory/paging/inactive_page_table.hpp b/arch/x86_64/pre/include/arch/memory/paging/inactive_page_table.hpp deleted file mode 100644 index 8d96740..0000000 --- a/arch/x86_64/pre/include/arch/memory/paging/inactive_page_table.hpp +++ /dev/null @@ -1,39 +0,0 @@ -#ifndef TEACHOS_ARCH_X86_64_MEMORY_PAGING_INACTIVE_PAGE_TABLE_HPP -#define TEACHOS_ARCH_X86_64_MEMORY_PAGING_INACTIVE_PAGE_TABLE_HPP - -#include "arch/memory/allocator/physical_frame.hpp" -#include "arch/memory/paging/active_page_table.hpp" -#include "arch/memory/paging/temporary_page.hpp" - -namespace teachos::arch::memory::paging -{ - /** - * @brief By the CPU used level 4 page table. - */ - struct inactive_page_table - { - /** - * @brief Constructor. - * - * @param frame Frame that should be mapped as the level 4 page table. - */ - inactive_page_table(allocator::physical_frame frame); - - /** - * @brief Constructor. - * - * @param frame Frame that should be mapped as the level 4 page table. - * @param active_page_table Actual active page table that should be unmapped so we can map a new level 4 - * page table. - * @param temporary_page Temporary page that should be used to map the given frame as the new level 4 page - * table. - */ - inactive_page_table(allocator::physical_frame frame, active_page_table & active_page_table, - temporary_page & temporary_page); - - allocator::physical_frame page_table_level_4_frame; ///< Temporary level 4 page table - }; - -} // namespace teachos::arch::memory::paging - -#endif // TEACHOS_ARCH_X86_64_MEMORY_PAGING_INACTIVE_PAGE_TABLE_HPP diff --git a/arch/x86_64/pre/include/arch/memory/paging/kernel_mapper.hpp b/arch/x86_64/pre/include/arch/memory/paging/kernel_mapper.hpp deleted file mode 100644 index 581f142..0000000 --- a/arch/x86_64/pre/include/arch/memory/paging/kernel_mapper.hpp +++ /dev/null @@ -1,180 +0,0 @@ -#ifndef TEACHOS_ARCH_X86_64_MEMORY_PAGING_KERNEL_MAPPER_HPP -#define TEACHOS_ARCH_X86_64_MEMORY_PAGING_KERNEL_MAPPER_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 -#include - -namespace teachos::arch::memory::paging -{ - /** - * @brief Kernel mapper that allows to remap the kernel elf sections in C++. - * - * @tparam T Contract the allocator that should be used to allocate frames for the remapping process has to fulfill. - */ - template - struct kernel_mapper - { - /** - * @brief Constructor. - * - * @param allocator Allocator that should be used to allocate frames for the remapping process. - * @param mem_info Information about elf kernel sections required for remapping process. - */ - kernel_mapper(T & allocator, multiboot::memory_information const & mem_info) - : allocator(allocator) - , mem_info(mem_info) - { - // Nothing to do - } - - /** - * @brief Remap the kernel, meaning we map the entire kernel and all of it's elf sections with the correct flags - * into memory and then replace the created mapping with the current one. - * - * @note We have to use a workaround with an - * 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() -> 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{0xCAFE'BABE}, allocator}; - 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"); - inactive_page_table new_table{frame.value(), active_table, temporary_page}; - remap_elf_kernel_sections(new_table, temporary_page, active_table); - auto const old_table = switch_active_page_table(new_table); - // Turn old level 4 page table, mapped by assembler code into stack guard page. - // Only works if the identity mapped page tables by assembler are right above the stack. - 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); - } - - private: - /** - * @brief Remaps the kernel elf sections. - * - * This is done with switching the current level 4 page table recursive - * mapping to any unmapped address in memory and then actually mapping the level 4 page table on that address. - * Once the remapping process is done we can restore the original recursive mapping with the complete remapped - * kernel. - * - * @note Because we change the entries we also have to ensure we flush the translation lookaside buffer, before we - * map the entries. - * - * @param inactive_table Level 4 page table we temporarily map the kernel into. - * @param temporary_page Temporary page that should be used for the mapping process and then - * unmapped once finished. - * @param active_table Active level 4 page table that has its recursive mapping overwritten temporarily and then - * restored once the process is finished. - */ - 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( - 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); - kernel::cpu::tlb_flush_all(); - map_elf_kernel_sections(active_table); - - page_table_level4[511].set_entry(backup, entry::PRESENT | entry::WRITABLE); - kernel::cpu::tlb_flush_all(); - temporary_page.unmap_page(active_table); - } - - /** - * @brief Switches the current active table pointed to by the CR3 register with another page table that is currently - * inactive. - * - * @param new_table Inactive page table that should now be made active and replace the current active one. - * @return The previous active page table. - */ - auto switch_active_page_table(inactive_page_table new_table) -> inactive_page_table - { - 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(); - kernel::cpu::write_control_register(kernel::cpu::control_register::CR3, new_address); - return old_table; - } - - /** - * @brief Maps the required entries according to every elf section and it's contained frames. Additionally each of - * thoose frames gets the correct entry flags according to elf section flags. - * - * @param active_table Active level 4 page table that should be used to map the required elf sections into entries. - * Has had its recursive mapping temporarily replaced and points to unmapped place in memory. - */ - auto map_elf_kernel_sections(active_page_table & active_table) -> void - { - exception_handling::assert(!mem_info.sections.empty(), "[Kernel Mapper] Kernel elf sections empty"); - constexpr std::array USER_SECTION_BASES = { - 0x102000, // .boot_bss (Contains statically allocated variables) - 0x209000, // .stl_text (Contains code for custom std implementations and standard library code) - 0x217000, // .user_text (Contains the actual user code executed) - 0x21E000, // .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)) - { - continue; - } - exception_handling::assert(section.physical_address % allocator::PAGE_FRAME_SIZE == 0U, - "[Kernel Mapper] Section must be page aligned"); - auto const start_frame = allocator::physical_frame::containing_address(section.physical_address); - // End address is exclusive, so that it is not part of the section anymore (one past the last frame of this - // section). But end frame would now point to the actual last frame and not one past the last frame, therefore - // we increment by one to get one past the last frame of this section. - auto const end_frame = - ++(allocator::physical_frame::containing_address(section.physical_address + section.section_size - 1)); - - allocator::frame_container::iterator const begin{start_frame}; - allocator::frame_container::iterator const end{end_frame}; - allocator::frame_container const frames{begin, end}; - 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) - { - active_table.identity_map(allocator, frame, entry.get_flags()); - } - } - - auto const vga_buffer_frame = - allocator::physical_frame::containing_address(video::vga::text::DEFAULT_VGA_TEXT_BUFFER_ADDRESS); - active_table.identity_map(allocator, vga_buffer_frame, entry::WRITABLE); - } - - T & allocator; - multiboot::memory_information const & - mem_info; ///< Information about elf kernel sections required for remapping process. - }; - -} // namespace teachos::arch::memory::paging - -#endif // TEACHOS_ARCH_X86_64_MEMORY_PAGING_KERNEL_MAPPER_HPP diff --git a/arch/x86_64/pre/include/arch/memory/paging/page_entry.hpp b/arch/x86_64/pre/include/arch/memory/paging/page_entry.hpp deleted file mode 100644 index 8147c5c..0000000 --- a/arch/x86_64/pre/include/arch/memory/paging/page_entry.hpp +++ /dev/null @@ -1,121 +0,0 @@ -#ifndef TEACHOS_ARCH_X86_64_MEMORY_PAGING_PAGE_ENTRY_HPP -#define TEACHOS_ARCH_X86_64_MEMORY_PAGING_PAGE_ENTRY_HPP - -#include "arch/memory/allocator/physical_frame.hpp" -#include "arch/memory/multiboot/elf_symbols_section.hpp" - -#include -#include - -namespace teachos::arch::memory::paging -{ - /** - * @brief Marks a specific entry in an actual page table. - */ - struct entry - { - /** - * @brief Possible set bits in our underlying std::bitset and the meaning when they are set. - */ - enum bitset : uint64_t - { - 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_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. - DIRTY = 1UL << 6UL, ///< Page has been writen too. - HUGE_PAGE = 1UL << 7UL, ///< Page is huge (2 MiB page size in P2 page table and 1 GiB in P3 page table, - ///< instead of 4 KiB). Has to be false for P1 and P4 page tables. - GLOBAL = 1UL << 8UL, ///< Page is not flushed from caches on address space switches (PGE bit of CR4 register - ///< has to be set) - EXECUTING_CODE_FORBIDDEN = - 1UL << 63UL, ///< Page is forbidden from executing code (NXE bit in the EFER register has to be set) - }; - - /** - * @brief Defaulted constructor. - */ - entry() = default; - - /** - * @brief Creates a new entry object from a 64bit address. Should follow the scheme where bit index 12 - 51 are the - * actual address and the other bits are flags. - * - * @param flags Flags that will be passed to underlying std::bitset. - */ - explicit entry(uint64_t flags); - - /** - * @brief Creates a new entry converting the given elf section flags into the corresponding correct entry flags. - * - * @note Enables us to set the correct flags on a entry depending on which elf section it is contained in. For - * example entries of .text sections should be executable and read only or entries of .data sections should be - * writable but not executable. - * - * @param elf_flags Elf section flags we want to convert into entry flags. - */ - explicit entry(multiboot::elf_section_flags elf_flags); - - /** - * @brief Whether the current page is unused, meaning the underlying std::bitset is 0. - * - * @return Current page is in memory. - */ - auto is_unused() const -> bool; - - /** - * @brief Marks the page as unused, meaning the underlying std::bitset is set to 0. - */ - 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. - * - * @return Calculated physical frame entry is pointing too. - */ - auto calculate_pointed_to_frame() const -> std::optional; - - /** - * @brief Copies the address and flags from the given physical frame into the underlying std::bitset - * - * @param frame Physical frame that contains the address we want to copy into our underlying std::bitset. - * @param additional_flags Entry flags which will be copied into our underlying std::bitset. - */ - auto set_entry(allocator::physical_frame frame, std::bitset<64U> additional_flags) -> void; - - /** - * @brief Checks if the given std::bitset is a subset or equivalent to the underlying std::bitset. - * - * @note Meaning that all bits that are set in the given std::bitset also have to be set in the underlyng - * std::bitset. Any additional bits that are set are not relevant. - * - * @param other Flags that we want to compare against and check if the underlying std::bitset has the same bits set. - * @return Whether the given flags are a subset or equivalent with the underlying std::bitset. - */ - auto contains_flags(std::bitset<64U> other) const -> bool; - - /** - * @brief Extracts only the flags from the underlying entry and ignores all bits that contain the physical address. - * - * @return Extracted entry flags, without the physical address. - */ - auto get_flags() const -> std::bitset<64U>; - - private: - std::bitset<64U> flags; ///< Underlying bitset used to read the flags from. Bits 9 - 11 and 52 - 62 can be - ///< freely used for additional flags by the operating system. - }; - -} // namespace teachos::arch::memory::paging - -#endif // TEACHOS_ARCH_X86_64_MEMORY_PAGING_PAGE_ENTRY_HPP diff --git a/arch/x86_64/pre/include/arch/memory/paging/page_table.hpp b/arch/x86_64/pre/include/arch/memory/paging/page_table.hpp deleted file mode 100644 index 247086c..0000000 --- a/arch/x86_64/pre/include/arch/memory/paging/page_table.hpp +++ /dev/null @@ -1,157 +0,0 @@ -#ifndef TEACHOS_ARCH_X86_64_MEMORY_PAGING_PAGE_TABLE_HPP -#define TEACHOS_ARCH_X86_64_MEMORY_PAGING_PAGE_TABLE_HPP - -#include "arch/exception_handling/assert.hpp" -#include "arch/memory/allocator/concept.hpp" -#include "arch/memory/paging/page_entry.hpp" - -namespace teachos::arch::memory::paging -{ - constexpr std::size_t PAGE_TABLE_ENTRY_COUNT = 512U; ///< Default entry count of a page table in x86_84 is 512. - - /** - * @brief Forward delcaration of the page_table, because it should only be accessible over the handle. - * - * @note The actual methods or constructor are not defined meaning they are not callable from outside. Instead the - * struct is only fully defined in the implementation (.cpp) file of the page table, and therefore the memthods are - * only accesible in that file. - */ - struct page_table; - - /** - * @brief Handle that ensures accessing the page table is safe because it adds additional checks to the next_table - * method and ensures it can only be called if the table level is not LEVEL1. - */ - struct page_table_handle - { - /** - * @brief Level of the page table. - * - * Level 1 will not be able to call next_table anymore, because it would result in - * attempting to access memory that it should not. - */ - enum level : uint8_t - { - LEVEL1, - LEVEL2, - LEVEL3, - LEVEL4 - }; - - /** - * @brief Constructor. - * - * @param table Underlying page table the handle should point to. - * @param table_level Level the underlying page table is on, used to ensure safety. - */ - page_table_handle(page_table * table, level table_level); - - /** - * @brief Set every entry of the page to unused. - */ - auto zero_entries() -> void; - - /** - * @brief Checks if all entries of this page are unused. - */ - auto is_empty() const -> bool; - - /** - * @brief Get the current table level. - * - * @return Current table level. - */ - auto get_level() const -> level; - - /** - * @brief Returns the next page table level from the given page table index. Meaning we - * use an index into a Level 4 page table to get the according Level 3 page table. - * - * @note If this method is called with a Level 1 page table it will instead assert and halt execution, because there - * is no furthere page table and mangeling up and returning the physical address would cause hard to debug issues. - * - * @param table_index Index of this page table in the page table one level lower. - */ - auto next_table(std::size_t table_index) const -> std::optional; - - /** - * @brief Call next_table and then checks if the table already exists, if it does not it will use the given - * allocator to get the next free frame and set the entry to that instead. - * - * @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 - 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 - // been created itself. So we have to do that before we are able to allocate the wanted frame. This has to be done - // for every level, meaning we potenitally create a level 4, level 3 and level 2 page entry, each pointing to a - // page table one level below. - if (!next_handle.has_value()) - { - auto const allocated_frame = allocator.allocate_frame(); - exception_handling::assert(allocated_frame.has_value(), "[Page mapper] Unable to allocate frame"); - this->operator[](table_index).set_entry(allocated_frame.value(), entry::PRESENT | entry::WRITABLE); - // There should now be an entry at the previously not existent index, therefore we can simply access it again. - next_handle = next_table(table_index); - 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(); - } - - /** - * @brief Index operator overload to access specific mutable entry directy. - * - * @param index Index of the entry we want to access and only read. - * @return Entry at the given table index. - */ - auto operator[](std::size_t index) -> entry &; - - /** - * @brief Index operator overload to access specific immutable entry directy. - * - * @param index Index of the entry we want to access and read or write. - * @return Entry at the given table index. - */ - auto operator[](std::size_t index) const -> entry const &; - - /** - * @brief Pre decrement operator on the page table level enum, is defined so we can use it as a replacement - * for an int index in a range based for loop. - * - * @note Will halt execution if called with page_table_handle::LEVEL1, because there is no level below. Has to be - * defined as either a friend function or inline header method, because we define an operator of another type. In - * this instance friend function was choosen, because the struct itself also requires the operator, but declaring - * before the struct is not possible, because the enum is in the struct. This is inpossible because the struct - * requires the operator declared before itself to work, and the operator requires the struct declared before itself - * to work. Furthermore this allows the defintion of the method to be done in the cpp, avoiding includes in the - * header file. - * - * @param value Value we want to decrement on - * @return New level value decrement by one, meaning the level is also decrement by one Level4 --> Level3, ... - */ - friend auto operator--(level & value) -> level &; - - private: - page_table * table; ///< Handle to underlying page table, can never be null (invariant ensured by - ///< constructor) - level table_level; ///< Level page table is currently on, depends on how often next_level was - ///< called successfuly. - }; - -} // namespace teachos::arch::memory::paging - -#endif // TEACHOS_ARCH_X86_64_MEMORY_PAGING_PAGE_TABLE_HPP diff --git a/arch/x86_64/pre/include/arch/memory/paging/temporary_page.hpp b/arch/x86_64/pre/include/arch/memory/paging/temporary_page.hpp deleted file mode 100644 index d0d7781..0000000 --- a/arch/x86_64/pre/include/arch/memory/paging/temporary_page.hpp +++ /dev/null @@ -1,64 +0,0 @@ -#ifndef TEACHOS_ARCH_X86_64_MEMORY_PAGING_TEMPORARY_PAGE_HPP -#define TEACHOS_ARCH_X86_64_MEMORY_PAGING_TEMPORARY_PAGE_HPP - -#include "arch/memory/allocator/physical_frame.hpp" -#include "arch/memory/allocator/tiny_frame_allocator.hpp" -#include "arch/memory/paging/active_page_table.hpp" -#include "arch/memory/paging/virtual_page.hpp" - -namespace teachos::arch::memory::paging -{ - /** - * @brief A temporary page used to remap the kernel. - */ - struct temporary_page - { - /** - * @brief Construct a new temporary page object. - * - * @tparam Type constraint of the allocator, being that is follows the given concept and contains an allocate and - * deallocate method. - * @param page Page to turn into temporary page. - * @param allocator Frame allocator used to fill page. - */ - template - temporary_page(virtual_page page, T & allocator) - : page{page} - , allocator{allocator} - { - // Nothing to do - } - - /** - * @brief Unmap the current page. - * - * @param active_table The current active page table. - */ - auto unmap_page(active_page_table & active_table) -> void; - - /** - * @brief Map the temporary page to a frame. - * - * @param frame The frame to which the page is mapped. - * @param active_table The current active page table. - * @return level1 page table handle containing the mapped page. - */ - auto map_table_frame(allocator::physical_frame frame, active_page_table & active_table) -> page_table_handle; - - private: - /** - * @brief Map the temporary page to a frame. - * - * @param frame The frame to which the page is mapped. - * @param active_table The current active page table. - * @return The virtual address of the page. - */ - auto map_to_frame(allocator::physical_frame frame, active_page_table & active_table) -> virtual_address; - - virtual_page page; ///< Underlying virtual page we want to temporarily map. - allocator::tiny_frame_allocator allocator; ///< Allocator that should be used to map the temporary page. - }; - -} // namespace teachos::arch::memory::paging - -#endif // TEACHOS_ARCH_X86_64_MEMORY_PAGING_TEMPORARY_PAGE_HPP \ No newline at end of file diff --git a/arch/x86_64/pre/include/arch/memory/paging/virtual_page.hpp b/arch/x86_64/pre/include/arch/memory/paging/virtual_page.hpp deleted file mode 100644 index 1a20eae..0000000 --- a/arch/x86_64/pre/include/arch/memory/paging/virtual_page.hpp +++ /dev/null @@ -1,91 +0,0 @@ -#ifndef TEACHOS_ARCH_X86_64_MEMORY_PAGING_VIRTUAL_PAGE_HPP -#define TEACHOS_ARCH_X86_64_MEMORY_PAGING_VIRTUAL_PAGE_HPP - -#include "arch/memory/allocator/physical_frame.hpp" -#include "arch/memory/paging/page_table.hpp" - -#include -#include -#include - -namespace teachos::arch::memory::paging -{ - using virtual_address = std::size_t; - - /** - * @brief Virtual page entry contained in P1 page tables - */ - struct virtual_page - { - /** - * @brief Defaulted constructor. - */ - constexpr virtual_page() = default; - - /** - * @brief Constructor. - * - * @param page_number Index number of the current virtual page, used to distinguish it from other pages. - */ - explicit constexpr virtual_page(std::size_t page_number) - : page_number(page_number) - { - // Nothing to do - } - - /** - * @brief Returns the virtual page the given address is contained in. - * - * @param address Virtual address we want to get the corresponding virtual page for. - * @return Frame the given address is contained in. - */ - auto static containing_address(virtual_address address) -> virtual_page; - - /** - * @brief Evaluates the start address of the virtual page. - * - * @return Start address of the virtual page. - */ - auto start_address() const -> virtual_address; - - /** - * @brief Calculates the index into the page table with the given level, which leads to this virtual page. - * - * @param level Level of the page table we want to calculate the index for. - * @return Index into the page table with the given level. - */ - auto get_level_index(page_table_handle::level level) const -> size_t; - - /** - * @brief Post increment operator. Returns a copy of the value. - * - * @return Copy of the incremented underlying page number. - */ - auto operator++(int) -> virtual_page; - - /** - * @brief Pre increment operator. Returns a reference to the changed value. - * - * @return Reference to the incremented underlying page number. - */ - auto operator++() -> virtual_page &; - - /** - * @brief Defaulted equals operator. - */ - auto operator==(virtual_page const & other) const -> bool = default; - - /** - * @brief Defaulted three-way comparsion operator. - */ - auto operator<=>(virtual_page const & other) const -> std::partial_ordering = default; - - std::size_t page_number = - {}; ///< Index number of the current virtual page, used to distinguish it from other pages. - }; - - using page_container = stl::container>; - -} // namespace teachos::arch::memory::paging - -#endif // TEACHOS_ARCH_X86_64_MEMORY_PAGING_VIRTUAL_PAGE_HPP diff --git a/arch/x86_64/pre/src/memory/allocator/area_frame_allocator.cpp b/arch/x86_64/pre/src/memory/allocator/area_frame_allocator.cpp deleted file mode 100644 index 3105023..0000000 --- a/arch/x86_64/pre/src/memory/allocator/area_frame_allocator.cpp +++ /dev/null @@ -1,88 +0,0 @@ -#include "arch/memory/allocator/area_frame_allocator.hpp" - -#include "arch/exception_handling/assert.hpp" - -#include -#include -#include - -namespace teachos::arch::memory::allocator -{ - area_frame_allocator::area_frame_allocator(multiboot::memory_information const & mem_info) - : next_free_frame() - , current_area(std::nullopt) - , memory_areas(mem_info.areas) - , kernel_start(physical_frame::containing_address(mem_info.kernel_start)) - , kernel_end(physical_frame::containing_address(mem_info.kernel_end)) - , multiboot_start(physical_frame::containing_address(mem_info.multiboot_start)) - , multiboot_end(physical_frame::containing_address(mem_info.multiboot_end)) - { - choose_next_area(); - } - - auto area_frame_allocator::choose_next_area() -> void - { - current_area = std::nullopt; - auto next_area_with_free_frames = memory_areas | std::views::filter([this](auto const & area) { - auto address = area.base_address + area.area_length - 1; - return physical_frame::containing_address(address) >= next_free_frame; - }); - - auto const lowest_area_with_free_frames = std::ranges::min_element( - next_area_with_free_frames, [](auto const & a, auto const & b) { return a.base_address < b.base_address; }); - - if (lowest_area_with_free_frames != next_area_with_free_frames.end()) - { - current_area = *lowest_area_with_free_frames; - // Update the `next_free_frame` according to the new memory area - auto const start_frame = physical_frame::containing_address(current_area.value().base_address); - if (next_free_frame < start_frame) - { - next_free_frame = start_frame; - } - } - } - - auto area_frame_allocator::allocate_frame() -> std::optional - { - // Only try to allocate memory if current_area is not null, because - // the current_area is null if there is no more available memory. - if (!current_area.has_value()) - { - return std::nullopt; - } - - auto const address = current_area.value().base_address + current_area.value().area_length - 1; - physical_frame current_area_last_frame = physical_frame::containing_address(address); - - if (next_free_frame > current_area_last_frame) - { - // All frames of current area are used, switch to next area. - choose_next_area(); - } - else if (next_free_frame >= kernel_start && next_free_frame <= kernel_end) - { - // `physical_frame` is used by the kernel or multiboot information structure. - next_free_frame = allocator::physical_frame{kernel_end.frame_number + 1}; - } - else if (next_free_frame >= multiboot_start && next_free_frame <= multiboot_end) - { - // `physical_frame` is used by the kernel or multiboot information structure. - next_free_frame = allocator::physical_frame{multiboot_end.frame_number + 1}; - } - else - { - // Frame is unused, increment `next_free_frame` and return it. - next_free_frame.frame_number += 1; - return next_free_frame; - } - - // `physical_frame` was not valid, try it again with the updated `next_free_frame`. - return allocate_frame(); - } - - auto area_frame_allocator::deallocate_frame(physical_frame const & physical_frame) -> void - { - (void)physical_frame; - } -} // namespace teachos::arch::memory::allocator diff --git a/arch/x86_64/pre/src/memory/allocator/tiny_frame_allocator.cpp b/arch/x86_64/pre/src/memory/allocator/tiny_frame_allocator.cpp deleted file mode 100644 index 3cdf9c7..0000000 --- a/arch/x86_64/pre/src/memory/allocator/tiny_frame_allocator.cpp +++ /dev/null @@ -1,34 +0,0 @@ -#include "arch/memory/allocator/tiny_frame_allocator.hpp" - -#include "arch/exception_handling/panic.hpp" - -namespace teachos::arch::memory::allocator -{ - auto tiny_frame_allocator::allocate_frame() -> std::optional - { - for (auto & frame_option : frames) - { - if (frame_option.has_value()) - { - auto value = frame_option; - frame_option.reset(); - return value; - } - } - return std::nullopt; - } - - auto tiny_frame_allocator::deallocate_frame(physical_frame const & physical_frame) -> void - { - for (auto & frame_option : frames) - { - if (!frame_option.has_value()) - { - frame_option.emplace(physical_frame); - return; - } - } - exception_handling::panic( - "[Tiny Frame Allocator] Attempted to deallocate more than the 3 frames, that can be held"); - } -} // namespace teachos::arch::memory::allocator diff --git a/arch/x86_64/pre/src/memory/main.cpp b/arch/x86_64/pre/src/memory/main.cpp deleted file mode 100644 index b5980db..0000000 --- a/arch/x86_64/pre/src/memory/main.cpp +++ /dev/null @@ -1,77 +0,0 @@ -#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/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 - -namespace teachos::arch::memory -{ - namespace - { - std::optional static frame_allocator; - - auto create_frame_allocator(multiboot::memory_information const & memory_information) - -> allocator::area_frame_allocator & - { - 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 - { - bool static has_been_called = false; - arch::exception_handling::assert(!has_been_called, - "[Initialization] Memory management has already been initialized"); - has_been_called = true; - - auto const memory_information = multiboot::read_multiboot2(); - decltype(auto) allocator = create_frame_allocator(memory_information); - - 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); - kernel.remap_kernel(); - video::vga::text::write("Kernel remapping successful", video::vga::text::common_attributes::green_on_black); - video::vga::text::newline(); - - 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(); - } -} // namespace teachos::arch::memory diff --git a/arch/x86_64/pre/src/memory/multiboot/elf_symbols_section.cpp b/arch/x86_64/pre/src/memory/multiboot/elf_symbols_section.cpp deleted file mode 100644 index 3105120..0000000 --- a/arch/x86_64/pre/src/memory/multiboot/elf_symbols_section.cpp +++ /dev/null @@ -1,16 +0,0 @@ -#include "arch/memory/multiboot/elf_symbols_section.hpp" - -namespace teachos::arch::memory::multiboot -{ - auto elf_section_flags::contains_flags(std::bitset<64U> other) const -> bool - { - return (flags & other) == other; - } - - auto elf_section_header::is_null() const -> bool - { - return name_table_index == 0U && type == elf_section_type::INACTIVE && flags == elf_section_flags(0U) && - physical_address == 0U && file_offset == 0U && additional_information == 0U && address_alignment == 0U && - fixed_table_entry_size == 0U; - } -} // namespace teachos::arch::memory::multiboot diff --git a/arch/x86_64/pre/src/memory/multiboot/reader.cpp b/arch/x86_64/pre/src/memory/multiboot/reader.cpp deleted file mode 100644 index b05e6b3..0000000 --- a/arch/x86_64/pre/src/memory/multiboot/reader.cpp +++ /dev/null @@ -1,135 +0,0 @@ -#include "arch/memory/multiboot/reader.hpp" - -#include "arch/boot/pointers.hpp" -#include "arch/exception_handling/assert.hpp" -#include "multiboot2/information.hpp" -// #include "arch/memory/multiboot/elf_symbols_section.hpp" -// #include "arch/memory/multiboot/info.hpp" - -#include -#include - -// namespace teachos::arch::memory::multiboot -// { -// namespace -// { -// template -// requires std::is_pointer::value -// auto align_to_8_byte_boundary(T ptr, uint32_t size) -> T -// { -// return reinterpret_cast(reinterpret_cast(ptr) + ((size + 7) & ~7)); -// } - -// auto process_memory_map(memory_map_header * mminfo) -> memory_area_container -// { -// auto const expected_entry_size = mminfo->entry_size; -// auto constexpr actual_entry_size = sizeof(memory_area); -// exception_handling::assert(expected_entry_size == actual_entry_size, -// "[Multiboot Reader] Unexpected memory area entry size"); - -// auto const total_size = mminfo->info.size; -// auto const total_entries_size = total_size - sizeof(memory_map_header) + actual_entry_size; -// auto const number_of_entries = total_entries_size / actual_entry_size; - -// auto const begin = memory_area_container::iterator{&mminfo->entries}; -// auto const end = begin + number_of_entries; -// return memory_area_container{begin, end}; -// } - -// auto process_elf_sections(elf_symbols_section_header * symbol, std::size_t & kernel_start, std::size_t & -// kernel_end) -// -> elf_section_header_container -// { -// auto const expected_entry_size = symbol->entry_size; -// auto constexpr actual_entry_size = sizeof(elf_section_header); -// exception_handling::assert(expected_entry_size == actual_entry_size, -// "[Multiboot Reader] Unexpected elf section header entry size"); - -// auto const expected_total_size = symbol->info.size; -// auto const actual_total_entry_size = actual_entry_size * symbol->number_of_sections; -// auto constexpr actual_total_section_size = sizeof(elf_symbols_section_header) - sizeof(uint32_t); -// auto const actual_total_size = actual_total_entry_size + actual_total_section_size; -// exception_handling::assert(expected_total_size == actual_total_size, -// "[Multiboot Reader] Unexpected elf symbols section header total size"); - -// auto const begin = elf_section_header_container::iterator{reinterpret_cast(&symbol->end)}; auto const end = begin + symbol->number_of_sections; -// exception_handling::assert(begin->is_null(), -// "[Multiboot Reader] Elf symbols section not starting with SHT_NULL section"); - -// elf_section_header_container sections{begin, end}; - -// auto allocated_sections = sections | std::views::filter([](auto const & section) { -// return section.flags.contains_flags(elf_section_flags::OCCUPIES_MEMORY); -// }); - -// auto const elf_section_with_lowest_physical_address = std::ranges::min_element( -// allocated_sections, [](auto const & a, auto const & b) { return a.physical_address < b.physical_address; -// }); - -// auto const elf_section_with_highest_physical_address = -// std::ranges::max_element(allocated_sections, [](auto const & a, auto const & b) { -// auto a_physical_address_end = a.physical_address + a.section_size; -// auto b_physical_address_end = b.physical_address + b.section_size; -// return a_physical_address_end < b_physical_address_end; -// }); - -// auto const symbol_table_section_count = std::ranges::count_if(sections, [](auto const & section) { -// return section.type == elf_section_type::DYNAMIC_SYMBOL_TABLE || section.type == -// elf_section_type::SYMBOL_TABLE; -// }); -// auto const dynamic_section_count = std::ranges::count_if( -// sections, [](auto const & section) { return section.type == elf_section_type::DYNAMIC; }); - -// exception_handling::assert( -// symbol_table_section_count == 1U, -// "[Multiboot Reader] ELF Specifications allows only (1) symbol table section, but got more"); -// exception_handling::assert( -// dynamic_section_count <= 1U, -// "[Multiboot Reader] ELF Specifications allows only (1) or less dynamic sections, but got more"); - -// auto const lowest_elf_section = *elf_section_with_lowest_physical_address; -// kernel_start = lowest_elf_section.physical_address; - -// auto const highest_elf_section = *elf_section_with_highest_physical_address; -// kernel_end = highest_elf_section.physical_address + highest_elf_section.section_size; - -// return sections; -// } -// } // namespace - -// auto read_multiboot2() -> memory_information -// { -// memory_information mem_info{UINT64_MAX, -// 0U, -// elf_section_header_container{}, -// boot::multiboot_information_pointer, -// 0U, -// memory_area_container{}}; - -// auto const multiboot_information_pointer = reinterpret_cast(boot::multiboot_information_pointer); -// auto const multiboot_tag = &multiboot_information_pointer->tags; -// mem_info.multiboot_end = mem_info.multiboot_start + multiboot_information_pointer->total_size; - -// for (auto tag = multiboot_tag; tag->type != tag_type::END; tag = align_to_8_byte_boundary(tag, tag->size)) -// { -// switch (tag->type) -// { -// case tag_type::ELF_SECTIONS: { -// auto const symbol = reinterpret_cast(tag); -// mem_info.sections = process_elf_sections(symbol, mem_info.kernel_start, mem_info.kernel_end); -// break; -// } -// case tag_type::MEMORY_MAP: { -// auto const mminfo = reinterpret_cast(tag); -// mem_info.areas = process_memory_map(mminfo); -// break; -// } -// default: -// // All other cases are not important and can be ignored. -// break; -// } -// } -// return mem_info; -// } -// } // namespace teachos::arch::memory::multiboot diff --git a/arch/x86_64/pre/src/memory/paging/active_page_table.cpp b/arch/x86_64/pre/src/memory/paging/active_page_table.cpp deleted file mode 100644 index 930588d..0000000 --- a/arch/x86_64/pre/src/memory/paging/active_page_table.cpp +++ /dev/null @@ -1,101 +0,0 @@ -#include "arch/memory/paging/active_page_table.hpp" - -namespace teachos::arch::memory::paging -{ - namespace - { - constexpr paging::virtual_address PAGE_TABLE_LEVEL_4_ADDRESS = 0xffff'ffff'ffff'f000; - } - - auto active_page_table::create_or_get() -> active_page_table & - { - page_table_handle static active_handle{reinterpret_cast(PAGE_TABLE_LEVEL_4_ADDRESS), - page_table_handle::LEVEL4}; - active_page_table static active_page{active_handle}; - return active_page; - } - - auto active_page_table::operator[](std::size_t index) -> entry & - { - return active_handle[index]; - } - - auto active_page_table::translate_address(virtual_address address) -> std::optional - { - auto const offset = address % allocator::PAGE_FRAME_SIZE; - auto const page = virtual_page::containing_address(address); - auto const frame = translate_page(page); - - if (frame.has_value()) - { - return frame.value().frame_number * allocator::PAGE_FRAME_SIZE + offset; - } - - return std::nullopt; - } - - auto active_page_table::translate_page(virtual_page page) -> std::optional - { - auto current_handle = active_handle; - - for (auto level = page_table_handle::LEVEL4; level != page_table_handle::LEVEL1; --level) - { - auto const next_handle = current_handle.next_table(page.get_level_index(level)); - // If the next table method failed then it is highly likely that it was a huge page and we therefore have to - // parse the table differently. Therefore, we attempt to parse it using the method required by huge pages. - if (!next_handle.has_value()) - { - return translate_huge_page(page); - } - current_handle = next_handle.value(); - } - - auto const level1_index = page.get_level_index(page_table_handle::LEVEL1); - auto const level1_entry = current_handle[level1_index]; - return level1_entry.calculate_pointed_to_frame(); - } - - auto active_page_table::translate_huge_page(virtual_page page) -> std::optional - { - auto current_handle = active_handle; - auto level3_handle = current_handle.next_table(page.get_level_index(page_table_handle::LEVEL4)); - - if (!level3_handle.has_value()) - { - return std::nullopt; - } - - auto const level3_entry = level3_handle.value()[page.get_level_index(page_table_handle::LEVEL3)]; - auto const level3_frame = level3_entry.calculate_pointed_to_frame(); - if (level3_frame.has_value() && level3_entry.contains_flags(entry::HUGE_PAGE)) - { - exception_handling::assert( - level3_frame.value().frame_number % (PAGE_TABLE_ENTRY_COUNT * PAGE_TABLE_ENTRY_COUNT) == 0U, - "[Page Mapper] Physical address must be 1 GiB aligned"); - return allocator::physical_frame{level3_frame.value().frame_number + - page.get_level_index(page_table_handle::LEVEL2) * PAGE_TABLE_ENTRY_COUNT + - page.get_level_index(page_table_handle::LEVEL1)}; - } - - auto level2_handle = level3_handle.value().next_table(page.get_level_index(page_table_handle::LEVEL3)); - if (level2_handle.has_value()) - { - auto const level2_entry = level2_handle.value()[page.get_level_index(page_table_handle::LEVEL2)]; - auto const level2_frame = level2_entry.calculate_pointed_to_frame(); - if (level2_frame.has_value() && level2_entry.contains_flags(entry::HUGE_PAGE)) - { - exception_handling::assert(level2_frame.value().frame_number % PAGE_TABLE_ENTRY_COUNT == 0U, - "[Page Mapper] Physical address must be 2 MiB aligned"); - return allocator::physical_frame{level2_frame.value().frame_number + - page.get_level_index(page_table_handle::LEVEL1)}; - } - } - return std::nullopt; - } - - active_page_table::active_page_table(page_table_handle active_handle) - : active_handle(active_handle) - { - // Nothing to do - } -} // namespace teachos::arch::memory::paging diff --git a/arch/x86_64/pre/src/memory/paging/inactive_page_table.cpp b/arch/x86_64/pre/src/memory/paging/inactive_page_table.cpp deleted file mode 100644 index 4e0610e..0000000 --- a/arch/x86_64/pre/src/memory/paging/inactive_page_table.cpp +++ /dev/null @@ -1,20 +0,0 @@ -#include "arch/memory/paging/inactive_page_table.hpp" - -namespace teachos::arch::memory::paging -{ - inactive_page_table::inactive_page_table(allocator::physical_frame frame) - : page_table_level_4_frame{frame} - { - // Nothing to do - } - - inactive_page_table::inactive_page_table(allocator::physical_frame frame, active_page_table & active_page_table, - temporary_page & temporary_page) - : page_table_level_4_frame{frame} - { - auto table = temporary_page.map_table_frame(page_table_level_4_frame, active_page_table); - table.zero_entries(); - table[511].set_entry(page_table_level_4_frame, entry::PRESENT | entry::WRITABLE); - temporary_page.unmap_page(active_page_table); - } -} // namespace teachos::arch::memory::paging diff --git a/arch/x86_64/pre/src/memory/paging/page_entry.cpp b/arch/x86_64/pre/src/memory/paging/page_entry.cpp deleted file mode 100644 index ec45068..0000000 --- a/arch/x86_64/pre/src/memory/paging/page_entry.cpp +++ /dev/null @@ -1,78 +0,0 @@ -#include "arch/memory/paging/page_entry.hpp" - -#include "arch/exception_handling/assert.hpp" - -namespace teachos::arch::memory::paging -{ - namespace - { - constexpr std::size_t PHYSICAL_ADDRESS_MASK = 0x000f'ffff'ffff'f000; - } // namespace - - entry::entry(uint64_t flags) - : flags(flags) - { - // Nothing to do. - } - - entry::entry(multiboot::elf_section_flags elf_flags) - { - if (elf_flags.contains_flags(multiboot::elf_section_flags::OCCUPIES_MEMORY)) - { - 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; - } - } - - auto entry::is_unused() const -> bool - { - return flags == 0U; - } - - 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 - { - if (contains_flags(PRESENT)) - { - auto const address = flags.to_ulong() & PHYSICAL_ADDRESS_MASK; - return allocator::physical_frame::containing_address(address); - } - return std::nullopt; - } - - auto entry::contains_flags(std::bitset<64U> other) const -> bool - { - return (flags & other) == other; - } - - auto entry::set_entry(allocator::physical_frame frame, std::bitset<64U> additional_flags) -> void - { - 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(); - } - - auto entry::get_flags() const -> std::bitset<64U> - { - return flags.to_ulong() & ~PHYSICAL_ADDRESS_MASK; - } -} // namespace teachos::arch::memory::paging diff --git a/arch/x86_64/pre/src/memory/paging/page_table.cpp b/arch/x86_64/pre/src/memory/paging/page_table.cpp deleted file mode 100644 index e79c3e5..0000000 --- a/arch/x86_64/pre/src/memory/paging/page_table.cpp +++ /dev/null @@ -1,140 +0,0 @@ -#include "arch/memory/paging/page_table.hpp" - -#include -#include -#include - -/* - * This is a linker variable reference. This referenc cannot reside inside a namespace, because in - * that case the compiler would try to find arch::memory::paging::_end_of_image inside the ELF file. - */ -extern char _end_of_image; - -namespace teachos::arch::memory::paging -{ - /** - * @brief A Page table containing 512 entries. - */ - struct page_table - { - auto zero_entries() -> void; - - auto is_empty() const -> bool; - - auto next_table(std::size_t table_index) const -> std::optional; - - auto operator[](std::size_t index) -> entry &; - - auto operator[](std::size_t index) const -> entry const &; - - private: - /** - * @brief Calculates the address of the next page table level for the given table index. - * - * @note The next page table address is only valid if the corresponding entry is present and not a huge page. - * Meaning we use an index into a Level 4 page table to get the according Level 3 page table address. - * - * @param table_index Index of this page table in the page table one level higher. - * @return An optional of the address of the next page table or null. - */ - auto next_table_address(std::size_t table_index) const -> std::optional; - - std::array entries = - {}; ///< Entries containing addresses to page tables of a level below or - ///< actual virtual addresses for the level 1 page table. - }; - - auto page_table::zero_entries() -> void - { - std::ranges::for_each(entries, [](auto & entry) { entry.set_unused(); }); - } - - auto page_table::is_empty() const -> bool - { - return std::all_of(entries.begin(), entries.end(), [](entry const & entry) { return entry.is_unused(); }); - } - - auto page_table::next_table(std::size_t table_index) const -> std::optional - { - auto const address = next_table_address(table_index); - if (address.has_value()) - { - return reinterpret_cast(address.value()); - } - return std::nullopt; - } - - auto page_table::operator[](std::size_t index) -> entry & - { - exception_handling::assert(index < PAGE_TABLE_ENTRY_COUNT, "[Page Table] Index out of bounds"); - return entries[index]; - } - - auto page_table::operator[](std::size_t index) const -> entry const & - { - exception_handling::assert(index < PAGE_TABLE_ENTRY_COUNT, "[Page Table] Index out of bounds"); - return entries[index]; - } - - auto page_table::next_table_address(std::size_t table_index) const -> std::optional - { - auto const entry = this->operator[](table_index); - - if (entry.contains_flags(entry::PRESENT) && !entry.contains_flags(entry::HUGE_PAGE)) - { - auto const table_address = reinterpret_cast(this); - return ((table_address << 9) | (table_index << 12)); - } - return std::nullopt; - } - - page_table_handle::page_table_handle(page_table * table, page_table_handle::level table_level) - : table(table) - , table_level(table_level) - { - exception_handling::assert(table != nullptr, - "[Page Table] Attempted to pass nullptr as table to page table table method"); - } - - auto page_table_handle::zero_entries() -> void - { - table->zero_entries(); - } - - auto page_table_handle::is_empty() const -> bool - { - return table->is_empty(); - } - - auto page_table_handle::next_table(std::size_t table_index) const -> std::optional - { - exception_handling::assert(table_level != page_table_handle::LEVEL1, - "[Page Table] Attempted to call next_table on level 1 page table"); - auto const next_table = table->next_table(table_index); - if (next_table.has_value()) - { - auto const new_level = static_cast(table_level - 1); - return page_table_handle{next_table.value(), new_level}; - } - return std::nullopt; - } - - auto page_table_handle::get_level() const -> page_table_handle::level - { - return table_level; - } - - auto page_table_handle::operator[](std::size_t index) -> entry & - { - return table->operator[](index); - } - - auto operator--(page_table_handle::level & value) -> page_table_handle::level & - { - exception_handling::assert(value != page_table_handle::LEVEL1, - "[Page table] Attempted to decrement enum to value outside of range"); - auto new_value = static_cast::type>(value); - value = static_cast(--new_value); - return value; - } -} // namespace teachos::arch::memory::paging diff --git a/arch/x86_64/pre/src/memory/paging/temporary_page.cpp b/arch/x86_64/pre/src/memory/paging/temporary_page.cpp deleted file mode 100644 index 8e73523..0000000 --- a/arch/x86_64/pre/src/memory/paging/temporary_page.cpp +++ /dev/null @@ -1,29 +0,0 @@ -#include "arch/memory/paging/temporary_page.hpp" - -#include "arch/memory/paging/page_entry.hpp" - -namespace teachos::arch::memory::paging -{ - auto temporary_page::map_table_frame(allocator::physical_frame frame, active_page_table & active_table) - -> page_table_handle - { - page_table_handle handle{reinterpret_cast(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 - { - exception_handling::assert(!active_table.translate_page(page).has_value(), - "[Temporary page] Page is already mapped"); - - active_table.map_page_to_frame(allocator, page, frame, entry::WRITABLE); - return page.start_address(); - } - - auto temporary_page::unmap_page(active_page_table & active_table) -> void - { - active_table.unmap_page(allocator, page); - } -} // namespace teachos::arch::memory::paging diff --git a/arch/x86_64/pre/src/memory/paging/virtual_page.cpp b/arch/x86_64/pre/src/memory/paging/virtual_page.cpp deleted file mode 100644 index 8d34918..0000000 --- a/arch/x86_64/pre/src/memory/paging/virtual_page.cpp +++ /dev/null @@ -1,36 +0,0 @@ -#include "arch/memory/paging/virtual_page.hpp" - -#include "arch/exception_handling/assert.hpp" - -namespace teachos::arch::memory::paging -{ - auto virtual_page::containing_address(virtual_address address) -> virtual_page - { - exception_handling::assert(address < 0x0000'8000'0000'0000 || address >= 0xffff'8000'0000'0000, - "[Virtual Page] Attempted to create virtual page from invalid address"); - return virtual_page{address / allocator::PAGE_FRAME_SIZE}; - } - - auto virtual_page::start_address() const -> virtual_address - { - return page_number * allocator::PAGE_FRAME_SIZE; - } - - auto virtual_page::get_level_index(page_table_handle::level level) const -> size_t - { - return (page_number >> (level * 9U)) & 0x1FF; - } - - auto virtual_page::operator++(int) -> virtual_page - { - virtual_page const old_value = *this; - ++page_number; - return old_value; - } - - auto virtual_page::operator++() -> virtual_page & - { - ++page_number; - return *this; - } -} // namespace teachos::arch::memory::paging -- cgit v1.2.3