aboutsummaryrefslogtreecommitdiff
path: root/arch/x86_64/arch/cpu/control_register.hpp
diff options
context:
space:
mode:
Diffstat (limited to 'arch/x86_64/arch/cpu/control_register.hpp')
-rw-r--r--arch/x86_64/arch/cpu/control_register.hpp249
1 files changed, 249 insertions, 0 deletions
diff --git a/arch/x86_64/arch/cpu/control_register.hpp b/arch/x86_64/arch/cpu/control_register.hpp
new file mode 100644
index 0000000..9cedc35
--- /dev/null
+++ b/arch/x86_64/arch/cpu/control_register.hpp
@@ -0,0 +1,249 @@
+#ifndef TEACHOS_X86_64_CPU_CONTROL_REGISTERS_HPP
+#define TEACHOS_X86_64_CPU_CONTROL_REGISTERS_HPP
+
+// IWYU pragma: private, include <arch/cpu/registers.hpp>
+
+#include <kapi/memory.hpp>
+
+#include <kstd/ext/bitfield_enum>
+
+#include <cstdint>
+#include <string_view>
+#include <type_traits>
+#include <utility>
+
+namespace arch::cpu
+{
+ namespace impl
+ {
+ //! The assembler templates used to access (r/w) CR0;
+ constexpr auto static cr0_asm = std::pair<std::string_view, std::string_view>{"mov %%cr0, %0", "mov %0, %%cr0"};
+
+ //! The assembler templates used to access (r/w) CR2;
+ constexpr auto static cr2_asm = std::pair<std::string_view, std::string_view>{"mov %%cr2, %0", "mov %0, %%cr2"};
+
+ //! The assembler templates used to access (r/w) CR3;
+ constexpr auto static cr3_asm = std::pair<std::string_view, std::string_view>{"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 arch::cpu
+
+namespace kstd::ext
+{
+ template<>
+ struct is_bitfield_enum<arch::cpu::cr0_flags> : std::true_type
+ {
+ };
+
+ template<>
+ struct is_bitfield_enum<arch::cpu::cr3_flags> : std::true_type
+ {
+ };
+} // namespace kstd::ext
+
+namespace arch::cpu
+{
+ //! 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<typename Derived, typename ValueType, typename = void>
+ struct control_register_with_flags
+ {
+ private:
+ constexpr control_register_with_flags() noexcept = default;
+ friend Derived;
+ };
+
+ //! @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<typename Derived, typename ValueType>
+ struct control_register_with_flags<Derived, ValueType, std::enable_if_t<kstd::ext::bitfield_enum<ValueType>>>
+ {
+ //! 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<typename ValueType, auto AssemblerTemplates>
+ struct control_register : control_register_with_flags<control_register<ValueType, AssemblerTemplates>, 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(kapi::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())}
+ {}
+
+ //! 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 -> kapi::memory::physical_address
+ {
+ constexpr auto address_shift = 12uz;
+ return kapi::memory::physical_address{m_address << address_shift};
+ }
+
+ //! Encode the frame aligned physical address of the root page map into this value.
+ //!
+ //! @param frame The frame containing a PML4.
+ constexpr auto frame(kapi::memory::frame frame) -> void
+ {
+ m_address = static_cast<std::uint64_t>(frame.number());
+ }
+
+ //! 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<cr3_flags>(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<std::uint64_t>(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<std::uint64_t>(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<std::uint64_t>(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 arch::cpu
+
+#endif \ No newline at end of file