1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
//! Abstractions for cpu flags and control registers

use core::arch::asm;

use bitfield_struct::bitfield;

use super::paging::Physical;

/// The RFLAGS register.
#[bitfield(u32)]
pub struct Flags {
    /// Set by hardware if last arithmetic operation generated a carry out of the
    /// most-significant bit of the result.
    pub carry: bool,
    __: bool,
    /// Set by hardware if last result has an even number of 1 bits (only for some operations).
    pub parity: bool,
    __: bool,
    /// Set by hardware if last arithmetic operation generated a carry ouf of bit 3 of the
    /// result.
    pub auxiliary_carry: bool,
    __: bool,
    /// Set by hardware if last arithmetic operation resulted in a zero value.
    pub zero: bool,
    /// Set by hardware if last arithmetic operation resulted in a negative value.
    pub sign: bool,
    /// Enable single-step mode for debugging.
    pub trap: bool,
    /// Enable interrupts.
    pub interrupt: bool,
    /// Determines the order in which strings are processed.
    pub direction: bool,
    /// Set by hardware to indicate that the sign bit of the result of the last signed integer
    /// operation differs from the source operands.
    pub overflow: bool,
    /// Specifies the privilege level required for executing I/O address-space instructions.
    pub iopl_low: bool,
    /// Specifies the privilege level required for executing I/O address-space instructions.
    pub iopl_high: bool,
    /// Used by `iret` in hardware task switch mode to determine if current task is nested.
    pub nested_task: bool,
    __: bool,
    /// Temporarily disables debug exceptions so that an instruction can be restarted after a debug exception
    /// without immediately causing another debug exception.
    pub resume: bool,
    /// Enable the virtual-8086 mode.
    pub virtual_8086_mode: bool,
    /// Enable automatic alignment checking if [Cr0::alignment_mask] is set. Only works if CPL is 3.
    pub alignment_check: bool,
    /// Virtual interrupt flag.
    pub virtual_interrupt: bool,
    /// Virtual interrupt pending.
    pub virtual_interrupt_pending: bool,
    /// Able to use CPUID instruction.
    pub id: bool,
    #[bits(10)]
    __: (),
}

impl Flags {
    pub fn read() -> Self {
        let value: u32;
        unsafe { asm!("pushfd\npop {}", out(reg) value, options(nomem)) };
        Self(value)
    }
}

/// Contains system control flags that control operating mode and states of the processor.
#[bitfield(u32)]
pub struct Cr0 {
    /// Enables protected mode.
    pub protected_mode_enable: bool,
    /// Enables monitoring of the coprocessor, typical for x87 instructions.
    pub monitor_coprocessor: bool,
    /// Indicates that the processor does not have an internal or external x87 FPU when set.
    pub emulate_coprocessor: bool,
    /// Automatically set to 1 on _hardware_ task switch.
    ///
    /// This flags allows lazily saving x87/MMX/SSE instructions on hardware context switches.
    pub task_switched: bool,
    __: bool,
    /// Enables the native error reporting mechanism for x87 FPU errors.
    pub numeric_error: bool,
    #[bits(10)]
    __: (),
    /// Controls whether supervisor-level writes to read-only pages are inhibited.
    pub write_protect: bool,
    __: bool,
    /// Enables automatic alignment checking.
    pub alignment_mask: bool,
    #[bits(10)]
    __: (),
    /// Ignored. Used to control write-back/write-through cache strategy on older CPUs.
    pub not_write_through: bool,
    /// Disables internal caches (only for some cases).
    pub cache_disable: bool,
    /// Enables page translation.
    pub paging: bool,
}

impl Cr0 {
    pub fn read() -> Self {
        let value: u32;
        unsafe { asm!("mov {}, cr0", out(reg) value, options(nomem)) };
        Self(value)
    }

    pub fn update(f: impl FnOnce(Self) -> Self) {
        unsafe { f(Self::read()).write() }
    }

    unsafe fn write(self) {
        asm!("mov cr0, {}", in(reg) self.0, options(nostack));
    }
}

/// Contains the page-fault linear address.
#[repr(transparent)]
pub struct Cr2(pub u32);

impl Cr2 {
    pub fn read() -> Self {
        let value: u32;
        unsafe { asm!("mov {}, cr2", out(reg) value, options(nomem)) };
        Self(value)
    }
}

/// Contains the physical address of the base of the paging-structure hierarchy
/// and two flags (PCD and PWT).
///
/// The PCD and PWT flags control caching of that paging structure in the
/// processor’s internal data caches (they do not control TLB caching of
/// page-directory information).
#[bitfield(u32)]
pub struct Cr3 {
    #[bits(3)]
    __: (),
    /// Page-level write-through.
    pub write_through: bool,
    /// Page-level cache disable.
    pub cache_disable: bool,
    #[bits(7)]
    __: (),
    /// Physical address of the page directory.
    /// The lower 12 bits of the address are assumed to be 0.
    /// The page directory must thus be aligned to a page (4K) boundary.
    #[bits(20)]
    pub address: Physical,
}

impl Cr3 {
    pub fn read() -> Self {
        let value: u32;
        unsafe { asm!("mov {}, cr3", out(reg) value, options(nomem)) };
        Self(value)
    }

    pub unsafe fn write(self) {
        asm!("mov cr3, {}", in(reg) self.0, options(nostack));
    }
}