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