From 3a2efa4ebc6b07a2304416262d5032a32dcddd8b Mon Sep 17 00:00:00 2001 From: Felix Morgner Date: Mon, 14 Jul 2025 13:06:17 +0000 Subject: x86_64: fix syscall error code reading --- arch/x86_64/src/context_switching/syscall/main.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'arch/x86_64') diff --git a/arch/x86_64/src/context_switching/syscall/main.cpp b/arch/x86_64/src/context_switching/syscall/main.cpp index e291c10..b4ab468 100644 --- a/arch/x86_64/src/context_switching/syscall/main.cpp +++ b/arch/x86_64/src/context_switching/syscall/main.cpp @@ -27,7 +27,7 @@ namespace teachos::arch::context_switching::syscall asm volatile("mov %%r9, %[output]" : [output] "=m"(values.arg_5)); error error_code{}; - asm volatile("mov %%rax, %[output]" : [output] "=m"(error_code)); + asm volatile("mov %%al, %[output]" : [output] "=m"(error_code)); return {error_code, values}; } -- cgit v1.2.3 From d275ced60d63b1618169d755d228a860dfd23237 Mon Sep 17 00:00:00 2001 From: Felix Morgner Date: Mon, 14 Jul 2025 13:06:47 +0000 Subject: memory: adapt to changes memory layout --- arch/x86_64/include/arch/memory/paging/kernel_mapper.hpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'arch/x86_64') diff --git a/arch/x86_64/include/arch/memory/paging/kernel_mapper.hpp b/arch/x86_64/include/arch/memory/paging/kernel_mapper.hpp index 81ac0cb..3afb54b 100644 --- a/arch/x86_64/include/arch/memory/paging/kernel_mapper.hpp +++ b/arch/x86_64/include/arch/memory/paging/kernel_mapper.hpp @@ -127,8 +127,8 @@ namespace teachos::arch::memory::paging 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) - 0x218000, // .user_text (Contains the actual user code executed) - 0x21F000, // .user_data (Contains static user variables) + 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) -- cgit v1.2.3 From 22fbbf849497c32f5b237ab70e9ed8aef63e54cf Mon Sep 17 00:00:00 2001 From: Felix Morgner Date: Mon, 14 Jul 2025 15:21:32 +0000 Subject: libs: extract multiboot library --- arch/x86_64/CMakeLists.txt | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'arch/x86_64') diff --git a/arch/x86_64/CMakeLists.txt b/arch/x86_64/CMakeLists.txt index 57b3a60..bd06ea7 100644 --- a/arch/x86_64/CMakeLists.txt +++ b/arch/x86_64/CMakeLists.txt @@ -69,6 +69,10 @@ target_sources("_memory" PRIVATE "src/memory/heap/global_heap_allocator.cpp" ) +target_link_libraries("_memory" PUBLIC + "multiboot2::multiboot2" +) + #[============================================================================[ # The STL Library #]============================================================================] -- cgit v1.2.3 From e7eedd234954509f4f5ec52b2d62cbc4a1723936 Mon Sep 17 00:00:00 2001 From: Felix Morgner Date: Mon, 14 Jul 2025 15:39:09 +0000 Subject: libs: begin extraction of kernel std --- arch/x86_64/CMakeLists.txt | 13 +- .../arch/memory/heap/linked_list_allocator.hpp | 5 +- .../arch/memory/heap/user_heap_allocator.hpp | 5 +- .../arch/memory/multiboot/elf_symbols_section.hpp | 41 ++-- arch/x86_64/include/arch/memory/multiboot/info.hpp | 64 ------ .../include/arch/memory/multiboot/memory_map.hpp | 53 ----- .../include/arch/memory/multiboot/reader.hpp | 19 +- arch/x86_64/include/arch/stl/mutex.hpp | 60 ----- .../src/memory/heap/linked_list_allocator.cpp | 2 +- arch/x86_64/src/memory/multiboot/reader.cpp | 250 +++++++++++---------- arch/x86_64/src/stl/mutex.cpp | 16 -- 11 files changed, 172 insertions(+), 356 deletions(-) delete mode 100644 arch/x86_64/include/arch/memory/multiboot/info.hpp delete mode 100644 arch/x86_64/include/arch/memory/multiboot/memory_map.hpp delete mode 100644 arch/x86_64/include/arch/stl/mutex.hpp delete mode 100644 arch/x86_64/src/stl/mutex.cpp (limited to 'arch/x86_64') diff --git a/arch/x86_64/CMakeLists.txt b/arch/x86_64/CMakeLists.txt index bd06ea7..511fe43 100644 --- a/arch/x86_64/CMakeLists.txt +++ b/arch/x86_64/CMakeLists.txt @@ -71,14 +71,7 @@ target_sources("_memory" PRIVATE target_link_libraries("_memory" PUBLIC "multiboot2::multiboot2" -) - -#[============================================================================[ -# The STL Library -#]============================================================================] - -target_sources("_stl" PRIVATE - "src/stl/mutex.cpp" + "libs::kstd" ) #[============================================================================[ @@ -115,6 +108,10 @@ target_sources("_context" PRIVATE "src/context_switching/interrupt_descriptor_table/segment_selector.cpp" ) +target_link_libraries("_context" PUBLIC + "libs::kstd" +) + #[============================================================================[ # The Interrupt Handlers #]============================================================================] diff --git a/arch/x86_64/include/arch/memory/heap/linked_list_allocator.hpp b/arch/x86_64/include/arch/memory/heap/linked_list_allocator.hpp index 582b4af..bbbad19 100644 --- a/arch/x86_64/include/arch/memory/heap/linked_list_allocator.hpp +++ b/arch/x86_64/include/arch/memory/heap/linked_list_allocator.hpp @@ -3,7 +3,8 @@ #include "arch/memory/heap/heap_allocator.hpp" #include "arch/memory/heap/memory_block.hpp" -#include "arch/stl/mutex.hpp" + +#include namespace teachos::arch::memory::heap { @@ -112,7 +113,7 @@ namespace teachos::arch::memory::heap std::size_t size) -> void; memory_block * first; ///< First free entry in our memory. - stl::mutex mutex; ///< Mutex to ensure only one thread calls allocate or deallocate at once. + kstd::mutex mutex; ///< Mutex to ensure only one thread calls allocate or deallocate at once. }; } // namespace teachos::arch::memory::heap diff --git a/arch/x86_64/include/arch/memory/heap/user_heap_allocator.hpp b/arch/x86_64/include/arch/memory/heap/user_heap_allocator.hpp index 6b1b7bb..3b47f15 100644 --- a/arch/x86_64/include/arch/memory/heap/user_heap_allocator.hpp +++ b/arch/x86_64/include/arch/memory/heap/user_heap_allocator.hpp @@ -2,8 +2,9 @@ #define TEACHOS_ARCH_X86_64_MEMORY_HEAP_USER_HEAP_ALLOCATOR_HPP #include "arch/memory/heap/memory_block.hpp" -#include "arch/stl/mutex.hpp" +// #include +#include #include namespace teachos::arch::memory::heap @@ -141,7 +142,7 @@ namespace teachos::arch::memory::heap std::size_t size) -> void; memory_block * first = {}; ///< First free entry in our memory. - stl::mutex mutex = {}; ///< Mutex to ensure only one thread calls allocate or deallocate at once. + kstd::mutex mutex = {}; ///< Mutex to ensure only one thread calls allocate or deallocate at once. }; } // namespace teachos::arch::memory::heap diff --git a/arch/x86_64/include/arch/memory/multiboot/elf_symbols_section.hpp b/arch/x86_64/include/arch/memory/multiboot/elf_symbols_section.hpp index 0a25ca9..348c159 100644 --- a/arch/x86_64/include/arch/memory/multiboot/elf_symbols_section.hpp +++ b/arch/x86_64/include/arch/memory/multiboot/elf_symbols_section.hpp @@ -1,9 +1,9 @@ #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 "arch/memory/multiboot/info.hpp" +// #include "arch/stl/container.hpp" +// #include "arch/stl/contiguous_pointer_iterator.hpp" #include #include @@ -145,24 +145,25 @@ namespace teachos::arch::memory::multiboot 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. - }; + // /** + // * @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>; + // using elf_section_header_container = stl::container>; } // namespace teachos::arch::memory::multiboot diff --git a/arch/x86_64/include/arch/memory/multiboot/info.hpp b/arch/x86_64/include/arch/memory/multiboot/info.hpp deleted file mode 100644 index a9abf12..0000000 --- a/arch/x86_64/include/arch/memory/multiboot/info.hpp +++ /dev/null @@ -1,64 +0,0 @@ -#ifndef TEACHOS_ARCH_X86_64_MEMORY_MULTIBOOT_INFO_HPP -#define TEACHOS_ARCH_X86_64_MEMORY_MULTIBOOT_INFO_HPP - -#include - -namespace teachos::arch::memory::multiboot -{ - /** - * @brief Defines all possible types a multiboot2 tag structure can have. - * - * @note See - * https://github.com/rhboot/grub2/blob/fedora-39/include/multiboot2.h for more information on the structure of the - * tag headers and see https://github.com/rhboot/grub2/blob/fedora-39/include/multiboot.h for more information on the - * actual header contents and their following data. - */ - enum struct tag_type : uint32_t - { - END, ///< Signals final tag for the multiboot2 information structure. - CMDLINE, ///< Contains the command line string. - BOOT_LOADER_NAME, ///< Contains the name of the boot loader booting the kernel. - MODULE, ///< Indicates the boot module which was loaded along the kernel image. - BASIC_MEMORY_INFO, ///< Contains the amount of lower (0MB start address) and upper memory (1MB start address). - BOOTDEV, ///< Indicates which BIOS disk device the hoot loader has loaded the OS image from. - MEMORY_MAP, ///< Describes the memory layout of the system with individual areas and their flags. - VBE_INFO, ///< Includes information to access and utilize the device GPU. - FRAMEBUFFER, ///< VBE framebuffer information. - ELF_SECTIONS, ///< Includes list of all section headers from the loaded ELF kernel. - APM_INFO, ///< Advanced Power Management information. - EFI32, ///< EFI 32 bit system table pointer. - EFI64, ///< EFI 64 bit system table pointer. - SMBIOS, ///< Contains copy of all Sytem Management BIOS tables. - ACPI_OLD, ///< Contains copy of RSDP as defined per ACPI1.0 specification. - ACPI_NEW, ///< Contains copy of RSDP as defined per ACPI2.0 or later specification. - NETWORK, ///< Contains network information specified specified as DHCP. - EFI_MEMORY_MAP, ///< Contains EFI memory map. - EFI_BS_NOT_TERMINATED, ///< Indicated ExitBootServies wasn't called. - EFI32_IMAGE_HANDLE, ///< EFI 32 bit image handle pointer. - EFI64_IMAGE_HANDLE, ///< EFI 64 bit imae handle pointer. - LOAD_BASE_ADDRESS ///< Contains image load base physical address. - }; - - /** - * @brief Basic structure that every entry in the multi_boot_tag array of the multi_boot_info struct has to begin - * with. - */ - struct tag - { - tag_type type; ///< Specific type of this multi_boot_tag entry, used to differentiate handling. - uint32_t size; ///< Total size of this multi_boot_tag entry with all fields of the actual type. - }; - - /** - * @brief Basic structure the multiboot_information_pointer points too and which contains all information of - * multiboot2 in the tags array of different types. The start as well as the content has to be 8 byte aligned. - */ - struct info_header - { - uint32_t total_size; ///< Total size of all multiboot::tags and their data. - alignas(8) struct tag tags; ///< Specific tags. - }; - -} // namespace teachos::arch::memory::multiboot - -#endif // TEACHOS_ARCH_X86_64_MEMORY_MULTIBOOT_INFO_HPP diff --git a/arch/x86_64/include/arch/memory/multiboot/memory_map.hpp b/arch/x86_64/include/arch/memory/multiboot/memory_map.hpp deleted file mode 100644 index 68394c8..0000000 --- a/arch/x86_64/include/arch/memory/multiboot/memory_map.hpp +++ /dev/null @@ -1,53 +0,0 @@ -#ifndef TEACHOS_ARCH_X86_64_MEMORY_MULTIBOOT_MEMORY_MAP_HPP -#define TEACHOS_ARCH_X86_64_MEMORY_MULTIBOOT_MEMORY_MAP_HPP - -#include "arch/memory/multiboot/info.hpp" -#include "arch/stl/container.hpp" -#include "arch/stl/contiguous_pointer_iterator.hpp" - -#include - -namespace teachos::arch::memory::multiboot -{ - /** - * @brief Defines all memory area types possible that the memory region can be in. - */ - enum struct memory_area_type : uint32_t - { - AVAILABLE = 1, ///< Region is available for use by the OS. - RESERVED, ///< Region is reserved by firmware or bootloader and should not be used by OS. - ACPI_AVAILABLE, ///< Region is reclaimable by OS after ACPI event. - RESERVED_HIBERNATION, ///< Region is used for Non-volatile Storage (NVS). - DEFECTIVE ///< Region is defective or unusable. - }; - - /** - * @brief Defines an entry in the entries array of the memory_map struct. - * - * @note Last value needs to be padded, because the size of the entry needs to be - * exactly 24 bytes and not one byte more. - */ - struct memory_area - { - uint64_t base_address; ///< Base address the memory region starts at. - uint64_t area_length; ///< Size of the memory region, added to base_address results in the final address. - alignas(8) memory_area_type type; ///< Specific type of memory the region can contain. - }; - - /** - * @brief Defines an entry in the multi_boot_tag array of the multi_boot_info struct, of type - * multi_boot_tag_type::MEMORY_MAP. - */ - struct memory_map_header - { - tag info; ///< Basic multi_boot_tag information. - uint32_t entry_size; ///< Size of each entry in the memory_area array. Guaranteed multiple of 8. - uint32_t entry_version; ///< Version of the entries, currently 0. - struct memory_area entries; ///< Specific memory regions. - }; - - using memory_area_container = stl::container>; - -} // namespace teachos::arch::memory::multiboot - -#endif // TEACHOS_ARCH_X86_64_MEMORY_MULTIBOOT_MEMORY_MAP_HPP diff --git a/arch/x86_64/include/arch/memory/multiboot/reader.hpp b/arch/x86_64/include/arch/memory/multiboot/reader.hpp index bda0c43..c5464cb 100644 --- a/arch/x86_64/include/arch/memory/multiboot/reader.hpp +++ b/arch/x86_64/include/arch/memory/multiboot/reader.hpp @@ -1,10 +1,14 @@ #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 "arch/memory/multiboot/memory_map.hpp" #include +#include +#include namespace teachos::arch::memory::multiboot { @@ -14,12 +18,13 @@ namespace teachos::arch::memory::multiboot */ 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. - elf_section_header_container sections; ///< 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. - memory_area_container areas; ///< Contains non-owning pointers to all memory areas. + 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. }; /** diff --git a/arch/x86_64/include/arch/stl/mutex.hpp b/arch/x86_64/include/arch/stl/mutex.hpp deleted file mode 100644 index a7d297d..0000000 --- a/arch/x86_64/include/arch/stl/mutex.hpp +++ /dev/null @@ -1,60 +0,0 @@ -#ifndef TEACHOS_ARCH_X86_64_STL_MUTEX_HPP -#define TEACHOS_ARCH_X86_64_STL_MUTEX_HPP - -#include - -namespace teachos::arch::stl -{ - /** - * @brief Custom mutex implementation, that simply wraps an atomic boolean to keep track if the mutex is already in - * use by another thread or not. - */ - struct mutex - { - /** - * @brief Defaulted constructor. - */ - mutex() = default; - - /** - * @brief Defaulted destructor. - */ - ~mutex() = default; - - /** - * @brief Deleted copy constructor. - */ - mutex(const mutex &) = delete; - - /** - * @brief Deleted assignment operator. - */ - mutex & operator=(const mutex &) = delete; - - /** - * @brief Lock the mutex (blocks for as long as it is not available). - */ - [[gnu::section(".stl_text")]] - auto lock() -> void; - - /** - * @brief Try to lock the mutex (non-blocking). - * - * @return True if lock has been acquired and false otherwise. - */ - [[gnu::section(".stl_text")]] - auto try_lock() -> bool; - - /** - * @brief Unlock the mutex. - */ - [[gnu::section(".stl_text")]] - auto unlock() -> void; - - private: - std::atomic locked = {false}; // Atomic boolean to track if mutex is locked or not. - }; - -} // namespace teachos::arch::stl - -#endif // TEACHOS_ARCH_X86_64_STL_MUTEX_HPP diff --git a/arch/x86_64/src/memory/heap/linked_list_allocator.cpp b/arch/x86_64/src/memory/heap/linked_list_allocator.cpp index 63a6111..00ca366 100644 --- a/arch/x86_64/src/memory/heap/linked_list_allocator.cpp +++ b/arch/x86_64/src/memory/heap/linked_list_allocator.cpp @@ -9,7 +9,7 @@ namespace teachos::arch::memory::heap { linked_list_allocator::linked_list_allocator(std::size_t heap_start, std::size_t heap_end) : first(nullptr) - , mutex{stl::mutex{}} + , mutex{kstd::mutex{}} { auto const heap_size = heap_end - heap_start; exception_handling::assert( diff --git a/arch/x86_64/src/memory/multiboot/reader.cpp b/arch/x86_64/src/memory/multiboot/reader.cpp index 2bf5b25..b05e6b3 100644 --- a/arch/x86_64/src/memory/multiboot/reader.cpp +++ b/arch/x86_64/src/memory/multiboot/reader.cpp @@ -2,130 +2,134 @@ #include "arch/boot/pointers.hpp" #include "arch/exception_handling/assert.hpp" -#include "arch/memory/multiboot/elf_symbols_section.hpp" -#include "arch/memory/multiboot/info.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 +// 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/src/stl/mutex.cpp b/arch/x86_64/src/stl/mutex.cpp deleted file mode 100644 index 232a11c..0000000 --- a/arch/x86_64/src/stl/mutex.cpp +++ /dev/null @@ -1,16 +0,0 @@ -#include "arch/stl/mutex.hpp" - -namespace teachos::arch::stl -{ - auto mutex::lock() -> void - { - while (!try_lock()) - { - // Nothing to do - } - } - - auto mutex::try_lock() -> bool { return !locked.exchange(true, std::memory_order_acquire); } - - auto mutex::unlock() -> void { locked.store(false, std::memory_order_release); } -} // namespace teachos::arch::stl -- cgit v1.2.3 From 1b603d1145b9ee10b1b12a0f765bd2bc1ebe2b3c Mon Sep 17 00:00:00 2001 From: Felix Morgner Date: Mon, 14 Jul 2025 15:39:59 +0000 Subject: libs: rename multiboot alias --- arch/x86_64/CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'arch/x86_64') diff --git a/arch/x86_64/CMakeLists.txt b/arch/x86_64/CMakeLists.txt index 511fe43..c8ff216 100644 --- a/arch/x86_64/CMakeLists.txt +++ b/arch/x86_64/CMakeLists.txt @@ -70,8 +70,8 @@ target_sources("_memory" PRIVATE ) target_link_libraries("_memory" PUBLIC - "multiboot2::multiboot2" "libs::kstd" + "libs::multiboot2" ) #[============================================================================[ -- cgit v1.2.3 From ec572bff8150e2f8cd2dc99e053c5e8c8a0b99e3 Mon Sep 17 00:00:00 2001 From: Felix Morgner Date: Mon, 14 Jul 2025 16:25:00 +0000 Subject: arch: prepare interfaces --- arch/x86_64/CMakeLists.txt | 314 +++++++++++++++++++++++++------------------- arch/x86_64/src/boot/boot.s | 2 +- arch/x86_64/src/io.cpp | 22 ++++ arch/x86_64/src/memory.cpp | 62 +++++++++ arch/x86_64/src/system.cpp | 12 ++ 5 files changed, 275 insertions(+), 137 deletions(-) create mode 100644 arch/x86_64/src/io.cpp create mode 100644 arch/x86_64/src/memory.cpp create mode 100644 arch/x86_64/src/system.cpp (limited to 'arch/x86_64') diff --git a/arch/x86_64/CMakeLists.txt b/arch/x86_64/CMakeLists.txt index c8ff216..19bc78c 100644 --- a/arch/x86_64/CMakeLists.txt +++ b/arch/x86_64/CMakeLists.txt @@ -1,156 +1,198 @@ -#[============================================================================[ -# The Kernel Library -#]============================================================================] - -set(TEACHOS_KERNEL_LINKER_SCRIPT "${CMAKE_CURRENT_SOURCE_DIR}/scripts/kernel.ld") -mark_as_advanced(TEACHOS_KERNEL_LINKER_SCRIPT) - -target_sources("_kernel" PRIVATE - "src/kernel/main.cpp" - "src/kernel/cpu/control_register.cpp" - "src/kernel/cpu/gdtr.cpp" - "src/kernel/cpu/idtr.cpp" - "src/kernel/cpu/if.cpp" - "src/kernel/cpu/call.cpp" - "src/kernel/cpu/msr.cpp" - "src/kernel/cpu/segment_register.cpp" - "src/kernel/cpu/tlb.cpp" - "src/kernel/cpu/tr.cpp" -) - -target_link_options("_kernel" PRIVATE - "-T${TEACHOS_KERNEL_LINKER_SCRIPT}" -) - -set_target_properties("_kernel" PROPERTIES - LINK_DEPENDS "${TEACHOS_KERNEL_LINKER_SCRIPT}" -) +add_library("arch-x86_64" OBJECT) +add_library("arch::x86_64" ALIAS "arch-x86_64") -#[============================================================================[ -# The Bootstrap Library -#]============================================================================] - -target_sources("_boot" PRIVATE - "src/boot/boot.s" - "src/boot/crti.s" - "src/boot/crtn.s" - "src/boot/multiboot.s" +target_include_directories("arch-x86_64" PUBLIC + "include" ) -#[============================================================================[ -# The Video Library -#]============================================================================] +target_link_libraries("arch-x86_64" PUBLIC + "kern" -target_sources("_video" PRIVATE - "src/video/vga/text.cpp" + "libs::multiboot2" ) -#[============================================================================[ -# The Memory Library -#]============================================================================] - -target_sources("_memory" PRIVATE - "src/memory/main.cpp" - "src/memory/multiboot/elf_symbols_section.cpp" - "src/memory/multiboot/reader.cpp" - "src/memory/allocator/area_frame_allocator.cpp" - "src/memory/allocator/tiny_frame_allocator.cpp" - "src/memory/allocator/physical_frame.cpp" - "src/memory/paging/page_entry.cpp" - "src/memory/paging/page_table.cpp" - "src/memory/paging/temporary_page.cpp" - "src/memory/paging/virtual_page.cpp" - "src/memory/paging/active_page_table.cpp" - "src/memory/paging/inactive_page_table.cpp" - "src/memory/heap/bump_allocator.cpp" - "src/memory/heap/user_heap_allocator.cpp" - "src/memory/heap/memory_block.cpp" - "src/memory/heap/linked_list_allocator.cpp" - "src/memory/heap/global_heap_allocator.cpp" +target_link_options("arch-x86_64" PUBLIC + "-T${CMAKE_CURRENT_SOURCE_DIR}/scripts/kernel.ld" ) -target_link_libraries("_memory" PUBLIC - "libs::kstd" - "libs::multiboot2" +set_target_properties("arch-x86_64" PROPERTIES + LINK_DEPENDS "${CMAKE_CURRENT_SOURCE_DIR}/scripts/kernel.ld" ) #[============================================================================[ -# The Exception handling Library +# arch::any Implementation #]============================================================================] -target_sources("_exception" PRIVATE - "src/exception_handling/assert.cpp" - "src/exception_handling/abort.cpp" - "src/exception_handling/panic.cpp" - "src/exception_handling/pure_virtual.cpp" +target_sources("arch-x86_64" PRIVATE + "src/io.cpp" + "src/memory.cpp" + "src/system.cpp" ) #[============================================================================[ -# The Context switching Library +# Bootstrap Code #]============================================================================] -target_sources("_context" PRIVATE - "src/context_switching/segment_descriptor_table/access_byte.cpp" - "src/context_switching/segment_descriptor_table/gdt_flags.cpp" - "src/context_switching/segment_descriptor_table/global_descriptor_table_pointer.cpp" - "src/context_switching/segment_descriptor_table/global_descriptor_table.cpp" - "src/context_switching/segment_descriptor_table/segment_descriptor_base.cpp" - "src/context_switching/segment_descriptor_table/segment_descriptor_extension.cpp" - "src/context_switching/main.cpp" - "src/context_switching/syscall/main.cpp" - "src/context_switching/syscall/syscall_enable.cpp" - "src/context_switching/syscall/syscall_handler.cpp" - "src/context_switching/interrupt_descriptor_table/gate_descriptor.cpp" - "src/context_switching/interrupt_descriptor_table/idt_flags.cpp" - "src/context_switching/interrupt_descriptor_table/interrupt_descriptor_table_pointer.cpp" - "src/context_switching/interrupt_descriptor_table/interrupt_descriptor_table.cpp" - "src/context_switching/interrupt_descriptor_table/ist_offset.cpp" - "src/context_switching/interrupt_descriptor_table/segment_selector.cpp" +target_sources("arch-x86_64" PRIVATE + "src/boot/boot.s" + "src/boot/crti.s" + "src/boot/crtn.s" + "src/boot/multiboot.s" ) -target_link_libraries("_context" PUBLIC - "libs::kstd" -) - -#[============================================================================[ -# The Interrupt Handlers -#]============================================================================] - -target_sources("_interrupt_handling" PRIVATE - "src/interrupt_handling/generic_interrupt_handler.cpp" -) - -#[============================================================================[ -# The User code -#]============================================================================] - -target_sources("_context" PRIVATE - "src/user/main.cpp" -) - -#[============================================================================[ -# The Bootable ISO Image -#]============================================================================] - -find_package("grub-mkrescue") - -if(grub-mkrescue_FOUND) - file(GENERATE - OUTPUT "isofs/boot/grub/grub.cfg" - INPUT "support/grub.cfg.in" - ) - - add_custom_target("bootable-iso" - COMMAND "${GRUB_MKRESCUE_EXE}" - "-o" - "${PROJECT_BINARY_DIR}/teachos-$.iso" - "${CMAKE_CURRENT_BINARY_DIR}/isofs" - "$" - "2>/dev/null" - DEPENDS - "$" - "isofs/boot/grub/grub.cfg" - BYPRODUCTS "${PROJECT_BINARY_DIR}/teachos-$.iso" - COMMENT "Creating bootable ISO image" - ) -endif() +# #[============================================================================[ +# # The Kernel Library +# #]============================================================================] + +# set(TEACHOS_KERNEL_LINKER_SCRIPT "${CMAKE_CURRENT_SOURCE_DIR}/scripts/kernel.ld") +# mark_as_advanced(TEACHOS_KERNEL_LINKER_SCRIPT) + +# target_sources("_kernel" PRIVATE +# "src/kernel/main.cpp" +# "src/kernel/cpu/control_register.cpp" +# "src/kernel/cpu/gdtr.cpp" +# "src/kernel/cpu/idtr.cpp" +# "src/kernel/cpu/if.cpp" +# "src/kernel/cpu/call.cpp" +# "src/kernel/cpu/msr.cpp" +# "src/kernel/cpu/segment_register.cpp" +# "src/kernel/cpu/tlb.cpp" +# "src/kernel/cpu/tr.cpp" +# ) + +# target_link_options("_kernel" PRIVATE +# "-T${TEACHOS_KERNEL_LINKER_SCRIPT}" +# ) + +# set_target_properties("_kernel" PROPERTIES +# LINK_DEPENDS "${TEACHOS_KERNEL_LINKER_SCRIPT}" +# ) + +# #[============================================================================[ +# # The Bootstrap Library +# #]============================================================================] + +# target_sources("_boot" PRIVATE +# "src/boot/boot.s" +# "src/boot/crti.s" +# "src/boot/crtn.s" +# "src/boot/multiboot.s" +# ) + +# #[============================================================================[ +# # The Video Library +# #]============================================================================] + +# target_sources("_video" PRIVATE +# "src/video/vga/text.cpp" +# ) + +# #[============================================================================[ +# # The Memory Library +# #]============================================================================] + +# target_sources("_memory" PRIVATE +# "src/memory/main.cpp" +# "src/memory/multiboot/elf_symbols_section.cpp" +# "src/memory/multiboot/reader.cpp" +# "src/memory/allocator/area_frame_allocator.cpp" +# "src/memory/allocator/tiny_frame_allocator.cpp" +# "src/memory/allocator/physical_frame.cpp" +# "src/memory/paging/page_entry.cpp" +# "src/memory/paging/page_table.cpp" +# "src/memory/paging/temporary_page.cpp" +# "src/memory/paging/virtual_page.cpp" +# "src/memory/paging/active_page_table.cpp" +# "src/memory/paging/inactive_page_table.cpp" +# "src/memory/heap/bump_allocator.cpp" +# "src/memory/heap/user_heap_allocator.cpp" +# "src/memory/heap/memory_block.cpp" +# "src/memory/heap/linked_list_allocator.cpp" +# "src/memory/heap/global_heap_allocator.cpp" +# ) + +# target_link_libraries("_memory" PUBLIC +# "libs::kstd" +# "libs::multiboot2" +# ) + +# #[============================================================================[ +# # The Exception handling Library +# #]============================================================================] + +# target_sources("_exception" PRIVATE +# "src/exception_handling/assert.cpp" +# "src/exception_handling/abort.cpp" +# "src/exception_handling/panic.cpp" +# "src/exception_handling/pure_virtual.cpp" +# ) + +# #[============================================================================[ +# # The Context switching Library +# #]============================================================================] + +# target_sources("_context" PRIVATE +# "src/context_switching/segment_descriptor_table/access_byte.cpp" +# "src/context_switching/segment_descriptor_table/gdt_flags.cpp" +# "src/context_switching/segment_descriptor_table/global_descriptor_table_pointer.cpp" +# "src/context_switching/segment_descriptor_table/global_descriptor_table.cpp" +# "src/context_switching/segment_descriptor_table/segment_descriptor_base.cpp" +# "src/context_switching/segment_descriptor_table/segment_descriptor_extension.cpp" +# "src/context_switching/main.cpp" +# "src/context_switching/syscall/main.cpp" +# "src/context_switching/syscall/syscall_enable.cpp" +# "src/context_switching/syscall/syscall_handler.cpp" +# "src/context_switching/interrupt_descriptor_table/gate_descriptor.cpp" +# "src/context_switching/interrupt_descriptor_table/idt_flags.cpp" +# "src/context_switching/interrupt_descriptor_table/interrupt_descriptor_table_pointer.cpp" +# "src/context_switching/interrupt_descriptor_table/interrupt_descriptor_table.cpp" +# "src/context_switching/interrupt_descriptor_table/ist_offset.cpp" +# "src/context_switching/interrupt_descriptor_table/segment_selector.cpp" +# ) + +# target_link_libraries("_context" PUBLIC +# "libs::kstd" +# ) + +# #[============================================================================[ +# # The Interrupt Handlers +# #]============================================================================] + +# target_sources("_interrupt_handling" PRIVATE +# "src/interrupt_handling/generic_interrupt_handler.cpp" +# ) + +# #[============================================================================[ +# # The User code +# #]============================================================================] + +# target_sources("_context" PRIVATE +# "src/user/main.cpp" +# ) + +# #[============================================================================[ +# # The Bootable ISO Image +# #]============================================================================] + +# find_package("grub-mkrescue") + +# if(grub-mkrescue_FOUND) +# file(GENERATE +# OUTPUT "isofs/boot/grub/grub.cfg" +# INPUT "support/grub.cfg.in" +# ) + +# add_custom_target("bootable-iso" +# COMMAND "${GRUB_MKRESCUE_EXE}" +# "-o" +# "${PROJECT_BINARY_DIR}/teachos-$.iso" +# "${CMAKE_CURRENT_BINARY_DIR}/isofs" +# "$" +# "2>/dev/null" +# DEPENDS +# "$" +# "isofs/boot/grub/grub.cfg" +# BYPRODUCTS "${PROJECT_BINARY_DIR}/teachos-$.iso" +# COMMENT "Creating bootable ISO image" +# ) +# endif() diff --git a/arch/x86_64/src/boot/boot.s b/arch/x86_64/src/boot/boot.s index 7932045..5488073 100644 --- a/arch/x86_64/src/boot/boot.s +++ b/arch/x86_64/src/boot/boot.s @@ -364,5 +364,5 @@ _transition_to_long_mode: call _init - call kernel_main + call main call halt diff --git a/arch/x86_64/src/io.cpp b/arch/x86_64/src/io.cpp new file mode 100644 index 0000000..8808dbb --- /dev/null +++ b/arch/x86_64/src/io.cpp @@ -0,0 +1,22 @@ +namespace teachos::arch::io +{ + + // using x86_64::vga::text_mode::attributes; + // using x86_64::vga::text_mode::color; + + // namespace + // { + // auto constexpr error_attributes = + // attributes{.foreground = color::light_gray, .bright = true, .background = color::red, .blink = true}; + // } // namespace + + auto init() -> void + { + // kernel::set_print_handler([](auto text) { return x86_64::vga::text_mode::print(text); }); + // kernel::set_println_handler([](auto text) { return x86_64::vga::text_mode::println(text); }); + // kernel::set_print_error_handler([](auto text) { return x86_64::vga::text_mode::print(text, error_attributes); }); + // kernel::set_println_error_handler( + // [](auto text) { return x86_64::vga::text_mode::println(text, error_attributes); }); + } + +} // namespace teachos::arch::io diff --git a/arch/x86_64/src/memory.cpp b/arch/x86_64/src/memory.cpp new file mode 100644 index 0000000..245d7bd --- /dev/null +++ b/arch/x86_64/src/memory.cpp @@ -0,0 +1,62 @@ +#include "arch/memory.hpp" + +// #include "noarch/error.hpp" +// #include "noarch/print.hpp" +// #include "x86_64/bootstrap/mutiboot.hpp" +// #include "x86_64/memory/frame_allocator.hpp" +// #include "x86_64/memory/mbi_frame_allocator.hpp" + +// #include +// #include +// #include +// #include + +namespace teachos::arch::memory +{ + + // namespace + // { + // /** + // * @brief Remap the kernel according to the ELF information. + // * + // * After initial bootup, a basic identity mapping of the lower 1GiB is set up. This mapping allows execution + // from, + // * as well as read and write access to, the mapped memory. In order to protect the kernel, remap it according to + // the + // * information supplied by the ELF file. This means remapping code sections as read-only and data sections as + // * no-execute (and read only for .rodata). + // * + // * @param sections Information about the sections in the loaded kernel binary. + // * @param allocator The frame allocator used to create new page mappings. + // */ + // auto remap_kernel(elf::section_header_table const & sections, x86_64::memory::frame_allocator & allocator) -> + // void + // { + // static_cast(sections); + // static_cast(allocator); + // } + // } // namespace + + auto init() -> void + { + // kernel::println("Initializing memory"); + + // if (!x86_64::bootstrap::multiboot_information_pointer->has()) + // { + // kernel::panic("Received no memory map from the boot loader!"); + // } + + // if (!x86_64::bootstrap::multiboot_information_pointer->has()) + // { + // kernel::panic("Received no ELF symbol information from the boot loader!"); + // } + + // auto memory_map = x86_64::bootstrap::multiboot_information_pointer->memory_map(); + // auto elf_symbols = x86_64::bootstrap::multiboot_information_pointer->elf_symbols(); + // auto section_header_table = elf::section_header_table{elf_symbols.data(), elf::file_class::bits_64}; + // auto allocator = x86_64::memory::mbi_frame_allocator{memory_map, section_header_table}; + + // remap_kernel(section_header_table, allocator); + } + +} // namespace teachos::arch::memory diff --git a/arch/x86_64/src/system.cpp b/arch/x86_64/src/system.cpp new file mode 100644 index 0000000..60ebf0e --- /dev/null +++ b/arch/x86_64/src/system.cpp @@ -0,0 +1,12 @@ +#include "arch/system.hpp" + +namespace teachos::arch::system +{ + + auto halt() -> void + { + asm volatile("1: hlt\njmp 1b"); + __builtin_unreachable(); + } + +} // namespace teachos::arch::system -- cgit v1.2.3 From d1aaaeb615e148a13f46223c84819ba828e5209f Mon Sep 17 00:00:00 2001 From: Felix Morgner Date: Mon, 14 Jul 2025 16:42:26 +0000 Subject: arch: make linkable --- arch/x86_64/CMakeLists.txt | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) (limited to 'arch/x86_64') diff --git a/arch/x86_64/CMakeLists.txt b/arch/x86_64/CMakeLists.txt index 19bc78c..dd54b39 100644 --- a/arch/x86_64/CMakeLists.txt +++ b/arch/x86_64/CMakeLists.txt @@ -6,8 +6,7 @@ target_include_directories("arch-x86_64" PUBLIC ) target_link_libraries("arch-x86_64" PUBLIC - "kern" - + "arch::any" "libs::multiboot2" ) -- cgit v1.2.3 From 763f38fff9336e40fce27565861e85c95d003a12 Mon Sep 17 00:00:00 2001 From: Felix Morgner Date: Mon, 14 Jul 2025 20:14:38 +0000 Subject: libs: move vector to kstd --- arch/x86_64/include/arch/stl/vector.hpp | 601 -------------------------------- 1 file changed, 601 deletions(-) delete mode 100644 arch/x86_64/include/arch/stl/vector.hpp (limited to 'arch/x86_64') diff --git a/arch/x86_64/include/arch/stl/vector.hpp b/arch/x86_64/include/arch/stl/vector.hpp deleted file mode 100644 index 5314029..0000000 --- a/arch/x86_64/include/arch/stl/vector.hpp +++ /dev/null @@ -1,601 +0,0 @@ -#ifndef TEACHOS_ARCH_X86_64_STL_VECTOR_HPP -#define TEACHOS_ARCH_X86_64_STL_VECTOR_HPP - -#include "arch/exception_handling/panic.hpp" -#include "arch/stl/container.hpp" -#include "arch/stl/contiguous_pointer_iterator.hpp" - -#include - -namespace teachos::arch::stl -{ - /** - * @brief Custom vector implementation mirroring the std::vector to allow for the usage of STL functionality with our - * custom memory management. - * - * @tparam T Element the vector instance should contain. - */ - template - struct vector - { - using value_type = T; ///< Type of the elements contained in the container. - using size_type = std::size_t; ///< Type of the size in the container. - using reference = value_type &; ///< Type of reference to the elements. - using const_reference = value_type const &; ///< Type of constant reference to the elements. - using pointer = value_type *; ///< Type of pointer to the elements. - using const_pointer = value_type const *; ///< Type of constant pointer to the elements. - - /** - * @brief Default Constructor. - */ - vector() = default; - - /** - * @brief Constructs data with the given amount of elements containg the given value or alterantively the default - * constructed value. - * - * @param n Amount of elements we want to create and set the given value for. - * @param initial Inital value of all elements in the underlying data array. - */ - explicit vector(size_type n, value_type initial = value_type{}) - : _size(n) - , _capacity(n) - , _data(new value_type[_capacity]{}) - { - std::ranges::fill(*this, initial); - } - - /** - * @brief Constructs data by copying all element from the given exclusive range. - * - * @tparam InputIterator Template that should have atleast input iterator characteristics. - * @param first Input iterator to the first element in the range we want to copy from. - * @param last Input iterator to one past the last element in the range we want to copy from. - */ - template - explicit vector(InputIterator first, InputIterator last) - : _size(std::distance(first, last)) - , _capacity(std::distance(first, last)) - , _data(new value_type[_capacity]{}) - { - stl::container container{first, last}; - std::ranges::copy(container, _data); - } - - /** - * @brief Construct data by copying all elements from the initializer list. - * - * @param initalizer_list List we want to copy all elements from. - */ - explicit vector(std::initializer_list initalizer_list) - : _size(initalizer_list.size()) - , _capacity(initalizer_list.size()) - , _data(new value_type[_capacity]{}) - { - std::ranges::copy(initalizer_list, _data); - } - - /** - * @brief Copy constructor. - * - * @note Allocates underlying data container with the same capacity as vector we are copying from and copies all - * elements from it. - * - * @param other Other instance of vector we want to copy the data from. - */ - vector(vector const & other) - : _size(other._size) - , _capacity(other._capacity) - { - delete[] _data; - _data = new value_type[_capacity]{}; - std::ranges::copy(other, _data); - } - - /** - * @brief Copy assignment operator. - * - * @note Allocates underlying data container with the same capacity as vector we are copying from and copies all - * elements from it. - * - * @param other Other instance of vector we want to copy the data from. - * @return Newly created copy. - */ - [[gnu::section(".stl_text")]] - vector & operator=(vector const & other) - { - delete[] _data; - _size = other._size; - _capacity = other._capacity; - _data = new value_type[_capacity]{}; - std::ranges::copy(other, _data); - return *this; - } - - /** - * @brief Destructor. - */ - ~vector() { delete[] _data; } - - /** - * @brief Amount of elements currently contained in this vector, will fill up until we have reached the capacity. If - * that is the case the capacity is increased automatically. - * - * @return Current amount of elements. - */ - [[gnu::section(".stl_text")]] - auto size() const -> size_type - { - return _size; - } - - /** - * @brief Amount of space the vector currently has, can be different than the size, because we allocate more than we - * exactly require to decrease the amount of allocations and deallocation to improve speed. - * - * @return Current amount of space the vector has for elements. - */ - [[gnu::section(".stl_text")]] - auto capacity() const -> size_type - { - return _capacity; - } - - /** - * @brief Array indexing operator. Allowing to access element at the given index. - * - * @note Does not do any bounds checks use at() for that. - * - * @param index Index we want to access elements at. - * @return Reference to the underlying element. - */ - [[gnu::section(".stl_text")]] - auto operator[](size_type index) -> reference - { - return _data[index]; - } - - /** - * @brief Array indexing operator. Allowing to access element at the given index. - * - * @note Does not do any bounds checks use at() for that. - * - * @param index Index we want to access elements at. - * @return Reference to the underlying element. - */ - [[gnu::section(".stl_text")]] - auto operator[](size_type index) const -> const_reference - { - return _data[index]; - } - - /** - * @brief Array indexing operator. Allowing to access element at the given index. - * - * @note Ensures we do not access element outside of the bounds of the array, if we do further execution is halted. - * - * @param index Index we want to access elements at. - * @return Reference to the underlying element. - */ - [[gnu::section(".stl_text")]] - auto at(size_type index) -> reference - { - throw_if_out_of_range(index); - return this->operator[](index); - } - - /** - * @brief Array indexing operator. Allowing to access element at the given index. - * - * @note Ensures we do not access element outside of the bounds of the array, if we do further execution is halted. - * - * @param index Index we want to access elements at. - * @return Reference to the underlying element. - */ - [[gnu::section(".stl_text")]] - auto at(size_type index) const -> const_reference - { - throw_if_out_of_range(index); - return this->operator[](index); - } - - /** - * @brief Appends the given element value to the end of the container. The element is assigned through the - * assignment operator of the template type. The value is forwarded to the constructor as - * std::forward(value), meaning it is either moved (rvalue) or copied (lvalue). - * - * @note If after the operation the new size() is greater than old capacity() a reallocation takes place, - * in which case all iterators (including the end() iterator) and all references to the elements are invalidated. - * Otherwise only the end() iterator is invalidated. Uses a forward reference for the actual value passed, which - * allows the template method to be used by both lvalue and rvalues and compile a different implementation. - * - * @param value The value of the element to append. - */ - template - [[gnu::section(".stl_text")]] - auto push_back(U && value) -> void - { - increase_capacity_if_full(); - _data[_size] = std::forward(value); - (void)_size++; - } - - /** - * @brief Appends a new element to the end of the container. The element is constructed through a constructor of the - * template type. The arguments args... are forwarded to the constructor as std::forward(args).... - * - * If after the operation the new size() is greater than old capacity() a reallocation takes place, in which case - * all iterators (including the end() iterator) and all references to the elements are invalidated. Otherwise only - * the end() iterator is invalidated. Uses a forward reference for the actual value passed, which - * allows the template method to be used by both lvalue and rvalues and compile a different implementation. - * - * @tparam Args - * @param args Arguments to forward to the constructor of the element - * @return value_type& - */ - template - [[gnu::section(".stl_text")]] - auto emplace_back(Args &&... args) -> value_type & - { - increase_capacity_if_full(); - _data[_size] = value_type{std::forward(args)...}; - auto const index = _size++; - return _data[index]; - } - - /** - * @brief Removes the last element of the container. Calling pop_back on an empty container results in halting the - * further execution. Iterators and references to the last element are invalidated. The end() - * iterator is also invalidated. - */ - [[gnu::section(".stl_text")]] - auto pop_back() -> void - { - throw_if_empty(); - (void)_size--; - } - - /** - * @brief Returns an iterator to the first element of the vector. - * If the vector is empty, the returned iterator will be equal to end(). - * - * @return Iterator to the first element. - */ - [[gnu::section(".stl_text")]] - auto begin() noexcept -> pointer - { - return _data; - } - - /** - * @brief Returns an iterator to the first element of the vector. - * If the vector is empty, the returned iterator will be equal to end(). - * - * @return Iterator to the first element. - */ - [[gnu::section(".stl_text")]] - auto begin() const noexcept -> const_pointer - { - return _data; - } - - /** - * @brief Returns an iterator to the first element of the vector. - * If the vector is empty, the returned iterator will be equal to end(). - * - * @return Iterator to the first element. - */ - [[gnu::section(".stl_text")]] - auto cbegin() const noexcept -> const_pointer - { - return begin(); - } - - /** - * @brief Returns a reverse iterator to the first element of the reversed vector. It corresponds to the last element - * of the non-reversed vector. If the vector is empty, the returned iterator will be equal to rend(). - * - * @return Reverse iterator to the first element. - */ - [[gnu::section(".stl_text")]] - auto rbegin() noexcept -> pointer - { - return _data + _size - 1; - } - - /** - * @brief Returns a reverse iterator to the first element of the reversed vector. It corresponds to the last element - * of the non-reversed vector. If the vector is empty, the returned iterator will be equal to rend(). - * - * @return Reverse iterator to the first element. - */ - [[gnu::section(".stl_text")]] - auto rbegin() const noexcept -> const_pointer - { - return _data + _size - 1; - } - - /** - * @brief Returns a reverse iterator to the first element of the reversed vector. It corresponds to the last element - * of the non-reversed vector. If the vector is empty, the returned iterator will be equal to rend(). - * - * @return Reverse iterator to the first element. - */ - [[gnu::section(".stl_text")]] - auto crbegin() const noexcept -> const_pointer - { - return rbegin(); - } - - /** - * @brief Returns an iterator to the element following the last element of the vector. This element acts as a - * placeholder, attempting to access it results in undefined behavior. - * - * @return Iterator to the element following the last element. - */ - [[gnu::section(".stl_text")]] - auto end() noexcept -> pointer - { - return _data + _size; - } - - /** - * @brief Returns an iterator to the element following the last element of the vector. This element acts as a - * placeholder, attempting to access it results in undefined behavior. - * - * @return Iterator to the element following the last element. - */ - [[gnu::section(".stl_text")]] - auto end() const noexcept -> const_pointer - { - return _data + _size; - } - - /** - * @brief Returns an iterator to the element following the last element of the vector. This element acts as a - * placeholder, attempting to access it results in undefined behavior. - * - * @return Iterator to the element following the last element. - */ - [[gnu::section(".stl_text")]] - auto cend() const noexcept -> const_pointer - { - return end(); - } - - /** - * @brief Returns a reverse iterator to the element following the last element of the reversed vector. It - * corresponds to the element preceding the first element of the non-reversed vector. This element acts as a - * placeholder, attempting to access it results in undefined behavior. - * - * @return Reverse iterator to the element following the last element. - */ - [[gnu::section(".stl_text")]] - auto rend() noexcept -> pointer - { - return _data + size - 1; - } - - /** - * @brief Returns a reverse iterator to the element following the last element of the reversed vector. It - * corresponds to the element preceding the first element of the non-reversed vector. This element acts as a - * placeholder, attempting to access it results in undefined behavior. - * - * @return Reverse iterator to the element following the last element. - */ - [[gnu::section(".stl_text")]] - auto rend() const noexcept -> const_pointer - { - return _data + size - 1; - } - - /** - * @brief Returns a reverse iterator to the element following the last element of the reversed vector. It - * corresponds to the element preceding the first element of the non-reversed vector. This element acts as a - * placeholder, attempting to access it results in undefined behavior. - * - * @return Reverse iterator to the element following the last element. - */ - [[gnu::section(".stl_text")]] - auto crend() const noexcept -> const_pointer - { - return rbegin(); - } - - /** - * @brief Returns a pointer to the underlying array serving as element storage. The pointer is such that range - * [data(), data() + size()) is always a valid range, even if the container is empty (data() is not dereferenceable - * in that case). - * - * @return Pointer to the underlying element storage. For non-empty containers, the returned pointer compares equal - * to the address of the first element. - */ - [[gnu::section(".stl_text")]] - auto data() -> pointer - { - return _data; - } - - /** - * @brief Returns a pointer to the underlying array serving as element storage. The pointer is such that range - * [data(), data() + size()) is always a valid range, even if the container is empty (data() is not dereferenceable - * in that case). - * - * @return Pointer to the underlying element storage. For non-empty containers, the returned pointer compares equal - * to the address of the first element. - */ - [[gnu::section(".stl_text")]] - auto data() const -> const_pointer - { - return _data; - } - - /** - * @brief Returns a reference to the first element in the container. Calling front on an empty container causes - * undefined behavior. - * - * @return Reference to the first element. - */ - [[gnu::section(".stl_text")]] - auto front() -> reference - { - throw_if_empty(); - return *begin(); - } - - /** - * @brief Returns a reference to the first element in the container. Calling front on an empty container causes - * undefined behavior. - * - * @return Reference to the first element. - */ - [[gnu::section(".stl_text")]] - auto front() const -> const_reference - { - throw_if_empty(); - return *begin(); - } - - /** - * @brief Returns a reference to the last element in the container. Calling back on an empty container causes - * undefined behavior. - * - * @return Reference to the last element. - */ - [[gnu::section(".stl_text")]] - auto back() -> reference - { - throw_if_empty(); - return *rbegin(); - } - - /** - * @brief Returns a reference to the last element in the container. Calling back on an empty container causes - * undefined behavior. - * - * @return Reference to the last element. - */ - [[gnu::section(".stl_text")]] - auto back() const -> const_reference - { - throw_if_empty(); - return *rbegin(); - } - - /** - * @brief Increase the capacity of the vector (the total number of elements that the vector can hold without - * requiring reallocation) to a value that's greater or equal to new_cap. If new_cap is greater than the current - * capacity(), new storage is allocated, otherwise the function does nothing. - * - * reserve() does not change the size of the vector. - * - * If new_cap is greater than capacity(), all iterators (including the end() iterator) and all references to the - * elements are invalidated. Otherwise, no iterators or references are invalidated. - * - * After a call to reserve(), insertions will not trigger reallocation unless the insertion would make the size of - * the vector greater than the value of capacity(). - * - * @note Correctly using reserve() can prevent unnecessary reallocations, but inappropriate uses of reserve() (for - * instance, calling it before every push_back() call) may actually increase the number of reallocations (by causing - * the capacity to grow linearly rather than exponentially) and result in increased computational complexity and - * decreased performance. For example, a function that receives an arbitrary vector by reference and appends - * elements to it should usually not call reserve() on the vector, since it does not know of the vector's usage - * characteristics. - * - * When inserting a range, the range version of insert() is generally preferable as it preserves the correct - * capacity growth behavior, unlike reserve() followed by a series of push_back()s. - * - * reserve() cannot be used to reduce the capacity of the container; to that end shrink_to_fit() is provided. - * - * @param new_capacity New capacity of the vector, in number of elements - */ - [[gnu::section(".stl_text")]] - auto reserve(size_type new_capacity) -> void - { - if (new_capacity <= _capacity) - { - return; - } - - _capacity = new_capacity; - value_type * temp = new value_type[_capacity]{}; - stl::container container{begin(), end()}; - std::ranges::copy(container, temp); - delete[] _data; - _data = temp; - } - - /** - * @brief Requests the removal of unused capacity. Meaning it requests to reduce capacity() to size(). - * - * If reallocation occurs, all iterators (including the end() iterator) and all references to the elements are - * invalidated. If no reallocation occurs, no iterators or references are invalidated. - */ - [[gnu::section(".stl_text")]] - auto shrink_to_fit() -> void - { - if (_size == _capacity) - { - return; - } - - _capacity = _size; - value_type * temp = new value_type[_capacity]{}; - stl::container container{begin(), end()}; - std::copy(container, temp); - delete[] _data; - _data = temp; - } - - /** - * @brief Wheter there are currently any items this container or not. - * - * @return True if there are no elements, false if there are. - */ - [[gnu::section(".stl_text")]] - auto empty() const -> bool - { - return _size <= 0; - } - - private: - /** - * @brief Halts the execution of the application if the data container is currently empty. - */ - auto throw_if_empty() const -> void - { - if (empty()) - { - exception_handling::panic("[Vector] Attempted to access element of currently empty vector"); - } - } - - auto throw_if_out_of_range(size_type index) const -> void - { - if (index >= _size) - { - exception_handling::panic("[Vector] Attempted to read element at invalid index"); - } - } - - /** - * @brief Increases the internal capacity to 1 if it was previously 0 and to * 2 after that, meaning exponential - * growth. This is done to decrease the amount of single allocations done and because a power of 2 in memory size is - * normally perferable for the cache. - */ - auto increase_capacity_if_full() -> void - { - if (_size == _capacity) - { - reserve(_capacity == 0U ? 1U : _capacity * 2U); - } - } - - size_type _size = {}; ///< Amount of elements in the underlying data container - size_type _capacity = {}; ///< Amount of space for elements in the underlying data container - value_type * _data = {}; ///< Pointer to the first element in the underlying data container - }; - -} // namespace teachos::arch::stl - -#endif // TEACHOS_ARCH_X86_64_STL_VECTOR_HPP -- cgit v1.2.3 From 576935b6448802138639a324535614e0a966ead1 Mon Sep 17 00:00:00 2001 From: Felix Morgner Date: Mon, 14 Jul 2025 20:16:19 +0000 Subject: libs: move unique_ptr to kstd --- arch/x86_64/include/arch/stl/unique_pointer.hpp | 204 ------------------------ 1 file changed, 204 deletions(-) delete mode 100644 arch/x86_64/include/arch/stl/unique_pointer.hpp (limited to 'arch/x86_64') diff --git a/arch/x86_64/include/arch/stl/unique_pointer.hpp b/arch/x86_64/include/arch/stl/unique_pointer.hpp deleted file mode 100644 index 03b4ef3..0000000 --- a/arch/x86_64/include/arch/stl/unique_pointer.hpp +++ /dev/null @@ -1,204 +0,0 @@ -#ifndef TEACHOS_ARCH_X86_64_STL_UNIQUE_POINTER_HPP -#define TEACHOS_ARCH_X86_64_STL_UNIQUE_POINTER_HPP - -namespace teachos::arch::stl -{ - /** - * @brief Unique_pointer is a smart pointer that owns (is responsible for) and manages another object via a pointer - * and subsequently disposes of that object when the unique_pointer goes out of scope. - * - * @tparam T Type of the managed object. - */ - template - struct unique_pointer - { - /** - * @brief Constructor. - * - * @param ptr A pointer to an object to manage (default is nullptr). - */ - [[gnu::section(".stl_text")]] - explicit unique_pointer(T * ptr = nullptr) - : pointer(ptr) - { - // Nothing to do. - } - - /** - * @brief Destructor that deletes the managed object. - */ - [[gnu::section(".stl_text")]] - ~unique_pointer() - { - delete pointer; - } - - /** - * @brief Deleted copy constructor to enforce unique ownership. - */ - unique_pointer(const unique_pointer &) = delete; - - /** - * @brief Deleted copy assignment operator to enforce unique ownership. - */ - auto operator=(const unique_pointer &) -> unique_pointer & = delete; - - /** - * @brief Move constructor. - * - * @param other Unique pointer to move from. - */ - [[gnu::section(".stl_text")]] - unique_pointer(unique_pointer && other) noexcept - : pointer(other.pointer) - { - other.pointer = nullptr; - } - - /** - * @brief Move assignment operator. Transfers ownership from other to *this as if by calling reset(r.release()). - * - * @param other Smart pointer from which ownership will be transferred. - * @return Reference to this unique pointer. - */ - [[gnu::section(".stl_text")]] - auto operator=(unique_pointer && other) noexcept -> unique_pointer & - { - if (this != &other) - { - delete pointer; - pointer = other.pointer; - other.pointer = nullptr; - } - return *this; - } - - /** - * @brief Dereference operator. If get() is a null pointer, the behavior is undefined. - * - * @return Returns the object owned by *this, equivalent to *get(). - */ - [[gnu::section(".stl_text")]] - auto operator*() const -> T & - { - return *pointer; - } - - /** - * @brief Member access operator. - * - * @return Returns a pointer to the object owned by *this, i.e. get(). - */ - [[gnu::section(".stl_text")]] - auto operator->() const -> T * - { - return pointer; - } - - /** - * @brief Returns a pointer to the managed object or nullptr if no object is owned. - * - * @return Pointer to the managed object or nullptr if no object is owned. - */ - [[gnu::section(".stl_text")]] - auto get() const -> T * - { - return pointer; - } - - /** - * @brief Checks whether *this owns an object, i.e. whether get() != nullptr. - * - * @return true if *this owns an object, false otherwise. - */ - [[gnu::section(".stl_text")]] - explicit operator bool() const noexcept - { - return pointer != nullptr; - } - - /** - * @brief Releases the ownership of the managed object, if any. - * get() returns nullptr after the call. - * The caller is responsible for cleaning up the object (e.g. by use of get_deleter()). - * - * @return Pointer to the managed object or nullptr if there was no managed object, i.e. the value which would be - * returned by get() before the call. - */ - [[gnu::section(".stl_text")]] - auto release() -> T * - { - T * temp = pointer; - pointer = nullptr; - return temp; - } - - /** - * @brief Replaces the managed object. - * - * @note A test for self-reset, i.e. whether ptr points to an object already managed by *this, is not performed, - * except where provided as a compiler extension or as a debugging assert. Note that code such as - * p.reset(p.release()) does not involve self-reset, only code like p.reset(p.get()) does. - * - * @param ptr Pointer to a new object to manage (default = nullptr). - */ - [[gnu::section(".stl_text")]] - auto reset(T * ptr = nullptr) -> void - { - delete pointer; - pointer = ptr; - } - - /** - * @brief Swaps the managed objects and associated deleters of *this and another unique_ptr object other. - * - * @param other Another unique_ptr object to swap the managed object and the deleter with. - */ - [[gnu::section(".stl_text")]] - auto swap(unique_pointer & other) -> void - { - using std::swap; - swap(pointer, other.pointer); - } - - /** - * @brief Defaulted three-way comparator operator. - */ - [[gnu::section(".stl_text")]] - auto operator<=>(const unique_pointer & other) const = default; - - private: - T * pointer; ///< The managed pointer. - }; - - /** - * @brief Specializes the std::swap algorithm for stl::unique_ptr. Swaps the contents of lhs and rhs. Calls - * lhs.swap(rhs). - * - * @tparam T Type of the managed object. - * @param lhs, rhs Smart pointers whose contents to swap. - */ - template - auto swap(unique_pointer & lhs, unique_pointer & rhs) -> void - { - lhs.swap(rhs); - } - - /** - * @brief Constructs an object of type T and wraps it in a unique_pointer. Constructs a non-array type T. The - * arguments args are passed to the constructor of T. This overload participates in overload resolution only if T is - * not an array type. The function is equivalent to: unique_pointer(new T(std::forward(args)...)). - * - * @tparam T Type of the managed object. - * @tparam Args Argument types for T's constructor. - * @param args List of arguments with which an instance of T will be constructed. - * @returns Unique_pointer of an instance of type T. - */ - template - auto make_unique(Args &&... args) -> unique_pointer - { - return unique_pointer(new T(std::forward(args)...)); - } -} // namespace teachos::arch::stl - -#endif // TEACHOS_ARCH_X86_64_STL_UNIQUE_POINTER_HPP \ No newline at end of file -- cgit v1.2.3 From f12fa671ccadfdeaca1529157c3bd458f9e37c30 Mon Sep 17 00:00:00 2001 From: Felix Morgner Date: Mon, 14 Jul 2025 20:18:45 +0000 Subject: libs: move shared_pointer to kstd --- arch/x86_64/include/arch/stl/shared_pointer.hpp | 269 ------------------------ 1 file changed, 269 deletions(-) delete mode 100644 arch/x86_64/include/arch/stl/shared_pointer.hpp (limited to 'arch/x86_64') diff --git a/arch/x86_64/include/arch/stl/shared_pointer.hpp b/arch/x86_64/include/arch/stl/shared_pointer.hpp deleted file mode 100644 index c9796a8..0000000 --- a/arch/x86_64/include/arch/stl/shared_pointer.hpp +++ /dev/null @@ -1,269 +0,0 @@ -#ifndef TEACHOS_ARCH_X86_64_STL_SHARED_POINTER_HPP -#define TEACHOS_ARCH_X86_64_STL_SHARED_POINTER_HPP - -#include - -namespace teachos::arch::stl -{ - /** - * @brief Shared_pointer is a smart pointer that retains shared ownership of an object through a pointer. Several - * shared_pointer objects may own the same object. The object is destroyed and its memory deallocated when either of - * the following happens: the last remaining shared_pointer owning the object is destroyed; the last remaining - * shared_pointer owning the object is assigned another pointer via operator= or reset(). A - * shared_pointer can share ownership of an object while storing a pointer to another object. This feature can be used - * to point to member objects while owning the object they belong to. The stored pointer is the one accessed by get(), - * the dereference and the comparison operators. The managed pointer is the one passed to the deleter when use count - * reaches zero. - * - * @tparam T The type of the managed object. - */ - template - struct shared_pointer - { - /** - * @brief Constructor. - * - * @param pointer A pointer to an object to manage (default is nullptr). - */ - [[gnu::section(".stl_text")]] - explicit shared_pointer(T * pointer = nullptr) - : pointer(pointer) - , ref_count(new std::atomic(pointer != nullptr ? 1 : 0)) - { - // Nothing to do. - } - - /** - * @brief Copy constructor. - * - * @param other The shared_pointer to copy from. - */ - [[gnu::section(".stl_text")]] - shared_pointer(const shared_pointer & other) - : pointer(other.pointer) - , ref_count(other.ref_count) - { - if (pointer != nullptr) - { - ++(*ref_count); - } - } - - /** - * @brief Move constructor. - * - * @param other The shared_pointer to move from. - */ - [[gnu::section(".stl_text")]] - shared_pointer(shared_pointer && other) noexcept - : pointer(other.pointer) - , ref_count(other.ref_count) - { - other.pointer = nullptr; - other.ref_count = nullptr; - } - - /** - * @brief Copy assignment operator. Replaces the managed object with the one managed by r. Shares ownership of the - * object managed by r. If r manages no object, *this manages no object too. Equivalent to - * shared_ptr(r).swap(*this). - * - * @param other Another smart pointer to share the ownership with. - * @return Reference to this shared pointer. - */ - [[gnu::section(".stl_text")]] - shared_pointer & operator=(const shared_pointer & other) - { - if (this != &other) - { - cleanup(); - pointer = other.pointer; - ref_count = other.ref_count; - - if (pointer != nullptr) - { - ++(*ref_count); - } - } - - return *this; - } - - /** - * @brief Move assignment operator. Move-assigns a shared_ptr from r. After the assignment, *this contains a copy of - * the previous state of r, and r is empty. Equivalent to shared_ptr(std::move(r)).swap(*this). - * - * @param other Another smart pointer to acquire the ownership from. - * @return Reference to this shared pointer. - */ - [[gnu::section(".stl_text")]] - shared_pointer & operator=(shared_pointer && other) noexcept - { - if (this != &other) - { - cleanup(); - pointer = other.pointer; - ref_count = other.ref_count; - other.pointer = nullptr; - other.ref_count = nullptr; - } - - return *this; - } - - /** - * @brief Destructor. Cleans up resources if necessary. - */ - [[gnu::section(".stl_text")]] - ~shared_pointer() - { - cleanup(); - } - - /** - * @brief Replaces the managed object. - * - * @param ptr Pointer to a new object to manage (default = nullptr). - */ - [[gnu::section(".stl_text")]] - void reset(T * ptr = nullptr) - { - cleanup(); - pointer = ptr; - ref_count = new std::atomic(ptr != nullptr ? 1 : 0); - } - - /** - * @brief Exchanges the stored pointer values and the ownerships of *this and r. Reference counts, if any, are not - * adjusted. - * - * @param other The shared_pointer to swap with. - */ - [[gnu::section(".stl_text")]] - void swap(shared_pointer & other) - { - std::swap(pointer, other.pointer); - std::swap(ref_count, other.ref_count); - } - - /** - * @brief Dereference operator. If get() is a null pointer, the behavior is undefined. - * - * @return Returns the object owned by *this, equivalent to *get(). - */ - [[gnu::section(".stl_text")]] - auto operator*() const -> T & - { - return *pointer; - } - - /** - * @brief Member access operator. - * - * @return Returns a pointer to the object owned by *this, i.e. get(). - */ - [[gnu::section(".stl_text")]] - auto operator->() const -> T * - { - return pointer; - } - - /** - * @brief Returns a pointer to the managed object or nullptr if no object is owned. - * - * @return Pointer to the managed object or nullptr if no object is owned. - */ - [[gnu::section(".stl_text")]] - auto get() const -> T * - { - return pointer; - } - - /** - * @brief Returns the number of different shared_pointer instances (*this included) managing the current object. If - * there is no managed object, ​0​ is returned. - * - * @note Common use cases include comparison with ​0​. If use_count returns zero, the shared pointer is empty - * and manages no objects (whether or not its stored pointer is nullptr). Comparison with 1. If use_count returns 1, - * there are no other owners. - * - * @return The number of Shared_pointer instances managing the current object or ​0​ if there is no managed - * object. - */ - [[gnu::section(".stl_text")]] - auto use_count() const -> std::size_t - { - if (pointer != nullptr) - { - return *ref_count; - } - - return 0; - } - - /** - * @brief Checks whether *this owns an object, i.e. whether get() != nullptr. - * - * @return true if *this owns an object, false otherwise. - */ - [[gnu::section(".stl_text")]] - explicit operator bool() const - { - return pointer != nullptr; - } - - /** - * @brief Defaulted three-way comparator operator. - */ - [[gnu::section(".stl_text")]] - auto operator<=>(const shared_pointer & other) const = default; - - private: - /** - * @brief Releases ownership and deletes the object if this was the last ereference to the owned managed object. - */ - [[gnu::section(".stl_text")]] - auto cleanup() -> void - { - if (pointer != nullptr && ref_count != nullptr && --(*ref_count) == 0) - { - delete pointer; - delete ref_count; - } - } - - T * pointer; ///< The managed object. - std::atomic * ref_count; ///< Reference count. - }; - - /** - * @brief Specializes the std::swap algorithm for stl::unique_ptr. Swaps the contents of lhs and rhs. Calls - * lhs.swap(rhs). - * - * @tparam T Type of the managed object. - * @param lhs, rhs Smart pointers whose contents to swap. - */ - template - auto swap(shared_pointer & lhs, shared_pointer & rhs) -> void - { - lhs.swap(rhs); - } - - /** - * @brief Constructs an object of type T and wraps it in a shared_pointer. Constructs a non-array type T. The - * arguments args are passed to the constructor of T. This overload participates in overload resolution only if T is - * not an array type. The function is equivalent to: shared_pointer(new T(std::forward(args)...)). - * - * @tparam T Type of the managed object. - * @tparam Args Argument types for T's constructor. - * @param args List of arguments with which an instance of T will be constructed. - * @returns Shared_pointer of an instance of type T. - */ - template - auto make_shared(Args &&... args) -> shared_pointer - { - return shared_pointer(new T(std::forward(args)...)); - } -} // namespace teachos::arch::stl - -#endif // TEACHOS_ARCH_X86_64_STL_SHARED_POINTER_HPP \ No newline at end of file -- cgit v1.2.3 From 67785bfc07072fce56331052c1cd8de023eb2f4c Mon Sep 17 00:00:00 2001 From: Felix Morgner Date: Mon, 14 Jul 2025 20:19:00 +0000 Subject: x86_64: remove container --- arch/x86_64/include/arch/stl/container.hpp | 99 ---------- .../arch/stl/contiguous_pointer_iterator.hpp | 216 --------------------- 2 files changed, 315 deletions(-) delete mode 100644 arch/x86_64/include/arch/stl/container.hpp delete mode 100644 arch/x86_64/include/arch/stl/contiguous_pointer_iterator.hpp (limited to 'arch/x86_64') diff --git a/arch/x86_64/include/arch/stl/container.hpp b/arch/x86_64/include/arch/stl/container.hpp deleted file mode 100644 index 4ea08c7..0000000 --- a/arch/x86_64/include/arch/stl/container.hpp +++ /dev/null @@ -1,99 +0,0 @@ -#ifndef TEACHOS_ARCH_X86_64_STL_CONTAINER_HPP -#define TEACHOS_ARCH_X86_64_STL_CONTAINER_HPP - -#include - -namespace teachos::arch::stl -{ - /** - * @brief Minimal iterator concept required for usage in container - */ - template - concept Iterator = std::forward_iterator; - - /** - * @brief Read-only container for given template type, that allow to easily use this container instance in C++20 - * ranges calls. - * - * @tparam T Iterator the container uses to signal the start and end of it's data, has to atleast be a simple forward - * iterator. - */ - template - struct container - { - using iterator = T; ///< Iterators used by this container. - using size_type = std::size_t; ///< Maximum size of this container. - - /** - * @brief Defaulted constructor. - */ - container() = default; - - /** - * @brief Constructor. - * - * @param begin Iterator containing non-owning pointer to the first element of all memory areas. - * @param end Iterator pointing to one past the last element of all memory areas. - */ - container(iterator begin, iterator end) - : begin_itr(begin) - , end_itr(end) - { - // Nothing to do - } - - /** - * @brief Returns the iterator pointing to the first element of the memory area. - * Allows using this class in the for each loop, because it follows the InputIterator template scheme. - * - * @return Iterator pointing to first element of the memory area. - */ - [[gnu::section(".stl_text")]] - auto begin() const -> iterator - { - return begin_itr; - } - - /** - * @brief Returns the iterator pointing to one past the last element of the memory area. - * Allows using this class in the for each loop, because it follows the InputIterator template scheme. - * - * @return Iterator pointing to one past the last element of the memory area. - */ - [[gnu::section(".stl_text")]] - auto end() const -> iterator - { - return end_itr; - } - - /** - * @brief Calculates the size of this container, simply subtracts the iterator pointing to the first element by the - * last. - * - * @return Actual size of this container. - */ - [[gnu::section(".stl_text")]] - auto size() const -> size_type - { - return std::distance(begin(), end()); - } - - /** - * @brief Calcualtes the size and returns true if the size is 0 and the container therefore emtpy. - * - * @return Whether the container is empty, size being 0 or not - */ - [[gnu::section(".stl_text")]] - auto empty() const -> bool - { - return size() == 0; - } - - private: - iterator begin_itr = {}; ///< Pointer to the first element of the given template type. - iterator end_itr = {}; ///< Pointer to one pas the last element of the given template type. - }; - -} // namespace teachos::arch::stl - -#endif // TEACHOS_ARCH_X86_64_STL_CONTAINER_HPP diff --git a/arch/x86_64/include/arch/stl/contiguous_pointer_iterator.hpp b/arch/x86_64/include/arch/stl/contiguous_pointer_iterator.hpp deleted file mode 100644 index f2dfb2b..0000000 --- a/arch/x86_64/include/arch/stl/contiguous_pointer_iterator.hpp +++ /dev/null @@ -1,216 +0,0 @@ -#ifndef TEACHOS_ARCH_X86_64_STL_CONTIGUOUS_POINTER_ITERATOR_HPP -#define TEACHOS_ARCH_X86_64_STL_CONTIGUOUS_POINTER_ITERATOR_HPP - -#include - -namespace teachos::arch::stl -{ - /** - * @brief Generic contiguous iterator for given template type. Allows to easily use this iterator instance in - * algorithm calls. - * - * @note Allows any value that is contained in an array in memory, which is a block of contiguous memory. This is the - * case because we assume we can simply increment or decrement the pointer address to get the next valid instance of - * the given value type. - * - * @tparam T Value the iterator points too. - */ - template - struct contiguous_pointer_iterator - { - using iterator_category = std::contiguous_iterator_tag; ///< Iterator category of this type. - using difference_type = std::ptrdiff_t; ///< Type when diving one instance of this iterator by another. - using value_type = T; ///< Underlying value pointed to by this iterator. - using reference_type = value_type &; ///< Reference to value returned by dereference * operation. - using pointer_type = value_type *; ///< Pointer to value returned by arrow -> operation. - - /** - * @brief Defaulted constructor. - */ - contiguous_pointer_iterator() = default; - - /** - * @brief Constructor. - * - * @param p Underlying address the iterator should point too. - */ - explicit contiguous_pointer_iterator(value_type * p) - : ptr(p) - { - // Nothing to do - } - - /** - * @brief Dereferences the initally given pointer to its value. - * - * @return Reference to the value. - */ - [[gnu::section(".stl_text")]] - auto operator*() const -> reference_type - { - return *ptr; - } - - /** - * @brief Get underlying value, which is the intially passed pointer. - * - * @return Pointer to the underlying value passed intially. - */ - [[gnu::section(".stl_text")]] - auto operator->() const -> pointer_type - { - return ptr; - } - - /** - * @brief Pre decrement operator. Returns a reference to the changed address. - * - * @return Reference to the decremented underlying address. - */ - [[gnu::section(".stl_text")]] - auto operator--() -> contiguous_pointer_iterator & - { - contiguous_pointer_iterator const old_value = *this; - ++ptr; - return old_value; - } - - /** - * @brief Pre increment operator. Returns a reference to the changed address. - * - * @return Reference to the incremented underlying address. - */ - [[gnu::section(".stl_text")]] - auto operator++() -> contiguous_pointer_iterator & - { - ++ptr; - return *this; - } - - /** - * @brief Post decrement operator. Returns a copy of the address. - * - * @return Copy of the decremented underlying address. - */ - [[gnu::section(".stl_text")]] - auto operator--(int) -> contiguous_pointer_iterator - { - auto const old_value = *this; - --ptr; - return old_value; - } - - /** - * @brief Post increment operator. Returns a copy of the address. - * - * @return Copy of the incremented underlying address. - */ - [[gnu::section(".stl_text")]] - auto operator++(int) -> contiguous_pointer_iterator - { - auto const old_value = *this; - ++ptr; - return old_value; - } - - /** - * @brief Addition assignment operator. Returns a reference to the changed address. - * - * @param value Value we want to add to the underlying address. - * @return Reference to the changed underlying address. - */ - [[gnu::section(".stl_text")]] - auto operator+=(difference_type value) -> contiguous_pointer_iterator & - { - ptr += value; - return *this; - } - - /** - * @brief Subtraction assignment operator. Returns a reference to the changed address. - * - * @param value Value we want to subtract from the underlying address. - * @return Reference to the changed underlying address. - */ - [[gnu::section(".stl_text")]] - auto operator-=(difference_type value) -> contiguous_pointer_iterator & - { - ptr -= value; - return *this; - } - - /** - * @brief Addition operator. Returns the changed address. - * - * @param value Value we want to add to a copy of the underlying address. - * @return Copy of underlying address incremented by the given value. - */ - [[gnu::section(".stl_text")]] - auto operator+(difference_type value) const -> contiguous_pointer_iterator - { - return contiguous_pointer_iterator{ptr + value}; - } - - /** - * @brief Subtraction operator. Returns the changed address. - * - * @param value Value we want to subtrcat from a copy of the underlying address. - * @return Copy of underlying address decremented by the given value. - */ - [[gnu::section(".stl_text")]] - auto operator-(difference_type value) const -> contiguous_pointer_iterator - { - return contiguous_pointer_iterator{ptr - value}; - } - - /** - * @brief Subtraction operator. Returns the size difference between two iterators. - * - * @param other Other iterator we want to substract the underlying address with ours. - * @return Size difference between the underlying address of this instance and the given iterator. - */ - [[gnu::section(".stl_text")]] - auto operator-(const contiguous_pointer_iterator & other) const -> difference_type - { - return ptr - other.ptr; - } - - /** - * @brief Index operator overload. Returns a reference to the value at the given index. Simply returns the - * dereferenced underlying pointer incremented by the given index. - * - * @param index Index we want to access and get the value from. - * @return Reference to the value at the given index. - */ - [[gnu::section(".stl_text")]] - auto operator[](difference_type index) const -> value_type & - { - return *(ptr + index); - } - - /** - * @brief Defaulted comparsion operator. Simply compares the memory address of both iterators. - * - * @param other Other iterator to compare to. - * @return Whether both iterators point to the same underlying address in memory. - */ - [[gnu::section(".stl_text")]] - auto operator==(contiguous_pointer_iterator const & other) const -> bool = default; - - /** - * @brief Defaulted threeway comparsion operator. Simply compares the memory address of both iterators. - * - * @param other Other iterator to compare to. - * @return Whether the given iterator is smaller or larger than this iterator. - */ - [[gnu::section(".stl_text")]] - auto operator<=>(contiguous_pointer_iterator const & other) const -> std::strong_ordering = default; - - private: - pointer_type ptr = - {}; ///< Underlying value the iterator is currently pointing too and should increment or decrement. - }; - -} // namespace teachos::arch::stl - -#endif // TEACHOS_ARCH_X86_64_STL_CONTIGUOUS_POINTER_ITERATOR_HPP -- cgit v1.2.3 From be0d5d9453edb871393cd8ee5c83ad15f6ef8c9d Mon Sep 17 00:00:00 2001 From: Felix Morgner Date: Mon, 14 Jul 2025 20:21:01 +0000 Subject: libs: move stack to kstd --- arch/x86_64/include/arch/stl/stack.hpp | 212 --------------------------------- 1 file changed, 212 deletions(-) delete mode 100644 arch/x86_64/include/arch/stl/stack.hpp (limited to 'arch/x86_64') diff --git a/arch/x86_64/include/arch/stl/stack.hpp b/arch/x86_64/include/arch/stl/stack.hpp deleted file mode 100644 index 48bcf10..0000000 --- a/arch/x86_64/include/arch/stl/stack.hpp +++ /dev/null @@ -1,212 +0,0 @@ -#ifndef TEACHOS_ARCH_X86_64_STL_STACK_HPP -#define TEACHOS_ARCH_X86_64_STL_STACK_HPP - -#include "arch/exception_handling/panic.hpp" -#include "arch/stl/vector.hpp" - -namespace teachos::arch::stl -{ - /** - * @brief Custom stack implementation mirroring the std::stack to allow for the usage of STL functionality with our - * custom memory management. - * - * @tparam T Element the stack instance should contain. - * @tparam Container Actual underlying container that should be wrapped to provide stack functionality. Requires - * access to pop_back(), push_back(), back(), size(), empty() and emplace_back() - */ - template> - struct stack - { - using container_type = Container; ///< Type of the underlying container used to implement stack-like interface. - using value_type = Container::value_type; ///< Type of the elements contained in the underlying container. - using size_type = Container::size_type; ///< Type of the size in the underlying container. - using reference = Container::reference; ///< Type of reference to the elements. - using const_reference = Container::const_reference; ///< Type of constant reference to the elements. - - /** - * @brief Default Constructor. - */ - stack() = default; - - /** - * @brief Constructs data with the given amount of elements containg the given value or alterantively the default - * constructed value. - * - * @param n Amount of elements we want to create and set the given value for. - * @param initial Inital value of all elements in the underlying data array. - */ - [[gnu::section(".stl_text")]] - explicit stack(size_type n, value_type initial = value_type{}) - : _container(n, initial) - { - // Nothing to do. - } - - /** - * @brief Constructs data by copying all element from the given exclusive range. - * - * @tparam InputIterator Template that should have atleast input iterator characteristics. - * @param first Input iterator to the first element in the range we want to copy from. - * @param last Input iterator to one past the last element in the range we want to copy from. - */ - template - [[gnu::section(".stl_text")]] - explicit stack(InputIterator first, InputIterator last) - : _container(first, last) - { - // Nothing to do. - } - - /** - * @brief Construct data by copying all elements from the initializer list. - * - * @param initalizer_list List we want to copy all elements from. - */ - [[gnu::section(".stl_text")]] - explicit stack(std::initializer_list initalizer_list) - : _container(initalizer_list) - { - // Nothing to do. - } - - /** - * @brief Copy constructor. - * - * @note Allocates underlying data container with the same capacity as stack we are copying from and copies all - * elements from it. - * - * @param other Other instance of stack we want to copy the data from. - */ - [[gnu::section(".stl_text")]] - stack(stack const & other) - : _container(other) - { - // Nothing to do. - } - - /** - * @brief Copy assignment operator. - * - * @note Allocates underlying data container with the same capacity as vector we are copying from and copies all - * elements from it. - * - * @param other Other instance of vector we want to copy the data from. - * @return Newly created copy. - */ - [[gnu::section(".stl_text")]] - stack & operator=(stack const & other) - { - _container = other; - } - - /** - * @brief Destructor. - */ - ~stack() = default; - - /** - * @brief Amount of elements currently contained in this vector, will fill up until we have reached the capacity. If - * that is the case the capacity is increased automatically. - * - * @return Current amount of elements. - */ - [[gnu::section(".stl_text")]] - auto size() const -> size_type - { - return _container.size(); - } - - /** - * @brief Returns a reference to the last element in the container. Calling back on an empty container causes - * undefined behavior. - * - * @return Reference to the last element. - */ - [[gnu::section(".stl_text")]] - auto top() -> reference - { - return _container.back(); - } - - /** - * @brief Returns a reference to the last element in the container. Calling back on an empty container causes - * undefined behavior. - * - * @return Reference to the last element. - */ - [[gnu::section(".stl_text")]] - auto top() const -> const_reference - { - return _container.back(); - } - - /** - * @brief Appends the given element value to the end of the container. The element is assigned through the - * assignment operator of the template type. The value is forwarded to the constructor as - * std::forward(value), meaning it is either moved (rvalue) or copied (lvalue). - * - * @note If after the operation the new size() is greater than old capacity() a reallocation takes place, - * in which case all iterators (including the end() iterator) and all references to the elements are invalidated. - * Otherwise only the end() iterator is invalidated. Uses a forward reference for the actual value passed, which - * allows the template method to be used by both lvalue and rvalues and compile a different implementation. - * - * @param value The value of the element to append. - */ - template - [[gnu::section(".stl_text")]] - auto push(U && value) -> void - { - _container.push_back(std::forward(value)); - } - - /** - * @brief Appends a new element to the end of the container. The element is constructed through a constructor of the - * template type. The arguments args... are forwarded to the constructor as std::forward(args).... - * - * If after the operation the new size() is greater than old capacity() a reallocation takes place, in which case - * all iterators (including the end() iterator) and all references to the elements are invalidated. Otherwise only - * the end() iterator is invalidated. Uses a forward reference for the actual value passed, which - * allows the template method to be used by both lvalue and rvalues and compile a different implementation. - * - * @tparam Args - * @param args Arguments to forward to the constructor of the element - * @return value_type& - */ - template - [[gnu::section(".stl_text")]] - auto emplace(Args &&... args) -> reference - { - _container.emplace_back(std::forward(args)...); - } - - /** - * @brief Removes the last element of the container. - * - * @note Calling pop_back on an empty container results in halting the - * further execution. Iterators and references to the last element are invalidated. The end() - * iterator is also invalidated. - */ - [[gnu::section(".stl_text")]] - auto pop() -> void - { - _container.pop_back(); - } - - /** - * @brief Wheter there are currently any items this container or not. - * - * @return True if there are no elements, false if there are. - */ - [[gnu::section(".stl_text")]] - auto empty() const -> bool - { - return _container.empty(); - } - - private: - container_type _container = {}; ///< Underlying container used by the stack to actually save the data. - }; - -} // namespace teachos::arch::stl - -#endif // TEACHOS_ARCH_X86_64_STL_STACK_HPP -- cgit v1.2.3 From 23b75cc23b8bab97eb2803e5110641c0f04bfc80 Mon Sep 17 00:00:00 2001 From: Felix Morgner Date: Mon, 14 Jul 2025 20:21:26 +0000 Subject: x86_64: remove forward_value_iterator --- .../include/arch/stl/forward_value_iterator.hpp | 121 --------------------- 1 file changed, 121 deletions(-) delete mode 100644 arch/x86_64/include/arch/stl/forward_value_iterator.hpp (limited to 'arch/x86_64') diff --git a/arch/x86_64/include/arch/stl/forward_value_iterator.hpp b/arch/x86_64/include/arch/stl/forward_value_iterator.hpp deleted file mode 100644 index be3d8e6..0000000 --- a/arch/x86_64/include/arch/stl/forward_value_iterator.hpp +++ /dev/null @@ -1,121 +0,0 @@ -#ifndef TEACHOS_ARCH_X86_64_STL_FORWARD_VALUE_ITERATOR_HPP -#define TEACHOS_ARCH_X86_64_STL_FORWARD_VALUE_ITERATOR_HPP - -#include - -namespace teachos::arch::stl -{ - /** - * @brief Concept for a type to have a post and prefix increment operator, that returns the correct type. - */ - template - concept Incrementable = requires(T t) { - { ++t } -> std::same_as; - { t++ } -> std::same_as; - }; - - /** - * @brief Iterable concept for the forward value iterator, meaning the type itself is incrementable and comparable. - */ - template - concept Iterable = std::regular && Incrementable; - - /** - * @brief Generic forward iterator for given template type. Allows to easily use this iterator - * instance in algorithm calls. - * - * @note Allows any value that itself can be incremented until we have reached the end, does not interact with the - * address of the value in any way. - * - * @tparam T Value the iterator contains. - */ - template - struct forward_value_iterator - { - using iterator_category = std::forward_iterator_tag; ///< Iterator category of this type. - using difference_type = std::ptrdiff_t; ///< Type when diving one instance of this iterator by another. - using value_type = T; ///< Underlying value contained by this iterator. - using const_reference_type = - value_type const &; ///< Constant reference to value returned by dereference * operation. - using const_pointer_type = value_type const *; ///< Constant pointer to value returned by arrow -> operation. - - /** - * @brief Defaulted constructor. - */ - forward_value_iterator() = default; - - /** - * @brief Constructor. - * - * @param value Underlying value the iterator contains. - */ - explicit forward_value_iterator(value_type value) - : value(value) - { - // Nothing to do - } - - /** - * @brief Returns the initally given value. - * - * @return Reference to the value. - */ - [[gnu::section(".stl_text")]] - auto operator*() const -> const_reference_type - { - return value; - } - - /** - * @brief Gets pointer to the underlying value passed intially. - * - * @return Pointer to the underlying value passed intially. - */ - [[gnu::section(".stl_text")]] - auto operator->() const -> const_pointer_type - { - return &value; - } - - /** - * @brief Pre increment operator. Returns a reference to the changed value. - * - * @return Reference to the incremented underlying value. - */ - [[gnu::section(".stl_text")]] - auto operator++() -> forward_value_iterator & - { - ++value; - return *this; - } - - /** - * @brief Post increment operator. Returns a copy of the value. - * - * @return Copy of the incremented underlying value. - */ - [[gnu::section(".stl_text")]] - auto operator++(int) -> forward_value_iterator - { - auto const old_value = *this; - ++value; - return old_value; - } - - /** - * @brief Defaulted comparsion operator. Simply compares the memory address of both iterators. - * - * @param other Other iterator to compare to. - * @return Whether both iterators point to the same underlying address in memory. - */ - [[gnu::section(".stl_text")]] - auto operator==(forward_value_iterator const & other) const -> bool = default; - - private: - value_type value = - {}; ///< Underlying value the iterator is currently pointing too and should increment or decrement. - }; - -} // namespace teachos::arch::stl - -#endif // TEACHOS_ARCH_X86_64_STL_FORWARD_VALUE_ITERATOR_HPP -- cgit v1.2.3 From 3a47a8bd0edcfa3aa03562d0a5c390ef85ad0c6b Mon Sep 17 00:00:00 2001 From: Felix Morgner Date: Mon, 14 Jul 2025 21:08:02 +0000 Subject: x86_64: move basic text output implementation --- arch/x86_64/CMakeLists.txt | 9 ++ arch/x86_64/include/arch/video/vga/io.hpp | 39 ------- arch/x86_64/include/arch/video/vga/text.hpp | 169 --------------------------- arch/x86_64/include/x86_64/vga/io.hpp | 39 +++++++ arch/x86_64/include/x86_64/vga/text.hpp | 173 ++++++++++++++++++++++++++++ arch/x86_64/src/io.cpp | 28 ++--- arch/x86_64/src/vga/text.cpp | 66 +++++++++++ arch/x86_64/src/video/vga/text.cpp | 66 ----------- 8 files changed, 301 insertions(+), 288 deletions(-) delete mode 100644 arch/x86_64/include/arch/video/vga/io.hpp delete mode 100644 arch/x86_64/include/arch/video/vga/text.hpp create mode 100644 arch/x86_64/include/x86_64/vga/io.hpp create mode 100644 arch/x86_64/include/x86_64/vga/text.hpp create mode 100644 arch/x86_64/src/vga/text.cpp delete mode 100644 arch/x86_64/src/video/vga/text.cpp (limited to 'arch/x86_64') diff --git a/arch/x86_64/CMakeLists.txt b/arch/x86_64/CMakeLists.txt index dd54b39..86c9559 100644 --- a/arch/x86_64/CMakeLists.txt +++ b/arch/x86_64/CMakeLists.txt @@ -7,6 +7,7 @@ target_include_directories("arch-x86_64" PUBLIC target_link_libraries("arch-x86_64" PUBLIC "arch::any" + "os::kern" "libs::multiboot2" ) @@ -39,6 +40,14 @@ target_sources("arch-x86_64" PRIVATE "src/boot/multiboot.s" ) +#[============================================================================[ +# VGA Code +#]============================================================================] + +target_sources("arch-x86_64" PRIVATE + "src/vga/text.cpp" +) + # #[============================================================================[ # # The Kernel Library # #]============================================================================] diff --git a/arch/x86_64/include/arch/video/vga/io.hpp b/arch/x86_64/include/arch/video/vga/io.hpp deleted file mode 100644 index c399fad..0000000 --- a/arch/x86_64/include/arch/video/vga/io.hpp +++ /dev/null @@ -1,39 +0,0 @@ -#ifndef TEACHOS_ARCH_X86_64_VIDEO_VGA_IO_HPP -#define TEACHOS_ARCH_X86_64_VIDEO_VGA_IO_HPP - -#include "arch/io/port_io.hpp" - -#include - -namespace teachos::arch::video::vga -{ - namespace crtc - { - /** - * @brief The address port of the CRT Controller. - */ - using address_port = arch::io::port<0x3d4, 1>; - - /** - * @brief The data port of the CRT Controller. - */ - using data_port = arch::io::port<0x3d5, 1>; - - namespace registers - { - /** - * @brief The address of the Cursor Start register of the CRTC. - */ - [[maybe_unused]] auto constexpr cursor_start = std::byte{0x0a}; - - /** - * @brief The address of the Cursor End register of the CRTC. - */ - [[maybe_unused]] auto constexpr curser_end = std::byte{0x0b}; - } // namespace registers - - }; // namespace crtc - -} // namespace teachos::arch::video::vga - -#endif // TEACHOS_ARCH_X86_64_VIDEO_VGA_IO_HPP diff --git a/arch/x86_64/include/arch/video/vga/text.hpp b/arch/x86_64/include/arch/video/vga/text.hpp deleted file mode 100644 index cfbf98f..0000000 --- a/arch/x86_64/include/arch/video/vga/text.hpp +++ /dev/null @@ -1,169 +0,0 @@ -#ifndef TEACHOS_ARCH_X86_64_VIDEO_VGA_TEXT_HPP -#define TEACHOS_ARCH_X86_64_VIDEO_VGA_TEXT_HPP - -#include -#include -#include - -namespace teachos::arch::video::vga::text -{ - auto constexpr DEFAULT_VGA_TEXT_BUFFER_ADDRESS = 0xB8000; - - /** - * @brief The colors available in the standard VGA text mode. - */ - enum struct color : std::uint8_t - { - black, ///< Equivalent to HTML color \#000000. - blue, ///< Equivalent to HTML color \#0000AA. - green, ///< Equivalent to HTML color \#00AA00. - cyan, ///< Equivalent to HTML color \#00AAAA. - red, ///< Equivalent to HTML color \#AA0000. - purple, ///< Equivalent to HTML color \#AA00AA. - brown, ///< Equivalent to HTML color \#AA5500. - gray, ///< Equivalent to HTML color \#AAAAAA. - }; - - /** - * @brief The foreground color modification flag. - */ - enum struct foreground_flag : bool - { - none, ///< Apply no flag e.g., keep color as is. - intense, ///< Make the color more intense (usually brighter). - }; - - /** - * @brief The background color modification flag. - */ - enum struct background_flag : bool - { - none, ///< Apply no flag e.g., keep color as is. - blink_or_bright, ///< Make the cell blink or more intense, dependent on the VGA configuration. - }; - - /** - * @brief The VGA text mode attribute. - * - * @note In the text mode of VGA, every code point being presented is followed by an attribute description. This - * allows for the modification of how the relevant "cell" is presented. - * - * @see vga::text::foreground_flag - * @see vga::text::background_flag - */ - struct attribute - { - color foreground_color : 3; ///< The foreground color of the cell, e.g. the color of the code point. - enum foreground_flag foreground_flag : 1; ///< The foreground color modification flag of the cell. - color bacground_color : 3; ///< The background color of the cell. - enum background_flag background_flag : 1; ///< The background color modification flag of the cell. - }; - - static_assert(sizeof(attribute) == 1, "The VGA text mode attribute must fit inside a single byte."); - - /** - * @brief Commonly used VGA text mode attributes. - */ - namespace common_attributes - { - /** - * @brief Make the affected cell display with a gray foreground and black background. - */ - [[maybe_unused]] auto constexpr gray_on_black = - attribute{color::gray, foreground_flag::none, color::black, background_flag::none}; - - /** - * @brief Make the affected cell display with a green foreground and black background. - */ - [[maybe_unused]] auto constexpr green_on_black = - attribute{color::green, foreground_flag::none, color::black, background_flag::none}; - - /** - * @brief Make the affected cell display with a white (gray + intense) foreground and red background. - */ - [[maybe_unused]] auto constexpr white_on_red = - attribute{color::gray, foreground_flag::intense, color::red, background_flag::none}; - } // namespace common_attributes - - /** - * @brief Clear the VGA text mode buffer. - * - * @note This function also resets the text mode buffer pointer. - * - * @param attribute The attribute to "clear" the screen with. - */ - auto clear(attribute attribute = common_attributes::gray_on_black) -> void; - - /** - * @brief Enable or disable the VGA text mode cursor. - * - * @param enabled Whether or not to enable the cursors. - */ - auto cursor(bool enabled) -> void; - - /** - * @brief Move the cursor to a new line, scrolling the buffer if necessary. - */ - auto newline() -> void; - - /** - * @brief Write a string of code points to the VGA text buffer. - * - * @note This function also updates the text mode buffer pointer. - * - * @param code_points A string of (8-bit) code points to write to the VGA text mode buffer. - * @param attribute The attribute to apply to the written sequence of code points. - * @see vga::text::attribute - */ - auto write(std::string_view code_points, attribute attribute) -> void; - - /** - * @brief Write a single character to the VGA text buffer. - * - * @note This function also updates the text mode buffer pointer. - * - * @param code_point A code point to write to the VGA text mode buffer. - * @param attribute The attribute to apply to the written sequence of code points. - * @see vga::text::attribute - */ - auto write_char(char code_point, attribute attribute) -> void; - - template - concept Integral = std::is_integral_v; - - /** - * @brief Write a integral value to the VGA text buffer. - * - * @note This function also updates the text mode buffer pointer. - * - * @param value A integral value to write to the VGA text mode buffer. - * @param attribute The attribute to apply to the written sequence of code points. - * @see vga::text::attribute - */ - template - auto write_number(T value, attribute attribute) -> void - { - T current_value = value; - T divisor = 1; - - while (current_value > 9) - { - divisor *= 10; - current_value = current_value / 10; - } - - current_value = value; - while (divisor > 0) - { - uint8_t quotient = current_value / divisor; - char ascii_digit = quotient + '0'; - - write_char(ascii_digit, attribute); - current_value %= divisor; - divisor /= 10; - } - } - -} // namespace teachos::arch::video::vga::text - -#endif // TEACHOS_ARCH_X86_64_VIDEO_VGA_TEXT_HPP \ No newline at end of file diff --git a/arch/x86_64/include/x86_64/vga/io.hpp b/arch/x86_64/include/x86_64/vga/io.hpp new file mode 100644 index 0000000..803dc21 --- /dev/null +++ b/arch/x86_64/include/x86_64/vga/io.hpp @@ -0,0 +1,39 @@ +#ifndef TEACHOS_X86_64_VGA_IO_HPP +#define TEACHOS_X86_64_VGA_IO_HPP + +#include "arch/io/port_io.hpp" + +#include + +namespace teachos::x86_64::vga::io +{ + namespace crtc + { + /** + * @brief The address port of the CRT Controller. + */ + using address_port = arch::io::port<0x3d4, 1>; + + /** + * @brief The data port of the CRT Controller. + */ + using data_port = arch::io::port<0x3d5, 1>; + + namespace registers + { + /** + * @brief The address of the Cursor Start register of the CRTC. + */ + [[maybe_unused]] auto constexpr cursor_start = std::byte{0x0a}; + + /** + * @brief The address of the Cursor End register of the CRTC. + */ + [[maybe_unused]] auto constexpr cursor_end = std::byte{0x0b}; + } // namespace registers + + }; // namespace crtc + +} // namespace teachos::x86_64::vga::io + +#endif \ No newline at end of file diff --git a/arch/x86_64/include/x86_64/vga/text.hpp b/arch/x86_64/include/x86_64/vga/text.hpp new file mode 100644 index 0000000..267eae9 --- /dev/null +++ b/arch/x86_64/include/x86_64/vga/text.hpp @@ -0,0 +1,173 @@ +#ifndef TEACHOS_X86_64_VIDEO_VGA_TEXT_HPP +#define TEACHOS_X86_64_VIDEO_VGA_TEXT_HPP + +#include +#include +#include + +namespace teachos::x86_64::vga::text +{ + /** + * @brief The colors available in the standard VGA text mode. + */ + enum struct color : std::uint8_t + { + black, ///< Equivalent to HTML color \#000000. + blue, ///< Equivalent to HTML color \#0000AA. + green, ///< Equivalent to HTML color \#00AA00. + cyan, ///< Equivalent to HTML color \#00AAAA. + red, ///< Equivalent to HTML color \#AA0000. + purple, ///< Equivalent to HTML color \#AA00AA. + brown, ///< Equivalent to HTML color \#AA5500. + gray, ///< Equivalent to HTML color \#AAAAAA. + }; + + /** + * @brief The foreground color modification flag. + */ + enum struct foreground_flag : bool + { + none, ///< Apply no flag e.g., keep color as is. + intense, ///< Make the color more intense (usually brighter). + }; + + /** + * @brief The background color modification flag. + */ + enum struct background_flag : bool + { + none, ///< Apply no flag e.g., keep color as is. + blink_or_bright, ///< Make the cell blink or more intense, dependent on the VGA configuration. + }; + + /** + * @brief The VGA text mode attribute. + * + * @note In the text mode of VGA, every code point being presented is followed by an attribute description. This + * allows for the modification of how the relevant "cell" is presented. + * + * @see vga::text::foreground_flag + * @see vga::text::background_flag + */ + struct attribute + { + color foreground_color : 3; ///< The foreground color of the cell, e.g. the color of the code point. + enum foreground_flag foreground_flag : 1; ///< The foreground color modification flag of the cell. + color bacground_color : 3; ///< The background color of the cell. + enum background_flag background_flag : 1; ///< The background color modification flag of the cell. + }; + + static_assert(sizeof(attribute) == 1, "The VGA text mode attribute must fit inside a single byte."); + + /** + * @brief Commonly used VGA text mode attributes. + */ + namespace common_attributes + { + /** + * @brief Make the affected cell display with a gray foreground and black background. + */ + [[maybe_unused]] auto constexpr gray_on_black = + attribute{color::gray, foreground_flag::none, color::black, background_flag::none}; + + /** + * @brief Make the affected cell display with a green foreground and black background. + */ + [[maybe_unused]] auto constexpr green_on_black = + attribute{color::green, foreground_flag::none, color::black, background_flag::none}; + + /** + * @brief Make the affected cell display with a green foreground and black background. + */ + [[maybe_unused]] auto constexpr red_on_black = + attribute{color::red, foreground_flag::none, color::black, background_flag::none}; + + /** + * @brief Make the affected cell display with a white (gray + intense) foreground and red background. + */ + [[maybe_unused]] auto constexpr white_on_red = + attribute{color::gray, foreground_flag::intense, color::red, background_flag::none}; + } // namespace common_attributes + + /** + * @brief Clear the VGA text mode buffer. + * + * @note This function also resets the text mode buffer pointer. + * + * @param attribute The attribute to "clear" the screen with. + */ + auto clear(attribute attribute = common_attributes::gray_on_black) -> void; + + /** + * @brief Enable or disable the VGA text mode cursor. + * + * @param enabled Whether or not to enable the cursors. + */ + auto cursor(bool enabled) -> void; + + /** + * @brief Move the cursor to a new line, scrolling the buffer if necessary. + */ + auto newline() -> void; + + /** + * @brief Write a string of code points to the VGA text buffer. + * + * @note This function also updates the text mode buffer pointer. + * + * @param code_points A string of (8-bit) code points to write to the VGA text mode buffer. + * @param attribute The attribute to apply to the written sequence of code points. + * @see vga::text::attribute + */ + auto write(std::string_view code_points, attribute attribute) -> void; + + /** + * @brief Write a single character to the VGA text buffer. + * + * @note This function also updates the text mode buffer pointer. + * + * @param code_point A code point to write to the VGA text mode buffer. + * @param attribute The attribute to apply to the written sequence of code points. + * @see vga::text::attribute + */ + auto write_char(char code_point, attribute attribute) -> void; + + template + concept Integral = std::is_integral_v; + + /** + * @brief Write a integral value to the VGA text buffer. + * + * @note This function also updates the text mode buffer pointer. + * + * @param value A integral value to write to the VGA text mode buffer. + * @param attribute The attribute to apply to the written sequence of code points. + * @see vga::text::attribute + */ + template + auto write_number(T value, attribute attribute) -> void + { + T current_value = value; + T divisor = 1; + + while (current_value > 9) + { + divisor *= 10; + current_value = current_value / 10; + } + + current_value = value; + while (divisor > 0) + { + uint8_t quotient = current_value / divisor; + char ascii_digit = quotient + '0'; + + write_char(ascii_digit, attribute); + current_value %= divisor; + divisor /= 10; + } + } + +} // namespace teachos::x86_64::vga::text + +#endif // TEACHOS_ARCH_X86_64_VIDEO_VGA_TEXT_HPP \ No newline at end of file diff --git a/arch/x86_64/src/io.cpp b/arch/x86_64/src/io.cpp index 8808dbb..5fb1c85 100644 --- a/arch/x86_64/src/io.cpp +++ b/arch/x86_64/src/io.cpp @@ -1,22 +1,22 @@ +#include "kern/print.hpp" +#include "x86_64/vga/text.hpp" + namespace teachos::arch::io { - // using x86_64::vga::text_mode::attributes; - // using x86_64::vga::text_mode::color; - - // namespace - // { - // auto constexpr error_attributes = - // attributes{.foreground = color::light_gray, .bright = true, .background = color::red, .blink = true}; - // } // namespace - auto init() -> void { - // kernel::set_print_handler([](auto text) { return x86_64::vga::text_mode::print(text); }); - // kernel::set_println_handler([](auto text) { return x86_64::vga::text_mode::println(text); }); - // kernel::set_print_error_handler([](auto text) { return x86_64::vga::text_mode::print(text, error_attributes); }); - // kernel::set_println_error_handler( - // [](auto text) { return x86_64::vga::text_mode::println(text, error_attributes); }); + teachos::set_print_handler( + [](auto text) { return x86_64::vga::text::write(text, x86_64::vga::text::common_attributes::green_on_black); }); + teachos::set_println_handler( + [](auto text) { return x86_64::vga::text::write(text, x86_64::vga::text::common_attributes::green_on_black); }); + + teachos::set_print_error_handler( + [](auto text) { return x86_64::vga::text::write(text, x86_64::vga::text::common_attributes::red_on_black); }); + teachos::set_println_error_handler( + [](auto text) { return x86_64::vga::text::write(text, x86_64::vga::text::common_attributes::red_on_black); }); + + teachos::println("[x86-64] Basic VGA text output initialized."); } } // namespace teachos::arch::io diff --git a/arch/x86_64/src/vga/text.cpp b/arch/x86_64/src/vga/text.cpp new file mode 100644 index 0000000..9b7946d --- /dev/null +++ b/arch/x86_64/src/vga/text.cpp @@ -0,0 +1,66 @@ +#include "x86_64/vga/text.hpp" + +#include "arch/asm_pointer.hpp" +#include "x86_64/vga/io.hpp" + +#include +#include +#include +#include + +extern "C" teachos::arch::asm_pointer> vga_buffer_pointer; + +namespace teachos::x86_64::vga::text +{ + namespace + { + // auto constexpr DEFAULT_VGA_TEXT_BUFFER_ADDRESS = 0xB8000; + + auto buffer_offset = std::ptrdiff_t{}; + + auto constexpr DEFAULT_TEXT_BUFFER_WIDTH = 80U; + auto constexpr DEFAULT_TEXT_BUFFER_HEIGHT = 25U; + } // namespace + + auto clear(attribute attribute) -> void + { + buffer_offset = 0; + std::ranges::fill_n(vga_buffer_pointer.get(), 2000, std::pair{' ', attribute}); + } + + auto cursor(bool enabled) -> void + { + auto cursor_disable_byte = std::byte{!enabled} << 5; + + io::crtc::address_port::write(io::crtc::registers::cursor_start); + io::crtc::data_port::write(io::crtc::data_port::read() | cursor_disable_byte); + } + + auto newline() -> void + { + auto current_line = buffer_offset / DEFAULT_TEXT_BUFFER_WIDTH; + auto next_line = current_line + 1; + + if (next_line >= DEFAULT_TEXT_BUFFER_HEIGHT) + { + auto begin = vga_buffer_pointer + DEFAULT_TEXT_BUFFER_WIDTH; + auto end = vga_buffer_pointer + DEFAULT_TEXT_BUFFER_WIDTH * DEFAULT_TEXT_BUFFER_HEIGHT; + std::ranges::move(begin, end, vga_buffer_pointer.get()); + buffer_offset = current_line * DEFAULT_TEXT_BUFFER_WIDTH; + } + else + { + buffer_offset = next_line * DEFAULT_TEXT_BUFFER_WIDTH; + } + } + + auto write_char(char code_point, attribute attribute) -> void + { + vga_buffer_pointer[buffer_offset++] = std::pair{code_point, attribute}; + }; + + auto write(std::string_view code_points, attribute attribute) -> void + { + std::ranges::for_each(code_points, [&](auto code_point) { write_char(code_point, attribute); }); + } +} // namespace teachos::x86_64::vga::text diff --git a/arch/x86_64/src/video/vga/text.cpp b/arch/x86_64/src/video/vga/text.cpp deleted file mode 100644 index b070a0a..0000000 --- a/arch/x86_64/src/video/vga/text.cpp +++ /dev/null @@ -1,66 +0,0 @@ -#include "arch/video/vga/text.hpp" - -#include "arch/video/vga/io.hpp" -#include "memory/asm_pointer.hpp" - -#include -#include -#include - -extern "C" std::pair * vga_buffer_pointer; - -namespace teachos::arch::video::vga::text -{ - namespace - { - auto constexpr DEFAULT_TEXT_BUFFER_WIDTH = 80U; - auto constexpr DEFAULT_TEXT_BUFFER_HEIGHT = 25U; - - auto constinit text_buffer = teachos::memory::asm_pointer{vga_buffer_pointer}; - } // namespace - - auto clear(attribute attribute) -> void - { - *text_buffer = reinterpret_cast(DEFAULT_VGA_TEXT_BUFFER_ADDRESS); - std::ranges::fill_n(*text_buffer, 2000, std::pair{' ', attribute}); - } - - auto cursor(bool enabled) -> void - { - auto cursor_disable_byte = std::byte{!enabled} << 5; - - crtc::address_port::write(crtc::registers::cursor_start); - crtc::data_port::write(vga::crtc::data_port::read() | cursor_disable_byte); - } - - auto newline() -> void - { - auto base = reinterpret_cast(DEFAULT_VGA_TEXT_BUFFER_ADDRESS); - auto & raw_buffer = *text_buffer; - auto current_line = (raw_buffer - base) / DEFAULT_TEXT_BUFFER_WIDTH; - auto next_line = current_line + 1; - - if (next_line >= DEFAULT_TEXT_BUFFER_HEIGHT) - { - auto begin = base + DEFAULT_TEXT_BUFFER_WIDTH; - auto end = base + DEFAULT_TEXT_BUFFER_WIDTH * DEFAULT_TEXT_BUFFER_HEIGHT; - std::ranges::move(begin, end, base); - raw_buffer = base + current_line * DEFAULT_TEXT_BUFFER_WIDTH; - } - else - { - raw_buffer = base + next_line * DEFAULT_TEXT_BUFFER_WIDTH; - } - } - - auto write_char(char code_point, attribute attribute) -> void - { - auto & p = *text_buffer; - (*p++) = std::pair{code_point, attribute}; - }; - - auto write(std::string_view code_points, attribute attribute) -> void - { - std::ranges::for_each(code_points, [&](auto code_point) { write_char(code_point, attribute); }); - } -} // namespace teachos::arch::video::vga::text -- cgit v1.2.3 From 05ac8c2bdd000d27b38411db2223eabb649c318f Mon Sep 17 00:00:00 2001 From: Felix Morgner Date: Mon, 14 Jul 2025 21:29:10 +0000 Subject: build: reintroduce bootable ISO --- arch/x86_64/src/boot/boot.s | 2 +- arch/x86_64/src/io.cpp | 14 ++++++++++---- arch/x86_64/src/vga/text.cpp | 2 -- arch/x86_64/support/grub.cfg.in | 2 +- 4 files changed, 12 insertions(+), 8 deletions(-) (limited to 'arch/x86_64') diff --git a/arch/x86_64/src/boot/boot.s b/arch/x86_64/src/boot/boot.s index 5488073..728380d 100644 --- a/arch/x86_64/src/boot/boot.s +++ b/arch/x86_64/src/boot/boot.s @@ -105,7 +105,7 @@ mesage_long_mode_not_supported: * We need a pointer to our current position in the VGA text buffer. */ .global vga_buffer_pointer -vga_buffer_pointer: .long 0xb8000 +vga_buffer_pointer: .quad 0xb8000 /** * Code for the bootstrapping process. diff --git a/arch/x86_64/src/io.cpp b/arch/x86_64/src/io.cpp index 5fb1c85..8e9e411 100644 --- a/arch/x86_64/src/io.cpp +++ b/arch/x86_64/src/io.cpp @@ -6,15 +6,21 @@ namespace teachos::arch::io auto init() -> void { + x86_64::vga::text::cursor(false); + teachos::set_print_handler( [](auto text) { return x86_64::vga::text::write(text, x86_64::vga::text::common_attributes::green_on_black); }); - teachos::set_println_handler( - [](auto text) { return x86_64::vga::text::write(text, x86_64::vga::text::common_attributes::green_on_black); }); + teachos::set_println_handler([](auto text) { + x86_64::vga::text::write(text, x86_64::vga::text::common_attributes::green_on_black); + x86_64::vga::text::newline(); + }); teachos::set_print_error_handler( [](auto text) { return x86_64::vga::text::write(text, x86_64::vga::text::common_attributes::red_on_black); }); - teachos::set_println_error_handler( - [](auto text) { return x86_64::vga::text::write(text, x86_64::vga::text::common_attributes::red_on_black); }); + teachos::set_println_error_handler([](auto text) { + x86_64::vga::text::write(text, x86_64::vga::text::common_attributes::red_on_black); + x86_64::vga::text::newline(); + }); teachos::println("[x86-64] Basic VGA text output initialized."); } diff --git a/arch/x86_64/src/vga/text.cpp b/arch/x86_64/src/vga/text.cpp index 9b7946d..16abf08 100644 --- a/arch/x86_64/src/vga/text.cpp +++ b/arch/x86_64/src/vga/text.cpp @@ -14,8 +14,6 @@ namespace teachos::x86_64::vga::text { namespace { - // auto constexpr DEFAULT_VGA_TEXT_BUFFER_ADDRESS = 0xB8000; - auto buffer_offset = std::ptrdiff_t{}; auto constexpr DEFAULT_TEXT_BUFFER_WIDTH = 80U; diff --git a/arch/x86_64/support/grub.cfg.in b/arch/x86_64/support/grub.cfg.in index 86674dd..49f19ce 100644 --- a/arch/x86_64/support/grub.cfg.in +++ b/arch/x86_64/support/grub.cfg.in @@ -2,6 +2,6 @@ timeout=2 default=0 menuentry "TeachOS" { - multiboot2 /$ + multiboot2 /$ boot } \ No newline at end of file -- cgit v1.2.3 From a832505d9696ae66248b53602d41637bef4868aa Mon Sep 17 00:00:00 2001 From: Felix Morgner Date: Fri, 18 Jul 2025 10:49:03 +0000 Subject: kernel: turn into a PIE --- arch/x86_64/src/boot/boot.s | 112 ++++++++++++++++++++++++++++++++------------ 1 file changed, 82 insertions(+), 30 deletions(-) (limited to 'arch/x86_64') diff --git a/arch/x86_64/src/boot/boot.s b/arch/x86_64/src/boot/boot.s index 728380d..ada6426 100644 --- a/arch/x86_64/src/boot/boot.s +++ b/arch/x86_64/src/boot/boot.s @@ -71,16 +71,7 @@ stack_top: global_descriptor_table: .quad 0 global_descriptor_table_code = . - global_descriptor_table .quad (1<<43) | (1<<44) | (1<<47) | (1<<53) - -/** - * We also need a pointer that we can load into the GDTR. - * - * The pointer consists of a word describing the size of the table minus 1 and - * the pointer to the actual table. - */ -global_descriptor_table_pointer: -.word . - global_descriptor_table - 1 -.quad global_descriptor_table +global_descriptor_table_end: /** * We are going to print some messages in case we panic during boot, so we are @@ -130,7 +121,12 @@ _panic: push %ebp mov %esp, %ebp - push message_prefix_panic + call .Lpanic_get_ip +.Lpanic_get_ip: + pop %eax + lea (message_prefix_panic - .Lpanic_get_ip)(%eax), %eax + + push %eax push $0x4f call _print add $8, %esp @@ -155,10 +151,17 @@ _print: push %ebx push %esi + push %edi + + call .Lprint_get_ip +.Lprint_get_ip: + pop %edi + lea (vga_buffer_pointer - .Lprint_get_ip)(%edi), %edi + mov 8(%ebp), %eax mov 12(%ebp), %ebx mov $0, %ecx - mov (vga_buffer_pointer), %esi + mov (%edi), %esi .Lprint_loop: mov (%ebx, %ecx), %dl @@ -171,9 +174,11 @@ _print: .Lupdate_vga_buffer_address: shl $1, %ecx - add %ecx, (vga_buffer_pointer) + add %ecx, %esi + mov %esi, (%edi) .Lprint_end: + pop %edi pop %esi pop %ebx mov %ebp, %esp @@ -187,7 +192,12 @@ _print: */ .global _start _start: - mov $stack_top, %esp + call .Lstart_get_ip +.Lstart_get_ip: + pop %esi + lea (stack_top - .Lstart_get_ip)(%esi), %ebx + + mov %ebx, %esp mov %esp, %ebp call assert_loaded_by_multiboot2_loader @@ -197,8 +207,19 @@ _start: call enable_paging call enable_sse - lgdt (global_descriptor_table_pointer) - jmp $global_descriptor_table_code, $_transition_to_long_mode + sub $10, %esp + lea (global_descriptor_table - .Lstart_get_ip)(%esi), %eax + movw $(global_descriptor_table_end - global_descriptor_table -1), (%esp) + mov %eax, 2(%esp) + movl $0, 6(%esp) + + lgdt (%esp) + add $10, %esp + + lea (_transition_to_long_mode - .Lstart_get_ip)(%esi), %eax + pushl $global_descriptor_table_code + pushl %eax + lret call halt @@ -217,7 +238,11 @@ assert_cpu_supports_long_mode: jz .Llong_mode_assertion_failed ret .Llong_mode_assertion_failed: - push $mesage_long_mode_not_supported + call .Lassert_cpu_supports_long_mode_fail_get_ip +.Lassert_cpu_supports_long_mode_fail_get_ip: + pop %eax + lea (mesage_long_mode_not_supported - .Lassert_cpu_supports_long_mode_fail_get_ip)(%eax), %eax + push %eax call _panic /** @@ -245,7 +270,11 @@ assert_cpuid_instruction_is_supported: je .Lcpuid_assertion_fail ret .Lcpuid_assertion_fail: - push $message_cpuid_instruction_no_supported + call .Lassert_cpuid_instruction_is_supported_fail_get_ip +.Lassert_cpuid_instruction_is_supported_fail_get_ip: + pop %eax + lea (message_cpuid_instruction_no_supported - .Lassert_cpuid_instruction_is_supported_fail_get_ip)(%eax), %eax + push %eax call _panic /** @@ -259,10 +288,18 @@ assert_loaded_by_multiboot2_loader: cmp $0x36d76289, %eax /* Check if we received the expected magic */ jne .Lmultiboot2_assertion_fail /* Panic otherwise */ - mov %ebx, multiboot_information_pointer /* Store the MBI pointer */ + call .Lassert_loaded_by_multiboot2_loader_get_ip +.Lassert_loaded_by_multiboot2_loader_get_ip: + pop %eax + lea (multiboot_information_pointer - .Lassert_loaded_by_multiboot2_loader_get_ip)(%eax), %eax + mov %ebx, (%eax) /* Store the MBI pointer */ ret .Lmultiboot2_assertion_fail: - push $message_not_loaded_by_multiboot2 + call .Lassert_loaded_by_multiboot2_loader_fail_get_ip +.Lassert_loaded_by_multiboot2_loader_fail_get_ip: + pop %eax + lea (message_not_loaded_by_multiboot2 - .Lassert_loaded_by_multiboot2_loader_fail_get_ip)(%eax), %eax + push %eax call _panic /** @@ -272,7 +309,10 @@ assert_loaded_by_multiboot2_loader: * set up for use. */ enable_paging: - mov $page_map_level_4, %eax + call .Lenable_paging_get_ip +.Lenable_paging_get_ip: + pop %eax +lea (page_map_level_4 - .Lenable_paging_get_ip)(%eax), %eax mov %eax, %cr3 /* Enable Physical Address Extension */ @@ -316,37 +356,46 @@ enable_sse: * page map entries. */ prepare_page_maps: + call .Lprepare_page_maps_get_ip +.Lprepare_page_maps_get_ip: + pop %edi /* Map the P4 table recursively */ - mov $page_map_level_4, %eax + lea (page_map_level_4 - .Lprepare_page_maps_get_ip)(%edi), %eax + mov %eax, %ebx or $0b11, %eax /* Write present + writable flags into eax register */ - mov %eax, (page_map_level_4 + 511 * 8) + mov %eax, (511 * 8)(%ebx) /* Add an entry to the PML4, pointing to the PML3 */ - mov $page_map_level_3, %eax + lea (page_map_level_3 - .Lprepare_page_maps_get_ip)(%edi), %eax + lea (page_map_level_4 - .Lprepare_page_maps_get_ip)(%edi), %ebx or $0x3, %eax - mov %eax, (page_map_level_4 + ((0x0000000000100000 >> 39) & 0x1ff) * 8) + mov %eax, (((0x0000000000100000 >> 39) & 0x1ff) * 8)(%ebx) /* Add an entry to the PML3, pointing to the PML2 */ - mov $page_map_level_2, %eax + lea (page_map_level_2 - .Lprepare_page_maps_get_ip)(%edi), %eax + lea (page_map_level_3 - .Lprepare_page_maps_get_ip)(%edi), %ebx or $0x3, %eax - mov %eax, (page_map_level_3 + ((0x0000000000100000 >> 30) & 0x1ff) * 8) + mov %eax, (((0x0000000000100000 >> 30) & 0x1ff) * 8)(%ebx) xor %ecx, %ecx - mov $_end_linear, %esi + push %esi + lea (_end_linear - .Lprepare_page_maps_get_ip)(%edi), %esi shr $21, %esi add $2, %esi .Lmap_pages: + lea (page_map_level_2 - .Lprepare_page_maps_get_ip)(%edi), %ebx mov $(1 << 21), %eax mul %ecx or $((1 << 0) | (1 << 1) | (1 << 7)), %eax - mov %eax, page_map_level_2(,%ecx,8) + mov %eax, (%ebx,%ecx,8) inc %ecx cmp %esi, %ecx jne .Lmap_pages + pop %esi ret .section .boot_text, "ax", @progbits @@ -360,7 +409,10 @@ _transition_to_long_mode: mov %rax, %fs mov %rax, %gs - movl $0xb8000, (vga_buffer_pointer) + leaq vga_buffer_pointer(%rip), %rax + movl $0xb8000, (%rax) + + xor %rax, %rax call _init -- cgit v1.2.3 From 14ed096fc5de6844cb116f3319c0d03043d26ea2 Mon Sep 17 00:00:00 2001 From: Felix Morgner Date: Thu, 17 Jul 2025 21:09:02 +0000 Subject: x86-64: prepare new architecture --- arch/x86_64/CMakeLists.txt | 8 ++ .../arch/memory/allocator/area_frame_allocator.hpp | 12 +- .../arch/memory/allocator/physical_frame.hpp | 86 ------------ arch/x86_64/include/x86_64/memory/address.hpp | 47 +++++++ arch/x86_64/include/x86_64/memory/frame.hpp | 95 +++++++++++++ .../include/x86_64/memory/region_allocator.hpp | 76 +++++++++++ arch/x86_64/scripts/x86-64-linker-script.txt | 150 +++++++++++++++++++++ arch/x86_64/src/boot/boot.s | 2 +- arch/x86_64/src/memory.cpp | 90 ++++++------- .../x86_64/src/memory/allocator/physical_frame.cpp | 24 ---- arch/x86_64/src/memory/region_allocator.cpp | 95 +++++++++++++ 11 files changed, 519 insertions(+), 166 deletions(-) delete mode 100644 arch/x86_64/include/arch/memory/allocator/physical_frame.hpp create mode 100644 arch/x86_64/include/x86_64/memory/address.hpp create mode 100644 arch/x86_64/include/x86_64/memory/frame.hpp create mode 100644 arch/x86_64/include/x86_64/memory/region_allocator.hpp create mode 100644 arch/x86_64/scripts/x86-64-linker-script.txt delete mode 100644 arch/x86_64/src/memory/allocator/physical_frame.cpp create mode 100644 arch/x86_64/src/memory/region_allocator.cpp (limited to 'arch/x86_64') diff --git a/arch/x86_64/CMakeLists.txt b/arch/x86_64/CMakeLists.txt index 86c9559..d1af777 100644 --- a/arch/x86_64/CMakeLists.txt +++ b/arch/x86_64/CMakeLists.txt @@ -40,6 +40,14 @@ target_sources("arch-x86_64" PRIVATE "src/boot/multiboot.s" ) +#[============================================================================[ +# Memory Code +#]============================================================================] + +target_sources("arch-x86_64" PRIVATE + "src/memory/region_allocator.cpp" +) + #[============================================================================[ # VGA Code #]============================================================================] diff --git a/arch/x86_64/include/arch/memory/allocator/area_frame_allocator.hpp b/arch/x86_64/include/arch/memory/allocator/area_frame_allocator.hpp index 6cb5f56..a86c9b7 100644 --- a/arch/x86_64/include/arch/memory/allocator/area_frame_allocator.hpp +++ b/arch/x86_64/include/arch/memory/allocator/area_frame_allocator.hpp @@ -1,12 +1,12 @@ -#ifndef TEACHOS_ARCH_X86_64_MEMORY_ALLOCATOR_AREA_FRAME_ALLOCATOR_HPP -#define TEACHOS_ARCH_X86_64_MEMORY_ALLOCATOR_AREA_FRAME_ALLOCATOR_HPP +#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 "arch/memory/allocator/physical_frame.hpp" +// #include "arch/memory/multiboot/reader.hpp" #include -namespace teachos::arch::memory::allocator +namespace x86_64::memory { /** * @brief Allocates memory linearly using memory areas read from the multiboot2 information pointer and leaks any @@ -62,6 +62,6 @@ namespace teachos::arch::memory::allocator physical_frame const multiboot_end; ///< The end address of the multiboot code in memory. }; -} // namespace teachos::arch::memory::allocator +} // namespace x86_64::memory #endif // TEACHOS_ARCH_X86_64_MEMORY_ALLOCATOR_AREA_FRAME_ALLOCATOR_HPP diff --git a/arch/x86_64/include/arch/memory/allocator/physical_frame.hpp b/arch/x86_64/include/arch/memory/allocator/physical_frame.hpp deleted file mode 100644 index cb6c5b3..0000000 --- a/arch/x86_64/include/arch/memory/allocator/physical_frame.hpp +++ /dev/null @@ -1,86 +0,0 @@ -#ifndef TEACHOS_ARCH_X86_64_MEMORY_ALLOCATOR_PHYSICAL_FRAME_HPP -#define TEACHOS_ARCH_X86_64_MEMORY_ALLOCATOR_PHYSICAL_FRAME_HPP - -#include "arch/stl/container.hpp" -#include "arch/stl/forward_value_iterator.hpp" - -#include -#include -#include - -namespace teachos::arch::memory::allocator -{ - using physical_address = std::size_t; - - std::size_t constexpr PAGE_FRAME_SIZE = 4096U; ///< Default page size of x86_84 is always 4KiB. - - /** - * @brief Specific physical frame containing helper functions to determine if a specific address is in that - * physical frame or not. - */ - struct physical_frame - { - /** - * @brief Defaulted constructor. - */ - constexpr physical_frame() = default; - - /** - * @brief Constructor. - * - * @param frame_number Index number that should be assigned to this physical frame. - */ - explicit constexpr physical_frame(std::size_t frame_number) - : frame_number(frame_number) - { - // Nothing to do - } - - /** - * @brief Returns the physical frame the given address is contained in. - * - * @param address Physical address we want to get the corresponding physical frame for. - * @return Frame the given address is contained in. - */ - auto static containing_address(physical_address address) -> physical_frame; - - /** - * @brief Get the start address of this physical frame. - * - * @return Start address of the physical frame. - */ - auto start_address() const -> physical_address; - - /** - * @brief Post increment operator. Returns a copy of the value. - * - * @return Copy of the incremented underlying frame number. - */ - auto operator++(int) -> physical_frame; - - /** - * @brief Pre increment operator. Returns a reference to the changed value. - * - * @return Reference to the incremented underlying frame number. - */ - auto operator++() -> physical_frame &; - - /** - * @brief Defaulted equals operator. - */ - auto operator==(physical_frame const & other) const -> bool = default; - - /** - * @brief Defaulted three-way comparsion operator. - */ - auto operator<=>(physical_frame const & other) const -> std::partial_ordering = default; - - std::size_t frame_number = - {}; ///< Index number of the current physical frame, used to distinguish it from other frames. - }; - - using frame_container = stl::container>; - -} // namespace teachos::arch::memory::allocator - -#endif // TEACHOS_ARCH_X86_64_MEMORY_ALLOCATOR_PHYSICAL_FRAME_HPP diff --git a/arch/x86_64/include/x86_64/memory/address.hpp b/arch/x86_64/include/x86_64/memory/address.hpp new file mode 100644 index 0000000..20e9655 --- /dev/null +++ b/arch/x86_64/include/x86_64/memory/address.hpp @@ -0,0 +1,47 @@ +#ifndef TEACHOS_X86_64_MEMORY_ADDRESS_HPP +#define TEACHOS_X86_64_MEMORY_ADDRESS_HPP + +#include +#include +#include +#include + +namespace teachos::x86_64::memory +{ + + enum struct address_type : bool + { + linear, + physical, + }; + + template + struct address + { + constexpr explicit address(std::uintptr_t value) noexcept + : m_value{value} + { + } + + explicit address(std::byte * pointer) noexcept + : m_value{std::bit_cast(pointer)} + { + } + + explicit operator std::byte *() const noexcept { return std::bit_cast(m_value); } + + auto constexpr operator<=>(address const &) const noexcept -> std::strong_ordering = default; + auto constexpr operator==(address const &) const noexcept -> bool = default; + + auto constexpr raw() const noexcept -> std::uintptr_t { return m_value; } + + private: + std::uintptr_t m_value{}; + }; + + using linear_address = address; + using physical_address = address; + +} // namespace teachos::x86_64::memory + +#endif \ No newline at end of file diff --git a/arch/x86_64/include/x86_64/memory/frame.hpp b/arch/x86_64/include/x86_64/memory/frame.hpp new file mode 100644 index 0000000..21565fd --- /dev/null +++ b/arch/x86_64/include/x86_64/memory/frame.hpp @@ -0,0 +1,95 @@ +#ifndef TEACHOS_X86_64_MEMORY_FRAME_HPP +#define TEACHOS_X86_64_MEMORY_FRAME_HPP + +#include "x86_64/memory/address.hpp" + +#include +#include + +namespace teachos::x86_64::memory +{ + /** + * @brief Specific physical frame containing helper functions to determine if a specific address is in that + * physical frame or not. + */ + struct frame + { + auto static inline constexpr DEFAULT_SIZE = std::size_t{4096}; ///< Default page size of x86_84 is always 4KiB. + + /** + * @brief Defaulted constructor. + */ + constexpr frame() = default; + + /** + * @brief Constructor. + * + * @param frame_number Index number that should be assigned to this physical frame. + */ + explicit constexpr frame(std::size_t number) + : m_number(number) + { + } + + /** + * @brief Returns the physical frame the given address is contained in. + * + * @param address Physical address we want to get the corresponding physical frame for. + * @return Frame the given address is contained in. + */ + auto constexpr static containing(physical_address address) noexcept -> frame + { + return frame{address.raw() / DEFAULT_SIZE}; + } + + /** + * @brief Get the start address of this physical frame. + * + * @return Start address of the physical frame. + */ + auto constexpr start_address() const noexcept -> physical_address + { + return physical_address{m_number * DEFAULT_SIZE}; + } + + auto constexpr operator+(std::size_t offset) const noexcept -> frame { return frame{m_number + offset}; } + + /** + * @brief Post increment operator. Returns a copy of the value. + * + * @return Copy of the incremented underlying frame number. + */ + auto constexpr operator++(int) noexcept -> frame + { + auto copy = *this; + return ++copy; + } + + /** + * @brief Pre increment operator. Returns a reference to the changed value. + * + * @return Reference to the incremented underlying frame number. + */ + auto constexpr operator++() noexcept -> frame & + { + ++m_number; + return *this; + } + + /** + * @brief Defaulted equals operator. + */ + auto constexpr operator==(frame const & other) const noexcept -> bool = default; + + /** + * @brief Defaulted three-way comparison operator. + */ + auto constexpr operator<=>(frame const & other) const noexcept -> std::strong_ordering = default; + + private: + std::size_t m_number{}; ///< Index number of the current physical frame, used to distinguish it from other frames. + }; + +} // namespace teachos::x86_64::memory + +#endif // TEACHOS_ARCH_X86_64_MEMORY_ALLOCATOR_frame_HPP diff --git a/arch/x86_64/include/x86_64/memory/region_allocator.hpp b/arch/x86_64/include/x86_64/memory/region_allocator.hpp new file mode 100644 index 0000000..23bea10 --- /dev/null +++ b/arch/x86_64/include/x86_64/memory/region_allocator.hpp @@ -0,0 +1,76 @@ +#ifndef TEACHOS_X86_64_MEMORY_REGION_ALLOCATOR_HPP +#define TEACHOS_X86_64_MEMORY_REGION_ALLOCATOR_HPP + +#include "x86_64/memory/address.hpp" +#include "x86_64/memory/frame.hpp" + +#include + +#include +#include + +namespace teachos::x86_64::memory +{ + /** + * @brief Allocates memory linearly using memory areas read from the multiboot2 information pointer and leaks any + * deallocated frames. + */ + struct region_allocator + { + struct memory_information + { + std::pair image_range; + std::pair mbi_range; + multiboot2::memory_map memory_map; + }; + + /** + * @brief Constructor. + * + * @param mem_info Structure containing all relevant information to map and allocate memory. + */ + explicit region_allocator(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 allocated in the first place. + * + * @param physical_frame Previously allocated physical_frame that should be deallocated. + */ + auto deallocate_frame(frame const & physical_frame) -> void; + + private: + /** + * @brief Find the next memory area and write it into current_area. + */ + auto choose_next_area() -> void; + + frame m_next_frame; ///< The physical_frame after the last allocated one. + std::optional m_current_region; ///< The memory region currently allocated from + multiboot2::memory_map m_memory_map; ///< The boot loader supplied memory map. + frame const m_kernel_start; ///< The start address of the kernel code in memory. + frame const m_kernel_end; ///< The end address of the kernel code in memory. + frame const m_multiboot_start; ///< The start address of the multiboot code in memory. + frame const m_multiboot_end; ///< The end address of the multiboot code in memory. + }; + +} // namespace teachos::x86_64::memory + +#endif // TEACHOS_ARCH_X86_64_MEMORY_ALLOCATOR_AREA_FRAME_ALLOCATOR_HPP diff --git a/arch/x86_64/scripts/x86-64-linker-script.txt b/arch/x86_64/scripts/x86-64-linker-script.txt new file mode 100644 index 0000000..3d9a7ae --- /dev/null +++ b/arch/x86_64/scripts/x86-64-linker-script.txt @@ -0,0 +1,150 @@ +ENTRY(_start) + +/***************************************************************************** + * Virtual and linear start addresses of the TeachOS kernel + *****************************************************************************/ +TEACHOS_LOW = 1M; + +PHDRS { + boot_rodata PT_LOAD FLAGS(4); + boot_text PT_LOAD FLAGS(5); + boot_data PT_LOAD FLAGS(6); + + text PT_LOAD FLAGS(5); + data PT_LOAD FLAGS(6); + rodata PT_LOAD FLAGS(4); +} + +SECTIONS +{ + /*************************************************************************** + * Load the bootstrap code into low memory. We need to be accessible in + * 32-Bit mode, so we want to live down low, but we need to leave the 1MiB + * hole open since some BIOS functionality resides below it. + ***************************************************************************/ + . = TEACHOS_LOW; + + /*************************************************************************** + * We want to be able to be able to access all memory (linear and virtual) + * during bootstrapping and operation. To achieve this, we define some + * symbols at the beginning. + ***************************************************************************/ + _start_linear = .; + _start_virtual = .; + + /*************************************************************************** + * The bootstrapping infratructure goes first. We first place the read-only + * data, followed by our code, initialized mutable data, and finally our + * uninitialized mutable data. + ***************************************************************************/ + .boot_rodata ALIGN(4K) : AT(ADDR (.boot_rodata)) + { + KEEP(*(.boot_mbh)) + *(.boot_rodata) + } :boot_rodata + + .boot_text ALIGN(4K) : AT(ADDR (.boot_text)) + { + *(.boot_text) + } :boot_text + + .boot_bss ALIGN(4K) : AT(ADDR (.boot_bss)) + { + *(.boot_bss) + *(.boot_stack) + } + + .boot_data ALIGN(4K) : AT(ADDR (.boot_data)) + { + *(.boot_data) + } :boot_data + + /*************************************************************************** + * Now it is time to load the 64-bit kernel code. We + * make sure to align the loaded data onto a page boundary. + ***************************************************************************/ + .init ALIGN(4K) : AT(ADDR (.init)) + { + /* + * Make sure that the crt code is wrapped around the compiler generated + * initialization code. + */ + KEEP(*crti.s.o*(.init)) + KEEP(*(EXCLUDE_FILE (*crti.s.o* *crtn.s.o*) .init)) + KEEP(*crtn.s.o*(.init)) + } :text + + .fini ALIGN(4K) : AT(ADDR (.fini)) + { + /* + * Make sure that the crt code is wrapped around the compiler generated + * finalizer code. + */ + KEEP(*crti.s.o*(.fini)) + KEEP(*(EXCLUDE_FILE (*crti.s.o* *crtn.s.o*) .fini)) + KEEP(*crtn.s.o*(.fini)) + } + + .stl_text ALIGN(4K) : AT(ADDR (.stl_text)) + { + *(.stl_text .stl_text*) + KEEP(*libstdc++.a:*(.text .text.*)) + } + + .text ALIGN(4K) : AT(ADDR (.text)) + { + *(.text .text.*) + } + + .user_text ALIGN(4K) : AT(ADDR (.user_text)) + { + *(.user_text .user_text.*) + } + + .rodata ALIGN(4K) : AT (ADDR (.rodata)) + { + *(.rodata) + *(.rodata.*) + } :rodata + + .ctors ALIGN(4K) : AT (ADDR (.ctors)) + { + KEEP(*crtbegin.o(.ctors)) + KEEP(*(EXCLUDE_FILE (*crtend.o) .ctors)) + KEEP(*(SORT(.ctors.*))) + KEEP(*crtend.o(.ctors)) + } :data + + .dtors ALIGN(4K) : AT (ADDR (.dtors)) + { + KEEP(*crtbegin.o(.dtors)) + KEEP(*(EXCLUDE_FILE (*crtend.o) .dtors)) + KEEP(*(SORT(.dtors.*))) + KEEP(*crtend.o(.dtors)) + } + + .bss ALIGN(4K) : AT (ADDR (.bss)) + { + *(COMMON) + *(.bss*) + } + + .data ALIGN(4K) : AT (ADDR (.data)) + { + *(.data*) + } + + .user_data ALIGN(4K) : AT (ADDR (.user_data)) + { + *(.user_data .user_data.*) + } + + /*************************************************************************** + * In accordance with the symbol definitions at the start, we generate some + * symbols to mark the end of our loaded image. + ***************************************************************************/ + _end_virtual = ADDR(.bss) + SIZEOF(.bss); + _end_linear = _end_virtual; + + /DISCARD/ : { *(.comment) } +} diff --git a/arch/x86_64/src/boot/boot.s b/arch/x86_64/src/boot/boot.s index ada6426..e0cff7c 100644 --- a/arch/x86_64/src/boot/boot.s +++ b/arch/x86_64/src/boot/boot.s @@ -12,7 +12,7 @@ * Reserve some space for the Multiboot 2 information pointer. */ .global multiboot_information_pointer -multiboot_information_pointer: .skip 4 +multiboot_information_pointer: .skip 8 /** * Align page maps to 4 KiB or the assembler code, will cause crashes when attempting to enable paging. diff --git a/arch/x86_64/src/memory.cpp b/arch/x86_64/src/memory.cpp index 245d7bd..2b16beb 100644 --- a/arch/x86_64/src/memory.cpp +++ b/arch/x86_64/src/memory.cpp @@ -1,62 +1,54 @@ #include "arch/memory.hpp" -// #include "noarch/error.hpp" -// #include "noarch/print.hpp" -// #include "x86_64/bootstrap/mutiboot.hpp" -// #include "x86_64/memory/frame_allocator.hpp" -// #include "x86_64/memory/mbi_frame_allocator.hpp" +#include "kern/error.hpp" -// #include -// #include -// #include -// #include +#include "arch/asm_pointer.hpp" +#include "x86_64/memory/region_allocator.hpp" -namespace teachos::arch::memory -{ +#include - // namespace - // { - // /** - // * @brief Remap the kernel according to the ELF information. - // * - // * After initial bootup, a basic identity mapping of the lower 1GiB is set up. This mapping allows execution - // from, - // * as well as read and write access to, the mapped memory. In order to protect the kernel, remap it according to - // the - // * information supplied by the ELF file. This means remapping code sections as read-only and data sections as - // * no-execute (and read only for .rodata). - // * - // * @param sections Information about the sections in the loaded kernel binary. - // * @param allocator The frame allocator used to create new page mappings. - // */ - // auto remap_kernel(elf::section_header_table const & sections, x86_64::memory::frame_allocator & allocator) -> - // void - // { - // static_cast(sections); - // static_cast(allocator); - // } - // } // namespace +#include - auto init() -> void - { - // kernel::println("Initializing memory"); +extern "C" teachos::arch::asm_pointer multiboot_information_pointer; - // if (!x86_64::bootstrap::multiboot_information_pointer->has()) - // { - // kernel::panic("Received no memory map from the boot loader!"); - // } +namespace teachos::arch::memory +{ + namespace + { + auto constinit is_initialized = std::atomic_flag{}; - // if (!x86_64::bootstrap::multiboot_information_pointer->has()) - // { - // kernel::panic("Received no ELF symbol information from the boot loader!"); - // } + // auto create_memory_information() -> x86_64::memory::region_allocator::memory_information { - // auto memory_map = x86_64::bootstrap::multiboot_information_pointer->memory_map(); - // auto elf_symbols = x86_64::bootstrap::multiboot_information_pointer->elf_symbols(); - // auto section_header_table = elf::section_header_table{elf_symbols.data(), elf::file_class::bits_64}; - // auto allocator = x86_64::memory::mbi_frame_allocator{memory_map, section_header_table}; + // }; + } // namespace - // remap_kernel(section_header_table, allocator); + auto init() -> void + { + if (is_initialized.test_and_set()) + { + teachos::panic("[x86_64] Memory management has already been initialized."); + } + + auto memory_map = multiboot_information_pointer->maybe_memory_map(); + if (!memory_map) + { + teachos::panic("[x86_64] No memory map available."); + } + + // auto mem_info = x86_64::memory::region_allocator::memory_information{}; + // auto allocator = x86_64::memory::region_allocator{mem_info}; + + // 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/src/memory/allocator/physical_frame.cpp b/arch/x86_64/src/memory/allocator/physical_frame.cpp deleted file mode 100644 index ec387a1..0000000 --- a/arch/x86_64/src/memory/allocator/physical_frame.cpp +++ /dev/null @@ -1,24 +0,0 @@ -#include "arch/memory/allocator/physical_frame.hpp" - -namespace teachos::arch::memory::allocator -{ - auto physical_frame::containing_address(physical_address address) -> physical_frame - { - return physical_frame{address / PAGE_FRAME_SIZE}; - } - - auto physical_frame::start_address() const -> physical_address { return frame_number * PAGE_FRAME_SIZE; } - - auto physical_frame::operator++(int) -> physical_frame - { - physical_frame const old_value = *this; - ++frame_number; - return old_value; - } - - auto physical_frame::operator++() -> physical_frame & - { - ++frame_number; - return *this; - } -} // namespace teachos::arch::memory::allocator diff --git a/arch/x86_64/src/memory/region_allocator.cpp b/arch/x86_64/src/memory/region_allocator.cpp new file mode 100644 index 0000000..c9a98b4 --- /dev/null +++ b/arch/x86_64/src/memory/region_allocator.cpp @@ -0,0 +1,95 @@ +// #include "arch/memory/allocator/region_allocator.hpp" + +// #include "arch/exception_handling/assert.hpp" + +// #include +// #include +// #include + +#include "x86_64/memory/region_allocator.hpp" + +#include "x86_64/memory/address.hpp" +#include "x86_64/memory/frame.hpp" + +#include + +#include +#include + +namespace teachos::x86_64::memory +{ + namespace + { + auto constexpr last_frame(multiboot2::memory_map::region const & region) + { + return frame::containing(physical_address{region.base + region.size_in_B - 1}); + } + } // namespace + + region_allocator::region_allocator(memory_information const & mem_info) + : m_next_frame{} + , m_current_region{} + , m_memory_map{} + , m_kernel_start(frame::containing(mem_info.image_range.first)) + , m_kernel_end(frame::containing(mem_info.image_range.second)) + , m_multiboot_start(frame::containing(mem_info.mbi_range.first)) + , m_multiboot_end(frame::containing(mem_info.mbi_range.second)) + { + choose_next_area(); + } + + auto region_allocator::choose_next_area() -> void + { + m_current_region.reset(); + auto next_area_with_free_frames = + m_memory_map | std::views::filter(&multiboot2::memory_map::region::available) | + std::views::filter([next = m_next_frame](auto const & region) { return last_frame(region) >= next; }); + + auto lowest_region_with_free_frames = + std::ranges::min_element(next_area_with_free_frames, [](auto lhs, auto rhs) { return lhs.base < rhs.base; }); + + if (lowest_region_with_free_frames != next_area_with_free_frames.end()) + { + m_current_region = *lowest_region_with_free_frames; + auto start_frame = frame::containing(physical_address{m_current_region->base}); + if (m_next_frame < start_frame) + { + m_next_frame = start_frame; + } + } + } + + auto region_allocator::allocate_frame() -> std::optional + { + if (!m_current_region) + { + return {}; + } + + auto const end_address = physical_address{m_current_region->base + m_current_region->size_in_B - 1}; + auto end_frame = frame::containing(end_address); + + if (m_next_frame > end_frame) + { + choose_next_area(); + } + else if (m_next_frame >= m_kernel_start && m_next_frame <= m_kernel_end) + { + m_next_frame = m_kernel_end + 1; + } + else if (m_next_frame >= m_multiboot_start && m_next_frame <= m_multiboot_end) + { + m_next_frame = m_multiboot_end + 1; + } + else + { + auto allocated = m_next_frame; + ++m_next_frame; + return allocated; + } + + return allocate_frame(); + } + + auto region_allocator::deallocate_frame(frame const &) -> void {} +} // namespace teachos::x86_64::memory -- cgit v1.2.3 From 4ae38294b0db1870f82cc402dc4a8bb38cea4a67 Mon Sep 17 00:00:00 2001 From: Felix Morgner Date: Fri, 18 Jul 2025 11:16:43 +0000 Subject: x86_64: don't lose multi boot information pointer Since the transition to a PIE, more registers are required to perform the relative lookups of data references. As part of that change, a subtle mistake was introduced in _start, overwriting the multiboot information pointer that gets handed to kernel by the boot loader in %ebx. --- arch/x86_64/src/boot/boot.s | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'arch/x86_64') diff --git a/arch/x86_64/src/boot/boot.s b/arch/x86_64/src/boot/boot.s index e0cff7c..ba5c6f0 100644 --- a/arch/x86_64/src/boot/boot.s +++ b/arch/x86_64/src/boot/boot.s @@ -195,9 +195,9 @@ _start: call .Lstart_get_ip .Lstart_get_ip: pop %esi - lea (stack_top - .Lstart_get_ip)(%esi), %ebx + lea (stack_top - .Lstart_get_ip)(%esi), %ecx - mov %ebx, %esp + mov %ecx, %esp mov %esp, %ebp call assert_loaded_by_multiboot2_loader -- cgit v1.2.3 From fd6282947bb13af4cfff8cf2209c442b568275f3 Mon Sep 17 00:00:00 2001 From: Felix Morgner Date: Fri, 18 Jul 2025 11:35:58 +0000 Subject: x86_64: add data segment to boot GDT --- arch/x86_64/src/boot/boot.s | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) (limited to 'arch/x86_64') diff --git a/arch/x86_64/src/boot/boot.s b/arch/x86_64/src/boot/boot.s index ba5c6f0..2decf26 100644 --- a/arch/x86_64/src/boot/boot.s +++ b/arch/x86_64/src/boot/boot.s @@ -58,19 +58,22 @@ stack_top: .section .boot_rodata, "a", @progbits /** - * A valid Global Descriptor Table is still required in long mode. However, we - * only need a single entry for the "code segment", so we will setup a single - * segment table below. + * A valid Global Descriptor Table is still required in long mode. * + * Bit 41: "RW" in the access byte => mark the segment as readable (code) or writable (data). * Bit 43: "E" in the access byte => mark the segment as executable. * Bit 44: "S" in the access byte => mark the segment as code or data. * Bit 47: "P" in the access byte => mark the segment as being present. * Bit 53: "L" in the flags byte => mark the segment as being for long mode */ -global_descriptor_table: .quad 0 +global_descriptor_table: +global_descriptor_table_null = . - global_descriptor_table +.quad 0 global_descriptor_table_code = . - global_descriptor_table -.quad (1<<43) | (1<<44) | (1<<47) | (1<<53) +.quad (1<<41) | (1<<43) | (1<<44) | (1<<47) | (1<<53) +global_descriptor_table_data = . - global_descriptor_table +.quad (1<<41) | (1<<44) | (1<<47) global_descriptor_table_end: /** @@ -402,7 +405,7 @@ prepare_page_maps: .code64 _transition_to_long_mode: - xor %rax, %rax + mov $global_descriptor_table_data, %rax mov %rax, %ss mov %rax, %ds mov %rax, %es -- cgit v1.2.3 From 3a67a3e1088508148002e7c20befa571fb0b72c0 Mon Sep 17 00:00:00 2001 From: Felix Morgner Date: Fri, 18 Jul 2025 11:44:15 +0000 Subject: x86_64: set GDT entries as accessed. --- arch/x86_64/src/boot/boot.s | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) (limited to 'arch/x86_64') diff --git a/arch/x86_64/src/boot/boot.s b/arch/x86_64/src/boot/boot.s index 2decf26..f3d9585 100644 --- a/arch/x86_64/src/boot/boot.s +++ b/arch/x86_64/src/boot/boot.s @@ -60,6 +60,7 @@ stack_top: /** * A valid Global Descriptor Table is still required in long mode. * + * Bit 41: "A" in the access byte => mark the segment as accessed. * Bit 41: "RW" in the access byte => mark the segment as readable (code) or writable (data). * Bit 43: "E" in the access byte => mark the segment as executable. * Bit 44: "S" in the access byte => mark the segment as code or data. @@ -71,9 +72,9 @@ global_descriptor_table: global_descriptor_table_null = . - global_descriptor_table .quad 0 global_descriptor_table_code = . - global_descriptor_table -.quad (1<<41) | (1<<43) | (1<<44) | (1<<47) | (1<<53) +.quad (1<<40) | (1<<41) | (1<<43) | (1<<44) | (1<<47) | (1<<53) global_descriptor_table_data = . - global_descriptor_table -.quad (1<<41) | (1<<44) | (1<<47) +.quad (1<<40) | (1<<41) | (1<<44) | (1<<47) global_descriptor_table_end: /** -- cgit v1.2.3 From eb22cdcad4c27527a63a6e457e80c752f76821c6 Mon Sep 17 00:00:00 2001 From: Felix Morgner Date: Mon, 21 Jul 2025 12:13:10 +0000 Subject: x86_64: clean up bootstrap code --- arch/x86_64/src/boot/boot.s | 512 +++++++++++++++++++++++--------------------- 1 file changed, 265 insertions(+), 247 deletions(-) (limited to 'arch/x86_64') diff --git a/arch/x86_64/src/boot/boot.s b/arch/x86_64/src/boot/boot.s index f3d9585..7a46795 100644 --- a/arch/x86_64/src/boot/boot.s +++ b/arch/x86_64/src/boot/boot.s @@ -1,50 +1,22 @@ -.extern _end_physical -.extern _init -.extern kernel_main - - /** - * Uninitialized data for the bootstrapping process. + * @brief Uninitialized data for the bootstrapping process. */ .section .boot_bss, "aw", @nobits /** - * Reserve some space for the Multiboot 2 information pointer. + * @brief Storage for the multiboot2 information pointer. */ .global multiboot_information_pointer multiboot_information_pointer: .skip 8 -/** - * Align page maps to 4 KiB or the assembler code, will cause crashes when attempting to enable paging. - */ .align 4096 -/** - * Reserve space for the page maps we are going to use during startup. - * - * Note: We are going to use large pages to make the initial mapping code - * simpler. - * - * We need: - * - A single PML 4 (since we will only use 4-level paging) - * - 1 PML 3 - * - 1 PML 2 - */ - -.global page_map_level_4 page_map_level_4: .skip 512 * 8 - -.global page_map_level_3 page_map_level_3: .skip 512 * 8 - -.global page_map_level_2 page_map_level_2: .skip 512 * 8 /** - * Stack space for the bootstrapping process. - * - * Note: We are going to reserve 1 MiB for now. If/when the kernel requires - * more space to run, it will have to relocate the stack. + * @brief Storage for the bootstrap stack. */ .section .boot_stack, "aw", @nobits .align 16 @@ -53,210 +25,240 @@ stack_bottom: .skip 1 << 20 stack_top: /** - * Constants for the bootstrapping process. + * @brief Constants for the bootstrapping process. */ .section .boot_rodata, "a", @progbits /** - * A valid Global Descriptor Table is still required in long mode. - * - * Bit 41: "A" in the access byte => mark the segment as accessed. - * Bit 41: "RW" in the access byte => mark the segment as readable (code) or writable (data). - * Bit 43: "E" in the access byte => mark the segment as executable. - * Bit 44: "S" in the access byte => mark the segment as code or data. - * Bit 47: "P" in the access byte => mark the segment as being present. - * Bit 53: "L" in the flags byte => mark the segment as being for long mode + * @brief A basic GDT for long mode. */ - global_descriptor_table: +.set GDT_ACCESSED, 40 +.set GDT_READ_WRITE, 41 +.set GDT_EXECUTABLE, 43 +.set GDT_DESCRIPTOR_TYPE, 44 +.set GDT_PRESENT, 47 +.set GDT_LONG_MODE, 53 global_descriptor_table_null = . - global_descriptor_table .quad 0 global_descriptor_table_code = . - global_descriptor_table -.quad (1<<40) | (1<<41) | (1<<43) | (1<<44) | (1<<47) | (1<<53) +.quad (1 << GDT_ACCESSED) | (1 << GDT_READ_WRITE) | (1 << GDT_EXECUTABLE) | (1 << GDT_DESCRIPTOR_TYPE) | (1 << GDT_PRESENT) | (1 << GDT_LONG_MODE) global_descriptor_table_data = . - global_descriptor_table -.quad (1<<40) | (1<<41) | (1<<44) | (1<<47) +.quad (1 << GDT_ACCESSED) | (1 << GDT_READ_WRITE) | (1 << GDT_DESCRIPTOR_TYPE) | (1 << GDT_PRESENT) global_descriptor_table_end: -/** - * We are going to print some messages in case we panic during boot, so we are - * going to store them here as well - */ -.global message_prefix_panic -message_prefix_panic: -.string "TeachOS Panic: " -message_not_loaded_by_multiboot2: -.string "The operating system was not loaded by a Multiboot 2 loader." -message_cpuid_instruction_no_supported: -.string "The 'cpuid' instruction is not supported on this platform." -mesage_long_mode_not_supported: -.string "Long mode is not supported by this platform." +message_prefix_panic: .string "Panic: " +message_not_loaded_by_multiboot2: .string "The operating system was not loaded by a Multiboot 2 loader." +message_cpuid_instruction_no_supported: .string "The 'cpuid' instruction is not supported on this platform." +message_long_mode_not_supported: .string "Long mode is not supported by this platform." +message_return_from_kernel_main: .string "Execution returned from kernel main." /** - * Mutable data for the bootstrapping process. + * @brief Initialized data for the bootstrapping process. */ .section .boot_data, "aw", @progbits /** - * We need a pointer to our current position in the VGA text buffer. + * @brief A pointer to the current position within the VGA text buffer. */ .global vga_buffer_pointer vga_buffer_pointer: .quad 0xb8000 /** - * Code for the bootstrapping process. + * @brief Code for the bootstrapping process. */ .section .boot_text, "ax", @progbits .align 16 .code32 -.global halt -halt: -1: - hlt - jmp 1b +.macro pie_base + push %esi + call 0f + 0: + pop %esi +.endm + +.macro function_start + push %ebp + mov %esp, %ebp +.endm + +.macro function_end + leave + ret +.endm + +.macro pie_function_start + function_start + pie_base +.endm + +.macro pie_function_end + pop %esi + function_end +.endm /** - * Print a given panic message and then halt the machine. + * @brief Prepare the environment and start the kernel. * - * Parameters: - * - [stack - 0] message: the message to print + * This function performs all necessary checks to ensure the system was loaded + * by the expected loader and supports all features required to run the kernel. + * If successful, it prepares the system by setting up memory virtualization + * and then start the kernel proper. + * + * @param %eax The Multiboot 2 magic marker. + * @param %ebx The Multiboot 2 information pointer. + * @return void This function does not return. */ -_panic: - push %ebp +.global _start +_start: + call 0f +0: + pop %esi + + lea (stack_top - 0b)(%esi), %ecx + + mov %ecx, %esp mov %esp, %ebp - call .Lpanic_get_ip -.Lpanic_get_ip: - pop %eax - lea (message_prefix_panic - .Lpanic_get_ip)(%eax), %eax + call _assert_loaded_by_multiboot2_loader + call _save_multiboot_information_pointer - push %eax - push $0x4f - call _print - add $8, %esp + call _assert_cpuid_instruction_is_supported + call _assert_cpu_supports_long_mode - push 8(%ebp) - push 0x4f - call _print - add $8, %esp + call _prepare_page_maps + call _enable_paging + call _enable_sse + call _reload_gdt + + lea (_transition_to_long_mode - 0b)(%esi), %eax + pushl $global_descriptor_table_code + pushl %eax + lret + +/** + * @brief Halt the system. + * + * This function will instruct the CPU to halt. It will try to keep the CPU + * halted, even if interrupts occur. + * + * @return This function never returns. + */ +.global halt +_halt: + function_start - call halt +1: + hlt + jmp 1b + + function_end /** - * Print a message via the VGA buffer. + * @brief Print a message via the VGA text buffer. * - * Parameters: - * - [stack - 4] message: the message to print - * - [stack - 0] color: the color of the message + * @param ebp+12 The message to print. + * @param ebp+8 The color to print the message in. */ _print: - push %ebp - mov %esp, %ebp + pie_function_start - push %ebx - push %esi push %edi + push %ebx - call .Lprint_get_ip -.Lprint_get_ip: - pop %edi - lea (vga_buffer_pointer - .Lprint_get_ip)(%edi), %edi - - mov 8(%ebp), %eax - mov 12(%ebp), %ebx + mov 8(%ebp), %al + mov 12(%ebp), %edx mov $0, %ecx - mov (%edi), %esi - -.Lprint_loop: - mov (%ebx, %ecx), %dl - test %dl, %dl - je .Lupdate_vga_buffer_address - mov %dl, (%esi, %ecx, 2) - mov %al, 1(%esi, %ecx, 2) + lea (vga_buffer_pointer - 0b)(%esi), %edi + mov (%edi), %edi + +1: + mov (%edx, %ecx), %bl + test %bl, %bl + je 2f + mov %bl, (%edi, %ecx, 2) + mov %al, 1(%edi, %ecx, 2) inc %ecx - jmp .Lprint_loop + jmp 1b -.Lupdate_vga_buffer_address: +2: shl $1, %ecx - add %ecx, %esi - mov %esi, (%edi) + add %ecx, %edi + lea (vga_buffer_pointer - 0b)(%esi), %ecx + mov %edi, (%ecx) -.Lprint_end: - pop %edi - pop %esi pop %ebx - mov %ebp, %esp - pop %ebp - ret + pop %edi + + pie_function_end /** - * This is our entry point after being loaded by the bootloader. + * @brief Print a given panic message and then halt the machine as if by calling ::halt() * - * Having this in assembly makes it easier for us to keep things together. + * @param ebp+4 A message to print. + * @return This function does not return. */ -.global _start -_start: - call .Lstart_get_ip -.Lstart_get_ip: - pop %esi - lea (stack_top - .Lstart_get_ip)(%esi), %ecx - - mov %ecx, %esp - mov %esp, %ebp +_panic: + pie_function_start - call assert_loaded_by_multiboot2_loader - call assert_cpuid_instruction_is_supported - call assert_cpu_supports_long_mode - call prepare_page_maps - call enable_paging - call enable_sse + lea (message_prefix_panic - 0b)(%esi), %eax - sub $10, %esp - lea (global_descriptor_table - .Lstart_get_ip)(%esi), %eax - movw $(global_descriptor_table_end - global_descriptor_table -1), (%esp) - mov %eax, 2(%esp) - movl $0, 6(%esp) + push %eax + push $0x4f + call _print - lgdt (%esp) - add $10, %esp + mov 16(%ebp), %eax + mov %eax, 8(%ebp) + call _print + add $8, %esp - lea (_transition_to_long_mode - .Lstart_get_ip)(%esi), %eax - pushl $global_descriptor_table_code - pushl %eax - lret + call _halt - call halt + pie_function_end /** - * Assert that the CPU supports going into long mode. + * Assert that we were loaded by a Multiboot 2 compliant bootloader. + * + * This assertion will panic the system if the magic signature was not found. + * If we were loaded my an appropriate bootloader, this function also saves + * the provided MBI pointer to `multiboot_information_pointer`. */ -assert_cpu_supports_long_mode: - mov $0x80000000, %eax - cpuid - cmp $0x80000001, %eax - jb .Llong_mode_assertion_failed +_assert_loaded_by_multiboot2_loader: + pie_function_start - mov $0x80000001, %eax - cpuid - test $(1 << 29), %edx - jz .Llong_mode_assertion_failed - ret -.Llong_mode_assertion_failed: - call .Lassert_cpu_supports_long_mode_fail_get_ip -.Lassert_cpu_supports_long_mode_fail_get_ip: - pop %eax - lea (mesage_long_mode_not_supported - .Lassert_cpu_supports_long_mode_fail_get_ip)(%eax), %eax + .set MULTIBOOT2_MAGIC, 0x36d76289 + cmp $MULTIBOOT2_MAGIC, %eax + je 1f + lea (message_not_loaded_by_multiboot2 - 0b)(%esi), %eax push %eax call _panic +1: + pie_function_end /** - * Assert that the CPU supports the CPUID instruction. + * @brief Store the multiboot 2 information pointer in the global memory. + * + * @return void + */ +_save_multiboot_information_pointer: + pie_function_start + + lea (multiboot_information_pointer - 0b)(%esi), %eax + mov %ebx, (%eax) + + pie_function_end + +/** + * @brief Assert that the CPU supports the CPUID instruction. * * The primary way to check for support of the instruction is to flip the ID * bin in EFLAGS and then check if this changed was accepted. If so, the CPU * supports the CPUID instruction, otherwise it most-likely doesn't. */ -assert_cpuid_instruction_is_supported: +_assert_cpuid_instruction_is_supported: + pie_function_start + pushfl pop %eax mov %eax, %ecx @@ -271,52 +273,100 @@ assert_cpuid_instruction_is_supported: popfl cmp %ecx, %eax - je .Lcpuid_assertion_fail - ret -.Lcpuid_assertion_fail: - call .Lassert_cpuid_instruction_is_supported_fail_get_ip -.Lassert_cpuid_instruction_is_supported_fail_get_ip: - pop %eax - lea (message_cpuid_instruction_no_supported - .Lassert_cpuid_instruction_is_supported_fail_get_ip)(%eax), %eax + jne 1f + lea (message_cpuid_instruction_no_supported - 0b)(%esi), %eax push %eax call _panic +1: + pie_function_end + /** - * Assert that we were loaded by a Multiboot 2 compliant bootloader. - * - * This assertion will panic the system if the magic signature was not found. - * If we were loaded my an appropriate bootloader, this function also saves - * the provided MBI pointer to `multiboot_information_pointer`. + * @brief Assert that the CPU supports going into long mode. */ -assert_loaded_by_multiboot2_loader: - cmp $0x36d76289, %eax /* Check if we received the - expected magic */ - jne .Lmultiboot2_assertion_fail /* Panic otherwise */ - call .Lassert_loaded_by_multiboot2_loader_get_ip -.Lassert_loaded_by_multiboot2_loader_get_ip: - pop %eax - lea (multiboot_information_pointer - .Lassert_loaded_by_multiboot2_loader_get_ip)(%eax), %eax - mov %ebx, (%eax) /* Store the MBI pointer */ - ret -.Lmultiboot2_assertion_fail: - call .Lassert_loaded_by_multiboot2_loader_fail_get_ip -.Lassert_loaded_by_multiboot2_loader_fail_get_ip: - pop %eax - lea (message_not_loaded_by_multiboot2 - .Lassert_loaded_by_multiboot2_loader_fail_get_ip)(%eax), %eax +_assert_cpu_supports_long_mode: + pie_function_start + + mov $0x80000000, %eax + cpuid + cmp $0x80000001, %eax + jb 1f + + mov $0x80000001, %eax + cpuid + test $(1 << 29), %edx + jnz 2f +1: + lea (message_long_mode_not_supported - 0b)(%esi), %eax push %eax call _panic +2: + pie_function_end /** - * Enable paging. + * @brief Prepare a recursive page map hierarchy + * + * We map all physical memory we were loaded in plus one additional page. The + * mapping is done in terms of huge pages (2 MiB per page) to save on required + * page map entries. + * + * @return void + */ +_prepare_page_maps: + pie_function_start + push %ebx + + .set HUGE_PAGES_TO_MAP, 16 + + /* Map the P4 table recursively */ + lea (page_map_level_4 - 0b)(%esi), %eax + mov %eax, %ebx + or $0b11, %ebx + mov %ebx, (511 * 8)(%eax) + + /* Add an entry to the PML4, pointing to the PML3 */ + lea (page_map_level_3 - 0b)(%esi), %ebx + or $0b11, %ebx + mov %ebx, (((0x0000000000100000 >> 39) & 0x1ff) * 8)(%eax) + + /* Add an entry to the PML3, pointing to the PML2 */ + lea (page_map_level_3 - 0b)(%esi), %eax + lea (page_map_level_2 - 0b)(%esi), %ebx + or $0b11, %ebx + mov %ebx, (((0x0000000000100000 >> 30) & 0x1ff) * 8)(%eax) + + /* Add entries for huge pages to the PML2 */ + push %edi + lea (page_map_level_2 - 0b)(%esi), %ebx + mov $HUGE_PAGES_TO_MAP, %edi + xor %ecx, %ecx + +1: + mov $(1 << 21), %eax + mul %ecx + or $0b10000011, %eax + mov %eax, (%ebx, %ecx, 8) + + inc %ecx + cmp %edi, %ecx + jne 1b + + pop %edi + pop %ebx + pie_function_end + +/** + * @p Enable memory virtualization via paging. * * Note: This routine expects for there to be a valid set of page maps already * set up for use. - */ -enable_paging: - call .Lenable_paging_get_ip -.Lenable_paging_get_ip: - pop %eax -lea (page_map_level_4 - .Lenable_paging_get_ip)(%eax), %eax + * + * @return void + */For +_enable_paging: + pie_function_start + + lea (page_map_level_4 - 0b)(%esi), %eax mov %eax, %cr3 /* Enable Physical Address Extension */ @@ -335,12 +385,14 @@ lea (page_map_level_4 - .Lenable_paging_get_ip)(%eax), %eax or $(1 << 31), %eax mov %eax, %cr0 - ret + pie_function_end /** - * Enable use of SSE instructions. + * @brief Enable use of SSE instructions. */ -enable_sse: +_enable_sse: + function_start + mov %cr0, %eax and $0xfffffffb, %eax or $0x00000002, %eax @@ -350,57 +402,26 @@ enable_sse: or $(3 << 9), %eax mov %eax, %cr4 - ret + function_end /** - * Prepare the page maps. + * @brief Prepare a new GTD and load make it active. * - * We map all physical memory we were loaded in plus one additional page. The - * mapping is done in terms of huge pages (2 MiB per page) to save on required - * page map entries. + * @return void */ -prepare_page_maps: - call .Lprepare_page_maps_get_ip -.Lprepare_page_maps_get_ip: - pop %edi - /* Map the P4 table recursively */ - lea (page_map_level_4 - .Lprepare_page_maps_get_ip)(%edi), %eax - mov %eax, %ebx - or $0b11, %eax /* Write present + writable flags into eax register */ - mov %eax, (511 * 8)(%ebx) - - /* Add an entry to the PML4, pointing to the PML3 */ - lea (page_map_level_3 - .Lprepare_page_maps_get_ip)(%edi), %eax - lea (page_map_level_4 - .Lprepare_page_maps_get_ip)(%edi), %ebx - or $0x3, %eax - mov %eax, (((0x0000000000100000 >> 39) & 0x1ff) * 8)(%ebx) - - /* Add an entry to the PML3, pointing to the PML2 */ - lea (page_map_level_2 - .Lprepare_page_maps_get_ip)(%edi), %eax - lea (page_map_level_3 - .Lprepare_page_maps_get_ip)(%edi), %ebx - or $0x3, %eax - mov %eax, (((0x0000000000100000 >> 30) & 0x1ff) * 8)(%ebx) - - xor %ecx, %ecx - - push %esi - lea (_end_linear - .Lprepare_page_maps_get_ip)(%edi), %esi - shr $21, %esi - add $2, %esi +_reload_gdt: + pie_function_start -.Lmap_pages: - lea (page_map_level_2 - .Lprepare_page_maps_get_ip)(%edi), %ebx - mov $(1 << 21), %eax - mul %ecx - or $((1 << 0) | (1 << 1) | (1 << 7)), %eax - mov %eax, (%ebx,%ecx,8) + sub $10, %esp + lea (global_descriptor_table - 0b)(%esi), %eax + movw $(global_descriptor_table_end - global_descriptor_table -1), (%esp) + mov %eax, 2(%esp) + movl $0, 6(%esp) - inc %ecx - cmp %esi, %ecx - jne .Lmap_pages + lgdt (%esp) + add $10, %esp - pop %esi - ret + pie_function_end .section .boot_text, "ax", @progbits .code64 @@ -413,12 +434,9 @@ _transition_to_long_mode: mov %rax, %fs mov %rax, %gs - leaq vga_buffer_pointer(%rip), %rax - movl $0xb8000, (%rax) - xor %rax, %rax call _init call main - call halt + call _halt -- cgit v1.2.3 From ce8683e63fc9ef59e1800927afb1753507a42ef6 Mon Sep 17 00:00:00 2001 From: Felix Morgner Date: Tue, 22 Jul 2025 20:48:11 +0000 Subject: x86_64: extract constants from bootstrap code --- arch/x86_64/CMakeLists.txt | 2 +- arch/x86_64/include/x86_64/boot/boot.hpp | 66 +++++ arch/x86_64/src/boot/boot.S | 433 ++++++++++++++++++++++++++++++ arch/x86_64/src/boot/boot.s | 442 ------------------------------- arch/x86_64/src/memory.cpp | 5 +- 5 files changed, 502 insertions(+), 446 deletions(-) create mode 100644 arch/x86_64/include/x86_64/boot/boot.hpp create mode 100644 arch/x86_64/src/boot/boot.S delete mode 100644 arch/x86_64/src/boot/boot.s (limited to 'arch/x86_64') diff --git a/arch/x86_64/CMakeLists.txt b/arch/x86_64/CMakeLists.txt index d1af777..a32a0f5 100644 --- a/arch/x86_64/CMakeLists.txt +++ b/arch/x86_64/CMakeLists.txt @@ -34,7 +34,7 @@ target_sources("arch-x86_64" PRIVATE #]============================================================================] target_sources("arch-x86_64" PRIVATE - "src/boot/boot.s" + "src/boot/boot.S" "src/boot/crti.s" "src/boot/crtn.s" "src/boot/multiboot.s" diff --git a/arch/x86_64/include/x86_64/boot/boot.hpp b/arch/x86_64/include/x86_64/boot/boot.hpp new file mode 100644 index 0000000..066e49e --- /dev/null +++ b/arch/x86_64/include/x86_64/boot/boot.hpp @@ -0,0 +1,66 @@ +#ifndef TEACHOS_X86_64_BOOT_BOOT_H +#define TEACHOS_X86_64_BOOT_BOOT_H + +#ifdef __ASSEMBLER__ +/** + * @brief The number of huge pages to map during bootstrap. + */ +#define HUGE_PAGES_TO_MAP (16) + +/** + * @brief The magic value to be set in eax by the multiboot 2 loader. + */ +#define MULTIBOOT2_MAGIC (0x36d76289) + +/** + * @brief The "A" bit in a GDT entry. + */ +#define GDT_ACCESSED (1 << 40) + +/** + * @brief The "R/W" bit in a GDT entry + */ +#define GDT_READ_WRITE (1 << 41) + +/** + * @brief The "E" bit in a GDT entry. + */ +#define GDT_EXECUTABLE (1 << 43) + +/** + * @brief The "S" bit in a GDT entry. + */ +#define GDT_DESCRIPTOR_TYPE (1 << 44) + +/** + * @brief The "P" bit in a GDT entry. + */ +#define GDT_PRESENT (1 << 47) + +/** + * @brief The "L" bit in a GDT entry. + */ +#define GDT_LONG_MODE (1 << 53) + +#else + +#include "arch/asm_pointer.hpp" + +#include + +extern "C" +{ + /** + * @brief A pointer to the multiboot 2 information structure provided by the boot loader. + */ + extern teachos::arch::asm_pointer multiboot_information_pointer; + + /** + * @brief A pointer to the VGA text mode buffer. + */ + extern teachos::arch::asm_pointer> vga_buffer_pointer; +} + +#endif + +#endif diff --git a/arch/x86_64/src/boot/boot.S b/arch/x86_64/src/boot/boot.S new file mode 100644 index 0000000..d65c865 --- /dev/null +++ b/arch/x86_64/src/boot/boot.S @@ -0,0 +1,433 @@ +#include "x86_64/boot/boot.hpp" + +/** + * @brief Uninitialized data for the bootstrapping process. + */ +.section .boot_bss, "aw", @nobits + +/** + * @brief Storage for the multiboot2 information pointer. + */ +.global multiboot_information_pointer +multiboot_information_pointer: .skip 8 + +.align 4096 + +page_map_level_4: .skip 512 * 8 +page_map_level_3: .skip 512 * 8 +page_map_level_2: .skip 512 * 8 + +/** + * @brief Storage for the bootstrap stack. + */ +.section .boot_stack, "aw", @nobits +.align 16 + +stack_bottom: .skip 1 << 20 +stack_top: + +/** + * @brief Constants for the bootstrapping process. + */ +.section .boot_rodata, "a", @progbits + +/** + * @brief A basic GDT for long mode. + */ +global_descriptor_table: +global_descriptor_table_null = . - global_descriptor_table +.quad 0 +global_descriptor_table_code = . - global_descriptor_table +.quad GDT_ACCESSED | GDT_READ_WRITE | GDT_EXECUTABLE | GDT_DESCRIPTOR_TYPE | GDT_PRESENT | GDT_LONG_MODE +global_descriptor_table_data = . - global_descriptor_table +.quad GDT_ACCESSED | GDT_READ_WRITE | GDT_DESCRIPTOR_TYPE | GDT_PRESENT +global_descriptor_table_end: + +message_prefix_panic: .string "Panic: " +message_not_loaded_by_multiboot2: .string "The operating system was not loaded by a Multiboot 2 loader." +message_cpuid_instruction_no_supported: .string "The 'cpuid' instruction is not supported on this platform." +message_long_mode_not_supported: .string "Long mode is not supported by this platform." +message_return_from_kernel_main: .string "Execution returned from kernel main." + +/** + * @brief Initialized data for the bootstrapping process. + */ +.section .boot_data, "aw", @progbits + +/** + * @brief A pointer to the current position within the VGA text buffer. + */ +.global vga_buffer_pointer +vga_buffer_pointer: .quad 0xb8000 + +/** + * @brief Code for the bootstrapping process. + */ +.section .boot_text, "ax", @progbits +.align 16 +.code32 + +.macro pie_base + push %esi + call 0f + 0: + pop %esi +.endm + +.macro function_start + push %ebp + mov %esp, %ebp +.endm + +.macro function_end + leave + ret +.endm + +.macro pie_function_start + function_start + pie_base +.endm + +.macro pie_function_end + pop %esi + function_end +.endm + +/** + * @brief Prepare the environment and start the kernel. + * + * This function performs all necessary checks to ensure the system was loaded + * by the expected loader and supports all features required to run the kernel. + * If successful, it prepares the system by setting up memory virtualization + * and then start the kernel proper. + * + * @param %eax The Multiboot 2 magic marker. + * @param %ebx The Multiboot 2 information pointer. + * @return void This function does not return. + */ +_start: + call 0f +0: + pop %esi + + lea (stack_top - 0b)(%esi), %ecx + + mov %ecx, %esp + mov %esp, %ebp + + call _assert_loaded_by_multiboot2_loader + call _save_multiboot_information_pointer + + call _assert_cpuid_instruction_is_supported + call _assert_cpu_supports_long_mode + + call _prepare_page_maps + call _enable_paging + call _enable_sse + call _reload_gdt + + lea (_transition_to_long_mode - 0b)(%esi), %eax + pushl $global_descriptor_table_code + pushl %eax + lret + +/** + * @brief Halt the system. + * + * This function will instruct the CPU to halt. It will try to keep the CPU + * halted, even if interrupts occur. + * + * @return This function never returns. + */ +_halt: + function_start + +1: + hlt + jmp 1b + + function_end + +/** + * @brief Print a message via the VGA text buffer. + * + * @param ebp+12 The message to print. + * @param ebp+8 The color to print the message in. + */ +_print: + pie_function_start + + push %edi + push %ebx + + mov 8(%ebp), %al + mov 12(%ebp), %edx + mov $0, %ecx + lea (vga_buffer_pointer - 0b)(%esi), %edi + mov (%edi), %edi + +1: + mov (%edx, %ecx), %bl + test %bl, %bl + je 2f + mov %bl, (%edi, %ecx, 2) + mov %al, 1(%edi, %ecx, 2) + inc %ecx + jmp 1b + +2: + shl $1, %ecx + add %ecx, %edi + lea (vga_buffer_pointer - 0b)(%esi), %ecx + mov %edi, (%ecx) + + pop %ebx + pop %edi + + pie_function_end + +/** + * @brief Print a given panic message and then halt the machine as if by calling ::halt() + * + * @param ebp+4 A message to print. + * @return This function does not return. + */ +_panic: + pie_function_start + + lea (message_prefix_panic - 0b)(%esi), %eax + + push %eax + push $0x4f + call _print + + mov 16(%ebp), %eax + mov %eax, 8(%ebp) + call _print + add $8, %esp + + call _halt + + pie_function_end + +/** + * Assert that we were loaded by a Multiboot 2 compliant bootloader. + * + * This assertion will panic the system if the magic signature was not found. + * If we were loaded my an appropriate bootloader, this function also saves + * the provided MBI pointer to `multiboot_information_pointer`. + */ +_assert_loaded_by_multiboot2_loader: + pie_function_start + + cmp $MULTIBOOT2_MAGIC, %eax + je 1f + lea (message_not_loaded_by_multiboot2 - 0b)(%esi), %eax + push %eax + call _panic +1: + pie_function_end + +/** + * @brief Store the multiboot 2 information pointer in the global memory. + * + * @return void + */ +_save_multiboot_information_pointer: + pie_function_start + + lea (multiboot_information_pointer - 0b)(%esi), %eax + mov %ebx, (%eax) + + pie_function_end + +/** + * @brief Assert that the CPU supports the CPUID instruction. + * + * The primary way to check for support of the instruction is to flip the ID + * bin in EFLAGS and then check if this changed was accepted. If so, the CPU + * supports the CPUID instruction, otherwise it most-likely doesn't. + */ +_assert_cpuid_instruction_is_supported: + pie_function_start + + pushfl + pop %eax + mov %eax, %ecx + + xor $(1 << 21), %eax /* Flip the ID bit */ + push %eax /* Move the new bitset on the stack for loading */ + popfl /* Load the flags with ID set back into EFLAGS */ + pushfl /* Copy the flags back onto the stack */ + pop %eax /* Load the flags for further checking */ + + push %ecx + popfl + + cmp %ecx, %eax + jne 1f + lea (message_cpuid_instruction_no_supported - 0b)(%esi), %eax + push %eax + call _panic + +1: + pie_function_end + +/** + * @brief Assert that the CPU supports going into long mode. + */ +_assert_cpu_supports_long_mode: + pie_function_start + + mov $0x80000000, %eax + cpuid + cmp $0x80000001, %eax + jb 1f + + mov $0x80000001, %eax + cpuid + test $(1 << 29), %edx + jnz 2f +1: + lea (message_long_mode_not_supported - 0b)(%esi), %eax + push %eax + call _panic +2: + pie_function_end + +/** + * @brief Prepare a recursive page map hierarchy + * + * We map all physical memory we were loaded in plus one additional page. The + * mapping is done in terms of huge pages (2 MiB per page) to save on required + * page map entries. + * + * @return void + */ +_prepare_page_maps: + pie_function_start + push %ebx + + /* Map the P4 table recursively */ + lea (page_map_level_4 - 0b)(%esi), %eax + mov %eax, %ebx + or $0b11, %ebx + mov %ebx, (511 * 8)(%eax) + + /* Add an entry to the PML4, pointing to the PML3 */ + lea (page_map_level_3 - 0b)(%esi), %ebx + or $0b11, %ebx + mov %ebx, (((0x0000000000100000 >> 39) & 0x1ff) * 8)(%eax) + + /* Add an entry to the PML3, pointing to the PML2 */ + lea (page_map_level_3 - 0b)(%esi), %eax + lea (page_map_level_2 - 0b)(%esi), %ebx + or $0b11, %ebx + mov %ebx, (((0x0000000000100000 >> 30) & 0x1ff) * 8)(%eax) + + /* Add entries for huge pages to the PML2 */ + push %edi + lea (page_map_level_2 - 0b)(%esi), %ebx + mov $HUGE_PAGES_TO_MAP, %edi + xor %ecx, %ecx + +1: + mov $(1 << 21), %eax + mul %ecx + or $0b10000011, %eax + mov %eax, (%ebx, %ecx, 8) + + inc %ecx + cmp %edi, %ecx + jne 1b + + pop %edi + pop %ebx + pie_function_end + +/** + * @p Enable memory virtualization via paging. + * + * Note: This routine expects for there to be a valid set of page maps already + * set up for use. + * + * @return void + */ +_enable_paging: + pie_function_start + + lea (page_map_level_4 - 0b)(%esi), %eax + mov %eax, %cr3 + + /* Enable Physical Address Extension */ + mov %cr4, %eax + or $(1 << 5), %eax + mov %eax, %cr4 + + /* Enable long mode support */ + mov $0xC0000080, %ecx + rdmsr + or $(1 << 8), %eax + wrmsr + + /* Enable paging */ + mov %cr0, %eax + or $(1 << 31), %eax + mov %eax, %cr0 + + pie_function_end + +/** + * @brief Enable use of SSE instructions. + */ +_enable_sse: + function_start + + mov %cr0, %eax + and $0xfffffffb, %eax + or $0x00000002, %eax + mov %eax, %cr0 + + mov %cr4, %eax + or $(3 << 9), %eax + mov %eax, %cr4 + + function_end + +/** + * @brief Prepare a new GTD and load make it active. + * + * @return void + */ +_reload_gdt: + pie_function_start + + sub $10, %esp + lea (global_descriptor_table - 0b)(%esi), %eax + movw $(global_descriptor_table_end - global_descriptor_table -1), (%esp) + mov %eax, 2(%esp) + movl $0, 6(%esp) + + lgdt (%esp) + add $10, %esp + + pie_function_end + +.section .boot_text, "ax", @progbits +.code64 + +_transition_to_long_mode: + mov $global_descriptor_table_data, %rax + mov %rax, %ss + mov %rax, %ds + mov %rax, %es + mov %rax, %fs + mov %rax, %gs + + xor %rax, %rax + + call _init + + call main + call _halt diff --git a/arch/x86_64/src/boot/boot.s b/arch/x86_64/src/boot/boot.s deleted file mode 100644 index 7a46795..0000000 --- a/arch/x86_64/src/boot/boot.s +++ /dev/null @@ -1,442 +0,0 @@ -/** - * @brief Uninitialized data for the bootstrapping process. - */ -.section .boot_bss, "aw", @nobits - -/** - * @brief Storage for the multiboot2 information pointer. - */ -.global multiboot_information_pointer -multiboot_information_pointer: .skip 8 - -.align 4096 - -page_map_level_4: .skip 512 * 8 -page_map_level_3: .skip 512 * 8 -page_map_level_2: .skip 512 * 8 - -/** - * @brief Storage for the bootstrap stack. - */ -.section .boot_stack, "aw", @nobits -.align 16 - -stack_bottom: .skip 1 << 20 -stack_top: - -/** - * @brief Constants for the bootstrapping process. - */ -.section .boot_rodata, "a", @progbits - -/** - * @brief A basic GDT for long mode. - */ -global_descriptor_table: -.set GDT_ACCESSED, 40 -.set GDT_READ_WRITE, 41 -.set GDT_EXECUTABLE, 43 -.set GDT_DESCRIPTOR_TYPE, 44 -.set GDT_PRESENT, 47 -.set GDT_LONG_MODE, 53 -global_descriptor_table_null = . - global_descriptor_table -.quad 0 -global_descriptor_table_code = . - global_descriptor_table -.quad (1 << GDT_ACCESSED) | (1 << GDT_READ_WRITE) | (1 << GDT_EXECUTABLE) | (1 << GDT_DESCRIPTOR_TYPE) | (1 << GDT_PRESENT) | (1 << GDT_LONG_MODE) -global_descriptor_table_data = . - global_descriptor_table -.quad (1 << GDT_ACCESSED) | (1 << GDT_READ_WRITE) | (1 << GDT_DESCRIPTOR_TYPE) | (1 << GDT_PRESENT) -global_descriptor_table_end: - -message_prefix_panic: .string "Panic: " -message_not_loaded_by_multiboot2: .string "The operating system was not loaded by a Multiboot 2 loader." -message_cpuid_instruction_no_supported: .string "The 'cpuid' instruction is not supported on this platform." -message_long_mode_not_supported: .string "Long mode is not supported by this platform." -message_return_from_kernel_main: .string "Execution returned from kernel main." - -/** - * @brief Initialized data for the bootstrapping process. - */ -.section .boot_data, "aw", @progbits - -/** - * @brief A pointer to the current position within the VGA text buffer. - */ -.global vga_buffer_pointer -vga_buffer_pointer: .quad 0xb8000 - -/** - * @brief Code for the bootstrapping process. - */ -.section .boot_text, "ax", @progbits -.align 16 -.code32 - -.macro pie_base - push %esi - call 0f - 0: - pop %esi -.endm - -.macro function_start - push %ebp - mov %esp, %ebp -.endm - -.macro function_end - leave - ret -.endm - -.macro pie_function_start - function_start - pie_base -.endm - -.macro pie_function_end - pop %esi - function_end -.endm - -/** - * @brief Prepare the environment and start the kernel. - * - * This function performs all necessary checks to ensure the system was loaded - * by the expected loader and supports all features required to run the kernel. - * If successful, it prepares the system by setting up memory virtualization - * and then start the kernel proper. - * - * @param %eax The Multiboot 2 magic marker. - * @param %ebx The Multiboot 2 information pointer. - * @return void This function does not return. - */ -.global _start -_start: - call 0f -0: - pop %esi - - lea (stack_top - 0b)(%esi), %ecx - - mov %ecx, %esp - mov %esp, %ebp - - call _assert_loaded_by_multiboot2_loader - call _save_multiboot_information_pointer - - call _assert_cpuid_instruction_is_supported - call _assert_cpu_supports_long_mode - - call _prepare_page_maps - call _enable_paging - call _enable_sse - call _reload_gdt - - lea (_transition_to_long_mode - 0b)(%esi), %eax - pushl $global_descriptor_table_code - pushl %eax - lret - -/** - * @brief Halt the system. - * - * This function will instruct the CPU to halt. It will try to keep the CPU - * halted, even if interrupts occur. - * - * @return This function never returns. - */ -.global halt -_halt: - function_start - -1: - hlt - jmp 1b - - function_end - -/** - * @brief Print a message via the VGA text buffer. - * - * @param ebp+12 The message to print. - * @param ebp+8 The color to print the message in. - */ -_print: - pie_function_start - - push %edi - push %ebx - - mov 8(%ebp), %al - mov 12(%ebp), %edx - mov $0, %ecx - lea (vga_buffer_pointer - 0b)(%esi), %edi - mov (%edi), %edi - -1: - mov (%edx, %ecx), %bl - test %bl, %bl - je 2f - mov %bl, (%edi, %ecx, 2) - mov %al, 1(%edi, %ecx, 2) - inc %ecx - jmp 1b - -2: - shl $1, %ecx - add %ecx, %edi - lea (vga_buffer_pointer - 0b)(%esi), %ecx - mov %edi, (%ecx) - - pop %ebx - pop %edi - - pie_function_end - -/** - * @brief Print a given panic message and then halt the machine as if by calling ::halt() - * - * @param ebp+4 A message to print. - * @return This function does not return. - */ -_panic: - pie_function_start - - lea (message_prefix_panic - 0b)(%esi), %eax - - push %eax - push $0x4f - call _print - - mov 16(%ebp), %eax - mov %eax, 8(%ebp) - call _print - add $8, %esp - - call _halt - - pie_function_end - -/** - * Assert that we were loaded by a Multiboot 2 compliant bootloader. - * - * This assertion will panic the system if the magic signature was not found. - * If we were loaded my an appropriate bootloader, this function also saves - * the provided MBI pointer to `multiboot_information_pointer`. - */ -_assert_loaded_by_multiboot2_loader: - pie_function_start - - .set MULTIBOOT2_MAGIC, 0x36d76289 - cmp $MULTIBOOT2_MAGIC, %eax - je 1f - lea (message_not_loaded_by_multiboot2 - 0b)(%esi), %eax - push %eax - call _panic -1: - pie_function_end - -/** - * @brief Store the multiboot 2 information pointer in the global memory. - * - * @return void - */ -_save_multiboot_information_pointer: - pie_function_start - - lea (multiboot_information_pointer - 0b)(%esi), %eax - mov %ebx, (%eax) - - pie_function_end - -/** - * @brief Assert that the CPU supports the CPUID instruction. - * - * The primary way to check for support of the instruction is to flip the ID - * bin in EFLAGS and then check if this changed was accepted. If so, the CPU - * supports the CPUID instruction, otherwise it most-likely doesn't. - */ -_assert_cpuid_instruction_is_supported: - pie_function_start - - pushfl - pop %eax - mov %eax, %ecx - - xor $(1 << 21), %eax /* Flip the ID bit */ - push %eax /* Move the new bitset on the stack for loading */ - popfl /* Load the flags with ID set back into EFLAGS */ - pushfl /* Copy the flags back onto the stack */ - pop %eax /* Load the flags for further checking */ - - push %ecx - popfl - - cmp %ecx, %eax - jne 1f - lea (message_cpuid_instruction_no_supported - 0b)(%esi), %eax - push %eax - call _panic - -1: - pie_function_end - -/** - * @brief Assert that the CPU supports going into long mode. - */ -_assert_cpu_supports_long_mode: - pie_function_start - - mov $0x80000000, %eax - cpuid - cmp $0x80000001, %eax - jb 1f - - mov $0x80000001, %eax - cpuid - test $(1 << 29), %edx - jnz 2f -1: - lea (message_long_mode_not_supported - 0b)(%esi), %eax - push %eax - call _panic -2: - pie_function_end - -/** - * @brief Prepare a recursive page map hierarchy - * - * We map all physical memory we were loaded in plus one additional page. The - * mapping is done in terms of huge pages (2 MiB per page) to save on required - * page map entries. - * - * @return void - */ -_prepare_page_maps: - pie_function_start - push %ebx - - .set HUGE_PAGES_TO_MAP, 16 - - /* Map the P4 table recursively */ - lea (page_map_level_4 - 0b)(%esi), %eax - mov %eax, %ebx - or $0b11, %ebx - mov %ebx, (511 * 8)(%eax) - - /* Add an entry to the PML4, pointing to the PML3 */ - lea (page_map_level_3 - 0b)(%esi), %ebx - or $0b11, %ebx - mov %ebx, (((0x0000000000100000 >> 39) & 0x1ff) * 8)(%eax) - - /* Add an entry to the PML3, pointing to the PML2 */ - lea (page_map_level_3 - 0b)(%esi), %eax - lea (page_map_level_2 - 0b)(%esi), %ebx - or $0b11, %ebx - mov %ebx, (((0x0000000000100000 >> 30) & 0x1ff) * 8)(%eax) - - /* Add entries for huge pages to the PML2 */ - push %edi - lea (page_map_level_2 - 0b)(%esi), %ebx - mov $HUGE_PAGES_TO_MAP, %edi - xor %ecx, %ecx - -1: - mov $(1 << 21), %eax - mul %ecx - or $0b10000011, %eax - mov %eax, (%ebx, %ecx, 8) - - inc %ecx - cmp %edi, %ecx - jne 1b - - pop %edi - pop %ebx - pie_function_end - -/** - * @p Enable memory virtualization via paging. - * - * Note: This routine expects for there to be a valid set of page maps already - * set up for use. - * - * @return void - */For -_enable_paging: - pie_function_start - - lea (page_map_level_4 - 0b)(%esi), %eax - mov %eax, %cr3 - - /* Enable Physical Address Extension */ - mov %cr4, %eax - or $(1 << 5), %eax - mov %eax, %cr4 - - /* Enable long mode support */ - mov $0xC0000080, %ecx - rdmsr - or $(1 << 8), %eax - wrmsr - - /* Enable paging */ - mov %cr0, %eax - or $(1 << 31), %eax - mov %eax, %cr0 - - pie_function_end - -/** - * @brief Enable use of SSE instructions. - */ -_enable_sse: - function_start - - mov %cr0, %eax - and $0xfffffffb, %eax - or $0x00000002, %eax - mov %eax, %cr0 - - mov %cr4, %eax - or $(3 << 9), %eax - mov %eax, %cr4 - - function_end - -/** - * @brief Prepare a new GTD and load make it active. - * - * @return void - */ -_reload_gdt: - pie_function_start - - sub $10, %esp - lea (global_descriptor_table - 0b)(%esi), %eax - movw $(global_descriptor_table_end - global_descriptor_table -1), (%esp) - mov %eax, 2(%esp) - movl $0, 6(%esp) - - lgdt (%esp) - add $10, %esp - - pie_function_end - -.section .boot_text, "ax", @progbits -.code64 - -_transition_to_long_mode: - mov $global_descriptor_table_data, %rax - mov %rax, %ss - mov %rax, %ds - mov %rax, %es - mov %rax, %fs - mov %rax, %gs - - xor %rax, %rax - - call _init - - call main - call _halt diff --git a/arch/x86_64/src/memory.cpp b/arch/x86_64/src/memory.cpp index 2b16beb..a31627b 100644 --- a/arch/x86_64/src/memory.cpp +++ b/arch/x86_64/src/memory.cpp @@ -2,14 +2,13 @@ #include "kern/error.hpp" -#include "arch/asm_pointer.hpp" -#include "x86_64/memory/region_allocator.hpp" +#include "x86_64/boot/boot.hpp" #include #include -extern "C" teachos::arch::asm_pointer multiboot_information_pointer; +// extern "C" teachos::arch::asm_pointer multiboot_information_pointer; namespace teachos::arch::memory { -- cgit v1.2.3 From c8cb4346064c69ab8431aa0d3c287e2fad60ce80 Mon Sep 17 00:00:00 2001 From: Felix Morgner Date: Tue, 22 Jul 2025 21:23:23 +0000 Subject: x86_64: split bootstrap code along mode lines --- arch/x86_64/CMakeLists.txt | 3 +- arch/x86_64/src/boot/boot.S | 433 ----------------------------------------- arch/x86_64/src/boot/boot32.S | 418 +++++++++++++++++++++++++++++++++++++++ arch/x86_64/src/boot/entry64.s | 21 ++ 4 files changed, 441 insertions(+), 434 deletions(-) delete mode 100644 arch/x86_64/src/boot/boot.S create mode 100644 arch/x86_64/src/boot/boot32.S create mode 100644 arch/x86_64/src/boot/entry64.s (limited to 'arch/x86_64') diff --git a/arch/x86_64/CMakeLists.txt b/arch/x86_64/CMakeLists.txt index a32a0f5..6bb9e53 100644 --- a/arch/x86_64/CMakeLists.txt +++ b/arch/x86_64/CMakeLists.txt @@ -34,9 +34,10 @@ target_sources("arch-x86_64" PRIVATE #]============================================================================] target_sources("arch-x86_64" PRIVATE - "src/boot/boot.S" + "src/boot/boot32.S" "src/boot/crti.s" "src/boot/crtn.s" + "src/boot/entry64.s" "src/boot/multiboot.s" ) diff --git a/arch/x86_64/src/boot/boot.S b/arch/x86_64/src/boot/boot.S deleted file mode 100644 index d65c865..0000000 --- a/arch/x86_64/src/boot/boot.S +++ /dev/null @@ -1,433 +0,0 @@ -#include "x86_64/boot/boot.hpp" - -/** - * @brief Uninitialized data for the bootstrapping process. - */ -.section .boot_bss, "aw", @nobits - -/** - * @brief Storage for the multiboot2 information pointer. - */ -.global multiboot_information_pointer -multiboot_information_pointer: .skip 8 - -.align 4096 - -page_map_level_4: .skip 512 * 8 -page_map_level_3: .skip 512 * 8 -page_map_level_2: .skip 512 * 8 - -/** - * @brief Storage for the bootstrap stack. - */ -.section .boot_stack, "aw", @nobits -.align 16 - -stack_bottom: .skip 1 << 20 -stack_top: - -/** - * @brief Constants for the bootstrapping process. - */ -.section .boot_rodata, "a", @progbits - -/** - * @brief A basic GDT for long mode. - */ -global_descriptor_table: -global_descriptor_table_null = . - global_descriptor_table -.quad 0 -global_descriptor_table_code = . - global_descriptor_table -.quad GDT_ACCESSED | GDT_READ_WRITE | GDT_EXECUTABLE | GDT_DESCRIPTOR_TYPE | GDT_PRESENT | GDT_LONG_MODE -global_descriptor_table_data = . - global_descriptor_table -.quad GDT_ACCESSED | GDT_READ_WRITE | GDT_DESCRIPTOR_TYPE | GDT_PRESENT -global_descriptor_table_end: - -message_prefix_panic: .string "Panic: " -message_not_loaded_by_multiboot2: .string "The operating system was not loaded by a Multiboot 2 loader." -message_cpuid_instruction_no_supported: .string "The 'cpuid' instruction is not supported on this platform." -message_long_mode_not_supported: .string "Long mode is not supported by this platform." -message_return_from_kernel_main: .string "Execution returned from kernel main." - -/** - * @brief Initialized data for the bootstrapping process. - */ -.section .boot_data, "aw", @progbits - -/** - * @brief A pointer to the current position within the VGA text buffer. - */ -.global vga_buffer_pointer -vga_buffer_pointer: .quad 0xb8000 - -/** - * @brief Code for the bootstrapping process. - */ -.section .boot_text, "ax", @progbits -.align 16 -.code32 - -.macro pie_base - push %esi - call 0f - 0: - pop %esi -.endm - -.macro function_start - push %ebp - mov %esp, %ebp -.endm - -.macro function_end - leave - ret -.endm - -.macro pie_function_start - function_start - pie_base -.endm - -.macro pie_function_end - pop %esi - function_end -.endm - -/** - * @brief Prepare the environment and start the kernel. - * - * This function performs all necessary checks to ensure the system was loaded - * by the expected loader and supports all features required to run the kernel. - * If successful, it prepares the system by setting up memory virtualization - * and then start the kernel proper. - * - * @param %eax The Multiboot 2 magic marker. - * @param %ebx The Multiboot 2 information pointer. - * @return void This function does not return. - */ -_start: - call 0f -0: - pop %esi - - lea (stack_top - 0b)(%esi), %ecx - - mov %ecx, %esp - mov %esp, %ebp - - call _assert_loaded_by_multiboot2_loader - call _save_multiboot_information_pointer - - call _assert_cpuid_instruction_is_supported - call _assert_cpu_supports_long_mode - - call _prepare_page_maps - call _enable_paging - call _enable_sse - call _reload_gdt - - lea (_transition_to_long_mode - 0b)(%esi), %eax - pushl $global_descriptor_table_code - pushl %eax - lret - -/** - * @brief Halt the system. - * - * This function will instruct the CPU to halt. It will try to keep the CPU - * halted, even if interrupts occur. - * - * @return This function never returns. - */ -_halt: - function_start - -1: - hlt - jmp 1b - - function_end - -/** - * @brief Print a message via the VGA text buffer. - * - * @param ebp+12 The message to print. - * @param ebp+8 The color to print the message in. - */ -_print: - pie_function_start - - push %edi - push %ebx - - mov 8(%ebp), %al - mov 12(%ebp), %edx - mov $0, %ecx - lea (vga_buffer_pointer - 0b)(%esi), %edi - mov (%edi), %edi - -1: - mov (%edx, %ecx), %bl - test %bl, %bl - je 2f - mov %bl, (%edi, %ecx, 2) - mov %al, 1(%edi, %ecx, 2) - inc %ecx - jmp 1b - -2: - shl $1, %ecx - add %ecx, %edi - lea (vga_buffer_pointer - 0b)(%esi), %ecx - mov %edi, (%ecx) - - pop %ebx - pop %edi - - pie_function_end - -/** - * @brief Print a given panic message and then halt the machine as if by calling ::halt() - * - * @param ebp+4 A message to print. - * @return This function does not return. - */ -_panic: - pie_function_start - - lea (message_prefix_panic - 0b)(%esi), %eax - - push %eax - push $0x4f - call _print - - mov 16(%ebp), %eax - mov %eax, 8(%ebp) - call _print - add $8, %esp - - call _halt - - pie_function_end - -/** - * Assert that we were loaded by a Multiboot 2 compliant bootloader. - * - * This assertion will panic the system if the magic signature was not found. - * If we were loaded my an appropriate bootloader, this function also saves - * the provided MBI pointer to `multiboot_information_pointer`. - */ -_assert_loaded_by_multiboot2_loader: - pie_function_start - - cmp $MULTIBOOT2_MAGIC, %eax - je 1f - lea (message_not_loaded_by_multiboot2 - 0b)(%esi), %eax - push %eax - call _panic -1: - pie_function_end - -/** - * @brief Store the multiboot 2 information pointer in the global memory. - * - * @return void - */ -_save_multiboot_information_pointer: - pie_function_start - - lea (multiboot_information_pointer - 0b)(%esi), %eax - mov %ebx, (%eax) - - pie_function_end - -/** - * @brief Assert that the CPU supports the CPUID instruction. - * - * The primary way to check for support of the instruction is to flip the ID - * bin in EFLAGS and then check if this changed was accepted. If so, the CPU - * supports the CPUID instruction, otherwise it most-likely doesn't. - */ -_assert_cpuid_instruction_is_supported: - pie_function_start - - pushfl - pop %eax - mov %eax, %ecx - - xor $(1 << 21), %eax /* Flip the ID bit */ - push %eax /* Move the new bitset on the stack for loading */ - popfl /* Load the flags with ID set back into EFLAGS */ - pushfl /* Copy the flags back onto the stack */ - pop %eax /* Load the flags for further checking */ - - push %ecx - popfl - - cmp %ecx, %eax - jne 1f - lea (message_cpuid_instruction_no_supported - 0b)(%esi), %eax - push %eax - call _panic - -1: - pie_function_end - -/** - * @brief Assert that the CPU supports going into long mode. - */ -_assert_cpu_supports_long_mode: - pie_function_start - - mov $0x80000000, %eax - cpuid - cmp $0x80000001, %eax - jb 1f - - mov $0x80000001, %eax - cpuid - test $(1 << 29), %edx - jnz 2f -1: - lea (message_long_mode_not_supported - 0b)(%esi), %eax - push %eax - call _panic -2: - pie_function_end - -/** - * @brief Prepare a recursive page map hierarchy - * - * We map all physical memory we were loaded in plus one additional page. The - * mapping is done in terms of huge pages (2 MiB per page) to save on required - * page map entries. - * - * @return void - */ -_prepare_page_maps: - pie_function_start - push %ebx - - /* Map the P4 table recursively */ - lea (page_map_level_4 - 0b)(%esi), %eax - mov %eax, %ebx - or $0b11, %ebx - mov %ebx, (511 * 8)(%eax) - - /* Add an entry to the PML4, pointing to the PML3 */ - lea (page_map_level_3 - 0b)(%esi), %ebx - or $0b11, %ebx - mov %ebx, (((0x0000000000100000 >> 39) & 0x1ff) * 8)(%eax) - - /* Add an entry to the PML3, pointing to the PML2 */ - lea (page_map_level_3 - 0b)(%esi), %eax - lea (page_map_level_2 - 0b)(%esi), %ebx - or $0b11, %ebx - mov %ebx, (((0x0000000000100000 >> 30) & 0x1ff) * 8)(%eax) - - /* Add entries for huge pages to the PML2 */ - push %edi - lea (page_map_level_2 - 0b)(%esi), %ebx - mov $HUGE_PAGES_TO_MAP, %edi - xor %ecx, %ecx - -1: - mov $(1 << 21), %eax - mul %ecx - or $0b10000011, %eax - mov %eax, (%ebx, %ecx, 8) - - inc %ecx - cmp %edi, %ecx - jne 1b - - pop %edi - pop %ebx - pie_function_end - -/** - * @p Enable memory virtualization via paging. - * - * Note: This routine expects for there to be a valid set of page maps already - * set up for use. - * - * @return void - */ -_enable_paging: - pie_function_start - - lea (page_map_level_4 - 0b)(%esi), %eax - mov %eax, %cr3 - - /* Enable Physical Address Extension */ - mov %cr4, %eax - or $(1 << 5), %eax - mov %eax, %cr4 - - /* Enable long mode support */ - mov $0xC0000080, %ecx - rdmsr - or $(1 << 8), %eax - wrmsr - - /* Enable paging */ - mov %cr0, %eax - or $(1 << 31), %eax - mov %eax, %cr0 - - pie_function_end - -/** - * @brief Enable use of SSE instructions. - */ -_enable_sse: - function_start - - mov %cr0, %eax - and $0xfffffffb, %eax - or $0x00000002, %eax - mov %eax, %cr0 - - mov %cr4, %eax - or $(3 << 9), %eax - mov %eax, %cr4 - - function_end - -/** - * @brief Prepare a new GTD and load make it active. - * - * @return void - */ -_reload_gdt: - pie_function_start - - sub $10, %esp - lea (global_descriptor_table - 0b)(%esi), %eax - movw $(global_descriptor_table_end - global_descriptor_table -1), (%esp) - mov %eax, 2(%esp) - movl $0, 6(%esp) - - lgdt (%esp) - add $10, %esp - - pie_function_end - -.section .boot_text, "ax", @progbits -.code64 - -_transition_to_long_mode: - mov $global_descriptor_table_data, %rax - mov %rax, %ss - mov %rax, %ds - mov %rax, %es - mov %rax, %fs - mov %rax, %gs - - xor %rax, %rax - - call _init - - call main - call _halt diff --git a/arch/x86_64/src/boot/boot32.S b/arch/x86_64/src/boot/boot32.S new file mode 100644 index 0000000..7e6c2ae --- /dev/null +++ b/arch/x86_64/src/boot/boot32.S @@ -0,0 +1,418 @@ +#include "x86_64/boot/boot.hpp" + +/** + * @brief Uninitialized data for the bootstrapping process. + */ +.section .boot_bss, "aw", @nobits + +/** + * @brief Storage for the multiboot2 information pointer. + */ +.global multiboot_information_pointer +multiboot_information_pointer: .skip 8 + +.align 4096 + +page_map_level_4: .skip 512 * 8 +page_map_level_3: .skip 512 * 8 +page_map_level_2: .skip 512 * 8 + +/** + * @brief Storage for the bootstrap stack. + */ +.section .boot_stack, "aw", @nobits +.align 16 + +stack_bottom: .skip 1 << 20 +stack_top: + +/** + * @brief Constants for the bootstrapping process. + */ +.section .boot_rodata, "a", @progbits + +.global global_descriptor_table_data + +/** + * @brief A basic GDT for long mode. + */ +global_descriptor_table: +global_descriptor_table_null = . - global_descriptor_table +.quad 0 +global_descriptor_table_code = . - global_descriptor_table +.quad GDT_ACCESSED | GDT_READ_WRITE | GDT_EXECUTABLE | GDT_DESCRIPTOR_TYPE | GDT_PRESENT | GDT_LONG_MODE +global_descriptor_table_data = . - global_descriptor_table +.quad GDT_ACCESSED | GDT_READ_WRITE | GDT_DESCRIPTOR_TYPE | GDT_PRESENT +global_descriptor_table_end: + +message_prefix_panic: .string "Panic: " +message_not_loaded_by_multiboot2: .string "The operating system was not loaded by a Multiboot 2 loader." +message_cpuid_instruction_no_supported: .string "The 'cpuid' instruction is not supported on this platform." +message_long_mode_not_supported: .string "Long mode is not supported by this platform." +message_return_from_kernel_main: .string "Execution returned from kernel main." + +/** + * @brief Initialized data for the bootstrapping process. + */ +.section .boot_data, "aw", @progbits + +/** + * @brief A pointer to the current position within the VGA text buffer. + */ +.global vga_buffer_pointer +vga_buffer_pointer: .quad 0xb8000 + +/** + * @brief Code for the bootstrapping process. + */ +.section .boot_text, "ax", @progbits +.align 16 +.code32 + +.macro pie_base + push %esi + call 0f + 0: + pop %esi +.endm + +.macro function_start + push %ebp + mov %esp, %ebp +.endm + +.macro function_end + leave + ret +.endm + +.macro pie_function_start + function_start + pie_base +.endm + +.macro pie_function_end + pop %esi + function_end +.endm + +/** + * @brief Prepare the environment and start the kernel. + * + * This function performs all necessary checks to ensure the system was loaded + * by the expected loader and supports all features required to run the kernel. + * If successful, it prepares the system by setting up memory virtualization + * and then start the kernel proper. + * + * @param %eax The Multiboot 2 magic marker. + * @param %ebx The Multiboot 2 information pointer. + * @return void This function does not return. + */ +.global _start +_start: + call 0f +0: + pop %esi + + lea (stack_top - 0b)(%esi), %ecx + + mov %ecx, %esp + mov %esp, %ebp + + call _assert_loaded_by_multiboot2_loader + call _save_multiboot_information_pointer + + call _assert_cpuid_instruction_is_supported + call _assert_cpu_supports_long_mode + + call _prepare_page_maps + call _enable_paging + call _enable_sse + call _reload_gdt + + lea (_entry64 - 0b)(%esi), %eax + pushl $global_descriptor_table_code + pushl %eax + lret + +/** + * @brief Halt the system. + * + * This function will instruct the CPU to halt. It will try to keep the CPU + * halted, even if interrupts occur. + * + * @return This function never returns. + */ +_halt: + function_start + +1: + hlt + jmp 1b + + function_end + +/** + * @brief Print a message via the VGA text buffer. + * + * @param ebp+12 The message to print. + * @param ebp+8 The color to print the message in. + */ +_print: + pie_function_start + + push %edi + push %ebx + + mov 8(%ebp), %al + mov 12(%ebp), %edx + mov $0, %ecx + lea (vga_buffer_pointer - 0b)(%esi), %edi + mov (%edi), %edi + +1: + mov (%edx, %ecx), %bl + test %bl, %bl + je 2f + mov %bl, (%edi, %ecx, 2) + mov %al, 1(%edi, %ecx, 2) + inc %ecx + jmp 1b + +2: + shl $1, %ecx + add %ecx, %edi + lea (vga_buffer_pointer - 0b)(%esi), %ecx + mov %edi, (%ecx) + + pop %ebx + pop %edi + + pie_function_end + +/** + * @brief Print a given panic message and then halt the machine as if by calling ::halt() + * + * @param ebp+4 A message to print. + * @return This function does not return. + */ +_panic: + pie_function_start + + lea (message_prefix_panic - 0b)(%esi), %eax + + push %eax + push $0x4f + call _print + + mov 16(%ebp), %eax + mov %eax, 8(%ebp) + call _print + add $8, %esp + + call _halt + + pie_function_end + +/** + * Assert that we were loaded by a Multiboot 2 compliant bootloader. + * + * This assertion will panic the system if the magic signature was not found. + * If we were loaded my an appropriate bootloader, this function also saves + * the provided MBI pointer to `multiboot_information_pointer`. + */ +_assert_loaded_by_multiboot2_loader: + pie_function_start + + cmp $MULTIBOOT2_MAGIC, %eax + je 1f + lea (message_not_loaded_by_multiboot2 - 0b)(%esi), %eax + push %eax + call _panic +1: + pie_function_end + +/** + * @brief Store the multiboot 2 information pointer in the global memory. + * + * @return void + */ +_save_multiboot_information_pointer: + pie_function_start + + lea (multiboot_information_pointer - 0b)(%esi), %eax + mov %ebx, (%eax) + + pie_function_end + +/** + * @brief Assert that the CPU supports the CPUID instruction. + * + * The primary way to check for support of the instruction is to flip the ID + * bin in EFLAGS and then check if this changed was accepted. If so, the CPU + * supports the CPUID instruction, otherwise it most-likely doesn't. + */ +_assert_cpuid_instruction_is_supported: + pie_function_start + + pushfl + pop %eax + mov %eax, %ecx + + xor $(1 << 21), %eax /* Flip the ID bit */ + push %eax /* Move the new bitset on the stack for loading */ + popfl /* Load the flags with ID set back into EFLAGS */ + pushfl /* Copy the flags back onto the stack */ + pop %eax /* Load the flags for further checking */ + + push %ecx + popfl + + cmp %ecx, %eax + jne 1f + lea (message_cpuid_instruction_no_supported - 0b)(%esi), %eax + push %eax + call _panic + +1: + pie_function_end + +/** + * @brief Assert that the CPU supports going into long mode. + */ +_assert_cpu_supports_long_mode: + pie_function_start + + mov $0x80000000, %eax + cpuid + cmp $0x80000001, %eax + jb 1f + + mov $0x80000001, %eax + cpuid + test $(1 << 29), %edx + jnz 2f +1: + lea (message_long_mode_not_supported - 0b)(%esi), %eax + push %eax + call _panic +2: + pie_function_end + +/** + * @brief Prepare a recursive page map hierarchy + * + * We map all physical memory we were loaded in plus one additional page. The + * mapping is done in terms of huge pages (2 MiB per page) to save on required + * page map entries. + * + * @return void + */ +_prepare_page_maps: + pie_function_start + push %ebx + + /* Map the P4 table recursively */ + lea (page_map_level_4 - 0b)(%esi), %eax + mov %eax, %ebx + or $0b11, %ebx + mov %ebx, (511 * 8)(%eax) + + /* Add an entry to the PML4, pointing to the PML3 */ + lea (page_map_level_3 - 0b)(%esi), %ebx + or $0b11, %ebx + mov %ebx, (((0x0000000000100000 >> 39) & 0x1ff) * 8)(%eax) + + /* Add an entry to the PML3, pointing to the PML2 */ + lea (page_map_level_3 - 0b)(%esi), %eax + lea (page_map_level_2 - 0b)(%esi), %ebx + or $0b11, %ebx + mov %ebx, (((0x0000000000100000 >> 30) & 0x1ff) * 8)(%eax) + + /* Add entries for huge pages to the PML2 */ + push %edi + lea (page_map_level_2 - 0b)(%esi), %ebx + mov $HUGE_PAGES_TO_MAP, %edi + xor %ecx, %ecx + +1: + mov $(1 << 21), %eax + mul %ecx + or $0b10000011, %eax + mov %eax, (%ebx, %ecx, 8) + + inc %ecx + cmp %edi, %ecx + jne 1b + + pop %edi + pop %ebx + pie_function_end + +/** + * @p Enable memory virtualization via paging. + * + * Note: This routine expects for there to be a valid set of page maps already + * set up for use. + * + * @return void + */ +_enable_paging: + pie_function_start + + lea (page_map_level_4 - 0b)(%esi), %eax + mov %eax, %cr3 + + /* Enable Physical Address Extension */ + mov %cr4, %eax + or $(1 << 5), %eax + mov %eax, %cr4 + + /* Enable long mode support */ + mov $0xC0000080, %ecx + rdmsr + or $(1 << 8), %eax + wrmsr + + /* Enable paging */ + mov %cr0, %eax + or $(1 << 31), %eax + mov %eax, %cr0 + + pie_function_end + +/** + * @brief Enable use of SSE instructions. + */ +_enable_sse: + function_start + + mov %cr0, %eax + and $0xfffffffb, %eax + or $0x00000002, %eax + mov %eax, %cr0 + + mov %cr4, %eax + or $(3 << 9), %eax + mov %eax, %cr4 + + function_end + +/** + * @brief Prepare a new GTD and load make it active. + * + * @return void + */ +_reload_gdt: + pie_function_start + + sub $10, %esp + lea (global_descriptor_table - 0b)(%esi), %eax + movw $(global_descriptor_table_end - global_descriptor_table -1), (%esp) + mov %eax, 2(%esp) + movl $0, 6(%esp) + + lgdt (%esp) + add $10, %esp + + pie_function_end diff --git a/arch/x86_64/src/boot/entry64.s b/arch/x86_64/src/boot/entry64.s new file mode 100644 index 0000000..f575c50 --- /dev/null +++ b/arch/x86_64/src/boot/entry64.s @@ -0,0 +1,21 @@ +.section .boot_text, "ax", @progbits +.code64 + +.global _entry64 +_entry64: + mov $global_descriptor_table_data, %rax + mov %rax, %ss + mov %rax, %ds + mov %rax, %es + mov %rax, %fs + mov %rax, %gs + + xor %rax, %rax + + call _init + + call main + +1: + hlt + jmp 1b -- cgit v1.2.3 From b4c45759c94d61493bbb88dacfd5c1bccfb13ac6 Mon Sep 17 00:00:00 2001 From: Felix Morgner Date: Wed, 23 Jul 2025 07:26:45 +0000 Subject: x86_64: remove stray copy of ld script --- arch/x86_64/scripts/x86-64-linker-script.txt | 150 --------------------------- 1 file changed, 150 deletions(-) delete mode 100644 arch/x86_64/scripts/x86-64-linker-script.txt (limited to 'arch/x86_64') diff --git a/arch/x86_64/scripts/x86-64-linker-script.txt b/arch/x86_64/scripts/x86-64-linker-script.txt deleted file mode 100644 index 3d9a7ae..0000000 --- a/arch/x86_64/scripts/x86-64-linker-script.txt +++ /dev/null @@ -1,150 +0,0 @@ -ENTRY(_start) - -/***************************************************************************** - * Virtual and linear start addresses of the TeachOS kernel - *****************************************************************************/ -TEACHOS_LOW = 1M; - -PHDRS { - boot_rodata PT_LOAD FLAGS(4); - boot_text PT_LOAD FLAGS(5); - boot_data PT_LOAD FLAGS(6); - - text PT_LOAD FLAGS(5); - data PT_LOAD FLAGS(6); - rodata PT_LOAD FLAGS(4); -} - -SECTIONS -{ - /*************************************************************************** - * Load the bootstrap code into low memory. We need to be accessible in - * 32-Bit mode, so we want to live down low, but we need to leave the 1MiB - * hole open since some BIOS functionality resides below it. - ***************************************************************************/ - . = TEACHOS_LOW; - - /*************************************************************************** - * We want to be able to be able to access all memory (linear and virtual) - * during bootstrapping and operation. To achieve this, we define some - * symbols at the beginning. - ***************************************************************************/ - _start_linear = .; - _start_virtual = .; - - /*************************************************************************** - * The bootstrapping infratructure goes first. We first place the read-only - * data, followed by our code, initialized mutable data, and finally our - * uninitialized mutable data. - ***************************************************************************/ - .boot_rodata ALIGN(4K) : AT(ADDR (.boot_rodata)) - { - KEEP(*(.boot_mbh)) - *(.boot_rodata) - } :boot_rodata - - .boot_text ALIGN(4K) : AT(ADDR (.boot_text)) - { - *(.boot_text) - } :boot_text - - .boot_bss ALIGN(4K) : AT(ADDR (.boot_bss)) - { - *(.boot_bss) - *(.boot_stack) - } - - .boot_data ALIGN(4K) : AT(ADDR (.boot_data)) - { - *(.boot_data) - } :boot_data - - /*************************************************************************** - * Now it is time to load the 64-bit kernel code. We - * make sure to align the loaded data onto a page boundary. - ***************************************************************************/ - .init ALIGN(4K) : AT(ADDR (.init)) - { - /* - * Make sure that the crt code is wrapped around the compiler generated - * initialization code. - */ - KEEP(*crti.s.o*(.init)) - KEEP(*(EXCLUDE_FILE (*crti.s.o* *crtn.s.o*) .init)) - KEEP(*crtn.s.o*(.init)) - } :text - - .fini ALIGN(4K) : AT(ADDR (.fini)) - { - /* - * Make sure that the crt code is wrapped around the compiler generated - * finalizer code. - */ - KEEP(*crti.s.o*(.fini)) - KEEP(*(EXCLUDE_FILE (*crti.s.o* *crtn.s.o*) .fini)) - KEEP(*crtn.s.o*(.fini)) - } - - .stl_text ALIGN(4K) : AT(ADDR (.stl_text)) - { - *(.stl_text .stl_text*) - KEEP(*libstdc++.a:*(.text .text.*)) - } - - .text ALIGN(4K) : AT(ADDR (.text)) - { - *(.text .text.*) - } - - .user_text ALIGN(4K) : AT(ADDR (.user_text)) - { - *(.user_text .user_text.*) - } - - .rodata ALIGN(4K) : AT (ADDR (.rodata)) - { - *(.rodata) - *(.rodata.*) - } :rodata - - .ctors ALIGN(4K) : AT (ADDR (.ctors)) - { - KEEP(*crtbegin.o(.ctors)) - KEEP(*(EXCLUDE_FILE (*crtend.o) .ctors)) - KEEP(*(SORT(.ctors.*))) - KEEP(*crtend.o(.ctors)) - } :data - - .dtors ALIGN(4K) : AT (ADDR (.dtors)) - { - KEEP(*crtbegin.o(.dtors)) - KEEP(*(EXCLUDE_FILE (*crtend.o) .dtors)) - KEEP(*(SORT(.dtors.*))) - KEEP(*crtend.o(.dtors)) - } - - .bss ALIGN(4K) : AT (ADDR (.bss)) - { - *(COMMON) - *(.bss*) - } - - .data ALIGN(4K) : AT (ADDR (.data)) - { - *(.data*) - } - - .user_data ALIGN(4K) : AT (ADDR (.user_data)) - { - *(.user_data .user_data.*) - } - - /*************************************************************************** - * In accordance with the symbol definitions at the start, we generate some - * symbols to mark the end of our loaded image. - ***************************************************************************/ - _end_virtual = ADDR(.bss) + SIZEOF(.bss); - _end_linear = _end_virtual; - - /DISCARD/ : { *(.comment) } -} -- cgit v1.2.3 From e97e86d169849527190cef1913efdd247e6f68df Mon Sep 17 00:00:00 2001 From: Felix Morgner Date: Wed, 23 Jul 2025 13:21:32 +0000 Subject: libs: move asm_ptr to kstd --- arch/x86_64/include/x86_64/boot/boot.hpp | 8 ++++---- arch/x86_64/src/vga/text.cpp | 9 ++++----- 2 files changed, 8 insertions(+), 9 deletions(-) (limited to 'arch/x86_64') diff --git a/arch/x86_64/include/x86_64/boot/boot.hpp b/arch/x86_64/include/x86_64/boot/boot.hpp index 066e49e..1887e73 100644 --- a/arch/x86_64/include/x86_64/boot/boot.hpp +++ b/arch/x86_64/include/x86_64/boot/boot.hpp @@ -44,21 +44,21 @@ #else -#include "arch/asm_pointer.hpp" - #include +#include + extern "C" { /** * @brief A pointer to the multiboot 2 information structure provided by the boot loader. */ - extern teachos::arch::asm_pointer multiboot_information_pointer; + extern kstd::asm_ptr multiboot_information_pointer; /** * @brief A pointer to the VGA text mode buffer. */ - extern teachos::arch::asm_pointer> vga_buffer_pointer; + extern kstd::asm_ptr> vga_buffer_pointer; } #endif diff --git a/arch/x86_64/src/vga/text.cpp b/arch/x86_64/src/vga/text.cpp index 16abf08..dcfdb6b 100644 --- a/arch/x86_64/src/vga/text.cpp +++ b/arch/x86_64/src/vga/text.cpp @@ -1,15 +1,14 @@ #include "x86_64/vga/text.hpp" -#include "arch/asm_pointer.hpp" +#include "x86_64/boot/boot.hpp" #include "x86_64/vga/io.hpp" #include +#include #include #include #include -extern "C" teachos::arch::asm_pointer> vga_buffer_pointer; - namespace teachos::x86_64::vga::text { namespace @@ -23,7 +22,7 @@ namespace teachos::x86_64::vga::text auto clear(attribute attribute) -> void { buffer_offset = 0; - std::ranges::fill_n(vga_buffer_pointer.get(), 2000, std::pair{' ', attribute}); + std::ranges::fill_n(vga_buffer_pointer.get(), 2000, std::pair{' ', std::bit_cast(attribute)}); } auto cursor(bool enabled) -> void @@ -54,7 +53,7 @@ namespace teachos::x86_64::vga::text auto write_char(char code_point, attribute attribute) -> void { - vga_buffer_pointer[buffer_offset++] = std::pair{code_point, attribute}; + vga_buffer_pointer[buffer_offset++] = std::pair{code_point, std::bit_cast(attribute)}; }; auto write(std::string_view code_points, attribute attribute) -> void -- cgit v1.2.3 From 29f0e6ad021997e4b6995de4f795781ac381f697 Mon Sep 17 00:00:00 2001 From: Felix Morgner Date: Wed, 23 Jul 2025 13:27:30 +0000 Subject: x86_64: move port_io to x86_64/io --- arch/x86_64/include/arch/io/port_io.hpp | 133 ------------------------------ arch/x86_64/include/x86_64/io/port_io.hpp | 132 +++++++++++++++++++++++++++++ arch/x86_64/include/x86_64/vga/io.hpp | 6 +- 3 files changed, 135 insertions(+), 136 deletions(-) delete mode 100644 arch/x86_64/include/arch/io/port_io.hpp create mode 100644 arch/x86_64/include/x86_64/io/port_io.hpp (limited to 'arch/x86_64') diff --git a/arch/x86_64/include/arch/io/port_io.hpp b/arch/x86_64/include/arch/io/port_io.hpp deleted file mode 100644 index ba41660..0000000 --- a/arch/x86_64/include/arch/io/port_io.hpp +++ /dev/null @@ -1,133 +0,0 @@ -#ifndef TEACHOS_ARCH_X86_64_IO_PORT_IO_HPP -#define TEACHOS_ARCH_X86_64_IO_PORT_IO_HPP - -#include -#include -#include -#include - -namespace teachos::arch::io -{ - /** - * @brief An I/O port of a given size at a given address. - * - * @tparam Address The address (port number) of the I/O port. - * @tparam Size The size (in bytes) of the I/O port. - */ - template - struct port - { - static_assert(Size == 1 || Size == 2 || Size == 4, "A port must be either 1, 2, or 4 bytes in size"); - - /** - * @brief The type of data available for reading and writing through this port. - */ - using io_type = - std::conditional_t>; - - /** - * @brief Write a byte to the I/O port. - * - * @param data The data to write to the I/O port. - */ - auto static write(io_type data) -> void - requires(Size == 1) - { - asm volatile("mov %[port], %%dx\n" - "mov %[data], %%al\n" - "out %%al, %%dx\n" - : - : [port] "i"(Address), [data] "im"(data) - : "dx", "al"); - } - - /** - * @brief Write a word to the I/O port. - * - * @param data The data to write to the I/O port. - */ - auto static write(io_type data) -> void - requires(Size == 2) - { - asm volatile("mov %[port], %%dx\n" - "mov %[data], %%ax\n" - "out %%ax, %%dx\n" - : - : [port] "i"(Address), [data] "im"(data) - : "dx", "ax"); - } - - /** - * @brief Write a double-word to the I/O port. - * - * @param data The data to write to the I/O port. - */ - auto static write(io_type data) -> void - requires(Size == 4) - { - asm volatile("mov %[port], %%dx\n" - "mov %[data], %%eax\n" - "out %%eax, %%dx\n" - : - : [port] "i"(Address), [data] "im"(data) - : "dx", "eax"); - } - - /** - * @brief Read a byte from the I/O port. - * - * @return The data read from the I/O port. - */ - auto static read() -> io_type - requires(Size == 1) - { - auto data = io_type{}; - asm volatile("mov %[port], %%dx\n" - "in %%dx, %%al\n" - "mov %%al, %[data]\n" - : [data] "=m"(data) - : [port] "i"(Address) - : "dx", "al"); - return data; - } - - /** - * @brief Read a word from the I/O port. - * - * @return The data read from the I/O port. - */ - auto static read() -> io_type - requires(Size == 2) - { - auto data = io_type{}; - asm volatile("mov %[port], %%dx\n" - "in %%dx, %%ax\n" - "mov %%ax, %[data]\n" - : [data] "=m"(data) - : [port] "i"(Address) - : "dx", "ax"); - return data; - } - - /** - * @brief Read a double-word from the I/O port. - * - * @return The data read from the I/O port. - */ - auto static read() -> io_type - requires(Size == 4) - { - auto data = io_type{}; - asm volatile("mov %[port], %%dx\n" - "in %%dx, %%eax\n" - "mov %%eax, %[data]\n" - : [data] "=m"(data) - : [port] "i"(Address) - : "dx", "eax"); - return data; - } - }; - -} // namespace teachos::arch::io - -#endif // TEACHOS_ARCH_X86_64_IO_PORT_IO_HPP diff --git a/arch/x86_64/include/x86_64/io/port_io.hpp b/arch/x86_64/include/x86_64/io/port_io.hpp new file mode 100644 index 0000000..4cf0b65 --- /dev/null +++ b/arch/x86_64/include/x86_64/io/port_io.hpp @@ -0,0 +1,132 @@ +#ifndef TEACHOS_X86_64_IO_PORT_IO_HPP +#define TEACHOS_X86_64_IO_PORT_IO_HPP + +#include +#include +#include + +namespace teachos::x86_64::io +{ + /** + * @brief An I/O port of a given size at a given address. + * + * @tparam Address The address (port number) of the I/O port. + * @tparam Size The size (in bytes) of the I/O port. + */ + template + struct port + { + static_assert(Size == 1 || Size == 2 || Size == 4, "A port must be either 1, 2, or 4 bytes in size"); + + /** + * @brief The type of data available for reading and writing through this port. + */ + using io_type = + std::conditional_t>; + + /** + * @brief Write a byte to the I/O port. + * + * @param data The data to write to the I/O port. + */ + auto static write(io_type data) -> void + requires(Size == 1) + { + asm volatile("mov %[port], %%dx\n" + "mov %[data], %%al\n" + "out %%al, %%dx\n" + : + : [port] "i"(Address), [data] "im"(data) + : "dx", "al"); + } + + /** + * @brief Write a word to the I/O port. + * + * @param data The data to write to the I/O port. + */ + auto static write(io_type data) -> void + requires(Size == 2) + { + asm volatile("mov %[port], %%dx\n" + "mov %[data], %%ax\n" + "out %%ax, %%dx\n" + : + : [port] "i"(Address), [data] "im"(data) + : "dx", "ax"); + } + + /** + * @brief Write a double-word to the I/O port. + * + * @param data The data to write to the I/O port. + */ + auto static write(io_type data) -> void + requires(Size == 4) + { + asm volatile("mov %[port], %%dx\n" + "mov %[data], %%eax\n" + "out %%eax, %%dx\n" + : + : [port] "i"(Address), [data] "im"(data) + : "dx", "eax"); + } + + /** + * @brief Read a byte from the I/O port. + * + * @return The data read from the I/O port. + */ + auto static read() -> io_type + requires(Size == 1) + { + auto data = io_type{}; + asm volatile("mov %[port], %%dx\n" + "in %%dx, %%al\n" + "mov %%al, %[data]\n" + : [data] "=m"(data) + : [port] "i"(Address) + : "dx", "al"); + return data; + } + + /** + * @brief Read a word from the I/O port. + * + * @return The data read from the I/O port. + */ + auto static read() -> io_type + requires(Size == 2) + { + auto data = io_type{}; + asm volatile("mov %[port], %%dx\n" + "in %%dx, %%ax\n" + "mov %%ax, %[data]\n" + : [data] "=m"(data) + : [port] "i"(Address) + : "dx", "ax"); + return data; + } + + /** + * @brief Read a double-word from the I/O port. + * + * @return The data read from the I/O port. + */ + auto static read() -> io_type + requires(Size == 4) + { + auto data = io_type{}; + asm volatile("mov %[port], %%dx\n" + "in %%dx, %%eax\n" + "mov %%eax, %[data]\n" + : [data] "=m"(data) + : [port] "i"(Address) + : "dx", "eax"); + return data; + } + }; + +} // namespace teachos::x86_64::io + +#endif \ No newline at end of file diff --git a/arch/x86_64/include/x86_64/vga/io.hpp b/arch/x86_64/include/x86_64/vga/io.hpp index 803dc21..4d99788 100644 --- a/arch/x86_64/include/x86_64/vga/io.hpp +++ b/arch/x86_64/include/x86_64/vga/io.hpp @@ -1,7 +1,7 @@ #ifndef TEACHOS_X86_64_VGA_IO_HPP #define TEACHOS_X86_64_VGA_IO_HPP -#include "arch/io/port_io.hpp" +#include "x86_64/io/port_io.hpp" #include @@ -12,12 +12,12 @@ namespace teachos::x86_64::vga::io /** * @brief The address port of the CRT Controller. */ - using address_port = arch::io::port<0x3d4, 1>; + using address_port = x86_64::io::port<0x3d4, 1>; /** * @brief The data port of the CRT Controller. */ - using data_port = arch::io::port<0x3d5, 1>; + using data_port = x86_64::io::port<0x3d5, 1>; namespace registers { -- cgit v1.2.3 From be5c7e992ef3f7827e7229d77af3f812484de260 Mon Sep 17 00:00:00 2001 From: Felix Morgner Date: Wed, 23 Jul 2025 13:52:28 +0000 Subject: x86_64: extract linker script interface header --- arch/x86_64/include/x86_64/boot/ld.hpp | 22 ++++++++++++++++++++++ arch/x86_64/src/memory.cpp | 25 +++++++++++++++++++------ 2 files changed, 41 insertions(+), 6 deletions(-) create mode 100644 arch/x86_64/include/x86_64/boot/ld.hpp (limited to 'arch/x86_64') diff --git a/arch/x86_64/include/x86_64/boot/ld.hpp b/arch/x86_64/include/x86_64/boot/ld.hpp new file mode 100644 index 0000000..e6b397b --- /dev/null +++ b/arch/x86_64/include/x86_64/boot/ld.hpp @@ -0,0 +1,22 @@ +#ifndef TEACHOS_X86_64_BOOT_LD_H +#define TEACHOS_X86_64_BOOT_LD_H + +#include + +extern "C" +{ + namespace teachos::x86_64::boot + { + /** + * @brief The first byte of the loaded kernel image. + */ + extern "C" std::byte _start_linear; + + /** + * @brief The first byte after the loaded kernel image. + */ + extern "C" std::byte _end_linear; + } // namespace teachos::x86_64::boot +} + +#endif diff --git a/arch/x86_64/src/memory.cpp b/arch/x86_64/src/memory.cpp index a31627b..105d69b 100644 --- a/arch/x86_64/src/memory.cpp +++ b/arch/x86_64/src/memory.cpp @@ -3,22 +3,33 @@ #include "kern/error.hpp" #include "x86_64/boot/boot.hpp" +#include "x86_64/boot/ld.hpp" +#include "x86_64/memory/address.hpp" +#include "x86_64/memory/region_allocator.hpp" #include #include -// extern "C" teachos::arch::asm_pointer multiboot_information_pointer; - namespace teachos::arch::memory { + using namespace x86_64::memory; + using namespace x86_64::boot; + namespace { auto constinit is_initialized = std::atomic_flag{}; - // auto create_memory_information() -> x86_64::memory::region_allocator::memory_information { + auto create_memory_information() -> region_allocator::memory_information + { + auto const & mbi = multiboot_information_pointer.get(); + auto map = mbi->memory_map(); - // }; + return {std::make_pair(physical_address{&_start_linear}, physical_address{&_end_linear}), + std::make_pair(physical_address{std::bit_cast(&mbi)}, + physical_address{std::bit_cast(&mbi) + mbi->size_bytes()}), + map}; + }; } // namespace auto init() -> void @@ -34,8 +45,10 @@ namespace teachos::arch::memory teachos::panic("[x86_64] No memory map available."); } - // auto mem_info = x86_64::memory::region_allocator::memory_information{}; - // auto allocator = x86_64::memory::region_allocator{mem_info}; + auto mem_info = create_memory_information(); + auto allocator = region_allocator{mem_info}; + + static_cast(allocator); // kernel::cpu::set_cr0_bit(kernel::cpu::cr0_flags::WRITE_PROTECT); // kernel::cpu::set_efer_bit(kernel::cpu::efer_flags::NXE); -- cgit v1.2.3 From 05df795a860eaedf43602beadc2d1637bd2cdd14 Mon Sep 17 00:00:00 2001 From: Felix Morgner Date: Wed, 23 Jul 2025 14:00:08 +0000 Subject: x86_64: improve linker script interface docs - Add file-level Doxygen block to provide overall context. - Clarify the origin of the declared symbols. - Add information regarding their intended use. --- arch/x86_64/include/x86_64/boot/ld.hpp | 51 +++++++++++++++++++++++++--------- 1 file changed, 38 insertions(+), 13 deletions(-) (limited to 'arch/x86_64') diff --git a/arch/x86_64/include/x86_64/boot/ld.hpp b/arch/x86_64/include/x86_64/boot/ld.hpp index e6b397b..6051dd7 100644 --- a/arch/x86_64/include/x86_64/boot/ld.hpp +++ b/arch/x86_64/include/x86_64/boot/ld.hpp @@ -1,22 +1,47 @@ +/** + * @file + * @brief Provides an interface to linker script defined symbols. + * + * @details + * This header provides declarations for symbols that are defined in the linker script itself. The symbols declared here + * provide important information, for example the start and end of the kernel image in physical memory. + * + * Any variables defined in this file must not be read themselves, but rather their address shall be taken, yielding a + * pointer to the memory location the represent. + * + * @see arch/x86_64/scripts/kernel.ld + */ + #ifndef TEACHOS_X86_64_BOOT_LD_H #define TEACHOS_X86_64_BOOT_LD_H #include -extern "C" +namespace teachos::x86_64::boot { - namespace teachos::x86_64::boot - { - /** - * @brief The first byte of the loaded kernel image. - */ - extern "C" std::byte _start_linear; + /** + * @var _start_linear + * @brief The first byte of the loaded kernel image. + * + * @details + * This symbols is defined in the kernel linker script and marks the start of the kernel image in physical memory. + * To use this symbol for its intended purpose, the address of it shall be taken. + * + * @see _end_linear + */ + extern "C" std::byte _start_linear; - /** - * @brief The first byte after the loaded kernel image. - */ - extern "C" std::byte _end_linear; - } // namespace teachos::x86_64::boot -} + /** + * @var _end_linear + * @brief The first byte after the loaded kernel image. + * + * @details + * This symbols is defined in the kernel linker script and marks the end of the kernel image in physical memory. + * To use this symbol for its intended purpose, the address of it shall be taken. + * + * @see _start_linear + */ + extern "C" std::byte _end_linear; +} // namespace teachos::x86_64::boot #endif -- cgit v1.2.3 From bb685cca3a537f0df4205050a9c52b411dee95c6 Mon Sep 17 00:00:00 2001 From: Felix Morgner Date: Wed, 23 Jul 2025 14:15:09 +0000 Subject: x86_64: rename _*_linear to _*_physical --- arch/x86_64/include/x86_64/boot/ld.hpp | 12 ++++++------ arch/x86_64/scripts/kernel.ld | 8 ++++---- arch/x86_64/src/memory.cpp | 2 +- 3 files changed, 11 insertions(+), 11 deletions(-) (limited to 'arch/x86_64') diff --git a/arch/x86_64/include/x86_64/boot/ld.hpp b/arch/x86_64/include/x86_64/boot/ld.hpp index 6051dd7..51eb23b 100644 --- a/arch/x86_64/include/x86_64/boot/ld.hpp +++ b/arch/x86_64/include/x86_64/boot/ld.hpp @@ -20,28 +20,28 @@ namespace teachos::x86_64::boot { /** - * @var _start_linear + * @var _start_physical * @brief The first byte of the loaded kernel image. * * @details * This symbols is defined in the kernel linker script and marks the start of the kernel image in physical memory. * To use this symbol for its intended purpose, the address of it shall be taken. * - * @see _end_linear + * @see _end_physical */ - extern "C" std::byte _start_linear; + extern "C" std::byte _start_physical; /** - * @var _end_linear + * @var _end_physical * @brief The first byte after the loaded kernel image. * * @details * This symbols is defined in the kernel linker script and marks the end of the kernel image in physical memory. * To use this symbol for its intended purpose, the address of it shall be taken. * - * @see _start_linear + * @see _start_physical */ - extern "C" std::byte _end_linear; + extern "C" std::byte _end_physical; } // namespace teachos::x86_64::boot #endif diff --git a/arch/x86_64/scripts/kernel.ld b/arch/x86_64/scripts/kernel.ld index 3d9a7ae..8af242f 100644 --- a/arch/x86_64/scripts/kernel.ld +++ b/arch/x86_64/scripts/kernel.ld @@ -3,7 +3,7 @@ ENTRY(_start) /***************************************************************************** * Virtual and linear start addresses of the TeachOS kernel *****************************************************************************/ -TEACHOS_LOW = 1M; +TEACHOS_PMA = 1M; PHDRS { boot_rodata PT_LOAD FLAGS(4); @@ -22,14 +22,14 @@ SECTIONS * 32-Bit mode, so we want to live down low, but we need to leave the 1MiB * hole open since some BIOS functionality resides below it. ***************************************************************************/ - . = TEACHOS_LOW; + . = TEACHOS_PMA; /*************************************************************************** * We want to be able to be able to access all memory (linear and virtual) * during bootstrapping and operation. To achieve this, we define some * symbols at the beginning. ***************************************************************************/ - _start_linear = .; + _start_physical = .; _start_virtual = .; /*************************************************************************** @@ -144,7 +144,7 @@ SECTIONS * symbols to mark the end of our loaded image. ***************************************************************************/ _end_virtual = ADDR(.bss) + SIZEOF(.bss); - _end_linear = _end_virtual; + _end_physical = _end_virtual; /DISCARD/ : { *(.comment) } } diff --git a/arch/x86_64/src/memory.cpp b/arch/x86_64/src/memory.cpp index 105d69b..b6901a5 100644 --- a/arch/x86_64/src/memory.cpp +++ b/arch/x86_64/src/memory.cpp @@ -25,7 +25,7 @@ namespace teachos::arch::memory auto const & mbi = multiboot_information_pointer.get(); auto map = mbi->memory_map(); - return {std::make_pair(physical_address{&_start_linear}, physical_address{&_end_linear}), + return {std::make_pair(physical_address{&_start_physical}, physical_address{&_end_physical}), std::make_pair(physical_address{std::bit_cast(&mbi)}, physical_address{std::bit_cast(&mbi) + mbi->size_bytes()}), map}; -- cgit v1.2.3 From 2ebf8d525e6a030efc8ca23bcbdf92c2d0cb8985 Mon Sep 17 00:00:00 2001 From: Felix Morgner Date: Thu, 24 Jul 2025 10:17:53 +0000 Subject: x86_64: implement high/low split --- arch/x86_64/scripts/kernel.ld | 35 ++++++++------- arch/x86_64/src/boot/boot32.S | 102 +++++++++++++++++++++++++++--------------- 2 files changed, 86 insertions(+), 51 deletions(-) (limited to 'arch/x86_64') diff --git a/arch/x86_64/scripts/kernel.ld b/arch/x86_64/scripts/kernel.ld index 8af242f..6ba8b80 100644 --- a/arch/x86_64/scripts/kernel.ld +++ b/arch/x86_64/scripts/kernel.ld @@ -4,6 +4,7 @@ ENTRY(_start) * Virtual and linear start addresses of the TeachOS kernel *****************************************************************************/ TEACHOS_PMA = 1M; +TEACHOS_VMA = 0xFFFFFFFF80000000; PHDRS { boot_rodata PT_LOAD FLAGS(4); @@ -25,12 +26,13 @@ SECTIONS . = TEACHOS_PMA; /*************************************************************************** - * We want to be able to be able to access all memory (linear and virtual) + * We want to be able to be able to access all memory (physical and virtual) * during bootstrapping and operation. To achieve this, we define some * symbols at the beginning. ***************************************************************************/ _start_physical = .; - _start_virtual = .; + _start_virtual = . + TEACHOS_VMA; + /*************************************************************************** * The bootstrapping infratructure goes first. We first place the read-only @@ -63,7 +65,10 @@ SECTIONS * Now it is time to load the 64-bit kernel code. We * make sure to align the loaded data onto a page boundary. ***************************************************************************/ - .init ALIGN(4K) : AT(ADDR (.init)) + . = ALIGN(4K); + . += TEACHOS_VMA; + + .init ALIGN(4K) : AT(ADDR(.init) - TEACHOS_VMA) { /* * Make sure that the crt code is wrapped around the compiler generated @@ -74,7 +79,7 @@ SECTIONS KEEP(*crtn.s.o*(.init)) } :text - .fini ALIGN(4K) : AT(ADDR (.fini)) + .fini ALIGN(4K) : AT(ADDR (.fini) - TEACHOS_VMA) { /* * Make sure that the crt code is wrapped around the compiler generated @@ -85,29 +90,29 @@ SECTIONS KEEP(*crtn.s.o*(.fini)) } - .stl_text ALIGN(4K) : AT(ADDR (.stl_text)) + .stl_text ALIGN(4K) : AT(ADDR (.stl_text) - TEACHOS_VMA) { *(.stl_text .stl_text*) KEEP(*libstdc++.a:*(.text .text.*)) } - .text ALIGN(4K) : AT(ADDR (.text)) + .text ALIGN(4K) : AT(ADDR (.text) - TEACHOS_VMA) { *(.text .text.*) } - .user_text ALIGN(4K) : AT(ADDR (.user_text)) + .user_text ALIGN(4K) : AT(ADDR (.user_text) - TEACHOS_VMA) { *(.user_text .user_text.*) } - .rodata ALIGN(4K) : AT (ADDR (.rodata)) + .rodata ALIGN(4K) : AT (ADDR (.rodata) - TEACHOS_VMA) { *(.rodata) *(.rodata.*) } :rodata - .ctors ALIGN(4K) : AT (ADDR (.ctors)) + .ctors ALIGN(4K) : AT (ADDR (.ctors) - TEACHOS_VMA) { KEEP(*crtbegin.o(.ctors)) KEEP(*(EXCLUDE_FILE (*crtend.o) .ctors)) @@ -115,7 +120,7 @@ SECTIONS KEEP(*crtend.o(.ctors)) } :data - .dtors ALIGN(4K) : AT (ADDR (.dtors)) + .dtors ALIGN(4K) : AT (ADDR (.dtors) - TEACHOS_VMA) { KEEP(*crtbegin.o(.dtors)) KEEP(*(EXCLUDE_FILE (*crtend.o) .dtors)) @@ -123,18 +128,18 @@ SECTIONS KEEP(*crtend.o(.dtors)) } - .bss ALIGN(4K) : AT (ADDR (.bss)) + .bss ALIGN(4K) : AT (ADDR (.bss) - TEACHOS_VMA) { *(COMMON) *(.bss*) } - .data ALIGN(4K) : AT (ADDR (.data)) + .data ALIGN(4K) : AT (ADDR (.data) - TEACHOS_VMA) { *(.data*) } - .user_data ALIGN(4K) : AT (ADDR (.user_data)) + .user_data ALIGN(4K) : AT (ADDR (.user_data) - TEACHOS_VMA) { *(.user_data .user_data.*) } @@ -143,8 +148,8 @@ SECTIONS * In accordance with the symbol definitions at the start, we generate some * symbols to mark the end of our loaded image. ***************************************************************************/ - _end_virtual = ADDR(.bss) + SIZEOF(.bss); - _end_physical = _end_virtual; + _end_virtual = .; + _end_physical = _end_virtual - TEACHOS_VMA; /DISCARD/ : { *(.comment) } } diff --git a/arch/x86_64/src/boot/boot32.S b/arch/x86_64/src/boot/boot32.S index 7e6c2ae..3039f38 100644 --- a/arch/x86_64/src/boot/boot32.S +++ b/arch/x86_64/src/boot/boot32.S @@ -13,9 +13,13 @@ multiboot_information_pointer: .skip 8 .align 4096 -page_map_level_4: .skip 512 * 8 -page_map_level_3: .skip 512 * 8 -page_map_level_2: .skip 512 * 8 +page_maps_start: +page_map_level_4: .skip 512 * 8 +page_map_level_3_high: .skip 512 * 8 +page_map_level_3_low: .skip 512 * 8 +page_map_level_2: .skip 512 * 8 +page_maps_end = . +page_maps_size = page_maps_end - page_maps_start /** * @brief Storage for the bootstrap stack. @@ -40,9 +44,9 @@ global_descriptor_table: global_descriptor_table_null = . - global_descriptor_table .quad 0 global_descriptor_table_code = . - global_descriptor_table -.quad GDT_ACCESSED | GDT_READ_WRITE | GDT_EXECUTABLE | GDT_DESCRIPTOR_TYPE | GDT_PRESENT | GDT_LONG_MODE +.quad GDT_READ_WRITE | GDT_EXECUTABLE | GDT_DESCRIPTOR_TYPE | GDT_PRESENT | GDT_LONG_MODE | (1 << 55) global_descriptor_table_data = . - global_descriptor_table -.quad GDT_ACCESSED | GDT_READ_WRITE | GDT_DESCRIPTOR_TYPE | GDT_PRESENT +.quad GDT_READ_WRITE | GDT_DESCRIPTOR_TYPE | GDT_PRESENT | (1 << 54) | (1 << 55) global_descriptor_table_end: message_prefix_panic: .string "Panic: " @@ -125,7 +129,10 @@ _start: call _assert_cpuid_instruction_is_supported call _assert_cpu_supports_long_mode + push $HUGE_PAGES_TO_MAP call _prepare_page_maps + add $4, %esp + call _enable_paging call _enable_sse call _reload_gdt @@ -302,51 +309,74 @@ _assert_cpu_supports_long_mode: /** * @brief Prepare a recursive page map hierarchy * - * We map all physical memory we were loaded in plus one additional page. The - * mapping is done in terms of huge pages (2 MiB per page) to save on required - * page map entries. - * + * @param ebp+8 The number of huge pages to map * @return void */ _prepare_page_maps: pie_function_start - push %ebx - /* Map the P4 table recursively */ - lea (page_map_level_4 - 0b)(%esi), %eax - mov %eax, %ebx - or $0b11, %ebx - mov %ebx, (511 * 8)(%eax) - - /* Add an entry to the PML4, pointing to the PML3 */ - lea (page_map_level_3 - 0b)(%esi), %ebx - or $0b11, %ebx - mov %ebx, (((0x0000000000100000 >> 39) & 0x1ff) * 8)(%eax) - - /* Add an entry to the PML3, pointing to the PML2 */ - lea (page_map_level_3 - 0b)(%esi), %eax - lea (page_map_level_2 - 0b)(%esi), %ebx - or $0b11, %ebx - mov %ebx, (((0x0000000000100000 >> 30) & 0x1ff) * 8)(%eax) - - /* Add entries for huge pages to the PML2 */ push %edi - lea (page_map_level_2 - 0b)(%esi), %ebx - mov $HUGE_PAGES_TO_MAP, %edi - xor %ecx, %ecx + + call _clear_page_map_memory + + lea (page_map_level_4 - 0b)(%esi), %edi + mov %edi, %eax + or $0b11, %eax + mov %eax, (510 * 8)(%edi) + + lea (page_map_level_3_low - 0b)(%esi), %eax + or $0b11, %eax + mov %eax, (%edi) + + lea (page_map_level_3_high - 0b)(%esi), %eax + or $0b11, %eax + mov %eax, (511 * 8)(%edi) + + lea (page_map_level_3_low - 0b)(%esi), %edi + lea (page_map_level_2 - 0b)(%esi), %eax + or $0b11, %eax + mov %eax, (%edi) + + lea (page_map_level_3_high - 0b)(%esi), %edi + lea (page_map_level_2 - 0b)(%esi), %eax + or $0b11, %eax + mov %eax, (510 * 8)(%edi) + + lea (page_map_level_2 - 0b)(%esi), %edi + mov 8(%ebp), %ecx 1: + dec %ecx mov $(1 << 21), %eax mul %ecx or $0b10000011, %eax - mov %eax, (%ebx, %ecx, 8) + mov %eax, (%edi, %ecx, 8) - inc %ecx - cmp %edi, %ecx - jne 1b + test %ecx, %ecx + jnz 1b pop %edi - pop %ebx + + pie_function_end + +/** + * @brief Clear all page map memory by filling it with 0s. + * + * @return void + */ +_clear_page_map_memory: + pie_function_start + + push %edi + + xor %eax, %eax + mov $page_maps_size, %ecx + shr $2, %ecx + lea (page_maps_start - 0b)(%esi), %edi + rep stosl + + pop %edi + pie_function_end /** -- cgit v1.2.3 From f62b05c93c6c539d899d2656c0638d404a036f1a Mon Sep 17 00:00:00 2001 From: Felix Morgner Date: Thu, 24 Jul 2025 12:28:23 +0000 Subject: x86_64: implement robust C++ global initialization Implement a comprehensive mechanism to ensure correct C++ runtime initialization before the kernel main function is called. This replaces the previous, incomplete reliance on an `_init` function. The new design robustly handles both legacy `.ctors` and modern `.init_array` initialization schemes used by the GNU toolchain. A single C++ function, `invoke_global_constructors`, now iterates through both arrays of function pointers to ensure all types of global initializers are executed. --- arch/x86_64/CMakeLists.txt | 3 +-- arch/x86_64/scripts/kernel.ld | 40 +++++++---------------------- arch/x86_64/src/boot/crti.s | 13 ---------- arch/x86_64/src/boot/crtn.s | 9 ------- arch/x86_64/src/boot/entry64.s | 4 +-- arch/x86_64/src/boot/initialize_runtime.cpp | 24 +++++++++++++++++ 6 files changed, 36 insertions(+), 57 deletions(-) delete mode 100644 arch/x86_64/src/boot/crti.s delete mode 100644 arch/x86_64/src/boot/crtn.s create mode 100644 arch/x86_64/src/boot/initialize_runtime.cpp (limited to 'arch/x86_64') diff --git a/arch/x86_64/CMakeLists.txt b/arch/x86_64/CMakeLists.txt index 6bb9e53..58daa3b 100644 --- a/arch/x86_64/CMakeLists.txt +++ b/arch/x86_64/CMakeLists.txt @@ -35,9 +35,8 @@ target_sources("arch-x86_64" PRIVATE target_sources("arch-x86_64" PRIVATE "src/boot/boot32.S" - "src/boot/crti.s" - "src/boot/crtn.s" "src/boot/entry64.s" + "src/boot/initialize_runtime.cpp" "src/boot/multiboot.s" ) diff --git a/arch/x86_64/scripts/kernel.ld b/arch/x86_64/scripts/kernel.ld index 6ba8b80..064b8d7 100644 --- a/arch/x86_64/scripts/kernel.ld +++ b/arch/x86_64/scripts/kernel.ld @@ -68,33 +68,11 @@ SECTIONS . = ALIGN(4K); . += TEACHOS_VMA; - .init ALIGN(4K) : AT(ADDR(.init) - TEACHOS_VMA) - { - /* - * Make sure that the crt code is wrapped around the compiler generated - * initialization code. - */ - KEEP(*crti.s.o*(.init)) - KEEP(*(EXCLUDE_FILE (*crti.s.o* *crtn.s.o*) .init)) - KEEP(*crtn.s.o*(.init)) - } :text - - .fini ALIGN(4K) : AT(ADDR (.fini) - TEACHOS_VMA) - { - /* - * Make sure that the crt code is wrapped around the compiler generated - * finalizer code. - */ - KEEP(*crti.s.o*(.fini)) - KEEP(*(EXCLUDE_FILE (*crti.s.o* *crtn.s.o*) .fini)) - KEEP(*crtn.s.o*(.fini)) - } - .stl_text ALIGN(4K) : AT(ADDR (.stl_text) - TEACHOS_VMA) { *(.stl_text .stl_text*) KEEP(*libstdc++.a:*(.text .text.*)) - } + } :text .text ALIGN(4K) : AT(ADDR (.text) - TEACHOS_VMA) { @@ -114,18 +92,18 @@ SECTIONS .ctors ALIGN(4K) : AT (ADDR (.ctors) - TEACHOS_VMA) { - KEEP(*crtbegin.o(.ctors)) - KEEP(*(EXCLUDE_FILE (*crtend.o) .ctors)) + __ctors_start = .; KEEP(*(SORT(.ctors.*))) - KEEP(*crtend.o(.ctors)) + KEEP(*(.ctors)) + __ctors_end = .; } :data - .dtors ALIGN(4K) : AT (ADDR (.dtors) - TEACHOS_VMA) + .init_array ALIGN(4K) : AT (ADDR (.init_array) - TEACHOS_VMA) { - KEEP(*crtbegin.o(.dtors)) - KEEP(*(EXCLUDE_FILE (*crtend.o) .dtors)) - KEEP(*(SORT(.dtors.*))) - KEEP(*crtend.o(.dtors)) + __init_array_start = .; + KEEP(*(SORT_BY_INIT_PRIORITY(.init_array.*))) + KEEP(*(.init_array)) + __init_array_end = .; } .bss ALIGN(4K) : AT (ADDR (.bss) - TEACHOS_VMA) diff --git a/arch/x86_64/src/boot/crti.s b/arch/x86_64/src/boot/crti.s deleted file mode 100644 index 26878fe..0000000 --- a/arch/x86_64/src/boot/crti.s +++ /dev/null @@ -1,13 +0,0 @@ -.code64 - -.section .init -.global _init -_init: - push %rbp - movq %rsp, %rbp - -.section .fini -.global _fini -_fini: - push %rbp - movq %rsp, %rbp diff --git a/arch/x86_64/src/boot/crtn.s b/arch/x86_64/src/boot/crtn.s deleted file mode 100644 index 06fb7ce..0000000 --- a/arch/x86_64/src/boot/crtn.s +++ /dev/null @@ -1,9 +0,0 @@ -.code64 - -.section .init - popq %rbp - ret - -.section .fini - popq %rbp - ret diff --git a/arch/x86_64/src/boot/entry64.s b/arch/x86_64/src/boot/entry64.s index f575c50..110ced8 100644 --- a/arch/x86_64/src/boot/entry64.s +++ b/arch/x86_64/src/boot/entry64.s @@ -12,8 +12,8 @@ _entry64: xor %rax, %rax - call _init - + call invoke_global_constructors + call main 1: diff --git a/arch/x86_64/src/boot/initialize_runtime.cpp b/arch/x86_64/src/boot/initialize_runtime.cpp new file mode 100644 index 0000000..9a3df0e --- /dev/null +++ b/arch/x86_64/src/boot/initialize_runtime.cpp @@ -0,0 +1,24 @@ +#include +#include +#include + +extern "C" +{ + using global_initializer = auto (*)() -> void; + + extern global_initializer __ctors_start; + extern global_initializer __ctors_end; + extern global_initializer __init_array_start; + extern global_initializer __init_array_end; + + auto invoke_global_constructors() -> void + { + auto constructors = std::span{&__ctors_start, &__ctors_end}; + auto initializers = std::span{&__init_array_start, &__init_array_end}; + + auto apply_invoke = [](auto invokable) { return std::invoke(invokable); }; + + std::ranges::for_each(constructors, apply_invoke); + std::ranges::for_each(initializers, apply_invoke); + } +} -- cgit v1.2.3 From ef907825e861b63726952bb34b425a98f34ed412 Mon Sep 17 00:00:00 2001 From: Felix Morgner Date: Thu, 24 Jul 2025 13:52:42 +0000 Subject: x86_64: provide a clean slate on entry to long mode --- arch/x86_64/src/boot/boot32.S | 4 ++++ arch/x86_64/src/boot/entry64.s | 14 ++++++++++++-- 2 files changed, 16 insertions(+), 2 deletions(-) (limited to 'arch/x86_64') diff --git a/arch/x86_64/src/boot/boot32.S b/arch/x86_64/src/boot/boot32.S index 3039f38..27eed4d 100644 --- a/arch/x86_64/src/boot/boot32.S +++ b/arch/x86_64/src/boot/boot32.S @@ -27,8 +27,12 @@ page_maps_size = page_maps_end - page_maps_start .section .boot_stack, "aw", @nobits .align 16 +.global stack_size +.global stack_bottom + stack_bottom: .skip 1 << 20 stack_top: +stack_size = stack_top - stack_bottom /** * @brief Constants for the bootstrapping process. diff --git a/arch/x86_64/src/boot/entry64.s b/arch/x86_64/src/boot/entry64.s index 110ced8..c5df5db 100644 --- a/arch/x86_64/src/boot/entry64.s +++ b/arch/x86_64/src/boot/entry64.s @@ -10,10 +10,20 @@ _entry64: mov %rax, %fs mov %rax, %gs - xor %rax, %rax - call invoke_global_constructors + xor %rax, %rax + mov %rax, %rbp + mov %rax, %rdx + mov %rax, %rsi + + mov $stack_size, %rcx + shr $3, %rcx + lea (stack_bottom), %rdi + rep stosq + + mov %rax, %rdi + call main 1: -- cgit v1.2.3 From 2b8fafa2bddc48ddec047de517115c8e65ee61e8 Mon Sep 17 00:00:00 2001 From: Felix Morgner Date: Thu, 24 Jul 2025 15:05:17 +0000 Subject: build: move linker script dependency to kernel --- arch/x86_64/CMakeLists.txt | 8 -------- 1 file changed, 8 deletions(-) (limited to 'arch/x86_64') diff --git a/arch/x86_64/CMakeLists.txt b/arch/x86_64/CMakeLists.txt index 58daa3b..431520e 100644 --- a/arch/x86_64/CMakeLists.txt +++ b/arch/x86_64/CMakeLists.txt @@ -11,14 +11,6 @@ target_link_libraries("arch-x86_64" PUBLIC "libs::multiboot2" ) -target_link_options("arch-x86_64" PUBLIC - "-T${CMAKE_CURRENT_SOURCE_DIR}/scripts/kernel.ld" -) - -set_target_properties("arch-x86_64" PROPERTIES - LINK_DEPENDS "${CMAKE_CURRENT_SOURCE_DIR}/scripts/kernel.ld" -) - #[============================================================================[ # arch::any Implementation #]============================================================================] -- cgit v1.2.3 From 4edbe94ce1266c9acc6a695fedf1d2edd4ce11cd Mon Sep 17 00:00:00 2001 From: Felix Morgner Date: Thu, 24 Jul 2025 15:31:31 +0000 Subject: build: factor out kernel API --- arch/x86_64/CMakeLists.txt | 3 +-- arch/x86_64/src/io.cpp | 44 ++++++++++++++++++++++++++------------------ arch/x86_64/src/memory.cpp | 12 ++++++------ arch/x86_64/src/system.cpp | 6 +++--- 4 files changed, 36 insertions(+), 29 deletions(-) (limited to 'arch/x86_64') diff --git a/arch/x86_64/CMakeLists.txt b/arch/x86_64/CMakeLists.txt index 431520e..4cb20b6 100644 --- a/arch/x86_64/CMakeLists.txt +++ b/arch/x86_64/CMakeLists.txt @@ -6,8 +6,7 @@ target_include_directories("arch-x86_64" PUBLIC ) target_link_libraries("arch-x86_64" PUBLIC - "arch::any" - "os::kern" + "api::kapi" "libs::multiboot2" ) diff --git a/arch/x86_64/src/io.cpp b/arch/x86_64/src/io.cpp index 8e9e411..eab6473 100644 --- a/arch/x86_64/src/io.cpp +++ b/arch/x86_64/src/io.cpp @@ -1,28 +1,36 @@ -#include "kern/print.hpp" +#include "kapi/io.hpp" + #include "x86_64/vga/text.hpp" -namespace teachos::arch::io +namespace teachos::io { auto init() -> void { + x86_64::vga::text::clear(); x86_64::vga::text::cursor(false); + } + + auto print(std::string_view text) -> void + { + x86_64::vga::text::write(text, x86_64::vga::text::common_attributes::green_on_black); + } + + auto println(std::string_view text) -> void + { + x86_64::vga::text::write(text, x86_64::vga::text::common_attributes::green_on_black); + x86_64::vga::text::newline(); + } + + auto print_error(std::string_view text) -> void + { + x86_64::vga::text::write(text, x86_64::vga::text::common_attributes::red_on_black); + } - teachos::set_print_handler( - [](auto text) { return x86_64::vga::text::write(text, x86_64::vga::text::common_attributes::green_on_black); }); - teachos::set_println_handler([](auto text) { - x86_64::vga::text::write(text, x86_64::vga::text::common_attributes::green_on_black); - x86_64::vga::text::newline(); - }); - - teachos::set_print_error_handler( - [](auto text) { return x86_64::vga::text::write(text, x86_64::vga::text::common_attributes::red_on_black); }); - teachos::set_println_error_handler([](auto text) { - x86_64::vga::text::write(text, x86_64::vga::text::common_attributes::red_on_black); - x86_64::vga::text::newline(); - }); - - teachos::println("[x86-64] Basic VGA text output initialized."); + auto println_error(std::string_view text) -> void + { + x86_64::vga::text::write(text, x86_64::vga::text::common_attributes::red_on_black); + x86_64::vga::text::newline(); } -} // namespace teachos::arch::io +} // namespace teachos::io diff --git a/arch/x86_64/src/memory.cpp b/arch/x86_64/src/memory.cpp index b6901a5..d1c1f03 100644 --- a/arch/x86_64/src/memory.cpp +++ b/arch/x86_64/src/memory.cpp @@ -1,6 +1,6 @@ -#include "arch/memory.hpp" +#include "kapi/memory.hpp" -#include "kern/error.hpp" +#include "kapi/system.hpp" #include "x86_64/boot/boot.hpp" #include "x86_64/boot/ld.hpp" @@ -11,7 +11,7 @@ #include -namespace teachos::arch::memory +namespace teachos::memory { using namespace x86_64::memory; using namespace x86_64::boot; @@ -36,13 +36,13 @@ namespace teachos::arch::memory { if (is_initialized.test_and_set()) { - teachos::panic("[x86_64] Memory management has already been initialized."); + system::panic("[x86_64] Memory management has already been initialized."); } auto memory_map = multiboot_information_pointer->maybe_memory_map(); if (!memory_map) { - teachos::panic("[x86_64] No memory map available."); + system::panic("[x86_64] No memory map available."); } auto mem_info = create_memory_information(); @@ -63,4 +63,4 @@ namespace teachos::arch::memory // video::vga::text::newline(); } -} // namespace teachos::arch::memory +} // namespace teachos::memory diff --git a/arch/x86_64/src/system.cpp b/arch/x86_64/src/system.cpp index 60ebf0e..2d4c3fe 100644 --- a/arch/x86_64/src/system.cpp +++ b/arch/x86_64/src/system.cpp @@ -1,6 +1,6 @@ -#include "arch/system.hpp" +#include "kapi/system.hpp" -namespace teachos::arch::system +namespace teachos::system { auto halt() -> void @@ -9,4 +9,4 @@ namespace teachos::arch::system __builtin_unreachable(); } -} // namespace teachos::arch::system +} // namespace teachos::system -- cgit v1.2.3 From c6629ba11c17601695b0542d7d1d1bf5dc036d84 Mon Sep 17 00:00:00 2001 From: Felix Morgner Date: Thu, 24 Jul 2025 15:37:36 +0000 Subject: build: clean up configuration --- arch/x86_64/CMakeLists.txt | 192 +++----------------------------------------- arch/x86_64/kapi/io.cpp | 36 +++++++++ arch/x86_64/kapi/memory.cpp | 66 +++++++++++++++ arch/x86_64/kapi/system.cpp | 12 +++ arch/x86_64/src/io.cpp | 36 --------- arch/x86_64/src/memory.cpp | 66 --------------- arch/x86_64/src/system.cpp | 12 --- 7 files changed, 125 insertions(+), 295 deletions(-) create mode 100644 arch/x86_64/kapi/io.cpp create mode 100644 arch/x86_64/kapi/memory.cpp create mode 100644 arch/x86_64/kapi/system.cpp delete mode 100644 arch/x86_64/src/io.cpp delete mode 100644 arch/x86_64/src/memory.cpp delete mode 100644 arch/x86_64/src/system.cpp (limited to 'arch/x86_64') diff --git a/arch/x86_64/CMakeLists.txt b/arch/x86_64/CMakeLists.txt index 4cb20b6..1cb2f9a 100644 --- a/arch/x86_64/CMakeLists.txt +++ b/arch/x86_64/CMakeLists.txt @@ -10,196 +10,26 @@ target_link_libraries("arch-x86_64" PUBLIC "libs::multiboot2" ) -#[============================================================================[ -# arch::any Implementation -#]============================================================================] - target_sources("arch-x86_64" PRIVATE - "src/io.cpp" - "src/memory.cpp" - "src/system.cpp" -) - -#[============================================================================[ -# Bootstrap Code -#]============================================================================] + # api::kapi implementation + "kapi/io.cpp" + "kapi/memory.cpp" + "kapi/system.cpp" -target_sources("arch-x86_64" PRIVATE + # Low-level bootstrap "src/boot/boot32.S" "src/boot/entry64.s" "src/boot/initialize_runtime.cpp" "src/boot/multiboot.s" -) -#[============================================================================[ -# Memory Code -#]============================================================================] - -target_sources("arch-x86_64" PRIVATE + # Memory management "src/memory/region_allocator.cpp" -) - -#[============================================================================[ -# VGA Code -#]============================================================================] -target_sources("arch-x86_64" PRIVATE + # VGA text mode "src/vga/text.cpp" ) -# #[============================================================================[ -# # The Kernel Library -# #]============================================================================] - -# set(TEACHOS_KERNEL_LINKER_SCRIPT "${CMAKE_CURRENT_SOURCE_DIR}/scripts/kernel.ld") -# mark_as_advanced(TEACHOS_KERNEL_LINKER_SCRIPT) - -# target_sources("_kernel" PRIVATE -# "src/kernel/main.cpp" -# "src/kernel/cpu/control_register.cpp" -# "src/kernel/cpu/gdtr.cpp" -# "src/kernel/cpu/idtr.cpp" -# "src/kernel/cpu/if.cpp" -# "src/kernel/cpu/call.cpp" -# "src/kernel/cpu/msr.cpp" -# "src/kernel/cpu/segment_register.cpp" -# "src/kernel/cpu/tlb.cpp" -# "src/kernel/cpu/tr.cpp" -# ) - -# target_link_options("_kernel" PRIVATE -# "-T${TEACHOS_KERNEL_LINKER_SCRIPT}" -# ) - -# set_target_properties("_kernel" PROPERTIES -# LINK_DEPENDS "${TEACHOS_KERNEL_LINKER_SCRIPT}" -# ) - -# #[============================================================================[ -# # The Bootstrap Library -# #]============================================================================] - -# target_sources("_boot" PRIVATE -# "src/boot/boot.s" -# "src/boot/crti.s" -# "src/boot/crtn.s" -# "src/boot/multiboot.s" -# ) - -# #[============================================================================[ -# # The Video Library -# #]============================================================================] - -# target_sources("_video" PRIVATE -# "src/video/vga/text.cpp" -# ) - -# #[============================================================================[ -# # The Memory Library -# #]============================================================================] - -# target_sources("_memory" PRIVATE -# "src/memory/main.cpp" -# "src/memory/multiboot/elf_symbols_section.cpp" -# "src/memory/multiboot/reader.cpp" -# "src/memory/allocator/area_frame_allocator.cpp" -# "src/memory/allocator/tiny_frame_allocator.cpp" -# "src/memory/allocator/physical_frame.cpp" -# "src/memory/paging/page_entry.cpp" -# "src/memory/paging/page_table.cpp" -# "src/memory/paging/temporary_page.cpp" -# "src/memory/paging/virtual_page.cpp" -# "src/memory/paging/active_page_table.cpp" -# "src/memory/paging/inactive_page_table.cpp" -# "src/memory/heap/bump_allocator.cpp" -# "src/memory/heap/user_heap_allocator.cpp" -# "src/memory/heap/memory_block.cpp" -# "src/memory/heap/linked_list_allocator.cpp" -# "src/memory/heap/global_heap_allocator.cpp" -# ) - -# target_link_libraries("_memory" PUBLIC -# "libs::kstd" -# "libs::multiboot2" -# ) - -# #[============================================================================[ -# # The Exception handling Library -# #]============================================================================] - -# target_sources("_exception" PRIVATE -# "src/exception_handling/assert.cpp" -# "src/exception_handling/abort.cpp" -# "src/exception_handling/panic.cpp" -# "src/exception_handling/pure_virtual.cpp" -# ) - -# #[============================================================================[ -# # The Context switching Library -# #]============================================================================] - -# target_sources("_context" PRIVATE -# "src/context_switching/segment_descriptor_table/access_byte.cpp" -# "src/context_switching/segment_descriptor_table/gdt_flags.cpp" -# "src/context_switching/segment_descriptor_table/global_descriptor_table_pointer.cpp" -# "src/context_switching/segment_descriptor_table/global_descriptor_table.cpp" -# "src/context_switching/segment_descriptor_table/segment_descriptor_base.cpp" -# "src/context_switching/segment_descriptor_table/segment_descriptor_extension.cpp" -# "src/context_switching/main.cpp" -# "src/context_switching/syscall/main.cpp" -# "src/context_switching/syscall/syscall_enable.cpp" -# "src/context_switching/syscall/syscall_handler.cpp" -# "src/context_switching/interrupt_descriptor_table/gate_descriptor.cpp" -# "src/context_switching/interrupt_descriptor_table/idt_flags.cpp" -# "src/context_switching/interrupt_descriptor_table/interrupt_descriptor_table_pointer.cpp" -# "src/context_switching/interrupt_descriptor_table/interrupt_descriptor_table.cpp" -# "src/context_switching/interrupt_descriptor_table/ist_offset.cpp" -# "src/context_switching/interrupt_descriptor_table/segment_selector.cpp" -# ) - -# target_link_libraries("_context" PUBLIC -# "libs::kstd" -# ) - -# #[============================================================================[ -# # The Interrupt Handlers -# #]============================================================================] - -# target_sources("_interrupt_handling" PRIVATE -# "src/interrupt_handling/generic_interrupt_handler.cpp" -# ) - -# #[============================================================================[ -# # The User code -# #]============================================================================] - -# target_sources("_context" PRIVATE -# "src/user/main.cpp" -# ) - -# #[============================================================================[ -# # The Bootable ISO Image -# #]============================================================================] - -# find_package("grub-mkrescue") - -# if(grub-mkrescue_FOUND) -# file(GENERATE -# OUTPUT "isofs/boot/grub/grub.cfg" -# INPUT "support/grub.cfg.in" -# ) - -# add_custom_target("bootable-iso" -# COMMAND "${GRUB_MKRESCUE_EXE}" -# "-o" -# "${PROJECT_BINARY_DIR}/teachos-$.iso" -# "${CMAKE_CURRENT_BINARY_DIR}/isofs" -# "$" -# "2>/dev/null" -# DEPENDS -# "$" -# "isofs/boot/grub/grub.cfg" -# BYPRODUCTS "${PROJECT_BINARY_DIR}/teachos-$.iso" -# COMMENT "Creating bootable ISO image" -# ) -# endif() +set(KERNEL_LINKER_SCRIPT + "${CMAKE_CURRENT_SOURCE_DIR}/scripts/kernel.ld" + PARENT_SCOPE +) \ No newline at end of file diff --git a/arch/x86_64/kapi/io.cpp b/arch/x86_64/kapi/io.cpp new file mode 100644 index 0000000..eab6473 --- /dev/null +++ b/arch/x86_64/kapi/io.cpp @@ -0,0 +1,36 @@ +#include "kapi/io.hpp" + +#include "x86_64/vga/text.hpp" + +namespace teachos::io +{ + + auto init() -> void + { + x86_64::vga::text::clear(); + x86_64::vga::text::cursor(false); + } + + auto print(std::string_view text) -> void + { + x86_64::vga::text::write(text, x86_64::vga::text::common_attributes::green_on_black); + } + + auto println(std::string_view text) -> void + { + x86_64::vga::text::write(text, x86_64::vga::text::common_attributes::green_on_black); + x86_64::vga::text::newline(); + } + + auto print_error(std::string_view text) -> void + { + x86_64::vga::text::write(text, x86_64::vga::text::common_attributes::red_on_black); + } + + auto println_error(std::string_view text) -> void + { + x86_64::vga::text::write(text, x86_64::vga::text::common_attributes::red_on_black); + x86_64::vga::text::newline(); + } + +} // namespace teachos::io diff --git a/arch/x86_64/kapi/memory.cpp b/arch/x86_64/kapi/memory.cpp new file mode 100644 index 0000000..d1c1f03 --- /dev/null +++ b/arch/x86_64/kapi/memory.cpp @@ -0,0 +1,66 @@ +#include "kapi/memory.hpp" + +#include "kapi/system.hpp" + +#include "x86_64/boot/boot.hpp" +#include "x86_64/boot/ld.hpp" +#include "x86_64/memory/address.hpp" +#include "x86_64/memory/region_allocator.hpp" + +#include + +#include + +namespace teachos::memory +{ + using namespace x86_64::memory; + using namespace x86_64::boot; + + namespace + { + auto constinit is_initialized = std::atomic_flag{}; + + auto create_memory_information() -> region_allocator::memory_information + { + auto const & mbi = multiboot_information_pointer.get(); + auto map = mbi->memory_map(); + + return {std::make_pair(physical_address{&_start_physical}, physical_address{&_end_physical}), + std::make_pair(physical_address{std::bit_cast(&mbi)}, + physical_address{std::bit_cast(&mbi) + mbi->size_bytes()}), + map}; + }; + } // namespace + + auto init() -> void + { + if (is_initialized.test_and_set()) + { + system::panic("[x86_64] Memory management has already been initialized."); + } + + auto memory_map = multiboot_information_pointer->maybe_memory_map(); + if (!memory_map) + { + system::panic("[x86_64] No memory map available."); + } + + auto mem_info = create_memory_information(); + auto allocator = region_allocator{mem_info}; + + static_cast(allocator); + + // 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::memory diff --git a/arch/x86_64/kapi/system.cpp b/arch/x86_64/kapi/system.cpp new file mode 100644 index 0000000..2d4c3fe --- /dev/null +++ b/arch/x86_64/kapi/system.cpp @@ -0,0 +1,12 @@ +#include "kapi/system.hpp" + +namespace teachos::system +{ + + auto halt() -> void + { + asm volatile("1: hlt\njmp 1b"); + __builtin_unreachable(); + } + +} // namespace teachos::system diff --git a/arch/x86_64/src/io.cpp b/arch/x86_64/src/io.cpp deleted file mode 100644 index eab6473..0000000 --- a/arch/x86_64/src/io.cpp +++ /dev/null @@ -1,36 +0,0 @@ -#include "kapi/io.hpp" - -#include "x86_64/vga/text.hpp" - -namespace teachos::io -{ - - auto init() -> void - { - x86_64::vga::text::clear(); - x86_64::vga::text::cursor(false); - } - - auto print(std::string_view text) -> void - { - x86_64::vga::text::write(text, x86_64::vga::text::common_attributes::green_on_black); - } - - auto println(std::string_view text) -> void - { - x86_64::vga::text::write(text, x86_64::vga::text::common_attributes::green_on_black); - x86_64::vga::text::newline(); - } - - auto print_error(std::string_view text) -> void - { - x86_64::vga::text::write(text, x86_64::vga::text::common_attributes::red_on_black); - } - - auto println_error(std::string_view text) -> void - { - x86_64::vga::text::write(text, x86_64::vga::text::common_attributes::red_on_black); - x86_64::vga::text::newline(); - } - -} // namespace teachos::io diff --git a/arch/x86_64/src/memory.cpp b/arch/x86_64/src/memory.cpp deleted file mode 100644 index d1c1f03..0000000 --- a/arch/x86_64/src/memory.cpp +++ /dev/null @@ -1,66 +0,0 @@ -#include "kapi/memory.hpp" - -#include "kapi/system.hpp" - -#include "x86_64/boot/boot.hpp" -#include "x86_64/boot/ld.hpp" -#include "x86_64/memory/address.hpp" -#include "x86_64/memory/region_allocator.hpp" - -#include - -#include - -namespace teachos::memory -{ - using namespace x86_64::memory; - using namespace x86_64::boot; - - namespace - { - auto constinit is_initialized = std::atomic_flag{}; - - auto create_memory_information() -> region_allocator::memory_information - { - auto const & mbi = multiboot_information_pointer.get(); - auto map = mbi->memory_map(); - - return {std::make_pair(physical_address{&_start_physical}, physical_address{&_end_physical}), - std::make_pair(physical_address{std::bit_cast(&mbi)}, - physical_address{std::bit_cast(&mbi) + mbi->size_bytes()}), - map}; - }; - } // namespace - - auto init() -> void - { - if (is_initialized.test_and_set()) - { - system::panic("[x86_64] Memory management has already been initialized."); - } - - auto memory_map = multiboot_information_pointer->maybe_memory_map(); - if (!memory_map) - { - system::panic("[x86_64] No memory map available."); - } - - auto mem_info = create_memory_information(); - auto allocator = region_allocator{mem_info}; - - static_cast(allocator); - - // 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::memory diff --git a/arch/x86_64/src/system.cpp b/arch/x86_64/src/system.cpp deleted file mode 100644 index 2d4c3fe..0000000 --- a/arch/x86_64/src/system.cpp +++ /dev/null @@ -1,12 +0,0 @@ -#include "kapi/system.hpp" - -namespace teachos::system -{ - - auto halt() -> void - { - asm volatile("1: hlt\njmp 1b"); - __builtin_unreachable(); - } - -} // namespace teachos::system -- cgit v1.2.3 From f00a0149b55a0fd57cc731b015c6f425bc720aba Mon Sep 17 00:00:00 2001 From: Felix Morgner Date: Thu, 24 Jul 2025 15:41:58 +0000 Subject: build: rename targets --- arch/x86_64/CMakeLists.txt | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) (limited to 'arch/x86_64') diff --git a/arch/x86_64/CMakeLists.txt b/arch/x86_64/CMakeLists.txt index 1cb2f9a..ddc2d46 100644 --- a/arch/x86_64/CMakeLists.txt +++ b/arch/x86_64/CMakeLists.txt @@ -1,16 +1,16 @@ -add_library("arch-x86_64" OBJECT) -add_library("arch::x86_64" ALIAS "arch-x86_64") +add_library("x86_64" OBJECT) +add_library("os::arch" ALIAS "x86_64") -target_include_directories("arch-x86_64" PUBLIC +target_include_directories("x86_64" PUBLIC "include" ) -target_link_libraries("arch-x86_64" PUBLIC - "api::kapi" +target_link_libraries("x86_64" PUBLIC + "os::kapi" "libs::multiboot2" ) -target_sources("arch-x86_64" PRIVATE +target_sources("x86_64" PRIVATE # api::kapi implementation "kapi/io.cpp" "kapi/memory.cpp" -- cgit v1.2.3 From 3b9bbbb4be529f2365b8bc2e43c1c8e9a65b1a07 Mon Sep 17 00:00:00 2001 From: Felix Morgner Date: Thu, 24 Jul 2025 16:15:31 +0000 Subject: x86_64: clean up vga hierarchy --- arch/x86_64/include/x86_64/device_io/port_io.hpp | 132 +++++++++++++++++++++++ arch/x86_64/include/x86_64/io/port_io.hpp | 132 ----------------------- arch/x86_64/include/x86_64/vga/crtc.hpp | 35 ++++++ arch/x86_64/include/x86_64/vga/io.hpp | 39 ------- arch/x86_64/src/vga/text.cpp | 6 +- 5 files changed, 170 insertions(+), 174 deletions(-) create mode 100644 arch/x86_64/include/x86_64/device_io/port_io.hpp delete mode 100644 arch/x86_64/include/x86_64/io/port_io.hpp create mode 100644 arch/x86_64/include/x86_64/vga/crtc.hpp delete mode 100644 arch/x86_64/include/x86_64/vga/io.hpp (limited to 'arch/x86_64') diff --git a/arch/x86_64/include/x86_64/device_io/port_io.hpp b/arch/x86_64/include/x86_64/device_io/port_io.hpp new file mode 100644 index 0000000..4cf0b65 --- /dev/null +++ b/arch/x86_64/include/x86_64/device_io/port_io.hpp @@ -0,0 +1,132 @@ +#ifndef TEACHOS_X86_64_IO_PORT_IO_HPP +#define TEACHOS_X86_64_IO_PORT_IO_HPP + +#include +#include +#include + +namespace teachos::x86_64::io +{ + /** + * @brief An I/O port of a given size at a given address. + * + * @tparam Address The address (port number) of the I/O port. + * @tparam Size The size (in bytes) of the I/O port. + */ + template + struct port + { + static_assert(Size == 1 || Size == 2 || Size == 4, "A port must be either 1, 2, or 4 bytes in size"); + + /** + * @brief The type of data available for reading and writing through this port. + */ + using io_type = + std::conditional_t>; + + /** + * @brief Write a byte to the I/O port. + * + * @param data The data to write to the I/O port. + */ + auto static write(io_type data) -> void + requires(Size == 1) + { + asm volatile("mov %[port], %%dx\n" + "mov %[data], %%al\n" + "out %%al, %%dx\n" + : + : [port] "i"(Address), [data] "im"(data) + : "dx", "al"); + } + + /** + * @brief Write a word to the I/O port. + * + * @param data The data to write to the I/O port. + */ + auto static write(io_type data) -> void + requires(Size == 2) + { + asm volatile("mov %[port], %%dx\n" + "mov %[data], %%ax\n" + "out %%ax, %%dx\n" + : + : [port] "i"(Address), [data] "im"(data) + : "dx", "ax"); + } + + /** + * @brief Write a double-word to the I/O port. + * + * @param data The data to write to the I/O port. + */ + auto static write(io_type data) -> void + requires(Size == 4) + { + asm volatile("mov %[port], %%dx\n" + "mov %[data], %%eax\n" + "out %%eax, %%dx\n" + : + : [port] "i"(Address), [data] "im"(data) + : "dx", "eax"); + } + + /** + * @brief Read a byte from the I/O port. + * + * @return The data read from the I/O port. + */ + auto static read() -> io_type + requires(Size == 1) + { + auto data = io_type{}; + asm volatile("mov %[port], %%dx\n" + "in %%dx, %%al\n" + "mov %%al, %[data]\n" + : [data] "=m"(data) + : [port] "i"(Address) + : "dx", "al"); + return data; + } + + /** + * @brief Read a word from the I/O port. + * + * @return The data read from the I/O port. + */ + auto static read() -> io_type + requires(Size == 2) + { + auto data = io_type{}; + asm volatile("mov %[port], %%dx\n" + "in %%dx, %%ax\n" + "mov %%ax, %[data]\n" + : [data] "=m"(data) + : [port] "i"(Address) + : "dx", "ax"); + return data; + } + + /** + * @brief Read a double-word from the I/O port. + * + * @return The data read from the I/O port. + */ + auto static read() -> io_type + requires(Size == 4) + { + auto data = io_type{}; + asm volatile("mov %[port], %%dx\n" + "in %%dx, %%eax\n" + "mov %%eax, %[data]\n" + : [data] "=m"(data) + : [port] "i"(Address) + : "dx", "eax"); + return data; + } + }; + +} // namespace teachos::x86_64::io + +#endif \ No newline at end of file diff --git a/arch/x86_64/include/x86_64/io/port_io.hpp b/arch/x86_64/include/x86_64/io/port_io.hpp deleted file mode 100644 index 4cf0b65..0000000 --- a/arch/x86_64/include/x86_64/io/port_io.hpp +++ /dev/null @@ -1,132 +0,0 @@ -#ifndef TEACHOS_X86_64_IO_PORT_IO_HPP -#define TEACHOS_X86_64_IO_PORT_IO_HPP - -#include -#include -#include - -namespace teachos::x86_64::io -{ - /** - * @brief An I/O port of a given size at a given address. - * - * @tparam Address The address (port number) of the I/O port. - * @tparam Size The size (in bytes) of the I/O port. - */ - template - struct port - { - static_assert(Size == 1 || Size == 2 || Size == 4, "A port must be either 1, 2, or 4 bytes in size"); - - /** - * @brief The type of data available for reading and writing through this port. - */ - using io_type = - std::conditional_t>; - - /** - * @brief Write a byte to the I/O port. - * - * @param data The data to write to the I/O port. - */ - auto static write(io_type data) -> void - requires(Size == 1) - { - asm volatile("mov %[port], %%dx\n" - "mov %[data], %%al\n" - "out %%al, %%dx\n" - : - : [port] "i"(Address), [data] "im"(data) - : "dx", "al"); - } - - /** - * @brief Write a word to the I/O port. - * - * @param data The data to write to the I/O port. - */ - auto static write(io_type data) -> void - requires(Size == 2) - { - asm volatile("mov %[port], %%dx\n" - "mov %[data], %%ax\n" - "out %%ax, %%dx\n" - : - : [port] "i"(Address), [data] "im"(data) - : "dx", "ax"); - } - - /** - * @brief Write a double-word to the I/O port. - * - * @param data The data to write to the I/O port. - */ - auto static write(io_type data) -> void - requires(Size == 4) - { - asm volatile("mov %[port], %%dx\n" - "mov %[data], %%eax\n" - "out %%eax, %%dx\n" - : - : [port] "i"(Address), [data] "im"(data) - : "dx", "eax"); - } - - /** - * @brief Read a byte from the I/O port. - * - * @return The data read from the I/O port. - */ - auto static read() -> io_type - requires(Size == 1) - { - auto data = io_type{}; - asm volatile("mov %[port], %%dx\n" - "in %%dx, %%al\n" - "mov %%al, %[data]\n" - : [data] "=m"(data) - : [port] "i"(Address) - : "dx", "al"); - return data; - } - - /** - * @brief Read a word from the I/O port. - * - * @return The data read from the I/O port. - */ - auto static read() -> io_type - requires(Size == 2) - { - auto data = io_type{}; - asm volatile("mov %[port], %%dx\n" - "in %%dx, %%ax\n" - "mov %%ax, %[data]\n" - : [data] "=m"(data) - : [port] "i"(Address) - : "dx", "ax"); - return data; - } - - /** - * @brief Read a double-word from the I/O port. - * - * @return The data read from the I/O port. - */ - auto static read() -> io_type - requires(Size == 4) - { - auto data = io_type{}; - asm volatile("mov %[port], %%dx\n" - "in %%dx, %%eax\n" - "mov %%eax, %[data]\n" - : [data] "=m"(data) - : [port] "i"(Address) - : "dx", "eax"); - return data; - } - }; - -} // namespace teachos::x86_64::io - -#endif \ No newline at end of file diff --git a/arch/x86_64/include/x86_64/vga/crtc.hpp b/arch/x86_64/include/x86_64/vga/crtc.hpp new file mode 100644 index 0000000..4b4eac5 --- /dev/null +++ b/arch/x86_64/include/x86_64/vga/crtc.hpp @@ -0,0 +1,35 @@ +#ifndef TEACHOS_X86_64_VGA_IO_HPP +#define TEACHOS_X86_64_VGA_IO_HPP + +#include "x86_64/device_io/port_io.hpp" + +#include + +namespace teachos::x86_64::vga::crtc +{ + /** + * @brief The address port of the CRT Controller. + */ + using address = x86_64::io::port<0x3d4, 1>; + + /** + * @brief The data port of the CRT Controller. + */ + using data = x86_64::io::port<0x3d5, 1>; + + namespace registers + { + /** + * @brief The address of the Cursor Start register of the CRTC. + */ + [[maybe_unused]] auto constexpr cursor_start = std::byte{0x0a}; + + /** + * @brief The address of the Cursor End register of the CRTC. + */ + [[maybe_unused]] auto constexpr cursor_end = std::byte{0x0b}; + } // namespace registers + +} // namespace teachos::x86_64::vga::crtc + +#endif \ No newline at end of file diff --git a/arch/x86_64/include/x86_64/vga/io.hpp b/arch/x86_64/include/x86_64/vga/io.hpp deleted file mode 100644 index 4d99788..0000000 --- a/arch/x86_64/include/x86_64/vga/io.hpp +++ /dev/null @@ -1,39 +0,0 @@ -#ifndef TEACHOS_X86_64_VGA_IO_HPP -#define TEACHOS_X86_64_VGA_IO_HPP - -#include "x86_64/io/port_io.hpp" - -#include - -namespace teachos::x86_64::vga::io -{ - namespace crtc - { - /** - * @brief The address port of the CRT Controller. - */ - using address_port = x86_64::io::port<0x3d4, 1>; - - /** - * @brief The data port of the CRT Controller. - */ - using data_port = x86_64::io::port<0x3d5, 1>; - - namespace registers - { - /** - * @brief The address of the Cursor Start register of the CRTC. - */ - [[maybe_unused]] auto constexpr cursor_start = std::byte{0x0a}; - - /** - * @brief The address of the Cursor End register of the CRTC. - */ - [[maybe_unused]] auto constexpr cursor_end = std::byte{0x0b}; - } // namespace registers - - }; // namespace crtc - -} // namespace teachos::x86_64::vga::io - -#endif \ No newline at end of file diff --git a/arch/x86_64/src/vga/text.cpp b/arch/x86_64/src/vga/text.cpp index dcfdb6b..5c94b84 100644 --- a/arch/x86_64/src/vga/text.cpp +++ b/arch/x86_64/src/vga/text.cpp @@ -1,7 +1,7 @@ #include "x86_64/vga/text.hpp" #include "x86_64/boot/boot.hpp" -#include "x86_64/vga/io.hpp" +#include "x86_64/vga/crtc.hpp" #include #include @@ -29,8 +29,8 @@ namespace teachos::x86_64::vga::text { auto cursor_disable_byte = std::byte{!enabled} << 5; - io::crtc::address_port::write(io::crtc::registers::cursor_start); - io::crtc::data_port::write(io::crtc::data_port::read() | cursor_disable_byte); + crtc::address::write(crtc::registers::cursor_start); + crtc::data::write(crtc::data::read() | cursor_disable_byte); } auto newline() -> void -- cgit v1.2.3 From 1b65136a11453fe7e89320dfe6170a0cd75e60dd Mon Sep 17 00:00:00 2001 From: Felix Morgner Date: Thu, 24 Jul 2025 16:33:10 +0000 Subject: x86_64: clean up hw details --- arch/x86_64/CMakeLists.txt | 4 ++ arch/x86_64/include/arch/boot/pointers.hpp | 15 ----- .../include/arch/kernel/cpu/control_register.hpp | 71 --------------------- arch/x86_64/include/arch/kernel/cpu/tlb.hpp | 27 -------- arch/x86_64/include/x86_64/cpu/registers.hpp | 72 ++++++++++++++++++++++ arch/x86_64/include/x86_64/memory/mmu.hpp | 27 ++++++++ arch/x86_64/src/cpu/registers.cpp | 64 +++++++++++++++++++ arch/x86_64/src/kernel/cpu/control_register.cpp | 66 -------------------- arch/x86_64/src/kernel/cpu/tlb.cpp | 16 ----- arch/x86_64/src/memory/mmu.cpp | 17 +++++ 10 files changed, 184 insertions(+), 195 deletions(-) delete mode 100644 arch/x86_64/include/arch/boot/pointers.hpp delete mode 100644 arch/x86_64/include/arch/kernel/cpu/control_register.hpp delete mode 100644 arch/x86_64/include/arch/kernel/cpu/tlb.hpp create mode 100644 arch/x86_64/include/x86_64/cpu/registers.hpp create mode 100644 arch/x86_64/include/x86_64/memory/mmu.hpp create mode 100644 arch/x86_64/src/cpu/registers.cpp delete mode 100644 arch/x86_64/src/kernel/cpu/control_register.cpp delete mode 100644 arch/x86_64/src/kernel/cpu/tlb.cpp create mode 100644 arch/x86_64/src/memory/mmu.cpp (limited to 'arch/x86_64') diff --git a/arch/x86_64/CMakeLists.txt b/arch/x86_64/CMakeLists.txt index ddc2d46..c585cbf 100644 --- a/arch/x86_64/CMakeLists.txt +++ b/arch/x86_64/CMakeLists.txt @@ -22,7 +22,11 @@ target_sources("x86_64" PRIVATE "src/boot/initialize_runtime.cpp" "src/boot/multiboot.s" + # CPU intrinsics + "src/cpu/registers.cpp" + # Memory management + "src/memory/mmu.cpp" "src/memory/region_allocator.cpp" # VGA text mode diff --git a/arch/x86_64/include/arch/boot/pointers.hpp b/arch/x86_64/include/arch/boot/pointers.hpp deleted file mode 100644 index fe9c657..0000000 --- a/arch/x86_64/include/arch/boot/pointers.hpp +++ /dev/null @@ -1,15 +0,0 @@ -#ifndef TEACHOS_ARCH_X86_64_BOOT_POINTERS_HPP -#define TEACHOS_ARCH_X86_64_BOOT_POINTERS_HPP - -#include - -namespace teachos::arch::boot -{ - /** - * @brief Address pointing to the start of the multiboot information structure. - */ - extern "C" size_t const multiboot_information_pointer; - -} // namespace teachos::arch::boot - -#endif // TEACHOS_ARCH_X86_64_BOOT_POINTERS_HPP diff --git a/arch/x86_64/include/arch/kernel/cpu/control_register.hpp b/arch/x86_64/include/arch/kernel/cpu/control_register.hpp deleted file mode 100644 index dcaf02d..0000000 --- a/arch/x86_64/include/arch/kernel/cpu/control_register.hpp +++ /dev/null @@ -1,71 +0,0 @@ -#ifndef TEACHOS_ARCH_X86_64_KERNEL_CPU_CR3_HPP -#define TEACHOS_ARCH_X86_64_KERNEL_CPU_CR3_HPP - -#include - -namespace teachos::arch::kernel::cpu -{ - /** - * @brief Control registers that can be read and written to. - * - * @note CR1 and CR5 - 7 are reserved and will throw an exception if they are accessed, therefore they are not defined - * in the enum. See https://en.wikipedia.org/wiki/Control_register#Control_registers_in_Intel_x86_series for more - * information. - */ - enum struct control_register : uint8_t - { - CR0, ///< Contains various control flags that modify basic operation of the processor, Machine Status World (MSW) - ///< register. - CR2 = 2U, ///< Contains Page Fault Linear Address (PFLA), when page fault occurs address program attended to accces - ///< is stored here. - CR3, ///< Enables process to translate linear addresses into physical addresses using paging, CR0 bit 32 Paging - ///< (PG) needs to be enabled simply contains the register value that represents the physical address of the - ///< level 4 page table used for paging in the system. Therefore reading this value allows to access the level - ///< 4 page table directly. Instead of over the virtual address 0xffffffff'fffff000, which then has to be - ///< first translated into a physical address. - CR4 ///< Used in protected mode to control operations. - }; - - /** - * @brief Control register 0 flags that can be set. - * - * @note Modifies the basic operation of the processor. Only the most important extensions are listed below, the rest - * are excluded for brevity. See https://en.wikipedia.org/wiki/Control_register#CR0 for more information. - */ - enum struct cr0_flags : uint64_t - { - PROTECTED_MODE_ENABLED = 1U << 0U, ///< System is in protected or system is in real mode. - TASK_SWITCHED = 1U << 3U, ///< Allows saving x87 task context upon a task switch only after x87 instruction used. - WRITE_PROTECT = 1U << 16U, ///< When set, the CPU cannot write to read-only pages when privilege level is 0. - PAGING = 1U << 31U, // Enable paging using the CR3 register. - }; - - /** - * @brief Reads the value of the given control register. - * - * @param cr Control register that should be read. - * @return Value of the control register. - */ - auto read_control_register(control_register cr) -> uint64_t; - - /** - * @brief Sets a specific bit in the Extended Feature Enable Register (EFER) Model-Specific Register (MSR) register. - * - * @param cr Control register that should be written. - * @param new_value New value that should be written. - */ - auto write_control_register(control_register cr, uint64_t new_value) -> void; - - /** - * @brief Sets a specific bit in the CR0. - * - * @note This function reads the current value of the CR0 register, ORs the specified - * bit with the current value, and writes the updated value back to the CR0. - * - * @param flag he flag to set in the CR0. - */ - auto set_cr0_bit(cr0_flags flag) -> void; - -} // namespace teachos::arch::kernel::cpu - -#endif // TEACHOS_ARCH_X86_64_KERNEL_CPU_CR3_HPP diff --git a/arch/x86_64/include/arch/kernel/cpu/tlb.hpp b/arch/x86_64/include/arch/kernel/cpu/tlb.hpp deleted file mode 100644 index f3e58a6..0000000 --- a/arch/x86_64/include/arch/kernel/cpu/tlb.hpp +++ /dev/null @@ -1,27 +0,0 @@ -#ifndef TEACHOS_ARCH_X86_64_KERNEL_CPU_TLB_HPP -#define TEACHOS_ARCH_X86_64_KERNEL_CPU_TLB_HPP - -#include "arch/memory/paging/virtual_page.hpp" - -namespace teachos::arch::kernel::cpu -{ - /** - * @brief Invalidates any translation lookaside buffer (TLB) entry for the page table the given address is cotained - * in. See https://www.felixcloutier.com/x86/invlpg for more information on the used x86 instruction. - * - * @param address Memory address, which will be used to determine the contained page and flush the TLB entry for - * that page. - */ - auto tlb_flush(memory::paging::virtual_address address) -> void; - - /** - * @brief Invalidates the translation lookaside buffer (TLB) entry for all page tables. - * - * @note Simply reassigns the CR3 register the value of the CR3 register, causing a flush of the TLB buffer, because - * the system has to assume that the location of the level 4 page table moved. - */ - auto tlb_flush_all() -> void; - -} // namespace teachos::arch::kernel::cpu - -#endif // TEACHOS_ARCH_X86_64_KERNEL_CPU_TLB_HPP diff --git a/arch/x86_64/include/x86_64/cpu/registers.hpp b/arch/x86_64/include/x86_64/cpu/registers.hpp new file mode 100644 index 0000000..607d559 --- /dev/null +++ b/arch/x86_64/include/x86_64/cpu/registers.hpp @@ -0,0 +1,72 @@ +#ifndef TEACHOS_X86_64_CPU_REGISTERS_HPP +#define TEACHOS_X86_64_CPU_REGISTERS_HPP + +#include + +namespace teachos::x86_64::cpu +{ + + /** + * @brief Control registers that can be read and written to. + * + * @note CR1 and CR5 - 7 are reserved and will throw an exception if they are accessed, therefore they are not defined + * in the enum. See https://en.wikipedia.org/wiki/Control_register#Control_registers_in_Intel_x86_series for more + * information. + */ + enum struct control_register : uint8_t + { + cr0, ///< Contains various control flags that modify basic operation of the processor, Machine Status World (MSW) + ///< register. + cr2 = 2U, ///< Contains Page Fault Linear Address (PFLA), when page fault occurs address program attended to accces + ///< is stored here. + cr3, ///< Enables process to translate linear addresses into physical addresses using paging, CR0 bit 32 Paging + ///< (PG) needs to be enabled simply contains the register value that represents the physical address of the + ///< level 4 page table used for paging in the system. Therefore reading this value allows to access the level + ///< 4 page table directly. Instead of over the virtual address 0xffffffff'fffff000, which then has to be + ///< first translated into a physical address. + cr4 ///< Used in protected mode to control operations. + }; + + /** + * @brief Control register 0 flags that can be set. + * + * @note Modifies the basic operation of the processor. Only the most important extensions are listed below, the rest + * are excluded for brevity. See https://en.wikipedia.org/wiki/Control_register#CR0 for more information. + */ + enum struct cr0_flags : uint64_t + { + PROTECTED_MODE_ENABLED = 1U << 0U, ///< System is in protected or system is in real mode. + TASK_SWITCHED = 1U << 3U, ///< Allows saving x87 task context upon a task switch only after x87 instruction used. + WRITE_PROTECT = 1U << 16U, ///< When set, the CPU cannot write to read-only pages when privilege level is 0. + PAGING = 1U << 31U, // Enable paging using the CR3 register. + }; + + /** + * @brief Reads the value of the given control register. + * + * @param cr Control register that should be read. + * @return Value of the control register. + */ + auto read_control_register(control_register cr) -> uint64_t; + + /** + * @brief Sets a specific bit in the Extended Feature Enable Register (EFER) Model-Specific Register (MSR) register. + * + * @param cr Control register that should be written. + * @param new_value New value that should be written. + */ + auto write_control_register(control_register cr, uint64_t new_value) -> void; + + /** + * @brief Sets a specific bit in the CR0. + * + * @note This function reads the current value of the CR0 register, ORs the specified + * bit with the current value, and writes the updated value back to the CR0. + * + * @param flag he flag to set in the CR0. + */ + auto set_cr0_bit(cr0_flags flag) -> void; + +} // namespace teachos::x86_64::cpu + +#endif \ No newline at end of file diff --git a/arch/x86_64/include/x86_64/memory/mmu.hpp b/arch/x86_64/include/x86_64/memory/mmu.hpp new file mode 100644 index 0000000..b03ffa2 --- /dev/null +++ b/arch/x86_64/include/x86_64/memory/mmu.hpp @@ -0,0 +1,27 @@ +#ifndef TEACHOS_X86_64_MEMORY_MMU_HPP +#define TEACHOS_X86_64_MEMORY_MMU_HPP + +#include "x86_64/memory/address.hpp" + +namespace teachos::x86_64::memory +{ + /** + * @brief Invalidates any translation lookaside buffer (TLB) entry for the page table the given address is cotained + * in. See https://www.felixcloutier.com/x86/invlpg for more information on the used x86 instruction. + * + * @param address Memory address, which will be used to determine the contained page and flush the TLB entry for + * that page. + */ + auto tlb_flush(linear_address address) -> void; + + /** + * @brief Invalidates the translation lookaside buffer (TLB) entry for all page tables. + * + * @note Simply reassigns the CR3 register the value of the CR3 register, causing a flush of the TLB buffer, because + * the system has to assume that the location of the level 4 page table moved. + */ + auto tlb_flush_all() -> void; + +} // namespace teachos::x86_64::memory + +#endif \ No newline at end of file diff --git a/arch/x86_64/src/cpu/registers.cpp b/arch/x86_64/src/cpu/registers.cpp new file mode 100644 index 0000000..7ade98d --- /dev/null +++ b/arch/x86_64/src/cpu/registers.cpp @@ -0,0 +1,64 @@ +#include "x86_64/cpu/registers.hpp" + +#include + +namespace teachos::x86_64::cpu +{ + auto read_control_register(control_register cr) -> uint64_t + { + uint64_t current_value; + switch (cr) + { + case control_register::cr0: + asm volatile("mov %%cr0, %[output]" : [output] "=r"(current_value)); + break; + case control_register::cr2: + asm volatile("mov %%cr2, %[output]" : [output] "=r"(current_value)); + break; + case control_register::cr3: + asm volatile("mov %%cr3, %[output]" : [output] "=r"(current_value)); + break; + case control_register::cr4: + asm volatile("mov %%cr4, %[output]" : [output] "=r"(current_value)); + break; + } + return current_value; + } + + auto write_control_register(control_register cr, uint64_t new_value) -> void + { + switch (cr) + { + case control_register::cr0: + asm volatile("mov %[input], %%cr0" + : /* no output from call */ + : [input] "r"(new_value) + : "memory"); + break; + case control_register::cr2: + asm volatile("mov %[input], %%cr2" + : /* no output from call */ + : [input] "r"(new_value) + : "memory"); + break; + case control_register::cr3: + asm volatile("mov %[input], %%cr3" + : /* no output from call */ + : [input] "r"(new_value) + : "memory"); + break; + case control_register::cr4: + asm volatile("mov %[input], %%cr4" + : /* no output from call */ + : [input] "r"(new_value) + : "memory"); + break; + } + } + + auto set_cr0_bit(cr0_flags flag) -> void + { + auto const cr0 = read_control_register(control_register::cr0); + write_control_register(control_register::cr0, static_cast::type>(flag) | cr0); + } +} // namespace teachos::x86_64::cpu diff --git a/arch/x86_64/src/kernel/cpu/control_register.cpp b/arch/x86_64/src/kernel/cpu/control_register.cpp deleted file mode 100644 index 41b8cd7..0000000 --- a/arch/x86_64/src/kernel/cpu/control_register.cpp +++ /dev/null @@ -1,66 +0,0 @@ -#include "arch/kernel/cpu/control_register.hpp" - -#include "arch/exception_handling/panic.hpp" - -#include - -namespace teachos::arch::kernel::cpu -{ - auto read_control_register(control_register cr) -> uint64_t - { - uint64_t current_value; - switch (cr) - { - case control_register::CR0: - asm volatile("mov %%cr0, %[output]" : [output] "=r"(current_value)); - break; - case control_register::CR2: - asm volatile("mov %%cr2, %[output]" : [output] "=r"(current_value)); - break; - case control_register::CR3: - asm volatile("mov %%cr3, %[output]" : [output] "=r"(current_value)); - break; - case control_register::CR4: - asm volatile("mov %%cr4, %[output]" : [output] "=r"(current_value)); - break; - } - return current_value; - } - - auto write_control_register(control_register cr, uint64_t new_value) -> void - { - switch (cr) - { - case control_register::CR0: - asm volatile("mov %[input], %%cr0" - : /* no output from call */ - : [input] "r"(new_value) - : "memory"); - break; - case control_register::CR2: - asm volatile("mov %[input], %%cr2" - : /* no output from call */ - : [input] "r"(new_value) - : "memory"); - break; - case control_register::CR3: - asm volatile("mov %[input], %%cr3" - : /* no output from call */ - : [input] "r"(new_value) - : "memory"); - break; - case control_register::CR4: - asm volatile("mov %[input], %%cr4" - : /* no output from call */ - : [input] "r"(new_value) - : "memory"); - break; - } - } - - auto set_cr0_bit(cr0_flags flag) -> void - { - auto const cr0 = read_control_register(control_register::CR0); - write_control_register(control_register::CR0, static_cast::type>(flag) | cr0); - } -} // namespace teachos::arch::kernel::cpu diff --git a/arch/x86_64/src/kernel/cpu/tlb.cpp b/arch/x86_64/src/kernel/cpu/tlb.cpp deleted file mode 100644 index a09001c..0000000 --- a/arch/x86_64/src/kernel/cpu/tlb.cpp +++ /dev/null @@ -1,16 +0,0 @@ -#include "arch/kernel/cpu/tlb.hpp" - -#include "arch/kernel/cpu/control_register.hpp" - -namespace teachos::arch::kernel::cpu -{ - auto tlb_flush(memory::paging::virtual_address address) -> void - { - asm volatile("invlpg (%[input])" : /* no output from call */ : [input] "r"(address) : "memory"); - } - - auto tlb_flush_all() -> void - { - write_control_register(cpu::control_register::CR3, read_control_register(cpu::control_register::CR3)); - } -} // namespace teachos::arch::kernel::cpu diff --git a/arch/x86_64/src/memory/mmu.cpp b/arch/x86_64/src/memory/mmu.cpp new file mode 100644 index 0000000..31783fe --- /dev/null +++ b/arch/x86_64/src/memory/mmu.cpp @@ -0,0 +1,17 @@ +#include "x86_64/memory/mmu.hpp" + +#include "x86_64/cpu/registers.hpp" + +namespace teachos::x86_64::memory +{ + auto tlb_flush(linear_address address) -> void + { + asm volatile("invlpg (%[input])" : /* no output from call */ : [input] "r"(address) : "memory"); + } + + auto tlb_flush_all() -> void + { + auto current_value = cpu::read_control_register(cpu::control_register::cr3); + cpu::write_control_register(cpu::control_register::cr3, current_value); + } +} // namespace teachos::x86_64::memory -- cgit v1.2.3 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 ----------- .../x86_64/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 -- .../include/arch/exception_handling/assert.hpp | 17 -- .../include/arch/exception_handling/panic.hpp | 23 --- .../generic_interrupt_handler.hpp | 34 ---- arch/x86_64/include/arch/kernel/cpu/call.hpp | 30 --- arch/x86_64/include/arch/kernel/cpu/gdtr.hpp | 27 --- arch/x86_64/include/arch/kernel/cpu/idtr.hpp | 27 --- arch/x86_64/include/arch/kernel/cpu/if.hpp | 21 --- arch/x86_64/include/arch/kernel/cpu/msr.hpp | 64 ------- .../include/arch/kernel/cpu/segment_register.hpp | 97 ---------- arch/x86_64/include/arch/kernel/cpu/tr.hpp | 24 --- arch/x86_64/include/arch/kernel/halt.hpp | 13 -- arch/x86_64/include/arch/kernel/main.hpp | 13 -- .../arch/memory/allocator/area_frame_allocator.hpp | 67 ------- .../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 ------------ .../include/arch/memory/heap/memory_block.hpp | 39 ---- .../arch/memory/heap/user_heap_allocator.hpp | 149 --------------- arch/x86_64/include/arch/memory/main.hpp | 30 --- .../arch/memory/multiboot/elf_symbols_section.hpp | 170 ----------------- .../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 ------------------ .../include/arch/memory/paging/page_entry.hpp | 121 ------------ .../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/include/arch/user/main.hpp | 16 -- .../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 ++++ .../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/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 --- arch/x86_64/src/context_switching/syscall/main.cpp | 35 ---- .../context_switching/syscall/syscall_enable.cpp | 32 ---- .../context_switching/syscall/syscall_handler.cpp | 118 ------------ arch/x86_64/src/exception_handling/abort.cpp | 15 -- arch/x86_64/src/exception_handling/assert.cpp | 15 -- arch/x86_64/src/exception_handling/panic.cpp | 22 --- .../x86_64/src/exception_handling/pure_virtual.cpp | 6 - .../generic_interrupt_handler.cpp | 13 -- arch/x86_64/src/kernel/cpu/call.cpp | 9 - arch/x86_64/src/kernel/cpu/gdtr.cpp | 19 -- arch/x86_64/src/kernel/cpu/idtr.cpp | 18 -- arch/x86_64/src/kernel/cpu/if.cpp | 7 - arch/x86_64/src/kernel/cpu/msr.cpp | 31 ---- arch/x86_64/src/kernel/cpu/segment_register.cpp | 98 ---------- arch/x86_64/src/kernel/cpu/tr.cpp | 16 -- arch/x86_64/src/kernel/main.cpp | 71 ------- .../src/memory/allocator/area_frame_allocator.cpp | 85 --------- .../src/memory/allocator/tiny_frame_allocator.cpp | 34 ---- arch/x86_64/src/memory/heap/bump_allocator.cpp | 54 ------ .../src/memory/heap/global_heap_allocator.cpp | 135 -------------- .../src/memory/heap/linked_list_allocator.cpp | 177 ------------------ arch/x86_64/src/memory/heap/memory_block.cpp | 15 -- .../x86_64/src/memory/heap/user_heap_allocator.cpp | 200 -------------------- arch/x86_64/src/memory/main.cpp | 77 -------- .../src/memory/multiboot/elf_symbols_section.cpp | 13 -- arch/x86_64/src/memory/multiboot/reader.cpp | 135 -------------- .../x86_64/src/memory/paging/active_page_table.cpp | 98 ---------- .../src/memory/paging/inactive_page_table.cpp | 20 -- arch/x86_64/src/memory/paging/page_entry.cpp | 63 ------- arch/x86_64/src/memory/paging/page_table.cpp | 128 ------------- arch/x86_64/src/memory/paging/temporary_page.cpp | 29 --- arch/x86_64/src/memory/paging/virtual_page.cpp | 33 ---- arch/x86_64/src/user/main.cpp | 35 ---- 192 files changed, 5493 insertions(+), 5493 deletions(-) delete mode 100644 arch/x86_64/include/arch/context_switching/interrupt_descriptor_table/gate_descriptor.hpp delete mode 100644 arch/x86_64/include/arch/context_switching/interrupt_descriptor_table/idt_flags.hpp delete mode 100644 arch/x86_64/include/arch/context_switching/interrupt_descriptor_table/interrupt_descriptor_table.hpp delete mode 100644 arch/x86_64/include/arch/context_switching/interrupt_descriptor_table/interrupt_descriptor_table_pointer.hpp delete mode 100644 arch/x86_64/include/arch/context_switching/interrupt_descriptor_table/ist_offset.hpp delete mode 100644 arch/x86_64/include/arch/context_switching/interrupt_descriptor_table/segment_selector.hpp delete mode 100644 arch/x86_64/include/arch/context_switching/main.hpp delete mode 100644 arch/x86_64/include/arch/context_switching/segment_descriptor_table/access_byte.hpp delete mode 100644 arch/x86_64/include/arch/context_switching/segment_descriptor_table/gdt_flags.hpp delete mode 100644 arch/x86_64/include/arch/context_switching/segment_descriptor_table/global_descriptor_table.hpp delete mode 100644 arch/x86_64/include/arch/context_switching/segment_descriptor_table/global_descriptor_table_pointer.hpp delete mode 100644 arch/x86_64/include/arch/context_switching/segment_descriptor_table/segment_descriptor_base.hpp delete mode 100644 arch/x86_64/include/arch/context_switching/segment_descriptor_table/segment_descriptor_extension.hpp delete mode 100644 arch/x86_64/include/arch/context_switching/segment_descriptor_table/segment_descriptor_type.hpp delete mode 100644 arch/x86_64/include/arch/context_switching/segment_descriptor_table/task_state_segment.hpp delete mode 100644 arch/x86_64/include/arch/context_switching/syscall/main.hpp delete mode 100644 arch/x86_64/include/arch/context_switching/syscall/syscall_enable.hpp delete mode 100644 arch/x86_64/include/arch/context_switching/syscall/syscall_handler.hpp delete mode 100644 arch/x86_64/include/arch/exception_handling/assert.hpp delete mode 100644 arch/x86_64/include/arch/exception_handling/panic.hpp delete mode 100644 arch/x86_64/include/arch/interrupt_handling/generic_interrupt_handler.hpp delete mode 100644 arch/x86_64/include/arch/kernel/cpu/call.hpp delete mode 100644 arch/x86_64/include/arch/kernel/cpu/gdtr.hpp delete mode 100644 arch/x86_64/include/arch/kernel/cpu/idtr.hpp delete mode 100644 arch/x86_64/include/arch/kernel/cpu/if.hpp delete mode 100644 arch/x86_64/include/arch/kernel/cpu/msr.hpp delete mode 100644 arch/x86_64/include/arch/kernel/cpu/segment_register.hpp delete mode 100644 arch/x86_64/include/arch/kernel/cpu/tr.hpp delete mode 100644 arch/x86_64/include/arch/kernel/halt.hpp delete mode 100644 arch/x86_64/include/arch/kernel/main.hpp delete mode 100644 arch/x86_64/include/arch/memory/allocator/area_frame_allocator.hpp delete mode 100644 arch/x86_64/include/arch/memory/allocator/concept.hpp delete mode 100644 arch/x86_64/include/arch/memory/allocator/tiny_frame_allocator.hpp delete mode 100644 arch/x86_64/include/arch/memory/heap/bump_allocator.hpp delete mode 100644 arch/x86_64/include/arch/memory/heap/global_heap_allocator.hpp delete mode 100644 arch/x86_64/include/arch/memory/heap/heap_allocator.hpp delete mode 100644 arch/x86_64/include/arch/memory/heap/linked_list_allocator.hpp delete mode 100644 arch/x86_64/include/arch/memory/heap/memory_block.hpp delete mode 100644 arch/x86_64/include/arch/memory/heap/user_heap_allocator.hpp delete mode 100644 arch/x86_64/include/arch/memory/main.hpp delete mode 100644 arch/x86_64/include/arch/memory/multiboot/elf_symbols_section.hpp delete mode 100644 arch/x86_64/include/arch/memory/multiboot/reader.hpp delete mode 100644 arch/x86_64/include/arch/memory/paging/active_page_table.hpp delete mode 100644 arch/x86_64/include/arch/memory/paging/inactive_page_table.hpp delete mode 100644 arch/x86_64/include/arch/memory/paging/kernel_mapper.hpp delete mode 100644 arch/x86_64/include/arch/memory/paging/page_entry.hpp delete mode 100644 arch/x86_64/include/arch/memory/paging/page_table.hpp delete mode 100644 arch/x86_64/include/arch/memory/paging/temporary_page.hpp delete mode 100644 arch/x86_64/include/arch/memory/paging/virtual_page.hpp delete mode 100644 arch/x86_64/include/arch/user/main.hpp 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 delete mode 100644 arch/x86_64/src/context_switching/interrupt_descriptor_table/gate_descriptor.cpp delete mode 100644 arch/x86_64/src/context_switching/interrupt_descriptor_table/idt_flags.cpp delete mode 100644 arch/x86_64/src/context_switching/interrupt_descriptor_table/interrupt_descriptor_table.cpp delete mode 100644 arch/x86_64/src/context_switching/interrupt_descriptor_table/interrupt_descriptor_table_pointer.cpp delete mode 100644 arch/x86_64/src/context_switching/interrupt_descriptor_table/ist_offset.cpp delete mode 100644 arch/x86_64/src/context_switching/interrupt_descriptor_table/segment_selector.cpp delete mode 100644 arch/x86_64/src/context_switching/main.cpp delete mode 100644 arch/x86_64/src/context_switching/segment_descriptor_table/access_byte.cpp delete mode 100644 arch/x86_64/src/context_switching/segment_descriptor_table/gdt_flags.cpp delete mode 100644 arch/x86_64/src/context_switching/segment_descriptor_table/global_descriptor_table.cpp delete mode 100644 arch/x86_64/src/context_switching/segment_descriptor_table/global_descriptor_table_pointer.cpp delete mode 100644 arch/x86_64/src/context_switching/segment_descriptor_table/segment_descriptor_base.cpp delete mode 100644 arch/x86_64/src/context_switching/segment_descriptor_table/segment_descriptor_extension.cpp delete mode 100644 arch/x86_64/src/context_switching/syscall/main.cpp delete mode 100644 arch/x86_64/src/context_switching/syscall/syscall_enable.cpp delete mode 100644 arch/x86_64/src/context_switching/syscall/syscall_handler.cpp delete mode 100644 arch/x86_64/src/exception_handling/abort.cpp delete mode 100644 arch/x86_64/src/exception_handling/assert.cpp delete mode 100644 arch/x86_64/src/exception_handling/panic.cpp delete mode 100644 arch/x86_64/src/exception_handling/pure_virtual.cpp delete mode 100644 arch/x86_64/src/interrupt_handling/generic_interrupt_handler.cpp delete mode 100644 arch/x86_64/src/kernel/cpu/call.cpp delete mode 100644 arch/x86_64/src/kernel/cpu/gdtr.cpp delete mode 100644 arch/x86_64/src/kernel/cpu/idtr.cpp delete mode 100644 arch/x86_64/src/kernel/cpu/if.cpp delete mode 100644 arch/x86_64/src/kernel/cpu/msr.cpp delete mode 100644 arch/x86_64/src/kernel/cpu/segment_register.cpp delete mode 100644 arch/x86_64/src/kernel/cpu/tr.cpp delete mode 100644 arch/x86_64/src/kernel/main.cpp delete mode 100644 arch/x86_64/src/memory/allocator/area_frame_allocator.cpp delete mode 100644 arch/x86_64/src/memory/allocator/tiny_frame_allocator.cpp delete mode 100644 arch/x86_64/src/memory/heap/bump_allocator.cpp delete mode 100644 arch/x86_64/src/memory/heap/global_heap_allocator.cpp delete mode 100644 arch/x86_64/src/memory/heap/linked_list_allocator.cpp delete mode 100644 arch/x86_64/src/memory/heap/memory_block.cpp delete mode 100644 arch/x86_64/src/memory/heap/user_heap_allocator.cpp delete mode 100644 arch/x86_64/src/memory/main.cpp delete mode 100644 arch/x86_64/src/memory/multiboot/elf_symbols_section.cpp delete mode 100644 arch/x86_64/src/memory/multiboot/reader.cpp delete mode 100644 arch/x86_64/src/memory/paging/active_page_table.cpp delete mode 100644 arch/x86_64/src/memory/paging/inactive_page_table.cpp delete mode 100644 arch/x86_64/src/memory/paging/page_entry.cpp delete mode 100644 arch/x86_64/src/memory/paging/page_table.cpp delete mode 100644 arch/x86_64/src/memory/paging/temporary_page.cpp delete mode 100644 arch/x86_64/src/memory/paging/virtual_page.cpp delete mode 100644 arch/x86_64/src/user/main.cpp (limited to 'arch/x86_64') diff --git a/arch/x86_64/include/arch/context_switching/interrupt_descriptor_table/gate_descriptor.hpp b/arch/x86_64/include/arch/context_switching/interrupt_descriptor_table/gate_descriptor.hpp deleted file mode 100644 index 07110c8..0000000 --- a/arch/x86_64/include/arch/context_switching/interrupt_descriptor_table/gate_descriptor.hpp +++ /dev/null @@ -1,69 +0,0 @@ -#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/include/arch/context_switching/interrupt_descriptor_table/idt_flags.hpp b/arch/x86_64/include/arch/context_switching/interrupt_descriptor_table/idt_flags.hpp deleted file mode 100644 index 5104c36..0000000 --- a/arch/x86_64/include/arch/context_switching/interrupt_descriptor_table/idt_flags.hpp +++ /dev/null @@ -1,81 +0,0 @@ - -#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/include/arch/context_switching/interrupt_descriptor_table/interrupt_descriptor_table.hpp b/arch/x86_64/include/arch/context_switching/interrupt_descriptor_table/interrupt_descriptor_table.hpp deleted file mode 100644 index b388e0e..0000000 --- a/arch/x86_64/include/arch/context_switching/interrupt_descriptor_table/interrupt_descriptor_table.hpp +++ /dev/null @@ -1,24 +0,0 @@ -#ifndef TEACHOS_ARCH_X86_64_CONTEXT_SWITCHING_INTERRUPT_DESCRIPTOR_TABLE_INTERRUPT_DESCRIPTOR_TABLE_HPP -#define TEACHOS_ARCH_X86_64_CONTEXT_SWITCHING_INTERRUPT_DESCRIPTOR_TABLE_INTERRUPT_DESCRIPTOR_TABLE_HPP - -#include "arch/context_switching/interrupt_descriptor_table/interrupt_descriptor_table_pointer.hpp" - -namespace teachos::arch::context_switching::interrupt_descriptor_table -{ - /** - * @brief Updates the IDTR with the created interrupt descriptor table. If it has not been created yet this - * method will create it. - */ - auto update_interrupt_descriptor_table_register() -> void; - - /** - * @brief Creates the interrupt descriptor table, with the minimum required configuration. If this method is called - * more than once, the previously created instance is returned instead. - * - * @return Reference to the created interrupt_descriptor_table. - */ - auto get_or_create_interrupt_descriptor_table() -> interrupt_descriptor_table &; - -} // namespace teachos::arch::context_switching::interrupt_descriptor_table - -#endif // TEACHOS_ARCH_X86_64_CONTEXT_SWITCHING_INTERRUPT_DESCRIPTOR_TABLE_INTERRUPT_DESCRIPTOR_TABLE_HPP diff --git a/arch/x86_64/include/arch/context_switching/interrupt_descriptor_table/interrupt_descriptor_table_pointer.hpp b/arch/x86_64/include/arch/context_switching/interrupt_descriptor_table/interrupt_descriptor_table_pointer.hpp deleted file mode 100644 index 7fe933b..0000000 --- a/arch/x86_64/include/arch/context_switching/interrupt_descriptor_table/interrupt_descriptor_table_pointer.hpp +++ /dev/null @@ -1,40 +0,0 @@ -#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/include/arch/context_switching/interrupt_descriptor_table/ist_offset.hpp b/arch/x86_64/include/arch/context_switching/interrupt_descriptor_table/ist_offset.hpp deleted file mode 100644 index e45bcf4..0000000 --- a/arch/x86_64/include/arch/context_switching/interrupt_descriptor_table/ist_offset.hpp +++ /dev/null @@ -1,45 +0,0 @@ -#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/include/arch/context_switching/interrupt_descriptor_table/segment_selector.hpp b/arch/x86_64/include/arch/context_switching/interrupt_descriptor_table/segment_selector.hpp deleted file mode 100644 index 2a7704e..0000000 --- a/arch/x86_64/include/arch/context_switching/interrupt_descriptor_table/segment_selector.hpp +++ /dev/null @@ -1,105 +0,0 @@ -#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/include/arch/context_switching/main.hpp b/arch/x86_64/include/arch/context_switching/main.hpp deleted file mode 100644 index f8477ea..0000000 --- a/arch/x86_64/include/arch/context_switching/main.hpp +++ /dev/null @@ -1,51 +0,0 @@ -#ifndef TEACHOS_ARCH_X86_64_CONTEXT_SWITCHING_MAIN_HPP -#define TEACHOS_ARCH_X86_64_CONTEXT_SWITCHING_MAIN_HPP - -#include "arch/context_switching/interrupt_descriptor_table/interrupt_descriptor_table.hpp" -#include "arch/context_switching/segment_descriptor_table/global_descriptor_table.hpp" - -namespace teachos::arch::context_switching -{ - /** - * @brief Contains the references to the tables required for context switching - */ - struct descriptor_tables - { - segment_descriptor_table::global_descriptor_table & gdt; ///< Reference to the global descriptor table. - interrupt_descriptor_table::interrupt_descriptor_table & idt; ///< Reference to the interrupt descriptor table. - }; - - /** - * @brief Creates the Interrupt Descriptor Table and Global Descriptor Table as a static variable the first time this - * method is called and update IDTR and GDTR registers values. - * - * @note Subsequent calls after the first one, will simply return the previously created tables, but not update the - * registers again. - * - * @return References to the statically created Interrupt Descriptor and Global Descriptor Table. - */ - auto initialize_descriptor_tables() -> descriptor_tables; - - /** - * @brief Switches from the current Kernel Mode (Level 0) to User Mode (Level 3). Will simply use predefined Segment - * Selectors for the User Data and User Code Segment, which are Index 3 and 4 in the GDT respectively. - */ - auto switch_to_user_mode() -> void; - - /** - * @brief Switches from the current Code and Data Segment to the given Code and Data Segment. - * - * @note This method will additionally call initialize_descriptor_tables, to ensure the GDTR and IDTR have been setup - * correctly before attempting to switch the context. This switch is achieved using a far return, which will once - * executed call the given void function. - * - * @param data_segment Data Segment that the SS, DS; ES, FS and GS register will be set too. - * @param code_segment Code Segment that the CS register will be set too. - * @param return_function Function that will be called once the switch has been achieved. - */ - auto switch_context(interrupt_descriptor_table::segment_selector data_segment, - interrupt_descriptor_table::segment_selector code_segment, void (*return_function)()) -> void; - -} // namespace teachos::arch::context_switching - -#endif // TEACHOS_ARCH_X86_64_CONTEXT_SWITCHING_MAIN_HPP diff --git a/arch/x86_64/include/arch/context_switching/segment_descriptor_table/access_byte.hpp b/arch/x86_64/include/arch/context_switching/segment_descriptor_table/access_byte.hpp deleted file mode 100644 index 7450330..0000000 --- a/arch/x86_64/include/arch/context_switching/segment_descriptor_table/access_byte.hpp +++ /dev/null @@ -1,104 +0,0 @@ - -#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/include/arch/context_switching/segment_descriptor_table/gdt_flags.hpp b/arch/x86_64/include/arch/context_switching/segment_descriptor_table/gdt_flags.hpp deleted file mode 100644 index e24b988..0000000 --- a/arch/x86_64/include/arch/context_switching/segment_descriptor_table/gdt_flags.hpp +++ /dev/null @@ -1,93 +0,0 @@ -#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/include/arch/context_switching/segment_descriptor_table/global_descriptor_table.hpp b/arch/x86_64/include/arch/context_switching/segment_descriptor_table/global_descriptor_table.hpp deleted file mode 100644 index 44f2692..0000000 --- a/arch/x86_64/include/arch/context_switching/segment_descriptor_table/global_descriptor_table.hpp +++ /dev/null @@ -1,37 +0,0 @@ -#ifndef TEACHOS_ARCH_X86_64_CONTEXT_SWITCHING_SEGMENT_DESCRIPTOR_TABLE_GLOBAL_DESCRIPTOR_TABLE_HPP -#define TEACHOS_ARCH_X86_64_CONTEXT_SWITCHING_SEGMENT_DESCRIPTOR_TABLE_GLOBAL_DESCRIPTOR_TABLE_HPP - -#include "arch/context_switching/segment_descriptor_table/global_descriptor_table_pointer.hpp" -#include "arch/context_switching/segment_descriptor_table/task_state_segment.hpp" - -namespace teachos::arch::context_switching::segment_descriptor_table -{ - /** - * @brief Creates the global descriptor table, with the minimum required configuration. If this method is called more - * than once, the previously created instance is returned instead. - * - * @return Reference to the created global_descriptor_table. - */ - auto get_or_create_gdt() -> global_descriptor_table &; - - /** - * @brief Updates the GDTR with the created global descriptor table. If it has not been created yet this - * method will create it. - * - * @note This method will only set the GDTR, but for the processor to actually register the change a far jump - * has to be executed. This also has to be done before updating the TR. - */ - auto update_gdtr() -> void; - - /** - * @brief Updates the TR with the created task state segment. If it has not been created yet this - * method will create it. - * - * @note This method should only be called after update_gdtr() and a far jump has been - * executed. Because before that trying to access the segment will cause an exception. - */ - auto update_tss_register() -> void; - -} // namespace teachos::arch::context_switching::segment_descriptor_table - -#endif // TEACHOS_ARCH_X86_64_CONTEXT_SWITCHING_SEGMENT_DESCRIPTOR_TABLE_GLOBAL_DESCRIPTOR_TABLE_HPP diff --git a/arch/x86_64/include/arch/context_switching/segment_descriptor_table/global_descriptor_table_pointer.hpp b/arch/x86_64/include/arch/context_switching/segment_descriptor_table/global_descriptor_table_pointer.hpp deleted file mode 100644 index 292ff70..0000000 --- a/arch/x86_64/include/arch/context_switching/segment_descriptor_table/global_descriptor_table_pointer.hpp +++ /dev/null @@ -1,41 +0,0 @@ -#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/include/arch/context_switching/segment_descriptor_table/segment_descriptor_base.hpp b/arch/x86_64/include/arch/context_switching/segment_descriptor_table/segment_descriptor_base.hpp deleted file mode 100644 index 933fb4d..0000000 --- a/arch/x86_64/include/arch/context_switching/segment_descriptor_table/segment_descriptor_base.hpp +++ /dev/null @@ -1,73 +0,0 @@ -#ifndef TEACHOS_ARCH_X86_64_CONTEXT_SWITCHING_SEGMENT_DESCRIPTOR_TABLE_SEGMENT_DESCRIPTOR_BASE_HPP -#define TEACHOS_ARCH_X86_64_CONTEXT_SWITCHING_SEGMENT_DESCRIPTOR_TABLE_SEGMENT_DESCRIPTOR_BASE_HPP - -#include "arch/context_switching/segment_descriptor_table/access_byte.hpp" -#include "arch/context_switching/segment_descriptor_table/gdt_flags.hpp" -#include "arch/context_switching/segment_descriptor_table/segment_descriptor_type.hpp" - -namespace teachos::arch::context_switching::segment_descriptor_table -{ - /** - * @brief Defines helper function for all states and the actual data the segment descriptor can have. - */ - struct [[gnu::packed]] segment_descriptor_base - { - /** - * @brief Default Constructor. - */ - segment_descriptor_base() = default; - - /** - * @brief Constructor. - * - * @note Created segment descriptor copies the given bytes into these components requiring the space of one - * segment descriptor entry in the global descriptor table being 64-bit. - * - 8 bit Access Type - * - 4 bit Flags - * - 32 bit Base Address - * - 20 bit Limit - * - * @param flags Copies the bits set from the given data into the individual components of a segment - * descriptor. - */ - explicit segment_descriptor_base(uint64_t flags); - - /** - * @brief Constructor. - * - * @param access_byte, flags, base, limit Copies the bits set from the given data into the individual components of - * a segment descriptor. - */ - segment_descriptor_base(access_byte access_byte, gdt_flags flags, uint32_t base, std::bitset<20U> limit); - - /** - * @brief Calculates the underlying segment type that this segement descriptor is describing. - */ - auto get_segment_type() const -> segment_descriptor_type; - - /** - * @brief Cast the underlying data into a combined 64-bit form, that contains all data. - * - * @return Underlying value combined into it's full size. - */ - operator uint64_t() const; - - /** - * @brief Allows to compare the underlying bits of two instances. - * - * @param other Other instance that we want to compare with. - * @return Whether the underlying set bits of both types are the same. - */ - auto operator==(segment_descriptor_base const & other) const -> bool = default; - - private: - // The order in private variables starts for the first variable being the rightmost bit. - uint16_t _limit_1 = {}; ///< First part of the limit field (0 - 15) - uint32_t _base_1 : 24 = {}; ///< First part of the base field (16 - 39) - access_byte _access = {}; ///< Access byte field (40 - 47) - gdt_flags _flag = {}; ///< Second part of the limit field + Flags field (48 - 55) - uint8_t _base_2 = {}; ///< Second part of the base field (56 - 63) - }; -} // namespace teachos::arch::context_switching::segment_descriptor_table - -#endif // TEACHOS_ARCH_X86_64_CONTEXT_SWITCHING_SEGMENT_DESCRIPTOR_TABLE_SEGMENT_DESCRIPTOR_BASE_HPP diff --git a/arch/x86_64/include/arch/context_switching/segment_descriptor_table/segment_descriptor_extension.hpp b/arch/x86_64/include/arch/context_switching/segment_descriptor_table/segment_descriptor_extension.hpp deleted file mode 100644 index 40bcc8a..0000000 --- a/arch/x86_64/include/arch/context_switching/segment_descriptor_table/segment_descriptor_extension.hpp +++ /dev/null @@ -1,74 +0,0 @@ -#ifndef TEACHOS_ARCH_X86_64_CONTEXT_SWITCHING_SEGMENT_DESCRIPTOR_TABLE_SEGMENT_DESCRIPTOR_EXTENSION_HPP -#define TEACHOS_ARCH_X86_64_CONTEXT_SWITCHING_SEGMENT_DESCRIPTOR_TABLE_SEGMENT_DESCRIPTOR_EXTENSION_HPP - -#include "arch/context_switching/segment_descriptor_table/segment_descriptor_base.hpp" - -namespace teachos::arch::context_switching::segment_descriptor_table -{ - __extension__ typedef __int128 int128_t; - __extension__ typedef unsigned __int128 uint128_t; - - /** - * @brief Defines helper function for all states and the actual data the segment descriptor can have. - */ - struct [[gnu::packed]] segment_descriptor_extension - { - /** - * @brief Default Constructor. - */ - segment_descriptor_extension() = default; - - /** - * @brief Constructor. - * - * @note Created segment descriptor copies the given bytes into these components requiring the space of two - * segment descriptor entry in the global descriptor table being 128-bit. Ending with a 32 bit reserved - * field that has to be used, because the segment descriptor needs to be big enough for two segment - * descriptor entries. - * - 8 bit Access Type - * - 4 bit Flags - * - 64 bit Base Address - * - 20 bit Limit - * - * @param flags Copies the bits set from the given data into the individual components of a segment - * descriptor. - */ - explicit segment_descriptor_extension(uint128_t flags); - - /** - * @brief Constructor. - * - * @param access_byte, flags, base, limit Copies the bits set from the given data into the individual components of - * a segment descriptor. - */ - segment_descriptor_extension(access_byte access_byte, gdt_flags flags, uint64_t base, std::bitset<20U> limit); - - /** - * @brief Returns the underlying base segment descriptor, being the first part of the segment descriptor consisting - * of two entries in the global descriptor table. - */ - auto get_first_gdt_entry() const -> segment_descriptor_base; - - /** - * @brief Returns the underlying extension to the segment descriptor, being the second part of the segment - * descriptor consiting of two entries in the global descriptor table. - */ - auto get_second_gdt_entry() const -> uint64_t; - - /** - * @brief Allows to compare the underlying bits of two instances. - * - * @param other Other instance that we want to compare with. - * @return Whether the underlying set bits of both types are the same. - */ - auto operator==(segment_descriptor_extension const & other) const -> bool = default; - - private: - // The order in private variables starts for the first variable being the rightmost bit. - segment_descriptor_base _base = {}; ///< Base Segment Descriptor representing single entry in GDT (0 - 63) - uint32_t _base_3 = {}; ///< Third part of the base field (63 - 95) - uint32_t : 32; ///< Reserved field used to ensure this struct is 128 bits big (96 - 127) - }; -} // namespace teachos::arch::context_switching::segment_descriptor_table - -#endif // TEACHOS_ARCH_X86_64_CONTEXT_SWITCHING_SEGMENT_DESCRIPTOR_TABLE_SEGMENT_DESCRIPTOR_EXTENSION_HPP diff --git a/arch/x86_64/include/arch/context_switching/segment_descriptor_table/segment_descriptor_type.hpp b/arch/x86_64/include/arch/context_switching/segment_descriptor_table/segment_descriptor_type.hpp deleted file mode 100644 index 8770b81..0000000 --- a/arch/x86_64/include/arch/context_switching/segment_descriptor_table/segment_descriptor_type.hpp +++ /dev/null @@ -1,27 +0,0 @@ -#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/include/arch/context_switching/segment_descriptor_table/task_state_segment.hpp b/arch/x86_64/include/arch/context_switching/segment_descriptor_table/task_state_segment.hpp deleted file mode 100644 index d4aa5e8..0000000 --- a/arch/x86_64/include/arch/context_switching/segment_descriptor_table/task_state_segment.hpp +++ /dev/null @@ -1,32 +0,0 @@ -#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/include/arch/context_switching/syscall/main.hpp b/arch/x86_64/include/arch/context_switching/syscall/main.hpp deleted file mode 100644 index 59adc13..0000000 --- a/arch/x86_64/include/arch/context_switching/syscall/main.hpp +++ /dev/null @@ -1,88 +0,0 @@ -#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/include/arch/context_switching/syscall/syscall_enable.hpp b/arch/x86_64/include/arch/context_switching/syscall/syscall_enable.hpp deleted file mode 100644 index 8cb468a..0000000 --- a/arch/x86_64/include/arch/context_switching/syscall/syscall_enable.hpp +++ /dev/null @@ -1,18 +0,0 @@ -#ifndef TEACHOS_ARCH_X86_64_CONTEXT_SWITCHING_SYSCALL_SYSCALL_ENABLE_HPP -#define TEACHOS_ARCH_X86_64_CONTEXT_SWITCHING_SYSCALL_SYSCALL_ENABLE_HPP - -namespace teachos::arch::context_switching::syscall -{ - /** - * @brief Enables and sets up internal CPU registers to allow for syscall to function correctly and switch context - * temporarily back to the kernel level. - * - * @note Configures the Model Specific Register required by syscall (LSTAR, FMASK, STAR) with the correct values so - * that the syscall_handler is called and sets the System Call Extension bit on the EFER flags to allow for syscall - * to be used. - */ - auto enable_syscall() -> void; - -} // namespace teachos::arch::context_switching::syscall - -#endif // TEACHOS_ARCH_X86_64_CONTEXT_SWITCHING_SYSCALL_SYSCALL_ENABLE_HPP diff --git a/arch/x86_64/include/arch/context_switching/syscall/syscall_handler.hpp b/arch/x86_64/include/arch/context_switching/syscall/syscall_handler.hpp deleted file mode 100644 index 2e7bcd1..0000000 --- a/arch/x86_64/include/arch/context_switching/syscall/syscall_handler.hpp +++ /dev/null @@ -1,18 +0,0 @@ -#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/include/arch/exception_handling/assert.hpp b/arch/x86_64/include/arch/exception_handling/assert.hpp deleted file mode 100644 index 1286768..0000000 --- a/arch/x86_64/include/arch/exception_handling/assert.hpp +++ /dev/null @@ -1,17 +0,0 @@ -#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/include/arch/exception_handling/panic.hpp b/arch/x86_64/include/arch/exception_handling/panic.hpp deleted file mode 100644 index 6a2404c..0000000 --- a/arch/x86_64/include/arch/exception_handling/panic.hpp +++ /dev/null @@ -1,23 +0,0 @@ -#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/include/arch/interrupt_handling/generic_interrupt_handler.hpp b/arch/x86_64/include/arch/interrupt_handling/generic_interrupt_handler.hpp deleted file mode 100644 index 15b35c1..0000000 --- a/arch/x86_64/include/arch/interrupt_handling/generic_interrupt_handler.hpp +++ /dev/null @@ -1,34 +0,0 @@ -#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/include/arch/kernel/cpu/call.hpp b/arch/x86_64/include/arch/kernel/cpu/call.hpp deleted file mode 100644 index 3c43304..0000000 --- a/arch/x86_64/include/arch/kernel/cpu/call.hpp +++ /dev/null @@ -1,30 +0,0 @@ -#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/include/arch/kernel/cpu/gdtr.hpp b/arch/x86_64/include/arch/kernel/cpu/gdtr.hpp deleted file mode 100644 index 68b950d..0000000 --- a/arch/x86_64/include/arch/kernel/cpu/gdtr.hpp +++ /dev/null @@ -1,27 +0,0 @@ -#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/include/arch/kernel/cpu/idtr.hpp b/arch/x86_64/include/arch/kernel/cpu/idtr.hpp deleted file mode 100644 index cb800d0..0000000 --- a/arch/x86_64/include/arch/kernel/cpu/idtr.hpp +++ /dev/null @@ -1,27 +0,0 @@ -#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/include/arch/kernel/cpu/if.hpp b/arch/x86_64/include/arch/kernel/cpu/if.hpp deleted file mode 100644 index 48707dc..0000000 --- a/arch/x86_64/include/arch/kernel/cpu/if.hpp +++ /dev/null @@ -1,21 +0,0 @@ -#ifndef TEACHOS_ARCH_X86_64_KERNEL_CPU_IF_HPP -#define TEACHOS_ARCH_X86_64_KERNEL_CPU_IF_HPP - -namespace teachos::arch::kernel::cpu -{ - /** - * @brief Sets the interrupt flag (IF) in the EFLAGS register. - * This allows the processor to respond to maskable hardware interrupts. - */ - auto set_interrupt_flag() -> void; - - /** - * @brief Clears the interrupt flag (IF) in the EFLAGS register. - * This will stop the processor to respond to maskable hardware interrupts and needs to be done before changing the - * Interrupt Descriptor Table with lidt. - */ - auto clear_interrupt_flag() -> void; - -} // namespace teachos::arch::kernel::cpu - -#endif // TEACHOS_ARCH_X86_64_KERNEL_CPU_IF_HPP diff --git a/arch/x86_64/include/arch/kernel/cpu/msr.hpp b/arch/x86_64/include/arch/kernel/cpu/msr.hpp deleted file mode 100644 index 99d6378..0000000 --- a/arch/x86_64/include/arch/kernel/cpu/msr.hpp +++ /dev/null @@ -1,64 +0,0 @@ -#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/include/arch/kernel/cpu/segment_register.hpp b/arch/x86_64/include/arch/kernel/cpu/segment_register.hpp deleted file mode 100644 index a236452..0000000 --- a/arch/x86_64/include/arch/kernel/cpu/segment_register.hpp +++ /dev/null @@ -1,97 +0,0 @@ -#ifndef TEACHOS_ARCH_X86_64_KERNEL_CPU_SEGMENT_REGISTER_HPP -#define TEACHOS_ARCH_X86_64_KERNEL_CPU_SEGMENT_REGISTER_HPP - -#include "arch/context_switching/interrupt_descriptor_table/segment_selector.hpp" - -namespace teachos::arch::kernel::cpu -{ - /** - * @brief Clear all Data Segment registers (DS / ES / FS / GS). - */ - auto reload_data_segment_registers() -> void; - - /** - * @brief Updates the value of the Data Segment Register (DS), Extra Segment Register (ES), Thread-Local Storage - * Registers (FS / GS). - * - * @note The Stack Segment Register (SS) value should also be updated, but the value can not be directly set in - * comparsion to the other registers. This is the case because the register is used for stack management and can not - * be directly changed, instead this has to be done by a special instruction. Therefore - * validate_data_segment_registers should only be called after set_code_segment_register has been called as well. - * - * @param data_segment Value that should be loaded into the registers. - */ - auto set_data_segment_registers(context_switching::interrupt_descriptor_table::segment_selector data_segment) -> void; - - /** - * @brief Returns the Segment Selector pointing to the Code Segment that has been loaded into the Code Segment - * Register (CS). - * - * @note The CS register can not be directly changed, instead a Far Return has to be executed to change it - * - * @return Segment Selector pointing to the currently loaded Code Segment. - */ - [[gnu::section(".user_text")]] - auto read_code_segment_register() -> context_switching::interrupt_descriptor_table::segment_selector; - - /** - * @brief Validates that all Data Segment Registers (DS / ES / FS / GS / SS) are the same as the given Data Segment - * and asserts and stops the application if they are not. - * - * @note This is only the case after set_code_segment_register has been executed as well, because it makes a far - * return that updates the SS register. - * - * @param data_segment Value that should be loaded into all Data Segment Registers. - */ - auto validate_data_segment_registers(context_switching::interrupt_descriptor_table::segment_selector data_segment) - -> void; - - /** - * @brief Validates that the Code Segment Register (CS) is the same as the given Code Segment - * and asserts and stops the application if they are not. - * - * @param code_segment Value that should be loaded into the Code Segment Register. - */ - auto validate_code_segment_register(context_switching::interrupt_descriptor_table::segment_selector code_segment) - -> void; - - /** - * @brief Simply forwards the call to validate_data_segment_registers and validate_code_segment_register and ensures - * that all Segment Registers, have been configured correctly. - * - * @note If all Segment Register have been set correctly the Context Switch using the set_code_segment_register method - * was successfull and the Privilege Level has been changed. - * - * @param data_segment Value that should be loaded into all Data Segment Registers. - * @param code_segment Value that should be loaded into the Code Segment Register. - */ - auto validate_segment_registers(context_switching::interrupt_descriptor_table::segment_selector data_segment, - context_switching::interrupt_descriptor_table::segment_selector code_segment) -> void; - - /** - * @brief Sets the value of the Code Segment Register (CS), this is achieved using a Far Return. - * - * @note The Far Return used by this method, will cause the context to switch, because we are changing from the - * current Code Segment and it's associated Privilege Level to another Code Segment. The given method will then be - * called in the new context and it should be possible to call validate_segment_registers, with the same values - * without assertions if the switch was successful. - * - * To achieve this Far Return we call IRETQ, which expects the stack to be defined a certain way to achieve that we: - * 1. Push the Data Segment Selector - * 2. Push the current Stack Pointer - * 3. Push Eflags - * 4. Push Code Segment Selector - * 5. Push Return Address - * - * @param data_segment Data Segment that should be loaded into the SS register. - * @param code_segment Code Segment that should be loaded into the CS register. - * @param address Function that we want to call in the new context created by the given Code Segment. - */ - [[gnu::naked]] - auto set_code_segment_register(context_switching::interrupt_descriptor_table::segment_selector data_segment, - context_switching::interrupt_descriptor_table::segment_selector code_segment, - uint64_t address) -> void; - -} // namespace teachos::arch::kernel::cpu - -#endif // TEACHOS_ARCH_X86_64_KERNEL_CPU_SEGMENT_REGISTER_HPP diff --git a/arch/x86_64/include/arch/kernel/cpu/tr.hpp b/arch/x86_64/include/arch/kernel/cpu/tr.hpp deleted file mode 100644 index 7c856f1..0000000 --- a/arch/x86_64/include/arch/kernel/cpu/tr.hpp +++ /dev/null @@ -1,24 +0,0 @@ -#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/include/arch/kernel/halt.hpp b/arch/x86_64/include/arch/kernel/halt.hpp deleted file mode 100644 index 377acc0..0000000 --- a/arch/x86_64/include/arch/kernel/halt.hpp +++ /dev/null @@ -1,13 +0,0 @@ -#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/include/arch/kernel/main.hpp b/arch/x86_64/include/arch/kernel/main.hpp deleted file mode 100644 index a13e5f4..0000000 --- a/arch/x86_64/include/arch/kernel/main.hpp +++ /dev/null @@ -1,13 +0,0 @@ -#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/include/arch/memory/allocator/area_frame_allocator.hpp b/arch/x86_64/include/arch/memory/allocator/area_frame_allocator.hpp deleted file mode 100644 index a86c9b7..0000000 --- a/arch/x86_64/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/include/arch/memory/allocator/concept.hpp b/arch/x86_64/include/arch/memory/allocator/concept.hpp deleted file mode 100644 index 2d3f4ae..0000000 --- a/arch/x86_64/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/include/arch/memory/allocator/tiny_frame_allocator.hpp b/arch/x86_64/include/arch/memory/allocator/tiny_frame_allocator.hpp deleted file mode 100644 index 1ceb74d..0000000 --- a/arch/x86_64/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 - { - 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/include/arch/memory/heap/bump_allocator.hpp b/arch/x86_64/include/arch/memory/heap/bump_allocator.hpp deleted file mode 100644 index 011f45c..0000000 --- a/arch/x86_64/include/arch/memory/heap/bump_allocator.hpp +++ /dev/null @@ -1,48 +0,0 @@ -#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/include/arch/memory/heap/global_heap_allocator.hpp b/arch/x86_64/include/arch/memory/heap/global_heap_allocator.hpp deleted file mode 100644 index c98c130..0000000 --- a/arch/x86_64/include/arch/memory/heap/global_heap_allocator.hpp +++ /dev/null @@ -1,118 +0,0 @@ -#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/include/arch/memory/heap/heap_allocator.hpp b/arch/x86_64/include/arch/memory/heap/heap_allocator.hpp deleted file mode 100644 index 420a1d3..0000000 --- a/arch/x86_64/include/arch/memory/heap/heap_allocator.hpp +++ /dev/null @@ -1,45 +0,0 @@ -#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/include/arch/memory/heap/linked_list_allocator.hpp b/arch/x86_64/include/arch/memory/heap/linked_list_allocator.hpp deleted file mode 100644 index bbbad19..0000000 --- a/arch/x86_64/include/arch/memory/heap/linked_list_allocator.hpp +++ /dev/null @@ -1,120 +0,0 @@ -#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/include/arch/memory/heap/memory_block.hpp b/arch/x86_64/include/arch/memory/heap/memory_block.hpp deleted file mode 100644 index 9d1fb02..0000000 --- a/arch/x86_64/include/arch/memory/heap/memory_block.hpp +++ /dev/null @@ -1,39 +0,0 @@ -#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/include/arch/memory/heap/user_heap_allocator.hpp b/arch/x86_64/include/arch/memory/heap/user_heap_allocator.hpp deleted file mode 100644 index 3b47f15..0000000 --- a/arch/x86_64/include/arch/memory/heap/user_heap_allocator.hpp +++ /dev/null @@ -1,149 +0,0 @@ -#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/include/arch/memory/main.hpp b/arch/x86_64/include/arch/memory/main.hpp deleted file mode 100644 index d51815f..0000000 --- a/arch/x86_64/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/include/arch/memory/multiboot/elf_symbols_section.hpp b/arch/x86_64/include/arch/memory/multiboot/elf_symbols_section.hpp deleted file mode 100644 index 348c159..0000000 --- a/arch/x86_64/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/include/arch/memory/multiboot/reader.hpp b/arch/x86_64/include/arch/memory/multiboot/reader.hpp deleted file mode 100644 index c5464cb..0000000 --- a/arch/x86_64/include/arch/memory/multiboot/reader.hpp +++ /dev/null @@ -1,58 +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/include/arch/memory/paging/active_page_table.hpp b/arch/x86_64/include/arch/memory/paging/active_page_table.hpp deleted file mode 100644 index f68d8b6..0000000 --- a/arch/x86_64/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. - */ - 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/include/arch/memory/paging/inactive_page_table.hpp b/arch/x86_64/include/arch/memory/paging/inactive_page_table.hpp deleted file mode 100644 index 8d96740..0000000 --- a/arch/x86_64/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/include/arch/memory/paging/kernel_mapper.hpp b/arch/x86_64/include/arch/memory/paging/kernel_mapper.hpp deleted file mode 100644 index 3afb54b..0000000 --- a/arch/x86_64/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{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/include/arch/memory/paging/page_entry.hpp b/arch/x86_64/include/arch/memory/paging/page_entry.hpp deleted file mode 100644 index 8147c5c..0000000 --- a/arch/x86_64/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/include/arch/memory/paging/page_table.hpp b/arch/x86_64/include/arch/memory/paging/page_table.hpp deleted file mode 100644 index b972337..0000000 --- a/arch/x86_64/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 -{ - 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/include/arch/memory/paging/temporary_page.hpp b/arch/x86_64/include/arch/memory/paging/temporary_page.hpp deleted file mode 100644 index d0d7781..0000000 --- a/arch/x86_64/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/include/arch/memory/paging/virtual_page.hpp b/arch/x86_64/include/arch/memory/paging/virtual_page.hpp deleted file mode 100644 index a6c8c39..0000000 --- a/arch/x86_64/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==(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/include/arch/user/main.hpp b/arch/x86_64/include/arch/user/main.hpp deleted file mode 100644 index c168a1f..0000000 --- a/arch/x86_64/include/arch/user/main.hpp +++ /dev/null @@ -1,16 +0,0 @@ -#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/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 diff --git a/arch/x86_64/src/context_switching/interrupt_descriptor_table/gate_descriptor.cpp b/arch/x86_64/src/context_switching/interrupt_descriptor_table/gate_descriptor.cpp deleted file mode 100644 index 28f289c..0000000 --- a/arch/x86_64/src/context_switching/interrupt_descriptor_table/gate_descriptor.cpp +++ /dev/null @@ -1,24 +0,0 @@ -#include "arch/context_switching/interrupt_descriptor_table/gate_descriptor.hpp" - -namespace teachos::arch::context_switching::interrupt_descriptor_table -{ - gate_descriptor::gate_descriptor(uint128_t flags) - : _offset_1(flags) - , _selector(flags >> 19U, flags >> 16U) - , _ist(flags >> 32U) - , _flags(flags >> 40U) - , _offset_2(flags >> 48U) - { - // Nothing to do. - } - - gate_descriptor::gate_descriptor(segment_selector selector, ist_offset ist, idt_flags flags, uint64_t offset) - : _offset_1(offset) - , _selector(selector) - , _ist(ist) - , _flags(flags) - , _offset_2(offset >> 16U) - { - // Nothing to do. - } -} // namespace teachos::arch::context_switching::interrupt_descriptor_table diff --git a/arch/x86_64/src/context_switching/interrupt_descriptor_table/idt_flags.cpp b/arch/x86_64/src/context_switching/interrupt_descriptor_table/idt_flags.cpp deleted file mode 100644 index d36a4c1..0000000 --- a/arch/x86_64/src/context_switching/interrupt_descriptor_table/idt_flags.cpp +++ /dev/null @@ -1,17 +0,0 @@ -#include "arch/context_switching/interrupt_descriptor_table/idt_flags.hpp" - -namespace teachos::arch::context_switching::interrupt_descriptor_table -{ - idt_flags::idt_flags(uint8_t flags) - : _flags(flags) - { - // Nothing to do. - } - - auto idt_flags::contains_flags(std::bitset<8U> other) const -> bool - { - return (std::bitset<8U>{_flags} & other) == other; - } - - auto idt_flags::operator|=(std::bitset<8U> other) -> void { _flags |= other.to_ulong(); } -} // namespace teachos::arch::context_switching::interrupt_descriptor_table diff --git a/arch/x86_64/src/context_switching/interrupt_descriptor_table/interrupt_descriptor_table.cpp b/arch/x86_64/src/context_switching/interrupt_descriptor_table/interrupt_descriptor_table.cpp deleted file mode 100644 index 7aa0859..0000000 --- a/arch/x86_64/src/context_switching/interrupt_descriptor_table/interrupt_descriptor_table.cpp +++ /dev/null @@ -1,53 +0,0 @@ -#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/src/context_switching/interrupt_descriptor_table/interrupt_descriptor_table_pointer.cpp b/arch/x86_64/src/context_switching/interrupt_descriptor_table/interrupt_descriptor_table_pointer.cpp deleted file mode 100644 index 7bcbae6..0000000 --- a/arch/x86_64/src/context_switching/interrupt_descriptor_table/interrupt_descriptor_table_pointer.cpp +++ /dev/null @@ -1,13 +0,0 @@ -#include "arch/context_switching/interrupt_descriptor_table/interrupt_descriptor_table_pointer.hpp" - -namespace teachos::arch::context_switching::interrupt_descriptor_table -{ - interrupt_descriptor_table_pointer::interrupt_descriptor_table_pointer(uint16_t table_length, - gate_descriptor * address) - : table_length(table_length) - , address(address) - { - // Nothing to do. - } - -} // namespace teachos::arch::context_switching::interrupt_descriptor_table diff --git a/arch/x86_64/src/context_switching/interrupt_descriptor_table/ist_offset.cpp b/arch/x86_64/src/context_switching/interrupt_descriptor_table/ist_offset.cpp deleted file mode 100644 index a70e75d..0000000 --- a/arch/x86_64/src/context_switching/interrupt_descriptor_table/ist_offset.cpp +++ /dev/null @@ -1,10 +0,0 @@ -#include "arch/context_switching/interrupt_descriptor_table/ist_offset.hpp" - -namespace teachos::arch::context_switching::interrupt_descriptor_table -{ - ist_offset::ist_offset(uint8_t index) - : _ist(index) - { - // Nothing to do. - } -} // namespace teachos::arch::context_switching::interrupt_descriptor_table diff --git a/arch/x86_64/src/context_switching/interrupt_descriptor_table/segment_selector.cpp b/arch/x86_64/src/context_switching/interrupt_descriptor_table/segment_selector.cpp deleted file mode 100644 index 27f0a3b..0000000 --- a/arch/x86_64/src/context_switching/interrupt_descriptor_table/segment_selector.cpp +++ /dev/null @@ -1,15 +0,0 @@ -#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/src/context_switching/main.cpp b/arch/x86_64/src/context_switching/main.cpp deleted file mode 100644 index 9539428..0000000 --- a/arch/x86_64/src/context_switching/main.cpp +++ /dev/null @@ -1,63 +0,0 @@ -#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/src/context_switching/segment_descriptor_table/access_byte.cpp b/arch/x86_64/src/context_switching/segment_descriptor_table/access_byte.cpp deleted file mode 100644 index e31e021..0000000 --- a/arch/x86_64/src/context_switching/segment_descriptor_table/access_byte.cpp +++ /dev/null @@ -1,17 +0,0 @@ -#include "arch/context_switching/segment_descriptor_table/access_byte.hpp" - -namespace teachos::arch::context_switching::segment_descriptor_table -{ - access_byte::access_byte(uint8_t flags) - : _flags(flags) - { - // Nothing to do. - } - - auto access_byte::contains_flags(std::bitset<8U> other) const -> bool - { - return (std::bitset<8U>{_flags} & other) == other; - } - - auto access_byte::operator|=(std::bitset<8U> other) -> void { _flags |= other.to_ulong(); } -} // namespace teachos::arch::context_switching::segment_descriptor_table diff --git a/arch/x86_64/src/context_switching/segment_descriptor_table/gdt_flags.cpp b/arch/x86_64/src/context_switching/segment_descriptor_table/gdt_flags.cpp deleted file mode 100644 index e444a24..0000000 --- a/arch/x86_64/src/context_switching/segment_descriptor_table/gdt_flags.cpp +++ /dev/null @@ -1,20 +0,0 @@ -#include "arch/context_switching/segment_descriptor_table/gdt_flags.hpp" - -namespace teachos::arch::context_switching::segment_descriptor_table -{ - gdt_flags::gdt_flags(uint8_t flags, std::bitset<20U> limit) - : _limit_2(limit.to_ulong() >> 16U) - , _flags(flags) - { - // Nothing to do. - } - - auto gdt_flags::contains_flags(std::bitset<4U> other) const -> bool - { - return (std::bitset<4U>{_flags} & other) == other; - } - - auto gdt_flags::get_limit() const -> std::bitset<4U> { return std::bitset<4U>{_limit_2}; } - - auto gdt_flags::operator|=(std::bitset<4U> other) -> void { _flags |= other.to_ulong(); } -} // namespace teachos::arch::context_switching::segment_descriptor_table diff --git a/arch/x86_64/src/context_switching/segment_descriptor_table/global_descriptor_table.cpp b/arch/x86_64/src/context_switching/segment_descriptor_table/global_descriptor_table.cpp deleted file mode 100644 index bbcee31..0000000 --- a/arch/x86_64/src/context_switching/segment_descriptor_table/global_descriptor_table.cpp +++ /dev/null @@ -1,109 +0,0 @@ -#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/src/context_switching/segment_descriptor_table/global_descriptor_table_pointer.cpp b/arch/x86_64/src/context_switching/segment_descriptor_table/global_descriptor_table_pointer.cpp deleted file mode 100644 index 79088b8..0000000 --- a/arch/x86_64/src/context_switching/segment_descriptor_table/global_descriptor_table_pointer.cpp +++ /dev/null @@ -1,11 +0,0 @@ -#include "arch/context_switching/segment_descriptor_table/global_descriptor_table_pointer.hpp" - -namespace teachos::arch::context_switching::segment_descriptor_table -{ - global_descriptor_table_pointer::global_descriptor_table_pointer(uint16_t table_length, uint64_t * address) - : table_length(table_length) - , address(address) - { - // Nothing to do. - } -} // namespace teachos::arch::context_switching::segment_descriptor_table diff --git a/arch/x86_64/src/context_switching/segment_descriptor_table/segment_descriptor_base.cpp b/arch/x86_64/src/context_switching/segment_descriptor_table/segment_descriptor_base.cpp deleted file mode 100644 index 04804d9..0000000 --- a/arch/x86_64/src/context_switching/segment_descriptor_table/segment_descriptor_base.cpp +++ /dev/null @@ -1,38 +0,0 @@ -#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/src/context_switching/segment_descriptor_table/segment_descriptor_extension.cpp b/arch/x86_64/src/context_switching/segment_descriptor_table/segment_descriptor_extension.cpp deleted file mode 100644 index a28ec9b..0000000 --- a/arch/x86_64/src/context_switching/segment_descriptor_table/segment_descriptor_extension.cpp +++ /dev/null @@ -1,24 +0,0 @@ -#include "arch/context_switching/segment_descriptor_table/segment_descriptor_extension.hpp" - -namespace teachos::arch::context_switching::segment_descriptor_table -{ - segment_descriptor_extension::segment_descriptor_extension(uint128_t flags) - : _base(flags) - , _base_3(flags >> 64U) - { - // Nothing to do. - } - - segment_descriptor_extension::segment_descriptor_extension(access_byte access_byte, gdt_flags flags, uint64_t base, - std::bitset<20U> limit) - : _base(access_byte, flags, base, limit) - , _base_3(base >> 32U) - { - // Nothing to do - } - - auto segment_descriptor_extension::get_first_gdt_entry() const -> segment_descriptor_base { return _base; } - - auto segment_descriptor_extension::get_second_gdt_entry() const -> uint64_t { return _base_3; } - -} // namespace teachos::arch::context_switching::segment_descriptor_table diff --git a/arch/x86_64/src/context_switching/syscall/main.cpp b/arch/x86_64/src/context_switching/syscall/main.cpp deleted file mode 100644 index b4ab468..0000000 --- a/arch/x86_64/src/context_switching/syscall/main.cpp +++ /dev/null @@ -1,35 +0,0 @@ -#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/src/context_switching/syscall/syscall_enable.cpp b/arch/x86_64/src/context_switching/syscall/syscall_enable.cpp deleted file mode 100644 index 3c43336..0000000 --- a/arch/x86_64/src/context_switching/syscall/syscall_enable.cpp +++ /dev/null @@ -1,32 +0,0 @@ -#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/src/context_switching/syscall/syscall_handler.cpp b/arch/x86_64/src/context_switching/syscall/syscall_handler.cpp deleted file mode 100644 index 84dbe5f..0000000 --- a/arch/x86_64/src/context_switching/syscall/syscall_handler.cpp +++ /dev/null @@ -1,118 +0,0 @@ -#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/src/exception_handling/abort.cpp b/arch/x86_64/src/exception_handling/abort.cpp deleted file mode 100644 index e12e4cb..0000000 --- a/arch/x86_64/src/exception_handling/abort.cpp +++ /dev/null @@ -1,15 +0,0 @@ -#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/src/exception_handling/assert.cpp b/arch/x86_64/src/exception_handling/assert.cpp deleted file mode 100644 index b2963de..0000000 --- a/arch/x86_64/src/exception_handling/assert.cpp +++ /dev/null @@ -1,15 +0,0 @@ -#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/src/exception_handling/panic.cpp b/arch/x86_64/src/exception_handling/panic.cpp deleted file mode 100644 index 8e3802a..0000000 --- a/arch/x86_64/src/exception_handling/panic.cpp +++ /dev/null @@ -1,22 +0,0 @@ -#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/src/exception_handling/pure_virtual.cpp b/arch/x86_64/src/exception_handling/pure_virtual.cpp deleted file mode 100644 index 67772f7..0000000 --- a/arch/x86_64/src/exception_handling/pure_virtual.cpp +++ /dev/null @@ -1,6 +0,0 @@ -#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/src/interrupt_handling/generic_interrupt_handler.cpp b/arch/x86_64/src/interrupt_handling/generic_interrupt_handler.cpp deleted file mode 100644 index 9d061a8..0000000 --- a/arch/x86_64/src/interrupt_handling/generic_interrupt_handler.cpp +++ /dev/null @@ -1,13 +0,0 @@ -#include "arch/interrupt_handling/generic_interrupt_handler.hpp" - -#include "arch/video/vga/text.hpp" - -namespace teachos::arch::interrupt_handling -{ - auto generic_interrupt_handler(interrupt_frame * frame) -> void - { - (void)frame; - video::vga::text::write("An Interrupt occurred.", video::vga::text::common_attributes::green_on_black); - video::vga::text::newline(); - } -} // namespace teachos::arch::interrupt_handling diff --git a/arch/x86_64/src/kernel/cpu/call.cpp b/arch/x86_64/src/kernel/cpu/call.cpp deleted file mode 100644 index 98fa248..0000000 --- a/arch/x86_64/src/kernel/cpu/call.cpp +++ /dev/null @@ -1,9 +0,0 @@ -#include "arch/kernel/cpu/call.hpp" - -namespace teachos::arch::kernel::cpu -{ - auto call(far_pointer pointer) -> void - { - asm volatile("rex64 lcall *%[input]" : /* no output from call */ : [input] "m"(pointer)); - } -} // namespace teachos::arch::kernel::cpu diff --git a/arch/x86_64/src/kernel/cpu/gdtr.cpp b/arch/x86_64/src/kernel/cpu/gdtr.cpp deleted file mode 100644 index 74a4e1c..0000000 --- a/arch/x86_64/src/kernel/cpu/gdtr.cpp +++ /dev/null @@ -1,19 +0,0 @@ -#include "arch/kernel/cpu/gdtr.hpp" - -#include "arch/context_switching/segment_descriptor_table/global_descriptor_table_pointer.hpp" - -namespace teachos::arch::kernel::cpu -{ - auto store_global_descriptor_table() -> context_switching::segment_descriptor_table::global_descriptor_table_pointer - { - context_switching::segment_descriptor_table::global_descriptor_table_pointer current_value{}; - asm("sgdt %[output]" : [output] "=m"(current_value)); - return current_value; - } - - auto load_global_descriptor_table( - context_switching::segment_descriptor_table::global_descriptor_table_pointer const & gdt_pointer) -> void - { - asm volatile("lgdt %[input]" : /* no output from call */ : [input] "m"(gdt_pointer)); - } -} // namespace teachos::arch::kernel::cpu diff --git a/arch/x86_64/src/kernel/cpu/idtr.cpp b/arch/x86_64/src/kernel/cpu/idtr.cpp deleted file mode 100644 index 7aa20c1..0000000 --- a/arch/x86_64/src/kernel/cpu/idtr.cpp +++ /dev/null @@ -1,18 +0,0 @@ -#include "arch/kernel/cpu/idtr.hpp" - -namespace teachos::arch::kernel::cpu -{ - auto store_interrupt_descriptor_table() - -> context_switching::interrupt_descriptor_table::interrupt_descriptor_table_pointer - { - context_switching::interrupt_descriptor_table::interrupt_descriptor_table_pointer current_value{}; - asm("sidt %[output]" : [output] "=m"(current_value)); - return current_value; - } - - auto load_interrupt_descriptor_table( - context_switching::interrupt_descriptor_table::interrupt_descriptor_table_pointer const & idt_pointer) -> void - { - asm volatile("lidt %[input]" : /* no output from call */ : [input] "m"(idt_pointer)); - } -} // namespace teachos::arch::kernel::cpu diff --git a/arch/x86_64/src/kernel/cpu/if.cpp b/arch/x86_64/src/kernel/cpu/if.cpp deleted file mode 100644 index 60a90a3..0000000 --- a/arch/x86_64/src/kernel/cpu/if.cpp +++ /dev/null @@ -1,7 +0,0 @@ -namespace teachos::arch::kernel::cpu -{ - auto set_interrupt_flag() -> void { asm volatile("sti"); } - - auto clear_interrupt_flag() -> void { asm volatile("cli"); } - -} // namespace teachos::arch::kernel::cpu diff --git a/arch/x86_64/src/kernel/cpu/msr.cpp b/arch/x86_64/src/kernel/cpu/msr.cpp deleted file mode 100644 index 9c474a1..0000000 --- a/arch/x86_64/src/kernel/cpu/msr.cpp +++ /dev/null @@ -1,31 +0,0 @@ -#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/src/kernel/cpu/segment_register.cpp b/arch/x86_64/src/kernel/cpu/segment_register.cpp deleted file mode 100644 index b08c9c4..0000000 --- a/arch/x86_64/src/kernel/cpu/segment_register.cpp +++ /dev/null @@ -1,98 +0,0 @@ -#include "arch/kernel/cpu/segment_register.hpp" - -#include "arch/context_switching/interrupt_descriptor_table/segment_selector.hpp" -#include "arch/exception_handling/assert.hpp" - -namespace teachos::arch::kernel::cpu -{ - auto reload_data_segment_registers() -> void - { - asm volatile("xor %%rax, %%rax\n" - "mov %%rax, %%ss\n" - "mov %%rax, %%ds\n" - "mov %%rax, %%es\n" - "mov %%rax, %%fs\n" - "mov %%rax, %%gs\n" - : /* no output from call */ - : /* no input to call */ - : "rax"); - } - - auto set_data_segment_registers(context_switching::interrupt_descriptor_table::segment_selector data_segment) -> void - { - asm volatile("xor %%rax, %%rax\n" - "mov %[input], %%ax\n" - "mov %%rax, %%ds\n" - "mov %%rax, %%es\n" - "mov %%rax, %%fs\n" - "mov %%rax, %%gs\n" - : /* no output from call */ - : [input] "m"(data_segment) - : "rax"); - } - - auto read_code_segment_register() -> context_switching::interrupt_descriptor_table::segment_selector - { - context_switching::interrupt_descriptor_table::segment_selector current_value{}; - asm volatile("mov %%cs, %[output]" : [output] "=r"(current_value)); - return current_value; - } - - auto validate_data_segment_registers(context_switching::interrupt_descriptor_table::segment_selector data_segment) - -> void - { - context_switching::interrupt_descriptor_table::segment_selector ss{}; - context_switching::interrupt_descriptor_table::segment_selector ds{}; - context_switching::interrupt_descriptor_table::segment_selector es{}; - context_switching::interrupt_descriptor_table::segment_selector fs{}; - context_switching::interrupt_descriptor_table::segment_selector gs{}; - - asm volatile( - "mov %%ss, %[ss_output]\n" - "mov %%ds, %[ds_output]\n" - "mov %%es, %[es_output]\n" - "mov %%fs, %[fs_output]\n" - "mov %%gs, %[gs_output]\n" - : [ss_output] "=r"(ss), [ds_output] "=r"(ds), [es_output] "=r"(es), [fs_output] "=r"(fs), [gs_output] "=r"(gs)); - - auto result = (ss == ds && ss == es && ss == fs && ss == gs); - exception_handling::assert(result, "[Segment Register] Values in data register are not the same."); - result = (ss == data_segment); - exception_handling::assert( - result, "[Segment Register] Expected Data Segment is not the same as the value in the Stack Segment register."); - } - - auto validate_code_segment_register(context_switching::interrupt_descriptor_table::segment_selector code_segment) - -> void - { - auto const cs = read_code_segment_register(); - exception_handling::assert( - cs == code_segment, - "[Segment Register] Expected Code Segment is not the same as the value in the Code Segment register."); - } - - auto validate_segment_registers(context_switching::interrupt_descriptor_table::segment_selector data_segment, - context_switching::interrupt_descriptor_table::segment_selector code_segment) -> void - { - validate_data_segment_registers(data_segment); - validate_code_segment_register(code_segment); - } - - auto set_code_segment_register(context_switching::interrupt_descriptor_table::segment_selector data_segment, - context_switching::interrupt_descriptor_table::segment_selector code_segment, - uint64_t address) -> void - { - asm volatile("mov %%rsp, %%rax\n" - "push %[data_segment]\n" - "push %%rax\n" - "pushfq\n" - "push %[code_segment]\n" - "mov %[return_function], %%rax\n" - "push %%rax\n" - "iretq\n" - : /* no output from call */ - : [data_segment] "m"(data_segment), [code_segment] "m"(code_segment), [return_function] "r"(address) - : "rax"); - } - -} // namespace teachos::arch::kernel::cpu diff --git a/arch/x86_64/src/kernel/cpu/tr.cpp b/arch/x86_64/src/kernel/cpu/tr.cpp deleted file mode 100644 index a435540..0000000 --- a/arch/x86_64/src/kernel/cpu/tr.cpp +++ /dev/null @@ -1,16 +0,0 @@ -#include "arch/kernel/cpu/tr.hpp" - -namespace teachos::arch::kernel::cpu -{ - auto store_task_register() -> uint16_t - { - uint16_t current_value{}; - asm("str %[output]" : [output] "=r"(current_value)); - return current_value; - } - - auto load_task_register(uint16_t gdt_offset) -> void - { - asm volatile("ltr %[input]" : /* no output from call */ : [input] "m"(gdt_offset)); - } -} // namespace teachos::arch::kernel::cpu diff --git a/arch/x86_64/src/kernel/main.cpp b/arch/x86_64/src/kernel/main.cpp deleted file mode 100644 index 43b5f90..0000000 --- a/arch/x86_64/src/kernel/main.cpp +++ /dev/null @@ -1,71 +0,0 @@ -#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/src/memory/allocator/area_frame_allocator.cpp b/arch/x86_64/src/memory/allocator/area_frame_allocator.cpp deleted file mode 100644 index a5a1b49..0000000 --- a/arch/x86_64/src/memory/allocator/area_frame_allocator.cpp +++ /dev/null @@ -1,85 +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/src/memory/allocator/tiny_frame_allocator.cpp b/arch/x86_64/src/memory/allocator/tiny_frame_allocator.cpp deleted file mode 100644 index 3cdf9c7..0000000 --- a/arch/x86_64/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/src/memory/heap/bump_allocator.cpp b/arch/x86_64/src/memory/heap/bump_allocator.cpp deleted file mode 100644 index 525f45c..0000000 --- a/arch/x86_64/src/memory/heap/bump_allocator.cpp +++ /dev/null @@ -1,54 +0,0 @@ -#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/src/memory/heap/global_heap_allocator.cpp b/arch/x86_64/src/memory/heap/global_heap_allocator.cpp deleted file mode 100644 index 35cd623..0000000 --- a/arch/x86_64/src/memory/heap/global_heap_allocator.cpp +++ /dev/null @@ -1,135 +0,0 @@ -#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/src/memory/heap/linked_list_allocator.cpp b/arch/x86_64/src/memory/heap/linked_list_allocator.cpp deleted file mode 100644 index 00ca366..0000000 --- a/arch/x86_64/src/memory/heap/linked_list_allocator.cpp +++ /dev/null @@ -1,177 +0,0 @@ -#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/src/memory/heap/memory_block.cpp b/arch/x86_64/src/memory/heap/memory_block.cpp deleted file mode 100644 index bc97bd6..0000000 --- a/arch/x86_64/src/memory/heap/memory_block.cpp +++ /dev/null @@ -1,15 +0,0 @@ -#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/src/memory/heap/user_heap_allocator.cpp b/arch/x86_64/src/memory/heap/user_heap_allocator.cpp deleted file mode 100644 index 427a68a..0000000 --- a/arch/x86_64/src/memory/heap/user_heap_allocator.cpp +++ /dev/null @@ -1,200 +0,0 @@ -#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/src/memory/main.cpp b/arch/x86_64/src/memory/main.cpp deleted file mode 100644 index 2746a71..0000000 --- a/arch/x86_64/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 - { - 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/src/memory/multiboot/elf_symbols_section.cpp b/arch/x86_64/src/memory/multiboot/elf_symbols_section.cpp deleted file mode 100644 index f5d126b..0000000 --- a/arch/x86_64/src/memory/multiboot/elf_symbols_section.cpp +++ /dev/null @@ -1,13 +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/src/memory/multiboot/reader.cpp b/arch/x86_64/src/memory/multiboot/reader.cpp deleted file mode 100644 index b05e6b3..0000000 --- a/arch/x86_64/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/src/memory/paging/active_page_table.cpp b/arch/x86_64/src/memory/paging/active_page_table.cpp deleted file mode 100644 index 0113869..0000000 --- a/arch/x86_64/src/memory/paging/active_page_table.cpp +++ /dev/null @@ -1,98 +0,0 @@ -#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/src/memory/paging/inactive_page_table.cpp b/arch/x86_64/src/memory/paging/inactive_page_table.cpp deleted file mode 100644 index 4e0610e..0000000 --- a/arch/x86_64/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/src/memory/paging/page_entry.cpp b/arch/x86_64/src/memory/paging/page_entry.cpp deleted file mode 100644 index 57045ca..0000000 --- a/arch/x86_64/src/memory/paging/page_entry.cpp +++ /dev/null @@ -1,63 +0,0 @@ -#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/src/memory/paging/page_table.cpp b/arch/x86_64/src/memory/paging/page_table.cpp deleted file mode 100644 index eb11810..0000000 --- a/arch/x86_64/src/memory/paging/page_table.cpp +++ /dev/null @@ -1,128 +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/src/memory/paging/temporary_page.cpp b/arch/x86_64/src/memory/paging/temporary_page.cpp deleted file mode 100644 index 8e73523..0000000 --- a/arch/x86_64/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/src/memory/paging/virtual_page.cpp b/arch/x86_64/src/memory/paging/virtual_page.cpp deleted file mode 100644 index d374156..0000000 --- a/arch/x86_64/src/memory/paging/virtual_page.cpp +++ /dev/null @@ -1,33 +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 < 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/src/user/main.cpp b/arch/x86_64/src/user/main.cpp deleted file mode 100644 index 8b07e4a..0000000 --- a/arch/x86_64/src/user/main.cpp +++ /dev/null @@ -1,35 +0,0 @@ -#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 dd04850c27e8bc273506f4a64bb28b7ddf111dc5 Mon Sep 17 00:00:00 2001 From: Felix Morgner Date: Thu, 24 Jul 2025 20:51:55 +0000 Subject: kapi: rework text device interface --- arch/x86_64/CMakeLists.txt | 2 +- arch/x86_64/include/x86_64/vga/text.hpp | 122 +++++++++++++------------------- arch/x86_64/kapi/cio.cpp | 16 +++++ arch/x86_64/kapi/io.cpp | 36 ---------- arch/x86_64/src/vga/text.cpp | 23 +++--- 5 files changed, 80 insertions(+), 119 deletions(-) create mode 100644 arch/x86_64/kapi/cio.cpp delete mode 100644 arch/x86_64/kapi/io.cpp (limited to 'arch/x86_64') diff --git a/arch/x86_64/CMakeLists.txt b/arch/x86_64/CMakeLists.txt index c585cbf..e8b5162 100644 --- a/arch/x86_64/CMakeLists.txt +++ b/arch/x86_64/CMakeLists.txt @@ -12,7 +12,7 @@ target_link_libraries("x86_64" PUBLIC target_sources("x86_64" PRIVATE # api::kapi implementation - "kapi/io.cpp" + "kapi/cio.cpp" "kapi/memory.cpp" "kapi/system.cpp" diff --git a/arch/x86_64/include/x86_64/vga/text.hpp b/arch/x86_64/include/x86_64/vga/text.hpp index 267eae9..f8e6f1b 100644 --- a/arch/x86_64/include/x86_64/vga/text.hpp +++ b/arch/x86_64/include/x86_64/vga/text.hpp @@ -1,9 +1,10 @@ #ifndef TEACHOS_X86_64_VIDEO_VGA_TEXT_HPP #define TEACHOS_X86_64_VIDEO_VGA_TEXT_HPP +#include "kapi/cio.hpp" + #include #include -#include namespace teachos::x86_64::vga::text { @@ -89,84 +90,57 @@ namespace teachos::x86_64::vga::text attribute{color::gray, foreground_flag::intense, color::red, background_flag::none}; } // namespace common_attributes - /** - * @brief Clear the VGA text mode buffer. - * - * @note This function also resets the text mode buffer pointer. - * - * @param attribute The attribute to "clear" the screen with. - */ - auto clear(attribute attribute = common_attributes::gray_on_black) -> void; - - /** - * @brief Enable or disable the VGA text mode cursor. - * - * @param enabled Whether or not to enable the cursors. - */ - auto cursor(bool enabled) -> void; + struct device final : teachos::cio::output_device + { + /** + * @brief Clear the VGA text mode buffer. + * + * @note This function also resets the text mode buffer pointer. + * + * @param attribute The attribute to "clear" the screen with. + */ + auto clear(attribute attribute = common_attributes::gray_on_black) -> void; - /** - * @brief Move the cursor to a new line, scrolling the buffer if necessary. - */ - auto newline() -> void; + /** + * @brief Enable or disable the VGA text mode cursor. + * + * @param enabled Whether or not to enable the cursors. + */ + auto cursor(bool enabled) -> void; - /** - * @brief Write a string of code points to the VGA text buffer. - * - * @note This function also updates the text mode buffer pointer. - * - * @param code_points A string of (8-bit) code points to write to the VGA text mode buffer. - * @param attribute The attribute to apply to the written sequence of code points. - * @see vga::text::attribute - */ - auto write(std::string_view code_points, attribute attribute) -> void; + auto write(std::string_view text) -> void override { write(text, common_attributes::green_on_black); } + auto writeln(std::string_view text) -> void override { writeln(text, common_attributes::green_on_black); } + auto write_error(std::string_view text) -> void override { write(text, common_attributes::red_on_black); } + auto writeln_error(std::string_view text) -> void override { writeln(text, common_attributes::red_on_black); } - /** - * @brief Write a single character to the VGA text buffer. - * - * @note This function also updates the text mode buffer pointer. - * - * @param code_point A code point to write to the VGA text mode buffer. - * @param attribute The attribute to apply to the written sequence of code points. - * @see vga::text::attribute - */ - auto write_char(char code_point, attribute attribute) -> void; + private: + /** + * @brief Move the cursor to a new line, scrolling the buffer if necessary. + */ + auto newline() -> void; - template - concept Integral = std::is_integral_v; + /** + * @brief Write a string of code points to the VGA text buffer. + * + * @note This function also updates the text mode buffer pointer. + * + * @param code_points A string of (8-bit) code points to write to the VGA text mode buffer. + * @param attribute The attribute to apply to the written sequence of code points. + * @see vga::text::attribute + */ + auto write(std::string_view code_points, attribute attribute) -> void; - /** - * @brief Write a integral value to the VGA text buffer. - * - * @note This function also updates the text mode buffer pointer. - * - * @param value A integral value to write to the VGA text mode buffer. - * @param attribute The attribute to apply to the written sequence of code points. - * @see vga::text::attribute - */ - template - auto write_number(T value, attribute attribute) -> void - { - T current_value = value; - T divisor = 1; - - while (current_value > 9) - { - divisor *= 10; - current_value = current_value / 10; - } - - current_value = value; - while (divisor > 0) - { - uint8_t quotient = current_value / divisor; - char ascii_digit = quotient + '0'; - - write_char(ascii_digit, attribute); - current_value %= divisor; - divisor /= 10; - } - } + /** + * @brief Write a string of code points followed by a newline to the VGA text buffer. + * + * @note This function also updates the text mode buffer pointer. + * + * @param code_points A string of (8-bit) code points to write to the VGA text mode buffer. + * @param attribute The attribute to apply to the written sequence of code points. + * @see vga::text::attribute + */ + auto writeln(std::string_view code_points, attribute attribute) -> void; + }; } // namespace teachos::x86_64::vga::text diff --git a/arch/x86_64/kapi/cio.cpp b/arch/x86_64/kapi/cio.cpp new file mode 100644 index 0000000..ac3ae39 --- /dev/null +++ b/arch/x86_64/kapi/cio.cpp @@ -0,0 +1,16 @@ +#include "kapi/cio.hpp" + +#include "x86_64/vga/text.hpp" + +namespace teachos::cio +{ + + auto static constinit vga_device = std::optional{}; + + auto init() -> void + { + vga_device.emplace(); + set_output_device(*vga_device); + } + +} // namespace teachos::cio diff --git a/arch/x86_64/kapi/io.cpp b/arch/x86_64/kapi/io.cpp deleted file mode 100644 index eab6473..0000000 --- a/arch/x86_64/kapi/io.cpp +++ /dev/null @@ -1,36 +0,0 @@ -#include "kapi/io.hpp" - -#include "x86_64/vga/text.hpp" - -namespace teachos::io -{ - - auto init() -> void - { - x86_64::vga::text::clear(); - x86_64::vga::text::cursor(false); - } - - auto print(std::string_view text) -> void - { - x86_64::vga::text::write(text, x86_64::vga::text::common_attributes::green_on_black); - } - - auto println(std::string_view text) -> void - { - x86_64::vga::text::write(text, x86_64::vga::text::common_attributes::green_on_black); - x86_64::vga::text::newline(); - } - - auto print_error(std::string_view text) -> void - { - x86_64::vga::text::write(text, x86_64::vga::text::common_attributes::red_on_black); - } - - auto println_error(std::string_view text) -> void - { - x86_64::vga::text::write(text, x86_64::vga::text::common_attributes::red_on_black); - x86_64::vga::text::newline(); - } - -} // namespace teachos::io diff --git a/arch/x86_64/src/vga/text.cpp b/arch/x86_64/src/vga/text.cpp index 5c94b84..af089fd 100644 --- a/arch/x86_64/src/vga/text.cpp +++ b/arch/x86_64/src/vga/text.cpp @@ -13,19 +13,24 @@ namespace teachos::x86_64::vga::text { namespace { - auto buffer_offset = std::ptrdiff_t{}; + auto constinit buffer_offset = std::ptrdiff_t{}; auto constexpr DEFAULT_TEXT_BUFFER_WIDTH = 80U; auto constexpr DEFAULT_TEXT_BUFFER_HEIGHT = 25U; + + auto write_char(char code_point, attribute attribute) -> void + { + vga_buffer_pointer[buffer_offset++] = std::pair{code_point, std::bit_cast(attribute)}; + }; } // namespace - auto clear(attribute attribute) -> void + auto device::clear(attribute attribute) -> void { buffer_offset = 0; std::ranges::fill_n(vga_buffer_pointer.get(), 2000, std::pair{' ', std::bit_cast(attribute)}); } - auto cursor(bool enabled) -> void + auto device::cursor(bool enabled) -> void { auto cursor_disable_byte = std::byte{!enabled} << 5; @@ -33,7 +38,7 @@ namespace teachos::x86_64::vga::text crtc::data::write(crtc::data::read() | cursor_disable_byte); } - auto newline() -> void + auto device::newline() -> void { auto current_line = buffer_offset / DEFAULT_TEXT_BUFFER_WIDTH; auto next_line = current_line + 1; @@ -51,13 +56,15 @@ namespace teachos::x86_64::vga::text } } - auto write_char(char code_point, attribute attribute) -> void + auto device::write(std::string_view code_points, attribute attribute) -> void { - vga_buffer_pointer[buffer_offset++] = std::pair{code_point, std::bit_cast(attribute)}; - }; + std::ranges::for_each(code_points, [&](auto code_point) { write_char(code_point, attribute); }); + } - auto write(std::string_view code_points, attribute attribute) -> void + auto device::writeln(std::string_view code_points, attribute attribute) -> void { std::ranges::for_each(code_points, [&](auto code_point) { write_char(code_point, attribute); }); + newline(); } + } // namespace teachos::x86_64::vga::text -- cgit v1.2.3 From 7671b1f0e4790b43bb8f8747b63cd44b7a65e10f Mon Sep 17 00:00:00 2001 From: Felix Morgner Date: Mon, 27 Oct 2025 13:58:55 +0000 Subject: x86_64: move kapi implementation to src --- arch/x86_64/CMakeLists.txt | 10 +++---- arch/x86_64/kapi/cio.cpp | 16 ---------- arch/x86_64/kapi/memory.cpp | 66 ----------------------------------------- arch/x86_64/kapi/system.cpp | 12 -------- arch/x86_64/src/kapi/cio.cpp | 16 ++++++++++ arch/x86_64/src/kapi/memory.cpp | 66 +++++++++++++++++++++++++++++++++++++++++ arch/x86_64/src/kapi/system.cpp | 12 ++++++++ 7 files changed, 99 insertions(+), 99 deletions(-) delete mode 100644 arch/x86_64/kapi/cio.cpp delete mode 100644 arch/x86_64/kapi/memory.cpp delete mode 100644 arch/x86_64/kapi/system.cpp create mode 100644 arch/x86_64/src/kapi/cio.cpp create mode 100644 arch/x86_64/src/kapi/memory.cpp create mode 100644 arch/x86_64/src/kapi/system.cpp (limited to 'arch/x86_64') diff --git a/arch/x86_64/CMakeLists.txt b/arch/x86_64/CMakeLists.txt index e8b5162..9be7b04 100644 --- a/arch/x86_64/CMakeLists.txt +++ b/arch/x86_64/CMakeLists.txt @@ -11,11 +11,6 @@ target_link_libraries("x86_64" PUBLIC ) target_sources("x86_64" PRIVATE - # api::kapi implementation - "kapi/cio.cpp" - "kapi/memory.cpp" - "kapi/system.cpp" - # Low-level bootstrap "src/boot/boot32.S" "src/boot/entry64.s" @@ -25,6 +20,11 @@ target_sources("x86_64" PRIVATE # CPU intrinsics "src/cpu/registers.cpp" + # api::kapi implementation + "src/kapi/cio.cpp" + "src/kapi/memory.cpp" + "src/kapi/system.cpp" + # Memory management "src/memory/mmu.cpp" "src/memory/region_allocator.cpp" diff --git a/arch/x86_64/kapi/cio.cpp b/arch/x86_64/kapi/cio.cpp deleted file mode 100644 index ac3ae39..0000000 --- a/arch/x86_64/kapi/cio.cpp +++ /dev/null @@ -1,16 +0,0 @@ -#include "kapi/cio.hpp" - -#include "x86_64/vga/text.hpp" - -namespace teachos::cio -{ - - auto static constinit vga_device = std::optional{}; - - auto init() -> void - { - vga_device.emplace(); - set_output_device(*vga_device); - } - -} // namespace teachos::cio diff --git a/arch/x86_64/kapi/memory.cpp b/arch/x86_64/kapi/memory.cpp deleted file mode 100644 index d1c1f03..0000000 --- a/arch/x86_64/kapi/memory.cpp +++ /dev/null @@ -1,66 +0,0 @@ -#include "kapi/memory.hpp" - -#include "kapi/system.hpp" - -#include "x86_64/boot/boot.hpp" -#include "x86_64/boot/ld.hpp" -#include "x86_64/memory/address.hpp" -#include "x86_64/memory/region_allocator.hpp" - -#include - -#include - -namespace teachos::memory -{ - using namespace x86_64::memory; - using namespace x86_64::boot; - - namespace - { - auto constinit is_initialized = std::atomic_flag{}; - - auto create_memory_information() -> region_allocator::memory_information - { - auto const & mbi = multiboot_information_pointer.get(); - auto map = mbi->memory_map(); - - return {std::make_pair(physical_address{&_start_physical}, physical_address{&_end_physical}), - std::make_pair(physical_address{std::bit_cast(&mbi)}, - physical_address{std::bit_cast(&mbi) + mbi->size_bytes()}), - map}; - }; - } // namespace - - auto init() -> void - { - if (is_initialized.test_and_set()) - { - system::panic("[x86_64] Memory management has already been initialized."); - } - - auto memory_map = multiboot_information_pointer->maybe_memory_map(); - if (!memory_map) - { - system::panic("[x86_64] No memory map available."); - } - - auto mem_info = create_memory_information(); - auto allocator = region_allocator{mem_info}; - - static_cast(allocator); - - // 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::memory diff --git a/arch/x86_64/kapi/system.cpp b/arch/x86_64/kapi/system.cpp deleted file mode 100644 index 2d4c3fe..0000000 --- a/arch/x86_64/kapi/system.cpp +++ /dev/null @@ -1,12 +0,0 @@ -#include "kapi/system.hpp" - -namespace teachos::system -{ - - auto halt() -> void - { - asm volatile("1: hlt\njmp 1b"); - __builtin_unreachable(); - } - -} // namespace teachos::system diff --git a/arch/x86_64/src/kapi/cio.cpp b/arch/x86_64/src/kapi/cio.cpp new file mode 100644 index 0000000..ac3ae39 --- /dev/null +++ b/arch/x86_64/src/kapi/cio.cpp @@ -0,0 +1,16 @@ +#include "kapi/cio.hpp" + +#include "x86_64/vga/text.hpp" + +namespace teachos::cio +{ + + auto static constinit vga_device = std::optional{}; + + auto init() -> void + { + vga_device.emplace(); + set_output_device(*vga_device); + } + +} // namespace teachos::cio diff --git a/arch/x86_64/src/kapi/memory.cpp b/arch/x86_64/src/kapi/memory.cpp new file mode 100644 index 0000000..d1c1f03 --- /dev/null +++ b/arch/x86_64/src/kapi/memory.cpp @@ -0,0 +1,66 @@ +#include "kapi/memory.hpp" + +#include "kapi/system.hpp" + +#include "x86_64/boot/boot.hpp" +#include "x86_64/boot/ld.hpp" +#include "x86_64/memory/address.hpp" +#include "x86_64/memory/region_allocator.hpp" + +#include + +#include + +namespace teachos::memory +{ + using namespace x86_64::memory; + using namespace x86_64::boot; + + namespace + { + auto constinit is_initialized = std::atomic_flag{}; + + auto create_memory_information() -> region_allocator::memory_information + { + auto const & mbi = multiboot_information_pointer.get(); + auto map = mbi->memory_map(); + + return {std::make_pair(physical_address{&_start_physical}, physical_address{&_end_physical}), + std::make_pair(physical_address{std::bit_cast(&mbi)}, + physical_address{std::bit_cast(&mbi) + mbi->size_bytes()}), + map}; + }; + } // namespace + + auto init() -> void + { + if (is_initialized.test_and_set()) + { + system::panic("[x86_64] Memory management has already been initialized."); + } + + auto memory_map = multiboot_information_pointer->maybe_memory_map(); + if (!memory_map) + { + system::panic("[x86_64] No memory map available."); + } + + auto mem_info = create_memory_information(); + auto allocator = region_allocator{mem_info}; + + static_cast(allocator); + + // 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::memory diff --git a/arch/x86_64/src/kapi/system.cpp b/arch/x86_64/src/kapi/system.cpp new file mode 100644 index 0000000..2d4c3fe --- /dev/null +++ b/arch/x86_64/src/kapi/system.cpp @@ -0,0 +1,12 @@ +#include "kapi/system.hpp" + +namespace teachos::system +{ + + auto halt() -> void + { + asm volatile("1: hlt\njmp 1b"); + __builtin_unreachable(); + } + +} // namespace teachos::system -- cgit v1.2.3 From 6434de8ff75a9143847ef529bc209790ac4909b3 Mon Sep 17 00:00:00 2001 From: Felix Morgner Date: Wed, 29 Oct 2025 11:09:42 +0100 Subject: kapi: move frame and address to KAPI --- arch/x86_64/include/x86_64/boot/boot.hpp | 26 +++--- arch/x86_64/include/x86_64/boot/ld.hpp | 4 +- arch/x86_64/include/x86_64/cpu/registers.hpp | 4 +- arch/x86_64/include/x86_64/device_io/port_io.hpp | 4 +- arch/x86_64/include/x86_64/memory/address.hpp | 47 ----------- arch/x86_64/include/x86_64/memory/frame.hpp | 95 ---------------------- arch/x86_64/include/x86_64/memory/mmu.hpp | 6 +- .../include/x86_64/memory/region_allocator.hpp | 8 +- arch/x86_64/include/x86_64/vga/crtc.hpp | 8 +- arch/x86_64/include/x86_64/vga/text.hpp | 4 +- arch/x86_64/src/cpu/registers.cpp | 4 +- arch/x86_64/src/kapi/cio.cpp | 2 +- arch/x86_64/src/kapi/memory.cpp | 8 +- arch/x86_64/src/memory/mmu.cpp | 6 +- arch/x86_64/src/memory/region_allocator.cpp | 16 +--- arch/x86_64/src/vga/text.cpp | 6 +- 16 files changed, 54 insertions(+), 194 deletions(-) delete mode 100644 arch/x86_64/include/x86_64/memory/address.hpp delete mode 100644 arch/x86_64/include/x86_64/memory/frame.hpp (limited to 'arch/x86_64') diff --git a/arch/x86_64/include/x86_64/boot/boot.hpp b/arch/x86_64/include/x86_64/boot/boot.hpp index 1887e73..86f8ce3 100644 --- a/arch/x86_64/include/x86_64/boot/boot.hpp +++ b/arch/x86_64/include/x86_64/boot/boot.hpp @@ -48,18 +48,22 @@ #include -extern "C" +namespace teachos::boot::x86_64 { - /** - * @brief A pointer to the multiboot 2 information structure provided by the boot loader. - */ - extern kstd::asm_ptr multiboot_information_pointer; - - /** - * @brief A pointer to the VGA text mode buffer. - */ - extern kstd::asm_ptr> vga_buffer_pointer; -} + + extern "C" + { + /** + * @brief A pointer to the multiboot 2 information structure provided by the boot loader. + */ + extern kstd::asm_ptr multiboot_information_pointer; + + /** + * @brief A pointer to the VGA text mode buffer. + */ + extern kstd::asm_ptr> vga_buffer_pointer; + } +} // namespace teachos::boot::x86_64 #endif diff --git a/arch/x86_64/include/x86_64/boot/ld.hpp b/arch/x86_64/include/x86_64/boot/ld.hpp index 51eb23b..cf59c66 100644 --- a/arch/x86_64/include/x86_64/boot/ld.hpp +++ b/arch/x86_64/include/x86_64/boot/ld.hpp @@ -17,7 +17,7 @@ #include -namespace teachos::x86_64::boot +namespace teachos::boot::x86_64 { /** * @var _start_physical @@ -42,6 +42,6 @@ namespace teachos::x86_64::boot * @see _start_physical */ extern "C" std::byte _end_physical; -} // namespace teachos::x86_64::boot +} // namespace teachos::boot::x86_64 #endif diff --git a/arch/x86_64/include/x86_64/cpu/registers.hpp b/arch/x86_64/include/x86_64/cpu/registers.hpp index 607d559..d19acfc 100644 --- a/arch/x86_64/include/x86_64/cpu/registers.hpp +++ b/arch/x86_64/include/x86_64/cpu/registers.hpp @@ -3,7 +3,7 @@ #include -namespace teachos::x86_64::cpu +namespace teachos::cpu::x86_64 { /** @@ -67,6 +67,6 @@ namespace teachos::x86_64::cpu */ auto set_cr0_bit(cr0_flags flag) -> void; -} // namespace teachos::x86_64::cpu +} // namespace teachos::cpu::x86_64 #endif \ No newline at end of file diff --git a/arch/x86_64/include/x86_64/device_io/port_io.hpp b/arch/x86_64/include/x86_64/device_io/port_io.hpp index 4cf0b65..352a4d0 100644 --- a/arch/x86_64/include/x86_64/device_io/port_io.hpp +++ b/arch/x86_64/include/x86_64/device_io/port_io.hpp @@ -5,7 +5,7 @@ #include #include -namespace teachos::x86_64::io +namespace teachos::io::x86_64 { /** * @brief An I/O port of a given size at a given address. @@ -127,6 +127,6 @@ namespace teachos::x86_64::io } }; -} // namespace teachos::x86_64::io +} // namespace teachos::io::x86_64 #endif \ No newline at end of file diff --git a/arch/x86_64/include/x86_64/memory/address.hpp b/arch/x86_64/include/x86_64/memory/address.hpp deleted file mode 100644 index 20e9655..0000000 --- a/arch/x86_64/include/x86_64/memory/address.hpp +++ /dev/null @@ -1,47 +0,0 @@ -#ifndef TEACHOS_X86_64_MEMORY_ADDRESS_HPP -#define TEACHOS_X86_64_MEMORY_ADDRESS_HPP - -#include -#include -#include -#include - -namespace teachos::x86_64::memory -{ - - enum struct address_type : bool - { - linear, - physical, - }; - - template - struct address - { - constexpr explicit address(std::uintptr_t value) noexcept - : m_value{value} - { - } - - explicit address(std::byte * pointer) noexcept - : m_value{std::bit_cast(pointer)} - { - } - - explicit operator std::byte *() const noexcept { return std::bit_cast(m_value); } - - auto constexpr operator<=>(address const &) const noexcept -> std::strong_ordering = default; - auto constexpr operator==(address const &) const noexcept -> bool = default; - - auto constexpr raw() const noexcept -> std::uintptr_t { return m_value; } - - private: - std::uintptr_t m_value{}; - }; - - using linear_address = address; - using physical_address = address; - -} // namespace teachos::x86_64::memory - -#endif \ No newline at end of file diff --git a/arch/x86_64/include/x86_64/memory/frame.hpp b/arch/x86_64/include/x86_64/memory/frame.hpp deleted file mode 100644 index 21565fd..0000000 --- a/arch/x86_64/include/x86_64/memory/frame.hpp +++ /dev/null @@ -1,95 +0,0 @@ -#ifndef TEACHOS_X86_64_MEMORY_FRAME_HPP -#define TEACHOS_X86_64_MEMORY_FRAME_HPP - -#include "x86_64/memory/address.hpp" - -#include -#include - -namespace teachos::x86_64::memory -{ - /** - * @brief Specific physical frame containing helper functions to determine if a specific address is in that - * physical frame or not. - */ - struct frame - { - auto static inline constexpr DEFAULT_SIZE = std::size_t{4096}; ///< Default page size of x86_84 is always 4KiB. - - /** - * @brief Defaulted constructor. - */ - constexpr frame() = default; - - /** - * @brief Constructor. - * - * @param frame_number Index number that should be assigned to this physical frame. - */ - explicit constexpr frame(std::size_t number) - : m_number(number) - { - } - - /** - * @brief Returns the physical frame the given address is contained in. - * - * @param address Physical address we want to get the corresponding physical frame for. - * @return Frame the given address is contained in. - */ - auto constexpr static containing(physical_address address) noexcept -> frame - { - return frame{address.raw() / DEFAULT_SIZE}; - } - - /** - * @brief Get the start address of this physical frame. - * - * @return Start address of the physical frame. - */ - auto constexpr start_address() const noexcept -> physical_address - { - return physical_address{m_number * DEFAULT_SIZE}; - } - - auto constexpr operator+(std::size_t offset) const noexcept -> frame { return frame{m_number + offset}; } - - /** - * @brief Post increment operator. Returns a copy of the value. - * - * @return Copy of the incremented underlying frame number. - */ - auto constexpr operator++(int) noexcept -> frame - { - auto copy = *this; - return ++copy; - } - - /** - * @brief Pre increment operator. Returns a reference to the changed value. - * - * @return Reference to the incremented underlying frame number. - */ - auto constexpr operator++() noexcept -> frame & - { - ++m_number; - return *this; - } - - /** - * @brief Defaulted equals operator. - */ - auto constexpr operator==(frame const & other) const noexcept -> bool = default; - - /** - * @brief Defaulted three-way comparison operator. - */ - auto constexpr operator<=>(frame const & other) const noexcept -> std::strong_ordering = default; - - private: - std::size_t m_number{}; ///< Index number of the current physical frame, used to distinguish it from other frames. - }; - -} // namespace teachos::x86_64::memory - -#endif // TEACHOS_ARCH_X86_64_MEMORY_ALLOCATOR_frame_HPP diff --git a/arch/x86_64/include/x86_64/memory/mmu.hpp b/arch/x86_64/include/x86_64/memory/mmu.hpp index b03ffa2..323d18a 100644 --- a/arch/x86_64/include/x86_64/memory/mmu.hpp +++ b/arch/x86_64/include/x86_64/memory/mmu.hpp @@ -1,9 +1,9 @@ #ifndef TEACHOS_X86_64_MEMORY_MMU_HPP #define TEACHOS_X86_64_MEMORY_MMU_HPP -#include "x86_64/memory/address.hpp" +#include "kapi/memory/address.hpp" -namespace teachos::x86_64::memory +namespace teachos::memory::x86_64 { /** * @brief Invalidates any translation lookaside buffer (TLB) entry for the page table the given address is cotained @@ -22,6 +22,6 @@ namespace teachos::x86_64::memory */ auto tlb_flush_all() -> void; -} // namespace teachos::x86_64::memory +} // namespace teachos::memory::x86_64 #endif \ No newline at end of file diff --git a/arch/x86_64/include/x86_64/memory/region_allocator.hpp b/arch/x86_64/include/x86_64/memory/region_allocator.hpp index 23bea10..a918195 100644 --- a/arch/x86_64/include/x86_64/memory/region_allocator.hpp +++ b/arch/x86_64/include/x86_64/memory/region_allocator.hpp @@ -1,15 +1,15 @@ #ifndef TEACHOS_X86_64_MEMORY_REGION_ALLOCATOR_HPP #define TEACHOS_X86_64_MEMORY_REGION_ALLOCATOR_HPP -#include "x86_64/memory/address.hpp" -#include "x86_64/memory/frame.hpp" +#include "kapi/memory/address.hpp" +#include "kapi/memory/frame.hpp" #include #include #include -namespace teachos::x86_64::memory +namespace teachos::memory::x86_64 { /** * @brief Allocates memory linearly using memory areas read from the multiboot2 information pointer and leaks any @@ -71,6 +71,6 @@ namespace teachos::x86_64::memory frame const m_multiboot_end; ///< The end address of the multiboot code in memory. }; -} // namespace teachos::x86_64::memory +} // namespace teachos::memory::x86_64 #endif // TEACHOS_ARCH_X86_64_MEMORY_ALLOCATOR_AREA_FRAME_ALLOCATOR_HPP diff --git a/arch/x86_64/include/x86_64/vga/crtc.hpp b/arch/x86_64/include/x86_64/vga/crtc.hpp index 4b4eac5..e5ab9f1 100644 --- a/arch/x86_64/include/x86_64/vga/crtc.hpp +++ b/arch/x86_64/include/x86_64/vga/crtc.hpp @@ -5,17 +5,17 @@ #include -namespace teachos::x86_64::vga::crtc +namespace teachos::vga::x86_64::crtc { /** * @brief The address port of the CRT Controller. */ - using address = x86_64::io::port<0x3d4, 1>; + using address = io::x86_64::port<0x3d4, 1>; /** * @brief The data port of the CRT Controller. */ - using data = x86_64::io::port<0x3d5, 1>; + using data = io::x86_64::port<0x3d5, 1>; namespace registers { @@ -30,6 +30,6 @@ namespace teachos::x86_64::vga::crtc [[maybe_unused]] auto constexpr cursor_end = std::byte{0x0b}; } // namespace registers -} // namespace teachos::x86_64::vga::crtc +} // namespace teachos::vga::x86_64::crtc #endif \ No newline at end of file diff --git a/arch/x86_64/include/x86_64/vga/text.hpp b/arch/x86_64/include/x86_64/vga/text.hpp index f8e6f1b..c193576 100644 --- a/arch/x86_64/include/x86_64/vga/text.hpp +++ b/arch/x86_64/include/x86_64/vga/text.hpp @@ -6,7 +6,7 @@ #include #include -namespace teachos::x86_64::vga::text +namespace teachos::vga::x86_64::text { /** * @brief The colors available in the standard VGA text mode. @@ -142,6 +142,6 @@ namespace teachos::x86_64::vga::text auto writeln(std::string_view code_points, attribute attribute) -> void; }; -} // namespace teachos::x86_64::vga::text +} // namespace teachos::vga::x86_64::text #endif // TEACHOS_ARCH_X86_64_VIDEO_VGA_TEXT_HPP \ No newline at end of file diff --git a/arch/x86_64/src/cpu/registers.cpp b/arch/x86_64/src/cpu/registers.cpp index 7ade98d..8646829 100644 --- a/arch/x86_64/src/cpu/registers.cpp +++ b/arch/x86_64/src/cpu/registers.cpp @@ -2,7 +2,7 @@ #include -namespace teachos::x86_64::cpu +namespace teachos::cpu::x86_64 { auto read_control_register(control_register cr) -> uint64_t { @@ -61,4 +61,4 @@ namespace teachos::x86_64::cpu auto const cr0 = read_control_register(control_register::cr0); write_control_register(control_register::cr0, static_cast::type>(flag) | cr0); } -} // namespace teachos::x86_64::cpu +} // namespace teachos::cpu::x86_64 diff --git a/arch/x86_64/src/kapi/cio.cpp b/arch/x86_64/src/kapi/cio.cpp index ac3ae39..eb0142a 100644 --- a/arch/x86_64/src/kapi/cio.cpp +++ b/arch/x86_64/src/kapi/cio.cpp @@ -5,7 +5,7 @@ namespace teachos::cio { - auto static constinit vga_device = std::optional{}; + auto static constinit vga_device = std::optional{}; auto init() -> void { diff --git a/arch/x86_64/src/kapi/memory.cpp b/arch/x86_64/src/kapi/memory.cpp index d1c1f03..55e6ba9 100644 --- a/arch/x86_64/src/kapi/memory.cpp +++ b/arch/x86_64/src/kapi/memory.cpp @@ -1,10 +1,10 @@ #include "kapi/memory.hpp" +#include "kapi/memory/frame.hpp" #include "kapi/system.hpp" #include "x86_64/boot/boot.hpp" #include "x86_64/boot/ld.hpp" -#include "x86_64/memory/address.hpp" #include "x86_64/memory/region_allocator.hpp" #include @@ -13,8 +13,10 @@ namespace teachos::memory { - using namespace x86_64::memory; - using namespace x86_64::boot; + using namespace boot::x86_64; + using namespace memory::x86_64; + + std::size_t const PLATFORM_FRAME_SIZE{4096}; namespace { diff --git a/arch/x86_64/src/memory/mmu.cpp b/arch/x86_64/src/memory/mmu.cpp index 31783fe..e573b4e 100644 --- a/arch/x86_64/src/memory/mmu.cpp +++ b/arch/x86_64/src/memory/mmu.cpp @@ -2,8 +2,10 @@ #include "x86_64/cpu/registers.hpp" -namespace teachos::x86_64::memory +namespace teachos::memory::x86_64 { + namespace cpu = cpu::x86_64; + auto tlb_flush(linear_address address) -> void { asm volatile("invlpg (%[input])" : /* no output from call */ : [input] "r"(address) : "memory"); @@ -14,4 +16,4 @@ namespace teachos::x86_64::memory auto current_value = cpu::read_control_register(cpu::control_register::cr3); cpu::write_control_register(cpu::control_register::cr3, current_value); } -} // namespace teachos::x86_64::memory +} // namespace teachos::memory::x86_64 diff --git a/arch/x86_64/src/memory/region_allocator.cpp b/arch/x86_64/src/memory/region_allocator.cpp index c9a98b4..91a5d49 100644 --- a/arch/x86_64/src/memory/region_allocator.cpp +++ b/arch/x86_64/src/memory/region_allocator.cpp @@ -1,22 +1,14 @@ -// #include "arch/memory/allocator/region_allocator.hpp" - -// #include "arch/exception_handling/assert.hpp" - -// #include -// #include -// #include - #include "x86_64/memory/region_allocator.hpp" -#include "x86_64/memory/address.hpp" -#include "x86_64/memory/frame.hpp" +#include "kapi/memory/address.hpp" +#include "kapi/memory/frame.hpp" #include #include #include -namespace teachos::x86_64::memory +namespace teachos::memory::x86_64 { namespace { @@ -92,4 +84,4 @@ namespace teachos::x86_64::memory } auto region_allocator::deallocate_frame(frame const &) -> void {} -} // namespace teachos::x86_64::memory +} // namespace teachos::memory::x86_64 diff --git a/arch/x86_64/src/vga/text.cpp b/arch/x86_64/src/vga/text.cpp index af089fd..8f78ea9 100644 --- a/arch/x86_64/src/vga/text.cpp +++ b/arch/x86_64/src/vga/text.cpp @@ -9,8 +9,10 @@ #include #include -namespace teachos::x86_64::vga::text +namespace teachos::vga::x86_64::text { + using boot::x86_64::vga_buffer_pointer; + namespace { auto constinit buffer_offset = std::ptrdiff_t{}; @@ -67,4 +69,4 @@ namespace teachos::x86_64::vga::text newline(); } -} // namespace teachos::x86_64::vga::text +} // namespace teachos::vga::x86_64::text -- cgit v1.2.3 From e7b04ef7f5da8e014e8b85fcf65448b317cca8ff Mon Sep 17 00:00:00 2001 From: Felix Morgner Date: Wed, 29 Oct 2025 11:15:50 +0100 Subject: kapi: move halt to cpu namespace --- arch/x86_64/CMakeLists.txt | 2 +- arch/x86_64/src/kapi/cpu.cpp | 12 ++++++++++++ arch/x86_64/src/kapi/system.cpp | 12 ------------ 3 files changed, 13 insertions(+), 13 deletions(-) create mode 100644 arch/x86_64/src/kapi/cpu.cpp delete mode 100644 arch/x86_64/src/kapi/system.cpp (limited to 'arch/x86_64') diff --git a/arch/x86_64/CMakeLists.txt b/arch/x86_64/CMakeLists.txt index 9be7b04..a681347 100644 --- a/arch/x86_64/CMakeLists.txt +++ b/arch/x86_64/CMakeLists.txt @@ -22,8 +22,8 @@ target_sources("x86_64" PRIVATE # api::kapi implementation "src/kapi/cio.cpp" + "src/kapi/cpu.cpp" "src/kapi/memory.cpp" - "src/kapi/system.cpp" # Memory management "src/memory/mmu.cpp" diff --git a/arch/x86_64/src/kapi/cpu.cpp b/arch/x86_64/src/kapi/cpu.cpp new file mode 100644 index 0000000..22543ee --- /dev/null +++ b/arch/x86_64/src/kapi/cpu.cpp @@ -0,0 +1,12 @@ +#include "kapi/cpu.hpp" + +namespace teachos::cpu +{ + + auto halt() -> void + { + asm volatile("1: hlt\njmp 1b"); + __builtin_unreachable(); + } + +} // namespace teachos::cpu diff --git a/arch/x86_64/src/kapi/system.cpp b/arch/x86_64/src/kapi/system.cpp deleted file mode 100644 index 2d4c3fe..0000000 --- a/arch/x86_64/src/kapi/system.cpp +++ /dev/null @@ -1,12 +0,0 @@ -#include "kapi/system.hpp" - -namespace teachos::system -{ - - auto halt() -> void - { - asm volatile("1: hlt\njmp 1b"); - __builtin_unreachable(); - } - -} // namespace teachos::system -- cgit v1.2.3 From 845a96f5e6bfbbbeba19bf3df07f0e9de53d9a88 Mon Sep 17 00:00:00 2001 From: Felix Morgner Date: Wed, 29 Oct 2025 11:40:49 +0100 Subject: kapi: export frame_allocator interface --- arch/x86_64/include/x86_64/memory/region_allocator.hpp | 7 ++++--- arch/x86_64/src/kapi/memory.cpp | 12 ++++++++++++ arch/x86_64/src/memory/region_allocator.cpp | 6 +++--- 3 files changed, 19 insertions(+), 6 deletions(-) (limited to 'arch/x86_64') diff --git a/arch/x86_64/include/x86_64/memory/region_allocator.hpp b/arch/x86_64/include/x86_64/memory/region_allocator.hpp index a918195..913b0bb 100644 --- a/arch/x86_64/include/x86_64/memory/region_allocator.hpp +++ b/arch/x86_64/include/x86_64/memory/region_allocator.hpp @@ -3,6 +3,7 @@ #include "kapi/memory/address.hpp" #include "kapi/memory/frame.hpp" +#include "kapi/memory/frame_allocator.hpp" #include @@ -15,7 +16,7 @@ namespace teachos::memory::x86_64 * @brief Allocates memory linearly using memory areas read from the multiboot2 information pointer and leaks any * deallocated frames. */ - struct region_allocator + struct region_allocator final : frame_allocator { struct memory_information { @@ -43,7 +44,7 @@ namespace teachos::memory::x86_64 * * @return next free physical frame or nullopt if none was found. */ - auto allocate_frame() -> std::optional; + auto allocate() -> std::optional override; /** * @brief Deallocates a previously allocated physical frame. @@ -54,7 +55,7 @@ namespace teachos::memory::x86_64 * * @param physical_frame Previously allocated physical_frame that should be deallocated. */ - auto deallocate_frame(frame const & physical_frame) -> void; + auto release(frame frame) -> void override; private: /** diff --git a/arch/x86_64/src/kapi/memory.cpp b/arch/x86_64/src/kapi/memory.cpp index 55e6ba9..be47941 100644 --- a/arch/x86_64/src/kapi/memory.cpp +++ b/arch/x86_64/src/kapi/memory.cpp @@ -1,6 +1,7 @@ #include "kapi/memory.hpp" #include "kapi/memory/frame.hpp" +#include "kapi/memory/frame_allocator.hpp" #include "kapi/system.hpp" #include "x86_64/boot/boot.hpp" @@ -21,6 +22,7 @@ namespace teachos::memory namespace { auto constinit is_initialized = std::atomic_flag{}; + auto constinit allocator = static_cast(nullptr); auto create_memory_information() -> region_allocator::memory_information { @@ -34,6 +36,16 @@ namespace teachos::memory }; } // namespace + auto active_allocator() -> frame_allocator & + { + if (!allocator) + { + system::panic("[x86_64] The frame allocator has not been set yet."); + } + + return *allocator; + } + auto init() -> void { if (is_initialized.test_and_set()) diff --git a/arch/x86_64/src/memory/region_allocator.cpp b/arch/x86_64/src/memory/region_allocator.cpp index 91a5d49..11ffce2 100644 --- a/arch/x86_64/src/memory/region_allocator.cpp +++ b/arch/x86_64/src/memory/region_allocator.cpp @@ -51,7 +51,7 @@ namespace teachos::memory::x86_64 } } - auto region_allocator::allocate_frame() -> std::optional + auto region_allocator::allocate() -> std::optional { if (!m_current_region) { @@ -80,8 +80,8 @@ namespace teachos::memory::x86_64 return allocated; } - return allocate_frame(); + return allocate(); } - auto region_allocator::deallocate_frame(frame const &) -> void {} + auto region_allocator::release(frame) -> void {} } // namespace teachos::memory::x86_64 -- cgit v1.2.3 From c71d18f32e06fb456bc2829d9dfc5b42b78160b0 Mon Sep 17 00:00:00 2001 From: Felix Morgner Date: Wed, 29 Oct 2025 11:53:34 +0100 Subject: x86_64: reduce using namespace use --- arch/x86_64/src/kapi/memory.cpp | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) (limited to 'arch/x86_64') diff --git a/arch/x86_64/src/kapi/memory.cpp b/arch/x86_64/src/kapi/memory.cpp index be47941..46686b6 100644 --- a/arch/x86_64/src/kapi/memory.cpp +++ b/arch/x86_64/src/kapi/memory.cpp @@ -6,6 +6,7 @@ #include "x86_64/boot/boot.hpp" #include "x86_64/boot/ld.hpp" +#include "x86_64/cpu/registers.hpp" #include "x86_64/memory/region_allocator.hpp" #include @@ -14,9 +15,6 @@ namespace teachos::memory { - using namespace boot::x86_64; - using namespace memory::x86_64; - std::size_t const PLATFORM_FRAME_SIZE{4096}; namespace @@ -24,12 +22,13 @@ namespace teachos::memory auto constinit is_initialized = std::atomic_flag{}; auto constinit allocator = static_cast(nullptr); - auto create_memory_information() -> region_allocator::memory_information + auto create_memory_information() -> x86_64::region_allocator::memory_information { - auto const & mbi = multiboot_information_pointer.get(); + auto const & mbi = boot::x86_64::multiboot_information_pointer.get(); auto map = mbi->memory_map(); - return {std::make_pair(physical_address{&_start_physical}, physical_address{&_end_physical}), + return {std::make_pair(physical_address{&boot::x86_64::_start_physical}, + physical_address{&boot::x86_64::_end_physical}), std::make_pair(physical_address{std::bit_cast(&mbi)}, physical_address{std::bit_cast(&mbi) + mbi->size_bytes()}), map}; @@ -53,19 +52,20 @@ namespace teachos::memory system::panic("[x86_64] Memory management has already been initialized."); } - auto memory_map = multiboot_information_pointer->maybe_memory_map(); + auto memory_map = boot::x86_64::multiboot_information_pointer->maybe_memory_map(); if (!memory_map) { system::panic("[x86_64] No memory map available."); } auto mem_info = create_memory_information(); - auto allocator = region_allocator{mem_info}; + auto allocator = x86_64::region_allocator{mem_info}; static_cast(allocator); - // kernel::cpu::set_cr0_bit(kernel::cpu::cr0_flags::WRITE_PROTECT); - // kernel::cpu::set_efer_bit(kernel::cpu::efer_flags::NXE); + cpu::x86_64::set_cr0_bit(cpu::x86_64::cr0_flags::WRITE_PROTECT); + + // set_efer_bit(efer_flags::NXE); // paging::kernel_mapper kernel(allocator, memory_information); // kernel.remap_kernel(); -- cgit v1.2.3 From f5aee1e1ab521d6aeb4c79f6ef276625159e202f Mon Sep 17 00:00:00 2001 From: Felix Morgner Date: Wed, 29 Oct 2025 11:58:12 +0100 Subject: x86_64: extract early boot steps --- arch/x86_64/src/kapi/memory.cpp | 33 +++++++++++++++++++-------------- 1 file changed, 19 insertions(+), 14 deletions(-) (limited to 'arch/x86_64') diff --git a/arch/x86_64/src/kapi/memory.cpp b/arch/x86_64/src/kapi/memory.cpp index 46686b6..a29a0cd 100644 --- a/arch/x86_64/src/kapi/memory.cpp +++ b/arch/x86_64/src/kapi/memory.cpp @@ -33,6 +33,23 @@ namespace teachos::memory physical_address{std::bit_cast(&mbi) + mbi->size_bytes()}), map}; }; + + auto create_early_frame_allocator() + { + auto memory_map = boot::x86_64::multiboot_information_pointer->maybe_memory_map(); + if (!memory_map) + { + system::panic("[x86_64] Failed to create early allocator, no memory map available."); + } + + return x86_64::region_allocator{create_memory_information()}; + } + + auto enable_cpu_protections() -> void + { + cpu::x86_64::set_cr0_bit(cpu::x86_64::cr0_flags::WRITE_PROTECT); + // set_efer_bit(efer_flags::NXE); + } } // namespace auto active_allocator() -> frame_allocator & @@ -52,20 +69,8 @@ namespace teachos::memory system::panic("[x86_64] Memory management has already been initialized."); } - auto memory_map = boot::x86_64::multiboot_information_pointer->maybe_memory_map(); - if (!memory_map) - { - system::panic("[x86_64] No memory map available."); - } - - auto mem_info = create_memory_information(); - auto allocator = x86_64::region_allocator{mem_info}; - - static_cast(allocator); - - cpu::x86_64::set_cr0_bit(cpu::x86_64::cr0_flags::WRITE_PROTECT); - - // set_efer_bit(efer_flags::NXE); + [[maybe_unused]] auto allocator = create_early_frame_allocator(); + enable_cpu_protections(); // paging::kernel_mapper kernel(allocator, memory_information); // kernel.remap_kernel(); -- cgit v1.2.3 From f06fc25b9a54a800c5301eec7320903380da947c Mon Sep 17 00:00:00 2001 From: Felix Morgner Date: Wed, 29 Oct 2025 12:11:09 +0100 Subject: x86_64/memory: simplify region allocator --- arch/x86_64/src/memory/region_allocator.cpp | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) (limited to 'arch/x86_64') diff --git a/arch/x86_64/src/memory/region_allocator.cpp b/arch/x86_64/src/memory/region_allocator.cpp index 11ffce2..d94e810 100644 --- a/arch/x86_64/src/memory/region_allocator.cpp +++ b/arch/x86_64/src/memory/region_allocator.cpp @@ -16,6 +16,11 @@ namespace teachos::memory::x86_64 { return frame::containing(physical_address{region.base + region.size_in_B - 1}); } + + auto constexpr falls_within(frame const & candidate, frame const & start, frame const & end) + { + return candidate >= start && candidate <= end; + } } // namespace region_allocator::region_allocator(memory_information const & mem_info) @@ -65,19 +70,17 @@ namespace teachos::memory::x86_64 { choose_next_area(); } - else if (m_next_frame >= m_kernel_start && m_next_frame <= m_kernel_end) + else if (falls_within(m_next_frame, m_kernel_start, m_kernel_end)) { m_next_frame = m_kernel_end + 1; } - else if (m_next_frame >= m_multiboot_start && m_next_frame <= m_multiboot_end) + else if (falls_within(m_next_frame, m_multiboot_start, m_multiboot_end)) { m_next_frame = m_multiboot_end + 1; } else { - auto allocated = m_next_frame; - ++m_next_frame; - return allocated; + return m_next_frame++; } return allocate(); -- cgit v1.2.3 From 40e67a6dba7837c562613016bdc8bad17d069e57 Mon Sep 17 00:00:00 2001 From: Felix Morgner Date: Wed, 29 Oct 2025 12:24:41 +0100 Subject: x86_64/memory: fix region allocator initialization During construction, the memory map was not extracted from the supplied memory information. This lead to a situation in which the allocator would never allocate any frames since it believed that there was no memory in the system. --- arch/x86_64/src/memory/region_allocator.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'arch/x86_64') diff --git a/arch/x86_64/src/memory/region_allocator.cpp b/arch/x86_64/src/memory/region_allocator.cpp index d94e810..13103c7 100644 --- a/arch/x86_64/src/memory/region_allocator.cpp +++ b/arch/x86_64/src/memory/region_allocator.cpp @@ -26,7 +26,7 @@ namespace teachos::memory::x86_64 region_allocator::region_allocator(memory_information const & mem_info) : m_next_frame{} , m_current_region{} - , m_memory_map{} + , m_memory_map{mem_info.memory_map} , m_kernel_start(frame::containing(mem_info.image_range.first)) , m_kernel_end(frame::containing(mem_info.image_range.second)) , m_multiboot_start(frame::containing(mem_info.mbi_range.first)) -- cgit v1.2.3 From 465817e3e124bafb95de0d8a030b66bc067046b6 Mon Sep 17 00:00:00 2001 From: Felix Morgner Date: Wed, 29 Oct 2025 13:35:58 +0100 Subject: x86_64/memory: fix region_allocator initialization --- arch/x86_64/src/kapi/memory.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'arch/x86_64') diff --git a/arch/x86_64/src/kapi/memory.cpp b/arch/x86_64/src/kapi/memory.cpp index a29a0cd..61d462f 100644 --- a/arch/x86_64/src/kapi/memory.cpp +++ b/arch/x86_64/src/kapi/memory.cpp @@ -29,8 +29,8 @@ namespace teachos::memory return {std::make_pair(physical_address{&boot::x86_64::_start_physical}, physical_address{&boot::x86_64::_end_physical}), - std::make_pair(physical_address{std::bit_cast(&mbi)}, - physical_address{std::bit_cast(&mbi) + mbi->size_bytes()}), + std::make_pair(physical_address{std::bit_cast(mbi)}, + physical_address{std::bit_cast(mbi) + mbi->size_bytes()}), map}; }; -- cgit v1.2.3 From c2b0bdfe3b725166f16c742cdaca7969052df382 Mon Sep 17 00:00:00 2001 From: Felix Morgner Date: Wed, 29 Oct 2025 13:38:35 +0100 Subject: x86_64/boot: add missing linker script symbols --- arch/x86_64/include/x86_64/boot/ld.hpp | 66 ++++++++++++++++++++++------------ 1 file changed, 43 insertions(+), 23 deletions(-) (limited to 'arch/x86_64') diff --git a/arch/x86_64/include/x86_64/boot/ld.hpp b/arch/x86_64/include/x86_64/boot/ld.hpp index cf59c66..104e6ee 100644 --- a/arch/x86_64/include/x86_64/boot/ld.hpp +++ b/arch/x86_64/include/x86_64/boot/ld.hpp @@ -19,29 +19,49 @@ namespace teachos::boot::x86_64 { - /** - * @var _start_physical - * @brief The first byte of the loaded kernel image. - * - * @details - * This symbols is defined in the kernel linker script and marks the start of the kernel image in physical memory. - * To use this symbol for its intended purpose, the address of it shall be taken. - * - * @see _end_physical - */ - extern "C" std::byte _start_physical; - - /** - * @var _end_physical - * @brief The first byte after the loaded kernel image. - * - * @details - * This symbols is defined in the kernel linker script and marks the end of the kernel image in physical memory. - * To use this symbol for its intended purpose, the address of it shall be taken. - * - * @see _start_physical - */ - extern "C" std::byte _end_physical; + + extern "C" + { + /** + * @brief The first byte of the loaded kernel image. + * + * @details + * This symbol is defined in the kernel linker script and marks the start of the kernel image in physical memory. + * To use this symbol for its intended purpose, the address of it shall be taken. + * + * @see _end_physical + */ + extern std::byte _start_physical; + + /** + * @brief The first byte after the loaded kernel image. + * + * @details + * This symbol is defined in the kernel linker script and marks the end of the kernel image in physical memory. + * To use this symbol for its intended purpose, the address of it shall be taken. + * + * @see _start_physical + */ + extern std::byte _end_physical; + + /** + * @brief The first byte of the loaded kernel image in the virtual address space. + * + * @details + * This symbol is defined in the kernel linker script and marks the start of the kernel image in virtual memory. + * To use this symbol for its intended purpose, the address of it shall be taken. + */ + extern std::byte _start_virtual; + + /** + * @brief The first byte after the loaded kernel image in the virtual address space. + * + * @details + * This symbol is defined in the kernel linker script and marks the end of the kernel image in virtual memory. + * To use this symbol for its intended purpose, the address of it shall be taken. + */ + extern std::byte _end_virtual; + } } // namespace teachos::boot::x86_64 #endif -- 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 --- arch/x86_64/include/x86_64/boot/boot.hpp | 3 +- arch/x86_64/include/x86_64/vga/crtc.hpp | 4 +-- arch/x86_64/include/x86_64/vga/text.hpp | 28 ++++++++++++------ .../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 +++-- arch/x86_64/src/boot/initialize_runtime.cpp | 4 ++- arch/x86_64/src/memory/region_allocator.cpp | 4 +-- arch/x86_64/src/vga/text.cpp | 4 +-- 43 files changed, 247 insertions(+), 121 deletions(-) (limited to 'arch/x86_64') diff --git a/arch/x86_64/include/x86_64/boot/boot.hpp b/arch/x86_64/include/x86_64/boot/boot.hpp index 86f8ce3..3a3620d 100644 --- a/arch/x86_64/include/x86_64/boot/boot.hpp +++ b/arch/x86_64/include/x86_64/boot/boot.hpp @@ -2,6 +2,7 @@ #define TEACHOS_X86_64_BOOT_BOOT_H #ifdef __ASSEMBLER__ +/* clang-format off */ /** * @brief The number of huge pages to map during bootstrap. */ @@ -41,7 +42,7 @@ * @brief The "L" bit in a GDT entry. */ #define GDT_LONG_MODE (1 << 53) - +/* clang-format on */ #else #include diff --git a/arch/x86_64/include/x86_64/vga/crtc.hpp b/arch/x86_64/include/x86_64/vga/crtc.hpp index e5ab9f1..be72ac4 100644 --- a/arch/x86_64/include/x86_64/vga/crtc.hpp +++ b/arch/x86_64/include/x86_64/vga/crtc.hpp @@ -22,12 +22,12 @@ namespace teachos::vga::x86_64::crtc /** * @brief The address of the Cursor Start register of the CRTC. */ - [[maybe_unused]] auto constexpr cursor_start = std::byte{0x0a}; + [[maybe_unused]] constexpr auto cursor_start = std::byte{0x0a}; /** * @brief The address of the Cursor End register of the CRTC. */ - [[maybe_unused]] auto constexpr cursor_end = std::byte{0x0b}; + [[maybe_unused]] constexpr auto cursor_end = std::byte{0x0b}; } // namespace registers } // namespace teachos::vga::x86_64::crtc diff --git a/arch/x86_64/include/x86_64/vga/text.hpp b/arch/x86_64/include/x86_64/vga/text.hpp index c193576..858350f 100644 --- a/arch/x86_64/include/x86_64/vga/text.hpp +++ b/arch/x86_64/include/x86_64/vga/text.hpp @@ -68,25 +68,25 @@ namespace teachos::vga::x86_64::text /** * @brief Make the affected cell display with a gray foreground and black background. */ - [[maybe_unused]] auto constexpr gray_on_black = + [[maybe_unused]] constexpr auto gray_on_black = attribute{color::gray, foreground_flag::none, color::black, background_flag::none}; /** * @brief Make the affected cell display with a green foreground and black background. */ - [[maybe_unused]] auto constexpr green_on_black = + [[maybe_unused]] constexpr auto green_on_black = attribute{color::green, foreground_flag::none, color::black, background_flag::none}; /** * @brief Make the affected cell display with a green foreground and black background. */ - [[maybe_unused]] auto constexpr red_on_black = + [[maybe_unused]] constexpr auto red_on_black = attribute{color::red, foreground_flag::none, color::black, background_flag::none}; /** * @brief Make the affected cell display with a white (gray + intense) foreground and red background. */ - [[maybe_unused]] auto constexpr white_on_red = + [[maybe_unused]] constexpr auto white_on_red = attribute{color::gray, foreground_flag::intense, color::red, background_flag::none}; } // namespace common_attributes @@ -108,10 +108,22 @@ namespace teachos::vga::x86_64::text */ auto cursor(bool enabled) -> void; - auto write(std::string_view text) -> void override { write(text, common_attributes::green_on_black); } - auto writeln(std::string_view text) -> void override { writeln(text, common_attributes::green_on_black); } - auto write_error(std::string_view text) -> void override { write(text, common_attributes::red_on_black); } - auto writeln_error(std::string_view text) -> void override { writeln(text, common_attributes::red_on_black); } + auto write(std::string_view text) -> void override + { + write(text, common_attributes::green_on_black); + } + auto writeln(std::string_view text) -> void override + { + writeln(text, common_attributes::green_on_black); + } + auto write_error(std::string_view text) -> void override + { + write(text, common_attributes::red_on_black); + } + auto writeln_error(std::string_view text) -> void override + { + writeln(text, common_attributes::red_on_black); + } private: /** 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 { diff --git a/arch/x86_64/src/boot/initialize_runtime.cpp b/arch/x86_64/src/boot/initialize_runtime.cpp index 9a3df0e..70172c9 100644 --- a/arch/x86_64/src/boot/initialize_runtime.cpp +++ b/arch/x86_64/src/boot/initialize_runtime.cpp @@ -16,7 +16,9 @@ extern "C" auto constructors = std::span{&__ctors_start, &__ctors_end}; auto initializers = std::span{&__init_array_start, &__init_array_end}; - auto apply_invoke = [](auto invokable) { return std::invoke(invokable); }; + auto apply_invoke = [](auto invokable) { + return std::invoke(invokable); + }; std::ranges::for_each(constructors, apply_invoke); std::ranges::for_each(initializers, apply_invoke); diff --git a/arch/x86_64/src/memory/region_allocator.cpp b/arch/x86_64/src/memory/region_allocator.cpp index 13103c7..a0579a3 100644 --- a/arch/x86_64/src/memory/region_allocator.cpp +++ b/arch/x86_64/src/memory/region_allocator.cpp @@ -12,12 +12,12 @@ namespace teachos::memory::x86_64 { namespace { - auto constexpr last_frame(multiboot2::memory_map::region const & region) + constexpr auto last_frame(multiboot2::memory_map::region const & region) { return frame::containing(physical_address{region.base + region.size_in_B - 1}); } - auto constexpr falls_within(frame const & candidate, frame const & start, frame const & end) + constexpr auto falls_within(frame const & candidate, frame const & start, frame const & end) { return candidate >= start && candidate <= end; } diff --git a/arch/x86_64/src/vga/text.cpp b/arch/x86_64/src/vga/text.cpp index 8f78ea9..8aa809f 100644 --- a/arch/x86_64/src/vga/text.cpp +++ b/arch/x86_64/src/vga/text.cpp @@ -17,8 +17,8 @@ namespace teachos::vga::x86_64::text { auto constinit buffer_offset = std::ptrdiff_t{}; - auto constexpr DEFAULT_TEXT_BUFFER_WIDTH = 80U; - auto constexpr DEFAULT_TEXT_BUFFER_HEIGHT = 25U; + constexpr auto DEFAULT_TEXT_BUFFER_WIDTH = 80U; + constexpr auto DEFAULT_TEXT_BUFFER_HEIGHT = 25U; auto write_char(char code_point, attribute attribute) -> void { -- cgit v1.2.3 From 7b9df8bec5038e0316540d2397df632fb14c9169 Mon Sep 17 00:00:00 2001 From: Felix Morgner Date: Wed, 29 Oct 2025 17:01:22 +0100 Subject: chore: configure clang-tidy --- .../include/x86_64/memory/region_allocator.hpp | 8 +++---- arch/x86_64/include/x86_64/vga/crtc.hpp | 4 ++-- arch/x86_64/include/x86_64/vga/text.hpp | 26 ++++++++++++++-------- arch/x86_64/src/cpu/registers.cpp | 4 ++-- arch/x86_64/src/kapi/cio.cpp | 1 + arch/x86_64/src/kapi/memory.cpp | 14 +++++++----- arch/x86_64/src/vga/text.cpp | 7 ++++-- 7 files changed, 40 insertions(+), 24 deletions(-) (limited to 'arch/x86_64') diff --git a/arch/x86_64/include/x86_64/memory/region_allocator.hpp b/arch/x86_64/include/x86_64/memory/region_allocator.hpp index 913b0bb..4a18a0f 100644 --- a/arch/x86_64/include/x86_64/memory/region_allocator.hpp +++ b/arch/x86_64/include/x86_64/memory/region_allocator.hpp @@ -66,10 +66,10 @@ namespace teachos::memory::x86_64 frame m_next_frame; ///< The physical_frame after the last allocated one. std::optional m_current_region; ///< The memory region currently allocated from multiboot2::memory_map m_memory_map; ///< The boot loader supplied memory map. - frame const m_kernel_start; ///< The start address of the kernel code in memory. - frame const m_kernel_end; ///< The end address of the kernel code in memory. - frame const m_multiboot_start; ///< The start address of the multiboot code in memory. - frame const m_multiboot_end; ///< The end address of the multiboot code in memory. + frame m_kernel_start; ///< The start address of the kernel code in memory. + frame m_kernel_end; ///< The end address of the kernel code in memory. + frame m_multiboot_start; ///< The start address of the multiboot code in memory. + frame m_multiboot_end; ///< The end address of the multiboot code in memory. }; } // namespace teachos::memory::x86_64 diff --git a/arch/x86_64/include/x86_64/vga/crtc.hpp b/arch/x86_64/include/x86_64/vga/crtc.hpp index be72ac4..8bdc12d 100644 --- a/arch/x86_64/include/x86_64/vga/crtc.hpp +++ b/arch/x86_64/include/x86_64/vga/crtc.hpp @@ -10,12 +10,12 @@ namespace teachos::vga::x86_64::crtc /** * @brief The address port of the CRT Controller. */ - using address = io::x86_64::port<0x3d4, 1>; + using address = io::x86_64::port<0x3d4, 1>; // NOLINT(cppcoreguidelines-avoid-magic-numbers) /** * @brief The data port of the CRT Controller. */ - using data = io::x86_64::port<0x3d5, 1>; + using data = io::x86_64::port<0x3d5, 1>; // NOLINT(cppcoreguidelines-avoid-magic-numbers) namespace registers { diff --git a/arch/x86_64/include/x86_64/vga/text.hpp b/arch/x86_64/include/x86_64/vga/text.hpp index 858350f..d8919bf 100644 --- a/arch/x86_64/include/x86_64/vga/text.hpp +++ b/arch/x86_64/include/x86_64/vga/text.hpp @@ -54,7 +54,7 @@ namespace teachos::vga::x86_64::text { color foreground_color : 3; ///< The foreground color of the cell, e.g. the color of the code point. enum foreground_flag foreground_flag : 1; ///< The foreground color modification flag of the cell. - color bacground_color : 3; ///< The background color of the cell. + color background_color : 3; ///< The background color of the cell. enum background_flag background_flag : 1; ///< The background color modification flag of the cell. }; @@ -68,26 +68,34 @@ namespace teachos::vga::x86_64::text /** * @brief Make the affected cell display with a gray foreground and black background. */ - [[maybe_unused]] constexpr auto gray_on_black = - attribute{color::gray, foreground_flag::none, color::black, background_flag::none}; + [[maybe_unused]] constexpr auto gray_on_black = attribute{.foreground_color = color::gray, + .foreground_flag = foreground_flag::none, + .background_color = color::black, + .background_flag = background_flag::none}; /** * @brief Make the affected cell display with a green foreground and black background. */ - [[maybe_unused]] constexpr auto green_on_black = - attribute{color::green, foreground_flag::none, color::black, background_flag::none}; + [[maybe_unused]] constexpr auto green_on_black = attribute{.foreground_color = color::green, + .foreground_flag = foreground_flag::none, + .background_color = color::black, + .background_flag = background_flag::none}; /** * @brief Make the affected cell display with a green foreground and black background. */ - [[maybe_unused]] constexpr auto red_on_black = - attribute{color::red, foreground_flag::none, color::black, background_flag::none}; + [[maybe_unused]] constexpr auto red_on_black = attribute{.foreground_color = color::red, + .foreground_flag = foreground_flag::none, + .background_color = color::black, + .background_flag = background_flag::none}; /** * @brief Make the affected cell display with a white (gray + intense) foreground and red background. */ - [[maybe_unused]] constexpr auto white_on_red = - attribute{color::gray, foreground_flag::intense, color::red, background_flag::none}; + [[maybe_unused]] constexpr auto white_on_red = attribute{.foreground_color = color::gray, + .foreground_flag = foreground_flag::intense, + .background_color = color::red, + .background_flag = background_flag::none}; } // namespace common_attributes struct device final : teachos::cio::output_device diff --git a/arch/x86_64/src/cpu/registers.cpp b/arch/x86_64/src/cpu/registers.cpp index 8646829..d59776b 100644 --- a/arch/x86_64/src/cpu/registers.cpp +++ b/arch/x86_64/src/cpu/registers.cpp @@ -6,7 +6,7 @@ namespace teachos::cpu::x86_64 { auto read_control_register(control_register cr) -> uint64_t { - uint64_t current_value; + uint64_t current_value{}; switch (cr) { case control_register::cr0: @@ -59,6 +59,6 @@ namespace teachos::cpu::x86_64 auto set_cr0_bit(cr0_flags flag) -> void { auto const cr0 = read_control_register(control_register::cr0); - write_control_register(control_register::cr0, static_cast::type>(flag) | cr0); + write_control_register(control_register::cr0, static_cast>(flag) | cr0); } } // namespace teachos::cpu::x86_64 diff --git a/arch/x86_64/src/kapi/cio.cpp b/arch/x86_64/src/kapi/cio.cpp index eb0142a..456477a 100644 --- a/arch/x86_64/src/kapi/cio.cpp +++ b/arch/x86_64/src/kapi/cio.cpp @@ -5,6 +5,7 @@ namespace teachos::cio { + // NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables) auto static constinit vga_device = std::optional{}; auto init() -> void diff --git a/arch/x86_64/src/kapi/memory.cpp b/arch/x86_64/src/kapi/memory.cpp index 61d462f..e05fde9 100644 --- a/arch/x86_64/src/kapi/memory.cpp +++ b/arch/x86_64/src/kapi/memory.cpp @@ -19,19 +19,23 @@ namespace teachos::memory namespace { + // NOLINTBEGIN(cppcoreguidelines-avoid-non-const-global-variables) auto constinit is_initialized = std::atomic_flag{}; auto constinit allocator = static_cast(nullptr); + // NOLINTEND(cppcoreguidelines-avoid-non-const-global-variables) auto create_memory_information() -> x86_64::region_allocator::memory_information { auto const & mbi = boot::x86_64::multiboot_information_pointer.get(); auto map = mbi->memory_map(); - return {std::make_pair(physical_address{&boot::x86_64::_start_physical}, - physical_address{&boot::x86_64::_end_physical}), - std::make_pair(physical_address{std::bit_cast(mbi)}, - physical_address{std::bit_cast(mbi) + mbi->size_bytes()}), - map}; + // NOLINTBEGIN(cppcoreguidelines-pro-bounds-pointer-arithmetic) + return {.image_range = std::make_pair(physical_address{&boot::x86_64::_start_physical}, + physical_address{&boot::x86_64::_end_physical}), + .mbi_range = std::make_pair(physical_address{std::bit_cast(mbi)}, + physical_address{std::bit_cast(mbi) + mbi->size_bytes()}), + .memory_map = map}; + // NOLINTEND(cppcoreguidelines-pro-bounds-pointer-arithmetic) }; auto create_early_frame_allocator() diff --git a/arch/x86_64/src/vga/text.cpp b/arch/x86_64/src/vga/text.cpp index 8aa809f..0e0d353 100644 --- a/arch/x86_64/src/vga/text.cpp +++ b/arch/x86_64/src/vga/text.cpp @@ -15,10 +15,12 @@ namespace teachos::vga::x86_64::text namespace { + // NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables) auto constinit buffer_offset = std::ptrdiff_t{}; constexpr auto DEFAULT_TEXT_BUFFER_WIDTH = 80U; constexpr auto DEFAULT_TEXT_BUFFER_HEIGHT = 25U; + constexpr auto CURSOR_ENABLED_BIT = 5U; auto write_char(char code_point, attribute attribute) -> void { @@ -29,12 +31,13 @@ namespace teachos::vga::x86_64::text auto device::clear(attribute attribute) -> void { buffer_offset = 0; - std::ranges::fill_n(vga_buffer_pointer.get(), 2000, std::pair{' ', std::bit_cast(attribute)}); + std::ranges::fill_n(vga_buffer_pointer.get(), DEFAULT_TEXT_BUFFER_WIDTH * DEFAULT_TEXT_BUFFER_HEIGHT, + std::pair{' ', std::bit_cast(attribute)}); } auto device::cursor(bool enabled) -> void { - auto cursor_disable_byte = std::byte{!enabled} << 5; + auto cursor_disable_byte = std::byte{!enabled} << CURSOR_ENABLED_BIT; crtc::address::write(crtc::registers::cursor_start); crtc::data::write(crtc::data::read() | cursor_disable_byte); -- 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') 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 b1143bde71bb029ac2bf7d08ba422fcdaedd56a6 Mon Sep 17 00:00:00 2001 From: Felix Morgner Date: Fri, 31 Oct 2025 07:19:16 +0100 Subject: build: enable linting --- arch/x86_64/src/boot/initialize_runtime.cpp | 4 ++-- arch/x86_64/src/memory/region_allocator.cpp | 6 +++--- arch/x86_64/src/vga/text.cpp | 6 +++--- 3 files changed, 8 insertions(+), 8 deletions(-) (limited to 'arch/x86_64') diff --git a/arch/x86_64/src/boot/initialize_runtime.cpp b/arch/x86_64/src/boot/initialize_runtime.cpp index 70172c9..46dd5e4 100644 --- a/arch/x86_64/src/boot/initialize_runtime.cpp +++ b/arch/x86_64/src/boot/initialize_runtime.cpp @@ -16,8 +16,8 @@ extern "C" auto constructors = std::span{&__ctors_start, &__ctors_end}; auto initializers = std::span{&__init_array_start, &__init_array_end}; - auto apply_invoke = [](auto invokable) { - return std::invoke(invokable); + auto apply_invoke = [](auto invokable) -> void { + std::invoke(invokable); }; std::ranges::for_each(constructors, apply_invoke); diff --git a/arch/x86_64/src/memory/region_allocator.cpp b/arch/x86_64/src/memory/region_allocator.cpp index a0579a3..8ea76c6 100644 --- a/arch/x86_64/src/memory/region_allocator.cpp +++ b/arch/x86_64/src/memory/region_allocator.cpp @@ -40,10 +40,10 @@ namespace teachos::memory::x86_64 m_current_region.reset(); auto next_area_with_free_frames = m_memory_map | std::views::filter(&multiboot2::memory_map::region::available) | - std::views::filter([next = m_next_frame](auto const & region) { return last_frame(region) >= next; }); + std::views::filter([next = m_next_frame](auto const & region) -> bool { return last_frame(region) >= next; }); - auto lowest_region_with_free_frames = - std::ranges::min_element(next_area_with_free_frames, [](auto lhs, auto rhs) { return lhs.base < rhs.base; }); + auto lowest_region_with_free_frames = std::ranges::min_element( + next_area_with_free_frames, [](auto lhs, auto rhs) -> bool { return lhs.base < rhs.base; }); if (lowest_region_with_free_frames != next_area_with_free_frames.end()) { diff --git a/arch/x86_64/src/vga/text.cpp b/arch/x86_64/src/vga/text.cpp index 0e0d353..6ecffa3 100644 --- a/arch/x86_64/src/vga/text.cpp +++ b/arch/x86_64/src/vga/text.cpp @@ -48,7 +48,7 @@ namespace teachos::vga::x86_64::text auto current_line = buffer_offset / DEFAULT_TEXT_BUFFER_WIDTH; auto next_line = current_line + 1; - if (next_line >= DEFAULT_TEXT_BUFFER_HEIGHT) + if (std::cmp_greater_equal(next_line, DEFAULT_TEXT_BUFFER_HEIGHT)) { auto begin = vga_buffer_pointer + DEFAULT_TEXT_BUFFER_WIDTH; auto end = vga_buffer_pointer + DEFAULT_TEXT_BUFFER_WIDTH * DEFAULT_TEXT_BUFFER_HEIGHT; @@ -63,12 +63,12 @@ namespace teachos::vga::x86_64::text auto device::write(std::string_view code_points, attribute attribute) -> void { - std::ranges::for_each(code_points, [&](auto code_point) { write_char(code_point, attribute); }); + std::ranges::for_each(code_points, [&](auto code_point) -> void { write_char(code_point, attribute); }); } auto device::writeln(std::string_view code_points, attribute attribute) -> void { - std::ranges::for_each(code_points, [&](auto code_point) { write_char(code_point, attribute); }); + std::ranges::for_each(code_points, [&](auto code_point) -> void { write_char(code_point, attribute); }); newline(); } -- cgit v1.2.3 From 49dcbdaaca348784d7fa05e12a06123f4dc252ec Mon Sep 17 00:00:00 2001 From: Felix Morgner Date: Tue, 18 Nov 2025 18:14:30 +0100 Subject: x86_64/memory: perform slight cleanup --- arch/x86_64/include/x86_64/memory/region_allocator.hpp | 12 ++++++++++++ arch/x86_64/src/kapi/memory.cpp | 5 +---- 2 files changed, 13 insertions(+), 4 deletions(-) (limited to 'arch/x86_64') diff --git a/arch/x86_64/include/x86_64/memory/region_allocator.hpp b/arch/x86_64/include/x86_64/memory/region_allocator.hpp index 4a18a0f..3ddc8b6 100644 --- a/arch/x86_64/include/x86_64/memory/region_allocator.hpp +++ b/arch/x86_64/include/x86_64/memory/region_allocator.hpp @@ -20,8 +20,20 @@ namespace teachos::memory::x86_64 { struct memory_information { + //! The memory range occupied by the loaded kernel image. This includes all sections that are marked as occupying + //! space in the kernel executable. The internal structure of this area is more described in a more fine-grained + //! manner by the ELF symbol information provided in the Multiboot2 information by the loader. std::pair image_range; + + //! The memory range occupied by the loader supplied Multiboot2 information structure. In general, this + //! information is allocated somewhere in the range of the loaded image, but the loader protocol does not + //! guarantee this. It is thus imperative to be able to handle the cases where the loader chooses to allocate the + //! information structure outside of the image range. std::pair mbi_range; + + //! The loader supplied map of memory regions. These include available, unavailable, and reclaimable regions. In + //! general, only frames that are located in non-reserved (as in available) regions should be allocated for page + //! storage. multiboot2::memory_map memory_map; }; diff --git a/arch/x86_64/src/kapi/memory.cpp b/arch/x86_64/src/kapi/memory.cpp index e05fde9..aa3eb58 100644 --- a/arch/x86_64/src/kapi/memory.cpp +++ b/arch/x86_64/src/kapi/memory.cpp @@ -27,15 +27,12 @@ namespace teachos::memory auto create_memory_information() -> x86_64::region_allocator::memory_information { auto const & mbi = boot::x86_64::multiboot_information_pointer.get(); - auto map = mbi->memory_map(); - // NOLINTBEGIN(cppcoreguidelines-pro-bounds-pointer-arithmetic) return {.image_range = std::make_pair(physical_address{&boot::x86_64::_start_physical}, physical_address{&boot::x86_64::_end_physical}), .mbi_range = std::make_pair(physical_address{std::bit_cast(mbi)}, physical_address{std::bit_cast(mbi) + mbi->size_bytes()}), - .memory_map = map}; - // NOLINTEND(cppcoreguidelines-pro-bounds-pointer-arithmetic) + .memory_map = mbi->memory_map()}; }; auto create_early_frame_allocator() -- cgit v1.2.3 From a5ca21e45e9c8ead0b5895771c0b2f3fe3baf96b Mon Sep 17 00:00:00 2001 From: Felix Morgner Date: Fri, 21 Nov 2025 14:53:37 +0100 Subject: x86_64: rework control register access --- arch/x86_64/CMakeLists.txt | 3 - .../include/x86_64/cpu/impl/control_registers.hpp | 196 +++++++++++++++++++++ arch/x86_64/include/x86_64/cpu/registers.hpp | 68 +------ arch/x86_64/src/cpu/registers.cpp | 64 ------- arch/x86_64/src/kapi/memory.cpp | 3 +- arch/x86_64/src/memory/mmu.cpp | 4 +- 6 files changed, 201 insertions(+), 137 deletions(-) create mode 100644 arch/x86_64/include/x86_64/cpu/impl/control_registers.hpp delete mode 100644 arch/x86_64/src/cpu/registers.cpp (limited to 'arch/x86_64') diff --git a/arch/x86_64/CMakeLists.txt b/arch/x86_64/CMakeLists.txt index a681347..9c6d818 100644 --- a/arch/x86_64/CMakeLists.txt +++ b/arch/x86_64/CMakeLists.txt @@ -17,9 +17,6 @@ target_sources("x86_64" PRIVATE "src/boot/initialize_runtime.cpp" "src/boot/multiboot.s" - # CPU intrinsics - "src/cpu/registers.cpp" - # api::kapi implementation "src/kapi/cio.cpp" "src/kapi/cpu.cpp" diff --git a/arch/x86_64/include/x86_64/cpu/impl/control_registers.hpp b/arch/x86_64/include/x86_64/cpu/impl/control_registers.hpp new file mode 100644 index 0000000..d892360 --- /dev/null +++ b/arch/x86_64/include/x86_64/cpu/impl/control_registers.hpp @@ -0,0 +1,196 @@ +#ifndef TEACHOS_X86_64_CPU_IMPL_CONTROL_REGISTERS_HPP +#define TEACHOS_X86_64_CPU_IMPL_CONTROL_REGISTERS_HPP + +#include "kapi/memory/address.hpp" + +#include +#include +#include + +namespace teachos::cpu::x86_64 +{ + + namespace impl + { + constexpr auto static cr0_asm = std::pair{"mov %%cr0, %0", "mov %0, %%cr0"}; + constexpr auto static cr2_asm = std::pair{"mov %%cr2, %0", "mov %0, %%cr2"}; + constexpr auto static cr3_asm = std::pair{"mov %%cr3, %0", "mov %0, %%cr3"}; + + template + struct control_register_with_flags + { + }; + + template + struct control_register_with_flags>> + { + using flags = ValueType; + + auto static set(ValueType value) -> void + { + auto current = Derived::read(); + current |= value; + Derived::write(current); + } + + auto static clear(ValueType value) -> void + { + auto current = Derived::read(); + current &= ~value; + Derived::write(current); + } + }; + + template + struct control_register : control_register_with_flags, ValueType> + { + [[nodiscard]] auto static read() -> ValueType + { + auto value = ValueType{}; + asm volatile((AssemblerTemplates->first) : "=r"(value)); + return value; + } + + auto static write(ValueType value) -> void + { + asm volatile((AssemblerTemplates->second) : : "r"(value)); + } + }; + + enum struct cr0_flags : uint64_t + { + //! Enable protected mode. + protection_enable = 1uz << 0, + //! Enable wait-monitoring of the coprocessor after task switching. + monitor_coprocessor = 1uz << 1, + //! Emulate floating point coprocessor. + emulation = 1uz << 2, + //! Marks that a task switch has occurred. + task_switched = 1uz << 3, + //! Marks Intel 387 DX math coprocessor as available + extension_type = 1uz << 4, + //! Numeric error handling mode. + numeric_error = 1uz << 5, + //! Disable writing to read-only marked memory. + write_protect = 1uz << 16, + //! Enable Ring-3 alignment checks + alignment_check = 1uz << 18, + //! Disable write through + not_write_through = 1uz << 29, + //! Disable caching of memory accesses + cache_disable = 1uz << 30, + //! Enable paging + paging = 1uz << 31 + }; + + constexpr auto operator|(cr0_flags lhs, cr0_flags rhs) -> cr0_flags + { + return static_cast(static_cast>(lhs) | + static_cast>(rhs)); + } + + constexpr auto operator|=(cr0_flags & lhs, cr0_flags rhs) -> cr0_flags & + { + lhs = lhs | rhs; + return lhs; + } + + constexpr auto operator&(cr0_flags lhs, cr0_flags rhs) -> cr0_flags + { + return static_cast(static_cast>(lhs) & + static_cast>(rhs)); + } + + constexpr auto operator&=(cr0_flags & lhs, cr0_flags rhs) -> cr0_flags & + { + lhs = lhs & rhs; + return lhs; + } + + constexpr auto operator~(cr0_flags lhs) -> cr0_flags + { + // NOLINTNEXTLINE(clang-analyzer-optin.core.EnumCastOutOfRange) + return static_cast(~static_cast>(lhs)); + } + + enum struct cr3_flags : std::uint64_t + { + page_level_write_through = 1uz << 0, + page_level_cache_disable = 1uz << 1, + }; + + constexpr auto operator|(cr3_flags lhs, cr3_flags rhs) -> cr3_flags + { + return static_cast(static_cast>(lhs) | + static_cast>(rhs)); + } + + constexpr auto operator|=(cr3_flags & lhs, cr3_flags rhs) -> cr3_flags & + { + lhs = lhs | rhs; + return lhs; + } + + constexpr auto operator&(cr3_flags lhs, cr3_flags rhs) -> cr3_flags + { + return static_cast(static_cast>(lhs) & + static_cast>(rhs)); + } + + constexpr auto operator&=(cr3_flags & lhs, cr3_flags rhs) -> cr3_flags & + { + lhs = lhs & rhs; + return lhs; + } + + constexpr auto operator~(cr3_flags lhs) -> cr3_flags + { + // NOLINTNEXTLINE(clang-analyzer-optin.core.EnumCastOutOfRange) + return static_cast(~static_cast>(lhs)); + } + + struct cr3_value + { + constexpr cr3_value() = default; + + constexpr cr3_value(memory::physical_address address, cr3_flags flags = static_cast(0)) + : m_flags{static_cast(flags)} + , m_address{static_cast(address.raw())} + {} + + [[nodiscard]] constexpr auto address() const -> memory::physical_address + { + return memory::physical_address{m_address}; + } + + constexpr auto address(memory::physical_address address) -> void + { + m_address = static_cast(address.raw()); + } + + [[nodiscard]] constexpr auto flags() const -> cr3_flags + { + return static_cast(m_flags); + } + + constexpr auto flags(cr3_flags flags) -> void + { + m_flags = static_cast(flags); + } + + private: + std::uint64_t : 3; + std::uint64_t m_flags : 2 {}; + std::uint64_t : 7; + std::uint64_t m_address : 52 {}; + }; + + } // namespace impl + + using cr0 = impl::control_register; + using cr2 = impl::control_register; + using cr3 = impl::control_register; + +} // namespace teachos::cpu::x86_64 + +#endif \ No newline at end of file diff --git a/arch/x86_64/include/x86_64/cpu/registers.hpp b/arch/x86_64/include/x86_64/cpu/registers.hpp index d19acfc..7f71b95 100644 --- a/arch/x86_64/include/x86_64/cpu/registers.hpp +++ b/arch/x86_64/include/x86_64/cpu/registers.hpp @@ -1,72 +1,6 @@ #ifndef TEACHOS_X86_64_CPU_REGISTERS_HPP #define TEACHOS_X86_64_CPU_REGISTERS_HPP -#include - -namespace teachos::cpu::x86_64 -{ - - /** - * @brief Control registers that can be read and written to. - * - * @note CR1 and CR5 - 7 are reserved and will throw an exception if they are accessed, therefore they are not defined - * in the enum. See https://en.wikipedia.org/wiki/Control_register#Control_registers_in_Intel_x86_series for more - * information. - */ - enum struct control_register : uint8_t - { - cr0, ///< Contains various control flags that modify basic operation of the processor, Machine Status World (MSW) - ///< register. - cr2 = 2U, ///< Contains Page Fault Linear Address (PFLA), when page fault occurs address program attended to accces - ///< is stored here. - cr3, ///< Enables process to translate linear addresses into physical addresses using paging, CR0 bit 32 Paging - ///< (PG) needs to be enabled simply contains the register value that represents the physical address of the - ///< level 4 page table used for paging in the system. Therefore reading this value allows to access the level - ///< 4 page table directly. Instead of over the virtual address 0xffffffff'fffff000, which then has to be - ///< first translated into a physical address. - cr4 ///< Used in protected mode to control operations. - }; - - /** - * @brief Control register 0 flags that can be set. - * - * @note Modifies the basic operation of the processor. Only the most important extensions are listed below, the rest - * are excluded for brevity. See https://en.wikipedia.org/wiki/Control_register#CR0 for more information. - */ - enum struct cr0_flags : uint64_t - { - PROTECTED_MODE_ENABLED = 1U << 0U, ///< System is in protected or system is in real mode. - TASK_SWITCHED = 1U << 3U, ///< Allows saving x87 task context upon a task switch only after x87 instruction used. - WRITE_PROTECT = 1U << 16U, ///< When set, the CPU cannot write to read-only pages when privilege level is 0. - PAGING = 1U << 31U, // Enable paging using the CR3 register. - }; - - /** - * @brief Reads the value of the given control register. - * - * @param cr Control register that should be read. - * @return Value of the control register. - */ - auto read_control_register(control_register cr) -> uint64_t; - - /** - * @brief Sets a specific bit in the Extended Feature Enable Register (EFER) Model-Specific Register (MSR) register. - * - * @param cr Control register that should be written. - * @param new_value New value that should be written. - */ - auto write_control_register(control_register cr, uint64_t new_value) -> void; - - /** - * @brief Sets a specific bit in the CR0. - * - * @note This function reads the current value of the CR0 register, ORs the specified - * bit with the current value, and writes the updated value back to the CR0. - * - * @param flag he flag to set in the CR0. - */ - auto set_cr0_bit(cr0_flags flag) -> void; - -} // namespace teachos::cpu::x86_64 +#include "x86_64/cpu/impl/control_registers.hpp" // IWYU pragma: export #endif \ No newline at end of file diff --git a/arch/x86_64/src/cpu/registers.cpp b/arch/x86_64/src/cpu/registers.cpp deleted file mode 100644 index d59776b..0000000 --- a/arch/x86_64/src/cpu/registers.cpp +++ /dev/null @@ -1,64 +0,0 @@ -#include "x86_64/cpu/registers.hpp" - -#include - -namespace teachos::cpu::x86_64 -{ - auto read_control_register(control_register cr) -> uint64_t - { - uint64_t current_value{}; - switch (cr) - { - case control_register::cr0: - asm volatile("mov %%cr0, %[output]" : [output] "=r"(current_value)); - break; - case control_register::cr2: - asm volatile("mov %%cr2, %[output]" : [output] "=r"(current_value)); - break; - case control_register::cr3: - asm volatile("mov %%cr3, %[output]" : [output] "=r"(current_value)); - break; - case control_register::cr4: - asm volatile("mov %%cr4, %[output]" : [output] "=r"(current_value)); - break; - } - return current_value; - } - - auto write_control_register(control_register cr, uint64_t new_value) -> void - { - switch (cr) - { - case control_register::cr0: - asm volatile("mov %[input], %%cr0" - : /* no output from call */ - : [input] "r"(new_value) - : "memory"); - break; - case control_register::cr2: - asm volatile("mov %[input], %%cr2" - : /* no output from call */ - : [input] "r"(new_value) - : "memory"); - break; - case control_register::cr3: - asm volatile("mov %[input], %%cr3" - : /* no output from call */ - : [input] "r"(new_value) - : "memory"); - break; - case control_register::cr4: - asm volatile("mov %[input], %%cr4" - : /* no output from call */ - : [input] "r"(new_value) - : "memory"); - break; - } - } - - auto set_cr0_bit(cr0_flags flag) -> void - { - auto const cr0 = read_control_register(control_register::cr0); - write_control_register(control_register::cr0, static_cast>(flag) | cr0); - } -} // namespace teachos::cpu::x86_64 diff --git a/arch/x86_64/src/kapi/memory.cpp b/arch/x86_64/src/kapi/memory.cpp index aa3eb58..4766bb3 100644 --- a/arch/x86_64/src/kapi/memory.cpp +++ b/arch/x86_64/src/kapi/memory.cpp @@ -6,6 +6,7 @@ #include "x86_64/boot/boot.hpp" #include "x86_64/boot/ld.hpp" +#include "x86_64/cpu/impl/control_registers.hpp" #include "x86_64/cpu/registers.hpp" #include "x86_64/memory/region_allocator.hpp" @@ -48,7 +49,7 @@ namespace teachos::memory auto enable_cpu_protections() -> void { - cpu::x86_64::set_cr0_bit(cpu::x86_64::cr0_flags::WRITE_PROTECT); + cpu::x86_64::cr0::clear(cpu::x86_64::cr0::flags::write_protect); // set_efer_bit(efer_flags::NXE); } } // namespace diff --git a/arch/x86_64/src/memory/mmu.cpp b/arch/x86_64/src/memory/mmu.cpp index e573b4e..8ec8a2e 100644 --- a/arch/x86_64/src/memory/mmu.cpp +++ b/arch/x86_64/src/memory/mmu.cpp @@ -13,7 +13,7 @@ namespace teachos::memory::x86_64 auto tlb_flush_all() -> void { - auto current_value = cpu::read_control_register(cpu::control_register::cr3); - cpu::write_control_register(cpu::control_register::cr3, current_value); + auto paging_root = cpu::cr3::read(); + cpu::cr3::write(paging_root); } } // namespace teachos::memory::x86_64 -- cgit v1.2.3 From 7f935cdc3e57d80c4ef83760e1a616c33684271d Mon Sep 17 00:00:00 2001 From: Felix Morgner Date: Fri, 21 Nov 2025 14:55:10 +0100 Subject: x86_64: fix enabling of write protection --- arch/x86_64/src/kapi/memory.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'arch/x86_64') diff --git a/arch/x86_64/src/kapi/memory.cpp b/arch/x86_64/src/kapi/memory.cpp index 4766bb3..6102bee 100644 --- a/arch/x86_64/src/kapi/memory.cpp +++ b/arch/x86_64/src/kapi/memory.cpp @@ -49,7 +49,7 @@ namespace teachos::memory auto enable_cpu_protections() -> void { - cpu::x86_64::cr0::clear(cpu::x86_64::cr0::flags::write_protect); + cpu::x86_64::cr0::set(cpu::x86_64::cr0::flags::write_protect); // set_efer_bit(efer_flags::NXE); } } // namespace -- cgit v1.2.3 From 9ee028e52bd60affded3b90ea8dcd20c93bb5025 Mon Sep 17 00:00:00 2001 From: Felix Morgner Date: Mon, 24 Nov 2025 13:16:35 +0100 Subject: x86-64/kapi: reimplement ia32_efer support --- .../x86_64/cpu/impl/model_specific_register.hpp | 112 +++++++++++++++++++++ arch/x86_64/include/x86_64/cpu/registers.hpp | 3 +- arch/x86_64/src/kapi/memory.cpp | 3 +- 3 files changed, 115 insertions(+), 3 deletions(-) create mode 100644 arch/x86_64/include/x86_64/cpu/impl/model_specific_register.hpp (limited to 'arch/x86_64') diff --git a/arch/x86_64/include/x86_64/cpu/impl/model_specific_register.hpp b/arch/x86_64/include/x86_64/cpu/impl/model_specific_register.hpp new file mode 100644 index 0000000..8a41a8a --- /dev/null +++ b/arch/x86_64/include/x86_64/cpu/impl/model_specific_register.hpp @@ -0,0 +1,112 @@ +#ifndef TEACHOS_X86_64_CPU_IMPL_MODEL_SPECIFIC_REGISTER_HPP +#define TEACHOS_X86_64_CPU_IMPL_MODEL_SPECIFIC_REGISTER_HPP + +#include +#include +#include + +namespace teachos::cpu::x86_64 +{ + namespace impl + { + constexpr auto ia32_efer_number = 0xC000'0080u; + + template + struct model_specific_register_with_flags + { + }; + + template + struct model_specific_register_with_flags>> + { + using flags = ValueType; + + auto static set(flags flag) -> void + { + auto current = Derived::read(); + current |= flag; + Derived::write(current); + } + + auto static clear(flags flag) -> void + { + auto current = Derived::read(); + current &= ~flag; + Derived::write(current); + } + + auto test(flags flag) -> void + { + return Derived::read() & flag; + } + }; + + template + struct model_specific_register + : model_specific_register_with_flags, ValueType> + { + struct raw_value + { + std::uint32_t low_half; + std::uint32_t high_half; + }; + + auto static read() -> ValueType + { + auto raw = raw_value{}; + asm volatile("rdmsr" : "=a"(raw.low_half), "=d"(raw.high_half) : "c"(Number)); + return static_cast(std::bit_cast(raw)); + } + + auto static write(ValueType value) -> void + { + auto raw = std::bit_cast(static_cast(value)); + asm volatile("wrmsr" : : "a"(raw.low_half), "d"(raw.high_half), "c"(Number)); + } + }; + + enum struct ia32_efer_flags : std::uint64_t + { + syscall_enable = 1uz << 0, + ia32e_mode_enable = 1uz << 8, + ia32e_mode_active = 1uz << 10, + execute_disable_bit_enable = 1uz << 11, + }; + + constexpr auto operator|(ia32_efer_flags lhs, ia32_efer_flags rhs) -> ia32_efer_flags + { + return static_cast(static_cast>(lhs) | + static_cast>(rhs)); + } + + constexpr auto operator|=(ia32_efer_flags & lhs, ia32_efer_flags rhs) -> ia32_efer_flags & + { + lhs = lhs | rhs; + return lhs; + } + + constexpr auto operator&(ia32_efer_flags lhs, ia32_efer_flags rhs) -> ia32_efer_flags + { + return static_cast(static_cast>(lhs) & + static_cast>(rhs)); + } + + constexpr auto operator&=(ia32_efer_flags & lhs, ia32_efer_flags rhs) -> ia32_efer_flags & + { + lhs = lhs & rhs; + return lhs; + } + + constexpr auto operator~(ia32_efer_flags lhs) -> ia32_efer_flags + { + // NOLINTNEXTLINE(clang-analyzer-optin.core.EnumCastOutOfRange) + return static_cast(~static_cast>(lhs)); + } + + } // namespace impl + + using i32_efer = impl::model_specific_register; + +} // namespace teachos::cpu::x86_64 + +#endif \ No newline at end of file diff --git a/arch/x86_64/include/x86_64/cpu/registers.hpp b/arch/x86_64/include/x86_64/cpu/registers.hpp index 7f71b95..97cba19 100644 --- a/arch/x86_64/include/x86_64/cpu/registers.hpp +++ b/arch/x86_64/include/x86_64/cpu/registers.hpp @@ -1,6 +1,7 @@ #ifndef TEACHOS_X86_64_CPU_REGISTERS_HPP #define TEACHOS_X86_64_CPU_REGISTERS_HPP -#include "x86_64/cpu/impl/control_registers.hpp" // IWYU pragma: export +#include "x86_64/cpu/impl/control_registers.hpp" // IWYU pragma: export +#include "x86_64/cpu/impl/model_specific_register.hpp" // IWYU pragma: export #endif \ No newline at end of file diff --git a/arch/x86_64/src/kapi/memory.cpp b/arch/x86_64/src/kapi/memory.cpp index 6102bee..d0d966b 100644 --- a/arch/x86_64/src/kapi/memory.cpp +++ b/arch/x86_64/src/kapi/memory.cpp @@ -6,7 +6,6 @@ #include "x86_64/boot/boot.hpp" #include "x86_64/boot/ld.hpp" -#include "x86_64/cpu/impl/control_registers.hpp" #include "x86_64/cpu/registers.hpp" #include "x86_64/memory/region_allocator.hpp" @@ -50,7 +49,7 @@ namespace teachos::memory auto enable_cpu_protections() -> void { cpu::x86_64::cr0::set(cpu::x86_64::cr0::flags::write_protect); - // set_efer_bit(efer_flags::NXE); + cpu::x86_64::i32_efer::set(cpu::x86_64::i32_efer::flags::execute_disable_bit_enable); } } // namespace -- cgit v1.2.3 From 2b3dca0d0329b61881ffbecca0f120cfda3314fa Mon Sep 17 00:00:00 2001 From: Felix Morgner Date: Mon, 24 Nov 2025 13:25:12 +0100 Subject: x86_64/kapi: clean up one linter warning --- arch/x86_64/src/kapi/memory.cpp | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) (limited to 'arch/x86_64') diff --git a/arch/x86_64/src/kapi/memory.cpp b/arch/x86_64/src/kapi/memory.cpp index d0d966b..5142a2a 100644 --- a/arch/x86_64/src/kapi/memory.cpp +++ b/arch/x86_64/src/kapi/memory.cpp @@ -19,10 +19,8 @@ namespace teachos::memory namespace { - // NOLINTBEGIN(cppcoreguidelines-avoid-non-const-global-variables) - auto constinit is_initialized = std::atomic_flag{}; + // NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables) auto constinit allocator = static_cast(nullptr); - // NOLINTEND(cppcoreguidelines-avoid-non-const-global-variables) auto create_memory_information() -> x86_64::region_allocator::memory_information { @@ -65,6 +63,7 @@ namespace teachos::memory auto init() -> void { + auto static constinit is_initialized = std::atomic_flag{}; if (is_initialized.test_and_set()) { system::panic("[x86_64] Memory management has already been initialized."); -- cgit v1.2.3 From 1a3c20cc9ea191a862eb7e8ac55b3a69ac74ad5e Mon Sep 17 00:00:00 2001 From: Felix Morgner Date: Mon, 24 Nov 2025 16:59:24 +0100 Subject: x86_64/vga: rely less on magic state --- arch/x86_64/CMakeLists.txt | 24 +++++++++++++++ arch/x86_64/include/x86_64/boot/boot.hpp | 24 +++++++-------- arch/x86_64/include/x86_64/boot/ld.hpp | 9 ++++++ arch/x86_64/include/x86_64/vga/text.hpp | 11 +++++++ arch/x86_64/src/boot/entry64.s | 19 ++++++++++++ arch/x86_64/src/kapi/memory.cpp | 4 +-- arch/x86_64/src/vga/text.cpp | 50 ++++++++++++++++++-------------- 7 files changed, 104 insertions(+), 37 deletions(-) (limited to 'arch/x86_64') diff --git a/arch/x86_64/CMakeLists.txt b/arch/x86_64/CMakeLists.txt index 9c6d818..cf85b36 100644 --- a/arch/x86_64/CMakeLists.txt +++ b/arch/x86_64/CMakeLists.txt @@ -30,6 +30,30 @@ target_sources("x86_64" PRIVATE "src/vga/text.cpp" ) +target_sources("x86_64" PRIVATE + FILE_SET HEADERS + BASE_DIRS "include" + FILES + # Low-level bootstrap + "include/x86_64/boot/boot.hpp" + "include/x86_64/boot/ld.hpp" + + # Low-level CPU access + "include/x86_64/cpu/registers.hpp" + "include/x86_64/cpu/impl/control_registers.hpp" + "include/x86_64/cpu/impl/model_specific_register.hpp" + + # Low-level device I/O + "include/x86_64/device_io/port_io.hpp" + + # Memory management + "include/x86_64/memory/mmu.hpp" + "include/x86_64/memory/region_allocator.hpp" + + # VGA I/O + "include/x86_64/vga/text.hpp" +) + set(KERNEL_LINKER_SCRIPT "${CMAKE_CURRENT_SOURCE_DIR}/scripts/kernel.ld" PARENT_SCOPE diff --git a/arch/x86_64/include/x86_64/boot/boot.hpp b/arch/x86_64/include/x86_64/boot/boot.hpp index 3a3620d..6dcd2de 100644 --- a/arch/x86_64/include/x86_64/boot/boot.hpp +++ b/arch/x86_64/include/x86_64/boot/boot.hpp @@ -45,26 +45,22 @@ /* clang-format on */ #else +#include "kapi/boot.hpp" // IWYU pragma: export + #include -#include +#include -namespace teachos::boot::x86_64 +namespace teachos::boot { - extern "C" + struct information { - /** - * @brief A pointer to the multiboot 2 information structure provided by the boot loader. - */ - extern kstd::asm_ptr multiboot_information_pointer; - - /** - * @brief A pointer to the VGA text mode buffer. - */ - extern kstd::asm_ptr> vga_buffer_pointer; - } -} // namespace teachos::boot::x86_64 + multiboot2::information_view const * mbi; + std::size_t vga_buffer_index; + }; + +} // namespace teachos::boot #endif diff --git a/arch/x86_64/include/x86_64/boot/ld.hpp b/arch/x86_64/include/x86_64/boot/ld.hpp index 104e6ee..9af3dc8 100644 --- a/arch/x86_64/include/x86_64/boot/ld.hpp +++ b/arch/x86_64/include/x86_64/boot/ld.hpp @@ -61,6 +61,15 @@ namespace teachos::boot::x86_64 * To use this symbol for its intended purpose, the address of it shall be taken. */ extern std::byte _end_virtual; + + /** + * @brief The first byte of the kernel's virtual address space. + * + * @details + * This symbol is defined in the kernel linker script and marks beginning of the kernel virtual address space. To + * use this symbol for its intended purpose, the address of it shall be taken. + */ + extern std::byte TEACHOS_VMA; } } // namespace teachos::boot::x86_64 diff --git a/arch/x86_64/include/x86_64/vga/text.hpp b/arch/x86_64/include/x86_64/vga/text.hpp index d8919bf..bb593e7 100644 --- a/arch/x86_64/include/x86_64/vga/text.hpp +++ b/arch/x86_64/include/x86_64/vga/text.hpp @@ -4,6 +4,7 @@ #include "kapi/cio.hpp" #include +#include #include namespace teachos::vga::x86_64::text @@ -100,6 +101,8 @@ namespace teachos::vga::x86_64::text struct device final : teachos::cio::output_device { + device(); + /** * @brief Clear the VGA text mode buffer. * @@ -134,6 +137,8 @@ namespace teachos::vga::x86_64::text } private: + using glyph = std::pair; + /** * @brief Move the cursor to a new line, scrolling the buffer if necessary. */ @@ -150,6 +155,8 @@ namespace teachos::vga::x86_64::text */ auto write(std::string_view code_points, attribute attribute) -> void; + auto write(char code_point, attribute attribute) -> void; + /** * @brief Write a string of code points followed by a newline to the VGA text buffer. * @@ -160,6 +167,10 @@ namespace teachos::vga::x86_64::text * @see vga::text::attribute */ auto writeln(std::string_view code_points, attribute attribute) -> void; + + std::span static const buffer; + + std::size_t m_position{}; }; } // namespace teachos::vga::x86_64::text diff --git a/arch/x86_64/src/boot/entry64.s b/arch/x86_64/src/boot/entry64.s index c5df5db..636e4cd 100644 --- a/arch/x86_64/src/boot/entry64.s +++ b/arch/x86_64/src/boot/entry64.s @@ -1,3 +1,16 @@ +.section .bss, "aw", @nobits + +//! A structure containing information gathered during the bootstrap process. +//! Expected layout (as described by teachos::boot::information): +//! +//! struct +//! { +//! multiboot2::information_view const * mbi; +//! std::size_t vga_buffer_index; +//! } +.global bootstrap_information +bootstrap_information: .skip 16 + .section .boot_text, "ax", @progbits .code64 @@ -10,6 +23,12 @@ _entry64: mov %rax, %fs mov %rax, %gs + mov multiboot_information_pointer, %rax + mov vga_buffer_pointer, %rdx + sub $0xb8000, %rdx + mov %rax, (bootstrap_information) + mov %rdx, (bootstrap_information + 8) + call invoke_global_constructors xor %rax, %rax diff --git a/arch/x86_64/src/kapi/memory.cpp b/arch/x86_64/src/kapi/memory.cpp index 5142a2a..3848fb8 100644 --- a/arch/x86_64/src/kapi/memory.cpp +++ b/arch/x86_64/src/kapi/memory.cpp @@ -24,7 +24,7 @@ namespace teachos::memory auto create_memory_information() -> x86_64::region_allocator::memory_information { - auto const & mbi = boot::x86_64::multiboot_information_pointer.get(); + auto const & mbi = boot::bootstrap_information.mbi; return {.image_range = std::make_pair(physical_address{&boot::x86_64::_start_physical}, physical_address{&boot::x86_64::_end_physical}), @@ -35,7 +35,7 @@ namespace teachos::memory auto create_early_frame_allocator() { - auto memory_map = boot::x86_64::multiboot_information_pointer->maybe_memory_map(); + auto memory_map = boot::bootstrap_information.mbi->maybe_memory_map(); if (!memory_map) { system::panic("[x86_64] Failed to create early allocator, no memory map available."); diff --git a/arch/x86_64/src/vga/text.cpp b/arch/x86_64/src/vga/text.cpp index 6ecffa3..8b7f01b 100644 --- a/arch/x86_64/src/vga/text.cpp +++ b/arch/x86_64/src/vga/text.cpp @@ -1,38 +1,41 @@ #include "x86_64/vga/text.hpp" +#include "kapi/boot.hpp" + #include "x86_64/boot/boot.hpp" +#include "x86_64/boot/ld.hpp" #include "x86_64/vga/crtc.hpp" #include #include #include +#include #include #include namespace teachos::vga::x86_64::text { - using boot::x86_64::vga_buffer_pointer; - namespace { - // NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables) - auto constinit buffer_offset = std::ptrdiff_t{}; - + constexpr auto BUFFER_BASE_ADDRESS = std::uintptr_t{0xb8000}; constexpr auto DEFAULT_TEXT_BUFFER_WIDTH = 80U; constexpr auto DEFAULT_TEXT_BUFFER_HEIGHT = 25U; constexpr auto CURSOR_ENABLED_BIT = 5U; - - auto write_char(char code_point, attribute attribute) -> void - { - vga_buffer_pointer[buffer_offset++] = std::pair{code_point, std::bit_cast(attribute)}; - }; } // namespace + std::span const device::buffer = + std::span{std::bit_cast(BUFFER_BASE_ADDRESS + + std::bit_cast(&teachos::boot::x86_64::TEACHOS_VMA)), + DEFAULT_TEXT_BUFFER_WIDTH * DEFAULT_TEXT_BUFFER_HEIGHT}; + + device::device() + : m_position{boot::bootstrap_information.vga_buffer_index} + {} + auto device::clear(attribute attribute) -> void { - buffer_offset = 0; - std::ranges::fill_n(vga_buffer_pointer.get(), DEFAULT_TEXT_BUFFER_WIDTH * DEFAULT_TEXT_BUFFER_HEIGHT, - std::pair{' ', std::bit_cast(attribute)}); + m_position = 0; + std::ranges::fill(buffer, std::pair{' ', std::bit_cast(attribute)}); } auto device::cursor(bool enabled) -> void @@ -45,30 +48,35 @@ namespace teachos::vga::x86_64::text auto device::newline() -> void { - auto current_line = buffer_offset / DEFAULT_TEXT_BUFFER_WIDTH; + auto current_line = m_position / DEFAULT_TEXT_BUFFER_WIDTH; auto next_line = current_line + 1; if (std::cmp_greater_equal(next_line, DEFAULT_TEXT_BUFFER_HEIGHT)) { - auto begin = vga_buffer_pointer + DEFAULT_TEXT_BUFFER_WIDTH; - auto end = vga_buffer_pointer + DEFAULT_TEXT_BUFFER_WIDTH * DEFAULT_TEXT_BUFFER_HEIGHT; - std::ranges::move(begin, end, vga_buffer_pointer.get()); - buffer_offset = current_line * DEFAULT_TEXT_BUFFER_WIDTH; + auto begin = buffer.begin() + DEFAULT_TEXT_BUFFER_WIDTH; + auto end = buffer.begin() + DEFAULT_TEXT_BUFFER_WIDTH * DEFAULT_TEXT_BUFFER_HEIGHT; + std::ranges::move(begin, end, buffer.begin()); + m_position = current_line * DEFAULT_TEXT_BUFFER_WIDTH; } else { - buffer_offset = next_line * DEFAULT_TEXT_BUFFER_WIDTH; + m_position = next_line * DEFAULT_TEXT_BUFFER_WIDTH; } } auto device::write(std::string_view code_points, attribute attribute) -> void { - std::ranges::for_each(code_points, [&](auto code_point) -> void { write_char(code_point, attribute); }); + std::ranges::for_each(code_points, [&](auto code_point) -> void { write(code_point, attribute); }); } + auto device::write(char code_point, attribute attribute) -> void + { + buffer[m_position++] = std::pair{code_point, std::bit_cast(attribute)}; + }; + auto device::writeln(std::string_view code_points, attribute attribute) -> void { - std::ranges::for_each(code_points, [&](auto code_point) -> void { write_char(code_point, attribute); }); + std::ranges::for_each(code_points, [&](auto code_point) -> void { write(code_point, attribute); }); newline(); } -- cgit v1.2.3 From 1db039ca1c67e8daba8b5ec6d5158cb2110e1410 Mon Sep 17 00:00:00 2001 From: Felix Morgner Date: Fri, 28 Nov 2025 16:06:15 +0100 Subject: x86_64: port basic page and page table abstractions --- arch/x86_64/CMakeLists.txt | 4 + arch/x86_64/include/x86_64/memory/page_table.hpp | 139 ++++++++++++++++++++++ arch/x86_64/include/x86_64/memory/paging_root.hpp | 27 +++++ arch/x86_64/src/kapi/memory.cpp | 10 +- arch/x86_64/src/memory/page_table.cpp | 57 +++++++++ arch/x86_64/src/memory/paging_root.cpp | 19 +++ 6 files changed, 252 insertions(+), 4 deletions(-) create mode 100644 arch/x86_64/include/x86_64/memory/page_table.hpp create mode 100644 arch/x86_64/include/x86_64/memory/paging_root.hpp create mode 100644 arch/x86_64/src/memory/page_table.cpp create mode 100644 arch/x86_64/src/memory/paging_root.cpp (limited to 'arch/x86_64') diff --git a/arch/x86_64/CMakeLists.txt b/arch/x86_64/CMakeLists.txt index cf85b36..8451945 100644 --- a/arch/x86_64/CMakeLists.txt +++ b/arch/x86_64/CMakeLists.txt @@ -24,6 +24,8 @@ target_sources("x86_64" PRIVATE # Memory management "src/memory/mmu.cpp" + "src/memory/page_table.cpp" + "src/memory/paging_root.cpp" "src/memory/region_allocator.cpp" # VGA text mode @@ -48,6 +50,8 @@ target_sources("x86_64" PRIVATE # Memory management "include/x86_64/memory/mmu.hpp" + "include/x86_64/memory/page_table.hpp" + "include/x86_64/memory/paging_root.hpp" "include/x86_64/memory/region_allocator.hpp" # VGA I/O diff --git a/arch/x86_64/include/x86_64/memory/page_table.hpp b/arch/x86_64/include/x86_64/memory/page_table.hpp new file mode 100644 index 0000000..53af5d0 --- /dev/null +++ b/arch/x86_64/include/x86_64/memory/page_table.hpp @@ -0,0 +1,139 @@ +#ifndef TEACHOS_X86_64_PAGE_TABLE_HPP +#define TEACHOS_X86_64_PAGE_TABLE_HPP + +#include "kapi/memory.hpp" + +#include +#include +#include +#include +#include +#include + +namespace teachos::memory::x86_64 +{ + + //! A table containing paging entries. + struct page_table + { + //! The maximum number of entries in this table. + constexpr auto static entry_count{512}; + + //! A single page table entry + struct entry + { + //! Flags marking the state and configuration of an entry. + enum struct flags : std::uint64_t + { + empty = 0, + present = 1uz << 0, + writable = 1uz << 1, + user_accessible = 1uz << 2, + write_through = 1uz << 3, + disable_cache = 1uz << 4, + accessed = 1uz << 5, + dirty = 1uz << 6, + huge_page = 1uz << 7, + global = 1uz << 8, + no_execute = 1uz << 63, + }; + + entry() = default; + + //! Clear this entry, ensuring all information is set to zero, marking the page represented by this entry as not + //! present. + auto clear() -> void; + + //! Check if the page represented by this entry is present at the containing page table's level. + [[nodiscard]] auto present() const -> bool; + + //! Check if the page represented by this entry is huge (2MiB, or 1GiB, depending on the containing page table's + //! level). + [[nodiscard]] auto huge() const -> bool; + + //! Get all flags present in this entry. + [[nodiscard]] auto all_flags() const -> flags; + + //! Get the frame number associated with this entry, if the referenced page is present. + [[nodiscard]] auto frame() const -> std::optional; + + //! Set the entry to reference the given frame with the given flags. + auto frame(struct frame frame, flags flags) -> void; + + private: + //! A mask to retrieve, or exclude, the frame number from the raw entry. + constexpr auto static frame_number_mask{0x000f'ffff'ffff'f000uz}; + + std::uint64_t m_raw{}; + }; + + //! Get the entry at the given index. + [[nodiscard]] auto operator[](std::size_t index) -> entry &; + [[nodiscard]] auto operator[](std::size_t index) const -> entry const &; + + //! Clear the entire page table, effectively evicting all entries. + auto clear() -> void; + + private: + std::array m_entries{}; + }; + + constexpr auto operator|(page_table::entry::flags lhs, page_table::entry::flags rhs) -> page_table::entry::flags + { + return std::bit_cast(std::to_underlying(lhs) | std::to_underlying(rhs)); + } + + constexpr auto operator&(page_table::entry::flags lhs, page_table::entry::flags rhs) -> page_table::entry::flags + { + return std::bit_cast(std::to_underlying(lhs) & std::to_underlying(rhs)); + } + + //! A recursively mapped page table. + template + requires(Level > 0uz && Level < 5uz) + struct recursive_page_table : page_table + { + constexpr auto static next_level = Level - 1uz; + constexpr auto static recursive_index = 0776uz; + + //! Get the next lower lever table. + [[nodiscard]] auto next(this auto && self, std::size_t index) + requires(next_level > 1) + { + return self.next_address(index).transform([](auto address) -> auto { + auto table_pointer = std::bit_cast *>(address); + return &std::forward_like(*table_pointer); + }); + } + + //! Get the next lower lever table. + [[nodiscard]] auto next(this auto && self, std::size_t index) + requires(next_level == 1) + { + return self.next_address(index).transform([](auto address) -> auto { + auto table_pointer = std::bit_cast(address); + return &std::forward_like(*table_pointer); + }); + } + + private: + constexpr auto static level_bits = 9; + constexpr auto static high_bit = 48; + constexpr auto static offset_bits = 12; + + [[nodiscard]] auto next_address(std::size_t index) const -> std::optional + { + if (auto entry = (*this)[index]; entry.present() && !entry.huge()) + { + auto this_address = std::bit_cast(this); + auto next_address = (this_address << level_bits) | 1uz << high_bit | (index << offset_bits); + return next_address; + } + + return std::nullopt; + } + }; + +} // namespace teachos::memory::x86_64 + +#endif \ No newline at end of file diff --git a/arch/x86_64/include/x86_64/memory/paging_root.hpp b/arch/x86_64/include/x86_64/memory/paging_root.hpp new file mode 100644 index 0000000..365e180 --- /dev/null +++ b/arch/x86_64/include/x86_64/memory/paging_root.hpp @@ -0,0 +1,27 @@ +#ifndef TEACHOS_X86_64_PAGING_ROOT_HPP +#define TEACHOS_X86_64_PAGING_ROOT_HPP + +#include "kapi/memory.hpp" + +#include "x86_64/memory/page_table.hpp" + +#include + +namespace teachos::memory::x86_64 +{ + + //! The active, recursively mapped, root map (e.g. PML4) + struct paging_root : recursive_page_table<4> + { + auto static get() -> paging_root &; + + [[nodiscard]] auto translate(linear_address address) const -> std::optional; + [[nodiscard]] auto translate(page page) const -> std::optional; + + private: + paging_root() = default; + }; + +} // namespace teachos::memory::x86_64 + +#endif \ No newline at end of file diff --git a/arch/x86_64/src/kapi/memory.cpp b/arch/x86_64/src/kapi/memory.cpp index 3848fb8..99dcb5c 100644 --- a/arch/x86_64/src/kapi/memory.cpp +++ b/arch/x86_64/src/kapi/memory.cpp @@ -12,6 +12,7 @@ #include #include +#include namespace teachos::memory { @@ -25,11 +26,12 @@ namespace teachos::memory auto create_memory_information() -> x86_64::region_allocator::memory_information { auto const & mbi = boot::bootstrap_information.mbi; + auto mbi_span = std::span{std::bit_cast(mbi), mbi->size_bytes()}; + auto image_span = std::span{&boot::x86_64::_start_physical, &boot::x86_64::_end_physical}; - return {.image_range = std::make_pair(physical_address{&boot::x86_64::_start_physical}, - physical_address{&boot::x86_64::_end_physical}), - .mbi_range = std::make_pair(physical_address{std::bit_cast(mbi)}, - physical_address{std::bit_cast(mbi) + mbi->size_bytes()}), + return {.image_range = + std::make_pair(physical_address{&image_span.front()}, physical_address{&image_span.back()}), + .mbi_range = std::make_pair(physical_address{&mbi_span.front()}, physical_address{&mbi_span.back()}), .memory_map = mbi->memory_map()}; }; diff --git a/arch/x86_64/src/memory/page_table.cpp b/arch/x86_64/src/memory/page_table.cpp new file mode 100644 index 0000000..c716c5c --- /dev/null +++ b/arch/x86_64/src/memory/page_table.cpp @@ -0,0 +1,57 @@ +#include "x86_64/memory/page_table.hpp" + +#include + +namespace teachos::memory::x86_64 +{ + + auto page_table::entry::clear() -> void + { + m_raw = 0; + } + + auto page_table::entry::present() const -> bool + { + return (flags() & flags::present) != flags::empty; + } + + auto page_table::entry::huge() const -> bool + { + return (flags() & flags::huge_page) != flags::empty; + } + + auto page_table::entry::all_flags() const -> flags + { + return std::bit_cast(m_raw & ~frame_number_mask); + } + + auto page_table::entry::frame() const -> std::optional + { + if (present()) + { + return frame::containing(physical_address{m_raw & frame_number_mask}); + } + return std::nullopt; + } + + auto page_table::entry::frame(struct frame frame, flags flags) -> void + { + m_raw = (frame.start_address().raw() | static_cast(flags)); + }; + + auto page_table::operator[](std::size_t index) -> entry & + { + return m_entries.at(index); + } + + auto page_table::operator[](std::size_t index) const -> entry const & + { + return m_entries.at(index); + } + + auto page_table::clear() -> void + { + std::ranges::for_each(m_entries, &page_table::entry::clear); + } + +} // namespace teachos::memory::x86_64 diff --git a/arch/x86_64/src/memory/paging_root.cpp b/arch/x86_64/src/memory/paging_root.cpp new file mode 100644 index 0000000..1234308 --- /dev/null +++ b/arch/x86_64/src/memory/paging_root.cpp @@ -0,0 +1,19 @@ +#include "x86_64/memory/paging_root.hpp" + +#include + +namespace teachos::memory::x86_64 +{ + + namespace + { + constexpr auto PML_RECURSIVE_BASE = std::uintptr_t{0177777'776'776'776'776'0000uz}; + } + + auto paging_root::get() -> paging_root & + { + auto pml4_address = std::bit_cast(PML_RECURSIVE_BASE); + return *pml4_address; + } + +} // namespace teachos::memory::x86_64 \ No newline at end of file -- cgit v1.2.3 From 57d140f41b462483b8f32a883cbb0b599b9feaed Mon Sep 17 00:00:00 2001 From: Felix Morgner Date: Sun, 30 Nov 2025 16:59:17 +0100 Subject: x86_64/memory: fix entry checks --- arch/x86_64/src/memory/page_table.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'arch/x86_64') diff --git a/arch/x86_64/src/memory/page_table.cpp b/arch/x86_64/src/memory/page_table.cpp index c716c5c..1273676 100644 --- a/arch/x86_64/src/memory/page_table.cpp +++ b/arch/x86_64/src/memory/page_table.cpp @@ -12,12 +12,12 @@ namespace teachos::memory::x86_64 auto page_table::entry::present() const -> bool { - return (flags() & flags::present) != flags::empty; + return (all_flags() & flags::present) != flags::empty; } auto page_table::entry::huge() const -> bool { - return (flags() & flags::huge_page) != flags::empty; + return (all_flags() & flags::huge_page) != flags::empty; } auto page_table::entry::all_flags() const -> flags -- cgit v1.2.3 From cf833d0e8676f6a5ee6b536b4cb5202e9446ab87 Mon Sep 17 00:00:00 2001 From: Felix Morgner Date: Mon, 1 Dec 2025 15:55:52 +0100 Subject: x86_64/memory: prevent copying of page root --- arch/x86_64/include/x86_64/memory/paging_root.hpp | 5 +++++ 1 file changed, 5 insertions(+) (limited to 'arch/x86_64') diff --git a/arch/x86_64/include/x86_64/memory/paging_root.hpp b/arch/x86_64/include/x86_64/memory/paging_root.hpp index 365e180..e9b8eef 100644 --- a/arch/x86_64/include/x86_64/memory/paging_root.hpp +++ b/arch/x86_64/include/x86_64/memory/paging_root.hpp @@ -15,6 +15,11 @@ namespace teachos::memory::x86_64 { auto static get() -> paging_root &; + paging_root(paging_root const &) = delete; + paging_root(paging_root &&) = delete; + auto operator=(paging_root const &) -> paging_root & = delete; + auto operator=(paging_root &&) -> paging_root & = delete; + [[nodiscard]] auto translate(linear_address address) const -> std::optional; [[nodiscard]] auto translate(page page) const -> std::optional; -- cgit v1.2.3 From 27eddc3fa8d4c6001f223670d6001554fc47b657 Mon Sep 17 00:00:00 2001 From: Felix Morgner Date: Mon, 1 Dec 2025 15:56:58 +0100 Subject: x86_64/memory: implement PML4 injection --- arch/x86_64/src/kapi/memory.cpp | 74 +++++++++++++++++++++++++++++++++++++++-- 1 file changed, 71 insertions(+), 3 deletions(-) (limited to 'arch/x86_64') diff --git a/arch/x86_64/src/kapi/memory.cpp b/arch/x86_64/src/kapi/memory.cpp index 99dcb5c..f34729a 100644 --- a/arch/x86_64/src/kapi/memory.cpp +++ b/arch/x86_64/src/kapi/memory.cpp @@ -1,28 +1,34 @@ #include "kapi/memory.hpp" -#include "kapi/memory/frame.hpp" -#include "kapi/memory/frame_allocator.hpp" +#include "kapi/cio.hpp" #include "kapi/system.hpp" #include "x86_64/boot/boot.hpp" #include "x86_64/boot/ld.hpp" #include "x86_64/cpu/registers.hpp" +#include "x86_64/memory/mmu.hpp" +#include "x86_64/memory/page_table.hpp" +#include "x86_64/memory/paging_root.hpp" #include "x86_64/memory/region_allocator.hpp" #include #include +#include #include namespace teachos::memory { std::size_t const PLATFORM_FRAME_SIZE{4096}; + std::size_t const PLATFORM_PAGE_SIZE{PLATFORM_FRAME_SIZE}; namespace { // NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables) auto constinit allocator = static_cast(nullptr); + constexpr auto static unused_page_address = 0x0000'7fff'cafe'faceuz; + auto create_memory_information() -> x86_64::region_allocator::memory_information { auto const & mbi = boot::bootstrap_information.mbi; @@ -51,6 +57,67 @@ namespace teachos::memory cpu::x86_64::cr0::set(cpu::x86_64::cr0::flags::write_protect); cpu::x86_64::i32_efer::set(cpu::x86_64::i32_efer::flags::execute_disable_bit_enable); } + + auto inject_faux_pml4(frame_allocator & allocator) -> void + { + using entry_flags = x86_64::page_table::entry::flags; + using page_table = x86_64::page_table; + + auto temporary_page = page::containing(linear_address{unused_page_address}); + auto temporary_page_address = temporary_page.start_address(); + + auto & pml4 = x86_64::paging_root::get(); + + // NOLINTBEGIN(cppcoreguidelines-avoid-magic-numbers) + auto faux_pml4_frame = + allocator.allocate() + .and_then([&](auto frame) -> auto { + auto index = temporary_page_address >> 39 & 0x1ffu; + pml4[index].frame(frame, entry_flags::present | entry_flags::writable); + return pml4.next(index); + }) + .and_then([&](auto pml) -> auto { + std::construct_at(pml); + auto index = temporary_page_address >> 30 & 0x1ffu; + (*pml)[index].frame(*allocator.allocate(), entry_flags::present | entry_flags::writable); + return pml->next(index); + }) + .and_then([&](auto pml) -> auto { + std::construct_at(pml); + auto index = temporary_page_address >> 21 & 0x1ffu; + (*pml)[index].frame(*allocator.allocate(), entry_flags::present | entry_flags::writable); + return pml->next(index); + }) + .transform([&](auto pml) -> auto { + std::construct_at(pml); + auto index = temporary_page_address >> 12 & 0x1ffu; + (*pml)[index].frame(*allocator.allocate(), entry_flags::present | entry_flags::writable); + return pml; + }) + .and_then([&](auto pml) -> auto { + auto faux_pml4_pointer = std::bit_cast(temporary_page_address.raw()); + auto faux_pml4 = std::construct_at(faux_pml4_pointer); + + auto index = temporary_page_address >> 12 & 0x1ffu; + auto frame = (*pml)[index].frame(); + + (*faux_pml4)[510].frame(*frame, entry_flags::present | entry_flags::writable); + return frame; + }); + // NOLINTEND(cppcoreguidelines-avoid-magic-numbers) + + if (!faux_pml4_frame) + { + system::panic("[MEM] Failed to map and construct faux PML4"); + } + + // NOLINTNEXTLINE(cppcoreguidelines-avoid-magic-numbers) + pml4[510].frame(*faux_pml4_frame, entry_flags::present | entry_flags::writable); + x86_64::tlb_flush_all(); + + cio::println("[MEM] Injected faux PML4 as recursive map."); + } + } // namespace auto active_allocator() -> frame_allocator & @@ -71,8 +138,9 @@ namespace teachos::memory system::panic("[x86_64] Memory management has already been initialized."); } - [[maybe_unused]] auto allocator = create_early_frame_allocator(); + auto allocator = create_early_frame_allocator(); enable_cpu_protections(); + inject_faux_pml4(allocator); // paging::kernel_mapper kernel(allocator, memory_information); // kernel.remap_kernel(); -- cgit v1.2.3 From 203355e51690073e571d4906d53f2494c3dad41b Mon Sep 17 00:00:00 2001 From: Felix Morgner Date: Mon, 1 Dec 2025 19:32:19 +0100 Subject: x86_64/memory: prepare scoped_mapping extraction --- arch/x86_64/CMakeLists.txt | 2 + .../include/x86_64/memory/scoped_mapping.hpp | 39 ++++++++++++ arch/x86_64/src/kapi/memory.cpp | 12 ++++ arch/x86_64/src/memory/scoped_mapping.cpp | 73 ++++++++++++++++++++++ 4 files changed, 126 insertions(+) create mode 100644 arch/x86_64/include/x86_64/memory/scoped_mapping.hpp create mode 100644 arch/x86_64/src/memory/scoped_mapping.cpp (limited to 'arch/x86_64') diff --git a/arch/x86_64/CMakeLists.txt b/arch/x86_64/CMakeLists.txt index 8451945..4d32f76 100644 --- a/arch/x86_64/CMakeLists.txt +++ b/arch/x86_64/CMakeLists.txt @@ -27,6 +27,7 @@ target_sources("x86_64" PRIVATE "src/memory/page_table.cpp" "src/memory/paging_root.cpp" "src/memory/region_allocator.cpp" + "src/memory/scoped_mapping.cpp" # VGA text mode "src/vga/text.cpp" @@ -53,6 +54,7 @@ target_sources("x86_64" PRIVATE "include/x86_64/memory/page_table.hpp" "include/x86_64/memory/paging_root.hpp" "include/x86_64/memory/region_allocator.hpp" + "include/x86_64/memory/scoped_mapping.hpp" # VGA I/O "include/x86_64/vga/text.hpp" diff --git a/arch/x86_64/include/x86_64/memory/scoped_mapping.hpp b/arch/x86_64/include/x86_64/memory/scoped_mapping.hpp new file mode 100644 index 0000000..1719e6b --- /dev/null +++ b/arch/x86_64/include/x86_64/memory/scoped_mapping.hpp @@ -0,0 +1,39 @@ +#ifndef TEACHOS_X86_64_SCOPED_MAPPING_HPP +#define TEACHOS_X86_64_SCOPED_MAPPING_HPP + +#include "kapi/memory.hpp" + +#include "x86_64/memory/page_table.hpp" + +namespace teachos::memory::x86_64 +{ + + struct scoped_mapping + { + scoped_mapping(scoped_mapping const &) = delete; + scoped_mapping(scoped_mapping &&); + scoped_mapping(linear_address address, frame_allocator & allocator); + + ~scoped_mapping(); + + auto operator=(scoped_mapping const &) -> scoped_mapping = delete; + auto operator=(scoped_mapping &&) -> scoped_mapping &; + + auto map(frame frame, page_table::entry::flags flags) -> std::byte *; + auto unmap() -> void; + + template + auto map_as(frame frame, page_table::entry::flags flags) -> DataType * + { + return std::bit_cast(map(frame, flags)); + } + + private: + linear_address m_address; + frame_allocator * m_allocator; + bool m_mapped; + }; + +} // namespace teachos::memory::x86_64 + +#endif \ No newline at end of file diff --git a/arch/x86_64/src/kapi/memory.cpp b/arch/x86_64/src/kapi/memory.cpp index f34729a..920c82b 100644 --- a/arch/x86_64/src/kapi/memory.cpp +++ b/arch/x86_64/src/kapi/memory.cpp @@ -10,6 +10,7 @@ #include "x86_64/memory/page_table.hpp" #include "x86_64/memory/paging_root.hpp" #include "x86_64/memory/region_allocator.hpp" +#include "x86_64/memory/scoped_mapping.hpp" #include @@ -140,8 +141,19 @@ namespace teachos::memory auto allocator = create_early_frame_allocator(); enable_cpu_protections(); + + // TODO: remove inject_faux_pml4(allocator); + // TODO: implement + auto temporary_mapper = x86_64::scoped_mapping{linear_address{unused_page_address}, allocator}; + auto new_pml4_frame = allocator.allocate(); + + auto new_plm4 = temporary_mapper.map_as( + *new_pml4_frame, x86_64::page_table::entry::flags::present | x86_64::page_table::entry::flags::writable); + (*new_plm4)[510].frame(new_pml4_frame.value(), + x86_64::page_table::entry::flags::present | x86_64::page_table::entry::flags::writable); + // 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); diff --git a/arch/x86_64/src/memory/scoped_mapping.cpp b/arch/x86_64/src/memory/scoped_mapping.cpp new file mode 100644 index 0000000..c436ee5 --- /dev/null +++ b/arch/x86_64/src/memory/scoped_mapping.cpp @@ -0,0 +1,73 @@ +#include "x86_64/memory/scoped_mapping.hpp" + +#include "kapi/memory.hpp" +#include "kapi/system.hpp" + +#include "x86_64/memory/mmu.hpp" + +#include + +namespace teachos::memory::x86_64 +{ + + scoped_mapping::scoped_mapping(scoped_mapping && other) + : m_address{std::exchange(other.m_address, linear_address{})} + , m_allocator{std::exchange(other.m_allocator, nullptr)} + , m_mapped{std::exchange(other.m_mapped, false)} + {} + + scoped_mapping::scoped_mapping(linear_address address, frame_allocator & allocator) + : m_address{address} + , m_allocator{&allocator} + , m_mapped{false} + {} + + scoped_mapping::~scoped_mapping() + { + if (m_mapped) + { + unmap(); + x86_64::tlb_flush(m_address); + } + } + + auto scoped_mapping::operator=(scoped_mapping && other) -> scoped_mapping & + { + if (&other == this) + { + return *this; + } + + using std::swap; + + swap(m_address, other.m_address); + swap(m_allocator, other.m_allocator); + swap(m_mapped, other.m_mapped); + + return *this; + } + + auto scoped_mapping::map(frame frame, page_table::entry::flags flags) -> std::byte * + { + static_cast(frame); + static_cast(flags); + + m_mapped = true; + + return nullptr; + } + + auto scoped_mapping::unmap() -> void + { + if (!m_mapped) + { + system::panic("[MEM] Tried to release an unmapped temporary mapping!"); + } + + // TODO: scan pages + // TODO: remove mapping + // TODO: release temporary table frames + m_mapped = false; + } + +} // namespace teachos::memory::x86_64 \ No newline at end of file -- cgit v1.2.3 From be86be1facfce8fe3f376153b9c582f2c5c026aa Mon Sep 17 00:00:00 2001 From: Felix Morgner Date: Tue, 2 Dec 2025 12:31:53 +0100 Subject: x86_64/memory: extend scoped_mapping --- arch/x86_64/CMakeLists.txt | 1 + .../include/x86_64/memory/page_utilities.hpp | 22 ++++++++++++ arch/x86_64/src/memory/paging_root.cpp | 41 ++++++++++++++++++++++ arch/x86_64/src/memory/scoped_mapping.cpp | 8 ++++- 4 files changed, 71 insertions(+), 1 deletion(-) create mode 100644 arch/x86_64/include/x86_64/memory/page_utilities.hpp (limited to 'arch/x86_64') diff --git a/arch/x86_64/CMakeLists.txt b/arch/x86_64/CMakeLists.txt index 4d32f76..f010dcf 100644 --- a/arch/x86_64/CMakeLists.txt +++ b/arch/x86_64/CMakeLists.txt @@ -52,6 +52,7 @@ target_sources("x86_64" PRIVATE # Memory management "include/x86_64/memory/mmu.hpp" "include/x86_64/memory/page_table.hpp" + "include/x86_64/memory/page_utilities.hpp" "include/x86_64/memory/paging_root.hpp" "include/x86_64/memory/region_allocator.hpp" "include/x86_64/memory/scoped_mapping.hpp" diff --git a/arch/x86_64/include/x86_64/memory/page_utilities.hpp b/arch/x86_64/include/x86_64/memory/page_utilities.hpp new file mode 100644 index 0000000..efd1b80 --- /dev/null +++ b/arch/x86_64/include/x86_64/memory/page_utilities.hpp @@ -0,0 +1,22 @@ +#ifndef TEACHOS_X86_64_PAGE_UTILITIES_HPP +#define TEACHOS_X86_64_PAGE_UTILITIES_HPP + +#include "kapi/memory.hpp" + +#include + +namespace teachos::memory::x86_64 +{ + + template + requires(Level > 0uz && Level < 5uz) + constexpr auto pml_index(page page) noexcept -> std::size_t + { + constexpr auto shift_width = (Level - 1) * 9; + constexpr auto index_mask = 0x1ffuz; + return page.number() >> shift_width & index_mask; + } + +} // namespace teachos::memory::x86_64 + +#endif \ No newline at end of file diff --git a/arch/x86_64/src/memory/paging_root.cpp b/arch/x86_64/src/memory/paging_root.cpp index 1234308..6b65f60 100644 --- a/arch/x86_64/src/memory/paging_root.cpp +++ b/arch/x86_64/src/memory/paging_root.cpp @@ -1,6 +1,11 @@ #include "x86_64/memory/paging_root.hpp" +#include "kapi/memory.hpp" + +#include "x86_64/memory/page_utilities.hpp" + #include +#include namespace teachos::memory::x86_64 { @@ -16,4 +21,40 @@ namespace teachos::memory::x86_64 return *pml4_address; } + auto paging_root::translate(linear_address address) const -> std::optional + { + auto offset = address.raw() % PLATFORM_PAGE_SIZE; + return translate(page::containing(address)).transform([offset](auto frame) -> auto { + return physical_address{frame.start_address().raw() + offset}; + }); + } + + auto paging_root::translate(page page) const -> std::optional + { + auto pml3 = next(pml_index<4>(page)); + + if (!pml3) + { + return std::nullopt; + } + + auto handle_huge_page = [&] -> std::optional { + auto pml3_entry = pml3.transform([&](auto pml3) -> auto { return (*pml3)[pml_index<3>(page)]; }); + if (pml3_entry && pml3_entry->huge()) + { + auto pml3_entry_frame = *pml3_entry->frame(); + return frame{pml3_entry_frame.number() + pml_index<2>(page) * entry_count + pml_index<1>(page)}; + } + + // auto pml3_entry = (**pml3)[page.start_address().raw() >> 39 & 0x1ff]; + + return std::nullopt; + }; + + return pml3.and_then([&](auto pml3) -> auto { return pml3->next(pml_index<3>(page)); }) + .and_then([&](auto pml2) -> auto { return pml2->next(pml_index<2>(page)); }) + .and_then([&](auto pml1) -> auto { return (*pml1)[pml_index<1>(page)].frame(); }) + .or_else(handle_huge_page); + } + } // namespace teachos::memory::x86_64 \ No newline at end of file diff --git a/arch/x86_64/src/memory/scoped_mapping.cpp b/arch/x86_64/src/memory/scoped_mapping.cpp index c436ee5..27c4785 100644 --- a/arch/x86_64/src/memory/scoped_mapping.cpp +++ b/arch/x86_64/src/memory/scoped_mapping.cpp @@ -4,6 +4,7 @@ #include "kapi/system.hpp" #include "x86_64/memory/mmu.hpp" +#include "x86_64/memory/paging_root.hpp" #include @@ -20,7 +21,12 @@ namespace teachos::memory::x86_64 : m_address{address} , m_allocator{&allocator} , m_mapped{false} - {} + { + if (paging_root::get().translate(address)) + { + system::panic("[MEM] Tried to map a page that is already mapped!"); + } + } scoped_mapping::~scoped_mapping() { -- cgit v1.2.3 From a96b1b4b43a1ed962b412c3d28db0fe00661d96f Mon Sep 17 00:00:00 2001 From: Felix Morgner Date: Tue, 2 Dec 2025 18:40:10 +0100 Subject: x86_64/memory: extract PML4 injection --- arch/x86_64/include/x86_64/memory/page_table.hpp | 3 + .../include/x86_64/memory/scoped_mapping.hpp | 4 +- arch/x86_64/src/kapi/memory.cpp | 79 ++++------------------ arch/x86_64/src/memory/page_table.cpp | 6 ++ arch/x86_64/src/memory/paging_root.cpp | 18 ++++- arch/x86_64/src/memory/scoped_mapping.cpp | 77 +++++++++++++++++---- 6 files changed, 103 insertions(+), 84 deletions(-) (limited to 'arch/x86_64') diff --git a/arch/x86_64/include/x86_64/memory/page_table.hpp b/arch/x86_64/include/x86_64/memory/page_table.hpp index 53af5d0..6b8eb8c 100644 --- a/arch/x86_64/include/x86_64/memory/page_table.hpp +++ b/arch/x86_64/include/x86_64/memory/page_table.hpp @@ -74,6 +74,9 @@ namespace teachos::memory::x86_64 //! Clear the entire page table, effectively evicting all entries. auto clear() -> void; + //! Check if the page table is empty. + [[nodiscard]] auto empty() const noexcept -> bool; + private: std::array m_entries{}; }; diff --git a/arch/x86_64/include/x86_64/memory/scoped_mapping.hpp b/arch/x86_64/include/x86_64/memory/scoped_mapping.hpp index 1719e6b..5737bb0 100644 --- a/arch/x86_64/include/x86_64/memory/scoped_mapping.hpp +++ b/arch/x86_64/include/x86_64/memory/scoped_mapping.hpp @@ -12,7 +12,7 @@ namespace teachos::memory::x86_64 { scoped_mapping(scoped_mapping const &) = delete; scoped_mapping(scoped_mapping &&); - scoped_mapping(linear_address address, frame_allocator & allocator); + scoped_mapping(page page, frame_allocator & allocator); ~scoped_mapping(); @@ -29,7 +29,7 @@ namespace teachos::memory::x86_64 } private: - linear_address m_address; + page m_page; frame_allocator * m_allocator; bool m_mapped; }; diff --git a/arch/x86_64/src/kapi/memory.cpp b/arch/x86_64/src/kapi/memory.cpp index 920c82b..36b1706 100644 --- a/arch/x86_64/src/kapi/memory.cpp +++ b/arch/x86_64/src/kapi/memory.cpp @@ -1,6 +1,5 @@ #include "kapi/memory.hpp" -#include "kapi/cio.hpp" #include "kapi/system.hpp" #include "x86_64/boot/boot.hpp" @@ -28,7 +27,8 @@ namespace teachos::memory // NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables) auto constinit allocator = static_cast(nullptr); - constexpr auto static unused_page_address = 0x0000'7fff'cafe'faceuz; + constexpr auto static unused_page_address = linear_address{0x0000'7fff'cafe'faceuz}; + constexpr auto static recursive_page_map_index = 510; auto create_memory_information() -> x86_64::region_allocator::memory_information { @@ -61,62 +61,18 @@ namespace teachos::memory auto inject_faux_pml4(frame_allocator & allocator) -> void { - using entry_flags = x86_64::page_table::entry::flags; - using page_table = x86_64::page_table; - - auto temporary_page = page::containing(linear_address{unused_page_address}); - auto temporary_page_address = temporary_page.start_address(); - - auto & pml4 = x86_64::paging_root::get(); - - // NOLINTBEGIN(cppcoreguidelines-avoid-magic-numbers) - auto faux_pml4_frame = - allocator.allocate() - .and_then([&](auto frame) -> auto { - auto index = temporary_page_address >> 39 & 0x1ffu; - pml4[index].frame(frame, entry_flags::present | entry_flags::writable); - return pml4.next(index); - }) - .and_then([&](auto pml) -> auto { - std::construct_at(pml); - auto index = temporary_page_address >> 30 & 0x1ffu; - (*pml)[index].frame(*allocator.allocate(), entry_flags::present | entry_flags::writable); - return pml->next(index); - }) - .and_then([&](auto pml) -> auto { - std::construct_at(pml); - auto index = temporary_page_address >> 21 & 0x1ffu; - (*pml)[index].frame(*allocator.allocate(), entry_flags::present | entry_flags::writable); - return pml->next(index); - }) - .transform([&](auto pml) -> auto { - std::construct_at(pml); - auto index = temporary_page_address >> 12 & 0x1ffu; - (*pml)[index].frame(*allocator.allocate(), entry_flags::present | entry_flags::writable); - return pml; - }) - .and_then([&](auto pml) -> auto { - auto faux_pml4_pointer = std::bit_cast(temporary_page_address.raw()); - auto faux_pml4 = std::construct_at(faux_pml4_pointer); - - auto index = temporary_page_address >> 12 & 0x1ffu; - auto frame = (*pml)[index].frame(); - - (*faux_pml4)[510].frame(*frame, entry_flags::present | entry_flags::writable); - return frame; - }); - // NOLINTEND(cppcoreguidelines-avoid-magic-numbers) - - if (!faux_pml4_frame) - { - system::panic("[MEM] Failed to map and construct faux PML4"); - } + using namespace x86_64; + using entry_flags = page_table::entry::flags; + + auto temporary_mapper = scoped_mapping{page::containing(unused_page_address), allocator}; + auto new_pml4_frame = allocator.allocate(); - // NOLINTNEXTLINE(cppcoreguidelines-avoid-magic-numbers) - pml4[510].frame(*faux_pml4_frame, entry_flags::present | entry_flags::writable); - x86_64::tlb_flush_all(); + auto pml4 = std::construct_at(temporary_mapper.map_as(*new_pml4_frame, entry_flags::writable)); + (*pml4)[recursive_page_map_index].frame(new_pml4_frame.value(), entry_flags::present | entry_flags::writable); + paging_root::get()[recursive_page_map_index].frame(new_pml4_frame.value(), + entry_flags::present | entry_flags::writable); - cio::println("[MEM] Injected faux PML4 as recursive map."); + tlb_flush_all(); } } // namespace @@ -141,19 +97,8 @@ namespace teachos::memory auto allocator = create_early_frame_allocator(); enable_cpu_protections(); - - // TODO: remove inject_faux_pml4(allocator); - // TODO: implement - auto temporary_mapper = x86_64::scoped_mapping{linear_address{unused_page_address}, allocator}; - auto new_pml4_frame = allocator.allocate(); - - auto new_plm4 = temporary_mapper.map_as( - *new_pml4_frame, x86_64::page_table::entry::flags::present | x86_64::page_table::entry::flags::writable); - (*new_plm4)[510].frame(new_pml4_frame.value(), - x86_64::page_table::entry::flags::present | x86_64::page_table::entry::flags::writable); - // 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); diff --git a/arch/x86_64/src/memory/page_table.cpp b/arch/x86_64/src/memory/page_table.cpp index 1273676..60bf94d 100644 --- a/arch/x86_64/src/memory/page_table.cpp +++ b/arch/x86_64/src/memory/page_table.cpp @@ -54,4 +54,10 @@ namespace teachos::memory::x86_64 std::ranges::for_each(m_entries, &page_table::entry::clear); } + auto page_table::empty() const noexcept -> bool + { + return std::ranges::all_of(m_entries, + [](auto const & entry) -> auto { return entry.all_flags() == entry::flags::empty; }); + } + } // namespace teachos::memory::x86_64 diff --git a/arch/x86_64/src/memory/paging_root.cpp b/arch/x86_64/src/memory/paging_root.cpp index 6b65f60..a29304e 100644 --- a/arch/x86_64/src/memory/paging_root.cpp +++ b/arch/x86_64/src/memory/paging_root.cpp @@ -40,13 +40,27 @@ namespace teachos::memory::x86_64 auto handle_huge_page = [&] -> std::optional { auto pml3_entry = pml3.transform([&](auto pml3) -> auto { return (*pml3)[pml_index<3>(page)]; }); - if (pml3_entry && pml3_entry->huge()) + if (!pml3_entry) + { + return std::nullopt; + } + else if (pml3_entry->huge()) { auto pml3_entry_frame = *pml3_entry->frame(); return frame{pml3_entry_frame.number() + pml_index<2>(page) * entry_count + pml_index<1>(page)}; } - // auto pml3_entry = (**pml3)[page.start_address().raw() >> 39 & 0x1ff]; + auto pml2 = (*pml3)->next(pml_index<3>(page)); + auto pml2_entry = pml2.transform([&](auto pml2) -> auto { return (*pml2)[pml_index<2>(page)]; }); + if (!pml2_entry) + { + return std::nullopt; + } + else if (pml2_entry->huge()) + { + auto pml2_entry_frame = *pml2_entry->frame(); + return frame{pml2_entry_frame.number() + pml_index<1>(page)}; + } return std::nullopt; }; diff --git a/arch/x86_64/src/memory/scoped_mapping.cpp b/arch/x86_64/src/memory/scoped_mapping.cpp index 27c4785..602198e 100644 --- a/arch/x86_64/src/memory/scoped_mapping.cpp +++ b/arch/x86_64/src/memory/scoped_mapping.cpp @@ -4,25 +4,28 @@ #include "kapi/system.hpp" #include "x86_64/memory/mmu.hpp" +#include "x86_64/memory/page_table.hpp" +#include "x86_64/memory/page_utilities.hpp" #include "x86_64/memory/paging_root.hpp" +#include #include namespace teachos::memory::x86_64 { scoped_mapping::scoped_mapping(scoped_mapping && other) - : m_address{std::exchange(other.m_address, linear_address{})} + : m_page{std::exchange(other.m_page, page{})} , m_allocator{std::exchange(other.m_allocator, nullptr)} , m_mapped{std::exchange(other.m_mapped, false)} {} - scoped_mapping::scoped_mapping(linear_address address, frame_allocator & allocator) - : m_address{address} + scoped_mapping::scoped_mapping(page page, frame_allocator & allocator) + : m_page{page} , m_allocator{&allocator} , m_mapped{false} { - if (paging_root::get().translate(address)) + if (paging_root::get().translate(page)) { system::panic("[MEM] Tried to map a page that is already mapped!"); } @@ -33,7 +36,7 @@ namespace teachos::memory::x86_64 if (m_mapped) { unmap(); - x86_64::tlb_flush(m_address); + x86_64::tlb_flush(m_page.start_address()); } } @@ -46,7 +49,7 @@ namespace teachos::memory::x86_64 using std::swap; - swap(m_address, other.m_address); + swap(m_page, other.m_page); swap(m_allocator, other.m_allocator); swap(m_mapped, other.m_mapped); @@ -55,12 +58,38 @@ namespace teachos::memory::x86_64 auto scoped_mapping::map(frame frame, page_table::entry::flags flags) -> std::byte * { - static_cast(frame); - static_cast(flags); + auto & pml4 = paging_root::get(); + auto pml4_index = pml_index<4>(m_page); + if (!pml4[pml4_index].present()) + { + auto new_frame = m_allocator->allocate(); + pml4[pml4_index].frame(*new_frame, page_table::entry::flags::present | flags); + std::construct_at(pml4.next(pml4_index).value()); + } - m_mapped = true; + auto pml3 = pml4.next(pml4_index).value(); + auto pml3_index = pml_index<3>(m_page); + if (!(*pml3)[pml3_index].present()) + { + auto new_frame = m_allocator->allocate(); + (*pml3)[pml3_index].frame(*new_frame, page_table::entry::flags::present | flags); + std::construct_at((*pml3).next(pml3_index).value()); + } - return nullptr; + auto pml2 = (*pml3).next(pml3_index).value(); + auto pml2_index = pml_index<2>(m_page); + if (!(*pml2)[pml2_index].present()) + { + auto new_frame = m_allocator->allocate(); + (*pml2)[pml2_index].frame(*new_frame, page_table::entry::flags::present | flags); + std::construct_at((*pml2).next(pml2_index).value()); + } + + auto pml1 = (*pml2).next(pml2_index).value(); + auto pml1_index = pml_index<1>(m_page); + (*pml1)[pml1_index].frame(frame, page_table::entry::flags::present | flags); + + return static_cast(frame.start_address()); } auto scoped_mapping::unmap() -> void @@ -70,9 +99,31 @@ namespace teachos::memory::x86_64 system::panic("[MEM] Tried to release an unmapped temporary mapping!"); } - // TODO: scan pages - // TODO: remove mapping - // TODO: release temporary table frames + auto pml3 = paging_root::get().next(pml_index<4>(m_page)).value(); + auto pml2 = pml3->next(pml_index<3>(m_page)).value(); + auto pml1 = pml2->next(pml_index<2>(m_page)).value(); + + auto pml1_entry = (*pml1)[pml_index<1>(m_page)]; + (*pml1)[pml_index<1>(m_page)].clear(); + if (pml1->empty()) + { + m_allocator->release(pml1_entry.frame().value()); + } + + auto pml2_entry = (*pml2)[pml_index<2>(m_page)]; + (*pml2)[pml_index<2>(m_page)].clear(); + if (pml2->empty()) + { + m_allocator->release(pml2_entry.frame().value()); + } + + auto pml3_entry = (*pml3)[pml_index<3>(m_page)]; + (*pml3)[pml_index<3>(m_page)].clear(); + if (pml3->empty()) + { + m_allocator->release(pml3_entry.frame().value()); + } + m_mapped = false; } -- cgit v1.2.3 From 44d4844ce63e7c00b4028bb918eed5d9fb795578 Mon Sep 17 00:00:00 2001 From: Felix Morgner Date: Tue, 2 Dec 2025 18:43:29 +0100 Subject: x86_64/build: automatically collect headers --- arch/x86_64/CMakeLists.txt | 26 +++----------------------- 1 file changed, 3 insertions(+), 23 deletions(-) (limited to 'arch/x86_64') diff --git a/arch/x86_64/CMakeLists.txt b/arch/x86_64/CMakeLists.txt index f010dcf..9afbce3 100644 --- a/arch/x86_64/CMakeLists.txt +++ b/arch/x86_64/CMakeLists.txt @@ -33,32 +33,12 @@ target_sources("x86_64" PRIVATE "src/vga/text.cpp" ) +file(GLOB_RECURSE ARCH_HEADERS RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} "include/**.hpp") + target_sources("x86_64" PRIVATE FILE_SET HEADERS BASE_DIRS "include" - FILES - # Low-level bootstrap - "include/x86_64/boot/boot.hpp" - "include/x86_64/boot/ld.hpp" - - # Low-level CPU access - "include/x86_64/cpu/registers.hpp" - "include/x86_64/cpu/impl/control_registers.hpp" - "include/x86_64/cpu/impl/model_specific_register.hpp" - - # Low-level device I/O - "include/x86_64/device_io/port_io.hpp" - - # Memory management - "include/x86_64/memory/mmu.hpp" - "include/x86_64/memory/page_table.hpp" - "include/x86_64/memory/page_utilities.hpp" - "include/x86_64/memory/paging_root.hpp" - "include/x86_64/memory/region_allocator.hpp" - "include/x86_64/memory/scoped_mapping.hpp" - - # VGA I/O - "include/x86_64/vga/text.hpp" + FILES ${ARCH_HEADERS} ) set(KERNEL_LINKER_SCRIPT -- cgit v1.2.3 From 331c070547634a2096c5e2165559fb0f11ee6330 Mon Sep 17 00:00:00 2001 From: Felix Morgner Date: Tue, 2 Dec 2025 18:49:36 +0100 Subject: kapi: make PLATFORM_*_SIZE constexpr --- arch/x86_64/CMakeLists.txt | 7 ++++++- arch/x86_64/src/kapi/memory.cpp | 2 -- arch/x86_64/src/memory/paging_root.cpp | 2 +- 3 files changed, 7 insertions(+), 4 deletions(-) (limited to 'arch/x86_64') diff --git a/arch/x86_64/CMakeLists.txt b/arch/x86_64/CMakeLists.txt index 9afbce3..4d984cf 100644 --- a/arch/x86_64/CMakeLists.txt +++ b/arch/x86_64/CMakeLists.txt @@ -41,7 +41,12 @@ target_sources("x86_64" PRIVATE FILES ${ARCH_HEADERS} ) +target_compile_definitions("x86_64" PUBLIC + "PLATFORM_PAGE_SIZE=4096" + "PLATFORM_FRAME_SIZE=PLATFORM_PAGE_SIZE" +) + set(KERNEL_LINKER_SCRIPT "${CMAKE_CURRENT_SOURCE_DIR}/scripts/kernel.ld" PARENT_SCOPE -) \ No newline at end of file +) diff --git a/arch/x86_64/src/kapi/memory.cpp b/arch/x86_64/src/kapi/memory.cpp index 36b1706..1a8b36f 100644 --- a/arch/x86_64/src/kapi/memory.cpp +++ b/arch/x86_64/src/kapi/memory.cpp @@ -19,8 +19,6 @@ namespace teachos::memory { - std::size_t const PLATFORM_FRAME_SIZE{4096}; - std::size_t const PLATFORM_PAGE_SIZE{PLATFORM_FRAME_SIZE}; namespace { diff --git a/arch/x86_64/src/memory/paging_root.cpp b/arch/x86_64/src/memory/paging_root.cpp index a29304e..6b8e1ab 100644 --- a/arch/x86_64/src/memory/paging_root.cpp +++ b/arch/x86_64/src/memory/paging_root.cpp @@ -23,7 +23,7 @@ namespace teachos::memory::x86_64 auto paging_root::translate(linear_address address) const -> std::optional { - auto offset = address.raw() % PLATFORM_PAGE_SIZE; + auto offset = address.raw() % page::size; return translate(page::containing(address)).transform([offset](auto frame) -> auto { return physical_address{frame.start_address().raw() + offset}; }); -- cgit v1.2.3 From 588fe1a3600475bdc312a4fe758d2f1125eb149c Mon Sep 17 00:00:00 2001 From: Felix Morgner Date: Tue, 2 Dec 2025 18:56:12 +0100 Subject: x86_64: basic code cleanup --- arch/x86_64/include/x86_64/memory/page_table.hpp | 6 +++--- arch/x86_64/include/x86_64/memory/paging_root.hpp | 2 ++ arch/x86_64/src/kapi/memory.cpp | 2 +- 3 files changed, 6 insertions(+), 4 deletions(-) (limited to 'arch/x86_64') diff --git a/arch/x86_64/include/x86_64/memory/page_table.hpp b/arch/x86_64/include/x86_64/memory/page_table.hpp index 6b8eb8c..c5102a2 100644 --- a/arch/x86_64/include/x86_64/memory/page_table.hpp +++ b/arch/x86_64/include/x86_64/memory/page_table.hpp @@ -16,9 +16,6 @@ namespace teachos::memory::x86_64 //! A table containing paging entries. struct page_table { - //! The maximum number of entries in this table. - constexpr auto static entry_count{512}; - //! A single page table entry struct entry { @@ -67,6 +64,9 @@ namespace teachos::memory::x86_64 std::uint64_t m_raw{}; }; + //! The maximum number of entries in this table. + constexpr auto static entry_count{page::size / sizeof(entry)}; + //! Get the entry at the given index. [[nodiscard]] auto operator[](std::size_t index) -> entry &; [[nodiscard]] auto operator[](std::size_t index) const -> entry const &; diff --git a/arch/x86_64/include/x86_64/memory/paging_root.hpp b/arch/x86_64/include/x86_64/memory/paging_root.hpp index e9b8eef..a9ab6a6 100644 --- a/arch/x86_64/include/x86_64/memory/paging_root.hpp +++ b/arch/x86_64/include/x86_64/memory/paging_root.hpp @@ -20,6 +20,8 @@ namespace teachos::memory::x86_64 auto operator=(paging_root const &) -> paging_root & = delete; auto operator=(paging_root &&) -> paging_root & = delete; + ~paging_root() = delete; + [[nodiscard]] auto translate(linear_address address) const -> std::optional; [[nodiscard]] auto translate(page page) const -> std::optional; diff --git a/arch/x86_64/src/kapi/memory.cpp b/arch/x86_64/src/kapi/memory.cpp index 1a8b36f..e138641 100644 --- a/arch/x86_64/src/kapi/memory.cpp +++ b/arch/x86_64/src/kapi/memory.cpp @@ -26,7 +26,7 @@ namespace teachos::memory auto constinit allocator = static_cast(nullptr); constexpr auto static unused_page_address = linear_address{0x0000'7fff'cafe'faceuz}; - constexpr auto static recursive_page_map_index = 510; + constexpr auto static recursive_page_map_index = x86_64::page_table::entry_count - 2; auto create_memory_information() -> x86_64::region_allocator::memory_information { -- cgit v1.2.3 From 148c54a3d470c6019ebebe1387a7d889a2b8808e Mon Sep 17 00:00:00 2001 From: Felix Morgner Date: Tue, 2 Dec 2025 19:17:42 +0100 Subject: x86_64/memory: introduce frame allocation buffer --- arch/x86_64/CMakeLists.txt | 2 +- .../include/x86_64/memory/buffered_allocator.hpp | 67 ++++++++++++++++++++++ arch/x86_64/src/kapi/memory.cpp | 5 +- 3 files changed, 72 insertions(+), 2 deletions(-) create mode 100644 arch/x86_64/include/x86_64/memory/buffered_allocator.hpp (limited to 'arch/x86_64') diff --git a/arch/x86_64/CMakeLists.txt b/arch/x86_64/CMakeLists.txt index 4d984cf..7f01744 100644 --- a/arch/x86_64/CMakeLists.txt +++ b/arch/x86_64/CMakeLists.txt @@ -35,7 +35,7 @@ target_sources("x86_64" PRIVATE file(GLOB_RECURSE ARCH_HEADERS RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} "include/**.hpp") -target_sources("x86_64" PRIVATE +target_sources("x86_64" PUBLIC FILE_SET HEADERS BASE_DIRS "include" FILES ${ARCH_HEADERS} diff --git a/arch/x86_64/include/x86_64/memory/buffered_allocator.hpp b/arch/x86_64/include/x86_64/memory/buffered_allocator.hpp new file mode 100644 index 0000000..2d05010 --- /dev/null +++ b/arch/x86_64/include/x86_64/memory/buffered_allocator.hpp @@ -0,0 +1,67 @@ +#ifndef TEACHOS_X86_64_BUFFERED_ALLOCATOR_HPP +#define TEACHOS_X86_64_BUFFERED_ALLOCATOR_HPP + +#include "kapi/memory.hpp" + +#include +#include +#include + +namespace teachos::memory::x86_64 +{ + + template + struct buffered_allocator : frame_allocator + { + explicit buffered_allocator(frame_allocator * underlying) + : m_underlying{underlying} + { + std::ranges::generate(m_pool, [this] { return m_underlying->allocate(); }); + } + + buffered_allocator(buffered_allocator const &) = delete; + buffered_allocator(buffered_allocator &&) = default; + + ~buffered_allocator() override + { + std::ranges::for_each(m_pool, [this](auto const & maybe_frame) { + if (maybe_frame) + { + m_underlying->release(*maybe_frame); + } + }); + } + + auto operator=(buffered_allocator const &) = delete; + auto operator=(buffered_allocator &&) = delete; + + auto allocate() -> std::optional override + { + auto found = std::ranges::find_if(m_pool, [](auto const & candidate) { return candidate.has_value(); }); + if (found == std::end(m_pool)) + { + return m_underlying->allocate(); + } + auto frame = found->value(); + found->reset(); + return frame; + } + + auto release(frame frame) -> void override + { + auto found = std::ranges::find_if(m_pool, [](auto const & candidate) { return !candidate; }); + if (found == std::end(m_pool)) + { + return m_underlying->release(frame); + } + (*found) = frame; + } + + private: + frame_allocator * m_underlying; + std::array, BufferSize> m_pool{}; + }; + +} // namespace teachos::memory::x86_64 + +#endif \ No newline at end of file diff --git a/arch/x86_64/src/kapi/memory.cpp b/arch/x86_64/src/kapi/memory.cpp index e138641..703b3e1 100644 --- a/arch/x86_64/src/kapi/memory.cpp +++ b/arch/x86_64/src/kapi/memory.cpp @@ -5,6 +5,7 @@ #include "x86_64/boot/boot.hpp" #include "x86_64/boot/ld.hpp" #include "x86_64/cpu/registers.hpp" +#include "x86_64/memory/buffered_allocator.hpp" #include "x86_64/memory/mmu.hpp" #include "x86_64/memory/page_table.hpp" #include "x86_64/memory/paging_root.hpp" @@ -95,7 +96,9 @@ namespace teachos::memory auto allocator = create_early_frame_allocator(); enable_cpu_protections(); - inject_faux_pml4(allocator); + + auto allocation_buffer = x86_64::buffered_allocator<4>{&allocator}; + inject_faux_pml4(allocation_buffer); // paging::kernel_mapper kernel(allocator, memory_information); // kernel.remap_kernel(); -- cgit v1.2.3 From 9331afdcbbe95bc1bd79d657f0d7c5b91a19a375 Mon Sep 17 00:00:00 2001 From: Felix Morgner Date: Tue, 2 Dec 2025 19:39:03 +0100 Subject: x86_64/memory: fix temporary page unmapping --- arch/x86_64/src/kapi/memory.cpp | 34 ++++++++++++++++++++++++++++++- arch/x86_64/src/memory/scoped_mapping.cpp | 2 ++ 2 files changed, 35 insertions(+), 1 deletion(-) (limited to 'arch/x86_64') diff --git a/arch/x86_64/src/kapi/memory.cpp b/arch/x86_64/src/kapi/memory.cpp index 703b3e1..0e4b78e 100644 --- a/arch/x86_64/src/kapi/memory.cpp +++ b/arch/x86_64/src/kapi/memory.cpp @@ -8,6 +8,7 @@ #include "x86_64/memory/buffered_allocator.hpp" #include "x86_64/memory/mmu.hpp" #include "x86_64/memory/page_table.hpp" +#include "x86_64/memory/page_utilities.hpp" #include "x86_64/memory/paging_root.hpp" #include "x86_64/memory/region_allocator.hpp" #include "x86_64/memory/scoped_mapping.hpp" @@ -63,15 +64,46 @@ namespace teachos::memory using namespace x86_64; using entry_flags = page_table::entry::flags; - auto temporary_mapper = scoped_mapping{page::containing(unused_page_address), allocator}; + auto page = page::containing(unused_page_address); + + auto temporary_mapper = scoped_mapping{page, allocator}; auto new_pml4_frame = allocator.allocate(); auto pml4 = std::construct_at(temporary_mapper.map_as(*new_pml4_frame, entry_flags::writable)); (*pml4)[recursive_page_map_index].frame(new_pml4_frame.value(), entry_flags::present | entry_flags::writable); + + auto pml4_index = pml_index<4>(page); + auto & old_pml4 = paging_root::get(); + auto pml4_entry = old_pml4[pml4_index]; + + auto pml3_index = pml_index<3>(page); + auto old_pml3 = old_pml4.next(pml4_index); + auto pml3_entry = (**old_pml3)[pml3_index]; + + auto pml2_index = pml_index<2>(page); + auto old_pml2 = (**old_pml3).next(pml3_index); + auto pml2_entry = (**old_pml2)[pml2_index]; + + auto pml1_index = pml_index<1>(page); + auto old_pml1 = (**old_pml2).next(pml2_index); + auto pml1_entry = (**old_pml1)[pml1_index]; + paging_root::get()[recursive_page_map_index].frame(new_pml4_frame.value(), entry_flags::present | entry_flags::writable); tlb_flush_all(); + + auto & new_pml4 = paging_root::get(); + new_pml4[pml4_index] = pml4_entry; + + auto new_pml3 = new_pml4.next(pml4_index); + (**new_pml3)[pml3_index] = pml3_entry; + + auto new_pml2 = (**new_pml3).next(pml3_index); + (**new_pml2)[pml2_index] = pml2_entry; + + auto new_pml1 = (**new_pml2).next(pml2_index); + (**new_pml1)[pml1_index] = pml1_entry; } } // namespace diff --git a/arch/x86_64/src/memory/scoped_mapping.cpp b/arch/x86_64/src/memory/scoped_mapping.cpp index 602198e..191a7ad 100644 --- a/arch/x86_64/src/memory/scoped_mapping.cpp +++ b/arch/x86_64/src/memory/scoped_mapping.cpp @@ -89,6 +89,8 @@ namespace teachos::memory::x86_64 auto pml1_index = pml_index<1>(m_page); (*pml1)[pml1_index].frame(frame, page_table::entry::flags::present | flags); + m_mapped = true; + return static_cast(frame.start_address()); } -- cgit v1.2.3 From a08847ded5fba25859e7a3ad06ae3fed342d4d6a Mon Sep 17 00:00:00 2001 From: Felix Morgner Date: Wed, 3 Dec 2025 14:02:07 +0100 Subject: x86_64/boot: move stack to higher half --- arch/x86_64/src/boot/boot32.S | 11 ++++------- arch/x86_64/src/boot/entry64.s | 9 +++++++++ 2 files changed, 13 insertions(+), 7 deletions(-) (limited to 'arch/x86_64') diff --git a/arch/x86_64/src/boot/boot32.S b/arch/x86_64/src/boot/boot32.S index 27eed4d..79b3ec7 100644 --- a/arch/x86_64/src/boot/boot32.S +++ b/arch/x86_64/src/boot/boot32.S @@ -27,12 +27,9 @@ page_maps_size = page_maps_end - page_maps_start .section .boot_stack, "aw", @nobits .align 16 -.global stack_size -.global stack_bottom - -stack_bottom: .skip 1 << 20 -stack_top: -stack_size = stack_top - stack_bottom +early_stack_bottom: .skip 1 << 8 +early_stack_top: +early_stack_size = early_stack_top - early_stack_bottom /** * @brief Constants for the bootstrapping process. @@ -122,7 +119,7 @@ _start: 0: pop %esi - lea (stack_top - 0b)(%esi), %ecx + lea (early_stack_top - 0b)(%esi), %ecx mov %ecx, %esp mov %esp, %ebp diff --git a/arch/x86_64/src/boot/entry64.s b/arch/x86_64/src/boot/entry64.s index 636e4cd..2932354 100644 --- a/arch/x86_64/src/boot/entry64.s +++ b/arch/x86_64/src/boot/entry64.s @@ -11,6 +11,12 @@ .global bootstrap_information bootstrap_information: .skip 16 +.align 16 +.global stack_top +stack_bottom: .skip 1 << 20 +stack_top: +stack_size = stack_top - stack_bottom + .section .boot_text, "ax", @progbits .code64 @@ -23,6 +29,9 @@ _entry64: mov %rax, %fs mov %rax, %gs + mov $stack_top, %rsp + mov %rsp, %rbp + mov multiboot_information_pointer, %rax mov vga_buffer_pointer, %rdx sub $0xb8000, %rdx -- cgit v1.2.3 From 9907cb6c0108b89b846fee52de56a9c335caaedc Mon Sep 17 00:00:00 2001 From: Felix Morgner Date: Wed, 3 Dec 2025 17:08:44 +0100 Subject: x86_64/memory: fix return in scoped_mapping::map Previously, scoped_mapping::map returned the start address of the frame. Unfortunately, the initial mapping performed in the bootstrap code maps physical memory starting at 0x0000'0000'0000'0000, which means no fault was triggered. The map function now correctly return the start address of the scoped_mapping's page, which must alway work by definition. --- arch/x86_64/src/memory/scoped_mapping.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'arch/x86_64') diff --git a/arch/x86_64/src/memory/scoped_mapping.cpp b/arch/x86_64/src/memory/scoped_mapping.cpp index 191a7ad..5446b2a 100644 --- a/arch/x86_64/src/memory/scoped_mapping.cpp +++ b/arch/x86_64/src/memory/scoped_mapping.cpp @@ -91,7 +91,7 @@ namespace teachos::memory::x86_64 m_mapped = true; - return static_cast(frame.start_address()); + return static_cast(m_page.start_address()); } auto scoped_mapping::unmap() -> void -- cgit v1.2.3 From d74936964ecd5e5c961b90feb9323ca999ce9026 Mon Sep 17 00:00:00 2001 From: Felix Morgner Date: Wed, 3 Dec 2025 17:23:12 +0100 Subject: x86_64/memory: improve scoped_mapping docs --- .../include/x86_64/memory/scoped_mapping.hpp | 35 ++++++++++++++++++---- 1 file changed, 30 insertions(+), 5 deletions(-) (limited to 'arch/x86_64') diff --git a/arch/x86_64/include/x86_64/memory/scoped_mapping.hpp b/arch/x86_64/include/x86_64/memory/scoped_mapping.hpp index 5737bb0..dff54ec 100644 --- a/arch/x86_64/include/x86_64/memory/scoped_mapping.hpp +++ b/arch/x86_64/include/x86_64/memory/scoped_mapping.hpp @@ -8,26 +8,51 @@ namespace teachos::memory::x86_64 { + //! A page mapping that, if established, maps a given frame to a given unused page, unmapping it on destruction. It + //! allows for an easy way to quickly map a page that is not required to be mapped forever. When mapping a frame, new + //! page tables may be allocated. On destruction, these pages tables, or rather their respective frames, will be + //! released again. struct scoped_mapping { - scoped_mapping(scoped_mapping const &) = delete; - scoped_mapping(scoped_mapping &&); + //! Copying a scoped mapping would be meaningless. + scoped_mapping(scoped_mapping const &) noexcept = delete; + + //! Adopt an existing scoped mapping, transferring mapping ownership to this new object. + scoped_mapping(scoped_mapping &&) noexcept; + + //! Construct a new scoped mapping, which can be used to map a frame to the given unused page. + //! @param page An unused page. If the page is already mapped, this constructor will panic. + //! @param allocator An allocator to be used to allocate any page maps required to map the frame. scoped_mapping(page page, frame_allocator & allocator); - ~scoped_mapping(); + //! Unmap the mapped frame if one was mapped. + //! @note Any page tables that were allocated to support the mapping will be released. + ~scoped_mapping() noexcept; + //! Copying a scoped mapping would be meaningless. auto operator=(scoped_mapping const &) -> scoped_mapping = delete; - auto operator=(scoped_mapping &&) -> scoped_mapping &; + //! Adopt an existing scoped mapping, swapping mapping ownerships between the objects. + auto operator=(scoped_mapping &&) noexcept -> scoped_mapping &; + + //! Map the given frame with the given flags. + //! @note If a mapping has already been established, this function will panic + //! @param frame A frame to map. + //! @param flags The flags, besides the present flag, to apply to the mapping. + //! @return A pointer to the first byte of the mapped frame. auto map(frame frame, page_table::entry::flags flags) -> std::byte *; - auto unmap() -> void; + //! Map the given frame, returning a typed pointer. template auto map_as(frame frame, page_table::entry::flags flags) -> DataType * { return std::bit_cast(map(frame, flags)); } + //! Unmap the mapped frame. + //! @note If no frame was ever mapped, this function will panic. + auto unmap() -> void; + private: page m_page; frame_allocator * m_allocator; -- cgit v1.2.3 From 448632c3c9c919e8eda44e8a83082f60983057b7 Mon Sep 17 00:00:00 2001 From: Felix Morgner Date: Wed, 3 Dec 2025 20:16:36 +0100 Subject: x86_64/memory: add missing noexcept specifiers --- arch/x86_64/src/memory/scoped_mapping.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'arch/x86_64') diff --git a/arch/x86_64/src/memory/scoped_mapping.cpp b/arch/x86_64/src/memory/scoped_mapping.cpp index 5446b2a..6e2328d 100644 --- a/arch/x86_64/src/memory/scoped_mapping.cpp +++ b/arch/x86_64/src/memory/scoped_mapping.cpp @@ -14,7 +14,7 @@ namespace teachos::memory::x86_64 { - scoped_mapping::scoped_mapping(scoped_mapping && other) + scoped_mapping::scoped_mapping(scoped_mapping && other) noexcept : m_page{std::exchange(other.m_page, page{})} , m_allocator{std::exchange(other.m_allocator, nullptr)} , m_mapped{std::exchange(other.m_mapped, false)} @@ -40,7 +40,7 @@ namespace teachos::memory::x86_64 } } - auto scoped_mapping::operator=(scoped_mapping && other) -> scoped_mapping & + auto scoped_mapping::operator=(scoped_mapping && other) noexcept -> scoped_mapping & { if (&other == this) { -- cgit v1.2.3 From f3dd2b2f5bd1b5dd4d4309a30db3c7733b8f63bb Mon Sep 17 00:00:00 2001 From: Felix Morgner Date: Wed, 3 Dec 2025 20:40:54 +0100 Subject: x86_64/memory: only deallocate allocated frames --- .../include/x86_64/memory/scoped_mapping.hpp | 1 + arch/x86_64/src/memory/scoped_mapping.cpp | 39 +++++++++++++++------- 2 files changed, 28 insertions(+), 12 deletions(-) (limited to 'arch/x86_64') diff --git a/arch/x86_64/include/x86_64/memory/scoped_mapping.hpp b/arch/x86_64/include/x86_64/memory/scoped_mapping.hpp index dff54ec..99585bc 100644 --- a/arch/x86_64/include/x86_64/memory/scoped_mapping.hpp +++ b/arch/x86_64/include/x86_64/memory/scoped_mapping.hpp @@ -57,6 +57,7 @@ namespace teachos::memory::x86_64 page m_page; frame_allocator * m_allocator; bool m_mapped; + std::uint8_t m_allocated; }; } // namespace teachos::memory::x86_64 diff --git a/arch/x86_64/src/memory/scoped_mapping.cpp b/arch/x86_64/src/memory/scoped_mapping.cpp index 6e2328d..be15330 100644 --- a/arch/x86_64/src/memory/scoped_mapping.cpp +++ b/arch/x86_64/src/memory/scoped_mapping.cpp @@ -18,12 +18,14 @@ namespace teachos::memory::x86_64 : m_page{std::exchange(other.m_page, page{})} , m_allocator{std::exchange(other.m_allocator, nullptr)} , m_mapped{std::exchange(other.m_mapped, false)} + , m_allocated{std::exchange(other.m_allocated, 0)} {} scoped_mapping::scoped_mapping(page page, frame_allocator & allocator) : m_page{page} , m_allocator{&allocator} , m_mapped{false} + , m_allocated{} { if (paging_root::get().translate(page)) { @@ -52,6 +54,7 @@ namespace teachos::memory::x86_64 swap(m_page, other.m_page); swap(m_allocator, other.m_allocator); swap(m_mapped, other.m_mapped); + swap(m_allocated, other.m_allocated); return *this; } @@ -65,6 +68,7 @@ namespace teachos::memory::x86_64 auto new_frame = m_allocator->allocate(); pml4[pml4_index].frame(*new_frame, page_table::entry::flags::present | flags); std::construct_at(pml4.next(pml4_index).value()); + m_allocated |= 1uz << 2; } auto pml3 = pml4.next(pml4_index).value(); @@ -74,6 +78,7 @@ namespace teachos::memory::x86_64 auto new_frame = m_allocator->allocate(); (*pml3)[pml3_index].frame(*new_frame, page_table::entry::flags::present | flags); std::construct_at((*pml3).next(pml3_index).value()); + m_allocated |= 1uz << 1; } auto pml2 = (*pml3).next(pml3_index).value(); @@ -83,6 +88,7 @@ namespace teachos::memory::x86_64 auto new_frame = m_allocator->allocate(); (*pml2)[pml2_index].frame(*new_frame, page_table::entry::flags::present | flags); std::construct_at((*pml2).next(pml2_index).value()); + m_allocated |= 1uz << 0; } auto pml1 = (*pml2).next(pml2_index).value(); @@ -105,25 +111,34 @@ namespace teachos::memory::x86_64 auto pml2 = pml3->next(pml_index<3>(m_page)).value(); auto pml1 = pml2->next(pml_index<2>(m_page)).value(); - auto pml1_entry = (*pml1)[pml_index<1>(m_page)]; - (*pml1)[pml_index<1>(m_page)].clear(); - if (pml1->empty()) + if (m_allocated & 1uz << 0) { - m_allocator->release(pml1_entry.frame().value()); + auto pml1_entry = (*pml1)[pml_index<1>(m_page)]; + (*pml1)[pml_index<1>(m_page)].clear(); + if (pml1->empty()) + { + m_allocator->release(pml1_entry.frame().value()); + } } - auto pml2_entry = (*pml2)[pml_index<2>(m_page)]; - (*pml2)[pml_index<2>(m_page)].clear(); - if (pml2->empty()) + if (m_allocated & 1uz << 1) { - m_allocator->release(pml2_entry.frame().value()); + auto pml2_entry = (*pml2)[pml_index<2>(m_page)]; + (*pml2)[pml_index<2>(m_page)].clear(); + if (pml2->empty()) + { + m_allocator->release(pml2_entry.frame().value()); + } } - auto pml3_entry = (*pml3)[pml_index<3>(m_page)]; - (*pml3)[pml_index<3>(m_page)].clear(); - if (pml3->empty()) + if (m_allocated & 1uz << 2) { - m_allocator->release(pml3_entry.frame().value()); + auto pml3_entry = (*pml3)[pml_index<3>(m_page)]; + (*pml3)[pml_index<3>(m_page)].clear(); + if (pml3->empty()) + { + m_allocator->release(pml3_entry.frame().value()); + } } m_mapped = false; -- cgit v1.2.3 From fc26187d9ace9798d9494341f3513eb0840b006d Mon Sep 17 00:00:00 2001 From: Felix Morgner Date: Wed, 3 Dec 2025 20:45:30 +0100 Subject: x86_64/memory: make scoped_mapping swappable --- .../include/x86_64/memory/scoped_mapping.hpp | 4 +++ arch/x86_64/src/memory/scoped_mapping.cpp | 31 +++++++++++++--------- 2 files changed, 23 insertions(+), 12 deletions(-) (limited to 'arch/x86_64') diff --git a/arch/x86_64/include/x86_64/memory/scoped_mapping.hpp b/arch/x86_64/include/x86_64/memory/scoped_mapping.hpp index 99585bc..d4844fb 100644 --- a/arch/x86_64/include/x86_64/memory/scoped_mapping.hpp +++ b/arch/x86_64/include/x86_64/memory/scoped_mapping.hpp @@ -53,6 +53,8 @@ namespace teachos::memory::x86_64 //! @note If no frame was ever mapped, this function will panic. auto unmap() -> void; + auto swap(scoped_mapping & other) -> void; + private: page m_page; frame_allocator * m_allocator; @@ -60,6 +62,8 @@ namespace teachos::memory::x86_64 std::uint8_t m_allocated; }; + auto swap(scoped_mapping & lhs, scoped_mapping & rhs) -> void; + } // namespace teachos::memory::x86_64 #endif \ No newline at end of file diff --git a/arch/x86_64/src/memory/scoped_mapping.cpp b/arch/x86_64/src/memory/scoped_mapping.cpp index be15330..c9c6459 100644 --- a/arch/x86_64/src/memory/scoped_mapping.cpp +++ b/arch/x86_64/src/memory/scoped_mapping.cpp @@ -44,18 +44,7 @@ namespace teachos::memory::x86_64 auto scoped_mapping::operator=(scoped_mapping && other) noexcept -> scoped_mapping & { - if (&other == this) - { - return *this; - } - - using std::swap; - - swap(m_page, other.m_page); - swap(m_allocator, other.m_allocator); - swap(m_mapped, other.m_mapped); - swap(m_allocated, other.m_allocated); - + this->swap(other); return *this; } @@ -144,4 +133,22 @@ namespace teachos::memory::x86_64 m_mapped = false; } + auto scoped_mapping::swap(scoped_mapping & other) -> void + { + using std::swap; + + if (&other == this) + return; + + swap(m_page, other.m_page); + swap(m_allocator, other.m_allocator); + swap(m_mapped, other.m_mapped); + swap(m_allocated, other.m_allocated); + } + + auto swap(scoped_mapping & lhs, scoped_mapping & rhs) -> void + { + lhs.swap(rhs); + } + } // namespace teachos::memory::x86_64 \ No newline at end of file -- cgit v1.2.3 From 36cd516c84cf2edd16defcd39e99e2bee0bca892 Mon Sep 17 00:00:00 2001 From: Felix Morgner Date: Thu, 4 Dec 2025 22:52:35 +0100 Subject: x86_64/memory: simplify initialization implementation --- arch/x86_64/src/kapi/memory.cpp | 27 ++++++++++++++------------- 1 file changed, 14 insertions(+), 13 deletions(-) (limited to 'arch/x86_64') diff --git a/arch/x86_64/src/kapi/memory.cpp b/arch/x86_64/src/kapi/memory.cpp index 0e4b78e..d09a4c1 100644 --- a/arch/x86_64/src/kapi/memory.cpp +++ b/arch/x86_64/src/kapi/memory.cpp @@ -30,18 +30,7 @@ namespace teachos::memory constexpr auto static unused_page_address = linear_address{0x0000'7fff'cafe'faceuz}; constexpr auto static recursive_page_map_index = x86_64::page_table::entry_count - 2; - auto create_memory_information() -> x86_64::region_allocator::memory_information - { - auto const & mbi = boot::bootstrap_information.mbi; - auto mbi_span = std::span{std::bit_cast(mbi), mbi->size_bytes()}; - auto image_span = std::span{&boot::x86_64::_start_physical, &boot::x86_64::_end_physical}; - - return {.image_range = - std::make_pair(physical_address{&image_span.front()}, physical_address{&image_span.back()}), - .mbi_range = std::make_pair(physical_address{&mbi_span.front()}, physical_address{&mbi_span.back()}), - .memory_map = mbi->memory_map()}; - }; - + //! Instantiate a basic, memory region based, early frame allocator for remapping. auto create_early_frame_allocator() { auto memory_map = boot::bootstrap_information.mbi->maybe_memory_map(); @@ -50,15 +39,27 @@ namespace teachos::memory system::panic("[x86_64] Failed to create early allocator, no memory map available."); } - return x86_64::region_allocator{create_memory_information()}; + auto const & mbi = boot::bootstrap_information.mbi; + auto mbi_span = std::span{std::bit_cast(mbi), mbi->size_bytes()}; + auto image_span = std::span{&boot::x86_64::_start_physical, &boot::x86_64::_end_physical}; + + return x86_64::region_allocator{ + { + .image_range = std::make_pair(physical_address{&image_span.front()}, physical_address{&image_span.back()}), + .mbi_range = std::make_pair(physical_address{&mbi_span.front()}, physical_address{&mbi_span.back()}), + .memory_map = *memory_map, + } + }; } + //! Enable additional CPU protection features, required during later stages of the kernel. auto enable_cpu_protections() -> void { cpu::x86_64::cr0::set(cpu::x86_64::cr0::flags::write_protect); cpu::x86_64::i32_efer::set(cpu::x86_64::i32_efer::flags::execute_disable_bit_enable); } + //! Inject, or graft, a faux recursive PML4 into the active page mapping structure. auto inject_faux_pml4(frame_allocator & allocator) -> void { using namespace x86_64; -- cgit v1.2.3 From 448a79328544e3ecb72d0b3b95c0b9b0d49faafc Mon Sep 17 00:00:00 2001 From: Felix Morgner Date: Wed, 10 Dec 2025 12:27:37 +0100 Subject: x86_64/memory: fix scoped_mapping unmap logic --- arch/x86_64/src/memory/scoped_mapping.cpp | 32 +++++++++++++------------------ 1 file changed, 13 insertions(+), 19 deletions(-) (limited to 'arch/x86_64') diff --git a/arch/x86_64/src/memory/scoped_mapping.cpp b/arch/x86_64/src/memory/scoped_mapping.cpp index c9c6459..44dbf45 100644 --- a/arch/x86_64/src/memory/scoped_mapping.cpp +++ b/arch/x86_64/src/memory/scoped_mapping.cpp @@ -96,38 +96,32 @@ namespace teachos::memory::x86_64 system::panic("[MEM] Tried to release an unmapped temporary mapping!"); } - auto pml3 = paging_root::get().next(pml_index<4>(m_page)).value(); + auto pml4 = &paging_root::get(); + auto pml3 = pml4->next(pml_index<4>(m_page)).value(); auto pml2 = pml3->next(pml_index<3>(m_page)).value(); auto pml1 = pml2->next(pml_index<2>(m_page)).value(); + (*pml1)[pml_index<1>(m_page)].clear(); + if (m_allocated & 1uz << 0) { - auto pml1_entry = (*pml1)[pml_index<1>(m_page)]; - (*pml1)[pml_index<1>(m_page)].clear(); - if (pml1->empty()) - { - m_allocator->release(pml1_entry.frame().value()); - } + auto pml1_frame = (*pml2)[pml_index<2>(m_page)].frame().value(); + m_allocator->release(pml1_frame); + (*pml2)[pml_index<2>(m_page)].clear(); } if (m_allocated & 1uz << 1) { - auto pml2_entry = (*pml2)[pml_index<2>(m_page)]; - (*pml2)[pml_index<2>(m_page)].clear(); - if (pml2->empty()) - { - m_allocator->release(pml2_entry.frame().value()); - } + auto pml2_frame = (*pml3)[pml_index<3>(m_page)].frame().value(); + m_allocator->release(pml2_frame); + (*pml3)[pml_index<3>(m_page)].clear(); } if (m_allocated & 1uz << 2) { - auto pml3_entry = (*pml3)[pml_index<3>(m_page)]; - (*pml3)[pml_index<3>(m_page)].clear(); - if (pml3->empty()) - { - m_allocator->release(pml3_entry.frame().value()); - } + auto pml3_frame = (*pml4)[pml_index<4>(m_page)].frame().value(); + m_allocator->release(pml3_frame); + (*pml4)[pml_index<4>(m_page)].clear(); } m_mapped = false; -- cgit v1.2.3 From b9d445bf92725d79269becf978059e040519c00a Mon Sep 17 00:00:00 2001 From: Felix Morgner Date: Wed, 10 Dec 2025 16:51:44 +0100 Subject: x86_64/memory: implement simple kernel remapper --- arch/x86_64/CMakeLists.txt | 1 + .../x86_64/include/x86_64/memory/kernel_mapper.hpp | 33 +++++++ arch/x86_64/include/x86_64/memory/page_table.hpp | 16 +++ arch/x86_64/include/x86_64/memory/paging_root.hpp | 8 ++ arch/x86_64/src/kapi/memory.cpp | 20 ++-- arch/x86_64/src/memory/kernel_mapper.cpp | 107 +++++++++++++++++++++ arch/x86_64/src/memory/page_table.cpp | 13 +++ arch/x86_64/src/memory/paging_root.cpp | 55 ++++++++++- 8 files changed, 246 insertions(+), 7 deletions(-) create mode 100644 arch/x86_64/include/x86_64/memory/kernel_mapper.hpp create mode 100644 arch/x86_64/src/memory/kernel_mapper.cpp (limited to 'arch/x86_64') diff --git a/arch/x86_64/CMakeLists.txt b/arch/x86_64/CMakeLists.txt index 7f01744..0b9009c 100644 --- a/arch/x86_64/CMakeLists.txt +++ b/arch/x86_64/CMakeLists.txt @@ -23,6 +23,7 @@ target_sources("x86_64" PRIVATE "src/kapi/memory.cpp" # Memory management + "src/memory/kernel_mapper.cpp" "src/memory/mmu.cpp" "src/memory/page_table.cpp" "src/memory/paging_root.cpp" diff --git a/arch/x86_64/include/x86_64/memory/kernel_mapper.hpp b/arch/x86_64/include/x86_64/memory/kernel_mapper.hpp new file mode 100644 index 0000000..4b681ae --- /dev/null +++ b/arch/x86_64/include/x86_64/memory/kernel_mapper.hpp @@ -0,0 +1,33 @@ +#ifndef TEACHOS_X86_64_KERNEL_MAPPER_HPP +#define TEACHOS_X86_64_KERNEL_MAPPER_HPP + +#include "kapi/memory.hpp" + +#include +#include +#include + +#include + +namespace teachos::memory::x86_64 +{ + + struct kernel_mapper + { + using section_header_type = elf::section_header; + + kernel_mapper(frame_allocator & allocator, multiboot2::information_view const * mbi); + + auto remap_kernel() -> void; + + private: + auto map_section(section_header_type const & section, std::string_view name) -> void; + + frame_allocator * m_allocator; + multiboot2::information_view const * m_mbi; + std::uintptr_t m_kernel_load_base; + }; + +} // namespace teachos::memory::x86_64 + +#endif \ No newline at end of file diff --git a/arch/x86_64/include/x86_64/memory/page_table.hpp b/arch/x86_64/include/x86_64/memory/page_table.hpp index c5102a2..ce34e23 100644 --- a/arch/x86_64/include/x86_64/memory/page_table.hpp +++ b/arch/x86_64/include/x86_64/memory/page_table.hpp @@ -51,6 +51,12 @@ namespace teachos::memory::x86_64 //! Get all flags present in this entry. [[nodiscard]] auto all_flags() const -> flags; + //! Set the flags of this entry to the given set. + auto all_flags(flags flags) -> void; + + //! Additively set the given flags in this entry. + auto operator|=(flags rhs) -> entry &; + //! Get the frame number associated with this entry, if the referenced page is present. [[nodiscard]] auto frame() const -> std::optional; @@ -86,11 +92,21 @@ namespace teachos::memory::x86_64 return std::bit_cast(std::to_underlying(lhs) | std::to_underlying(rhs)); } + constexpr auto operator|=(page_table::entry::flags & lhs, page_table::entry::flags rhs) -> page_table::entry::flags & + { + return lhs = lhs | rhs; + } + constexpr auto operator&(page_table::entry::flags lhs, page_table::entry::flags rhs) -> page_table::entry::flags { return std::bit_cast(std::to_underlying(lhs) & std::to_underlying(rhs)); } + constexpr auto operator~(page_table::entry::flags flags) + { + return std::bit_cast(~std::to_underlying(flags)); + } + //! A recursively mapped page table. template requires(Level > 0uz && Level < 5uz) diff --git a/arch/x86_64/include/x86_64/memory/paging_root.hpp b/arch/x86_64/include/x86_64/memory/paging_root.hpp index a9ab6a6..d4aa023 100644 --- a/arch/x86_64/include/x86_64/memory/paging_root.hpp +++ b/arch/x86_64/include/x86_64/memory/paging_root.hpp @@ -25,6 +25,14 @@ namespace teachos::memory::x86_64 [[nodiscard]] auto translate(linear_address address) const -> std::optional; [[nodiscard]] auto translate(page page) const -> std::optional; + //! Map the given page into the given frame using the given flags. + //! + //! @param page A page to map. + //! @param frame The frame into which to map the page. + //! @param flags The flags to apply to the mapping. + //! @param allocator The frame allocator used to allocate any required page tables. + auto map(page page, frame frame, entry::flags flags, frame_allocator & allocator) -> std::optional; + private: paging_root() = default; }; diff --git a/arch/x86_64/src/kapi/memory.cpp b/arch/x86_64/src/kapi/memory.cpp index d09a4c1..ed3edef 100644 --- a/arch/x86_64/src/kapi/memory.cpp +++ b/arch/x86_64/src/kapi/memory.cpp @@ -1,11 +1,14 @@ #include "kapi/memory.hpp" +#include "kapi/cio.hpp" #include "kapi/system.hpp" #include "x86_64/boot/boot.hpp" #include "x86_64/boot/ld.hpp" +#include "x86_64/cpu/impl/control_registers.hpp" #include "x86_64/cpu/registers.hpp" #include "x86_64/memory/buffered_allocator.hpp" +#include "x86_64/memory/kernel_mapper.hpp" #include "x86_64/memory/mmu.hpp" #include "x86_64/memory/page_table.hpp" #include "x86_64/memory/page_utilities.hpp" @@ -60,7 +63,7 @@ namespace teachos::memory } //! Inject, or graft, a faux recursive PML4 into the active page mapping structure. - auto inject_faux_pml4(frame_allocator & allocator) -> void + auto inject_faux_pml4(frame_allocator & allocator) { using namespace x86_64; using entry_flags = page_table::entry::flags; @@ -105,6 +108,8 @@ namespace teachos::memory auto new_pml1 = (**new_pml2).next(pml2_index); (**new_pml1)[pml1_index] = pml1_entry; + + return *new_pml4_frame; } } // namespace @@ -131,12 +136,15 @@ namespace teachos::memory enable_cpu_protections(); auto allocation_buffer = x86_64::buffered_allocator<4>{&allocator}; - inject_faux_pml4(allocation_buffer); + auto new_pml4_frame = inject_faux_pml4(allocation_buffer); - // 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(); + auto mapper = x86_64::kernel_mapper{allocation_buffer, boot::bootstrap_information.mbi}; + mapper.remap_kernel(); + cio::println("[x86_64:MEM] prepared new kernel image page maps."); + + auto cr3 = cpu::x86_64::cr3::read(); + cr3.address(new_pml4_frame.start_address()); + cpu::x86_64::cr3::write(cr3); // 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); diff --git a/arch/x86_64/src/memory/kernel_mapper.cpp b/arch/x86_64/src/memory/kernel_mapper.cpp new file mode 100644 index 0000000..b1d12a4 --- /dev/null +++ b/arch/x86_64/src/memory/kernel_mapper.cpp @@ -0,0 +1,107 @@ +#include "x86_64/memory/kernel_mapper.hpp" + +#include "kapi/cio.hpp" +#include "kapi/memory.hpp" +#include "kapi/system.hpp" + +#include "x86_64/boot/ld.hpp" +#include "x86_64/memory/page_table.hpp" +#include "x86_64/memory/paging_root.hpp" + +#include +#include + +#include +#include + +namespace teachos::memory::x86_64 +{ + + inline namespace + { + using namespace std::string_view_literals; + + constexpr auto static ignored_section_prefixes = std::array{ + ".boot_"sv, + }; + + constexpr auto static user_accessible_prefixes = std::array{ + ".user"sv, + ".stl"sv, + }; + + } // namespace + + kernel_mapper::kernel_mapper(frame_allocator & allocator, multiboot2::information_view const * mbi) + : m_allocator{&allocator} + , m_mbi(std::move(mbi)) + , m_kernel_load_base{std::bit_cast(&boot::x86_64::TEACHOS_VMA)} + {} + + auto kernel_mapper::remap_kernel() -> void + { + auto elf_information = m_mbi->maybe_elf_symbols(); + if (!elf_information) + { + system::panic("[x86_64:MEM] ELF section information is not available."); + } + + auto sections = *elf_information; + auto allocated_sections = + std::views::all(sections) | std::views::filter(&elf::section_header::allocated) | + std::views::filter([&](auto const & section) -> auto { + auto name = sections.name(section); + return !std::ranges::any_of(ignored_section_prefixes, + [&](auto const & prefix) -> auto { return name.starts_with(prefix); }); + }); + + if (allocated_sections.empty()) + { + system::panic("[x86_64:MEM] No allocated ELF sections were found."); + } + + std::ranges::for_each(allocated_sections, + [&](auto const & section) -> auto { map_section(section, sections.name(section)); }); + } + + auto kernel_mapper::map_section(section_header_type const & section, std::string_view name) -> void + { + cio::print("[x86_64:MEM] mapping "); + cio::println(name); + + auto number_of_pages = (section.size + (PLATFORM_PAGE_SIZE - 1)) / PLATFORM_PAGE_SIZE; + + auto linear_start_address = linear_address{section.virtual_load_address}; + auto physical_start_address = physical_address{section.virtual_load_address & ~m_kernel_load_base}; + + auto first_page = page::containing(linear_start_address); + auto first_frame = frame::containing(physical_start_address); + + auto page_flags = page_table::entry::flags::empty; + + if (section.writable()) + { + page_flags |= page_table::entry::flags::writable; + } + + if (!section.executable()) + { + page_flags |= page_table::entry::flags::no_execute; + } + + auto is_prefix_of_name = [=](auto prefix) -> bool { + return name.starts_with(prefix); + }; + + if (std::ranges::any_of(user_accessible_prefixes, is_prefix_of_name)) + { + page_flags |= page_table::entry::flags::user_accessible; + } + + for (auto i = 0uz; i < number_of_pages; ++i) + { + paging_root::get().map(page{first_page.number() + i}, frame{first_frame.number() + i}, page_flags, *m_allocator); + } + } + +} // namespace teachos::memory::x86_64 \ No newline at end of file diff --git a/arch/x86_64/src/memory/page_table.cpp b/arch/x86_64/src/memory/page_table.cpp index 60bf94d..e797b22 100644 --- a/arch/x86_64/src/memory/page_table.cpp +++ b/arch/x86_64/src/memory/page_table.cpp @@ -1,6 +1,7 @@ #include "x86_64/memory/page_table.hpp" #include +#include namespace teachos::memory::x86_64 { @@ -25,6 +26,18 @@ namespace teachos::memory::x86_64 return std::bit_cast(m_raw & ~frame_number_mask); } + auto page_table::entry::all_flags(flags flags) -> void + { + m_raw = (m_raw & ~frame_number_mask) | std::to_underlying(flags); + } + + auto page_table::entry::operator|=(flags rhs) -> page_table::entry & + { + auto raw_flags = std::to_underlying(rhs) & ~frame_number_mask; + m_raw |= raw_flags; + return *this; + } + auto page_table::entry::frame() const -> std::optional { if (present()) diff --git a/arch/x86_64/src/memory/paging_root.cpp b/arch/x86_64/src/memory/paging_root.cpp index 6b8e1ab..5ca2bf0 100644 --- a/arch/x86_64/src/memory/paging_root.cpp +++ b/arch/x86_64/src/memory/paging_root.cpp @@ -1,10 +1,15 @@ #include "x86_64/memory/paging_root.hpp" #include "kapi/memory.hpp" +#include "kapi/system.hpp" +#include "x86_64/memory/page_table.hpp" #include "x86_64/memory/page_utilities.hpp" +#include "x86_64/memory/scoped_mapping.hpp" +#include #include +#include #include namespace teachos::memory::x86_64 @@ -13,7 +18,45 @@ namespace teachos::memory::x86_64 namespace { constexpr auto PML_RECURSIVE_BASE = std::uintptr_t{0177777'776'776'776'776'0000uz}; - } + + //! Perform the actual mapping of the page, via the recursive page map. + //! + //! On any level above PML1, the entries need to not be no_execute, because the image is densely packed. The entries + //! also need to be writable, since the mapping is being performed through the recursive page map hierarchy. When + //! setting the final entry in the PML1, the actually desired flags are set as is, with the present bit added, thus + //! still enforcing non-writability and non-execution of the affected page. + template + requires(Level > 1uz && Level < 5uz) + auto do_map(recursive_page_table * pml, page page, page_table::entry::flags flags, + frame_allocator & allocator) + { + auto index = pml_index(page); + flags = flags & ~page_table::entry::flags::no_execute; + flags = flags | page_table::entry::flags::writable; + if (!(*pml)[index].present()) + { + auto new_table_frame = allocator.allocate(); + auto mapping = scoped_mapping{page, allocator}; + (*pml)[index].frame(new_table_frame.value(), page_table::entry::flags::present | flags); + auto new_table = std::optional{std::construct_at(*pml->next(index))}; + return new_table; + } + (*pml)[index] |= flags; + return pml->next(index); + } + + //! Perform the actual PML1 update. + auto do_map(page_table * pml, page page, frame frame, page_table::entry::flags flags) -> std::optional + { + auto index = pml_index<1>(page); + if ((*pml)[index].present()) + { + system::panic("[x86_64:MEM] Tried to map a page that is already mapped"); + } + (*pml)[index].frame(frame, page_table::entry::flags::present | flags); + return std::optional{static_cast(page.start_address())}; + } + } // namespace auto paging_root::get() -> paging_root & { @@ -71,4 +114,14 @@ namespace teachos::memory::x86_64 .or_else(handle_huge_page); } + auto paging_root::map(page page, frame frame, page_table::entry::flags flags, frame_allocator & allocator) + -> std::optional + { + return std::optional{this} + .and_then([&](auto pml) -> auto { return do_map(pml, page, flags, allocator); }) + .and_then([&](auto pml) -> auto { return do_map(pml, page, flags, allocator); }) + .and_then([&](auto pml) -> auto { return do_map(pml, page, flags, allocator); }) + .and_then([&](auto pml) -> auto { return do_map(pml, page, frame, flags); }); + } + } // namespace teachos::memory::x86_64 \ No newline at end of file -- cgit v1.2.3 From f0c5ac3c8222d4d89b8e2d2a726427a7ec64e538 Mon Sep 17 00:00:00 2001 From: Felix Morgner Date: Wed, 10 Dec 2025 17:20:14 +0100 Subject: kstd: extract bitwise enum operations --- .../include/x86_64/cpu/impl/control_registers.hpp | 77 +++++----------------- .../x86_64/cpu/impl/model_specific_register.hpp | 44 ++++--------- arch/x86_64/include/x86_64/memory/page_table.hpp | 31 ++++----- 3 files changed, 42 insertions(+), 110 deletions(-) (limited to 'arch/x86_64') diff --git a/arch/x86_64/include/x86_64/cpu/impl/control_registers.hpp b/arch/x86_64/include/x86_64/cpu/impl/control_registers.hpp index d892360..0c2254a 100644 --- a/arch/x86_64/include/x86_64/cpu/impl/control_registers.hpp +++ b/arch/x86_64/include/x86_64/cpu/impl/control_registers.hpp @@ -1,8 +1,12 @@ #ifndef TEACHOS_X86_64_CPU_IMPL_CONTROL_REGISTERS_HPP #define TEACHOS_X86_64_CPU_IMPL_CONTROL_REGISTERS_HPP +// IWYU pragma: private + #include "kapi/memory/address.hpp" +#include + #include #include #include @@ -83,72 +87,12 @@ namespace teachos::cpu::x86_64 paging = 1uz << 31 }; - constexpr auto operator|(cr0_flags lhs, cr0_flags rhs) -> cr0_flags - { - return static_cast(static_cast>(lhs) | - static_cast>(rhs)); - } - - constexpr auto operator|=(cr0_flags & lhs, cr0_flags rhs) -> cr0_flags & - { - lhs = lhs | rhs; - return lhs; - } - - constexpr auto operator&(cr0_flags lhs, cr0_flags rhs) -> cr0_flags - { - return static_cast(static_cast>(lhs) & - static_cast>(rhs)); - } - - constexpr auto operator&=(cr0_flags & lhs, cr0_flags rhs) -> cr0_flags & - { - lhs = lhs & rhs; - return lhs; - } - - constexpr auto operator~(cr0_flags lhs) -> cr0_flags - { - // NOLINTNEXTLINE(clang-analyzer-optin.core.EnumCastOutOfRange) - return static_cast(~static_cast>(lhs)); - } - enum struct cr3_flags : std::uint64_t { page_level_write_through = 1uz << 0, page_level_cache_disable = 1uz << 1, }; - constexpr auto operator|(cr3_flags lhs, cr3_flags rhs) -> cr3_flags - { - return static_cast(static_cast>(lhs) | - static_cast>(rhs)); - } - - constexpr auto operator|=(cr3_flags & lhs, cr3_flags rhs) -> cr3_flags & - { - lhs = lhs | rhs; - return lhs; - } - - constexpr auto operator&(cr3_flags lhs, cr3_flags rhs) -> cr3_flags - { - return static_cast(static_cast>(lhs) & - static_cast>(rhs)); - } - - constexpr auto operator&=(cr3_flags & lhs, cr3_flags rhs) -> cr3_flags & - { - lhs = lhs & rhs; - return lhs; - } - - constexpr auto operator~(cr3_flags lhs) -> cr3_flags - { - // NOLINTNEXTLINE(clang-analyzer-optin.core.EnumCastOutOfRange) - return static_cast(~static_cast>(lhs)); - } - struct cr3_value { constexpr cr3_value() = default; @@ -193,4 +137,17 @@ namespace teachos::cpu::x86_64 } // namespace teachos::cpu::x86_64 +namespace kstd::ext +{ + template<> + struct is_bitfield_enum : std::true_type + { + }; + + template<> + struct is_bitfield_enum : std::true_type + { + }; +} // namespace kstd::ext + #endif \ No newline at end of file diff --git a/arch/x86_64/include/x86_64/cpu/impl/model_specific_register.hpp b/arch/x86_64/include/x86_64/cpu/impl/model_specific_register.hpp index 8a41a8a..ff2c8ad 100644 --- a/arch/x86_64/include/x86_64/cpu/impl/model_specific_register.hpp +++ b/arch/x86_64/include/x86_64/cpu/impl/model_specific_register.hpp @@ -1,6 +1,10 @@ #ifndef TEACHOS_X86_64_CPU_IMPL_MODEL_SPECIFIC_REGISTER_HPP #define TEACHOS_X86_64_CPU_IMPL_MODEL_SPECIFIC_REGISTER_HPP +// IWYU pragma: private + +#include + #include #include #include @@ -73,40 +77,20 @@ namespace teachos::cpu::x86_64 execute_disable_bit_enable = 1uz << 11, }; - constexpr auto operator|(ia32_efer_flags lhs, ia32_efer_flags rhs) -> ia32_efer_flags - { - return static_cast(static_cast>(lhs) | - static_cast>(rhs)); - } - - constexpr auto operator|=(ia32_efer_flags & lhs, ia32_efer_flags rhs) -> ia32_efer_flags & - { - lhs = lhs | rhs; - return lhs; - } - - constexpr auto operator&(ia32_efer_flags lhs, ia32_efer_flags rhs) -> ia32_efer_flags - { - return static_cast(static_cast>(lhs) & - static_cast>(rhs)); - } - - constexpr auto operator&=(ia32_efer_flags & lhs, ia32_efer_flags rhs) -> ia32_efer_flags & - { - lhs = lhs & rhs; - return lhs; - } - - constexpr auto operator~(ia32_efer_flags lhs) -> ia32_efer_flags - { - // NOLINTNEXTLINE(clang-analyzer-optin.core.EnumCastOutOfRange) - return static_cast(~static_cast>(lhs)); - } - } // namespace impl using i32_efer = impl::model_specific_register; } // namespace teachos::cpu::x86_64 +namespace kstd::ext +{ + + template<> + struct is_bitfield_enum : std::true_type + { + }; + +} // namespace kstd::ext + #endif \ No newline at end of file diff --git a/arch/x86_64/include/x86_64/memory/page_table.hpp b/arch/x86_64/include/x86_64/memory/page_table.hpp index ce34e23..22616e8 100644 --- a/arch/x86_64/include/x86_64/memory/page_table.hpp +++ b/arch/x86_64/include/x86_64/memory/page_table.hpp @@ -3,11 +3,14 @@ #include "kapi/memory.hpp" +#include + #include #include #include #include #include +#include #include namespace teachos::memory::x86_64 @@ -87,26 +90,6 @@ namespace teachos::memory::x86_64 std::array m_entries{}; }; - constexpr auto operator|(page_table::entry::flags lhs, page_table::entry::flags rhs) -> page_table::entry::flags - { - return std::bit_cast(std::to_underlying(lhs) | std::to_underlying(rhs)); - } - - constexpr auto operator|=(page_table::entry::flags & lhs, page_table::entry::flags rhs) -> page_table::entry::flags & - { - return lhs = lhs | rhs; - } - - constexpr auto operator&(page_table::entry::flags lhs, page_table::entry::flags rhs) -> page_table::entry::flags - { - return std::bit_cast(std::to_underlying(lhs) & std::to_underlying(rhs)); - } - - constexpr auto operator~(page_table::entry::flags flags) - { - return std::bit_cast(~std::to_underlying(flags)); - } - //! A recursively mapped page table. template requires(Level > 0uz && Level < 5uz) @@ -155,4 +138,12 @@ namespace teachos::memory::x86_64 } // namespace teachos::memory::x86_64 +namespace kstd::ext +{ + template<> + struct is_bitfield_enum : std::true_type + { + }; +} // namespace kstd::ext + #endif \ No newline at end of file -- cgit v1.2.3 From eafbf588760c289b7f54a4771b39af0ccfe8cf59 Mon Sep 17 00:00:00 2001 From: Felix Morgner Date: Wed, 10 Dec 2025 21:55:42 +0100 Subject: kapi: extract page_mapper interface --- arch/x86_64/CMakeLists.txt | 1 + .../x86_64/include/x86_64/memory/kernel_mapper.hpp | 5 +- arch/x86_64/include/x86_64/memory/page_table.hpp | 65 ++++++++++++++ arch/x86_64/include/x86_64/memory/paging_root.hpp | 9 +- .../x86_64/memory/recursive_page_mapper.hpp | 23 +++++ .../include/x86_64/memory/scoped_mapping.hpp | 9 +- arch/x86_64/src/kapi/memory.cpp | 29 +++++-- arch/x86_64/src/memory/kernel_mapper.cpp | 7 +- arch/x86_64/src/memory/paging_root.cpp | 52 ++++++++++-- arch/x86_64/src/memory/recursive_page_mapper.cpp | 38 +++++++++ arch/x86_64/src/memory/scoped_mapping.cpp | 99 ++-------------------- 11 files changed, 215 insertions(+), 122 deletions(-) create mode 100644 arch/x86_64/include/x86_64/memory/recursive_page_mapper.hpp create mode 100644 arch/x86_64/src/memory/recursive_page_mapper.cpp (limited to 'arch/x86_64') diff --git a/arch/x86_64/CMakeLists.txt b/arch/x86_64/CMakeLists.txt index 0b9009c..7bd8d07 100644 --- a/arch/x86_64/CMakeLists.txt +++ b/arch/x86_64/CMakeLists.txt @@ -27,6 +27,7 @@ target_sources("x86_64" PRIVATE "src/memory/mmu.cpp" "src/memory/page_table.cpp" "src/memory/paging_root.cpp" + "src/memory/recursive_page_mapper.cpp" "src/memory/region_allocator.cpp" "src/memory/scoped_mapping.cpp" diff --git a/arch/x86_64/include/x86_64/memory/kernel_mapper.hpp b/arch/x86_64/include/x86_64/memory/kernel_mapper.hpp index 4b681ae..1f217ae 100644 --- a/arch/x86_64/include/x86_64/memory/kernel_mapper.hpp +++ b/arch/x86_64/include/x86_64/memory/kernel_mapper.hpp @@ -1,8 +1,6 @@ #ifndef TEACHOS_X86_64_KERNEL_MAPPER_HPP #define TEACHOS_X86_64_KERNEL_MAPPER_HPP -#include "kapi/memory.hpp" - #include #include #include @@ -16,14 +14,13 @@ namespace teachos::memory::x86_64 { using section_header_type = elf::section_header; - kernel_mapper(frame_allocator & allocator, multiboot2::information_view const * mbi); + explicit kernel_mapper(multiboot2::information_view const * mbi); auto remap_kernel() -> void; private: auto map_section(section_header_type const & section, std::string_view name) -> void; - frame_allocator * m_allocator; multiboot2::information_view const * m_mbi; std::uintptr_t m_kernel_load_base; }; diff --git a/arch/x86_64/include/x86_64/memory/page_table.hpp b/arch/x86_64/include/x86_64/memory/page_table.hpp index 22616e8..6a9c045 100644 --- a/arch/x86_64/include/x86_64/memory/page_table.hpp +++ b/arch/x86_64/include/x86_64/memory/page_table.hpp @@ -146,4 +146,69 @@ namespace kstd::ext }; } // namespace kstd::ext +namespace teachos::memory::x86_64 +{ + + constexpr auto to_mapper_flags(page_table::entry::flags flags) -> page_mapper::flags + { + using table_flags = page_table::entry::flags; + using mapper_flags = page_mapper::flags; + + auto result = mapper_flags{}; + + if ((flags & table_flags::no_execute) == table_flags::empty) + { + result |= mapper_flags::executable; + } + + if ((flags & table_flags::writable) != table_flags::empty) + { + result |= mapper_flags::writable; + } + + if ((flags & table_flags::disable_cache) != table_flags::empty) + { + result |= mapper_flags::uncached; + } + + if ((flags & table_flags::user_accessible) == table_flags::empty) + { + result |= mapper_flags::supervisor; + } + + return result; + } + + constexpr auto to_table_flags(page_mapper::flags flags) -> page_table::entry::flags + { + using table_flags = page_table::entry::flags; + using mapper_flags = page_mapper::flags; + + auto result = table_flags{}; + + if ((flags & mapper_flags::executable) == mapper_flags::empty) + { + result |= table_flags::no_execute; + } + + if ((flags & mapper_flags::writable) != mapper_flags::empty) + { + result |= table_flags::writable; + } + + if ((flags & mapper_flags::uncached) != mapper_flags::empty) + { + result |= table_flags::disable_cache; + } + + if ((flags & mapper_flags::supervisor) != mapper_flags::empty) + { + result |= table_flags::user_accessible; + } + + return result; + } + +} // namespace teachos::memory::x86_64 + #endif \ No newline at end of file diff --git a/arch/x86_64/include/x86_64/memory/paging_root.hpp b/arch/x86_64/include/x86_64/memory/paging_root.hpp index d4aa023..ed95106 100644 --- a/arch/x86_64/include/x86_64/memory/paging_root.hpp +++ b/arch/x86_64/include/x86_64/memory/paging_root.hpp @@ -30,8 +30,13 @@ namespace teachos::memory::x86_64 //! @param page A page to map. //! @param frame The frame into which to map the page. //! @param flags The flags to apply to the mapping. - //! @param allocator The frame allocator used to allocate any required page tables. - auto map(page page, frame frame, entry::flags flags, frame_allocator & allocator) -> std::optional; + auto map(page page, frame frame, entry::flags flags) -> std::optional; + + //! Unmap the given page from virtual memory. + //! + //! @warning If the page has not previously been mapped, this function will panic. + //! @param page The page to unmap + auto unmap(page) -> void; private: paging_root() = default; diff --git a/arch/x86_64/include/x86_64/memory/recursive_page_mapper.hpp b/arch/x86_64/include/x86_64/memory/recursive_page_mapper.hpp new file mode 100644 index 0000000..a66c8d1 --- /dev/null +++ b/arch/x86_64/include/x86_64/memory/recursive_page_mapper.hpp @@ -0,0 +1,23 @@ +#ifndef TEACHOS_X86_64_RECURSIVE_PAGE_MAPPER_HPP +#define TEACHOS_X86_64_RECURSIVE_PAGE_MAPPER_HPP + +#include "kapi/memory.hpp" + +namespace teachos::memory::x86_64 +{ + + struct recursive_page_mapper : page_mapper + { + explicit recursive_page_mapper(frame_allocator & allocator); + + auto map(page page, frame frame, flags flags) -> std::byte * override; + auto unmap(page page) -> void override; + auto try_unmap(page page) -> bool override; + + private: + frame_allocator * m_allocator; + }; + +} // namespace teachos::memory::x86_64 + +#endif \ No newline at end of file diff --git a/arch/x86_64/include/x86_64/memory/scoped_mapping.hpp b/arch/x86_64/include/x86_64/memory/scoped_mapping.hpp index d4844fb..415ea8e 100644 --- a/arch/x86_64/include/x86_64/memory/scoped_mapping.hpp +++ b/arch/x86_64/include/x86_64/memory/scoped_mapping.hpp @@ -22,8 +22,7 @@ namespace teachos::memory::x86_64 //! Construct a new scoped mapping, which can be used to map a frame to the given unused page. //! @param page An unused page. If the page is already mapped, this constructor will panic. - //! @param allocator An allocator to be used to allocate any page maps required to map the frame. - scoped_mapping(page page, frame_allocator & allocator); + explicit scoped_mapping(page page); //! Unmap the mapped frame if one was mapped. //! @note Any page tables that were allocated to support the mapping will be released. @@ -53,17 +52,13 @@ namespace teachos::memory::x86_64 //! @note If no frame was ever mapped, this function will panic. auto unmap() -> void; - auto swap(scoped_mapping & other) -> void; + friend auto swap(scoped_mapping & lhs, scoped_mapping & rhs) -> void; private: page m_page; - frame_allocator * m_allocator; bool m_mapped; - std::uint8_t m_allocated; }; - auto swap(scoped_mapping & lhs, scoped_mapping & rhs) -> void; - } // namespace teachos::memory::x86_64 #endif \ No newline at end of file diff --git a/arch/x86_64/src/kapi/memory.cpp b/arch/x86_64/src/kapi/memory.cpp index ed3edef..00b9de3 100644 --- a/arch/x86_64/src/kapi/memory.cpp +++ b/arch/x86_64/src/kapi/memory.cpp @@ -13,6 +13,7 @@ #include "x86_64/memory/page_table.hpp" #include "x86_64/memory/page_utilities.hpp" #include "x86_64/memory/paging_root.hpp" +#include "x86_64/memory/recursive_page_mapper.hpp" #include "x86_64/memory/region_allocator.hpp" #include "x86_64/memory/scoped_mapping.hpp" @@ -29,6 +30,8 @@ namespace teachos::memory { // NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables) auto constinit allocator = static_cast(nullptr); + // NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables) + auto constinit mapper = static_cast(nullptr); constexpr auto static unused_page_address = linear_address{0x0000'7fff'cafe'faceuz}; constexpr auto static recursive_page_map_index = x86_64::page_table::entry_count - 2; @@ -70,7 +73,7 @@ namespace teachos::memory auto page = page::containing(unused_page_address); - auto temporary_mapper = scoped_mapping{page, allocator}; + auto temporary_mapper = scoped_mapping{page}; auto new_pml4_frame = allocator.allocate(); auto pml4 = std::construct_at(temporary_mapper.map_as(*new_pml4_frame, entry_flags::writable)); @@ -124,6 +127,15 @@ namespace teachos::memory return *allocator; } + auto active_mapper() -> page_mapper & + { + if (!mapper) + { + system::panic("[x86_64] The page mapper has not been set you."); + } + return *mapper; + } + auto init() -> void { auto static constinit is_initialized = std::atomic_flag{}; @@ -132,14 +144,19 @@ namespace teachos::memory system::panic("[x86_64] Memory management has already been initialized."); } - auto allocator = create_early_frame_allocator(); enable_cpu_protections(); - auto allocation_buffer = x86_64::buffered_allocator<4>{&allocator}; + auto early_allocator = create_early_frame_allocator(); + auto allocation_buffer = x86_64::buffered_allocator<4>{&early_allocator}; + allocator = &allocation_buffer; + + auto recursive_mapper = x86_64::recursive_page_mapper{allocation_buffer}; + mapper = &recursive_mapper; + auto new_pml4_frame = inject_faux_pml4(allocation_buffer); - auto mapper = x86_64::kernel_mapper{allocation_buffer, boot::bootstrap_information.mbi}; - mapper.remap_kernel(); + auto kernel_mapper = x86_64::kernel_mapper{boot::bootstrap_information.mbi}; + kernel_mapper.remap_kernel(); cio::println("[x86_64:MEM] prepared new kernel image page maps."); auto cr3 = cpu::x86_64::cr3::read(); @@ -149,6 +166,8 @@ namespace teachos::memory // 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(); + mapper = nullptr; + allocator = nullptr; } } // namespace teachos::memory diff --git a/arch/x86_64/src/memory/kernel_mapper.cpp b/arch/x86_64/src/memory/kernel_mapper.cpp index b1d12a4..f46b5b5 100644 --- a/arch/x86_64/src/memory/kernel_mapper.cpp +++ b/arch/x86_64/src/memory/kernel_mapper.cpp @@ -32,9 +32,8 @@ namespace teachos::memory::x86_64 } // namespace - kernel_mapper::kernel_mapper(frame_allocator & allocator, multiboot2::information_view const * mbi) - : m_allocator{&allocator} - , m_mbi(std::move(mbi)) + kernel_mapper::kernel_mapper(multiboot2::information_view const * mbi) + : m_mbi{std::move(mbi)} , m_kernel_load_base{std::bit_cast(&boot::x86_64::TEACHOS_VMA)} {} @@ -100,7 +99,7 @@ namespace teachos::memory::x86_64 for (auto i = 0uz; i < number_of_pages; ++i) { - paging_root::get().map(page{first_page.number() + i}, frame{first_frame.number() + i}, page_flags, *m_allocator); + paging_root::get().map(page{first_page.number() + i}, frame{first_frame.number() + i}, page_flags); } } diff --git a/arch/x86_64/src/memory/paging_root.cpp b/arch/x86_64/src/memory/paging_root.cpp index 5ca2bf0..c458093 100644 --- a/arch/x86_64/src/memory/paging_root.cpp +++ b/arch/x86_64/src/memory/paging_root.cpp @@ -27,16 +27,15 @@ namespace teachos::memory::x86_64 //! still enforcing non-writability and non-execution of the affected page. template requires(Level > 1uz && Level < 5uz) - auto do_map(recursive_page_table * pml, page page, page_table::entry::flags flags, - frame_allocator & allocator) + auto do_map(recursive_page_table * pml, page page, page_table::entry::flags flags) { auto index = pml_index(page); flags = flags & ~page_table::entry::flags::no_execute; flags = flags | page_table::entry::flags::writable; if (!(*pml)[index].present()) { - auto new_table_frame = allocator.allocate(); - auto mapping = scoped_mapping{page, allocator}; + auto new_table_frame = active_allocator().allocate(); + auto mapping = scoped_mapping{page}; (*pml)[index].frame(new_table_frame.value(), page_table::entry::flags::present | flags); auto new_table = std::optional{std::construct_at(*pml->next(index))}; return new_table; @@ -114,14 +113,49 @@ namespace teachos::memory::x86_64 .or_else(handle_huge_page); } - auto paging_root::map(page page, frame frame, page_table::entry::flags flags, frame_allocator & allocator) - -> std::optional + auto paging_root::map(page page, frame frame, page_table::entry::flags flags) -> std::optional { return std::optional{this} - .and_then([&](auto pml) -> auto { return do_map(pml, page, flags, allocator); }) - .and_then([&](auto pml) -> auto { return do_map(pml, page, flags, allocator); }) - .and_then([&](auto pml) -> auto { return do_map(pml, page, flags, allocator); }) + .and_then([&](auto pml) -> auto { return do_map(pml, page, flags); }) + .and_then([&](auto pml) -> auto { return do_map(pml, page, flags); }) + .and_then([&](auto pml) -> auto { return do_map(pml, page, flags); }) .and_then([&](auto pml) -> auto { return do_map(pml, page, frame, flags); }); } + auto paging_root::unmap(page page) -> void + { + if (!this->translate(page)) + { + system::panic("[x86_64:MEM] Tried to unmap a page that was not mapped."); + } + + auto pml4 = this; + auto pml3 = pml4->next(pml_index<4>(page)).value(); + auto pml2 = pml3->next(pml_index<3>(page)).value(); + auto pml1 = pml2->next(pml_index<2>(page)).value(); + + (*pml1)[pml_index<1>(page)].clear(); + + if (pml1->empty()) + { + auto pml1_frame = (*pml2)[pml_index<2>(page)].frame().value(); + active_allocator().release(pml1_frame); + (*pml2)[pml_index<2>(page)].clear(); + } + + if (pml2->empty()) + { + auto pml2_frame = (*pml3)[pml_index<3>(page)].frame().value(); + active_allocator().release(pml2_frame); + (*pml3)[pml_index<3>(page)].clear(); + } + + if (pml3->empty()) + { + auto pml3_frame = (*pml4)[pml_index<4>(page)].frame().value(); + active_allocator().release(pml3_frame); + (*pml4)[pml_index<4>(page)].clear(); + } + } + } // namespace teachos::memory::x86_64 \ No newline at end of file diff --git a/arch/x86_64/src/memory/recursive_page_mapper.cpp b/arch/x86_64/src/memory/recursive_page_mapper.cpp new file mode 100644 index 0000000..ea89f38 --- /dev/null +++ b/arch/x86_64/src/memory/recursive_page_mapper.cpp @@ -0,0 +1,38 @@ +#include "x86_64/memory/recursive_page_mapper.hpp" + +#include "kapi/system.hpp" + +#include "x86_64/memory/page_table.hpp" +#include "x86_64/memory/paging_root.hpp" + +namespace teachos::memory::x86_64 +{ + recursive_page_mapper::recursive_page_mapper(frame_allocator & allocator) + : m_allocator{&allocator} + {} + + auto recursive_page_mapper::map(page page, frame frame, flags flags) -> std::byte * + { + return paging_root::get().map(page, frame, to_table_flags(flags)).value_or(nullptr); + } + + auto recursive_page_mapper::unmap(page page) -> void + { + if (!try_unmap(page)) + { + system::panic("[x86_64:MEM] Tried to unmap a page that was not mapped."); + } + } + + auto recursive_page_mapper::try_unmap(page page) -> bool + { + auto & root = paging_root::get(); + if (!root.translate(page)) + { + return false; + } + root.unmap(page); + return true; + } + +} // namespace teachos::memory::x86_64 \ No newline at end of file diff --git a/arch/x86_64/src/memory/scoped_mapping.cpp b/arch/x86_64/src/memory/scoped_mapping.cpp index 44dbf45..6f3461c 100644 --- a/arch/x86_64/src/memory/scoped_mapping.cpp +++ b/arch/x86_64/src/memory/scoped_mapping.cpp @@ -5,10 +5,8 @@ #include "x86_64/memory/mmu.hpp" #include "x86_64/memory/page_table.hpp" -#include "x86_64/memory/page_utilities.hpp" #include "x86_64/memory/paging_root.hpp" -#include #include namespace teachos::memory::x86_64 @@ -16,16 +14,12 @@ namespace teachos::memory::x86_64 scoped_mapping::scoped_mapping(scoped_mapping && other) noexcept : m_page{std::exchange(other.m_page, page{})} - , m_allocator{std::exchange(other.m_allocator, nullptr)} , m_mapped{std::exchange(other.m_mapped, false)} - , m_allocated{std::exchange(other.m_allocated, 0)} {} - scoped_mapping::scoped_mapping(page page, frame_allocator & allocator) + scoped_mapping::scoped_mapping(page page) : m_page{page} - , m_allocator{&allocator} , m_mapped{false} - , m_allocated{} { if (paging_root::get().translate(page)) { @@ -44,105 +38,28 @@ namespace teachos::memory::x86_64 auto scoped_mapping::operator=(scoped_mapping && other) noexcept -> scoped_mapping & { - this->swap(other); + swap(*this, other); return *this; } auto scoped_mapping::map(frame frame, page_table::entry::flags flags) -> std::byte * { - auto & pml4 = paging_root::get(); - auto pml4_index = pml_index<4>(m_page); - if (!pml4[pml4_index].present()) - { - auto new_frame = m_allocator->allocate(); - pml4[pml4_index].frame(*new_frame, page_table::entry::flags::present | flags); - std::construct_at(pml4.next(pml4_index).value()); - m_allocated |= 1uz << 2; - } - - auto pml3 = pml4.next(pml4_index).value(); - auto pml3_index = pml_index<3>(m_page); - if (!(*pml3)[pml3_index].present()) - { - auto new_frame = m_allocator->allocate(); - (*pml3)[pml3_index].frame(*new_frame, page_table::entry::flags::present | flags); - std::construct_at((*pml3).next(pml3_index).value()); - m_allocated |= 1uz << 1; - } - - auto pml2 = (*pml3).next(pml3_index).value(); - auto pml2_index = pml_index<2>(m_page); - if (!(*pml2)[pml2_index].present()) - { - auto new_frame = m_allocator->allocate(); - (*pml2)[pml2_index].frame(*new_frame, page_table::entry::flags::present | flags); - std::construct_at((*pml2).next(pml2_index).value()); - m_allocated |= 1uz << 0; - } - - auto pml1 = (*pml2).next(pml2_index).value(); - auto pml1_index = pml_index<1>(m_page); - (*pml1)[pml1_index].frame(frame, page_table::entry::flags::present | flags); - + auto result = active_mapper().map(m_page, frame, to_mapper_flags(flags)); m_mapped = true; - - return static_cast(m_page.start_address()); + return result; } auto scoped_mapping::unmap() -> void { - if (!m_mapped) - { - system::panic("[MEM] Tried to release an unmapped temporary mapping!"); - } - - auto pml4 = &paging_root::get(); - auto pml3 = pml4->next(pml_index<4>(m_page)).value(); - auto pml2 = pml3->next(pml_index<3>(m_page)).value(); - auto pml1 = pml2->next(pml_index<2>(m_page)).value(); - - (*pml1)[pml_index<1>(m_page)].clear(); - - if (m_allocated & 1uz << 0) - { - auto pml1_frame = (*pml2)[pml_index<2>(m_page)].frame().value(); - m_allocator->release(pml1_frame); - (*pml2)[pml_index<2>(m_page)].clear(); - } - - if (m_allocated & 1uz << 1) - { - auto pml2_frame = (*pml3)[pml_index<3>(m_page)].frame().value(); - m_allocator->release(pml2_frame); - (*pml3)[pml_index<3>(m_page)].clear(); - } - - if (m_allocated & 1uz << 2) - { - auto pml3_frame = (*pml4)[pml_index<4>(m_page)].frame().value(); - m_allocator->release(pml3_frame); - (*pml4)[pml_index<4>(m_page)].clear(); - } - + active_mapper().unmap(m_page); m_mapped = false; } - auto scoped_mapping::swap(scoped_mapping & other) -> void - { - using std::swap; - - if (&other == this) - return; - - swap(m_page, other.m_page); - swap(m_allocator, other.m_allocator); - swap(m_mapped, other.m_mapped); - swap(m_allocated, other.m_allocated); - } - auto swap(scoped_mapping & lhs, scoped_mapping & rhs) -> void { - lhs.swap(rhs); + using std::swap; + swap(lhs.m_page, rhs.m_page); + swap(lhs.m_mapped, rhs.m_mapped); } } // namespace teachos::memory::x86_64 \ No newline at end of file -- cgit v1.2.3 From 998a001fc621ca0e7560ca09a8acd29469ae3373 Mon Sep 17 00:00:00 2001 From: Felix Morgner Date: Thu, 11 Dec 2025 17:46:02 +0100 Subject: docs: improve documentation --- arch/x86_64/include/x86_64/boot/ld.hpp | 95 +++--- .../include/x86_64/cpu/impl/control_registers.hpp | 339 ++++++++++++++------- .../include/x86_64/memory/buffered_allocator.hpp | 2 +- arch/x86_64/include/x86_64/memory/page_table.hpp | 145 ++++++--- arch/x86_64/include/x86_64/memory/paging_root.hpp | 2 +- .../include/x86_64/memory/region_allocator.hpp | 94 +++--- arch/x86_64/src/memory/page_table.cpp | 18 +- arch/x86_64/src/memory/region_allocator.cpp | 2 +- arch/x86_64/x86_64.dox | 14 + 9 files changed, 440 insertions(+), 271 deletions(-) create mode 100644 arch/x86_64/x86_64.dox (limited to 'arch/x86_64') diff --git a/arch/x86_64/include/x86_64/boot/ld.hpp b/arch/x86_64/include/x86_64/boot/ld.hpp index 9af3dc8..b073863 100644 --- a/arch/x86_64/include/x86_64/boot/ld.hpp +++ b/arch/x86_64/include/x86_64/boot/ld.hpp @@ -1,19 +1,19 @@ -/** - * @file - * @brief Provides an interface to linker script defined symbols. - * - * @details - * This header provides declarations for symbols that are defined in the linker script itself. The symbols declared here - * provide important information, for example the start and end of the kernel image in physical memory. - * - * Any variables defined in this file must not be read themselves, but rather their address shall be taken, yielding a - * pointer to the memory location the represent. - * - * @see arch/x86_64/scripts/kernel.ld - */ - -#ifndef TEACHOS_X86_64_BOOT_LD_H -#define TEACHOS_X86_64_BOOT_LD_H +//! @file +//! The interface to linker script defined symbols. +//! +//! This header provides declarations for symbols that are defined in the linker script itself. The symbols declared +//! here provide important information, for example the start and end of the kernel image in virtual and physical +//! memory. +//! +//! Any variables defined in this file must not be read themselves, but rather their address shall be taken, yielding a +//! pointer to the memory location the represent. +//! +//! @note The symbols declared in this header are declared using C-language linkage in order to suppress name mangling. +//! +//! @see arch/x86_64/scripts/kernel.ld + +#ifndef TEACHOS_X86_64_BOOT_LD_HPP +#define TEACHOS_X86_64_BOOT_LD_HPP #include @@ -22,55 +22,40 @@ namespace teachos::boot::x86_64 extern "C" { - /** - * @brief The first byte of the loaded kernel image. - * - * @details - * This symbol is defined in the kernel linker script and marks the start of the kernel image in physical memory. - * To use this symbol for its intended purpose, the address of it shall be taken. - * - * @see _end_physical - */ + //! The beginning of the kernel image in physical memory + //! + //! This symbol marks the start of the kernel image in physical memory. + //! + //! @see _end_physical extern std::byte _start_physical; - /** - * @brief The first byte after the loaded kernel image. - * - * @details - * This symbol is defined in the kernel linker script and marks the end of the kernel image in physical memory. - * To use this symbol for its intended purpose, the address of it shall be taken. - * - * @see _start_physical - */ + //! The first byte after the loaded kernel image. + //! + //! This symbol marks the end of the kernel image in physical memory. + //! + //! @see _start_physical extern std::byte _end_physical; - /** - * @brief The first byte of the loaded kernel image in the virtual address space. - * - * @details - * This symbol is defined in the kernel linker script and marks the start of the kernel image in virtual memory. - * To use this symbol for its intended purpose, the address of it shall be taken. - */ + //! The first byte of the loaded kernel image in the virtual address space. + //! + //! This symbol and marks the start of the kernel image in virtual memory. + //! + //! @see _end_virtual extern std::byte _start_virtual; - /** - * @brief The first byte after the loaded kernel image in the virtual address space. - * - * @details - * This symbol is defined in the kernel linker script and marks the end of the kernel image in virtual memory. - * To use this symbol for its intended purpose, the address of it shall be taken. - */ + //! The first byte after the loaded kernel image in the virtual address space. + //! + //! This symbol marks the end of the kernel image in virtual memory. + //! + //! @see _start_virtual extern std::byte _end_virtual; - /** - * @brief The first byte of the kernel's virtual address space. - * - * @details - * This symbol is defined in the kernel linker script and marks beginning of the kernel virtual address space. To - * use this symbol for its intended purpose, the address of it shall be taken. - */ + //! The first byte of the kernel's virtual address space. + //! + //! This symbol marks beginning of the kernel virtual address space. extern std::byte TEACHOS_VMA; } + } // namespace teachos::boot::x86_64 #endif diff --git a/arch/x86_64/include/x86_64/cpu/impl/control_registers.hpp b/arch/x86_64/include/x86_64/cpu/impl/control_registers.hpp index 0c2254a..c2ad3e7 100644 --- a/arch/x86_64/include/x86_64/cpu/impl/control_registers.hpp +++ b/arch/x86_64/include/x86_64/cpu/impl/control_registers.hpp @@ -13,141 +13,248 @@ namespace teachos::cpu::x86_64 { - namespace impl { + //! The assembler templates used to access (r/w) CR0; constexpr auto static cr0_asm = std::pair{"mov %%cr0, %0", "mov %0, %%cr0"}; - constexpr auto static cr2_asm = std::pair{"mov %%cr2, %0", "mov %0, %%cr2"}; - constexpr auto static cr3_asm = std::pair{"mov %%cr3, %0", "mov %0, %%cr3"}; - - template - struct control_register_with_flags - { - }; - template - struct control_register_with_flags>> - { - using flags = ValueType; - - auto static set(ValueType value) -> void - { - auto current = Derived::read(); - current |= value; - Derived::write(current); - } - - auto static clear(ValueType value) -> void - { - auto current = Derived::read(); - current &= ~value; - Derived::write(current); - } - }; - - template - struct control_register : control_register_with_flags, ValueType> - { - [[nodiscard]] auto static read() -> ValueType - { - auto value = ValueType{}; - asm volatile((AssemblerTemplates->first) : "=r"(value)); - return value; - } - - auto static write(ValueType value) -> void - { - asm volatile((AssemblerTemplates->second) : : "r"(value)); - } - }; - - enum struct cr0_flags : uint64_t - { - //! Enable protected mode. - protection_enable = 1uz << 0, - //! Enable wait-monitoring of the coprocessor after task switching. - monitor_coprocessor = 1uz << 1, - //! Emulate floating point coprocessor. - emulation = 1uz << 2, - //! Marks that a task switch has occurred. - task_switched = 1uz << 3, - //! Marks Intel 387 DX math coprocessor as available - extension_type = 1uz << 4, - //! Numeric error handling mode. - numeric_error = 1uz << 5, - //! Disable writing to read-only marked memory. - write_protect = 1uz << 16, - //! Enable Ring-3 alignment checks - alignment_check = 1uz << 18, - //! Disable write through - not_write_through = 1uz << 29, - //! Disable caching of memory accesses - cache_disable = 1uz << 30, - //! Enable paging - paging = 1uz << 31 - }; - - enum struct cr3_flags : std::uint64_t - { - page_level_write_through = 1uz << 0, - page_level_cache_disable = 1uz << 1, - }; - - struct cr3_value - { - constexpr cr3_value() = default; - - constexpr cr3_value(memory::physical_address address, cr3_flags flags = static_cast(0)) - : m_flags{static_cast(flags)} - , m_address{static_cast(address.raw())} - {} - - [[nodiscard]] constexpr auto address() const -> memory::physical_address - { - return memory::physical_address{m_address}; - } - - constexpr auto address(memory::physical_address address) -> void - { - m_address = static_cast(address.raw()); - } - - [[nodiscard]] constexpr auto flags() const -> cr3_flags - { - return static_cast(m_flags); - } - - constexpr auto flags(cr3_flags flags) -> void - { - m_flags = static_cast(flags); - } - - private: - std::uint64_t : 3; - std::uint64_t m_flags : 2 {}; - std::uint64_t : 7; - std::uint64_t m_address : 52 {}; - }; + //! The assembler templates used to access (r/w) CR2; + constexpr auto static cr2_asm = std::pair{"mov %%cr2, %0", "mov %0, %%cr2"}; + //! The assembler templates used to access (r/w) CR3; + constexpr auto static cr3_asm = std::pair{"mov %%cr3, %0", "mov %0, %%cr3"}; } // namespace impl - using cr0 = impl::control_register; - using cr2 = impl::control_register; - using cr3 = impl::control_register; + //! The flags that can be set on CR0 configuration register. + enum struct cr0_flags : uint64_t + { + //! Enable protected mode. + protection_enable = 1uz << 0, + //! Enable wait-monitoring of the coprocessor after task switching. + monitor_coprocessor = 1uz << 1, + //! Emulate floating point coprocessor. + emulation = 1uz << 2, + //! Marks that a task switch has occurred. + task_switched = 1uz << 3, + //! Marks Intel 387 DX math coprocessor as available + extension_type = 1uz << 4, + //! Numeric error handling mode. + numeric_error = 1uz << 5, + //! Disable writing to read-only marked memory. + write_protect = 1uz << 16, + //! Enable Ring-3 alignment checks + alignment_check = 1uz << 18, + //! Disable write through + not_write_through = 1uz << 29, + //! Disable caching of memory accesses + cache_disable = 1uz << 30, + //! Enable paging + paging = 1uz << 31 + }; + enum struct cr3_flags : std::uint64_t + { + page_level_write_through = 1uz << 0, + page_level_cache_disable = 1uz << 1, + }; } // namespace teachos::cpu::x86_64 namespace kstd::ext { template<> - struct is_bitfield_enum : std::true_type + struct is_bitfield_enum : std::true_type { }; template<> - struct is_bitfield_enum : std::true_type + struct is_bitfield_enum : std::true_type { }; } // namespace kstd::ext +namespace teachos::cpu::x86_64 +{ + //! A mixin for flag-oriented control registers. + //! + //! This mixin provides additional functionality for flag-oriented, or partially flag-oriented, control registers. A + //! control register is flag-oriented, when it's comprises is a bitfield and zero or more additional non-bitfield + //! parts. + //! + //! @tparam Derived The class deriving from this mixin. + //! @tparam ValueType The value type of the class deriving from this mixin. + template + struct control_register_with_flags + { + }; + + //! @copydoc control_register_with_flags + //! + //! @note This specialization provides the implementation for the case in which the value type of the control register + //! is an enum. + template + struct control_register_with_flags>> + { + //! The type of the flags used by this control register + using flags = ValueType; + + //! Set one or more flags in this control register. + //! + //! @warning This function is to be considered **UNSAFE**. Setting flags in a control register may lead to + //! unexpected CPU behavior if the prerequisites imposed by the CPU specification are not fulfilled. This function + //! will perform no additional checks, and may, by extension, crash the system. + //! + //! @param value One or a combination of flags to be set in the control register. + auto static set(ValueType value) -> void + { + auto current = Derived::read(); + current |= value; + Derived::write(current); + } + + //! Clear one or more flags in this control register. + //! + //! @warning This function is to be considered **UNSAFE**. Clearing flags in a control register may lead to + //! unexpected CPU behavior if the prerequisites imposed by the CPU specification are not fulfilled. This function + //! will perform no additional checks, and may, by extension, crash the system. + //! + //! @param value One or a combination of flags to be cleared in the control register. + auto static clear(ValueType value) -> void + { + auto current = Derived::read(); + current &= ~value; + Derived::write(current); + } + }; + + //! A CPU control register. + //! + //! CPU control registers are used to configure builtin features of the CPU, for example memory protection and FPU + //! error reporting. Writing to a control register is inherently dangerous, since a misconfiguration can leave the CPU + //! in an invalid/undefined state. + template + struct control_register : control_register_with_flags, ValueType> + { + //! Read the current value of the control register. + //! + //! @return The currently set value of the control register. + [[nodiscard]] auto static read() -> ValueType + { + auto value = ValueType{}; + asm volatile((AssemblerTemplates->first) : "=r"(value)); + return value; + } + + //! Write a new value to the control register. + //! + //! @warning This function should be considered **UNSAFE**. Writing values to a control register may lead to + //! unexpected CPU behavior if the prerequisites imposed by the CPU specification are not fulfilled. This function + //! will perform no additional checks, and may, by extension, crash the system. + //! + //! @param value The new value to write to the control register. + auto static write(ValueType value) -> void + { + asm volatile((AssemblerTemplates->second) : : "r"(value)); + } + }; + + //! The value type of the CR3 control register. + //! + //! The CR3 control register holds the root configuration of the virtual memory protection mechanism. It contains the + //! page aligned physical address of the root page map, as well as the root paging configuration flags. + struct cr3_value + { + //! Contstruct a 0-value CR3 value. + constexpr cr3_value() = default; + + //! Construct a CR3 value using the given root page map address and flags. + //! + //! @param address The physical address of the root page map + //! @param flags The root configuration flags of the paging system. + constexpr cr3_value(memory::physical_address address, cr3_flags flags = static_cast(0)) + : m_flags{static_cast(flags)} + , m_address{static_cast(address.raw())} + {} + + //! Extract the physical address of the root page map from this value. + //! + //! @return The physical address of the root page map. + [[nodiscard]] constexpr auto address() const -> memory::physical_address + { + return memory::physical_address{m_address}; + } + + //! Encode the page aligned physical address of the root page map into this value. + //! + //! @param address The page aligned physical address of the root page map. + constexpr auto address(memory::physical_address address) -> void + { + m_address = static_cast(address.raw()); + } + + //! Extract the root paging configuration flags from this value. + //! + //! @return The root paging configuration flags. + [[nodiscard]] constexpr auto flags() const -> cr3_flags + { + return static_cast(m_flags); + } + + //! Encode the root paging configuration flags into this value. + //! + //! @param flags The root paging configuration flags. + constexpr auto flags(cr3_flags flags) -> void + { + m_flags = static_cast(flags); + } + + //! Add the given flags to the current set of encoded root configuration flags of this value. + //! + //! @param flags The root configuration flags to add. + //! @return A reference to this value. + constexpr auto operator|=(cr3_flags flags) -> cr3_value & + { + m_flags |= static_cast(flags); + return *this; + } + + //! Mask the root configuration flags of this value. + //! + //! @param mask The mask to apply to the root configuration flags. + //! @return A reference to this value. + constexpr auto operator&=(cr3_flags mask) -> cr3_value & + { + m_flags &= static_cast(mask); + return *this; + } + + private: + //! Reserved bits. + std::uint64_t : 3; + //! The root paging configuration flags. + std::uint64_t m_flags : 2 {}; + //! Reserved bits. + std::uint64_t : 7; + //! The page aligned physical address of the root page map. + std::uint64_t m_address : 52 {}; + }; + + static_assert(sizeof(cr3_value) == sizeof(std::uint64_t)); + + //! Configuration Register 0. + //! + //! This configuration register holds various control flags to configure the configure the basic operation of the CPU. + using cr0 = control_register; + + //! Configuration Register 2. + //! + //! This configuration register holds the memory address the access to which has triggered the most recent page fault. + using cr2 = control_register; + + //! Configuration Register 3. + //! + //! This register holds the configuration of the virtual memory protection configuration. + using cr3 = control_register; + +} // namespace teachos::cpu::x86_64 + #endif \ No newline at end of file diff --git a/arch/x86_64/include/x86_64/memory/buffered_allocator.hpp b/arch/x86_64/include/x86_64/memory/buffered_allocator.hpp index 2d05010..d7e9491 100644 --- a/arch/x86_64/include/x86_64/memory/buffered_allocator.hpp +++ b/arch/x86_64/include/x86_64/memory/buffered_allocator.hpp @@ -35,7 +35,7 @@ namespace teachos::memory::x86_64 auto operator=(buffered_allocator const &) = delete; auto operator=(buffered_allocator &&) = delete; - auto allocate() -> std::optional override + auto allocate() noexcept -> std::optional override { auto found = std::ranges::find_if(m_pool, [](auto const & candidate) { return candidate.has_value(); }); if (found == std::end(m_pool)) diff --git a/arch/x86_64/include/x86_64/memory/page_table.hpp b/arch/x86_64/include/x86_64/memory/page_table.hpp index 6a9c045..6c102b7 100644 --- a/arch/x86_64/include/x86_64/memory/page_table.hpp +++ b/arch/x86_64/include/x86_64/memory/page_table.hpp @@ -16,60 +16,103 @@ namespace teachos::memory::x86_64 { - //! A table containing paging entries. + //! A table containing page mapping entries. + //! + //! Page tables exist in a multi-level hierarchy and are used to map pages (virtual memory) onto frames (physical + //! memory). Conceptually, pages represent the data found in a virtual address space, while frames represent their + //! storage. Only a level 1 page table maps an actual page onto a frame. All other page tables on higher levels do not + //! map payload pages, but rather their subordinate page tables. struct page_table { - //! A single page table entry + //! An entry in a page table. + //! + //! A page table entry is a combination of a frame number and a set of flags that determine the properties and + //! access rights to a mapped page. Entries at a higher level in the page hierarchy do not map a page directly, + //! unless that page is marked as huge on the relevant level, but rather map the next lower page table. struct entry { //! Flags marking the state and configuration of an entry. + //! + //! An entry in a page table may have any combination of these flags active at the same time. The final flags of a + //! page are determined as the strictest combination (logical AND) of all flags along the hierarchy. + //! + //! @note This is a bitfield enum as defined by kstd::ext::bitfield_enum. enum struct flags : std::uint64_t { empty = 0, - present = 1uz << 0, - writable = 1uz << 1, - user_accessible = 1uz << 2, - write_through = 1uz << 3, - disable_cache = 1uz << 4, - accessed = 1uz << 5, - dirty = 1uz << 6, - huge_page = 1uz << 7, - global = 1uz << 8, - no_execute = 1uz << 63, + present = 1uz << 0, //!< The page is mapped. + writable = 1uz << 1, //!< The page is writable. + user_accessible = 1uz << 2, //!< The page is accessible in user mode. + write_through = 1uz << 3, //!< Any writes to the page must immediately hit memory. + disable_cache = 1uz << 4, //!< Any writes to the page must never be cached. + accessed = 1uz << 5, //!< The page was accessed. + dirty = 1uz << 6, //!< The page was written to. + huge_page = 1uz << 7, //!< The page is huge. + global = 1uz << 8, //!< The TLB entry for this page must not be flushed on context switches. + no_execute = 1uz << 63, //!< The data in this page must not be executed. }; + //! Construct an empty entry. entry() = default; - //! Clear this entry, ensuring all information is set to zero, marking the page represented by this entry as not - //! present. - auto clear() -> void; - - //! Check if the page represented by this entry is present at the containing page table's level. - [[nodiscard]] auto present() const -> bool; - - //! Check if the page represented by this entry is huge (2MiB, or 1GiB, depending on the containing page table's - //! level). - [[nodiscard]] auto huge() const -> bool; + //! Clear this entry, ensuring all information is set to zero. + //! + //! This effectively marks the page represented by this entry as not present. In addition, it also removes any + //! information about the frame referenced by this entry. + auto clear() noexcept -> void; + + //! Check if the page represented by this entry is present. + //! + //! @note This function does not attempt to walk the page table hierarchy, but only performs a local check. This + //! means, that if the page is not mapped at a lower level, this will not be detected. + //! + //! @return @p true iff. the page is present at this level, @p false otherwise. + [[nodiscard]] auto present() const noexcept -> bool; + + //! Check if the page represented by this entry is a huge page. + //! + //! @note The effective size of the page depends on the level of the page table containing this entry. + //! + //! @return @p true iff. the page is marked as being huge, @p false otherwise. + [[nodiscard]] auto huge() const noexcept -> bool; //! Get all flags present in this entry. - [[nodiscard]] auto all_flags() const -> flags; + //! + //! @return The flags that are currently set on this entry. + [[nodiscard]] auto all_flags() const noexcept -> flags; - //! Set the flags of this entry to the given set. - auto all_flags(flags flags) -> void; + //! Set all flags of this entry. + //! + //! @param flags The flags to apply to this entry. + auto all_flags(flags flags) noexcept -> void; - //! Additively set the given flags in this entry. - auto operator|=(flags rhs) -> entry &; + //! Add the given flags to the flags of this entry. + //! + //! @param rhs The flags to add to this entry's flags. + //! @return A reference to this entry. + auto operator|=(flags rhs) noexcept -> entry &; //! Get the frame number associated with this entry, if the referenced page is present. - [[nodiscard]] auto frame() const -> std::optional; + //! + //! @return an engaged std::optional iff. this entry maps a page, std::nullopt otherwise. + [[nodiscard]] auto frame() const noexcept -> std::optional; - //! Set the entry to reference the given frame with the given flags. - auto frame(struct frame frame, flags flags) -> void; + //! Map this entry. + //! + //! @param frame The frame to map in this entry. + //! @param flags The flags to apply to this entry. + auto frame(struct frame frame, flags flags) noexcept -> void; private: //! A mask to retrieve, or exclude, the frame number from the raw entry. + //! + //! Page table entries in x86_64 are a compacted combination of the relevant flags and the frame number. This mask + //! represents the bits that make up the frame number in an entry. constexpr auto static frame_number_mask{0x000f'ffff'ffff'f000uz}; + //! The raw entry bytes. + //! + //! @see entry::frame_number_mask std::uint64_t m_raw{}; }; @@ -77,13 +120,24 @@ namespace teachos::memory::x86_64 constexpr auto static entry_count{page::size / sizeof(entry)}; //! Get the entry at the given index. + //! + //! @warning This function will panic if the entry index is out of bounds. + //! + //! @param index The index of the desired entry. + //! @return A reference to the entry at the given index. [[nodiscard]] auto operator[](std::size_t index) -> entry &; + + //! @copydoc page_table::operator[] [[nodiscard]] auto operator[](std::size_t index) const -> entry const &; - //! Clear the entire page table, effectively evicting all entries. - auto clear() -> void; + //! Clear the entire page table. + //! + //! This function effectively marks the page table as not mapping any pages. + auto clear() noexcept -> void; //! Check if the page table is empty. + //! + //! @return @p true iff. this page table has no entries marked present, @p false otherwise. [[nodiscard]] auto empty() const noexcept -> bool; private: @@ -91,6 +145,10 @@ namespace teachos::memory::x86_64 }; //! A recursively mapped page table. + //! + //! A page table in which at least one entry maps the same table. Recursive page tables allow for easy access to other + //! tables within the page mapping hierarchy, without having to map them prior to access, through careful construction + //! of linear addresses that pass through the same index multiple times. template requires(Level > 0uz && Level < 5uz) struct recursive_page_table : page_table @@ -99,7 +157,12 @@ namespace teachos::memory::x86_64 constexpr auto static recursive_index = 0776uz; //! Get the next lower lever table. - [[nodiscard]] auto next(this auto && self, std::size_t index) + //! + //! @param self The object type of this object. + //! @param index The index corresponding to the desired page map. + //! @return An engaged std::optional holding a pointer to the next lower page table iff. the next lower page table + //! at the desired index is present, std::nullopt otherwise. + [[nodiscard]] auto next(this auto && self, std::size_t index) noexcept requires(next_level > 1) { return self.next_address(index).transform([](auto address) -> auto { @@ -108,8 +171,10 @@ namespace teachos::memory::x86_64 }); } - //! Get the next lower lever table. - [[nodiscard]] auto next(this auto && self, std::size_t index) + //! @copydoc recursive_page_table::next + //! + //! @note This overload returns a non-hierarchical, or leaf, page table + [[nodiscard]] auto next(this auto && self, std::size_t index) noexcept requires(next_level == 1) { return self.next_address(index).transform([](auto address) -> auto { @@ -119,11 +184,19 @@ namespace teachos::memory::x86_64 } private: + //! The number of address bits used to represent the page index per level. constexpr auto static level_bits = 9; + //! The highest address bit. constexpr auto static high_bit = 48; + //! The number of bits representing the offset into a page. constexpr auto static offset_bits = 12; - [[nodiscard]] auto next_address(std::size_t index) const -> std::optional + //! Calculate the recursive address of the next lower page table. + //! + //! @param index The index of the desired page table. + //! @return An engaged std::optional holding the address of the new lower page table iff. the next lower page table + //! at the desired index is present, std::nullopt otherwise. + [[nodiscard]] auto next_address(std::size_t index) const noexcept -> std::optional { if (auto entry = (*this)[index]; entry.present() && !entry.huge()) { diff --git a/arch/x86_64/include/x86_64/memory/paging_root.hpp b/arch/x86_64/include/x86_64/memory/paging_root.hpp index ed95106..75ba120 100644 --- a/arch/x86_64/include/x86_64/memory/paging_root.hpp +++ b/arch/x86_64/include/x86_64/memory/paging_root.hpp @@ -36,7 +36,7 @@ namespace teachos::memory::x86_64 //! //! @warning If the page has not previously been mapped, this function will panic. //! @param page The page to unmap - auto unmap(page) -> void; + auto unmap(page page) -> void; private: paging_root() = default; diff --git a/arch/x86_64/include/x86_64/memory/region_allocator.hpp b/arch/x86_64/include/x86_64/memory/region_allocator.hpp index 3ddc8b6..84b7a94 100644 --- a/arch/x86_64/include/x86_64/memory/region_allocator.hpp +++ b/arch/x86_64/include/x86_64/memory/region_allocator.hpp @@ -12,76 +12,66 @@ namespace teachos::memory::x86_64 { - /** - * @brief Allocates memory linearly using memory areas read from the multiboot2 information pointer and leaks any - * deallocated frames. - */ + //! A simple, memory-region based frame allocator. + //! + //! This frame allocator linearly allocates frames that are in available memory regions. It automatically skips any + //! frames occupied by the kernel image or any bootloader provided data. + //! + //! @note This allocator will never release frames. struct region_allocator final : frame_allocator { struct memory_information { - //! The memory range occupied by the loaded kernel image. This includes all sections that are marked as occupying - //! space in the kernel executable. The internal structure of this area is more described in a more fine-grained - //! manner by the ELF symbol information provided in the Multiboot2 information by the loader. + //! The memory range occupied by the loaded kernel image. + //! + //! This includes all sections that are marked as occupying space in the kernel executable. The internal structure + //! of this area is more described in a more fine-grained manner by the ELF symbol information provided in the + //! Multiboot2 information by the loader. std::pair image_range; - //! The memory range occupied by the loader supplied Multiboot2 information structure. In general, this - //! information is allocated somewhere in the range of the loaded image, but the loader protocol does not - //! guarantee this. It is thus imperative to be able to handle the cases where the loader chooses to allocate the - //! information structure outside of the image range. + //! The memory range occupied by the loader supplied Multiboot2 information structure. + //! + //! In general, this information is allocated somewhere in the range of the loaded image, but the loader protocol + //! does not guarantee this. It is thus imperative to be able to handle the cases where the loader chooses to + //! allocate the information structure outside of the image range. std::pair mbi_range; - //! The loader supplied map of memory regions. These include available, unavailable, and reclaimable regions. In - //! general, only frames that are located in non-reserved (as in available) regions should be allocated for page - //! storage. + //! The loader supplied map of memory regions. + //! + //! These include available, unavailable, and reclaimable regions. In general, only frames that are located in + //! non-reserved (as in available) regions should be allocated for page storage. multiboot2::memory_map memory_map; }; - /** - * @brief Constructor. - * - * @param mem_info Structure containing all relevant information to map and allocate memory. - */ - explicit region_allocator(memory_information const & mem_info); + using region = multiboot2::memory_map::region; - /** - * @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() -> std::optional override; + //! Construct a new allocator using the provided memory information + //! + //! @param information The description of the detected memory regions as well as regions that are already occupied. + explicit region_allocator(memory_information const & information); - /** - * @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 allocated in the first place. - * - * @param physical_frame Previously allocated physical_frame that should be deallocated. - */ + //! @copydoc frame_allocator::allocate + //! + //! @note As long as free frames are available, successive calls to this implementation are guaranteed to yield + //! frames in ascending order. + auto allocate() noexcept -> std::optional override; + + //! @copydoc frame_allocator::release + //! + //! @note This implementation will never actually release any frames. auto release(frame frame) -> void override; private: - /** - * @brief Find the next memory area and write it into current_area. - */ + //! Find the next memory area and write it into current_area. auto choose_next_area() -> void; - frame m_next_frame; ///< The physical_frame after the last allocated one. - std::optional m_current_region; ///< The memory region currently allocated from - multiboot2::memory_map m_memory_map; ///< The boot loader supplied memory map. - frame m_kernel_start; ///< The start address of the kernel code in memory. - frame m_kernel_end; ///< The end address of the kernel code in memory. - frame m_multiboot_start; ///< The start address of the multiboot code in memory. - frame m_multiboot_end; ///< The end address of the multiboot code in memory. + frame m_next_frame; //!< The next available frame. + std::optional m_current_region; //!< The memory region currently used for allocation + multiboot2::memory_map m_memory_map; //!< The boot loader supplied memory map. + frame m_kernel_start; //!< The start of the kernel image in physical memory. + frame m_kernel_end; //!< The end of the kernel image in physical memory. + frame m_multiboot_start; //!< The start of the Multiboot2 information in physical memory. + frame m_multiboot_end; //!< The end of the Multiboot2 information in physical memory. }; } // namespace teachos::memory::x86_64 diff --git a/arch/x86_64/src/memory/page_table.cpp b/arch/x86_64/src/memory/page_table.cpp index e797b22..0e404e5 100644 --- a/arch/x86_64/src/memory/page_table.cpp +++ b/arch/x86_64/src/memory/page_table.cpp @@ -6,39 +6,39 @@ namespace teachos::memory::x86_64 { - auto page_table::entry::clear() -> void + auto page_table::entry::clear() noexcept -> void { m_raw = 0; } - auto page_table::entry::present() const -> bool + auto page_table::entry::present() const noexcept -> bool { return (all_flags() & flags::present) != flags::empty; } - auto page_table::entry::huge() const -> bool + auto page_table::entry::huge() const noexcept -> bool { return (all_flags() & flags::huge_page) != flags::empty; } - auto page_table::entry::all_flags() const -> flags + auto page_table::entry::all_flags() const noexcept -> flags { return std::bit_cast(m_raw & ~frame_number_mask); } - auto page_table::entry::all_flags(flags flags) -> void + auto page_table::entry::all_flags(flags flags) noexcept -> void { m_raw = (m_raw & ~frame_number_mask) | std::to_underlying(flags); } - auto page_table::entry::operator|=(flags rhs) -> page_table::entry & + auto page_table::entry::operator|=(flags rhs) noexcept -> page_table::entry & { auto raw_flags = std::to_underlying(rhs) & ~frame_number_mask; m_raw |= raw_flags; return *this; } - auto page_table::entry::frame() const -> std::optional + auto page_table::entry::frame() const noexcept -> std::optional { if (present()) { @@ -47,7 +47,7 @@ namespace teachos::memory::x86_64 return std::nullopt; } - auto page_table::entry::frame(struct frame frame, flags flags) -> void + auto page_table::entry::frame(struct frame frame, flags flags) noexcept -> void { m_raw = (frame.start_address().raw() | static_cast(flags)); }; @@ -62,7 +62,7 @@ namespace teachos::memory::x86_64 return m_entries.at(index); } - auto page_table::clear() -> void + auto page_table::clear() noexcept -> void { std::ranges::for_each(m_entries, &page_table::entry::clear); } diff --git a/arch/x86_64/src/memory/region_allocator.cpp b/arch/x86_64/src/memory/region_allocator.cpp index 8ea76c6..e477ec0 100644 --- a/arch/x86_64/src/memory/region_allocator.cpp +++ b/arch/x86_64/src/memory/region_allocator.cpp @@ -56,7 +56,7 @@ namespace teachos::memory::x86_64 } } - auto region_allocator::allocate() -> std::optional + auto region_allocator::allocate() noexcept -> std::optional { if (!m_current_region) { diff --git a/arch/x86_64/x86_64.dox b/arch/x86_64/x86_64.dox new file mode 100644 index 0000000..c5c218c --- /dev/null +++ b/arch/x86_64/x86_64.dox @@ -0,0 +1,14 @@ +//! @namespace teachos::boot::x86_64 +//! The %x86_64 implementation of teachos::boot +//! +//! This namespace contains %x86_64 specific implementations of and extensions to the teachos::boot namespace. + +//! @namespace teachos::cpu::x86_64 +//! The %x86_64 implementation of teachos::cpu +//! +//! This namespace contains %x86_64 specific implementations of and extensions to the teachos::cpu namespace. + +//! @namespace teachos::memory::x86_64 +//! The %x86_64 implementation of teachos::memory +//! +//! This namespace contains %x86_64 specific implementations of and extensions to the teachos::memory namespace. \ No newline at end of file -- cgit v1.2.3 From cf8d0d899ee17db734ce8ab7ee618333eb1767f2 Mon Sep 17 00:00:00 2001 From: Felix Morgner Date: Thu, 11 Dec 2025 18:36:23 +0100 Subject: kapi: finish documentation --- arch/x86_64/include/x86_64/memory/page_table.hpp | 4 ++-- arch/x86_64/include/x86_64/memory/recursive_page_mapper.hpp | 2 +- arch/x86_64/src/kapi/memory.cpp | 4 ++-- arch/x86_64/src/memory/paging_root.cpp | 8 ++++---- arch/x86_64/src/memory/recursive_page_mapper.cpp | 2 +- arch/x86_64/src/memory/scoped_mapping.cpp | 4 ++-- 6 files changed, 12 insertions(+), 12 deletions(-) (limited to 'arch/x86_64') diff --git a/arch/x86_64/include/x86_64/memory/page_table.hpp b/arch/x86_64/include/x86_64/memory/page_table.hpp index 6c102b7..3bc2a2b 100644 --- a/arch/x86_64/include/x86_64/memory/page_table.hpp +++ b/arch/x86_64/include/x86_64/memory/page_table.hpp @@ -246,7 +246,7 @@ namespace teachos::memory::x86_64 if ((flags & table_flags::user_accessible) == table_flags::empty) { - result |= mapper_flags::supervisor; + result |= mapper_flags::supervisor_only; } return result; @@ -274,7 +274,7 @@ namespace teachos::memory::x86_64 result |= table_flags::disable_cache; } - if ((flags & mapper_flags::supervisor) != mapper_flags::empty) + if ((flags & mapper_flags::supervisor_only) != mapper_flags::empty) { result |= table_flags::user_accessible; } diff --git a/arch/x86_64/include/x86_64/memory/recursive_page_mapper.hpp b/arch/x86_64/include/x86_64/memory/recursive_page_mapper.hpp index a66c8d1..dc52065 100644 --- a/arch/x86_64/include/x86_64/memory/recursive_page_mapper.hpp +++ b/arch/x86_64/include/x86_64/memory/recursive_page_mapper.hpp @@ -12,7 +12,7 @@ namespace teachos::memory::x86_64 auto map(page page, frame frame, flags flags) -> std::byte * override; auto unmap(page page) -> void override; - auto try_unmap(page page) -> bool override; + auto try_unmap(page page) noexcept -> bool override; private: frame_allocator * m_allocator; diff --git a/arch/x86_64/src/kapi/memory.cpp b/arch/x86_64/src/kapi/memory.cpp index 00b9de3..cd0bd77 100644 --- a/arch/x86_64/src/kapi/memory.cpp +++ b/arch/x86_64/src/kapi/memory.cpp @@ -117,7 +117,7 @@ namespace teachos::memory } // namespace - auto active_allocator() -> frame_allocator & + auto active_frame_allocator() -> frame_allocator & { if (!allocator) { @@ -127,7 +127,7 @@ namespace teachos::memory return *allocator; } - auto active_mapper() -> page_mapper & + auto active_page_mapper() -> page_mapper & { if (!mapper) { diff --git a/arch/x86_64/src/memory/paging_root.cpp b/arch/x86_64/src/memory/paging_root.cpp index c458093..078686b 100644 --- a/arch/x86_64/src/memory/paging_root.cpp +++ b/arch/x86_64/src/memory/paging_root.cpp @@ -34,7 +34,7 @@ namespace teachos::memory::x86_64 flags = flags | page_table::entry::flags::writable; if (!(*pml)[index].present()) { - auto new_table_frame = active_allocator().allocate(); + auto new_table_frame = active_frame_allocator().allocate(); auto mapping = scoped_mapping{page}; (*pml)[index].frame(new_table_frame.value(), page_table::entry::flags::present | flags); auto new_table = std::optional{std::construct_at(*pml->next(index))}; @@ -139,21 +139,21 @@ namespace teachos::memory::x86_64 if (pml1->empty()) { auto pml1_frame = (*pml2)[pml_index<2>(page)].frame().value(); - active_allocator().release(pml1_frame); + active_frame_allocator().release(pml1_frame); (*pml2)[pml_index<2>(page)].clear(); } if (pml2->empty()) { auto pml2_frame = (*pml3)[pml_index<3>(page)].frame().value(); - active_allocator().release(pml2_frame); + active_frame_allocator().release(pml2_frame); (*pml3)[pml_index<3>(page)].clear(); } if (pml3->empty()) { auto pml3_frame = (*pml4)[pml_index<4>(page)].frame().value(); - active_allocator().release(pml3_frame); + active_frame_allocator().release(pml3_frame); (*pml4)[pml_index<4>(page)].clear(); } } diff --git a/arch/x86_64/src/memory/recursive_page_mapper.cpp b/arch/x86_64/src/memory/recursive_page_mapper.cpp index ea89f38..47148f0 100644 --- a/arch/x86_64/src/memory/recursive_page_mapper.cpp +++ b/arch/x86_64/src/memory/recursive_page_mapper.cpp @@ -24,7 +24,7 @@ namespace teachos::memory::x86_64 } } - auto recursive_page_mapper::try_unmap(page page) -> bool + auto recursive_page_mapper::try_unmap(page page) noexcept -> bool { auto & root = paging_root::get(); if (!root.translate(page)) diff --git a/arch/x86_64/src/memory/scoped_mapping.cpp b/arch/x86_64/src/memory/scoped_mapping.cpp index 6f3461c..e243dc9 100644 --- a/arch/x86_64/src/memory/scoped_mapping.cpp +++ b/arch/x86_64/src/memory/scoped_mapping.cpp @@ -44,14 +44,14 @@ namespace teachos::memory::x86_64 auto scoped_mapping::map(frame frame, page_table::entry::flags flags) -> std::byte * { - auto result = active_mapper().map(m_page, frame, to_mapper_flags(flags)); + auto result = active_page_mapper().map(m_page, frame, to_mapper_flags(flags)); m_mapped = true; return result; } auto scoped_mapping::unmap() -> void { - active_mapper().unmap(m_page); + active_page_mapper().unmap(m_page); m_mapped = false; } -- cgit v1.2.3 From 67267c4d103de2673191c113c1e99e66965aab5c Mon Sep 17 00:00:00 2001 From: Felix Morgner Date: Fri, 12 Dec 2025 10:16:23 +0100 Subject: x86_64/device_io: simplify implementation --- arch/x86_64/include/x86_64/device_io/port_io.hpp | 155 ++++++++--------------- 1 file changed, 53 insertions(+), 102 deletions(-) (limited to 'arch/x86_64') diff --git a/arch/x86_64/include/x86_64/device_io/port_io.hpp b/arch/x86_64/include/x86_64/device_io/port_io.hpp index 352a4d0..ceabf4a 100644 --- a/arch/x86_64/include/x86_64/device_io/port_io.hpp +++ b/arch/x86_64/include/x86_64/device_io/port_io.hpp @@ -1,130 +1,81 @@ #ifndef TEACHOS_X86_64_IO_PORT_IO_HPP #define TEACHOS_X86_64_IO_PORT_IO_HPP +#include #include #include +#include #include namespace teachos::io::x86_64 { - /** - * @brief An I/O port of a given size at a given address. - * - * @tparam Address The address (port number) of the I/O port. - * @tparam Size The size (in bytes) of the I/O port. - */ + + //! An I/O port of a given size at a given address. + //! + //! Port I/O leverages a separate address space to communicate with devices via the memory bus, allowing for byte to + //! double-word sized transfers. + //! + //! @tparam Address The address (port number) of the I/O port. + //! @tparam Size The size (in bytes) of the I/O port. template + requires(Size == 1 || Size == 2 || Size == 4) struct port { - static_assert(Size == 1 || Size == 2 || Size == 4, "A port must be either 1, 2, or 4 bytes in size"); - - /** - * @brief The type of data available for reading and writing through this port. - */ - using io_type = + //! The type of data available for reading and writing through this port. + using value_type = std::conditional_t>; - /** - * @brief Write a byte to the I/O port. - * - * @param data The data to write to the I/O port. - */ - auto static write(io_type data) -> void - requires(Size == 1) + //! Read from the I/O port. + //! + //! @return The data read from the I/O port. + auto static read() noexcept -> value_type { - asm volatile("mov %[port], %%dx\n" - "mov %[data], %%al\n" - "out %%al, %%dx\n" - : - : [port] "i"(Address), [data] "im"(data) - : "dx", "al"); + auto data = value_type{}; + asm volatile((read_code[asm_template_index]) + : [data] "=m"(data) + : [port] "i"(Address) + : "dx", (data_register[asm_template_index])); + return data; } - /** - * @brief Write a word to the I/O port. - * - * @param data The data to write to the I/O port. - */ - auto static write(io_type data) -> void - requires(Size == 2) + //! Write data to the I/O port. + //! + //! @param data The data to write to the I/O port. + auto static write(value_type data) noexcept -> void { - asm volatile("mov %[port], %%dx\n" - "mov %[data], %%ax\n" - "out %%ax, %%dx\n" + asm volatile((write_code[asm_template_index]) : : [port] "i"(Address), [data] "im"(data) - : "dx", "ax"); + : "dx", (data_register[asm_template_index])); } - /** - * @brief Write a double-word to the I/O port. - * - * @param data The data to write to the I/O port. - */ - auto static write(io_type data) -> void - requires(Size == 4) - { - asm volatile("mov %[port], %%dx\n" - "mov %[data], %%eax\n" - "out %%eax, %%dx\n" - : - : [port] "i"(Address), [data] "im"(data) - : "dx", "eax"); - } + private: + //! The index into the assembly template and register arrays. + //! + //! This index is used to select the correct assembly templates for the given operation (read or write), as well as + //! the clobbered data register, from the relevant template arrays. + constexpr auto static asm_template_index = Size / 2; - /** - * @brief Read a byte from the I/O port. - * - * @return The data read from the I/O port. - */ - auto static read() -> io_type - requires(Size == 1) - { - auto data = io_type{}; - asm volatile("mov %[port], %%dx\n" - "in %%dx, %%al\n" - "mov %%al, %[data]\n" - : [data] "=m"(data) - : [port] "i"(Address) - : "dx", "al"); - return data; - } + //! The assembly templates used for reading from an I/O port. + constexpr auto static read_code = std::array{ + std::string_view{"mov %[port], %%dx\nin %%dx, %%al\nmov %%al, %[data]"}, + std::string_view{"mov %[port], %%dx\nin %%dx, %%ax\nmov %%ax, %[data]"}, + std::string_view{"mov %[port], %%dx\nin %%dx, %%eax\nmov %%eax, %[data]"}, + }; - /** - * @brief Read a word from the I/O port. - * - * @return The data read from the I/O port. - */ - auto static read() -> io_type - requires(Size == 2) - { - auto data = io_type{}; - asm volatile("mov %[port], %%dx\n" - "in %%dx, %%ax\n" - "mov %%ax, %[data]\n" - : [data] "=m"(data) - : [port] "i"(Address) - : "dx", "ax"); - return data; - } + //! The assembly templates used for writing to an I/O port. + constexpr auto static write_code = std::array{ + std::string_view{"mov %[port], %%dx\nmov %%dx, %[data]\nout %%al, %%dx"}, + std::string_view{"mov %[port], %%dx\nmov %%dx, %[data]\nout %%ax, %%dx"}, + std::string_view{"mov %[port], %%dx\nmov %%dx, %[data]\nout %%eax, %%dx"}, + }; - /** - * @brief Read a double-word from the I/O port. - * - * @return The data read from the I/O port. - */ - auto static read() -> io_type - requires(Size == 4) - { - auto data = io_type{}; - asm volatile("mov %[port], %%dx\n" - "in %%dx, %%eax\n" - "mov %%eax, %[data]\n" - : [data] "=m"(data) - : [port] "i"(Address) - : "dx", "eax"); - return data; - } + //! The registers clobbered by the I/O operation. + constexpr auto static data_register = std::array{ + std::string_view{"al"}, + std::string_view{"ax"}, + std::string_view{"eax"}, + }; }; } // namespace teachos::io::x86_64 -- cgit v1.2.3 From f8c3c91e81f434c26dd84e5047a4420e8fb40534 Mon Sep 17 00:00:00 2001 From: Felix Morgner Date: Fri, 12 Dec 2025 10:28:36 +0100 Subject: x86_64/device_io: switch to typed port io. --- arch/x86_64/include/x86_64/device_io/port_io.hpp | 22 +++++++++++++++++----- arch/x86_64/include/x86_64/vga/crtc.hpp | 4 ++-- 2 files changed, 19 insertions(+), 7 deletions(-) (limited to 'arch/x86_64') diff --git a/arch/x86_64/include/x86_64/device_io/port_io.hpp b/arch/x86_64/include/x86_64/device_io/port_io.hpp index ceabf4a..9f115eb 100644 --- a/arch/x86_64/include/x86_64/device_io/port_io.hpp +++ b/arch/x86_64/include/x86_64/device_io/port_io.hpp @@ -2,6 +2,7 @@ #define TEACHOS_X86_64_IO_PORT_IO_HPP #include +#include #include #include #include @@ -10,6 +11,16 @@ namespace teachos::io::x86_64 { + //! The requirements imposed on a type usable for port I/O. + template + concept port_io_type = requires { + requires sizeof(ValueType) == 1 || sizeof(ValueType) == 2 || sizeof(ValueType) == 4; + requires std::default_initializable; + std::bit_cast( + std::conditional_t>{}); + }; + //! An I/O port of a given size at a given address. //! //! Port I/O leverages a separate address space to communicate with devices via the memory bus, allowing for byte to @@ -17,13 +28,14 @@ namespace teachos::io::x86_64 //! //! @tparam Address The address (port number) of the I/O port. //! @tparam Size The size (in bytes) of the I/O port. - template - requires(Size == 1 || Size == 2 || Size == 4) + template struct port { //! The type of data available for reading and writing through this port. - using value_type = - std::conditional_t>; + using value_type = ValueType; + + //! The size of the I/O port + constexpr auto static size = sizeof(value_type); //! Read from the I/O port. //! @@ -54,7 +66,7 @@ namespace teachos::io::x86_64 //! //! This index is used to select the correct assembly templates for the given operation (read or write), as well as //! the clobbered data register, from the relevant template arrays. - constexpr auto static asm_template_index = Size / 2; + constexpr auto static asm_template_index = size / 2; //! The assembly templates used for reading from an I/O port. constexpr auto static read_code = std::array{ diff --git a/arch/x86_64/include/x86_64/vga/crtc.hpp b/arch/x86_64/include/x86_64/vga/crtc.hpp index 8bdc12d..b2c8028 100644 --- a/arch/x86_64/include/x86_64/vga/crtc.hpp +++ b/arch/x86_64/include/x86_64/vga/crtc.hpp @@ -10,12 +10,12 @@ namespace teachos::vga::x86_64::crtc /** * @brief The address port of the CRT Controller. */ - using address = io::x86_64::port<0x3d4, 1>; // NOLINT(cppcoreguidelines-avoid-magic-numbers) + using address = io::x86_64::port<0x3d4, std::byte>; // NOLINT(cppcoreguidelines-avoid-magic-numbers) /** * @brief The data port of the CRT Controller. */ - using data = io::x86_64::port<0x3d5, 1>; // NOLINT(cppcoreguidelines-avoid-magic-numbers) + using data = io::x86_64::port<0x3d5, std::byte>; // NOLINT(cppcoreguidelines-avoid-magic-numbers) namespace registers { -- cgit v1.2.3 From d89dd14c4492144d54ad755932b3e2c89a17bf74 Mon Sep 17 00:00:00 2001 From: Felix Morgner Date: Fri, 12 Dec 2025 10:50:40 +0100 Subject: x86_64/device_io: split port read/write features --- arch/x86_64/include/x86_64/device_io/port_io.hpp | 94 +++++++++++++----------- arch/x86_64/include/x86_64/vga/crtc.hpp | 8 +- 2 files changed, 56 insertions(+), 46 deletions(-) (limited to 'arch/x86_64') diff --git a/arch/x86_64/include/x86_64/device_io/port_io.hpp b/arch/x86_64/include/x86_64/device_io/port_io.hpp index 9f115eb..c3e5271 100644 --- a/arch/x86_64/include/x86_64/device_io/port_io.hpp +++ b/arch/x86_64/include/x86_64/device_io/port_io.hpp @@ -21,73 +21,79 @@ namespace teachos::io::x86_64 std::conditional_t>{}); }; - //! An I/O port of a given size at a given address. - //! - //! Port I/O leverages a separate address space to communicate with devices via the memory bus, allowing for byte to - //! double-word sized transfers. - //! - //! @tparam Address The address (port number) of the I/O port. - //! @tparam Size The size (in bytes) of the I/O port. - template - struct port + template + struct port_read { - //! The type of data available for reading and writing through this port. - using value_type = ValueType; - - //! The size of the I/O port - constexpr auto static size = sizeof(value_type); - //! Read from the I/O port. //! //! @return The data read from the I/O port. - auto static read() noexcept -> value_type + auto static read() noexcept { - auto data = value_type{}; - asm volatile((read_code[asm_template_index]) + auto data = typename Derived::value_type{}; + asm volatile((code[Derived::size / 2]) : [data] "=m"(data) - : [port] "i"(Address) - : "dx", (data_register[asm_template_index])); + : [port] "i"(Derived::size) + : "dx", (Derived::data_register)); return data; } + private: + //! The assembly templates used for reading from an I/O port. + constexpr auto static code = std::array{ + std::string_view{"mov %[port], %%dx\nin %%dx, %%al\nmov %%al, %[data]"}, + std::string_view{"mov %[port], %%dx\nin %%dx, %%ax\nmov %%ax, %[data]"}, + std::string_view{"mov %[port], %%dx\nin %%dx, %%eax\nmov %%eax, %[data]"}, + }; + }; + + template + struct port_write + { //! Write data to the I/O port. //! //! @param data The data to write to the I/O port. - auto static write(value_type data) noexcept -> void + auto static write(std::same_as auto data) noexcept -> void { - asm volatile((write_code[asm_template_index]) + asm volatile((code[Derived::size / 2]) : - : [port] "i"(Address), [data] "im"(data) - : "dx", (data_register[asm_template_index])); + : [port] "i"(Derived::address), [data] "im"(data) + : "dx", (Derived::data_register)); } private: - //! The index into the assembly template and register arrays. - //! - //! This index is used to select the correct assembly templates for the given operation (read or write), as well as - //! the clobbered data register, from the relevant template arrays. - constexpr auto static asm_template_index = size / 2; - - //! The assembly templates used for reading from an I/O port. - constexpr auto static read_code = std::array{ - std::string_view{"mov %[port], %%dx\nin %%dx, %%al\nmov %%al, %[data]"}, - std::string_view{"mov %[port], %%dx\nin %%dx, %%ax\nmov %%ax, %[data]"}, - std::string_view{"mov %[port], %%dx\nin %%dx, %%eax\nmov %%eax, %[data]"}, - }; - //! The assembly templates used for writing to an I/O port. - constexpr auto static write_code = std::array{ + constexpr auto static code = std::array{ std::string_view{"mov %[port], %%dx\nmov %%dx, %[data]\nout %%al, %%dx"}, std::string_view{"mov %[port], %%dx\nmov %%dx, %[data]\nout %%ax, %%dx"}, std::string_view{"mov %[port], %%dx\nmov %%dx, %[data]\nout %%eax, %%dx"}, }; + }; - //! The registers clobbered by the I/O operation. - constexpr auto static data_register = std::array{ - std::string_view{"al"}, - std::string_view{"ax"}, - std::string_view{"eax"}, - }; + //! An I/O port of a given size at a given address. + //! + //! Port I/O leverages a separate address space to communicate with devices via the memory bus, allowing for byte + //! to double-word sized transfers. + //! + //! @tparam Address The address (port number) of the I/O port. + //! @tparam Size The size (in bytes) of the I/O port. + //! @tparam Features The features (readable, writeable) + template typename... Features> + requires(sizeof...(Features) > 0) + struct port : Features>... + { + //! The type of the data of this port. + using value_type = ValueType; + + //! The address of this I/O port. + constexpr auto static address = Address; + + //! The size of this I/O port. + constexpr auto static size = sizeof(value_type); + + //! The register clobbered by the I/O operation. + constexpr auto static data_register = size == 1 ? std::string_view{"al"} + : size == 2 ? std::string_view{"ax"} + : std::string_view{"eax"}; }; } // namespace teachos::io::x86_64 diff --git a/arch/x86_64/include/x86_64/vga/crtc.hpp b/arch/x86_64/include/x86_64/vga/crtc.hpp index b2c8028..d4b4f51 100644 --- a/arch/x86_64/include/x86_64/vga/crtc.hpp +++ b/arch/x86_64/include/x86_64/vga/crtc.hpp @@ -7,15 +7,19 @@ namespace teachos::vga::x86_64::crtc { + namespace io = io::x86_64; + /** * @brief The address port of the CRT Controller. */ - using address = io::x86_64::port<0x3d4, std::byte>; // NOLINT(cppcoreguidelines-avoid-magic-numbers) + // NOLINTNEXTLINE(cppcoreguidelines-avoid-magic-numbers) + using address = io::port<0x3d4, std::byte, io::port_write>; /** * @brief The data port of the CRT Controller. */ - using data = io::x86_64::port<0x3d5, std::byte>; // NOLINT(cppcoreguidelines-avoid-magic-numbers) + // NOLINTNEXTLINE(cppcoreguidelines-avoid-magic-numbers) + using data = io::port<0x3d5, std::byte, io::port_read, io::port_write>; namespace registers { -- cgit v1.2.3 From 4bb59ebf68351754ab97e722914e0434c4d347fd Mon Sep 17 00:00:00 2001 From: Felix Morgner Date: Fri, 12 Dec 2025 10:51:55 +0100 Subject: x86_64/cio: disable cursor by default --- arch/x86_64/src/kapi/cio.cpp | 1 + 1 file changed, 1 insertion(+) (limited to 'arch/x86_64') diff --git a/arch/x86_64/src/kapi/cio.cpp b/arch/x86_64/src/kapi/cio.cpp index 456477a..3f169a1 100644 --- a/arch/x86_64/src/kapi/cio.cpp +++ b/arch/x86_64/src/kapi/cio.cpp @@ -11,6 +11,7 @@ namespace teachos::cio auto init() -> void { vga_device.emplace(); + vga_device->cursor(false); set_output_device(*vga_device); } -- cgit v1.2.3 From 822b17b852cdc8933881132dd7b862ee74117973 Mon Sep 17 00:00:00 2001 From: Felix Morgner Date: Fri, 12 Dec 2025 10:52:38 +0100 Subject: x86_64/cpu: update documentation and structure --- .../include/x86_64/cpu/impl/control_registers.hpp | 5 +- .../x86_64/cpu/impl/model_specific_register.hpp | 195 +++++++++++++-------- 2 files changed, 127 insertions(+), 73 deletions(-) (limited to 'arch/x86_64') diff --git a/arch/x86_64/include/x86_64/cpu/impl/control_registers.hpp b/arch/x86_64/include/x86_64/cpu/impl/control_registers.hpp index c2ad3e7..1f8c690 100644 --- a/arch/x86_64/include/x86_64/cpu/impl/control_registers.hpp +++ b/arch/x86_64/include/x86_64/cpu/impl/control_registers.hpp @@ -1,7 +1,7 @@ #ifndef TEACHOS_X86_64_CPU_IMPL_CONTROL_REGISTERS_HPP #define TEACHOS_X86_64_CPU_IMPL_CONTROL_REGISTERS_HPP -// IWYU pragma: private +// IWYU pragma: private, include "x86_64/cpu/registers.hpp" #include "kapi/memory/address.hpp" @@ -77,8 +77,7 @@ namespace teachos::cpu::x86_64 //! A mixin for flag-oriented control registers. //! //! This mixin provides additional functionality for flag-oriented, or partially flag-oriented, control registers. A - //! control register is flag-oriented, when it's comprises is a bitfield and zero or more additional non-bitfield - //! parts. + //! control register is flag-oriented, if it comprises a bitfield and zero or more additional non-bitfield parts. //! //! @tparam Derived The class deriving from this mixin. //! @tparam ValueType The value type of the class deriving from this mixin. diff --git a/arch/x86_64/include/x86_64/cpu/impl/model_specific_register.hpp b/arch/x86_64/include/x86_64/cpu/impl/model_specific_register.hpp index ff2c8ad..080b280 100644 --- a/arch/x86_64/include/x86_64/cpu/impl/model_specific_register.hpp +++ b/arch/x86_64/include/x86_64/cpu/impl/model_specific_register.hpp @@ -1,7 +1,7 @@ #ifndef TEACHOS_X86_64_CPU_IMPL_MODEL_SPECIFIC_REGISTER_HPP #define TEACHOS_X86_64_CPU_IMPL_MODEL_SPECIFIC_REGISTER_HPP -// IWYU pragma: private +// IWYU pragma: private, include "x86_64/cpu/registers.hpp" #include @@ -11,75 +11,19 @@ namespace teachos::cpu::x86_64 { - namespace impl - { - constexpr auto ia32_efer_number = 0xC000'0080u; - - template - struct model_specific_register_with_flags - { - }; - - template - struct model_specific_register_with_flags>> - { - using flags = ValueType; - - auto static set(flags flag) -> void - { - auto current = Derived::read(); - current |= flag; - Derived::write(current); - } - - auto static clear(flags flag) -> void - { - auto current = Derived::read(); - current &= ~flag; - Derived::write(current); - } - - auto test(flags flag) -> void - { - return Derived::read() & flag; - } - }; - - template - struct model_specific_register - : model_specific_register_with_flags, ValueType> - { - struct raw_value - { - std::uint32_t low_half; - std::uint32_t high_half; - }; - - auto static read() -> ValueType - { - auto raw = raw_value{}; - asm volatile("rdmsr" : "=a"(raw.low_half), "=d"(raw.high_half) : "c"(Number)); - return static_cast(std::bit_cast(raw)); - } - - auto static write(ValueType value) -> void - { - auto raw = std::bit_cast(static_cast(value)); - asm volatile("wrmsr" : : "a"(raw.low_half), "d"(raw.high_half), "c"(Number)); - } - }; - enum struct ia32_efer_flags : std::uint64_t - { - syscall_enable = 1uz << 0, - ia32e_mode_enable = 1uz << 8, - ia32e_mode_active = 1uz << 10, - execute_disable_bit_enable = 1uz << 11, - }; - - } // namespace impl - - using i32_efer = impl::model_specific_register; + //! The flags of the IA32_EFER (Extended Features Enable Register) MSR. + enum struct ia32_efer_flags : std::uint64_t + { + //! Enable the syscall and sysret instructions. + syscall_enable = 1uz << 0, + //! Enable IA-32e mode operation. + ia32e_mode_enable = 1uz << 8, + //! Indicates IA-32e mode is active (read-only) + ia32e_mode_active = 1uz << 10, + //! Enable the use of the NX page table bit. + execute_disable_bit_enable = 1uz << 11, + }; } // namespace teachos::cpu::x86_64 @@ -87,10 +31,121 @@ namespace kstd::ext { template<> - struct is_bitfield_enum : std::true_type + struct is_bitfield_enum : std::true_type { }; } // namespace kstd::ext +namespace teachos::cpu::x86_64 +{ + //! The MSR number for the IA32_EFER MSR + constexpr auto ia32_efer_number = 0xC000'0080u; + + //! A mixin for flag-oriented model specific registers. + //! + //! This mixin provides additional functionality for a flag-oriented model specific register. A models specific + //! register is flag-oriented, if it comprises a single field of bitfield. + //! + //! @tparam Derived The class deriving from this mixin. + //! @tparam ValueType The value type of the class deriving from this mixin. + template + struct model_specific_register_with_flags + { + }; + + //! @copydoc model_specific_register_with_flags + //! + //! @note This specialization provides the implementation for the case in which the value type of the model specific + //! register is a bitfield enum. + template + struct model_specific_register_with_flags>> + { + //! The of the flags used by this model specific register. + using flags = ValueType; + + //! Set one or more flags in this model specific register. + //! + //! @warning This function is to be considered **UNSAFE**. Setting flags in a model specific register may lead to + //! unexpected CPU behavior if the prerequisites imposed by the CPU specification are not fulfilled. This function + //! will perform no additional checks, and may, by extension, crash the system. + //! + //! @param flag One or a combination of flags to be set in the model specific register. + auto static set(flags flag) -> void + { + auto current = Derived::read(); + current |= flag; + Derived::write(current); + } + + //! Clear one or more flags in this model specific register. + //! + //! @warning This function is to be considered **UNSAFE**. Clearing flags in a model specific register may lead to + //! unexpected CPU behavior if the prerequisites imposed by the CPU specification are not fulfilled. This function + //! will perform no additional checks, and may, by extension, crash the system. + //! + //! @param flag One or a combination of flags to be cleared in the model specific register. + auto static clear(flags flag) -> void + { + auto current = Derived::read(); + current &= ~flag; + Derived::write(current); + } + + //! Test one or more flags in this model specific register + //! + //! @param flag One or a combination of flags to test for. + auto test(flags flag) -> flags + { + return Derived::read() & flag; + } + }; + + //! A model specific register (MSR) + //! + //! Model specific register are used to configure CPU features that a not necessarily present on all CPUs generations. + //! In the past, some MSRs have been defined to be architectural, meaning all CPUs of a given architecture (x86-64 in + //! this case) support them. Writing to a MSR is inherently dangerous, since a misconfiguration cal leave the CPU in + //! an invalid/undefined state. + //! + //! @tparam Number The register number of this MSR + //! @tparam ValueType The value type of this MSR + template + struct model_specific_register + : model_specific_register_with_flags, ValueType> + { + //! A raw MSR value, comprising two halfs. + //! + //! MSRs have been 64-bit in size even in the 32-bit intel architecture, and are thus written in two halfs. + struct raw_value + { + std::uint32_t low_half; //!< The lower half of the register value + std::uint32_t high_half; //!< The upper half of the register value + }; + + //! Read the current value of this MSR. + //! + //! @return The current value of this MSR. + auto static read() -> ValueType + { + auto raw = raw_value{}; + asm volatile("rdmsr" : "=a"(raw.low_half), "=d"(raw.high_half) : "c"(Number)); + return static_cast(std::bit_cast(raw)); + } + + //! Write a new value to this MSR. + //! + //! @param value The new value for this MSR. + auto static write(ValueType value) -> void + { + auto raw = std::bit_cast(static_cast(value)); + asm volatile("wrmsr" : : "a"(raw.low_half), "d"(raw.high_half), "c"(Number)); + } + }; + + //! The I32_EFER (Extended Feature Enable Register) MSR + using i32_efer = model_specific_register; + +} // namespace teachos::cpu::x86_64 + #endif \ No newline at end of file -- cgit v1.2.3 From 6fdfe22f75c7202eee84b9b89f6abd5dbc60fedc Mon Sep 17 00:00:00 2001 From: Felix Morgner Date: Fri, 12 Dec 2025 10:54:22 +0100 Subject: x86_64/cpu: flatten file hierarchy --- .../include/x86_64/cpu/control_registers.hpp | 259 +++++++++++++++++++++ .../include/x86_64/cpu/impl/control_registers.hpp | 259 --------------------- .../x86_64/cpu/impl/model_specific_register.hpp | 151 ------------ .../include/x86_64/cpu/model_specific_register.hpp | 151 ++++++++++++ arch/x86_64/include/x86_64/cpu/registers.hpp | 4 +- arch/x86_64/src/kapi/memory.cpp | 1 - 6 files changed, 412 insertions(+), 413 deletions(-) create mode 100644 arch/x86_64/include/x86_64/cpu/control_registers.hpp delete mode 100644 arch/x86_64/include/x86_64/cpu/impl/control_registers.hpp delete mode 100644 arch/x86_64/include/x86_64/cpu/impl/model_specific_register.hpp create mode 100644 arch/x86_64/include/x86_64/cpu/model_specific_register.hpp (limited to 'arch/x86_64') diff --git a/arch/x86_64/include/x86_64/cpu/control_registers.hpp b/arch/x86_64/include/x86_64/cpu/control_registers.hpp new file mode 100644 index 0000000..1f8c690 --- /dev/null +++ b/arch/x86_64/include/x86_64/cpu/control_registers.hpp @@ -0,0 +1,259 @@ +#ifndef TEACHOS_X86_64_CPU_IMPL_CONTROL_REGISTERS_HPP +#define TEACHOS_X86_64_CPU_IMPL_CONTROL_REGISTERS_HPP + +// IWYU pragma: private, include "x86_64/cpu/registers.hpp" + +#include "kapi/memory/address.hpp" + +#include + +#include +#include +#include + +namespace teachos::cpu::x86_64 +{ + namespace impl + { + //! The assembler templates used to access (r/w) CR0; + constexpr auto static cr0_asm = std::pair{"mov %%cr0, %0", "mov %0, %%cr0"}; + + //! The assembler templates used to access (r/w) CR2; + constexpr auto static cr2_asm = std::pair{"mov %%cr2, %0", "mov %0, %%cr2"}; + + //! The assembler templates used to access (r/w) CR3; + constexpr auto static cr3_asm = std::pair{"mov %%cr3, %0", "mov %0, %%cr3"}; + } // namespace impl + + //! The flags that can be set on CR0 configuration register. + enum struct cr0_flags : uint64_t + { + //! Enable protected mode. + protection_enable = 1uz << 0, + //! Enable wait-monitoring of the coprocessor after task switching. + monitor_coprocessor = 1uz << 1, + //! Emulate floating point coprocessor. + emulation = 1uz << 2, + //! Marks that a task switch has occurred. + task_switched = 1uz << 3, + //! Marks Intel 387 DX math coprocessor as available + extension_type = 1uz << 4, + //! Numeric error handling mode. + numeric_error = 1uz << 5, + //! Disable writing to read-only marked memory. + write_protect = 1uz << 16, + //! Enable Ring-3 alignment checks + alignment_check = 1uz << 18, + //! Disable write through + not_write_through = 1uz << 29, + //! Disable caching of memory accesses + cache_disable = 1uz << 30, + //! Enable paging + paging = 1uz << 31 + }; + + enum struct cr3_flags : std::uint64_t + { + page_level_write_through = 1uz << 0, + page_level_cache_disable = 1uz << 1, + }; +} // namespace teachos::cpu::x86_64 + +namespace kstd::ext +{ + template<> + struct is_bitfield_enum : std::true_type + { + }; + + template<> + struct is_bitfield_enum : std::true_type + { + }; +} // namespace kstd::ext + +namespace teachos::cpu::x86_64 +{ + //! A mixin for flag-oriented control registers. + //! + //! This mixin provides additional functionality for flag-oriented, or partially flag-oriented, control registers. A + //! control register is flag-oriented, if it comprises a bitfield and zero or more additional non-bitfield parts. + //! + //! @tparam Derived The class deriving from this mixin. + //! @tparam ValueType The value type of the class deriving from this mixin. + template + struct control_register_with_flags + { + }; + + //! @copydoc control_register_with_flags + //! + //! @note This specialization provides the implementation for the case in which the value type of the control register + //! is an enum. + template + struct control_register_with_flags>> + { + //! The type of the flags used by this control register + using flags = ValueType; + + //! Set one or more flags in this control register. + //! + //! @warning This function is to be considered **UNSAFE**. Setting flags in a control register may lead to + //! unexpected CPU behavior if the prerequisites imposed by the CPU specification are not fulfilled. This function + //! will perform no additional checks, and may, by extension, crash the system. + //! + //! @param value One or a combination of flags to be set in the control register. + auto static set(ValueType value) -> void + { + auto current = Derived::read(); + current |= value; + Derived::write(current); + } + + //! Clear one or more flags in this control register. + //! + //! @warning This function is to be considered **UNSAFE**. Clearing flags in a control register may lead to + //! unexpected CPU behavior if the prerequisites imposed by the CPU specification are not fulfilled. This function + //! will perform no additional checks, and may, by extension, crash the system. + //! + //! @param value One or a combination of flags to be cleared in the control register. + auto static clear(ValueType value) -> void + { + auto current = Derived::read(); + current &= ~value; + Derived::write(current); + } + }; + + //! A CPU control register. + //! + //! CPU control registers are used to configure builtin features of the CPU, for example memory protection and FPU + //! error reporting. Writing to a control register is inherently dangerous, since a misconfiguration can leave the CPU + //! in an invalid/undefined state. + template + struct control_register : control_register_with_flags, ValueType> + { + //! Read the current value of the control register. + //! + //! @return The currently set value of the control register. + [[nodiscard]] auto static read() -> ValueType + { + auto value = ValueType{}; + asm volatile((AssemblerTemplates->first) : "=r"(value)); + return value; + } + + //! Write a new value to the control register. + //! + //! @warning This function should be considered **UNSAFE**. Writing values to a control register may lead to + //! unexpected CPU behavior if the prerequisites imposed by the CPU specification are not fulfilled. This function + //! will perform no additional checks, and may, by extension, crash the system. + //! + //! @param value The new value to write to the control register. + auto static write(ValueType value) -> void + { + asm volatile((AssemblerTemplates->second) : : "r"(value)); + } + }; + + //! The value type of the CR3 control register. + //! + //! The CR3 control register holds the root configuration of the virtual memory protection mechanism. It contains the + //! page aligned physical address of the root page map, as well as the root paging configuration flags. + struct cr3_value + { + //! Contstruct a 0-value CR3 value. + constexpr cr3_value() = default; + + //! Construct a CR3 value using the given root page map address and flags. + //! + //! @param address The physical address of the root page map + //! @param flags The root configuration flags of the paging system. + constexpr cr3_value(memory::physical_address address, cr3_flags flags = static_cast(0)) + : m_flags{static_cast(flags)} + , m_address{static_cast(address.raw())} + {} + + //! Extract the physical address of the root page map from this value. + //! + //! @return The physical address of the root page map. + [[nodiscard]] constexpr auto address() const -> memory::physical_address + { + return memory::physical_address{m_address}; + } + + //! Encode the page aligned physical address of the root page map into this value. + //! + //! @param address The page aligned physical address of the root page map. + constexpr auto address(memory::physical_address address) -> void + { + m_address = static_cast(address.raw()); + } + + //! Extract the root paging configuration flags from this value. + //! + //! @return The root paging configuration flags. + [[nodiscard]] constexpr auto flags() const -> cr3_flags + { + return static_cast(m_flags); + } + + //! Encode the root paging configuration flags into this value. + //! + //! @param flags The root paging configuration flags. + constexpr auto flags(cr3_flags flags) -> void + { + m_flags = static_cast(flags); + } + + //! Add the given flags to the current set of encoded root configuration flags of this value. + //! + //! @param flags The root configuration flags to add. + //! @return A reference to this value. + constexpr auto operator|=(cr3_flags flags) -> cr3_value & + { + m_flags |= static_cast(flags); + return *this; + } + + //! Mask the root configuration flags of this value. + //! + //! @param mask The mask to apply to the root configuration flags. + //! @return A reference to this value. + constexpr auto operator&=(cr3_flags mask) -> cr3_value & + { + m_flags &= static_cast(mask); + return *this; + } + + private: + //! Reserved bits. + std::uint64_t : 3; + //! The root paging configuration flags. + std::uint64_t m_flags : 2 {}; + //! Reserved bits. + std::uint64_t : 7; + //! The page aligned physical address of the root page map. + std::uint64_t m_address : 52 {}; + }; + + static_assert(sizeof(cr3_value) == sizeof(std::uint64_t)); + + //! Configuration Register 0. + //! + //! This configuration register holds various control flags to configure the configure the basic operation of the CPU. + using cr0 = control_register; + + //! Configuration Register 2. + //! + //! This configuration register holds the memory address the access to which has triggered the most recent page fault. + using cr2 = control_register; + + //! Configuration Register 3. + //! + //! This register holds the configuration of the virtual memory protection configuration. + using cr3 = control_register; + +} // namespace teachos::cpu::x86_64 + +#endif \ No newline at end of file diff --git a/arch/x86_64/include/x86_64/cpu/impl/control_registers.hpp b/arch/x86_64/include/x86_64/cpu/impl/control_registers.hpp deleted file mode 100644 index 1f8c690..0000000 --- a/arch/x86_64/include/x86_64/cpu/impl/control_registers.hpp +++ /dev/null @@ -1,259 +0,0 @@ -#ifndef TEACHOS_X86_64_CPU_IMPL_CONTROL_REGISTERS_HPP -#define TEACHOS_X86_64_CPU_IMPL_CONTROL_REGISTERS_HPP - -// IWYU pragma: private, include "x86_64/cpu/registers.hpp" - -#include "kapi/memory/address.hpp" - -#include - -#include -#include -#include - -namespace teachos::cpu::x86_64 -{ - namespace impl - { - //! The assembler templates used to access (r/w) CR0; - constexpr auto static cr0_asm = std::pair{"mov %%cr0, %0", "mov %0, %%cr0"}; - - //! The assembler templates used to access (r/w) CR2; - constexpr auto static cr2_asm = std::pair{"mov %%cr2, %0", "mov %0, %%cr2"}; - - //! The assembler templates used to access (r/w) CR3; - constexpr auto static cr3_asm = std::pair{"mov %%cr3, %0", "mov %0, %%cr3"}; - } // namespace impl - - //! The flags that can be set on CR0 configuration register. - enum struct cr0_flags : uint64_t - { - //! Enable protected mode. - protection_enable = 1uz << 0, - //! Enable wait-monitoring of the coprocessor after task switching. - monitor_coprocessor = 1uz << 1, - //! Emulate floating point coprocessor. - emulation = 1uz << 2, - //! Marks that a task switch has occurred. - task_switched = 1uz << 3, - //! Marks Intel 387 DX math coprocessor as available - extension_type = 1uz << 4, - //! Numeric error handling mode. - numeric_error = 1uz << 5, - //! Disable writing to read-only marked memory. - write_protect = 1uz << 16, - //! Enable Ring-3 alignment checks - alignment_check = 1uz << 18, - //! Disable write through - not_write_through = 1uz << 29, - //! Disable caching of memory accesses - cache_disable = 1uz << 30, - //! Enable paging - paging = 1uz << 31 - }; - - enum struct cr3_flags : std::uint64_t - { - page_level_write_through = 1uz << 0, - page_level_cache_disable = 1uz << 1, - }; -} // namespace teachos::cpu::x86_64 - -namespace kstd::ext -{ - template<> - struct is_bitfield_enum : std::true_type - { - }; - - template<> - struct is_bitfield_enum : std::true_type - { - }; -} // namespace kstd::ext - -namespace teachos::cpu::x86_64 -{ - //! A mixin for flag-oriented control registers. - //! - //! This mixin provides additional functionality for flag-oriented, or partially flag-oriented, control registers. A - //! control register is flag-oriented, if it comprises a bitfield and zero or more additional non-bitfield parts. - //! - //! @tparam Derived The class deriving from this mixin. - //! @tparam ValueType The value type of the class deriving from this mixin. - template - struct control_register_with_flags - { - }; - - //! @copydoc control_register_with_flags - //! - //! @note This specialization provides the implementation for the case in which the value type of the control register - //! is an enum. - template - struct control_register_with_flags>> - { - //! The type of the flags used by this control register - using flags = ValueType; - - //! Set one or more flags in this control register. - //! - //! @warning This function is to be considered **UNSAFE**. Setting flags in a control register may lead to - //! unexpected CPU behavior if the prerequisites imposed by the CPU specification are not fulfilled. This function - //! will perform no additional checks, and may, by extension, crash the system. - //! - //! @param value One or a combination of flags to be set in the control register. - auto static set(ValueType value) -> void - { - auto current = Derived::read(); - current |= value; - Derived::write(current); - } - - //! Clear one or more flags in this control register. - //! - //! @warning This function is to be considered **UNSAFE**. Clearing flags in a control register may lead to - //! unexpected CPU behavior if the prerequisites imposed by the CPU specification are not fulfilled. This function - //! will perform no additional checks, and may, by extension, crash the system. - //! - //! @param value One or a combination of flags to be cleared in the control register. - auto static clear(ValueType value) -> void - { - auto current = Derived::read(); - current &= ~value; - Derived::write(current); - } - }; - - //! A CPU control register. - //! - //! CPU control registers are used to configure builtin features of the CPU, for example memory protection and FPU - //! error reporting. Writing to a control register is inherently dangerous, since a misconfiguration can leave the CPU - //! in an invalid/undefined state. - template - struct control_register : control_register_with_flags, ValueType> - { - //! Read the current value of the control register. - //! - //! @return The currently set value of the control register. - [[nodiscard]] auto static read() -> ValueType - { - auto value = ValueType{}; - asm volatile((AssemblerTemplates->first) : "=r"(value)); - return value; - } - - //! Write a new value to the control register. - //! - //! @warning This function should be considered **UNSAFE**. Writing values to a control register may lead to - //! unexpected CPU behavior if the prerequisites imposed by the CPU specification are not fulfilled. This function - //! will perform no additional checks, and may, by extension, crash the system. - //! - //! @param value The new value to write to the control register. - auto static write(ValueType value) -> void - { - asm volatile((AssemblerTemplates->second) : : "r"(value)); - } - }; - - //! The value type of the CR3 control register. - //! - //! The CR3 control register holds the root configuration of the virtual memory protection mechanism. It contains the - //! page aligned physical address of the root page map, as well as the root paging configuration flags. - struct cr3_value - { - //! Contstruct a 0-value CR3 value. - constexpr cr3_value() = default; - - //! Construct a CR3 value using the given root page map address and flags. - //! - //! @param address The physical address of the root page map - //! @param flags The root configuration flags of the paging system. - constexpr cr3_value(memory::physical_address address, cr3_flags flags = static_cast(0)) - : m_flags{static_cast(flags)} - , m_address{static_cast(address.raw())} - {} - - //! Extract the physical address of the root page map from this value. - //! - //! @return The physical address of the root page map. - [[nodiscard]] constexpr auto address() const -> memory::physical_address - { - return memory::physical_address{m_address}; - } - - //! Encode the page aligned physical address of the root page map into this value. - //! - //! @param address The page aligned physical address of the root page map. - constexpr auto address(memory::physical_address address) -> void - { - m_address = static_cast(address.raw()); - } - - //! Extract the root paging configuration flags from this value. - //! - //! @return The root paging configuration flags. - [[nodiscard]] constexpr auto flags() const -> cr3_flags - { - return static_cast(m_flags); - } - - //! Encode the root paging configuration flags into this value. - //! - //! @param flags The root paging configuration flags. - constexpr auto flags(cr3_flags flags) -> void - { - m_flags = static_cast(flags); - } - - //! Add the given flags to the current set of encoded root configuration flags of this value. - //! - //! @param flags The root configuration flags to add. - //! @return A reference to this value. - constexpr auto operator|=(cr3_flags flags) -> cr3_value & - { - m_flags |= static_cast(flags); - return *this; - } - - //! Mask the root configuration flags of this value. - //! - //! @param mask The mask to apply to the root configuration flags. - //! @return A reference to this value. - constexpr auto operator&=(cr3_flags mask) -> cr3_value & - { - m_flags &= static_cast(mask); - return *this; - } - - private: - //! Reserved bits. - std::uint64_t : 3; - //! The root paging configuration flags. - std::uint64_t m_flags : 2 {}; - //! Reserved bits. - std::uint64_t : 7; - //! The page aligned physical address of the root page map. - std::uint64_t m_address : 52 {}; - }; - - static_assert(sizeof(cr3_value) == sizeof(std::uint64_t)); - - //! Configuration Register 0. - //! - //! This configuration register holds various control flags to configure the configure the basic operation of the CPU. - using cr0 = control_register; - - //! Configuration Register 2. - //! - //! This configuration register holds the memory address the access to which has triggered the most recent page fault. - using cr2 = control_register; - - //! Configuration Register 3. - //! - //! This register holds the configuration of the virtual memory protection configuration. - using cr3 = control_register; - -} // namespace teachos::cpu::x86_64 - -#endif \ No newline at end of file diff --git a/arch/x86_64/include/x86_64/cpu/impl/model_specific_register.hpp b/arch/x86_64/include/x86_64/cpu/impl/model_specific_register.hpp deleted file mode 100644 index 080b280..0000000 --- a/arch/x86_64/include/x86_64/cpu/impl/model_specific_register.hpp +++ /dev/null @@ -1,151 +0,0 @@ -#ifndef TEACHOS_X86_64_CPU_IMPL_MODEL_SPECIFIC_REGISTER_HPP -#define TEACHOS_X86_64_CPU_IMPL_MODEL_SPECIFIC_REGISTER_HPP - -// IWYU pragma: private, include "x86_64/cpu/registers.hpp" - -#include - -#include -#include -#include - -namespace teachos::cpu::x86_64 -{ - - //! The flags of the IA32_EFER (Extended Features Enable Register) MSR. - enum struct ia32_efer_flags : std::uint64_t - { - //! Enable the syscall and sysret instructions. - syscall_enable = 1uz << 0, - //! Enable IA-32e mode operation. - ia32e_mode_enable = 1uz << 8, - //! Indicates IA-32e mode is active (read-only) - ia32e_mode_active = 1uz << 10, - //! Enable the use of the NX page table bit. - execute_disable_bit_enable = 1uz << 11, - }; - -} // namespace teachos::cpu::x86_64 - -namespace kstd::ext -{ - - template<> - struct is_bitfield_enum : std::true_type - { - }; - -} // namespace kstd::ext - -namespace teachos::cpu::x86_64 -{ - //! The MSR number for the IA32_EFER MSR - constexpr auto ia32_efer_number = 0xC000'0080u; - - //! A mixin for flag-oriented model specific registers. - //! - //! This mixin provides additional functionality for a flag-oriented model specific register. A models specific - //! register is flag-oriented, if it comprises a single field of bitfield. - //! - //! @tparam Derived The class deriving from this mixin. - //! @tparam ValueType The value type of the class deriving from this mixin. - template - struct model_specific_register_with_flags - { - }; - - //! @copydoc model_specific_register_with_flags - //! - //! @note This specialization provides the implementation for the case in which the value type of the model specific - //! register is a bitfield enum. - template - struct model_specific_register_with_flags>> - { - //! The of the flags used by this model specific register. - using flags = ValueType; - - //! Set one or more flags in this model specific register. - //! - //! @warning This function is to be considered **UNSAFE**. Setting flags in a model specific register may lead to - //! unexpected CPU behavior if the prerequisites imposed by the CPU specification are not fulfilled. This function - //! will perform no additional checks, and may, by extension, crash the system. - //! - //! @param flag One or a combination of flags to be set in the model specific register. - auto static set(flags flag) -> void - { - auto current = Derived::read(); - current |= flag; - Derived::write(current); - } - - //! Clear one or more flags in this model specific register. - //! - //! @warning This function is to be considered **UNSAFE**. Clearing flags in a model specific register may lead to - //! unexpected CPU behavior if the prerequisites imposed by the CPU specification are not fulfilled. This function - //! will perform no additional checks, and may, by extension, crash the system. - //! - //! @param flag One or a combination of flags to be cleared in the model specific register. - auto static clear(flags flag) -> void - { - auto current = Derived::read(); - current &= ~flag; - Derived::write(current); - } - - //! Test one or more flags in this model specific register - //! - //! @param flag One or a combination of flags to test for. - auto test(flags flag) -> flags - { - return Derived::read() & flag; - } - }; - - //! A model specific register (MSR) - //! - //! Model specific register are used to configure CPU features that a not necessarily present on all CPUs generations. - //! In the past, some MSRs have been defined to be architectural, meaning all CPUs of a given architecture (x86-64 in - //! this case) support them. Writing to a MSR is inherently dangerous, since a misconfiguration cal leave the CPU in - //! an invalid/undefined state. - //! - //! @tparam Number The register number of this MSR - //! @tparam ValueType The value type of this MSR - template - struct model_specific_register - : model_specific_register_with_flags, ValueType> - { - //! A raw MSR value, comprising two halfs. - //! - //! MSRs have been 64-bit in size even in the 32-bit intel architecture, and are thus written in two halfs. - struct raw_value - { - std::uint32_t low_half; //!< The lower half of the register value - std::uint32_t high_half; //!< The upper half of the register value - }; - - //! Read the current value of this MSR. - //! - //! @return The current value of this MSR. - auto static read() -> ValueType - { - auto raw = raw_value{}; - asm volatile("rdmsr" : "=a"(raw.low_half), "=d"(raw.high_half) : "c"(Number)); - return static_cast(std::bit_cast(raw)); - } - - //! Write a new value to this MSR. - //! - //! @param value The new value for this MSR. - auto static write(ValueType value) -> void - { - auto raw = std::bit_cast(static_cast(value)); - asm volatile("wrmsr" : : "a"(raw.low_half), "d"(raw.high_half), "c"(Number)); - } - }; - - //! The I32_EFER (Extended Feature Enable Register) MSR - using i32_efer = model_specific_register; - -} // namespace teachos::cpu::x86_64 - -#endif \ No newline at end of file diff --git a/arch/x86_64/include/x86_64/cpu/model_specific_register.hpp b/arch/x86_64/include/x86_64/cpu/model_specific_register.hpp new file mode 100644 index 0000000..080b280 --- /dev/null +++ b/arch/x86_64/include/x86_64/cpu/model_specific_register.hpp @@ -0,0 +1,151 @@ +#ifndef TEACHOS_X86_64_CPU_IMPL_MODEL_SPECIFIC_REGISTER_HPP +#define TEACHOS_X86_64_CPU_IMPL_MODEL_SPECIFIC_REGISTER_HPP + +// IWYU pragma: private, include "x86_64/cpu/registers.hpp" + +#include + +#include +#include +#include + +namespace teachos::cpu::x86_64 +{ + + //! The flags of the IA32_EFER (Extended Features Enable Register) MSR. + enum struct ia32_efer_flags : std::uint64_t + { + //! Enable the syscall and sysret instructions. + syscall_enable = 1uz << 0, + //! Enable IA-32e mode operation. + ia32e_mode_enable = 1uz << 8, + //! Indicates IA-32e mode is active (read-only) + ia32e_mode_active = 1uz << 10, + //! Enable the use of the NX page table bit. + execute_disable_bit_enable = 1uz << 11, + }; + +} // namespace teachos::cpu::x86_64 + +namespace kstd::ext +{ + + template<> + struct is_bitfield_enum : std::true_type + { + }; + +} // namespace kstd::ext + +namespace teachos::cpu::x86_64 +{ + //! The MSR number for the IA32_EFER MSR + constexpr auto ia32_efer_number = 0xC000'0080u; + + //! A mixin for flag-oriented model specific registers. + //! + //! This mixin provides additional functionality for a flag-oriented model specific register. A models specific + //! register is flag-oriented, if it comprises a single field of bitfield. + //! + //! @tparam Derived The class deriving from this mixin. + //! @tparam ValueType The value type of the class deriving from this mixin. + template + struct model_specific_register_with_flags + { + }; + + //! @copydoc model_specific_register_with_flags + //! + //! @note This specialization provides the implementation for the case in which the value type of the model specific + //! register is a bitfield enum. + template + struct model_specific_register_with_flags>> + { + //! The of the flags used by this model specific register. + using flags = ValueType; + + //! Set one or more flags in this model specific register. + //! + //! @warning This function is to be considered **UNSAFE**. Setting flags in a model specific register may lead to + //! unexpected CPU behavior if the prerequisites imposed by the CPU specification are not fulfilled. This function + //! will perform no additional checks, and may, by extension, crash the system. + //! + //! @param flag One or a combination of flags to be set in the model specific register. + auto static set(flags flag) -> void + { + auto current = Derived::read(); + current |= flag; + Derived::write(current); + } + + //! Clear one or more flags in this model specific register. + //! + //! @warning This function is to be considered **UNSAFE**. Clearing flags in a model specific register may lead to + //! unexpected CPU behavior if the prerequisites imposed by the CPU specification are not fulfilled. This function + //! will perform no additional checks, and may, by extension, crash the system. + //! + //! @param flag One or a combination of flags to be cleared in the model specific register. + auto static clear(flags flag) -> void + { + auto current = Derived::read(); + current &= ~flag; + Derived::write(current); + } + + //! Test one or more flags in this model specific register + //! + //! @param flag One or a combination of flags to test for. + auto test(flags flag) -> flags + { + return Derived::read() & flag; + } + }; + + //! A model specific register (MSR) + //! + //! Model specific register are used to configure CPU features that a not necessarily present on all CPUs generations. + //! In the past, some MSRs have been defined to be architectural, meaning all CPUs of a given architecture (x86-64 in + //! this case) support them. Writing to a MSR is inherently dangerous, since a misconfiguration cal leave the CPU in + //! an invalid/undefined state. + //! + //! @tparam Number The register number of this MSR + //! @tparam ValueType The value type of this MSR + template + struct model_specific_register + : model_specific_register_with_flags, ValueType> + { + //! A raw MSR value, comprising two halfs. + //! + //! MSRs have been 64-bit in size even in the 32-bit intel architecture, and are thus written in two halfs. + struct raw_value + { + std::uint32_t low_half; //!< The lower half of the register value + std::uint32_t high_half; //!< The upper half of the register value + }; + + //! Read the current value of this MSR. + //! + //! @return The current value of this MSR. + auto static read() -> ValueType + { + auto raw = raw_value{}; + asm volatile("rdmsr" : "=a"(raw.low_half), "=d"(raw.high_half) : "c"(Number)); + return static_cast(std::bit_cast(raw)); + } + + //! Write a new value to this MSR. + //! + //! @param value The new value for this MSR. + auto static write(ValueType value) -> void + { + auto raw = std::bit_cast(static_cast(value)); + asm volatile("wrmsr" : : "a"(raw.low_half), "d"(raw.high_half), "c"(Number)); + } + }; + + //! The I32_EFER (Extended Feature Enable Register) MSR + using i32_efer = model_specific_register; + +} // namespace teachos::cpu::x86_64 + +#endif \ No newline at end of file diff --git a/arch/x86_64/include/x86_64/cpu/registers.hpp b/arch/x86_64/include/x86_64/cpu/registers.hpp index 97cba19..cb56270 100644 --- a/arch/x86_64/include/x86_64/cpu/registers.hpp +++ b/arch/x86_64/include/x86_64/cpu/registers.hpp @@ -1,7 +1,7 @@ #ifndef TEACHOS_X86_64_CPU_REGISTERS_HPP #define TEACHOS_X86_64_CPU_REGISTERS_HPP -#include "x86_64/cpu/impl/control_registers.hpp" // IWYU pragma: export -#include "x86_64/cpu/impl/model_specific_register.hpp" // IWYU pragma: export +#include "x86_64/cpu/control_registers.hpp" // IWYU pragma: export +#include "x86_64/cpu/model_specific_register.hpp" // IWYU pragma: export #endif \ No newline at end of file diff --git a/arch/x86_64/src/kapi/memory.cpp b/arch/x86_64/src/kapi/memory.cpp index cd0bd77..a20483c 100644 --- a/arch/x86_64/src/kapi/memory.cpp +++ b/arch/x86_64/src/kapi/memory.cpp @@ -5,7 +5,6 @@ #include "x86_64/boot/boot.hpp" #include "x86_64/boot/ld.hpp" -#include "x86_64/cpu/impl/control_registers.hpp" #include "x86_64/cpu/registers.hpp" #include "x86_64/memory/buffered_allocator.hpp" #include "x86_64/memory/kernel_mapper.hpp" -- cgit v1.2.3 From 0a2c2c408d8fda248d56df349b4c9f9cc5e8bade Mon Sep 17 00:00:00 2001 From: Felix Morgner Date: Fri, 12 Dec 2025 10:56:11 +0100 Subject: x86_64/cpu: move register definitions --- .../x86_64/include/x86_64/cpu/control_register.hpp | 244 +++++++++++++++++++ .../include/x86_64/cpu/control_registers.hpp | 259 --------------------- .../include/x86_64/cpu/model_specific_register.hpp | 3 - arch/x86_64/include/x86_64/cpu/registers.hpp | 25 +- 4 files changed, 268 insertions(+), 263 deletions(-) create mode 100644 arch/x86_64/include/x86_64/cpu/control_register.hpp delete mode 100644 arch/x86_64/include/x86_64/cpu/control_registers.hpp (limited to 'arch/x86_64') diff --git a/arch/x86_64/include/x86_64/cpu/control_register.hpp b/arch/x86_64/include/x86_64/cpu/control_register.hpp new file mode 100644 index 0000000..35ffcae --- /dev/null +++ b/arch/x86_64/include/x86_64/cpu/control_register.hpp @@ -0,0 +1,244 @@ +#ifndef TEACHOS_X86_64_CPU_IMPL_CONTROL_REGISTERS_HPP +#define TEACHOS_X86_64_CPU_IMPL_CONTROL_REGISTERS_HPP + +// IWYU pragma: private, include "x86_64/cpu/registers.hpp" + +#include "kapi/memory/address.hpp" + +#include + +#include +#include +#include + +namespace teachos::cpu::x86_64 +{ + namespace impl + { + //! The assembler templates used to access (r/w) CR0; + constexpr auto static cr0_asm = std::pair{"mov %%cr0, %0", "mov %0, %%cr0"}; + + //! The assembler templates used to access (r/w) CR2; + constexpr auto static cr2_asm = std::pair{"mov %%cr2, %0", "mov %0, %%cr2"}; + + //! The assembler templates used to access (r/w) CR3; + constexpr auto static cr3_asm = std::pair{"mov %%cr3, %0", "mov %0, %%cr3"}; + } // namespace impl + + //! The flags that can be set on CR0 configuration register. + enum struct cr0_flags : uint64_t + { + //! Enable protected mode. + protection_enable = 1uz << 0, + //! Enable wait-monitoring of the coprocessor after task switching. + monitor_coprocessor = 1uz << 1, + //! Emulate floating point coprocessor. + emulation = 1uz << 2, + //! Marks that a task switch has occurred. + task_switched = 1uz << 3, + //! Marks Intel 387 DX math coprocessor as available + extension_type = 1uz << 4, + //! Numeric error handling mode. + numeric_error = 1uz << 5, + //! Disable writing to read-only marked memory. + write_protect = 1uz << 16, + //! Enable Ring-3 alignment checks + alignment_check = 1uz << 18, + //! Disable write through + not_write_through = 1uz << 29, + //! Disable caching of memory accesses + cache_disable = 1uz << 30, + //! Enable paging + paging = 1uz << 31 + }; + + enum struct cr3_flags : std::uint64_t + { + page_level_write_through = 1uz << 0, + page_level_cache_disable = 1uz << 1, + }; +} // namespace teachos::cpu::x86_64 + +namespace kstd::ext +{ + template<> + struct is_bitfield_enum : std::true_type + { + }; + + template<> + struct is_bitfield_enum : std::true_type + { + }; +} // namespace kstd::ext + +namespace teachos::cpu::x86_64 +{ + //! A mixin for flag-oriented control registers. + //! + //! This mixin provides additional functionality for flag-oriented, or partially flag-oriented, control registers. A + //! control register is flag-oriented, if it comprises a bitfield and zero or more additional non-bitfield parts. + //! + //! @tparam Derived The class deriving from this mixin. + //! @tparam ValueType The value type of the class deriving from this mixin. + template + struct control_register_with_flags + { + }; + + //! @copydoc control_register_with_flags + //! + //! @note This specialization provides the implementation for the case in which the value type of the control register + //! is an enum. + template + struct control_register_with_flags>> + { + //! The type of the flags used by this control register + using flags = ValueType; + + //! Set one or more flags in this control register. + //! + //! @warning This function is to be considered **UNSAFE**. Setting flags in a control register may lead to + //! unexpected CPU behavior if the prerequisites imposed by the CPU specification are not fulfilled. This function + //! will perform no additional checks, and may, by extension, crash the system. + //! + //! @param value One or a combination of flags to be set in the control register. + auto static set(ValueType value) -> void + { + auto current = Derived::read(); + current |= value; + Derived::write(current); + } + + //! Clear one or more flags in this control register. + //! + //! @warning This function is to be considered **UNSAFE**. Clearing flags in a control register may lead to + //! unexpected CPU behavior if the prerequisites imposed by the CPU specification are not fulfilled. This function + //! will perform no additional checks, and may, by extension, crash the system. + //! + //! @param value One or a combination of flags to be cleared in the control register. + auto static clear(ValueType value) -> void + { + auto current = Derived::read(); + current &= ~value; + Derived::write(current); + } + }; + + //! A CPU control register. + //! + //! CPU control registers are used to configure builtin features of the CPU, for example memory protection and FPU + //! error reporting. Writing to a control register is inherently dangerous, since a misconfiguration can leave the CPU + //! in an invalid/undefined state. + template + struct control_register : control_register_with_flags, ValueType> + { + //! Read the current value of the control register. + //! + //! @return The currently set value of the control register. + [[nodiscard]] auto static read() -> ValueType + { + auto value = ValueType{}; + asm volatile((AssemblerTemplates->first) : "=r"(value)); + return value; + } + + //! Write a new value to the control register. + //! + //! @warning This function should be considered **UNSAFE**. Writing values to a control register may lead to + //! unexpected CPU behavior if the prerequisites imposed by the CPU specification are not fulfilled. This function + //! will perform no additional checks, and may, by extension, crash the system. + //! + //! @param value The new value to write to the control register. + auto static write(ValueType value) -> void + { + asm volatile((AssemblerTemplates->second) : : "r"(value)); + } + }; + + //! The value type of the CR3 control register. + //! + //! The CR3 control register holds the root configuration of the virtual memory protection mechanism. It contains the + //! page aligned physical address of the root page map, as well as the root paging configuration flags. + struct cr3_value + { + //! Contstruct a 0-value CR3 value. + constexpr cr3_value() = default; + + //! Construct a CR3 value using the given root page map address and flags. + //! + //! @param address The physical address of the root page map + //! @param flags The root configuration flags of the paging system. + constexpr cr3_value(memory::physical_address address, cr3_flags flags = static_cast(0)) + : m_flags{static_cast(flags)} + , m_address{static_cast(address.raw())} + {} + + //! Extract the physical address of the root page map from this value. + //! + //! @return The physical address of the root page map. + [[nodiscard]] constexpr auto address() const -> memory::physical_address + { + return memory::physical_address{m_address}; + } + + //! Encode the page aligned physical address of the root page map into this value. + //! + //! @param address The page aligned physical address of the root page map. + constexpr auto address(memory::physical_address address) -> void + { + m_address = static_cast(address.raw()); + } + + //! Extract the root paging configuration flags from this value. + //! + //! @return The root paging configuration flags. + [[nodiscard]] constexpr auto flags() const -> cr3_flags + { + return static_cast(m_flags); + } + + //! Encode the root paging configuration flags into this value. + //! + //! @param flags The root paging configuration flags. + constexpr auto flags(cr3_flags flags) -> void + { + m_flags = static_cast(flags); + } + + //! Add the given flags to the current set of encoded root configuration flags of this value. + //! + //! @param flags The root configuration flags to add. + //! @return A reference to this value. + constexpr auto operator|=(cr3_flags flags) -> cr3_value & + { + m_flags |= static_cast(flags); + return *this; + } + + //! Mask the root configuration flags of this value. + //! + //! @param mask The mask to apply to the root configuration flags. + //! @return A reference to this value. + constexpr auto operator&=(cr3_flags mask) -> cr3_value & + { + m_flags &= static_cast(mask); + return *this; + } + + private: + //! Reserved bits. + std::uint64_t : 3; + //! The root paging configuration flags. + std::uint64_t m_flags : 2 {}; + //! Reserved bits. + std::uint64_t : 7; + //! The page aligned physical address of the root page map. + std::uint64_t m_address : 52 {}; + }; + + static_assert(sizeof(cr3_value) == sizeof(std::uint64_t)); + +} // namespace teachos::cpu::x86_64 + +#endif \ No newline at end of file diff --git a/arch/x86_64/include/x86_64/cpu/control_registers.hpp b/arch/x86_64/include/x86_64/cpu/control_registers.hpp deleted file mode 100644 index 1f8c690..0000000 --- a/arch/x86_64/include/x86_64/cpu/control_registers.hpp +++ /dev/null @@ -1,259 +0,0 @@ -#ifndef TEACHOS_X86_64_CPU_IMPL_CONTROL_REGISTERS_HPP -#define TEACHOS_X86_64_CPU_IMPL_CONTROL_REGISTERS_HPP - -// IWYU pragma: private, include "x86_64/cpu/registers.hpp" - -#include "kapi/memory/address.hpp" - -#include - -#include -#include -#include - -namespace teachos::cpu::x86_64 -{ - namespace impl - { - //! The assembler templates used to access (r/w) CR0; - constexpr auto static cr0_asm = std::pair{"mov %%cr0, %0", "mov %0, %%cr0"}; - - //! The assembler templates used to access (r/w) CR2; - constexpr auto static cr2_asm = std::pair{"mov %%cr2, %0", "mov %0, %%cr2"}; - - //! The assembler templates used to access (r/w) CR3; - constexpr auto static cr3_asm = std::pair{"mov %%cr3, %0", "mov %0, %%cr3"}; - } // namespace impl - - //! The flags that can be set on CR0 configuration register. - enum struct cr0_flags : uint64_t - { - //! Enable protected mode. - protection_enable = 1uz << 0, - //! Enable wait-monitoring of the coprocessor after task switching. - monitor_coprocessor = 1uz << 1, - //! Emulate floating point coprocessor. - emulation = 1uz << 2, - //! Marks that a task switch has occurred. - task_switched = 1uz << 3, - //! Marks Intel 387 DX math coprocessor as available - extension_type = 1uz << 4, - //! Numeric error handling mode. - numeric_error = 1uz << 5, - //! Disable writing to read-only marked memory. - write_protect = 1uz << 16, - //! Enable Ring-3 alignment checks - alignment_check = 1uz << 18, - //! Disable write through - not_write_through = 1uz << 29, - //! Disable caching of memory accesses - cache_disable = 1uz << 30, - //! Enable paging - paging = 1uz << 31 - }; - - enum struct cr3_flags : std::uint64_t - { - page_level_write_through = 1uz << 0, - page_level_cache_disable = 1uz << 1, - }; -} // namespace teachos::cpu::x86_64 - -namespace kstd::ext -{ - template<> - struct is_bitfield_enum : std::true_type - { - }; - - template<> - struct is_bitfield_enum : std::true_type - { - }; -} // namespace kstd::ext - -namespace teachos::cpu::x86_64 -{ - //! A mixin for flag-oriented control registers. - //! - //! This mixin provides additional functionality for flag-oriented, or partially flag-oriented, control registers. A - //! control register is flag-oriented, if it comprises a bitfield and zero or more additional non-bitfield parts. - //! - //! @tparam Derived The class deriving from this mixin. - //! @tparam ValueType The value type of the class deriving from this mixin. - template - struct control_register_with_flags - { - }; - - //! @copydoc control_register_with_flags - //! - //! @note This specialization provides the implementation for the case in which the value type of the control register - //! is an enum. - template - struct control_register_with_flags>> - { - //! The type of the flags used by this control register - using flags = ValueType; - - //! Set one or more flags in this control register. - //! - //! @warning This function is to be considered **UNSAFE**. Setting flags in a control register may lead to - //! unexpected CPU behavior if the prerequisites imposed by the CPU specification are not fulfilled. This function - //! will perform no additional checks, and may, by extension, crash the system. - //! - //! @param value One or a combination of flags to be set in the control register. - auto static set(ValueType value) -> void - { - auto current = Derived::read(); - current |= value; - Derived::write(current); - } - - //! Clear one or more flags in this control register. - //! - //! @warning This function is to be considered **UNSAFE**. Clearing flags in a control register may lead to - //! unexpected CPU behavior if the prerequisites imposed by the CPU specification are not fulfilled. This function - //! will perform no additional checks, and may, by extension, crash the system. - //! - //! @param value One or a combination of flags to be cleared in the control register. - auto static clear(ValueType value) -> void - { - auto current = Derived::read(); - current &= ~value; - Derived::write(current); - } - }; - - //! A CPU control register. - //! - //! CPU control registers are used to configure builtin features of the CPU, for example memory protection and FPU - //! error reporting. Writing to a control register is inherently dangerous, since a misconfiguration can leave the CPU - //! in an invalid/undefined state. - template - struct control_register : control_register_with_flags, ValueType> - { - //! Read the current value of the control register. - //! - //! @return The currently set value of the control register. - [[nodiscard]] auto static read() -> ValueType - { - auto value = ValueType{}; - asm volatile((AssemblerTemplates->first) : "=r"(value)); - return value; - } - - //! Write a new value to the control register. - //! - //! @warning This function should be considered **UNSAFE**. Writing values to a control register may lead to - //! unexpected CPU behavior if the prerequisites imposed by the CPU specification are not fulfilled. This function - //! will perform no additional checks, and may, by extension, crash the system. - //! - //! @param value The new value to write to the control register. - auto static write(ValueType value) -> void - { - asm volatile((AssemblerTemplates->second) : : "r"(value)); - } - }; - - //! The value type of the CR3 control register. - //! - //! The CR3 control register holds the root configuration of the virtual memory protection mechanism. It contains the - //! page aligned physical address of the root page map, as well as the root paging configuration flags. - struct cr3_value - { - //! Contstruct a 0-value CR3 value. - constexpr cr3_value() = default; - - //! Construct a CR3 value using the given root page map address and flags. - //! - //! @param address The physical address of the root page map - //! @param flags The root configuration flags of the paging system. - constexpr cr3_value(memory::physical_address address, cr3_flags flags = static_cast(0)) - : m_flags{static_cast(flags)} - , m_address{static_cast(address.raw())} - {} - - //! Extract the physical address of the root page map from this value. - //! - //! @return The physical address of the root page map. - [[nodiscard]] constexpr auto address() const -> memory::physical_address - { - return memory::physical_address{m_address}; - } - - //! Encode the page aligned physical address of the root page map into this value. - //! - //! @param address The page aligned physical address of the root page map. - constexpr auto address(memory::physical_address address) -> void - { - m_address = static_cast(address.raw()); - } - - //! Extract the root paging configuration flags from this value. - //! - //! @return The root paging configuration flags. - [[nodiscard]] constexpr auto flags() const -> cr3_flags - { - return static_cast(m_flags); - } - - //! Encode the root paging configuration flags into this value. - //! - //! @param flags The root paging configuration flags. - constexpr auto flags(cr3_flags flags) -> void - { - m_flags = static_cast(flags); - } - - //! Add the given flags to the current set of encoded root configuration flags of this value. - //! - //! @param flags The root configuration flags to add. - //! @return A reference to this value. - constexpr auto operator|=(cr3_flags flags) -> cr3_value & - { - m_flags |= static_cast(flags); - return *this; - } - - //! Mask the root configuration flags of this value. - //! - //! @param mask The mask to apply to the root configuration flags. - //! @return A reference to this value. - constexpr auto operator&=(cr3_flags mask) -> cr3_value & - { - m_flags &= static_cast(mask); - return *this; - } - - private: - //! Reserved bits. - std::uint64_t : 3; - //! The root paging configuration flags. - std::uint64_t m_flags : 2 {}; - //! Reserved bits. - std::uint64_t : 7; - //! The page aligned physical address of the root page map. - std::uint64_t m_address : 52 {}; - }; - - static_assert(sizeof(cr3_value) == sizeof(std::uint64_t)); - - //! Configuration Register 0. - //! - //! This configuration register holds various control flags to configure the configure the basic operation of the CPU. - using cr0 = control_register; - - //! Configuration Register 2. - //! - //! This configuration register holds the memory address the access to which has triggered the most recent page fault. - using cr2 = control_register; - - //! Configuration Register 3. - //! - //! This register holds the configuration of the virtual memory protection configuration. - using cr3 = control_register; - -} // namespace teachos::cpu::x86_64 - -#endif \ No newline at end of file diff --git a/arch/x86_64/include/x86_64/cpu/model_specific_register.hpp b/arch/x86_64/include/x86_64/cpu/model_specific_register.hpp index 080b280..857b444 100644 --- a/arch/x86_64/include/x86_64/cpu/model_specific_register.hpp +++ b/arch/x86_64/include/x86_64/cpu/model_specific_register.hpp @@ -143,9 +143,6 @@ namespace teachos::cpu::x86_64 } }; - //! The I32_EFER (Extended Feature Enable Register) MSR - using i32_efer = model_specific_register; - } // namespace teachos::cpu::x86_64 #endif \ No newline at end of file diff --git a/arch/x86_64/include/x86_64/cpu/registers.hpp b/arch/x86_64/include/x86_64/cpu/registers.hpp index cb56270..8eb89e3 100644 --- a/arch/x86_64/include/x86_64/cpu/registers.hpp +++ b/arch/x86_64/include/x86_64/cpu/registers.hpp @@ -1,7 +1,30 @@ #ifndef TEACHOS_X86_64_CPU_REGISTERS_HPP #define TEACHOS_X86_64_CPU_REGISTERS_HPP -#include "x86_64/cpu/control_registers.hpp" // IWYU pragma: export +#include "x86_64/cpu/control_register.hpp" // IWYU pragma: export #include "x86_64/cpu/model_specific_register.hpp" // IWYU pragma: export +namespace teachos::cpu::x86_64 +{ + + //! Configuration Register 0. + //! + //! This configuration register holds various control flags to configure the configure the basic operation of the CPU. + using cr0 = control_register; + + //! Configuration Register 2. + //! + //! This configuration register holds the memory address the access to which has triggered the most recent page fault. + using cr2 = control_register; + + //! Configuration Register 3. + //! + //! This register holds the configuration of the virtual memory protection configuration. + using cr3 = control_register; + + //! The I32_EFER (Extended Feature Enable Register) MSR + using i32_efer = model_specific_register; + +} // namespace teachos::cpu::x86_64 + #endif \ No newline at end of file -- cgit v1.2.3 From 8fc5f9e3cc28b07b1f120eb1ffedc042fa6662b8 Mon Sep 17 00:00:00 2001 From: Felix Morgner Date: Fri, 12 Dec 2025 13:00:26 +0100 Subject: x86_64/kapi: implement remaining mapping steps --- arch/x86_64/src/kapi/memory.cpp | 54 ++++++++++++++++++++++++++++++++++++----- 1 file changed, 48 insertions(+), 6 deletions(-) (limited to 'arch/x86_64') diff --git a/arch/x86_64/src/kapi/memory.cpp b/arch/x86_64/src/kapi/memory.cpp index a20483c..abc0526 100644 --- a/arch/x86_64/src/kapi/memory.cpp +++ b/arch/x86_64/src/kapi/memory.cpp @@ -1,5 +1,6 @@ #include "kapi/memory.hpp" +#include "kapi/boot.hpp" #include "kapi/cio.hpp" #include "kapi/system.hpp" @@ -19,6 +20,7 @@ #include #include +#include #include #include @@ -114,6 +116,42 @@ namespace teachos::memory return *new_pml4_frame; } + auto remap_kernel() -> void + { + auto kernel_mapper = x86_64::kernel_mapper{boot::bootstrap_information.mbi}; + kernel_mapper.remap_kernel(); + } + + auto remap_vga_text_mode_buffer(page_mapper & mapper) -> void + { + constexpr auto vga_base = std::uintptr_t{0xb8000}; + auto vga_physical_start = physical_address{vga_base}; + auto vga_virtual_start = linear_address{vga_base + std::bit_cast(&boot::x86_64::TEACHOS_VMA)}; + + auto page = page::containing(vga_virtual_start); + auto frame = frame::containing(vga_physical_start); + + mapper.map(page, frame, page_mapper::flags::writable); + } + + auto remap_multiboot_information(page_mapper & mapper) -> void + { + auto mbi_base = std::bit_cast(boot::bootstrap_information.mbi); + auto mbi_size = boot::bootstrap_information.mbi->size_bytes(); + auto mbi_physical_start = physical_address{mbi_base}; + auto mbi_virtual_start = linear_address{mbi_base + std::bit_cast(&boot::x86_64::TEACHOS_VMA)}; + auto mbi_block_count = (mbi_size + PLATFORM_FRAME_SIZE - 1) / PLATFORM_FRAME_SIZE; + + for (auto i = 0uz; i < mbi_block_count; ++i) + { + auto page = page::containing(mbi_virtual_start) + 1; + auto frame = frame::containing(mbi_physical_start) + 1; + mapper.map(page, frame, page_mapper::flags::empty); + } + + boot::bootstrap_information.mbi = std::bit_cast(mbi_virtual_start.raw()); + } + } // namespace auto active_frame_allocator() -> frame_allocator & @@ -138,11 +176,14 @@ namespace teachos::memory auto init() -> void { auto static constinit is_initialized = std::atomic_flag{}; + if (is_initialized.test_and_set()) { system::panic("[x86_64] Memory management has already been initialized."); } + cio::println("[x86_64:MEM] Enabling additional CPU protection features."); + enable_cpu_protections(); auto early_allocator = create_early_frame_allocator(); @@ -154,17 +195,18 @@ namespace teachos::memory auto new_pml4_frame = inject_faux_pml4(allocation_buffer); - auto kernel_mapper = x86_64::kernel_mapper{boot::bootstrap_information.mbi}; - kernel_mapper.remap_kernel(); - cio::println("[x86_64:MEM] prepared new kernel image page maps."); + cio::println("[x86_64:MEM] Preparing new paging hierarchy."); + + remap_kernel(); + remap_vga_text_mode_buffer(recursive_mapper); + remap_multiboot_information(recursive_mapper); + + cio::println("[x86_64:MEM] Switching to new paging hierarchy."); auto cr3 = cpu::x86_64::cr3::read(); cr3.address(new_pml4_frame.start_address()); cpu::x86_64::cr3::write(cr3); - // 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(); mapper = nullptr; allocator = nullptr; } -- cgit v1.2.3 From 50c9c9a1d963e66f7658ab31e9ecd65bf227cfff Mon Sep 17 00:00:00 2001 From: Felix Morgner Date: Fri, 12 Dec 2025 14:07:28 +0100 Subject: x86_64/memory: clean up dependencies --- .../x86_64/include/x86_64/memory/kernel_mapper.hpp | 6 +- arch/x86_64/include/x86_64/memory/page_table.hpp | 54 ++++++++ arch/x86_64/include/x86_64/memory/paging_root.hpp | 22 +-- .../include/x86_64/memory/scoped_mapping.hpp | 4 +- arch/x86_64/src/kapi/memory.cpp | 36 +++-- arch/x86_64/src/memory/kernel_mapper.cpp | 25 ++-- arch/x86_64/src/memory/paging_root.cpp | 149 +-------------------- arch/x86_64/src/memory/recursive_page_mapper.cpp | 85 +++++++++++- arch/x86_64/src/memory/scoped_mapping.cpp | 11 +- 9 files changed, 180 insertions(+), 212 deletions(-) (limited to 'arch/x86_64') diff --git a/arch/x86_64/include/x86_64/memory/kernel_mapper.hpp b/arch/x86_64/include/x86_64/memory/kernel_mapper.hpp index 1f217ae..5b9c2fd 100644 --- a/arch/x86_64/include/x86_64/memory/kernel_mapper.hpp +++ b/arch/x86_64/include/x86_64/memory/kernel_mapper.hpp @@ -1,6 +1,8 @@ #ifndef TEACHOS_X86_64_KERNEL_MAPPER_HPP #define TEACHOS_X86_64_KERNEL_MAPPER_HPP +#include "kapi/memory.hpp" + #include #include #include @@ -16,10 +18,10 @@ namespace teachos::memory::x86_64 explicit kernel_mapper(multiboot2::information_view const * mbi); - auto remap_kernel() -> void; + auto remap_kernel(page_mapper & mapper) -> void; private: - auto map_section(section_header_type const & section, std::string_view name) -> void; + auto map_section(section_header_type const & section, std::string_view name, page_mapper & mapper) -> void; multiboot2::information_view const * m_mbi; std::uintptr_t m_kernel_load_base; diff --git a/arch/x86_64/include/x86_64/memory/page_table.hpp b/arch/x86_64/include/x86_64/memory/page_table.hpp index 3bc2a2b..71ba5b7 100644 --- a/arch/x86_64/include/x86_64/memory/page_table.hpp +++ b/arch/x86_64/include/x86_64/memory/page_table.hpp @@ -3,6 +3,8 @@ #include "kapi/memory.hpp" +#include "x86_64/memory/page_utilities.hpp" + #include #include @@ -183,6 +185,58 @@ namespace teachos::memory::x86_64 }); } + [[nodiscard]] auto translate(linear_address address) const -> std::optional + requires(Level == 4) + { + auto offset = address.raw() % page::size; + return translate(page::containing(address)).transform([offset](auto frame) -> auto { + return physical_address{frame.start_address().raw() + offset}; + }); + } + + [[nodiscard]] auto translate(page page) const -> std::optional + requires(Level == 4) + { + auto pml3 = next(pml_index<4>(page)); + + if (!pml3) + { + return std::nullopt; + } + + auto handle_huge_page = [&] -> std::optional { + auto pml3_entry = pml3.transform([&](auto pml3) -> auto { return (*pml3)[pml_index<3>(page)]; }); + if (!pml3_entry) + { + return std::nullopt; + } + else if (pml3_entry->huge()) + { + auto pml3_entry_frame = *pml3_entry->frame(); + return frame{pml3_entry_frame.number() + pml_index<2>(page) * entry_count + pml_index<1>(page)}; + } + + auto pml2 = (*pml3)->next(pml_index<3>(page)); + auto pml2_entry = pml2.transform([&](auto pml2) -> auto { return (*pml2)[pml_index<2>(page)]; }); + if (!pml2_entry) + { + return std::nullopt; + } + else if (pml2_entry->huge()) + { + auto pml2_entry_frame = *pml2_entry->frame(); + return frame{pml2_entry_frame.number() + pml_index<1>(page)}; + } + + return std::nullopt; + }; + + return pml3.and_then([&](auto pml3) -> auto { return pml3->next(pml_index<3>(page)); }) + .and_then([&](auto pml2) -> auto { return pml2->next(pml_index<2>(page)); }) + .and_then([&](auto pml1) -> auto { return (*pml1)[pml_index<1>(page)].frame(); }) + .or_else(handle_huge_page); + } + private: //! The number of address bits used to represent the page index per level. constexpr auto static level_bits = 9; diff --git a/arch/x86_64/include/x86_64/memory/paging_root.hpp b/arch/x86_64/include/x86_64/memory/paging_root.hpp index 75ba120..47ee2f9 100644 --- a/arch/x86_64/include/x86_64/memory/paging_root.hpp +++ b/arch/x86_64/include/x86_64/memory/paging_root.hpp @@ -1,19 +1,15 @@ #ifndef TEACHOS_X86_64_PAGING_ROOT_HPP #define TEACHOS_X86_64_PAGING_ROOT_HPP -#include "kapi/memory.hpp" - #include "x86_64/memory/page_table.hpp" -#include - namespace teachos::memory::x86_64 { //! The active, recursively mapped, root map (e.g. PML4) struct paging_root : recursive_page_table<4> { - auto static get() -> paging_root &; + auto static get() -> paging_root *; paging_root(paging_root const &) = delete; paging_root(paging_root &&) = delete; @@ -22,22 +18,6 @@ namespace teachos::memory::x86_64 ~paging_root() = delete; - [[nodiscard]] auto translate(linear_address address) const -> std::optional; - [[nodiscard]] auto translate(page page) const -> std::optional; - - //! Map the given page into the given frame using the given flags. - //! - //! @param page A page to map. - //! @param frame The frame into which to map the page. - //! @param flags The flags to apply to the mapping. - auto map(page page, frame frame, entry::flags flags) -> std::optional; - - //! Unmap the given page from virtual memory. - //! - //! @warning If the page has not previously been mapped, this function will panic. - //! @param page The page to unmap - auto unmap(page page) -> void; - private: paging_root() = default; }; diff --git a/arch/x86_64/include/x86_64/memory/scoped_mapping.hpp b/arch/x86_64/include/x86_64/memory/scoped_mapping.hpp index 415ea8e..835e2df 100644 --- a/arch/x86_64/include/x86_64/memory/scoped_mapping.hpp +++ b/arch/x86_64/include/x86_64/memory/scoped_mapping.hpp @@ -22,7 +22,8 @@ namespace teachos::memory::x86_64 //! Construct a new scoped mapping, which can be used to map a frame to the given unused page. //! @param page An unused page. If the page is already mapped, this constructor will panic. - explicit scoped_mapping(page page); + //! @param mapper The page mapper to use for mapping and unmapping of the page. + explicit scoped_mapping(page page, page_mapper & mapper); //! Unmap the mapped frame if one was mapped. //! @note Any page tables that were allocated to support the mapping will be released. @@ -56,6 +57,7 @@ namespace teachos::memory::x86_64 private: page m_page; + page_mapper * m_mapper; bool m_mapped; }; diff --git a/arch/x86_64/src/kapi/memory.cpp b/arch/x86_64/src/kapi/memory.cpp index abc0526..ae0401e 100644 --- a/arch/x86_64/src/kapi/memory.cpp +++ b/arch/x86_64/src/kapi/memory.cpp @@ -67,25 +67,25 @@ namespace teachos::memory } //! Inject, or graft, a faux recursive PML4 into the active page mapping structure. - auto inject_faux_pml4(frame_allocator & allocator) + auto inject_faux_pml4(frame_allocator & allocator, page_mapper & mapper) { using namespace x86_64; using entry_flags = page_table::entry::flags; auto page = page::containing(unused_page_address); - auto temporary_mapper = scoped_mapping{page}; + auto temporary_mapper = scoped_mapping{page, mapper}; auto new_pml4_frame = allocator.allocate(); auto pml4 = std::construct_at(temporary_mapper.map_as(*new_pml4_frame, entry_flags::writable)); (*pml4)[recursive_page_map_index].frame(new_pml4_frame.value(), entry_flags::present | entry_flags::writable); auto pml4_index = pml_index<4>(page); - auto & old_pml4 = paging_root::get(); - auto pml4_entry = old_pml4[pml4_index]; + auto old_pml4 = paging_root::get(); + auto pml4_entry = (*old_pml4)[pml4_index]; auto pml3_index = pml_index<3>(page); - auto old_pml3 = old_pml4.next(pml4_index); + auto old_pml3 = old_pml4->next(pml4_index); auto pml3_entry = (**old_pml3)[pml3_index]; auto pml2_index = pml_index<2>(page); @@ -96,15 +96,15 @@ namespace teachos::memory auto old_pml1 = (**old_pml2).next(pml2_index); auto pml1_entry = (**old_pml1)[pml1_index]; - paging_root::get()[recursive_page_map_index].frame(new_pml4_frame.value(), - entry_flags::present | entry_flags::writable); + (*paging_root::get())[recursive_page_map_index].frame(new_pml4_frame.value(), + entry_flags::present | entry_flags::writable); tlb_flush_all(); - auto & new_pml4 = paging_root::get(); - new_pml4[pml4_index] = pml4_entry; + auto new_pml4 = paging_root::get(); + (*new_pml4)[pml4_index] = pml4_entry; - auto new_pml3 = new_pml4.next(pml4_index); + auto new_pml3 = new_pml4->next(pml4_index); (**new_pml3)[pml3_index] = pml3_entry; auto new_pml2 = (**new_pml3).next(pml3_index); @@ -116,10 +116,10 @@ namespace teachos::memory return *new_pml4_frame; } - auto remap_kernel() -> void + auto remap_kernel(page_mapper & mapper) -> void { auto kernel_mapper = x86_64::kernel_mapper{boot::bootstrap_information.mbi}; - kernel_mapper.remap_kernel(); + kernel_mapper.remap_kernel(mapper); } auto remap_vga_text_mode_buffer(page_mapper & mapper) -> void @@ -188,16 +188,13 @@ namespace teachos::memory auto early_allocator = create_early_frame_allocator(); auto allocation_buffer = x86_64::buffered_allocator<4>{&early_allocator}; - allocator = &allocation_buffer; - auto recursive_mapper = x86_64::recursive_page_mapper{allocation_buffer}; - mapper = &recursive_mapper; - - auto new_pml4_frame = inject_faux_pml4(allocation_buffer); cio::println("[x86_64:MEM] Preparing new paging hierarchy."); - remap_kernel(); + auto new_pml4_frame = inject_faux_pml4(allocation_buffer, recursive_mapper); + + remap_kernel(recursive_mapper); remap_vga_text_mode_buffer(recursive_mapper); remap_multiboot_information(recursive_mapper); @@ -206,9 +203,6 @@ namespace teachos::memory auto cr3 = cpu::x86_64::cr3::read(); cr3.address(new_pml4_frame.start_address()); cpu::x86_64::cr3::write(cr3); - - mapper = nullptr; - allocator = nullptr; } } // namespace teachos::memory diff --git a/arch/x86_64/src/memory/kernel_mapper.cpp b/arch/x86_64/src/memory/kernel_mapper.cpp index f46b5b5..a28cf00 100644 --- a/arch/x86_64/src/memory/kernel_mapper.cpp +++ b/arch/x86_64/src/memory/kernel_mapper.cpp @@ -5,8 +5,6 @@ #include "kapi/system.hpp" #include "x86_64/boot/ld.hpp" -#include "x86_64/memory/page_table.hpp" -#include "x86_64/memory/paging_root.hpp" #include #include @@ -17,7 +15,7 @@ namespace teachos::memory::x86_64 { - inline namespace + namespace { using namespace std::string_view_literals; @@ -37,7 +35,7 @@ namespace teachos::memory::x86_64 , m_kernel_load_base{std::bit_cast(&boot::x86_64::TEACHOS_VMA)} {} - auto kernel_mapper::remap_kernel() -> void + auto kernel_mapper::remap_kernel(page_mapper & mapper) -> void { auto elf_information = m_mbi->maybe_elf_symbols(); if (!elf_information) @@ -60,10 +58,11 @@ namespace teachos::memory::x86_64 } std::ranges::for_each(allocated_sections, - [&](auto const & section) -> auto { map_section(section, sections.name(section)); }); + [&](auto const & section) -> auto { map_section(section, sections.name(section), mapper); }); } - auto kernel_mapper::map_section(section_header_type const & section, std::string_view name) -> void + auto kernel_mapper::map_section(section_header_type const & section, std::string_view name, page_mapper & mapper) + -> void { cio::print("[x86_64:MEM] mapping "); cio::println(name); @@ -76,30 +75,30 @@ namespace teachos::memory::x86_64 auto first_page = page::containing(linear_start_address); auto first_frame = frame::containing(physical_start_address); - auto page_flags = page_table::entry::flags::empty; + auto page_flags = page_mapper::flags::empty; if (section.writable()) { - page_flags |= page_table::entry::flags::writable; + page_flags |= page_mapper::flags::writable; } - if (!section.executable()) + if (section.executable()) { - page_flags |= page_table::entry::flags::no_execute; + page_flags |= page_mapper::flags::executable; } auto is_prefix_of_name = [=](auto prefix) -> bool { return name.starts_with(prefix); }; - if (std::ranges::any_of(user_accessible_prefixes, is_prefix_of_name)) + if (!std::ranges::any_of(user_accessible_prefixes, is_prefix_of_name)) { - page_flags |= page_table::entry::flags::user_accessible; + page_flags |= page_mapper::flags::supervisor_only; } for (auto i = 0uz; i < number_of_pages; ++i) { - paging_root::get().map(page{first_page.number() + i}, frame{first_frame.number() + i}, page_flags); + mapper.map(first_page + i, first_frame + i, page_flags); } } diff --git a/arch/x86_64/src/memory/paging_root.cpp b/arch/x86_64/src/memory/paging_root.cpp index 078686b..4f88657 100644 --- a/arch/x86_64/src/memory/paging_root.cpp +++ b/arch/x86_64/src/memory/paging_root.cpp @@ -1,161 +1,18 @@ #include "x86_64/memory/paging_root.hpp" -#include "kapi/memory.hpp" -#include "kapi/system.hpp" - -#include "x86_64/memory/page_table.hpp" -#include "x86_64/memory/page_utilities.hpp" -#include "x86_64/memory/scoped_mapping.hpp" - -#include #include -#include -#include namespace teachos::memory::x86_64 { namespace { - constexpr auto PML_RECURSIVE_BASE = std::uintptr_t{0177777'776'776'776'776'0000uz}; - - //! Perform the actual mapping of the page, via the recursive page map. - //! - //! On any level above PML1, the entries need to not be no_execute, because the image is densely packed. The entries - //! also need to be writable, since the mapping is being performed through the recursive page map hierarchy. When - //! setting the final entry in the PML1, the actually desired flags are set as is, with the present bit added, thus - //! still enforcing non-writability and non-execution of the affected page. - template - requires(Level > 1uz && Level < 5uz) - auto do_map(recursive_page_table * pml, page page, page_table::entry::flags flags) - { - auto index = pml_index(page); - flags = flags & ~page_table::entry::flags::no_execute; - flags = flags | page_table::entry::flags::writable; - if (!(*pml)[index].present()) - { - auto new_table_frame = active_frame_allocator().allocate(); - auto mapping = scoped_mapping{page}; - (*pml)[index].frame(new_table_frame.value(), page_table::entry::flags::present | flags); - auto new_table = std::optional{std::construct_at(*pml->next(index))}; - return new_table; - } - (*pml)[index] |= flags; - return pml->next(index); - } - - //! Perform the actual PML1 update. - auto do_map(page_table * pml, page page, frame frame, page_table::entry::flags flags) -> std::optional - { - auto index = pml_index<1>(page); - if ((*pml)[index].present()) - { - system::panic("[x86_64:MEM] Tried to map a page that is already mapped"); - } - (*pml)[index].frame(frame, page_table::entry::flags::present | flags); - return std::optional{static_cast(page.start_address())}; - } + constexpr auto recursive_base = std::uintptr_t{0177777'776'776'776'776'0000uz}; } // namespace - auto paging_root::get() -> paging_root & - { - auto pml4_address = std::bit_cast(PML_RECURSIVE_BASE); - return *pml4_address; - } - - auto paging_root::translate(linear_address address) const -> std::optional - { - auto offset = address.raw() % page::size; - return translate(page::containing(address)).transform([offset](auto frame) -> auto { - return physical_address{frame.start_address().raw() + offset}; - }); - } - - auto paging_root::translate(page page) const -> std::optional - { - auto pml3 = next(pml_index<4>(page)); - - if (!pml3) - { - return std::nullopt; - } - - auto handle_huge_page = [&] -> std::optional { - auto pml3_entry = pml3.transform([&](auto pml3) -> auto { return (*pml3)[pml_index<3>(page)]; }); - if (!pml3_entry) - { - return std::nullopt; - } - else if (pml3_entry->huge()) - { - auto pml3_entry_frame = *pml3_entry->frame(); - return frame{pml3_entry_frame.number() + pml_index<2>(page) * entry_count + pml_index<1>(page)}; - } - - auto pml2 = (*pml3)->next(pml_index<3>(page)); - auto pml2_entry = pml2.transform([&](auto pml2) -> auto { return (*pml2)[pml_index<2>(page)]; }); - if (!pml2_entry) - { - return std::nullopt; - } - else if (pml2_entry->huge()) - { - auto pml2_entry_frame = *pml2_entry->frame(); - return frame{pml2_entry_frame.number() + pml_index<1>(page)}; - } - - return std::nullopt; - }; - - return pml3.and_then([&](auto pml3) -> auto { return pml3->next(pml_index<3>(page)); }) - .and_then([&](auto pml2) -> auto { return pml2->next(pml_index<2>(page)); }) - .and_then([&](auto pml1) -> auto { return (*pml1)[pml_index<1>(page)].frame(); }) - .or_else(handle_huge_page); - } - - auto paging_root::map(page page, frame frame, page_table::entry::flags flags) -> std::optional + auto paging_root::get() -> paging_root * { - return std::optional{this} - .and_then([&](auto pml) -> auto { return do_map(pml, page, flags); }) - .and_then([&](auto pml) -> auto { return do_map(pml, page, flags); }) - .and_then([&](auto pml) -> auto { return do_map(pml, page, flags); }) - .and_then([&](auto pml) -> auto { return do_map(pml, page, frame, flags); }); - } - - auto paging_root::unmap(page page) -> void - { - if (!this->translate(page)) - { - system::panic("[x86_64:MEM] Tried to unmap a page that was not mapped."); - } - - auto pml4 = this; - auto pml3 = pml4->next(pml_index<4>(page)).value(); - auto pml2 = pml3->next(pml_index<3>(page)).value(); - auto pml1 = pml2->next(pml_index<2>(page)).value(); - - (*pml1)[pml_index<1>(page)].clear(); - - if (pml1->empty()) - { - auto pml1_frame = (*pml2)[pml_index<2>(page)].frame().value(); - active_frame_allocator().release(pml1_frame); - (*pml2)[pml_index<2>(page)].clear(); - } - - if (pml2->empty()) - { - auto pml2_frame = (*pml3)[pml_index<3>(page)].frame().value(); - active_frame_allocator().release(pml2_frame); - (*pml3)[pml_index<3>(page)].clear(); - } - - if (pml3->empty()) - { - auto pml3_frame = (*pml4)[pml_index<4>(page)].frame().value(); - active_frame_allocator().release(pml3_frame); - (*pml4)[pml_index<4>(page)].clear(); - } + return std::bit_cast(recursive_base); } } // namespace teachos::memory::x86_64 \ No newline at end of file diff --git a/arch/x86_64/src/memory/recursive_page_mapper.cpp b/arch/x86_64/src/memory/recursive_page_mapper.cpp index 47148f0..fe4fd50 100644 --- a/arch/x86_64/src/memory/recursive_page_mapper.cpp +++ b/arch/x86_64/src/memory/recursive_page_mapper.cpp @@ -7,13 +7,63 @@ namespace teachos::memory::x86_64 { + namespace + { + //! Perform the actual mapping of the page, via the recursive page map. + //! + //! On any level above PML1, the entries need to not be no_execute, because the image is densely packed. The entries + //! also need to be writable, since the mapping is being performed through the recursive page map hierarchy. When + //! setting the final entry in the PML1, the actually desired flags are set as is, with the present bit + //! added, thus + //! still enforcing non-writability and non-execution of the affected page. + template + requires(Level > 1uz && Level < 5uz) + auto do_map(recursive_page_table * pml, page page, frame_allocator & allocator, page_mapper::flags flags) + { + auto index = pml_index(page); + auto entry_flags = to_table_flags(flags); + + entry_flags = entry_flags & ~page_table::entry::flags::no_execute; + entry_flags = entry_flags | page_table::entry::flags::writable; + if (!(*pml)[index].present()) + { + auto new_table_frame = allocator.allocate(); + (*pml)[index].frame(new_table_frame.value(), page_table::entry::flags::present | entry_flags); + auto new_table = std::optional{std::construct_at(*pml->next(index))}; + return new_table; + } + (*pml)[index] |= entry_flags; + return pml->next(index); + } + + //! Perform the actual PML1 update. + auto do_map(page_table * pml, page page, frame frame, page_mapper::flags flags) -> std::optional + { + auto index = pml_index<1>(page); + if ((*pml)[index].present()) + { + system::panic("[x86_64:MEM] Tried to map a page that is already mapped"); + } + (*pml)[index].frame(frame, page_table::entry::flags::present | to_table_flags(flags)); + return std::optional{static_cast(page.start_address())}; + } + + } // namespace + recursive_page_mapper::recursive_page_mapper(frame_allocator & allocator) : m_allocator{&allocator} {} auto recursive_page_mapper::map(page page, frame frame, flags flags) -> std::byte * { - return paging_root::get().map(page, frame, to_table_flags(flags)).value_or(nullptr); + auto pml4 = static_cast *>((paging_root::get())); + + return std::optional{pml4} + .and_then([&](auto pml) -> auto { return do_map(pml, page, *m_allocator, flags); }) + .and_then([&](auto pml) -> auto { return do_map(pml, page, *m_allocator, flags); }) + .and_then([&](auto pml) -> auto { return do_map(pml, page, *m_allocator, flags); }) + .and_then([&](auto pml) -> auto { return do_map(pml, page, frame, flags); }) + .value_or(nullptr); } auto recursive_page_mapper::unmap(page page) -> void @@ -26,12 +76,39 @@ namespace teachos::memory::x86_64 auto recursive_page_mapper::try_unmap(page page) noexcept -> bool { - auto & root = paging_root::get(); - if (!root.translate(page)) + if (!paging_root::get()->translate(page)) { return false; } - root.unmap(page); + + auto pml4 = paging_root::get(); + auto pml3 = pml4->next(pml_index<4>(page)).value(); + auto pml2 = pml3->next(pml_index<3>(page)).value(); + auto pml1 = pml2->next(pml_index<2>(page)).value(); + + (*pml1)[pml_index<1>(page)].clear(); + + if (pml1->empty()) + { + auto pml1_frame = (*pml2)[pml_index<2>(page)].frame().value(); + m_allocator->release(pml1_frame); + (*pml2)[pml_index<2>(page)].clear(); + } + + if (pml2->empty()) + { + auto pml2_frame = (*pml3)[pml_index<3>(page)].frame().value(); + m_allocator->release(pml2_frame); + (*pml3)[pml_index<3>(page)].clear(); + } + + if (pml3->empty()) + { + auto pml3_frame = (*pml4)[pml_index<4>(page)].frame().value(); + m_allocator->release(pml3_frame); + (*pml4)[pml_index<4>(page)].clear(); + } + return true; } diff --git a/arch/x86_64/src/memory/scoped_mapping.cpp b/arch/x86_64/src/memory/scoped_mapping.cpp index e243dc9..fa68387 100644 --- a/arch/x86_64/src/memory/scoped_mapping.cpp +++ b/arch/x86_64/src/memory/scoped_mapping.cpp @@ -14,14 +14,16 @@ namespace teachos::memory::x86_64 scoped_mapping::scoped_mapping(scoped_mapping && other) noexcept : m_page{std::exchange(other.m_page, page{})} + , m_mapper{std::exchange(other.m_mapper, nullptr)} , m_mapped{std::exchange(other.m_mapped, false)} {} - scoped_mapping::scoped_mapping(page page) + scoped_mapping::scoped_mapping(page page, page_mapper & mapper) : m_page{page} + , m_mapper{&mapper} , m_mapped{false} { - if (paging_root::get().translate(page)) + if (paging_root::get()->translate(page)) { system::panic("[MEM] Tried to map a page that is already mapped!"); } @@ -44,14 +46,14 @@ namespace teachos::memory::x86_64 auto scoped_mapping::map(frame frame, page_table::entry::flags flags) -> std::byte * { - auto result = active_page_mapper().map(m_page, frame, to_mapper_flags(flags)); + auto result = m_mapper->map(m_page, frame, to_mapper_flags(flags)); m_mapped = true; return result; } auto scoped_mapping::unmap() -> void { - active_page_mapper().unmap(m_page); + m_mapper->unmap(m_page); m_mapped = false; } @@ -59,6 +61,7 @@ namespace teachos::memory::x86_64 { using std::swap; swap(lhs.m_page, rhs.m_page); + swap(lhs.m_mapper, rhs.m_mapper); swap(lhs.m_mapped, rhs.m_mapped); } -- 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') 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 From 5f695cd3519d8a09a53485c08971f49ed92969ff Mon Sep 17 00:00:00 2001 From: Felix Morgner Date: Fri, 12 Dec 2025 14:11:25 +0100 Subject: x86_64/memory: silence linter warning --- arch/x86_64/CMakeLists.txt | 1 + arch/x86_64/src/memory/recursive_page_mapper.cpp | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) (limited to 'arch/x86_64') diff --git a/arch/x86_64/CMakeLists.txt b/arch/x86_64/CMakeLists.txt index 7bd8d07..2a6c878 100644 --- a/arch/x86_64/CMakeLists.txt +++ b/arch/x86_64/CMakeLists.txt @@ -45,6 +45,7 @@ target_sources("x86_64" PUBLIC target_compile_definitions("x86_64" PUBLIC "PLATFORM_PAGE_SIZE=4096" + "PLATFORM_PAGING_LEVELS=4uz" "PLATFORM_FRAME_SIZE=PLATFORM_PAGE_SIZE" ) diff --git a/arch/x86_64/src/memory/recursive_page_mapper.cpp b/arch/x86_64/src/memory/recursive_page_mapper.cpp index fe4fd50..798233a 100644 --- a/arch/x86_64/src/memory/recursive_page_mapper.cpp +++ b/arch/x86_64/src/memory/recursive_page_mapper.cpp @@ -17,7 +17,7 @@ namespace teachos::memory::x86_64 //! added, thus //! still enforcing non-writability and non-execution of the affected page. template - requires(Level > 1uz && Level < 5uz) + requires(Level > 1uz && Level <= PLATFORM_PAGING_LEVELS) auto do_map(recursive_page_table * pml, page page, frame_allocator & allocator, page_mapper::flags flags) { auto index = pml_index(page); -- cgit v1.2.3 From 2846867da7e88c3a665d0a8bed6ccec60e6476a1 Mon Sep 17 00:00:00 2001 From: Felix Morgner Date: Fri, 12 Dec 2025 17:41:26 +0100 Subject: build: move platform configuration to preset --- arch/x86_64/CMakeLists.txt | 6 ------ 1 file changed, 6 deletions(-) (limited to 'arch/x86_64') diff --git a/arch/x86_64/CMakeLists.txt b/arch/x86_64/CMakeLists.txt index 2a6c878..54f04cb 100644 --- a/arch/x86_64/CMakeLists.txt +++ b/arch/x86_64/CMakeLists.txt @@ -43,12 +43,6 @@ target_sources("x86_64" PUBLIC FILES ${ARCH_HEADERS} ) -target_compile_definitions("x86_64" PUBLIC - "PLATFORM_PAGE_SIZE=4096" - "PLATFORM_PAGING_LEVELS=4uz" - "PLATFORM_FRAME_SIZE=PLATFORM_PAGE_SIZE" -) - set(KERNEL_LINKER_SCRIPT "${CMAKE_CURRENT_SOURCE_DIR}/scripts/kernel.ld" PARENT_SCOPE -- cgit v1.2.3 From 40804526a58ddf2cc0df0750550c8dcfa7b7c57c Mon Sep 17 00:00:00 2001 From: Felix Morgner Date: Mon, 15 Dec 2025 11:34:30 +0100 Subject: x86_64/boot: use high-mem address of MBI --- arch/x86_64/include/x86_64/boot/boot.hpp | 3 +++ arch/x86_64/src/boot/entry64.s | 1 + arch/x86_64/src/kapi/memory.cpp | 6 ++---- arch/x86_64/src/memory/recursive_page_mapper.cpp | 3 +-- 4 files changed, 7 insertions(+), 6 deletions(-) (limited to 'arch/x86_64') diff --git a/arch/x86_64/include/x86_64/boot/boot.hpp b/arch/x86_64/include/x86_64/boot/boot.hpp index 6dcd2de..2c44659 100644 --- a/arch/x86_64/include/x86_64/boot/boot.hpp +++ b/arch/x86_64/include/x86_64/boot/boot.hpp @@ -56,7 +56,10 @@ namespace teachos::boot struct information { + //! A pointer to the loader provided Multiboot2 Information structure. multiboot2::information_view const * mbi; + + //! The index of the next character to be written in the VGA text buffer after handoff. std::size_t vga_buffer_index; }; diff --git a/arch/x86_64/src/boot/entry64.s b/arch/x86_64/src/boot/entry64.s index 2932354..657b0a8 100644 --- a/arch/x86_64/src/boot/entry64.s +++ b/arch/x86_64/src/boot/entry64.s @@ -33,6 +33,7 @@ _entry64: mov %rsp, %rbp mov multiboot_information_pointer, %rax + or $TEACHOS_VMA, %rax mov vga_buffer_pointer, %rdx sub $0xb8000, %rdx mov %rax, (bootstrap_information) diff --git a/arch/x86_64/src/kapi/memory.cpp b/arch/x86_64/src/kapi/memory.cpp index ae0401e..748893a 100644 --- a/arch/x86_64/src/kapi/memory.cpp +++ b/arch/x86_64/src/kapi/memory.cpp @@ -138,8 +138,8 @@ namespace teachos::memory { auto mbi_base = std::bit_cast(boot::bootstrap_information.mbi); auto mbi_size = boot::bootstrap_information.mbi->size_bytes(); - auto mbi_physical_start = physical_address{mbi_base}; - auto mbi_virtual_start = linear_address{mbi_base + std::bit_cast(&boot::x86_64::TEACHOS_VMA)}; + auto mbi_physical_start = physical_address{mbi_base & ~std::bit_cast(&boot::x86_64::TEACHOS_VMA)}; + auto mbi_virtual_start = linear_address{mbi_base}; auto mbi_block_count = (mbi_size + PLATFORM_FRAME_SIZE - 1) / PLATFORM_FRAME_SIZE; for (auto i = 0uz; i < mbi_block_count; ++i) @@ -148,8 +148,6 @@ namespace teachos::memory auto frame = frame::containing(mbi_physical_start) + 1; mapper.map(page, frame, page_mapper::flags::empty); } - - boot::bootstrap_information.mbi = std::bit_cast(mbi_virtual_start.raw()); } } // namespace diff --git a/arch/x86_64/src/memory/recursive_page_mapper.cpp b/arch/x86_64/src/memory/recursive_page_mapper.cpp index 798233a..ef234ad 100644 --- a/arch/x86_64/src/memory/recursive_page_mapper.cpp +++ b/arch/x86_64/src/memory/recursive_page_mapper.cpp @@ -14,8 +14,7 @@ namespace teachos::memory::x86_64 //! On any level above PML1, the entries need to not be no_execute, because the image is densely packed. The entries //! also need to be writable, since the mapping is being performed through the recursive page map hierarchy. When //! setting the final entry in the PML1, the actually desired flags are set as is, with the present bit - //! added, thus - //! still enforcing non-writability and non-execution of the affected page. + //! added, thus still enforcing non-writability and non-execution of the affected page. template requires(Level > 1uz && Level <= PLATFORM_PAGING_LEVELS) auto do_map(recursive_page_table * pml, page page, frame_allocator & allocator, page_mapper::flags flags) -- cgit v1.2.3 From 43ddde5e30a0d71aa11025a5ae232cea83e7fbde Mon Sep 17 00:00:00 2001 From: Felix Morgner Date: Mon, 15 Dec 2025 16:28:16 +0100 Subject: kapi: remodel memory API to follow cio API --- arch/x86_64/src/kapi/memory.cpp | 27 +++------------------------ 1 file changed, 3 insertions(+), 24 deletions(-) (limited to 'arch/x86_64') diff --git a/arch/x86_64/src/kapi/memory.cpp b/arch/x86_64/src/kapi/memory.cpp index 748893a..abfb32e 100644 --- a/arch/x86_64/src/kapi/memory.cpp +++ b/arch/x86_64/src/kapi/memory.cpp @@ -21,19 +21,17 @@ #include #include +#include +#include #include #include +#include namespace teachos::memory { namespace { - // NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables) - auto constinit allocator = static_cast(nullptr); - // NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables) - auto constinit mapper = static_cast(nullptr); - constexpr auto static unused_page_address = linear_address{0x0000'7fff'cafe'faceuz}; constexpr auto static recursive_page_map_index = x86_64::page_table::entry_count - 2; @@ -152,25 +150,6 @@ namespace teachos::memory } // namespace - auto active_frame_allocator() -> frame_allocator & - { - if (!allocator) - { - system::panic("[x86_64] The frame allocator has not been set yet."); - } - - return *allocator; - } - - auto active_page_mapper() -> page_mapper & - { - if (!mapper) - { - system::panic("[x86_64] The page mapper has not been set you."); - } - return *mapper; - } - auto init() -> void { auto static constinit is_initialized = std::atomic_flag{}; -- cgit v1.2.3 From 1945dd16716392e70e74efacd19e779f121bd1da Mon Sep 17 00:00:00 2001 From: Felix Morgner Date: Mon, 15 Dec 2025 16:46:51 +0100 Subject: chore: fix missing includes --- arch/x86_64/src/kapi/cio.cpp | 2 ++ arch/x86_64/src/memory/kernel_mapper.cpp | 6 ++++++ arch/x86_64/src/memory/mmu.cpp | 2 ++ arch/x86_64/src/memory/page_table.cpp | 6 ++++++ arch/x86_64/src/memory/paging_root.cpp | 1 + arch/x86_64/src/memory/recursive_page_mapper.cpp | 5 +++++ arch/x86_64/src/memory/region_allocator.cpp | 1 + arch/x86_64/src/memory/scoped_mapping.cpp | 1 + arch/x86_64/src/vga/text.cpp | 1 + 9 files changed, 25 insertions(+) (limited to 'arch/x86_64') diff --git a/arch/x86_64/src/kapi/cio.cpp b/arch/x86_64/src/kapi/cio.cpp index 3f169a1..ade02aa 100644 --- a/arch/x86_64/src/kapi/cio.cpp +++ b/arch/x86_64/src/kapi/cio.cpp @@ -2,6 +2,8 @@ #include "x86_64/vga/text.hpp" +#include + namespace teachos::cio { diff --git a/arch/x86_64/src/memory/kernel_mapper.cpp b/arch/x86_64/src/memory/kernel_mapper.cpp index a28cf00..4781d64 100644 --- a/arch/x86_64/src/memory/kernel_mapper.cpp +++ b/arch/x86_64/src/memory/kernel_mapper.cpp @@ -8,9 +8,15 @@ #include #include +#include #include +#include +#include +#include #include +#include +#include namespace teachos::memory::x86_64 { diff --git a/arch/x86_64/src/memory/mmu.cpp b/arch/x86_64/src/memory/mmu.cpp index 8ec8a2e..e15d94e 100644 --- a/arch/x86_64/src/memory/mmu.cpp +++ b/arch/x86_64/src/memory/mmu.cpp @@ -1,5 +1,7 @@ #include "x86_64/memory/mmu.hpp" +#include "kapi/memory.hpp" + #include "x86_64/cpu/registers.hpp" namespace teachos::memory::x86_64 diff --git a/arch/x86_64/src/memory/page_table.cpp b/arch/x86_64/src/memory/page_table.cpp index 0e404e5..2de099d 100644 --- a/arch/x86_64/src/memory/page_table.cpp +++ b/arch/x86_64/src/memory/page_table.cpp @@ -1,6 +1,12 @@ #include "x86_64/memory/page_table.hpp" +#include "kapi/memory.hpp" + #include +#include +#include +#include +#include #include namespace teachos::memory::x86_64 diff --git a/arch/x86_64/src/memory/paging_root.cpp b/arch/x86_64/src/memory/paging_root.cpp index 4f88657..d849a82 100644 --- a/arch/x86_64/src/memory/paging_root.cpp +++ b/arch/x86_64/src/memory/paging_root.cpp @@ -1,5 +1,6 @@ #include "x86_64/memory/paging_root.hpp" +#include #include namespace teachos::memory::x86_64 diff --git a/arch/x86_64/src/memory/recursive_page_mapper.cpp b/arch/x86_64/src/memory/recursive_page_mapper.cpp index ef234ad..c5aabcb 100644 --- a/arch/x86_64/src/memory/recursive_page_mapper.cpp +++ b/arch/x86_64/src/memory/recursive_page_mapper.cpp @@ -1,10 +1,15 @@ #include "x86_64/memory/recursive_page_mapper.hpp" +#include "kapi/memory.hpp" #include "kapi/system.hpp" #include "x86_64/memory/page_table.hpp" +#include "x86_64/memory/page_utilities.hpp" #include "x86_64/memory/paging_root.hpp" +#include +#include + namespace teachos::memory::x86_64 { namespace diff --git a/arch/x86_64/src/memory/region_allocator.cpp b/arch/x86_64/src/memory/region_allocator.cpp index e477ec0..dbe14cd 100644 --- a/arch/x86_64/src/memory/region_allocator.cpp +++ b/arch/x86_64/src/memory/region_allocator.cpp @@ -6,6 +6,7 @@ #include #include +#include #include namespace teachos::memory::x86_64 diff --git a/arch/x86_64/src/memory/scoped_mapping.cpp b/arch/x86_64/src/memory/scoped_mapping.cpp index fa68387..a86aaed 100644 --- a/arch/x86_64/src/memory/scoped_mapping.cpp +++ b/arch/x86_64/src/memory/scoped_mapping.cpp @@ -7,6 +7,7 @@ #include "x86_64/memory/page_table.hpp" #include "x86_64/memory/paging_root.hpp" +#include #include namespace teachos::memory::x86_64 diff --git a/arch/x86_64/src/vga/text.cpp b/arch/x86_64/src/vga/text.cpp index 8b7f01b..c9eee71 100644 --- a/arch/x86_64/src/vga/text.cpp +++ b/arch/x86_64/src/vga/text.cpp @@ -10,6 +10,7 @@ #include #include #include +#include #include #include -- cgit v1.2.3 From a9eeec745e29d89afd48ee43d09432eb6fc35be7 Mon Sep 17 00:00:00 2001 From: Felix Morgner Date: Mon, 15 Dec 2025 17:04:15 +0100 Subject: kapi/memory: initialize memory subsystem --- .../include/x86_64/memory/buffered_allocator.hpp | 3 +- arch/x86_64/src/kapi/memory.cpp | 36 +++++++++++++--------- arch/x86_64/src/memory/region_allocator.cpp | 1 + 3 files changed, 25 insertions(+), 15 deletions(-) (limited to 'arch/x86_64') diff --git a/arch/x86_64/include/x86_64/memory/buffered_allocator.hpp b/arch/x86_64/include/x86_64/memory/buffered_allocator.hpp index d7e9491..90ac878 100644 --- a/arch/x86_64/include/x86_64/memory/buffered_allocator.hpp +++ b/arch/x86_64/include/x86_64/memory/buffered_allocator.hpp @@ -6,6 +6,7 @@ #include #include #include +#include namespace teachos::memory::x86_64 { @@ -20,7 +21,7 @@ namespace teachos::memory::x86_64 } buffered_allocator(buffered_allocator const &) = delete; - buffered_allocator(buffered_allocator &&) = default; + buffered_allocator(buffered_allocator && other) noexcept = delete; ~buffered_allocator() override { diff --git a/arch/x86_64/src/kapi/memory.cpp b/arch/x86_64/src/kapi/memory.cpp index abfb32e..8c53c4c 100644 --- a/arch/x86_64/src/kapi/memory.cpp +++ b/arch/x86_64/src/kapi/memory.cpp @@ -24,6 +24,7 @@ #include #include #include +#include #include #include @@ -36,7 +37,7 @@ namespace teachos::memory constexpr auto static recursive_page_map_index = x86_64::page_table::entry_count - 2; //! Instantiate a basic, memory region based, early frame allocator for remapping. - auto create_early_frame_allocator() + auto collect_memory_information() { auto memory_map = boot::bootstrap_information.mbi->maybe_memory_map(); if (!memory_map) @@ -48,12 +49,10 @@ namespace teachos::memory auto mbi_span = std::span{std::bit_cast(mbi), mbi->size_bytes()}; auto image_span = std::span{&boot::x86_64::_start_physical, &boot::x86_64::_end_physical}; - return x86_64::region_allocator{ - { - .image_range = std::make_pair(physical_address{&image_span.front()}, physical_address{&image_span.back()}), - .mbi_range = std::make_pair(physical_address{&mbi_span.front()}, physical_address{&mbi_span.back()}), - .memory_map = *memory_map, - } + return x86_64::region_allocator::memory_information{ + .image_range = std::make_pair(physical_address{&image_span.front()}, physical_address{&image_span.back()}), + .mbi_range = std::make_pair(physical_address{&mbi_span.front()}, physical_address{&mbi_span.back()}), + .memory_map = *memory_map, }; } @@ -150,6 +149,12 @@ namespace teachos::memory } // namespace + // NOLINTBEGIN(cppcoreguidelines-avoid-non-const-global-variables) + auto constinit region_based_allocator = std::optional{}; + auto constinit buffered_allocator = std::optional>{}; + auto constinit recursive_page_mapper = std::optional{}; + // NOLINTEND(cppcoreguidelines-avoid-non-const-global-variables) + auto init() -> void { auto static constinit is_initialized = std::atomic_flag{}; @@ -163,23 +168,26 @@ namespace teachos::memory enable_cpu_protections(); - auto early_allocator = create_early_frame_allocator(); - auto allocation_buffer = x86_64::buffered_allocator<4>{&early_allocator}; - auto recursive_mapper = x86_64::recursive_page_mapper{allocation_buffer}; + region_based_allocator.emplace(collect_memory_information()); + buffered_allocator.emplace(&*region_based_allocator); + recursive_page_mapper.emplace(*buffered_allocator); cio::println("[x86_64:MEM] Preparing new paging hierarchy."); - auto new_pml4_frame = inject_faux_pml4(allocation_buffer, recursive_mapper); + auto new_pml4_frame = inject_faux_pml4(*buffered_allocator, *recursive_page_mapper); - remap_kernel(recursive_mapper); - remap_vga_text_mode_buffer(recursive_mapper); - remap_multiboot_information(recursive_mapper); + remap_kernel(*recursive_page_mapper); + remap_vga_text_mode_buffer(*recursive_page_mapper); + remap_multiboot_information(*recursive_page_mapper); cio::println("[x86_64:MEM] Switching to new paging hierarchy."); auto cr3 = cpu::x86_64::cr3::read(); cr3.address(new_pml4_frame.start_address()); cpu::x86_64::cr3::write(cr3); + + set_frame_allocator(*buffered_allocator); + set_page_mapper(*recursive_page_mapper); } } // namespace teachos::memory diff --git a/arch/x86_64/src/memory/region_allocator.cpp b/arch/x86_64/src/memory/region_allocator.cpp index dbe14cd..0f65b3a 100644 --- a/arch/x86_64/src/memory/region_allocator.cpp +++ b/arch/x86_64/src/memory/region_allocator.cpp @@ -8,6 +8,7 @@ #include #include #include +#include namespace teachos::memory::x86_64 { -- cgit v1.2.3