Skip to main content

strat9_kernel/arch/x86_64/
mod.rs

1//! x86_64 architecture-specific code
2//!
3//! Inspired by MaestroOS `arch/x86/mod.rs`
4
5pub mod apic;
6pub mod boot_timestamp;
7pub mod cpuid;
8pub mod gdt;
9pub mod idt;
10pub mod io;
11pub mod ioapic;
12pub mod keyboard;
13pub mod keyboard_layout;
14pub mod keyboard_us;
15pub mod mouse;
16pub mod msi;
17pub mod pci;
18pub mod percpu;
19pub mod pic;
20pub mod ring3_diag;
21pub mod serial;
22pub mod smp;
23pub mod speaker;
24pub mod syscall;
25pub mod timer;
26pub mod tlb;
27pub mod tss;
28pub mod vga;
29pub mod vgabuf;
30pub mod x2apic;
31
32use core::arch::asm;
33
34/// Initialize FPU, SSE, and optionally XSAVE for the current CPU.
35pub fn init_cpu_extensions() {
36    unsafe {
37        let mut cr4: u64;
38        asm!("mov {}, cr4", out(reg) cr4, options(nomem, nostack));
39        // OSFXSR (9) + OSXMMEXCPT (10)
40        cr4 |= (1 << 9) | (1 << 10);
41
42        if cpuid::host_uses_xsave() {
43            // OSXSAVE (18) : required before xsetbv/xgetbv
44            cr4 |= 1 << 18;
45        }
46        asm!("mov cr4, {}", in(reg) cr4, options(nomem, nostack));
47
48        let mut cr0: u64;
49        asm!("mov {}, cr0", out(reg) cr0, options(nomem, nostack));
50        cr0 &= !(1 << 2); // clear EM
51        cr0 |= 1 << 1; // set MP
52        asm!("mov cr0, {}", in(reg) cr0, options(nomem, nostack));
53
54        asm!("fninit", options(nomem, nostack));
55
56        if cpuid::host_uses_xsave() {
57            let xcr0 = cpuid::host_default_xcr0();
58            xsetbv(0, xcr0);
59        }
60    }
61}
62
63/// Read an Extended Control Register (XGETBV).
64#[inline]
65pub fn xgetbv(xcr: u32) -> u64 {
66    let eax: u32;
67    let edx: u32;
68    unsafe {
69        asm!(
70            "xgetbv",
71            in("ecx") xcr,
72            out("eax") eax,
73            out("edx") edx,
74            options(nomem, nostack),
75        );
76    }
77    ((edx as u64) << 32) | eax as u64
78}
79
80/// Write an Extended Control Register (XSETBV).
81///
82/// # Safety
83/// Caller must ensure CR4.OSXSAVE is set and the value is valid for XCR0.
84#[inline]
85pub unsafe fn xsetbv(xcr: u32, value: u64) {
86    asm!(
87        "xsetbv",
88        in("ecx") xcr,
89        in("eax") value as u32,
90        in("edx") (value >> 32) as u32,
91        options(nomem, nostack),
92    );
93}
94
95/// Halt the CPU until the next interrupt
96#[inline]
97pub fn hlt() {
98    unsafe {
99        asm!("hlt", options(nomem, nostack, preserves_flags));
100    }
101}
102
103/// Disable interrupts
104#[inline]
105pub fn cli() {
106    unsafe {
107        asm!("cli", options(nomem, nostack));
108    }
109}
110
111/// Enable interrupts
112#[inline]
113pub fn sti() {
114    unsafe {
115        asm!("sti", options(nomem, nostack));
116    }
117}
118
119/// Check if interrupts are enabled
120#[inline]
121pub fn interrupts_enabled() -> bool {
122    let rflags: u64;
123    unsafe {
124        asm!("pushfq; pop {}", out(reg) rflags, options(nomem));
125    }
126    rflags & 0x200 != 0
127}
128
129/// Save RFLAGS and disable interrupts. Returns saved flags.
130///
131/// Used to protect critical sections (e.g., scheduler lock) from
132/// being interrupted by the timer, which would cause deadlock on
133/// single-core systems.
134#[inline]
135pub fn save_flags_and_cli() -> u64 {
136    let flags: u64;
137    // SAFETY: pushfq/pop reads RFLAGS, cli disables interrupts.
138    // This is safe and required for single-core mutual exclusion.
139    unsafe {
140        asm!("pushfq; pop {0}; cli", out(reg) flags);
141    }
142    flags
143}
144
145/// Restore RFLAGS (including interrupt flag) from a previous save.
146///
147/// Pairs with `save_flags_and_cli()` to restore the previous interrupt state.
148#[inline]
149pub fn restore_flags(flags: u64) {
150    // SAFETY: push/popfq restores RFLAGS to a previously-saved valid state.
151    unsafe {
152        asm!("push {0}; popfq", in(reg) flags);
153    }
154}
155
156/// Read from a Model Specific Register
157#[inline]
158pub fn rdmsr(msr: u32) -> u64 {
159    let edx: u32;
160    let eax: u32;
161    unsafe {
162        asm!(
163            "rdmsr",
164            in("ecx") msr,
165            out("edx") edx,
166            out("eax") eax,
167            options(nostack)
168        );
169    }
170    ((edx as u64) << 32) | eax as u64
171}
172
173/// Write to a Model Specific Register
174#[inline]
175pub fn wrmsr(msr: u32, val: u64) {
176    let edx = (val >> 32) as u32;
177    let eax = val as u32;
178    unsafe {
179        asm!(
180            "wrmsr",
181            in("ecx") msr,
182            in("edx") edx,
183            in("eax") eax,
184            options(nostack)
185        );
186    }
187}
188
189/// Execute CPUID instruction.
190///
191/// rbx is reserved by LLVM, so we save/restore it manually.
192#[inline]
193pub fn cpuid(leaf: u32, sub_leaf: u32) -> (u32, u32, u32, u32) {
194    let eax: u32;
195    let ebx: u32;
196    let ecx: u32;
197    let edx: u32;
198    unsafe {
199        asm!(
200            "push rbx",
201            "cpuid",
202            "mov {ebx_out:e}, ebx",
203            "pop rbx",
204            inout("eax") leaf => eax,
205            inout("ecx") sub_leaf => ecx,
206            ebx_out = out(reg) ebx,
207            out("edx") edx,
208        );
209    }
210    (eax, ebx, ecx, edx)
211}
212
213/// Read the Time Stamp Counter (TSC).
214///
215/// Returns the number of CPU cycles since reset. Available from the
216/// very first instruction : use this as the sole timing source during
217/// early boot (before APIC/PIT timers are configured).
218#[inline]
219pub fn rdtsc() -> u64 {
220    let eax: u32;
221    let edx: u32;
222    unsafe {
223        asm!("rdtsc", out("eax") eax, out("edx") edx, options(nomem, nostack));
224    }
225    ((edx as u64) << 32) | eax as u64
226}