Skip to main content

strat9_kernel/boot/
dtb_boot.rs

1//! Boot protocol entry point.
2//!
3//! This module handles the kernel entry from the bootloader or PVH stub.
4//! The bootloader passes a pointer to a [`strat9_abi::boot::KernelArgs`] structure.
5//! PVH boot passes null : a minimal KernelArgs is built internally.
6//!
7//! # Boot flow
8//!
9//! ```text
10//! boot64.S (_start)
11//!   => save KernelArgs pointer, setup 8 KB bootstrap stack, clear BSS
12//!   => call kmain(args_ptr)
13//!     => enable SSE/OSXSAVE (CPU features)
14//!     => early serial output
15//!     => read KernelArgs from pointer (bootloader) or build_minimal_args (PVH)
16//!     => call crate::kernel_main(args)
17//!       => (buddy allocator init)
18//!       => allocate 256 KB kernel stack
19//!       => switch_stack(new_stack, continue_init)
20//! ```
21
22use crate::boot::fdt;
23
24/// Very early serial output using raw COM1 port I/O.
25/// Safe to call before any kernel subsystem is initialized.
26#[inline(always)]
27unsafe fn early_print(s: &[u8]) {
28    let thr: u16 = 0x3F8;
29    let lsr: u16 = 0x3F8 + 5;
30    for &b in s {
31        loop {
32            let status: u8;
33            core::arch::asm!("in al, dx", out("al") status, in("dx") lsr, options(nomem, nostack, preserves_flags));
34            if status & 0x20 != 0 {
35                break;
36            }
37        }
38        core::arch::asm!("out dx, al", in("dx") thr, in("al") b, options(nomem, nostack, preserves_flags));
39    }
40}
41
42/// Write a u64 as hex to COM1 (no alloc, no fmt traits).
43unsafe fn early_print_hex(mut val: u64) {
44    const HEX: &[u8; 16] = b"0123456789abcdef";
45    if val == 0 {
46        early_print(b"0");
47        return;
48    }
49    let mut buf = [0u8; 16];
50    let mut i = 16;
51    while val > 0 {
52        i -= 1;
53        buf[i] = HEX[(val & 0xf) as usize];
54        val >>= 4;
55    }
56    early_print(&buf[i..]);
57}
58
59/// Size of the real kernel stack allocated after buddy allocator init.
60pub const KERNEL_STACK_SIZE: usize = 256 * 1024;
61
62/// Saved kernel ELF base address and size for symbol resolution during panic.
63static mut KERNEL_ELF_BASE: u64 = 0;
64static mut KERNEL_ELF_SIZE: u64 = 0;
65
66/// Get the kernel ELF bytes as a slice. Returns None if not yet initialized.
67pub fn kernel_elf_bytes() -> Option<&'static [u8]> {
68    let base = unsafe { KERNEL_ELF_BASE };
69    let size = unsafe { KERNEL_ELF_SIZE } as usize;
70    if base == 0 || size == 0 {
71        return None;
72    }
73    Some(unsafe { core::slice::from_raw_parts(base as *const u8, size) })
74}
75
76/// Enable SSE (OSFXSR, OSXMMEXCPT) and FPU (NE) in CR0/CR4.
77#[inline(always)]
78unsafe fn enable_cpu_features() {
79    let mut cr0: u64;
80    core::arch::asm!("mov {}, cr0", out(reg) cr0);
81    cr0 &= !(1 << 2); // Clear EM (bit 2)
82    cr0 |= (1 << 1) | (1 << 5); // Set MP (bit 1) + NE (bit 5)
83    core::arch::asm!("mov cr0, {}", in(reg) cr0, options(nomem, nostack));
84
85    let mut cr4: u64;
86    core::arch::asm!("mov {}, cr4", out(reg) cr4);
87    cr4 |= 0x600; // OSFXSR (9) | OSXMMEXCPT (10)
88    core::arch::asm!("mov cr4, {}", in(reg) cr4, options(nomem, nostack));
89}
90
91/// Kernel entry point called by the bootloader or PVH boot.
92///
93/// For bootloader: rdi = pointer to [`strat9_abi::boot::KernelArgs`]
94/// For PVH: rdi = 0 (no KernelArgs provided; build a minimal set)
95///
96/// Runs on the 8 KB bootstrap stack from boot64.S. The KernelArgs are
97/// read (or built) here. The real kernel init and stack switch happen
98/// in `crate::kernel_main` after the buddy allocator is ready.
99#[no_mangle]
100#[allow(static_mut_refs)]
101pub unsafe extern "C" fn kmain(args_ptr: u64) -> ! {
102    // Step 1: Very early serial output - before anything else.
103    // Just use raw writes without init (bootloader already set up COM1).
104    {
105        let thr: u16 = 0x3F8;
106        let s = b"[kmain] RAW entry\r\n";
107        for &b in s {
108            let mut lsr: u8;
109            core::arch::asm!("in al, dx", in("dx") 0x3F8 + 5, out("al") lsr, options(nostack, preserves_flags));
110            loop {
111                core::arch::asm!("in al, dx", in("dx") 0x3F8 + 5, out("al") lsr, options(nostack, preserves_flags));
112                if lsr & 0x20 != 0 {
113                    break;
114                }
115            }
116            core::arch::asm!("out dx, al", in("dx") thr, in("al") b, options(nostack, preserves_flags));
117        }
118    }
119
120    // Step 2: Enable CPU features (SSE/OSXSAVE) : no asm magic, auditable.
121    enable_cpu_features();
122
123    // Step 3: Serial port with crate
124    {
125        let mut early_port = uart_16550::SerialPort::new(0x3F8);
126        let _ = core::fmt::Write::write_str(
127            &mut early_port,
128            "[kmain] *** Strat9-OS kernel entry ***\r\n",
129        );
130    }
131
132    // Step 3: Obtain KernelArgs : either from the bootloader pointer or
133    //         build a minimal set for PVH.
134    //
135    // NOTE: We use raw COM1 port I/O for ALL output here. The global
136    // serial_println! and format_args! vtable dispatch may not work
137    // before the full kernel is initialized.
138    let args = if args_ptr == 0 {
139        early_print(b"[kmain] PVH boot (no args)\r\n");
140        build_minimal_args()
141    } else {
142        early_print(b"[kmain] Bootloader boot (args=0x");
143        early_print_hex(args_ptr);
144        early_print(b")\r\n");
145        // Read KernelArgs from the bootloader-provided pointer.
146        // The struct is #[repr(C, packed)] so we copy field-by-field.
147        let ptr = args_ptr as *const strat9_abi::boot::KernelArgs;
148        strat9_abi::boot::KernelArgs {
149            magic: core::ptr::read_unaligned(core::ptr::addr_of!((*ptr).magic)),
150            abi_version: core::ptr::read_unaligned(core::ptr::addr_of!((*ptr).abi_version)),
151            kernel_base: core::ptr::read_unaligned(core::ptr::addr_of!((*ptr).kernel_base)),
152            kernel_size: core::ptr::read_unaligned(core::ptr::addr_of!((*ptr).kernel_size)),
153            acpi_rsdp_base: core::ptr::read_unaligned(core::ptr::addr_of!((*ptr).acpi_rsdp_base)),
154            memory_map_base: core::ptr::read_unaligned(core::ptr::addr_of!((*ptr).memory_map_base)),
155            memory_map_size: core::ptr::read_unaligned(core::ptr::addr_of!((*ptr).memory_map_size)),
156            framebuffer_addr: core::ptr::read_unaligned(core::ptr::addr_of!(
157                (*ptr).framebuffer_addr
158            )),
159            hhdm_offset: core::ptr::read_unaligned(core::ptr::addr_of!((*ptr).hhdm_offset)),
160            cmdline_ptr: core::ptr::read_unaligned(core::ptr::addr_of!((*ptr).cmdline_ptr)),
161            cmdline_len: core::ptr::read_unaligned(core::ptr::addr_of!((*ptr).cmdline_len)),
162            modules_base: core::ptr::read_unaligned(core::ptr::addr_of!((*ptr).modules_base)),
163            modules_size: core::ptr::read_unaligned(core::ptr::addr_of!((*ptr).modules_size)),
164            framebuffer_width: core::ptr::read_unaligned(core::ptr::addr_of!(
165                (*ptr).framebuffer_width
166            )),
167            framebuffer_height: core::ptr::read_unaligned(core::ptr::addr_of!(
168                (*ptr).framebuffer_height
169            )),
170            framebuffer_stride: core::ptr::read_unaligned(core::ptr::addr_of!(
171                (*ptr).framebuffer_stride
172            )),
173            framebuffer_bpp: core::ptr::read_unaligned(core::ptr::addr_of!((*ptr).framebuffer_bpp)),
174            framebuffer_red_mask_size: core::ptr::read_unaligned(core::ptr::addr_of!(
175                (*ptr).framebuffer_red_mask_size
176            )),
177            framebuffer_red_mask_shift: core::ptr::read_unaligned(core::ptr::addr_of!(
178                (*ptr).framebuffer_red_mask_shift
179            )),
180            framebuffer_green_mask_size: core::ptr::read_unaligned(core::ptr::addr_of!(
181                (*ptr).framebuffer_green_mask_size
182            )),
183            framebuffer_green_mask_shift: core::ptr::read_unaligned(core::ptr::addr_of!(
184                (*ptr).framebuffer_green_mask_shift
185            )),
186            framebuffer_blue_mask_size: core::ptr::read_unaligned(core::ptr::addr_of!(
187                (*ptr).framebuffer_blue_mask_size
188            )),
189            framebuffer_blue_mask_shift: core::ptr::read_unaligned(core::ptr::addr_of!(
190                (*ptr).framebuffer_blue_mask_shift
191            )),
192            bss_virt_base: core::ptr::read_unaligned(core::ptr::addr_of!((*ptr).bss_virt_base)),
193            bss_virt_size: core::ptr::read_unaligned(core::ptr::addr_of!((*ptr).bss_virt_size)),
194        }
195    };
196
197    if args.magic != strat9_abi::boot::STRAT9_BOOT_MAGIC {
198        early_print(b"[kmain] ERROR: Bad KernelArgs magic: 0x");
199        early_print_hex(
200            unsafe { core::ptr::read_unaligned(core::ptr::addr_of!(args.magic)) } as u64,
201        );
202        early_print(b"\r\n");
203        hlt_loop();
204    }
205
206    let memory_map_base =
207        unsafe { core::ptr::read_unaligned(core::ptr::addr_of!(args.memory_map_base)) };
208    let memory_map_size =
209        unsafe { core::ptr::read_unaligned(core::ptr::addr_of!(args.memory_map_size)) };
210    let framebuffer_addr =
211        unsafe { core::ptr::read_unaligned(core::ptr::addr_of!(args.framebuffer_addr)) };
212    let acpi_rsdp_base =
213        unsafe { core::ptr::read_unaligned(core::ptr::addr_of!(args.acpi_rsdp_base)) };
214    early_print(b"[kmain] mmap=0x");
215    early_print_hex(memory_map_base);
216    early_print(b"/0x");
217    early_print_hex(memory_map_size);
218    early_print(b" fb=0x");
219    early_print_hex(framebuffer_addr);
220    early_print(b" rsdp=0x");
221    early_print_hex(acpi_rsdp_base);
222    early_print(b"\r\n");
223
224    // Step 5: Hand off to kernel_main (still on bootstrap stack).
225    // kernel_main will allocate a real stack after buddy allocator init.
226    crate::kernel_main(&args as *const _);
227}
228
229/// Halt the CPU forever.
230#[inline(always)]
231fn hlt_loop() -> ! {
232    loop {
233        unsafe {
234            core::arch::asm!("hlt", options(nomem, nostack, preserves_flags));
235        }
236    }
237}
238
239/// Build minimal KernelArgs for PVH boot (no DTB).
240fn build_minimal_args() -> super::entry::KernelArgs {
241    super::entry::KernelArgs {
242        magic: strat9_abi::boot::STRAT9_BOOT_MAGIC,
243        abi_version: strat9_abi::boot::STRAT9_BOOT_ABI_VERSION,
244        kernel_base: 0,
245        kernel_size: 0,
246        acpi_rsdp_base: 0,
247        memory_map_base: 0,
248        memory_map_size: 0,
249        framebuffer_addr: 0,
250        framebuffer_width: 0,
251        framebuffer_height: 0,
252        framebuffer_stride: 0,
253        framebuffer_bpp: 0,
254        framebuffer_red_mask_size: 8,
255        framebuffer_red_mask_shift: 16,
256        framebuffer_green_mask_size: 8,
257        framebuffer_green_mask_shift: 8,
258        framebuffer_blue_mask_size: 8,
259        framebuffer_blue_mask_shift: 0,
260        hhdm_offset: 0,
261        cmdline_ptr: 0,
262        cmdline_len: 0,
263        modules_base: 0,
264        modules_size: 0,
265        bss_virt_base: 0,
266        bss_virt_size: 0,
267    }
268}