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}