diff options
| author | Felix Morgner <felix.morgner@ost.ch> | 2025-11-21 14:53:37 +0100 |
|---|---|---|
| committer | Felix Morgner <felix.morgner@ost.ch> | 2025-11-21 14:53:37 +0100 |
| commit | a5ca21e45e9c8ead0b5895771c0b2f3fe3baf96b (patch) | |
| tree | d8cdd8f24d18d46b62a567c1f4fe9be490a54d7e /arch | |
| parent | 886c920b8c943753a56ef4893e074c5754a1aa2d (diff) | |
| download | teachos-a5ca21e45e9c8ead0b5895771c0b2f3fe3baf96b.tar.xz teachos-a5ca21e45e9c8ead0b5895771c0b2f3fe3baf96b.zip | |
x86_64: rework control register access
Diffstat (limited to 'arch')
| -rw-r--r-- | arch/x86_64/CMakeLists.txt | 3 | ||||
| -rw-r--r-- | arch/x86_64/include/x86_64/cpu/impl/control_registers.hpp | 196 | ||||
| -rw-r--r-- | arch/x86_64/include/x86_64/cpu/registers.hpp | 68 | ||||
| -rw-r--r-- | arch/x86_64/src/cpu/registers.cpp | 64 | ||||
| -rw-r--r-- | arch/x86_64/src/kapi/memory.cpp | 3 | ||||
| -rw-r--r-- | arch/x86_64/src/memory/mmu.cpp | 4 |
6 files changed, 201 insertions, 137 deletions
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 <cstdint> +#include <string_view> +#include <type_traits> + +namespace teachos::cpu::x86_64 +{ + + namespace impl + { + constexpr auto static cr0_asm = std::pair<std::string_view, std::string_view>{"mov %%cr0, %0", "mov %0, %%cr0"}; + constexpr auto static cr2_asm = std::pair<std::string_view, std::string_view>{"mov %%cr2, %0", "mov %0, %%cr2"}; + constexpr auto static cr3_asm = std::pair<std::string_view, std::string_view>{"mov %%cr3, %0", "mov %0, %%cr3"}; + + template<typename Derived, typename ValueType, typename = void> + struct control_register_with_flags + { + }; + + template<typename Derived, typename ValueType> + struct control_register_with_flags<Derived, ValueType, std::enable_if_t<std::is_enum_v<ValueType>>> + { + 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<typename ValueType, auto AssemblerTemplates> + struct control_register : control_register_with_flags<control_register<ValueType, AssemblerTemplates>, 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<cr0_flags>(static_cast<std::underlying_type_t<cr0_flags>>(lhs) | + static_cast<std::underlying_type_t<cr0_flags>>(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<cr0_flags>(static_cast<std::underlying_type_t<cr0_flags>>(lhs) & + static_cast<std::underlying_type_t<cr0_flags>>(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<cr0_flags>(~static_cast<std::underlying_type_t<cr0_flags>>(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<cr3_flags>(static_cast<std::underlying_type_t<cr3_flags>>(lhs) | + static_cast<std::underlying_type_t<cr3_flags>>(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<cr3_flags>(static_cast<std::underlying_type_t<cr3_flags>>(lhs) & + static_cast<std::underlying_type_t<cr3_flags>>(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<cr3_flags>(~static_cast<std::underlying_type_t<cr3_flags>>(lhs)); + } + + struct cr3_value + { + constexpr cr3_value() = default; + + constexpr cr3_value(memory::physical_address address, cr3_flags flags = static_cast<cr3_flags>(0)) + : m_flags{static_cast<std::uint64_t>(flags)} + , m_address{static_cast<std::uint64_t>(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<std::uint64_t>(address.raw()); + } + + [[nodiscard]] constexpr auto flags() const -> cr3_flags + { + return static_cast<cr3_flags>(m_flags); + } + + constexpr auto flags(cr3_flags flags) -> void + { + m_flags = static_cast<std::uint64_t>(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<impl::cr0_flags, &impl::cr0_asm>; + using cr2 = impl::control_register<memory::linear_address, &impl::cr2_asm>; + using cr3 = impl::control_register<impl::cr3_value, &impl::cr3_asm>; + +} // 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 <cstdint> - -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 <type_traits> - -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<std::underlying_type_t<cr0_flags>>(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 |
