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