⚠️ VeridianOS Kernel Documentation - This is low-level kernel code. All functions are unsafe unless explicitly marked otherwise. no_std

veridian_kernel/arch/x86_64/
mod.rs

1//! x86_64 architecture support.
2//!
3//! Provides hardware initialization (GDT, IDT, PIC, APIC), interrupt control,
4//! serial I/O (COM1 at 0x3F8), VGA text output, and I/O port primitives
5//! for the x86_64 platform.
6
7pub mod acpi;
8pub mod acpi_pm;
9pub mod apic;
10pub mod boot;
11pub mod bootstrap;
12pub mod context;
13pub mod cpufreq;
14pub mod dpms;
15pub mod early_serial;
16pub mod entry;
17pub mod gdt;
18pub mod idt;
19pub mod kpti;
20pub mod mmu;
21pub mod msr;
22pub mod multiboot;
23pub mod pat;
24pub mod rtc;
25pub mod serial;
26pub mod syscall;
27pub mod timer;
28pub mod usermode;
29pub mod vga;
30
31/// Called from bootstrap on x86_64 via `crate::arch::init()`.
32pub fn init() {
33    // SAFETY: The cli instruction disables hardware interrupts. This is required
34    // during initialization to prevent interrupt handlers from firing before the
35    // IDT and PIC are properly configured. nomem/nostack confirm no memory access.
36    unsafe {
37        core::arch::asm!("cli", options(nomem, nostack));
38    }
39
40    println!("[ARCH] Starting GDT init...");
41    gdt::init();
42    println!("[ARCH] GDT initialized");
43
44    // Initialize SYSCALL/SYSRET support (must be after GDT init so that
45    // STAR MSR references valid user-mode selectors in the loaded GDT)
46    println!("[ARCH] Initializing SYSCALL/SYSRET...");
47    syscall::init_syscall();
48    println!("[ARCH] SYSCALL/SYSRET initialized");
49
50    println!("[ARCH] Starting IDT init...");
51    idt::init();
52    println!("[ARCH] IDT initialized");
53
54    // Initialize PIC (8259) before enabling interrupts
55    println!("[ARCH] Initializing PIC...");
56    // SAFETY: I/O port writes to the 8259 PIC (ports 0x20/0x21 for PIC1,
57    // 0xA0/0xA1 for PIC2) are required to initialize the interrupt controller.
58    // The initialization sequence (ICW1-ICW4) is well-defined by the 8259 spec.
59    // All interrupts are masked (0xFF) at the end to prevent spurious IRQs.
60    unsafe {
61        use x86_64::instructions::port::Port;
62
63        // Initialize PIC manually to ensure interrupts stay masked
64        const PIC1_COMMAND: u16 = 0x20;
65        const PIC1_DATA: u16 = 0x21;
66        const PIC2_COMMAND: u16 = 0xA0;
67        const PIC2_DATA: u16 = 0xA1;
68
69        let mut pic1_cmd = Port::<u8>::new(PIC1_COMMAND);
70        let mut pic1_data = Port::<u8>::new(PIC1_DATA);
71        let mut pic2_cmd = Port::<u8>::new(PIC2_COMMAND);
72        let mut pic2_data = Port::<u8>::new(PIC2_DATA);
73
74        // Start initialization sequence
75        pic1_cmd.write(0x11);
76        pic2_cmd.write(0x11);
77
78        // Set vector offsets
79        pic1_data.write(32);
80        pic2_data.write(40);
81
82        // Set cascading
83        pic1_data.write(4);
84        pic2_data.write(2);
85
86        // Set 8086 mode
87        pic1_data.write(0x01);
88        pic2_data.write(0x01);
89
90        // Mask all interrupts
91        pic1_data.write(0xFF);
92        pic2_data.write(0xFF);
93    }
94    println!("[ARCH] PIC initialized with all interrupts masked");
95
96    println!("[ARCH] Starting MMU init...");
97    mmu::init();
98    println!("[ARCH] MMU initialized");
99
100    // Initialize Local APIC + I/O APIC (additive to PIC -- PIC remains as
101    // fallback). APIC init is non-fatal: if it fails the kernel continues
102    // with PIC-only interrupt routing.
103    println!("[ARCH] Initializing APIC...");
104    match apic::init() {
105        Ok(()) => println!("[ARCH] APIC initialized"),
106        Err(e) => println!("[ARCH] APIC init skipped: {}", e),
107    }
108
109    // Parse ACPI tables (MADT, MCFG) from UEFI firmware for hardware topology.
110    // Non-fatal: falls back to defaults if RSDP unavailable.
111    println!("[ARCH] Parsing ACPI tables...");
112    match acpi::init() {
113        Ok(()) => println!("[ARCH] ACPI tables parsed"),
114        Err(e) => println!("[ARCH] ACPI init skipped: {}", e),
115    }
116
117    // Calibrate and start the APIC timer for preemptive scheduling.
118    // Requires APIC to be initialized. Non-fatal: falls back to PIC timer
119    // (which must be explicitly enabled elsewhere) if calibration fails.
120    if apic::is_initialized() {
121        println!("[ARCH] Calibrating APIC timer...");
122        match apic::calibrate_timer() {
123            Ok(tpm) => {
124                println!("[ARCH] APIC timer calibrated: {} ticks/ms", tpm);
125                // Start periodic timer at ~1000 Hz (1ms tick) for scheduler preemption.
126                match apic::start_timer(1000) {
127                    Ok(()) => println!("[ARCH] APIC timer started at 1000Hz"),
128                    Err(e) => println!("[ARCH] APIC timer start failed: {}", e),
129                }
130            }
131            Err(e) => println!("[ARCH] APIC timer calibration failed: {}", e),
132        }
133    }
134
135    // Enable interrupts now that APIC timer is configured and IDT handlers
136    // are registered. The APIC timer fires vector 48 for scheduler preemption.
137    // PIC interrupts remain masked (keyboard/timer enabled separately by
138    // bootstrap when needed).
139    println!("[ARCH] Enabling interrupts");
140    enable_interrupts();
141}
142
143/// Halt the CPU. Used by panic/shutdown paths via `crate::arch::halt()`.
144pub fn halt() -> ! {
145    use x86_64::instructions::hlt;
146    interrupts::disable();
147    loop {
148        hlt();
149    }
150}
151
152/// Enable hardware interrupts.
153pub fn enable_interrupts() {
154    x86_64::instructions::interrupts::enable();
155}
156
157/// Unmask the keyboard IRQ (IRQ1) on PIC1.
158///
159/// Reads the current PIC1 data mask, clears bit 1, and writes it back.
160/// This allows the keyboard interrupt (vector 33) to fire.
161pub fn enable_keyboard_irq() {
162    // SAFETY: Reading and writing the PIC1 data port (0x21) to unmask
163    // IRQ1 (keyboard). This is a standard PIC operation.
164    unsafe {
165        use x86_64::instructions::port::Port;
166        let mut pic1_data = Port::<u8>::new(0x21);
167        let mask = pic1_data.read();
168        pic1_data.write(mask & !0x02); // Clear bit 1 (IRQ1 = keyboard)
169    }
170}
171
172/// Unmask the timer IRQ (IRQ0) on PIC1.
173pub fn enable_timer_irq() {
174    // SAFETY: Reading and writing PIC1 data port to unmask IRQ0.
175    unsafe {
176        use x86_64::instructions::port::Port;
177        let mut pic1_data = Port::<u8>::new(0x21);
178        let mask = pic1_data.read();
179        pic1_data.write(mask & !0x01); // Clear bit 0 (IRQ0 = timer)
180    }
181}
182
183pub fn disable_interrupts() -> impl Drop {
184    struct InterruptGuard {
185        was_enabled: bool,
186    }
187
188    impl Drop for InterruptGuard {
189        fn drop(&mut self) {
190            if self.was_enabled {
191                x86_64::instructions::interrupts::enable();
192            }
193        }
194    }
195
196    let was_enabled = x86_64::instructions::interrupts::are_enabled();
197    x86_64::instructions::interrupts::disable();
198    InterruptGuard { was_enabled }
199}
200
201pub fn idle() {
202    x86_64::instructions::hlt();
203}
204
205/// Speculation barrier to mitigate Spectre-style attacks.
206/// Uses LFENCE which serializes instruction execution on Intel/AMD.
207#[inline(always)]
208pub fn speculation_barrier() {
209    // SAFETY: lfence is a serializing instruction that prevents speculative
210    // execution of subsequent instructions until all prior instructions
211    // complete. No side effects beyond pipeline serialization.
212    unsafe {
213        core::arch::asm!("lfence", options(nostack, nomem, preserves_flags));
214    }
215}
216
217pub fn serial_init() -> uart_16550::SerialPort {
218    // SAFETY: SerialPort::new(0x3F8) creates a serial port handle for COM1
219    // at the standard I/O base address. The address is well-known and the
220    // port is initialized immediately after construction.
221    let mut serial_port = unsafe { uart_16550::SerialPort::new(0x3F8) };
222    serial_port.init();
223    serial_port
224}
225
226/// Write a byte to an x86_64 I/O port.
227///
228/// # Safety
229/// The caller must ensure `port` is a valid I/O port address for the
230/// intended device. Writing to an incorrect port can cause undefined
231/// hardware behavior.
232pub unsafe fn outb(port: u16, value: u8) {
233    x86_64::instructions::port::Port::new(port).write(value);
234}
235
236/// Read a byte from an x86_64 I/O port.
237///
238/// # Safety
239/// The caller must ensure `port` is a valid I/O port address for the
240/// intended device. Reading from an incorrect port may return garbage
241/// or trigger hardware side effects.
242pub unsafe fn inb(port: u16) -> u8 {
243    x86_64::instructions::port::Port::new(port).read()
244}
245
246/// Write a 16-bit word to an x86_64 I/O port.
247///
248/// # Safety
249/// The caller must ensure `port` is a valid I/O port address for the
250/// intended device and that the device expects a 16-bit write.
251pub unsafe fn outw(port: u16, value: u16) {
252    x86_64::instructions::port::Port::new(port).write(value);
253}
254
255/// Read a 16-bit word from an x86_64 I/O port.
256///
257/// # Safety
258/// The caller must ensure `port` is a valid I/O port address for the
259/// intended device and that the device produces valid 16-bit reads.
260pub unsafe fn inw(port: u16) -> u16 {
261    x86_64::instructions::port::Port::new(port).read()
262}
263
264/// Write a 32-bit dword to an x86_64 I/O port.
265///
266/// # Safety
267/// The caller must ensure `port` is a valid I/O port address for the
268/// intended device and that the device expects a 32-bit write.
269pub unsafe fn outl(port: u16, value: u32) {
270    x86_64::instructions::port::Port::new(port).write(value);
271}
272
273/// Read a 32-bit dword from an x86_64 I/O port.
274///
275/// # Safety
276/// The caller must ensure `port` is a valid I/O port address for the
277/// intended device and that the device produces valid 32-bit reads.
278pub unsafe fn inl(port: u16) -> u32 {
279    x86_64::instructions::port::Port::new(port).read()
280}
281
282/// Kernel heap start address (mapped by bootloader 0.9)
283pub const HEAP_START: usize = 0x444444440000;
284
285/// Flush TLB for a specific virtual address. Called via
286/// `crate::arch::tlb_flush_address()`.
287pub fn tlb_flush_address(addr: u64) {
288    // SAFETY: `invlpg` invalidates the TLB entry for the page containing the
289    // given virtual address. Privileged, no side effects beyond TLB.
290    unsafe {
291        core::arch::asm!("invlpg [{}]", in(reg) addr);
292    }
293}
294
295/// Flush entire TLB. Called via `crate::arch::tlb_flush_all()`.
296pub fn tlb_flush_all() {
297    // SAFETY: Reloading CR3 with its current value flushes all non-global TLB
298    // entries. Privileged, no memory side effects.
299    unsafe {
300        let cr3: u64;
301        core::arch::asm!("mov {}, cr3", out(reg) cr3);
302        core::arch::asm!("mov cr3, {}", in(reg) cr3);
303    }
304}
305
306mod interrupts {
307    /// Disable hardware interrupts. Called from `halt()`.
308    pub fn disable() {
309        x86_64::instructions::interrupts::disable();
310    }
311}