aboutsummaryrefslogtreecommitdiff
path: root/arch
diff options
context:
space:
mode:
authorFelix Morgner <felix.morgner@ost.ch>2025-11-21 14:53:37 +0100
committerFelix Morgner <felix.morgner@ost.ch>2025-11-21 14:53:37 +0100
commita5ca21e45e9c8ead0b5895771c0b2f3fe3baf96b (patch)
treed8cdd8f24d18d46b62a567c1f4fe9be490a54d7e /arch
parent886c920b8c943753a56ef4893e074c5754a1aa2d (diff)
downloadteachos-a5ca21e45e9c8ead0b5895771c0b2f3fe3baf96b.tar.xz
teachos-a5ca21e45e9c8ead0b5895771c0b2f3fe3baf96b.zip
x86_64: rework control register access
Diffstat (limited to 'arch')
-rw-r--r--arch/x86_64/CMakeLists.txt3
-rw-r--r--arch/x86_64/include/x86_64/cpu/impl/control_registers.hpp196
-rw-r--r--arch/x86_64/include/x86_64/cpu/registers.hpp68
-rw-r--r--arch/x86_64/src/cpu/registers.cpp64
-rw-r--r--arch/x86_64/src/kapi/memory.cpp3
-rw-r--r--arch/x86_64/src/memory/mmu.cpp4
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