tartan_arch/
x86_common.rs

1//! Architecture-specific primitives common to 32-bit and 64-bit x86 processors.
2
3use core::arch::asm;
4use tartan_bitfield::bitfield;
5
6#[cfg(doc)]
7use features::BasicFeatures;
8#[cfg(doc)]
9use protection::IOPermissionBitmap;
10
11pub mod features;
12pub mod interrupt;
13pub mod io;
14pub mod paging;
15pub mod protection;
16
17
18bitfield! {
19    /// `EFLAGS`/`RFLAGS`: General flags, including control, status, and basic system
20    /// flags.
21    ///
22    /// Getters and setters for this structure only access a value in memory, not the
23    /// register itself. Use the [`get`](Self::get) and [`set`](Self::set) methods to work
24    /// with the actual register.
25    pub struct FlagRegister(usize) {
26        /// `CF`: Indicates an arithmetic instruction generated a carry/borrow (unsigned
27        /// overflow).
28        [0] pub carry,
29        /// `PF`: Indicates that the least-significant byte of the result has *even*
30        /// parity.
31        [2] pub parity,
32        /// `AF`: Indicates a carry/borrow/overflow out of bit 3 in binary-coded decimal
33        /// (BCD) arithmetic.
34        [4] pub aux_carry,
35        /// `ZF`: Indicates that the result of an instruction is zero.
36        [6] pub zero,
37        /// `SF`: Indicates that the most-significant bit of a result is 1.
38        [7] pub sign,
39        /// `TF`: Enable single-step debugging.
40        [8] pub trap,
41        /// `IF`: Enable non-maskable interrupts. Non-maskable interrupts are always
42        /// enabled.
43        [9] pub interrupt_enabled,
44        /// `DF`: String instructions work on addresses high-to-low when set, low-to-high
45        /// when clear.
46        [10] pub direction,
47        /// `OF`: Indicates that the result overflowed for signed arithmetic (carry/borrow
48        /// for the second-most-significant bit).
49        [11] pub signed_overflow,
50        /// `IOPL`: Sets the privilege threshold for a task to access I/O address space.
51        /// Smaller numbers are higher privilege.
52        ///
53        /// Individual I/O ports may still be accessible at lower privilege levels
54        /// (greater numeric values) if allowed by [`IOPermissionBitmap`] for the current
55        /// task.
56        [12..14] pub io_privilege_level: u8,
57        /// `NT`: Indicates that the processor should switch back to a parent task when it
58        /// executes an `IRET` instruction.
59        ///
60        /// Only supported in 32-bit mode. If this is set in 64-bit mode, `IRET` will
61        /// trigger an exception.
62        [14] pub nested_task,
63        /// `RF`: Disable instruction breakpoints.
64        [16] pub resume,
65        /// `VM`: Enable virtual real mode.
66        [17] pub virtual_8086_mode,
67        /// `AC`: Enable strict alignment checks for memory accesses in privilege level 3.
68        /// In privilege levels 0–2, allow access to pages assigned to lower privilege
69        /// levels.
70        ///
71        /// Alignment checking requires [`ControlRegister0::alignment_check_mask`]. Access
72        /// protection requires [`ControlRegister4::supervisor_access_prevention`].
73        [18] pub alignment_check_or_access_control,
74        /// `VIF`: Virtual counterpart to the `interrupt_enabled` flag, used with
75        /// [VME](ControlRegister4::virtual_8086_extensions) or
76        /// [PVI](ControlRegister4::protected_virtual_interrupts).
77        [19] pub virtual_interrupt_enabled,
78        /// `VIP`: Indicates an interrupt is pending for
79        /// [VME](ControlRegister4::virtual_8086_extensions) or
80        /// [PVI](ControlRegister4::protected_virtual_interrupts).
81        [20] pub virtual_interrupt_pending,
82        /// `ID`: Indicates `CPUID` support when the flag is modifiable.
83        [21] pub identification,
84    }
85}
86
87impl FlagRegister {
88    /// Retrieve the current value of the `EFLAGS` register.
89    pub fn get() -> Self {
90        let mut value = Self(0);
91        unsafe {
92            #[cfg(target_arch = "x86")]
93            asm!(
94                "
95                pushfd
96                pop {0:e}
97                ",
98                out(reg) value.0,
99            );
100
101            #[cfg(target_arch = "x86_64")]
102            asm!(
103                "
104                pushfq
105                pop {0:r}
106                ",
107                out(reg) value.0,
108            );
109        }
110        value
111    }
112
113    /// Update the `EFLAGS` register with the given value, as permission level allows.
114    ///
115    /// Some flags will be unaffected depending on the processor mode and permission level
116    /// flags. See the reference for the POPF instruction in the _Intel 64 and IA-32
117    /// Architectures Software Developer's Manual_, volume 2.
118    ///
119    /// # Safety
120    /// Altering certain system flags can have dramatic effects on the execution of this
121    /// and other programs, including memory safety. See volume 1 §3.4.3 ("EFLAGS
122    /// Register") and volume 3 §2.3 ("System Flags and Fields in the EFLAGS Register") of
123    /// the _Intel 64 and IA-32 Architectures Software Developer's Manual_.
124    pub unsafe fn set(value: Self) {
125        #[cfg(target_arch = "x86")]
126        asm!(
127            "
128            push {0:e}
129            popfd
130            ",
131            in(reg) value.0,
132        );
133
134        #[cfg(target_arch = "x86_64")]
135        asm!(
136            "
137            push {0:r}
138            popfq
139            ",
140            in(reg) value.0,
141        );
142    }
143}
144
145
146#[macro_export]
147#[doc(hidden)]
148macro_rules! simple_register_access {
149    [$struct:ident, $register:literal] => {
150        impl $struct {
151            /// Retrieve the current value of this register
152            pub fn get() -> Self {
153                let mut value = Self(0);
154                unsafe {
155                    core::arch::asm!(
156                        concat!("mov {0}, ", $register),
157                        out(reg) value.0,
158                    );
159                }
160                value
161            }
162
163            /// Update the register to the given value.
164            ///
165            /// # Safety
166            /// Altering certain system flags can have dramatic effects on the execution
167            /// of this and other programs, including memory safety.
168            pub unsafe fn set(value: Self) {
169                core::arch::asm!(
170                    concat!("mov ", $register, ", {0}"),
171                    in(reg) value.0,
172                );
173            }
174        }
175    }
176}
177
178#[macro_export]
179#[doc(hidden)]
180macro_rules! indexed_register_access {
181    [$struct:ident, $index:literal, $read_instr:literal, $write_instr:literal] => {
182        impl $struct {
183            /// Retrieve the current value of this register
184            pub fn get() -> Self {
185                let lower: u32;
186                let upper: u32;
187                unsafe {
188                    core::arch::asm!(
189                        $read_instr,
190                        in("ecx") $index,
191                        out("eax") lower,
192                        out("edx") upper,
193                    );
194                }
195                let mut value = Self(0);
196                value.0 |= u64::from(lower);
197                value.0 |= u64::from(upper) << 32;
198                value
199            }
200
201            /// Update the register to the given value.
202            ///
203            /// # Safety
204            /// Altering certain system flags can have dramatic effects on the execution
205            /// of this and other programs, including memory safety.
206            pub unsafe fn set(value: Self) {
207                #![allow(clippy::cast_possible_truncation)]
208                let lower = value.0 as u32;
209                let upper = (value.0 >> 32) as u32;
210                core::arch::asm!(
211                    $write_instr,
212                    in("ecx") $index,
213                    in("eax") lower,
214                    in("edx") upper,
215                );
216            }
217        }
218    }
219}
220
221#[macro_export]
222#[doc(hidden)]
223macro_rules! extended_register_access {
224    [$struct:ident, $index:literal] => {
225        $crate::indexed_register_access!($struct, $index, "xgetbv", "xsetbv");
226    }
227}
228
229#[macro_export]
230#[doc(hidden)]
231macro_rules! model_specific_register_access {
232    [$struct:ident, $index:literal] => {
233        $crate::indexed_register_access!($struct, $index, "rdmsr", "wrmsr");
234    }
235}
236
237
238bitfield! {
239    /// `CR0`: System control register with flags affecting protection, paging, and FPU
240    /// behavior.
241    ///
242    /// Getters and setters for this structure only access a value in memory, not the
243    /// register itself. Use the [`get`](Self::get) and [`set`](Self::set) methods to
244    /// work with the actual register.
245    pub struct ControlRegister0(usize) {
246        /// `CR0.PG`: Enable paging. Requires [`protected_mode`](Self::protected_mode).
247        [31] pub paging,
248        /// `CR0.CD`: Disable all memory caching.
249        [30] pub cache_disabled,
250        /// `CR0.NW`: Disable write-back/write-through caching.
251        [29] pub cache_not_write_through,
252        /// `CR0.AM`: Enables strict alignment checks for memory access, in combination
253        /// with [`FlagRegister::alignment_check_or_access_control`].
254        [18] pub alignment_check_mask,
255        /// `CR0.WP`: Enforce read-only pages even in privilege levels 0–2. They are
256        /// always enforced in level 3.
257        [16] pub write_protect,
258        /// `CR0.NE`: Use internal error mechanism for FPU errors, rather than DOS-style.
259        [ 5] pub native_fpu_error,
260        /// `CR0.ET`: On 386/486, 387 FPU instructions are supported if set. Always set
261        /// on modern processors.
262        [ 4] pub fpu_extension_type,
263        /// `CR0.TS`: Set by processor when task was switched but FPU context has not been
264        /// saved yet.
265        ///
266        /// Used to save work when the new task does not alter the FPU state. When an FPU
267        /// instruction is executed with this flag set, the processor raises an exception
268        /// that allows the OS to save the FPU state. This behavior can be altered by
269        /// other flags. See [`monitor_fpu_state`](Self::monitor_fpu_state).
270        [ 3] pub task_switched_without_fpu_state,
271        /// `CR0.EM`: Trigger an exception on all FPU instructions. Used to support
272        /// software emulation.
273        [ 2] pub fpu_emulation,
274        /// `CR0.MP`: Enable exception behavior described for the `CR0.TS` flag for the
275        /// (`F`)`WAIT` instruction.
276        [ 1] pub monitor_fpu,
277        /// Enable protected mode. Does not enable paging on its own. See
278        /// [`paging`](Self::paging).
279        [ 0] pub protected_mode,
280    }
281}
282
283simple_register_access!(ControlRegister0, "cr0");
284
285impl ControlRegister0 {
286    /// Directly clear the
287    /// [`task_switched_without_fpu_state`](Self::task_switched_without_fpu_state) flag in
288    /// this register using a single instruction.
289    ///
290    /// # Safety
291    /// Clearing this flag when inappropriate may clobber FPU state and potentially affect
292    /// memory safety.
293    pub unsafe fn clear_task_switched_without_fpu_state() {
294        asm!("clts");
295    }
296}
297
298
299bitfield! {
300    /// `CR4`: Miscellaneous system control flags.
301    ///
302    /// Getters and setters for this structure only access a value in memory, not the
303    /// register itself. Use the [`get`](Self::get) and [`set`](Self::set) methods to
304    /// work with the actual register.
305    pub struct ControlRegister4(usize) {
306        /// `CR4.VME`: Enable interrupts and exception handling in [virtual
307        /// real-mode](EFLAGS::virtual_8086_mode).
308        ///
309        /// Requires [`BasicFeatures::virtual_8086_extensions`].
310        [0] pub virtual_8086_extensions,
311
312        /// `CR4.PVI`: Enable virtual interrupts in protected mode.
313        ///
314        /// Requires [`BasicFeatures::virtual_8086_extensions`].
315        [1] pub protected_virtual_interrupts,
316
317        /// `CR4.TSD`: Disable access to processor timestamp counter except in privilege
318        /// level 0.
319        ///
320        /// Requires [`BasicFeatures::timestamp_counter`].
321        [2] pub timestamp_disabled,
322
323        /// `CR4.DE`: Enable newer debug register scheme where `DR4` and `DR5` are
324        /// unavailable.
325        ///
326        /// When this flag is clear, they are equivalent to `DR6` and `DR7`.
327        ///
328        /// Requires [`BasicFeatures::debugging_extensions`].
329        [3] pub debugging_extensions,
330
331        /// `CR4.PSE`: Support large pages (4MB). Applies to 32-bit mode only.
332        ///
333        /// When this flag is clear in 32-bit mode, pages are always 4KB. Large pages are
334        /// always enabled in 64-bit mode.
335        ///
336        /// Requires [`BasicFeatures::page_size_extensions`].
337        [4] pub page_size_extensions,
338
339        /// `CR4.PAE`: Enable pages to map to physical addresses larger than 32-bits.
340        ///
341        /// Required for 64-bit mode.
342        ///
343        /// Requires [`BasicFeatures::physical_address_extension`].
344        [5] pub physical_address_extension,
345
346        /// `CR4.MCE`: Enable machine-check exception.
347        ///
348        /// Requires [`BasicFeatures::machine_check_exception`].
349        [6] pub machine_check_exception,
350
351        /// `CR4.PGE`: Enable global pages, which are shared across task contexts.
352        ///
353        /// Requires [`BasicFeatures::global_pages`].
354        [7] pub global_pages,
355
356        /// `CR4.PCE`: Allow access to performance monitoring counter in privilege levels
357        /// 1–3 (always accessible in level 0).
358        [8] pub performance_counter,
359
360        /// `CR4.OSFXSR`: Enable the `FXSAVE`/`FXRSTOR` and SSE instructions, if present.
361        ///
362        /// These instructions require special support from the operating system.
363        ///
364        /// Requires [`BasicFeatures::fpu_save`].
365        [9] pub sse_and_fpu_save,
366
367        /// `CR4.OSXMMEXCPT`: Enable unmasked SIMD floating-point exception handling for
368        /// SSE instructions.
369        ///
370        /// This requires special support from the operating system.
371        [10] pub simd_exceptions,
372
373        /// `CR4.UMIP`: Prevent access to instructions that allow reads from
374        /// descriptor/task registers, except in privilege level 0.
375        [11] pub restrict_user_mode_instructions,
376
377        /// `CR4.LA57`: Support 57-bit addresses using 5-level paging in 64-bit mode.
378        #[cfg(any(target_arch = "x86_64", doc))]
379        #[doc(cfg(target_arch = "x86_64"))]
380        [12] pub five_level_paging,
381
382        /// `CR4.VMX` (**Intel-only**): Enable virtual machine extensions.
383        ///
384        /// Requires [`BasicFeatures::virtual_machine_extensions`].
385        [13] pub virtual_machine_extensions,
386
387        /// `CR4.SME` (**Intel-only**): Enable safer-mode extensions.
388        ///
389        /// Requires [`BasicFeatures::safer_mode_extensions`].
390        [14] pub safer_mode_extensions,
391
392        /// `CR4.FSGSBASE`: Enable instructions to load/store the `FS` and `GS` base
393        /// registers with 32/64-bit values in 64-bit mode.
394        #[cfg(any(target_arch = "x86_64", doc))]
395        #[doc(cfg(target_arch = "x86_64"))]
396        [16] pub extended_base_registers,
397
398        /// `CR4.PCIDE`: Enable process-context identifiers (PCID) in 64-bit mode.
399        ///
400        /// Requires [`BasicFeatures::process_context_ids`].
401        #[cfg(any(target_arch = "x86_64", doc))]
402        #[doc(cfg(target_arch = "x86_64"))]
403        [17] pub process_context_ids,
404
405        /// `CR4.OSXSAVE`: Enable instructions for saving and restoring extended processor
406        /// state (FPU/MMX/SSE/AVX).
407        ///
408        /// These instructions require special support from the operating system.
409        ///
410        /// Requires [`BasicFeatures::extended_state_save`].
411        [18] pub extended_state_save,
412
413        /// `CR4.SMEP`: Enable execution prevention in privilege levels 0–2.
414        [20] pub supervisor_execution_prevention,
415
416        /// `CR4.SMAP`: Enable access prevention in privilege levels 0–2.
417        [21] pub supervisor_access_prevention,
418
419        /// `CR4.PKE`: Use page protection keys in 64-bit mode to control access from
420        /// privilege level 3.
421        #[cfg(any(target_arch = "x86_64", doc))]
422        #[doc(cfg(target_arch = "x86_64"))]
423        [22] pub user_protection_keys,
424
425        /// `CR4.CET` (**Intel-only**): Enable control-flow enforcement technology.
426        /// Requires [`ControlRegister0::write_protect`].
427        [23] pub control_flow_enforcement,
428
429        /// `CR4.PKS` (**Intel-only**): Use page protection keys in 64-bit mode to control
430        /// access from privilege levels 0-2.
431        #[cfg(any(target_arch = "x86_64", doc))]
432        #[doc(cfg(target_arch = "x86_64"))]
433        [24] pub supervisor_protection_keys,
434    }
435}
436
437simple_register_access!(ControlRegister4, "cr4");
438
439
440bitfield! {
441    /// `XCR0`: System control flags that indicate OS support for context management for
442    /// various registers with the `XSAVE` feature.
443    ///
444    /// Getters and setters for this structure only access a value in memory, not the
445    /// register itself. Use the [`get`](Self::get) and [`set`](Self::set) methods to
446    /// work with the actual register.
447    ///
448    /// Requires [`BasicFeatures::extended_state_save`].
449    pub struct ExtendedControlRegister0(u64) {
450        /// `XCR0.X87`: Hardcoded to 1.
451        [0] pub fpu,
452
453        /// `XCR0.SSE`: Manage SSE state with `XSAVE`, including the `XMM` registers.
454        [1] pub sse,
455
456        /// `XCR0.AVX`: Manage 256-bit AVX state in upper halves of the `YMM` registers
457        /// with `XSAVE`.
458        ///
459        /// Requires [`sse`](Self::sse). The lower halves of these registers are
460        /// equivalent to `XMM` and are covered by that flag.
461        [2] pub avx_256,
462
463        /// `XCR0.BNDREG`: Manage MPX bounds registers with `XSAVE`.
464        ///
465        /// Requires [`mpx_bound_config_status`](Self::mpx_bound_config_status).
466        [3] pub mpx_bounds,
467
468        /// `XCR0.BNDCSR`: Manage MPX config and status registers with `XSAVE`.
469        ///
470        /// Requires [`mpx_bounds`](Self::mpx_bounds).
471        [4] pub mpx_bound_config_status,
472
473        /// `XCR0.OPMASK`: Manage AVX-512 opmask registers with `XSAVE`.
474        ///
475        /// Requires the other `avx_512_*` flags.
476        [5] pub avx_512_opmask,
477
478        /// `XCR0.ZMM_Hi256`: Manage 512-bit AVX state in the upper halves of the `ZMM`
479        /// registers up to `ZMM15` with `XSAVE`.
480        ///
481        /// Registers `ZMM8`–`ZMM15` are available in 64-bit mode only, so this only
482        /// applies up to `ZMM7` in 32-bit mode.
483        ///
484        /// Requires [`avx_256`](Self::avx_256) and the other `avx_512_*` flags. The lower
485        /// halves of these registers are equivalent to `YMM` and are covered by
486        /// `avx_256`(Self::avx_256).
487        [6] pub avx_512,
488
489        /// `XCR0.Hi16_ZMM`: Manage AVX-512 state in `ZMM16`–`ZMM31` with `XSAVE`.
490        ///
491        /// These registers are only available in 64-bit mode.
492        ///
493        /// Requires the other `avx_512_*` flags.
494        #[cfg(any(target_arch = "x86_64", doc))]
495        #[doc(cfg(target_arch = "x86_64"))]
496        [7] pub avx_512_extended,
497
498        /// `XCR0.PKRU`: Manage protection key rights registers with `XSAVE`.
499        [8] pub protection_key_rights,
500    }
501}
502
503extended_register_access!(ExtendedControlRegister0, 0);