kernel/cpu/
mod.rs

1use crate::process::ProcessContext;
2
3use self::{
4    gdt::{GlobalDescriptorTablePointer, SegmentSelector},
5    idt::InterruptDescriptorTablePointer,
6};
7
8pub mod gdt;
9pub mod idt;
10pub mod interrupts;
11
12const MAX_CPUS: usize = 8;
13
14pub mod flags {
15    pub const IF: u64 = 1 << 9;
16}
17
18#[allow(dead_code)]
19pub mod msr {
20    pub const APIC_BASE: u32 = 0x1b;
21    pub const EFER: u32 = 0xc0000080;
22
23    pub unsafe fn read(reg: u32) -> u64 {
24        let (eax, edx): (u32, u32);
25        core::arch::asm!("rdmsr", in("ecx") reg, out("eax") eax, out("edx") edx, options(readonly, nostack, preserves_flags));
26        ((edx as u64) << 32) | (eax as u64)
27    }
28
29    pub unsafe fn write(reg: u32, val: u64) {
30        let eax = val as u32;
31        let edx = (val >> 32) as u32;
32        core::arch::asm!("wrmsr", in("ecx") reg, in("eax") eax, in("edx") edx, options(readonly, nostack, preserves_flags));
33    }
34}
35
36#[allow(dead_code)]
37pub mod cpuid {
38    pub const FN_FEAT: u32 = 1;
39
40    pub const FEAT_EDX_TSC: u32 = 1 << 4;
41    pub const FEAT_EDX_APIC: u32 = 1 << 9;
42
43    #[macro_export]
44    macro_rules! cpuid {
45        ($rax:expr) => {
46            ::core::arch::x86_64::__cpuid_count($rax, 0)
47        };
48        ($rax:expr, $rcx:expr) => {
49            ::core::arch::x86_64::__cpuid_count($rax, $rcx)
50        };
51    }
52    #[allow(unused_imports)]
53    pub use cpuid;
54}
55
56static mut CPUS: [Cpu; MAX_CPUS] = [Cpu::empty(); MAX_CPUS];
57
58#[derive(Debug, Clone, Copy)]
59pub struct Cpu {
60    // index of myself inside `CPUS`
61    pub id: usize,
62    apic_id: u8,
63    old_interrupt_enable: bool,
64    // number of times we have called `cli`
65    n_cli: usize,
66
67    // saved context, when switching from kernel to user and vice versa
68    // if there is a value here, it indicates that we are running a processing now
69    // either about to run a process, or in the middle
70    //
71    // i.e., if this is `None`, then we are in the kernel and free to take a process
72    pub context: Option<ProcessContext>,
73    // the process id of the current process
74    pub process_id: u64,
75    pub scheduling: bool,
76}
77
78impl Cpu {
79    const fn empty() -> Self {
80        Self {
81            id: 0,
82            apic_id: 0,
83            old_interrupt_enable: false,
84            n_cli: 0,
85            context: None,
86            process_id: 0,
87            scheduling: false,
88        }
89    }
90
91    fn init(&mut self, id: usize, apic_id: u8) {
92        self.id = id;
93        self.apic_id = apic_id;
94    }
95
96    pub fn push_cli(&mut self) {
97        if self.n_cli == 0 {
98            let rflags = unsafe { rflags() };
99            let old_interrupt_flag = rflags & flags::IF != 0;
100            unsafe { clear_interrupts() };
101            self.old_interrupt_enable = old_interrupt_flag;
102        }
103        // re-read the flags
104        let rflags = unsafe { rflags() };
105        assert!(self.n_cli < usize::MAX);
106        assert_eq!(rflags & flags::IF, 0);
107        self.n_cli += 1;
108    }
109
110    pub fn pop_cli(&mut self) {
111        let rflags = unsafe { rflags() };
112        assert!(self.n_cli > 0);
113        assert_eq!(rflags & flags::IF, 0);
114
115        self.n_cli -= 1;
116        if self.n_cli == 0 && self.old_interrupt_enable {
117            unsafe { set_interrupts() };
118        }
119    }
120
121    pub fn interrupts_disabled(&self) -> bool {
122        unsafe { rflags() & flags::IF == 0 }
123    }
124
125    pub fn n_cli(&self) -> usize {
126        self.n_cli
127    }
128}
129
130pub fn cpu() -> &'static mut Cpu {
131    // TODO: use thread local to get the current cpu
132    unsafe { &mut CPUS[0] }
133}
134
135pub unsafe fn rflags() -> u64 {
136    let rflags: u64;
137    core::arch::asm!("pushfq; pop {0:r}", out(reg) rflags, options(nostack, preserves_flags));
138    rflags
139}
140
141unsafe fn outb(port: u16, val: u8) {
142    core::arch::asm!("out dx, al", in("al") val, in("dx") port, options(readonly, nostack, preserves_flags));
143}
144
145unsafe fn inb(port: u16) -> u8 {
146    let val: u8;
147    core::arch::asm!("in al, dx", out("al") val, in("dx") port, options(readonly, nostack, preserves_flags));
148    val
149}
150
151unsafe fn inw(port: u16) -> u16 {
152    let val: u16;
153    core::arch::asm!("in ax, dx", out("ax") val, in("dx") port, options(readonly, nostack, preserves_flags));
154    val
155}
156
157unsafe fn outw(port: u16, val: u16) {
158    core::arch::asm!("out dx, ax", in("ax") val, in("dx") port, options(readonly, nostack, preserves_flags));
159}
160
161unsafe fn outd(port: u16, val: u32) {
162    core::arch::asm!("out dx, eax", in("eax") val, in("dx") port, options(readonly, nostack, preserves_flags));
163}
164
165unsafe fn ind(port: u16) -> u32 {
166    let val: u32;
167    core::arch::asm!("in eax, dx", out("eax") val, in("dx") port, options(readonly, nostack, preserves_flags));
168    val
169}
170
171pub trait IoPortInt {
172    fn io_out(port: u16, val: Self);
173    fn io_in(port: u16) -> Self;
174}
175
176impl IoPortInt for u8 {
177    fn io_out(port: u16, val: Self) {
178        unsafe { outb(port, val) }
179    }
180    fn io_in(port: u16) -> Self {
181        unsafe { inb(port) }
182    }
183}
184
185impl IoPortInt for u16 {
186    fn io_out(port: u16, val: Self) {
187        unsafe { outw(port, val) }
188    }
189    fn io_in(port: u16) -> Self {
190        unsafe { inw(port) }
191    }
192}
193
194impl IoPortInt for u32 {
195    fn io_out(port: u16, val: Self) {
196        unsafe { outd(port, val) }
197    }
198    fn io_in(port: u16) -> Self {
199        unsafe { ind(port) }
200    }
201}
202
203pub unsafe fn io_out<T: IoPortInt>(port: u16, val: T) {
204    T::io_out(port, val);
205}
206
207pub unsafe fn io_in<T: IoPortInt>(port: u16) -> T {
208    T::io_in(port)
209}
210
211pub unsafe fn clear_interrupts() {
212    core::arch::asm!("cli", options(nomem, nostack, preserves_flags));
213}
214
215pub unsafe fn set_interrupts() {
216    core::arch::asm!("sti", options(nomem, nostack, preserves_flags));
217}
218
219#[allow(dead_code)]
220pub unsafe fn get_cr0() -> u64 {
221    let cr0: u64;
222    core::arch::asm!("mov {0:r}, cr0", out(reg) cr0, options(readonly, nostack, preserves_flags));
223    cr0
224}
225
226#[allow(dead_code)]
227pub unsafe fn set_cr0(cr0: u64) {
228    core::arch::asm!("mov cr0, rax", in("rax") cr0, options(nomem, nostack, preserves_flags));
229}
230
231pub unsafe fn set_cr3(cr3: u64) {
232    core::arch::asm!("mov cr3, rax", in("rax") cr3, options(nomem, nostack, preserves_flags));
233}
234
235pub unsafe fn get_cr3() -> u64 {
236    let cr3: u64;
237    core::arch::asm!("mov {0:r}, cr3", out(reg) cr3, options(readonly, nostack, preserves_flags));
238    cr3
239}
240
241#[allow(dead_code)]
242pub unsafe fn get_cr4() -> u64 {
243    let cr4: u64;
244    core::arch::asm!("mov {0:r}, cr4", out(reg) cr4, options(readonly, nostack, preserves_flags));
245    cr4
246}
247
248#[allow(dead_code)]
249pub unsafe fn set_cr4(cr4: u64) {
250    core::arch::asm!("mov cr4, rax", in("rax") cr4, options(nomem, nostack, preserves_flags));
251}
252
253/// SAFETY: the data pointed to by `gdtr` must be static and never change
254unsafe fn lgdt(gdtr: &GlobalDescriptorTablePointer) {
255    core::arch::asm!("lgdt [rax]", in("rax") gdtr, options(readonly, nostack, preserves_flags));
256}
257
258/// SAFETY: the data pointed to by `ldtr` must be static and never change
259unsafe fn lidt(ldtr: &InterruptDescriptorTablePointer) {
260    core::arch::asm!("lidt [rax]", in("rax") ldtr, options(readonly, nostack, preserves_flags));
261}
262
263unsafe fn ltr(tr: SegmentSelector) {
264    core::arch::asm!("ltr ax", in("ax") tr.0, options(nomem, nostack, preserves_flags));
265}
266
267unsafe fn set_cs(cs: SegmentSelector) {
268    core::arch::asm!(
269        "push {0:r}",
270        // this is not 0x1f, it is `2-forward`,
271        // which gives the offset of the nearest `2:` label
272        "lea {tmp}, [rip + 2f]",
273        "push {tmp}",
274        "retfq",
275        "2:",
276        in(reg) cs.0, tmp=lateout(reg) _, options(preserves_flags));
277}
278
279unsafe fn set_data_segments(ds: SegmentSelector) {
280    core::arch::asm!(
281        "mov ds, {0:r}",
282        "mov es, {0:r}",
283        "mov ss, {0:r}",
284        "mov fs, {0:r}",
285        "mov gs, {0:r}",
286        in(reg) ds.0, options(preserves_flags));
287}
288
289fn get_cs() -> u16 {
290    let cs: u16;
291    unsafe {
292        core::arch::asm!("mov {0:r}, cs", out(reg) cs, options(readonly, nostack, preserves_flags));
293    }
294    cs
295}
296
297pub unsafe fn halt() {
298    core::arch::asm!("hlt", options(nomem, nostack, preserves_flags));
299}
300
301pub unsafe fn invalidate_tlp(virtual_address: u64) {
302    core::arch::asm!("invlpg [{0}]", in(reg) virtual_address);
303}
304
305#[allow(dead_code)]
306pub unsafe fn read_tsc() -> u64 {
307    let (low, high): (u32, u32);
308    core::arch::asm!("rdtsc", out("eax") low, out("edx") high, options(nomem, nostack, preserves_flags));
309    ((high as u64) << 32) | (low as u64)
310}
311
312#[macro_export]
313macro_rules! rip {
314    () => {
315        {
316            let rip: u64;
317            unsafe {
318                core::arch::asm!("lea {0:r}, [rip]", out(reg) rip, options(nomem, nostack, preserves_flags));
319            }
320            rip
321        }
322    };
323}
324#[allow(unused_imports)]
325pub use rip;
326
327#[macro_export]
328macro_rules! rbp {
329    () => {
330        {
331            let rbp: u64;
332            unsafe {
333                core::arch::asm!("mov {0:r}, rbp", out(reg) rbp, options(nomem, nostack, preserves_flags));
334            }
335            rbp
336        }
337    };
338}
339#[allow(unused_imports)]
340pub use rbp;
341
342#[macro_export]
343macro_rules! rsp {
344    () => {
345        {
346            let rsp: u64;
347            unsafe {
348                core::arch::asm!("mov {0:r}, rsp", out(reg) rsp, options(nomem, nostack, preserves_flags));
349            }
350            rsp
351        }
352    };
353}
354#[allow(unused_imports)]
355pub use rsp;