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
//! Processor state stored in the RFLAGS register.

#[cfg(feature = "instructions")]
pub use self::x86_64::*;

use bitflags::bitflags;

bitflags! {
    /// The RFLAGS register.
    pub struct RFlags: u64 {
        /// Processor feature identification flag.
        ///
        /// If this flag is modifiable, the CPU supports CPUID.
        const ID = 1 << 21;
        /// Indicates that an external, maskable interrupt is pending.
        ///
        /// Used when virtual-8086 mode extensions (CR4.VME) or protected-mode virtual
        /// interrupts (CR4.PVI) are activated.
        const VIRTUAL_INTERRUPT_PENDING = 1 << 20;
        /// Virtual image of the INTERRUPT_FLAG bit.
        ///
        /// Used when virtual-8086 mode extensions (CR4.VME) or protected-mode virtual
        /// interrupts (CR4.PVI) are activated.
        const VIRTUAL_INTERRUPT = 1 << 19;
        /// Enable automatic alignment checking if CR0.AM is set. Only works if CPL is 3.
        const ALIGNMENT_CHECK = 1 << 18;
        /// Enable the virtual-8086 mode.
        const VIRTUAL_8086_MODE = 1 << 17;
        /// Allows to restart an instruction following an instrucion breakpoint.
        const RESUME_FLAG = 1 << 16;
        /// Used by `iret` in hardware task switch mode to determine if current task is nested.
        const NESTED_TASK = 1 << 14;
        /// The high bit of the I/O Privilege Level field.
        ///
        /// Specifies the privilege level required for executing I/O address-space instructions.
        const IOPL_HIGH = 1 << 13;
        /// The low bit of the I/O Privilege Level field.
        ///
        /// Specifies the privilege level required for executing I/O address-space instructions.
        const IOPL_LOW = 1 << 12;
        /// Set by hardware to indicate that the sign bit of the result of the last signed integer
        /// operation differs from the source operands.
        const OVERFLOW_FLAG = 1 << 11;
        /// Determines the order in which strings are processed.
        const DIRECTION_FLAG = 1 << 10;
        /// Enable interrupts.
        const INTERRUPT_FLAG = 1 << 9;
        /// Enable single-step mode for debugging.
        const TRAP_FLAG = 1 << 8;
        /// Set by hardware if last arithmetic operation resulted in a negative value.
        const SIGN_FLAG = 1 << 7;
        /// Set by hardware if last arithmetic operation resulted in a zero value.
        const ZERO_FLAG = 1 << 6;
        /// Set by hardware if last arithmetic operation generated a carry ouf of bit 3 of the
        /// result.
        const AUXILIARY_CARRY_FLAG = 1 << 4;
        /// Set by hardware if last result has an even number of 1 bits (only for some operations).
        const PARITY_FLAG = 1 << 2;
        /// Set by hardware if last arithmetic operation generated a carry out of the
        /// most-significant bit of the result.
        const CARRY_FLAG = 1;
    }
}

#[cfg(feature = "instructions")]
mod x86_64 {
    use super::*;

    /// Returns the current value of the RFLAGS register.
    ///
    /// Drops any unknown bits.
    #[inline]
    pub fn read() -> RFlags {
        RFlags::from_bits_truncate(read_raw())
    }

    /// Returns the raw current value of the RFLAGS register.
    #[inline]
    pub fn read_raw() -> u64 {
        let r: u64;

        #[cfg(feature = "inline_asm")]
        unsafe {
            asm!("pushfq; pop {}", out(reg) r, options(nomem, preserves_flags));
        }
        #[cfg(not(feature = "inline_asm"))]
        unsafe {
            r = crate::asm::x86_64_asm_read_rflags();
        }

        r
    }

    /// Writes the RFLAGS register, preserves reserved bits.
    ///
    /// ## Safety
    ///
    /// Unsafe because undefined becavior can occur if certain flags are modified. For example,
    /// the `DF` flag must be unset in all Rust code. Also, modifying `CF`, `PF`, or any other
    /// flags also used by Rust/LLVM can result in undefined behavior too.
    #[inline]
    pub unsafe fn write(flags: RFlags) {
        let old_value = read_raw();
        let reserved = old_value & !(RFlags::all().bits());
        let new_value = reserved | flags.bits();

        write_raw(new_value);
    }

    /// Writes the RFLAGS register.
    ///
    /// Does not preserve any bits, including reserved bits.
    ///
    ///
    /// ## Safety
    ///
    /// Unsafe because undefined becavior can occur if certain flags are modified. For example,
    /// the `DF` flag must be unset in all Rust code. Also, modifying `CF`, `PF`, or any other
    /// flags also used by Rust/LLVM can result in undefined behavior too.
    #[inline]
    pub unsafe fn write_raw(val: u64) {
        // HACK: we mark this function as preserves_flags to prevent Rust from restoring
        // saved flags after the "popf" below. See above note on safety.
        #[cfg(feature = "inline_asm")]
        asm!("push {}; popfq", in(reg) val, options(nomem, preserves_flags));

        #[cfg(not(feature = "inline_asm"))]
        crate::asm::x86_64_asm_write_rflags(val);
    }

    #[cfg(test)]
    mod test {
        use crate::registers::rflags::read;

        #[test]
        fn rflags_read() {
            let rflags = read();
            println!("{:#?}", rflags);
        }
    }
}