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

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}