diff options
| -rw-r--r-- | arch/x86_64/CMakeLists.txt | 2 | ||||
| -rw-r--r-- | arch/x86_64/include/arch/memory/cpu/control_register.hpp | 70 | ||||
| -rw-r--r-- | arch/x86_64/include/arch/memory/cpu/cr3.hpp | 27 | ||||
| -rw-r--r-- | arch/x86_64/include/arch/memory/cpu/msr.hpp | 47 | ||||
| -rw-r--r-- | arch/x86_64/include/arch/memory/paging/kernel_mapper.hpp | 10 | ||||
| -rw-r--r-- | arch/x86_64/src/memory/cpu/control_register.cpp | 66 | ||||
| -rw-r--r-- | arch/x86_64/src/memory/cpu/cr3.cpp | 23 | ||||
| -rw-r--r-- | arch/x86_64/src/memory/cpu/msr.cpp | 18 | ||||
| -rw-r--r-- | arch/x86_64/src/memory/cpu/tlb.cpp | 7 |
9 files changed, 185 insertions, 85 deletions
diff --git a/arch/x86_64/CMakeLists.txt b/arch/x86_64/CMakeLists.txt index 9fa8181..ee0141e 100644 --- a/arch/x86_64/CMakeLists.txt +++ b/arch/x86_64/CMakeLists.txt @@ -53,7 +53,7 @@ target_sources("_memory" PRIVATE "src/memory/paging/active_page_table.cpp" "src/memory/paging/inactive_page_table.cpp" "src/memory/cpu/tlb.cpp" - "src/memory/cpu/cr3.cpp" + "src/memory/cpu/control_register.cpp" ) #[============================================================================[ diff --git a/arch/x86_64/include/arch/memory/cpu/control_register.hpp b/arch/x86_64/include/arch/memory/cpu/control_register.hpp new file mode 100644 index 0000000..b48e48c --- /dev/null +++ b/arch/x86_64/include/arch/memory/cpu/control_register.hpp @@ -0,0 +1,70 @@ +#ifndef TEACHOS_ARCH_X86_64_MEMORY_CPU_CR3_HPP +#define TEACHOS_ARCH_X86_64_MEMORY_CPU_CR3_HPP + +#include <cstdint> + +namespace teachos::arch::memory::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 2 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 cr2_flags : uint32_t + { + PROTECTED_MODE_ENABLED = 1U << 0U, ///< System is in protected moe else 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. + * + * @note The cr3 register value 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. + * + * @return Physical address the level 4 page table is located at. + */ + + /** + * @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) -> std::size_t; + + /** + * @brief Writes the given value into the given control 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, std::size_t new_value) -> void; +} // namespace teachos::arch::memory::cpu + +#endif // TEACHOS_ARCH_X86_64_MEMORY_CPU_CR3_HPP diff --git a/arch/x86_64/include/arch/memory/cpu/cr3.hpp b/arch/x86_64/include/arch/memory/cpu/cr3.hpp deleted file mode 100644 index 51d5055..0000000 --- a/arch/x86_64/include/arch/memory/cpu/cr3.hpp +++ /dev/null @@ -1,27 +0,0 @@ -#ifndef TEACHOS_ARCH_X86_64_MEMORY_CPU_CR3_HPP -#define TEACHOS_ARCH_X86_64_MEMORY_CPU_CR3_HPP - -#include "arch/memory/allocator/physical_frame.hpp" - -namespace teachos::arch::memory::cpu -{ - /** - * @brief Reads the value of the cr3 register. - * - * @note The cr3 register value 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. - * - * @return Physical address the level 4 page table is located at. - */ - auto read_cr3_register() -> allocator::physical_address; - - /** - * @brief Writes the given value into the cr3 register. - * - * @param new_p4_table_address Physical address the newly kernel mapped level 4 page table is located at. - */ - auto write_cr3_register(allocator::physical_address new_p4_table_address) -> void; -} // namespace teachos::arch::memory::cpu - -#endif // TEACHOS_ARCH_X86_64_MEMORY_CPU_CR3_HPP diff --git a/arch/x86_64/include/arch/memory/cpu/msr.hpp b/arch/x86_64/include/arch/memory/cpu/msr.hpp index 662f3ac..1403995 100644 --- a/arch/x86_64/include/arch/memory/cpu/msr.hpp +++ b/arch/x86_64/include/arch/memory/cpu/msr.hpp @@ -6,63 +6,68 @@ namespace teachos::arch::memory::cpu { - constexpr uint32_t IA32_EFER = 0xC0000080; - - enum class msr_flags : uint64_t + /** + * @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 = 1U << 0ULL, ///< System Call Extensions - LME = 1U << 8ULL, ///< Long Mode Enabled - LMA = 1U << 10ULL, ///< Long Mode Active - NXE = 1U << 11ULL, ///< No-Execute Enable - SVME = 1U << 12ULL, ///< Secure Virtual Machine Enable - LMSLE = 1U << 13ULL, ///< Secure Virtual Machine Enable - FFXSR = 1U << 14ULL, ///< fast FXSAVE/FXSTOR - TCE = 1U << 15ULL, ///< Translation Cache Extension + 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 Model-Specific Register (MSR). * - * This function reads the value of an MSR specified by the given address. It + * @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 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. */ - uint64_t read_msr(uint32_t msr); + auto read_msr(uint32_t msr) -> void; /** * @brief Writes a 64-bit value to a Model-Specific Register (MSR). * - * This function writes a 64-bit value to the MSR specified by the given address. + * @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 value The 64-bit value to write to the MSR. + * @param new_value The 64-bit value to write to the MSR. */ - void write_msr(uint32_t msr, uint64_t value); + auto write_msr(uint32_t msr, uint64_t new_value) -> void; /** - * @brief Sets a specific bit in the MSR register. + * @brief Sets a specific bit in the Extended Feature Enable Register (EFER) Model-Specific Register (MSR) register. * - * This function reads the current value of the EFER register, ORs the specified + * @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. */ - void set_msr_bit(msr_flags flag); + auto set_efer_bit(efer_flags flag) -> void; /** * @brief Enables the No-Execute Enable (NXE) bit in the Extended Feature Enable Register (EFER). * - * This function reads the current value of the EFER register, enables the NXE bit + * @note This function reads the current value of the EFER register, enables the NXE bit * (bit 11), and writes the updated value back to the EFER register. Enabling the NXE * bit allows the processor to support No-Execute memory regions, which are required * for certain memory protection features like Data Execution Prevention (DEP). */ - void enable_nxe_bit(); + auto enable_nxe_bit() -> void; } // namespace teachos::arch::memory::cpu 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 5e51c64..53284b6 100644 --- a/arch/x86_64/include/arch/memory/paging/kernel_mapper.hpp +++ b/arch/x86_64/include/arch/memory/paging/kernel_mapper.hpp @@ -1,7 +1,7 @@ #ifndef TEACHOS_ARCH_X86_64_MEMORY_PAGING_KERNEL_MAPPER_HPP #define TEACHOS_ARCH_X86_64_MEMORY_PAGING_KERNEL_MAPPER_HPP -#include "arch/memory/cpu/cr3.hpp" +#include "arch/memory/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" @@ -74,7 +74,8 @@ namespace teachos::arch::memory::paging auto remap_elf_kernel_sections(inactive_page_table inactive_table, temporary_page & temporary_page, active_page_table & active_table) -> void { - auto const backup = allocator::physical_frame::containing_address(cpu::read_cr3_register()); + auto const backup = + allocator::physical_frame::containing_address(cpu::read_control_register(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); @@ -95,11 +96,12 @@ namespace teachos::arch::memory::paging */ auto switch_active_page_table(inactive_page_table new_table) -> inactive_page_table { - auto const backup = allocator::physical_frame::containing_address(cpu::read_cr3_register()); + auto const backup = + allocator::physical_frame::containing_address(cpu::read_control_register(cpu::control_register::CR3)); auto const old_table = inactive_page_table{backup}; auto const new_address = new_table.page_table_level_4_frame.start_address(); - cpu::write_cr3_register(new_address); + cpu::write_control_register(cpu::control_register::CR3, new_address); return old_table; } diff --git a/arch/x86_64/src/memory/cpu/control_register.cpp b/arch/x86_64/src/memory/cpu/control_register.cpp new file mode 100644 index 0000000..38a887c --- /dev/null +++ b/arch/x86_64/src/memory/cpu/control_register.cpp @@ -0,0 +1,66 @@ +#include "arch/memory/cpu/control_register.hpp" + +#include "arch/exception_handling/assert.hpp" + +namespace teachos::arch::memory::cpu +{ + auto read_control_register(control_register cr) -> std::size_t + { + std::size_t current_value; + switch (cr) + { + case control_register::CR0: + asm volatile("movq %%cr0, %[output]" : [output] "=r"(current_value)); + break; + case control_register::CR2: + asm volatile("movq %%cr2, %[output]" : [output] "=r"(current_value)); + break; + case control_register::CR3: + asm volatile("movq %%cr3, %[output]" : [output] "=r"(current_value)); + break; + case control_register::CR4: + asm volatile("movq %%cr4, %[output]" : [output] "=r"(current_value)); + break; + default: + exception_handling::assert(false, + "[Control Register] Attempted to read non-existent or reserved control register"); + break; + } + return current_value; + } + + auto write_control_register(control_register cr, std::size_t new_value) -> void + { + switch (cr) + { + case control_register::CR0: + asm volatile("movq %[input], %%cr0" + : /* no output from call */ + : [input] "r"(new_value) + : "memory"); + break; + case control_register::CR2: + asm volatile("movq %[input], %%cr2" + : /* no output from call */ + : [input] "r"(new_value) + : "memory"); + break; + case control_register::CR3: + asm volatile("movq %[input], %%cr3" + : /* no output from call */ + : [input] "r"(new_value) + : "memory"); + break; + case control_register::CR4: + asm volatile("movq %[input], %%cr4" + : /* no output from call */ + : [input] "r"(new_value) + : "memory"); + break; + default: + exception_handling::assert(false, + "[Control Register] Attempted to write non-existent or reserved control register"); + break; + } + } +} // namespace teachos::arch::memory::cpu diff --git a/arch/x86_64/src/memory/cpu/cr3.cpp b/arch/x86_64/src/memory/cpu/cr3.cpp deleted file mode 100644 index 7e48d40..0000000 --- a/arch/x86_64/src/memory/cpu/cr3.cpp +++ /dev/null @@ -1,23 +0,0 @@ -#include "arch/memory/cpu/cr3.hpp" - -#include "arch/exception_handling/assert.hpp" - -namespace teachos::arch::memory::cpu -{ - auto read_cr3_register() -> allocator::physical_address - { - allocator::physical_address cr3; - asm volatile("movq %%cr3, %[output]" : [output] "=r"(cr3) : /* no input into call */ : "memory"); - return cr3; - } - - auto write_cr3_register(allocator::physical_address new_p4_table_address) -> void - { - exception_handling::assert(new_p4_table_address % allocator::PAGE_FRAME_SIZE == 0U, - "[CR3] Physical address to be written into register must be page aligned"); - asm volatile("movq %[input], %%cr3" - : /* no output from call */ - : [input] "r"(new_p4_table_address) - : "memory"); - } -} // namespace teachos::arch::memory::cpu diff --git a/arch/x86_64/src/memory/cpu/msr.cpp b/arch/x86_64/src/memory/cpu/msr.cpp index 3a917c9..6e3d1d3 100644 --- a/arch/x86_64/src/memory/cpu/msr.cpp +++ b/arch/x86_64/src/memory/cpu/msr.cpp @@ -2,14 +2,19 @@ namespace teachos::arch::memory::cpu { - uint64_t read_msr(uint32_t msr) + namespace + { + constexpr uint32_t 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<uint64_t>(high) << 32) | low; } - void write_msr(uint32_t msr, uint64_t value) + auto write_msr(uint32_t msr, uint64_t value) -> void { uint32_t low = value & 0xFFFFFFFF; uint32_t high = value >> 32; @@ -18,12 +23,11 @@ namespace teachos::arch::memory::cpu : "c"(msr), "a"(low), "d"(high)); } - void set_msr_bit(msr_flags flag) + auto set_efer_bit(efer_flags flag) -> void { - uint64_t efer = read_msr(IA32_EFER); - write_msr(IA32_EFER, static_cast<uint64_t>(flag) | efer); + uint64_t const efer = read_msr(IA32_EFER_ADDRESS); + write_msr(IA32_EFER_ADDRESS, static_cast<uint64_t>(flag) | efer); } - void enable_nxe_bit() { set_msr_bit(msr_flags::NXE); } - + auto enable_nxe_bit() -> void { set_efer_bit(efer_flags::NXE); } } // namespace teachos::arch::memory::cpu diff --git a/arch/x86_64/src/memory/cpu/tlb.cpp b/arch/x86_64/src/memory/cpu/tlb.cpp index 1663e80..591d9fc 100644 --- a/arch/x86_64/src/memory/cpu/tlb.cpp +++ b/arch/x86_64/src/memory/cpu/tlb.cpp @@ -1,6 +1,6 @@ #include "arch/memory/cpu/tlb.hpp" -#include "arch/memory/cpu/cr3.hpp" +#include "arch/memory/cpu/control_register.hpp" namespace teachos::arch::memory::cpu { @@ -9,5 +9,8 @@ namespace teachos::arch::memory::cpu asm volatile("invlpg (%[input])" : /* no output from call */ : [input] "r"(address) : "memory"); } - auto tlb_flush_all() -> void { write_cr3_register(read_cr3_register()); } + auto tlb_flush_all() -> void + { + write_control_register(cpu::control_register::CR3, read_control_register(cpu::control_register::CR3)); + } } // namespace teachos::arch::memory::cpu |
