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}