hal_x86_64/
control_regs.rs

1use core::arch::asm;
2use hal_core::VAddr;
3use mycelium_util::bits::bitfield;
4
5pub mod cr3 {
6    use super::*;
7    use crate::{mm::size::Size4Kb, PAddr};
8    use hal_core::{mem::page::Page, Address};
9
10    #[derive(Copy, Clone, Eq, PartialEq)]
11    pub struct Flags(u64);
12
13    pub fn read() -> (Page<PAddr, Size4Kb>, Flags) {
14        let val: u64;
15        unsafe {
16            asm!("mov {0}, cr3", out(reg) val, options(readonly));
17        };
18        let addr = PAddr::from_u64(val);
19        tracing::trace!(rax = ?addr, "mov rax, cr3");
20        let pml4_page = Page::starting_at_fixed(addr)
21            .expect("PML4 physical addr not aligned! this is very bad");
22        (pml4_page, Flags(val))
23    }
24
25    /// # Safety
26    ///
27    /// Writing cr3 can break pretty much everything.
28    pub unsafe fn write(pml4: Page<PAddr, Size4Kb>, flags: Flags) {
29        let addr = pml4.base_addr().as_usize() as u64;
30        let val = addr | flags.0;
31        asm!("mov cr3, {0}", in(reg) val);
32    }
33
34    impl core::fmt::Debug for Flags {
35        fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
36            f.debug_tuple("cr3::Flags")
37                .field(&format_args!("{:#b}", self.0))
38                .finish()
39        }
40    }
41}
42
43bitfield! {
44    /// Control Register 0
45    #[derive(Eq, PartialEq)]
46    pub struct Cr0<u64> {
47        /// Protected Mode Enable (`PE`)
48        ///
49        /// Enables protected mode.
50        pub const PROTECTED_MODE_ENABLE: bool;
51        /// Monitor Coprocessor (`MP`).
52        ///
53        /// Enables monitoring of the coprocessor, typically for x87 instructions.
54        ///
55        /// Controls (together with the [`TASK_SWITCHED`] flag) whether a `WAIT`
56        /// or `FWAIT` instruction should cause an `#NE` exception.
57        pub const MONITOR_COPROCESSOR: bool;
58        /// x87 FPU Emulation (`EM`).
59        ///
60        /// Force all x87 and MMX instructions to cause an `#NE` exception.
61        pub const EMULATE_COPROCESSOR: bool;
62        /// Task Switched (`TS`).
63        ///
64        /// Set to 1 on hardware task switches. This allows lazily saving x87,
65        /// MMX, and SSE state on hardware context switches.
66        pub const TASK_SWITCHED: bool;
67        /// Extension Type (`ET`).
68        ///
69        /// Indicates support of 387DX math coprocessor instructions.
70        ///
71        /// Always set on all recent x86 processors, cannot be cleared.
72        pub const EXTENSION_TYPE: bool;
73        /// Numeric Error (`NE`).
74        ///
75        /// Enables native error reporting for x87 FPU errors.
76        pub const NUMERIC_ERROR: bool;
77        const _RESERVED_0 = 11;
78        /// Write Protect (`WP`).
79        ///
80        /// Enables write protection for ring 0 pages.
81        pub const WRITE_PROTECT: bool;
82        const _RESERVED_1 = 1;
83        /// Alignment Mask (`AM`).
84        ///
85        /// Enables user-mode alignment checking if the `ALIGNMENT_CHECK` bit in
86        /// `RFLAGS` is also set.
87        pub const ALIGNMENT_MASK: bool;
88        const _RESERVED_2 = 11;
89        /// Not Write Through (`NW`).
90        pub const NOT_WRITE_THROUGH: bool;
91        /// Cache Disable (`NW`).
92        pub const CACHE_DISABLE: bool;
93        /// Paging Enabled (`PG`).
94        ///
95        /// Enables paging, if [`PROTECTED_MODE_ENABLE`] is also set.
96        pub const PAGING_ENABLE: bool;
97    }
98}
99
100impl Cr0 {
101    #[must_use]
102    pub fn read() -> Self {
103        let bits: u64;
104        unsafe {
105            asm!("mov {0}, cr0", out(reg) bits, options(readonly));
106        };
107        Self::from_bits(bits)
108    }
109
110    /// Write a value to `CR0`.
111    ///
112    /// This function preserves the value of all reserved bits in `CR0`.
113    ///
114    /// # Safety
115    ///
116    /// Writing to `CR0` can do stuff.
117    pub unsafe fn write(value: Self) {
118        Self::update(|current| {
119            value
120                .with(Self::_RESERVED_0, current.get(Self::_RESERVED_0))
121                .with(Self::_RESERVED_1, current.get(Self::_RESERVED_1))
122                .with(Self::_RESERVED_2, current.get(Self::_RESERVED_2))
123        })
124    }
125
126    /// # Safety
127    ///
128    /// Writing to `CR4` can do stuff.
129    pub unsafe fn update(f: impl FnOnce(Self) -> Self) {
130        let curr = Self::read();
131        let value = f(curr);
132        tracing::trace!("mov cr0, {value:?}");
133        asm!("mov cr0, {0}", in(reg) value.bits());
134    }
135}
136
137bitfield! {
138    /// Control Register 4
139    #[derive(Eq, PartialEq)]
140    pub struct Cr4<u64> {
141        /// Virtual 8086 Mode Extensions (`VME`).
142        pub const VIRTUAL_8086_MODE_EXTENSIONS: bool;
143        /// Protected-Mode Virtual Interrupts (`PVI`).
144        pub const PROTECTED_MODE_VIRTUAL_INTERRUPTS: bool;
145        /// Time Stamp Disable (`TSD`).
146        pub const TIME_STAMP_DISABLE: bool;
147        /// Debugging Extensions (`DE`).
148        pub const DEBUGGING_EXTENSIONS: bool;
149        /// Page Size Extension (`PSE`).
150        ///
151        /// This enables the use of 4MB physical pages; always ignored in long mode.
152        pub const PAGE_SIZE_EXTENSION: bool;
153        /// Physical Address Extension (`PAE`).
154        ///
155        /// Enables the use of 2MB physical pages. Required in long mode.
156        pub const PHYSICAL_ADDRESS_EXTENSION: bool;
157        /// Machine Check Exception Enable (`MCE`).
158        pub const MACHINE_CHECK_EXCEPTION: bool;
159        /// Page Global Enable (`PGE`).
160        ///
161        /// Allows marking pages as global.
162        pub const PAGE_GLOBAL_ENABLE: bool;
163        /// Performance-Monitoring Counter Enable (`PCE`).
164        ///
165        /// Allows the
166        pub const PERFORMANCE_MONITOR_COUNTER: bool;
167        /// Operating System `FXSAVE`/`FXRSTOR` Support
168        ///
169        /// If enabled, the `FXSAVE` and `FXRSTOR` instructions are
170        /// available in both 64-bit and compatibility mode.
171        pub const OSFXSR: bool;
172        /// Operating System Support for Unmasked Floating-Point Exceptions
173        pub const OSXMMEXCPT: bool;
174        /// User-Mode Instruction Prevention (`UMIP`).
175        ///
176        /// If set, `SGDT`, `SIDT`, `SLDT`, `SMSW`, and `STR`, instructions
177        /// will result in a general protection fault (`#GP`) when in ring >
178        /// 0.
179        pub const USER_MODE_INSTRUCTION_PREVENTION: bool;
180        /// Virtual Machine Extensions (VMX) Enable
181        ///
182        /// **Note**: this extension is INTEL ONLY.
183        pub const VIRTUAL_MACHINE_EXTENSIONS: bool;
184        /// Safer Mode Extensions (SMX) Enable
185        ///
186        /// **Note**: this extension is INTEL ONLY.
187        pub const SAFER_MODE_EXTENSIONS: bool;
188        /// FSBASE/GSBASE Enable.
189        ///
190        /// Enables the instructions `RDFSBASE`,`RDGSBASE`, `WRFSBASE`, and `WRGSBASE`.
191        pub const FSGSBASE: bool;
192        /// PCID Enable (`PCIDE`).
193        pub const PCID_ENABLE: bool;
194        /// OS Support for `XSAVE` and Processor Extended States Enable
195        pub const OSXSAVE: bool;
196        /// Supervisor Mode Execution Protection Enable (`SMEP`).
197        pub const SUPERVISOR_EXECUTION_PROTECTION: bool;
198        /// Supervisor Mode Access Prevention Enable (`SMAP)
199        pub const SUPERVISOR_ACCESS_PREVENTION: bool;
200        /// Protection Key (Used) Enable (`PKE`).
201        ///
202        /// Enables protection keys for user-mode pages.
203        pub const PROTECTION_KEY_USER: bool;
204        /// Control-flow Enforcement Technology Enable (`CET`).
205        pub const CONTROL_FLOW_ENFORCEMENT: bool;
206        /// Protection Key (Supervisor) Enable (`PKS`).
207        ///
208        /// Enables protection keys for user-mode pages.
209        pub const PROTECTION_KEY_SUPERVISOR: bool;
210    }
211}
212
213impl Cr4 {
214    #[must_use]
215    pub fn read() -> Self {
216        let bits: u64;
217        unsafe {
218            asm!("mov {0}, cr4", out(reg) bits, options(readonly));
219        };
220        Self::from_bits(bits)
221    }
222
223    /// # Safety
224    ///
225    /// Writing to `CR4` can do stuff.
226    pub unsafe fn write(value: Self) {
227        tracing::trace!("mov cr4, {:?}", value);
228        asm!("mov cr4, {0}", in(reg) value.bits());
229    }
230
231    /// # Safety
232    ///
233    /// Writing to `CR4` can do stuff.
234    pub unsafe fn update(f: impl FnOnce(Self) -> Self) {
235        let curr = Self::read();
236        Self::write(f(curr));
237    }
238}
239
240/// Control Register 2 (CR2) contains the Page Fault Linear Address (PFLA).
241pub struct Cr2;
242
243impl Cr2 {
244    /// Returns the 32-bit Page Fault Linear Address (PFLA) stored in CR2.
245    pub fn read() -> VAddr {
246        let addr: u64;
247        unsafe {
248            asm!("mov {0}, cr2", out(reg) addr, options(readonly));
249        };
250        VAddr::from_u64(addr)
251    }
252}
253
254#[cfg(test)]
255mod tests {
256    use super::*;
257
258    #[test]
259    fn cr4_valid() {
260        Cr4::assert_valid();
261    }
262
263    #[test]
264    fn cr0_valid() {
265        Cr0::assert_valid();
266    }
267}