hal_x86_64/
cpu.rs

1use core::{arch::asm, convert::Infallible, fmt, mem};
2use mycelium_util::bits;
3
4pub mod entropy;
5pub mod intrinsics;
6#[cfg(feature = "alloc")]
7pub mod local;
8pub mod msr;
9pub use self::msr::Msr;
10
11#[repr(transparent)]
12pub struct Port {
13    num: u16,
14}
15
16/// Describes which descriptor table ([GDT], [LDT], or [IDT]) a selector references.
17///
18/// [GDT]: crate::segment::Gdt
19/// [LDT]: https://en.wikipedia.org/wiki/Global_Descriptor_Table#Local_Descriptor_Table
20/// [IDT]: crate::interrupt::Idt
21#[derive(Copy, Clone, Debug, Eq, PartialEq)]
22pub enum DescriptorTable {
23    /// The selector references a descriptor in the [Global Descriptor Table
24    /// (GDT)][gdt].
25    ///
26    /// [gdt]: crate::segment::Gdt
27    Gdt,
28
29    /// The selector references an entry in a [Local Descriptor Table
30    /// (LDT)][ldt]
31    ///
32    /// [ldt]: https://en.wikipedia.org/wiki/Global_Descriptor_Table#Local_Descriptor_Table
33    Ldt,
34
35    /// The selector references an interrupt handler in the [Interrupt
36    /// Descriptor Table(IDT)][idt].
37    ///
38    /// [idt]: crate::interrupt::Idt
39    Idt,
40}
41
42#[derive(Copy, Clone, Debug, Eq, PartialEq)]
43#[repr(u8)]
44pub enum Ring {
45    Ring0 = 0b00,
46    Ring1 = 0b01,
47    Ring2 = 0b10,
48    Ring3 = 0b11,
49}
50
51#[repr(C, packed)]
52pub(crate) struct DtablePtr {
53    limit: u16,
54    base: *const (),
55}
56
57/// An error indicating that a given CPU feature source is not supported on this
58/// CPU.
59#[derive(Copy, Clone, Debug, Eq, PartialEq)]
60pub struct FeatureNotSupported(&'static str);
61
62/// Halt the CPU.
63///
64/// This disables interrupts and performs the `hlt` instruction in a loop,
65/// forever.
66///
67/// # Notes
68///
69/// This halts the CPU.
70#[inline(always)]
71pub fn halt() -> ! {
72    unsafe {
73        // safety: this does exactly what it says on the tin lol
74        intrinsics::cli();
75        loop {
76            intrinsics::hlt();
77        }
78    }
79}
80
81/// Wait for an interrupt in a spin loop.
82///
83/// This is distinct from `core::hint::spin_loop`, as it is intended
84/// specifically for waiting for an interrupt, rather than progress from another
85/// thread. This should be called on each iteration of a loop that waits on a condition
86/// set by an interrupt handler.
87///
88/// This function will execute one [`intrinsics::sti`] instruction to enable interrupts
89/// followed by one [`intrinsics::hlt`] instruction to halt the CPU.
90#[inline(always)]
91pub fn wait_for_interrupt() {
92    unsafe {
93        intrinsics::sti();
94        intrinsics::hlt();
95    }
96}
97
98// === impl Port ===
99
100impl fmt::Debug for Port {
101    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
102        let Self { num } = self;
103        f.debug_struct("Port")
104            .field("num", &format_args!("{num:#02x}"))
105            .finish()
106    }
107}
108
109impl Port {
110    pub const fn at(address: u16) -> Self {
111        Port { num: address }
112    }
113
114    /// # Safety
115    ///
116    /// Reading from a CPU port is unsafe.
117    pub unsafe fn readb(&self) -> u8 {
118        let result: u8;
119        asm!("in al, dx", in("dx") self.num, out("al") result);
120        result
121    }
122
123    /// # Safety
124    ///
125    /// Writing to a CPU port is unsafe.
126    pub unsafe fn writeb(&self, value: u8) {
127        asm!("out dx, al", in("dx") self.num, in("al") value)
128    }
129
130    /// # Safety
131    ///
132    /// Reading from a CPU port is unsafe.
133    pub unsafe fn readl(&self) -> u32 {
134        let result: u32;
135        asm!("in eax, dx", in("dx") self.num, out("eax") result);
136        result
137    }
138
139    /// # Safety
140    ///
141    /// Writing to a CPU port is unsafe.
142    pub unsafe fn writel(&self, value: u32) {
143        asm!("out dx, eax", in("dx") self.num, in("eax") value)
144    }
145}
146
147// === impl DescriptorTable ===
148
149impl fmt::Display for DescriptorTable {
150    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
151        match self {
152            Self::Gdt => f.pad("GDT"),
153            Self::Ldt => f.pad("LDT"),
154            Self::Idt => f.pad("IDT"),
155        }
156    }
157}
158
159impl bits::FromBits<u16> for DescriptorTable {
160    type Error = Infallible;
161    const BITS: u32 = 2;
162    fn try_from_bits(bits: u16) -> Result<Self, Self::Error> {
163        Ok(match bits {
164            0b00 => Self::Gdt,
165            0b01 => Self::Idt,
166            0b10 => Self::Ldt,
167            0b11 => Self::Idt,
168            _ => unreachable!("only 2 bits should be unpacked!"),
169        })
170    }
171
172    fn into_bits(self) -> u16 {
173        todo!("eliza")
174    }
175}
176
177// === impl Ring ===
178
179impl Ring {
180    pub fn from_u8(u: u8) -> Self {
181        match u {
182            0b00 => Ring::Ring0,
183            0b01 => Ring::Ring1,
184            0b10 => Ring::Ring2,
185            0b11 => Ring::Ring3,
186            bits => panic!("invalid ring {bits:#02b}"),
187        }
188    }
189}
190
191impl bits::FromBits<u64> for Ring {
192    const BITS: u32 = 2;
193    type Error = core::convert::Infallible;
194
195    fn try_from_bits(u: u64) -> Result<Self, Self::Error> {
196        Ok(Self::from_u8(u as u8))
197    }
198
199    fn into_bits(self) -> u64 {
200        self as u8 as u64
201    }
202}
203
204impl bits::FromBits<u16> for Ring {
205    const BITS: u32 = 2;
206    type Error = core::convert::Infallible;
207
208    fn try_from_bits(u: u16) -> Result<Self, Self::Error> {
209        Ok(Self::from_u8(u as u8))
210    }
211
212    fn into_bits(self) -> u16 {
213        self as u8 as u16
214    }
215}
216
217impl bits::FromBits<u8> for Ring {
218    const BITS: u32 = 2;
219    type Error = core::convert::Infallible;
220
221    fn try_from_bits(u: u8) -> Result<Self, Self::Error> {
222        Ok(Self::from_u8(u))
223    }
224
225    fn into_bits(self) -> u8 {
226        self as u8
227    }
228}
229
230// === impl DtablePtr ===
231
232impl DtablePtr {
233    pub(crate) fn new<T>(t: &'static T) -> Self {
234        unsafe {
235            // safety: the `'static` lifetime ensures the pointed dtable is
236            // never going away
237            Self::new_unchecked(t)
238        }
239    }
240
241    pub(crate) unsafe fn new_unchecked<T>(t: &T) -> Self {
242        let limit = (mem::size_of::<T>() - 1) as u16;
243        let base = t as *const _ as *const ();
244
245        Self { limit, base }
246    }
247}
248
249impl fmt::Debug for DtablePtr {
250    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
251        // avoid creating misaligned references by moving these i guess? idk why
252        // rustc is okay with this but i'll trust it.
253        let Self { limit, base } = *self;
254        f.debug_struct("DtablePtr")
255            .field("base", &format_args!("{base:0p}",))
256            .field("limit", &limit)
257            .finish()
258    }
259}
260
261// === impl FeatureNotSupported ===
262
263impl fmt::Display for FeatureNotSupported {
264    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
265        write!(f, "this CPU does not support {}", self.0)
266    }
267}
268
269impl FeatureNotSupported {
270    pub fn feature_name(&self) -> &'static str {
271        self.0
272    }
273
274    pub(crate) fn new(feature_name: &'static str) -> Self {
275        Self(feature_name)
276    }
277}