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
#![cfg(any(target_arch = "x86", target_arch = "x86_64"))]
#![no_std]
#![cfg_attr(test, allow(unused_features))]
#![cfg_attr(all(test, feature = "vmtest"), feature(custom_test_frameworks))]
#![cfg_attr(all(test, feature = "vmtest"), test_runner(x86test::runner::runner))]
#![cfg_attr(feature = "unstable", feature(step_trait))]

use core::arch::asm;
#[cfg(target_arch = "x86")]
pub(crate) use core::arch::x86 as arch;
#[cfg(target_arch = "x86_64")]
pub(crate) use core::arch::x86_64 as arch;

macro_rules! bit {
    ($x:expr) => {
        1 << $x
    };
}

pub mod bits16;
pub mod bits32;
pub mod bits64;

pub mod apic;
pub mod controlregs;
pub mod debugregs;
pub mod dtables;
pub mod fence;
pub mod io;
pub mod irq;
pub mod msr;
pub mod random;
pub mod segmentation;
pub mod task;
pub mod time;
pub mod tlb;
pub mod vmx;

#[cfg(feature = "performance-counter")]
pub mod perfcnt;

/// A short-cut to the architecture (bits32 or bits64) this crate was compiled for.
pub mod current {
    #[cfg(target_arch = "x86")]
    pub use crate::bits32::*;
    #[cfg(target_arch = "x86_64")]
    pub use crate::bits64::*;
}

/// Support for the CPUID instructions.
pub mod cpuid {
    pub use raw_cpuid::*;
}

#[cfg(not(test))]
mod std {
    pub use core::fmt;
    pub use core::ops;
    pub use core::option;
}

#[cfg(all(test, feature = "vmtest"))]
extern crate klogger;
#[cfg(all(test, feature = "vmtest"))]
extern crate x86test;

#[derive(Copy, Clone, Debug, Eq, PartialEq)]
#[repr(u8)]
/// x86 Protection levels
///
/// # Note
/// This should not contain values larger than 2 bits, otherwise
/// segment descriptor code needs to be adjusted accordingly.
pub enum Ring {
    Ring0 = 0b00,
    Ring1 = 0b01,
    Ring2 = 0b10,
    Ring3 = 0b11,
}

/// Stops instruction execution and places the processor in a HALT state.
///
/// An enabled interrupt (including NMI and SMI), a debug exception, the BINIT#
/// signal, the INIT# signal, or the RESET# signal will resume execution. If an
/// interrupt (including NMI) is used to resume execution after a HLT instruction,
/// the saved instruction pointer (CS:EIP) points to the instruction following
/// the HLT instruction.
///
/// # Safety
/// Will cause a general protection fault if used outside of ring 0.
#[inline(always)]
pub unsafe fn halt() {
    asm!("hlt", options(att_syntax, nomem, nostack)); // check if preserves_flags
}

#[cfg(all(test, feature = "vmtest"))]
mod x86testing {
    use super::*;
    use x86test::*;

    #[x86test(should_halt)]
    fn should_halt() {
        unsafe { halt() }
    }

    #[x86test]
    fn should_not_halt() {}
}

/// Read Processor ID
///
/// Reads the value of the IA32_TSC_AUX MSR (address C0000103H) into the
/// destination register.
///
/// # See also
/// `IA32_TSC_AUX` can also be read calling [`crate::time::rdtscp`].
///
/// # Safety
/// May fail with #UD if rdpid is not supported (check CPUID).
#[inline(always)]
pub unsafe fn rdpid() -> u64 {
    #[cfg(target_pointer_width = "64")]
    let mut pid: u64;
    #[cfg(target_pointer_width = "32")]
    let mut pid: u32;
    asm!("rdpid {pid}", pid = out(reg) pid, options(att_syntax));
    pid.into()
}

#[cfg(all(test, feature = "utest"))]
mod test {
    use super::*;

    #[test]
    fn test_rdpid() {
        let rdpid_support = cpuid::CpuId::new()
            .get_extended_feature_info()
            .map_or(false, |finfo| finfo.has_rdpid());
        unsafe {
            if rdpid_support {
                let pid1 = rdpid();
                let pid2 = rdpid();
                // Let's hope we didn't migrate
                assert!(pid1 == pid2, "RDPID not consistent values?");
            }
        }
    }
}