veridian_kernel/arch/x86_64/boot.rs
1//! x86_64 boot entry point using the `bootloader` crate.
2//!
3//! Receives `BootInfo` from the bootloader and performs early hardware
4//! initialization including serial setup, VGA output, and kernel heap init.
5
6use bootloader_api::BootInfo;
7
8// Store boot info for later use (mutable reference for 0.11+ API)
9//
10// SAFETY JUSTIFICATION: This static mut is intentionally kept because:
11// 1. The bootloader crate API provides boot info as &'static mut BootInfo
12// 2. This must be stored before any kernel initialization (pre-heap)
13// 3. Written once during entry_point! macro callback, read-only afterwards
14// 4. Cannot use OnceLock as it requires heap (alloc) which isn't available yet
15// 5. The bootloader guarantees the reference is valid for the kernel lifetime
16#[allow(static_mut_refs)]
17pub static mut BOOT_INFO: Option<&'static mut BootInfo> = None;
18
19// Early initialization that must happen before kernel_main
20pub fn early_boot_init() {
21 // SAFETY: The cli instruction disables hardware interrupts. Required during
22 // early boot to prevent interrupt handlers from firing before the IDT is set
23 // up.
24 unsafe {
25 core::arch::asm!("cli", options(nomem, nostack));
26 }
27
28 // SAFETY: 0xb8000 is the standard VGA text buffer address on x86 PCs.
29 // write_volatile ensures the write is not optimized away. Always mapped
30 // during early boot.
31 unsafe {
32 let vga = 0xb8000 as *mut u16;
33 // Write 'B' in white on black
34 vga.write_volatile(0x0F42);
35 }
36
37 // SAFETY: Direct I/O port writes to COM1 (0x3F8) to initialize the serial
38 // port for early boot debugging. The 16550 UART initialization sequence
39 // (disable IRQs, set baud rate, configure line control) is well-defined.
40 unsafe {
41 // Direct serial port initialization at 0x3F8
42 let base: u16 = 0x3F8;
43
44 // Disable interrupts
45 outb(base + 1, 0x00);
46 // Enable DLAB
47 outb(base + 3, 0x80);
48 // Set divisor to 3 (38400 baud)
49 outb(base, 0x03);
50 outb(base + 1, 0x00);
51 // 8 bits, no parity, one stop bit
52 outb(base + 3, 0x03);
53 // Enable FIFO
54 outb(base + 2, 0xC7);
55 // Enable IRQs, set RTS/DSR
56 outb(base + 4, 0x0B);
57
58 // Write boot marker
59 write_str(base, "BOOT_ENTRY\n");
60 }
61}
62
63/// Framebuffer information extracted from BootInfo.
64pub struct BootFramebufferInfo {
65 pub buffer: *mut u8,
66 pub width: usize,
67 pub height: usize,
68 /// Stride in bytes (bytes per scanline)
69 pub stride: usize,
70 /// Bytes per pixel (typically 4 for BGRA)
71 pub bpp: usize,
72 /// Pixel format (BGR or RGB)
73 pub is_bgr: bool,
74}
75
76/// Extract framebuffer information from the UEFI boot info.
77///
78/// Returns `None` if BootInfo is unavailable or has no framebuffer.
79/// The returned pointer is already a valid virtual address (the bootloader
80/// maps the framebuffer with `Mapping::Dynamic`).
81pub fn get_framebuffer_info() -> Option<BootFramebufferInfo> {
82 // SAFETY: BOOT_INFO is a static mut written once during early boot
83 // (in the entry_point! callback) and read-only afterwards. We are in
84 // single-threaded kernel init context (Stage 2+, before scheduler).
85 #[allow(static_mut_refs)]
86 let boot_info = unsafe { BOOT_INFO.as_mut()? };
87 let fb = boot_info.framebuffer.as_mut()?;
88 let info = fb.info();
89 let buffer_ptr = fb.buffer_mut().as_mut_ptr();
90 Some(BootFramebufferInfo {
91 buffer: buffer_ptr,
92 width: info.width,
93 height: info.height,
94 stride: info.stride * info.bytes_per_pixel,
95 bpp: info.bytes_per_pixel,
96 is_bgr: matches!(info.pixel_format, bootloader_api::info::PixelFormat::Bgr),
97 })
98}
99
100// I/O port functions: delegate to the canonical implementations in
101// the parent module (arch/x86_64/mod.rs) to avoid duplication.
102use super::{inb, outb};
103
104#[inline]
105unsafe fn write_str(base: u16, s: &str) {
106 for byte in s.bytes() {
107 // Wait for transmit buffer to be empty
108 loop {
109 let status = inb(base + 5);
110 if (status & 0x20) != 0 {
111 break;
112 }
113 }
114 // Send byte
115 outb(base, byte);
116 }
117}