Skip to main content

strat9_kernel/
lib.rs

1//! Strat9-OS Kernel (Bedrock)
2//!
3//! A minimal microkernel handling:
4//! - Scheduling
5//! - IPC (Inter-Process Communication)
6//! - Memory primitives
7//! - Interrupt routing
8//!
9//! Everything else runs as userspace component servers.
10
11#![no_std]
12#![no_main]
13#![feature(abi_x86_interrupt)]
14#![feature(alloc_error_handler)]
15#![feature(negative_impls)]
16
17extern crate alloc;
18
19// OSTD-like abstraction layer (minimal unsafe TCB)
20pub mod ostd;
21
22pub mod acpi;
23pub mod arch;
24pub mod audit;
25pub mod boot;
26pub mod capability;
27pub mod components;
28pub mod crypto;
29pub mod debug;
30pub mod debug_cfg;
31
32pub mod async_io;
33pub mod dma;
34pub mod entropy;
35pub mod framebuffer;
36pub mod hal;
37pub mod hardware;
38pub mod ipc;
39pub mod kaslr;
40pub mod memory;
41pub mod namespace;
42pub mod process;
43pub mod shell;
44pub mod silo;
45pub mod sync;
46pub mod syscall;
47pub mod trace;
48pub mod vfs;
49
50// Re-export the kernel entry point from the boot module
51pub use boot::dtb_boot::kmain;
52
53// serial_print! and serial_println! macros are #[macro_export]'ed
54// from arch::serial and available at crate root automatically.
55
56/// Initialize serial output
57pub fn init_serial() {
58    if crate::debug_cfg::SERIAL_ENABLED {
59        arch::serial::init();
60    }
61}
62
63/// Initialize the logger (uses serial)
64pub fn init_logger() {
65    boot::logger::init();
66}
67
68/// Initialize kernel components using the component system
69///
70/// This function initializes all kernel components in the correct order
71/// based on their dependencies and priorities.
72pub fn init_components(stage: component::InitStage) -> Result<(), component::ComponentInitError> {
73    component::init_all(stage)
74}
75
76use core::panic::PanicInfo;
77
78const PAGE_SIZE: u64 = 4096;
79const MAX_BOOT_MMAP_REGIONS_WORK: usize = strat9_abi::boot::MAX_BOOT_MEMORY_REGIONS;
80
81/// Static working buffer for the boot memory map (off stack to avoid overflow).
82static mut MMAP_WORK: [boot::entry::MemoryRegion; MAX_BOOT_MMAP_REGIONS_WORK] =
83    [null_region(); MAX_BOOT_MMAP_REGIONS_WORK];
84
85/// Global pointer to KernelArgs, set once during early boot.
86/// Components use this to access boot-time information (framebuffer, memory map, etc.).
87static mut BOOT_ARGS: Option<&'static boot::entry::KernelArgs> = None;
88
89/// Get the boot arguments. Returns `None` if called before `kernel_main`.
90pub fn boot_args() -> Option<&'static boot::entry::KernelArgs> {
91    // SAFETY: written once during early boot, read-only thereafter.
92    unsafe { BOOT_ARGS }
93}
94
95/// Performs the null region operation.
96const fn null_region() -> boot::entry::MemoryRegion {
97    boot::entry::MemoryRegion {
98        base: 0,
99        size: 0,
100        kind: boot::entry::MemoryKind::Reserved,
101    }
102}
103
104/// Performs the align down operation.
105#[inline]
106const fn align_down(value: u64, align: u64) -> u64 {
107    value & !(align - 1)
108}
109
110/// Performs the align up operation.
111#[inline]
112const fn align_up(value: u64, align: u64) -> u64 {
113    (value + align - 1) & !(align - 1)
114}
115
116/// Performs the virt or phys to phys operation.
117#[inline]
118const fn virt_or_phys_to_phys(addr: u64, hhdm: u64) -> u64 {
119    if hhdm != 0 && addr >= hhdm {
120        addr - hhdm
121    } else {
122        addr
123    }
124}
125
126/// Performs the reserve range in map operation.
127fn reserve_range_in_map(
128    map: &mut [boot::entry::MemoryRegion],
129    len: &mut usize,
130    reserve_start: u64,
131    reserve_end: u64,
132) {
133    if reserve_start >= reserve_end {
134        return;
135    }
136
137    let mut i = 0usize;
138    while i < *len {
139        let region = map[i];
140        if !matches!(
141            region.kind,
142            boot::entry::MemoryKind::Free | boot::entry::MemoryKind::Reclaim
143        ) {
144            i += 1;
145            continue;
146        }
147
148        let region_start = region.base;
149        let region_end = region.base.saturating_add(region.size);
150        if reserve_end <= region_start || reserve_start >= region_end {
151            i += 1;
152            continue;
153        }
154
155        let overlap_start = core::cmp::max(region_start, reserve_start);
156        let overlap_end = core::cmp::min(region_end, reserve_end);
157
158        if overlap_start <= region_start && overlap_end >= region_end {
159            map[i].kind = boot::entry::MemoryKind::Reserved;
160            i += 1;
161            continue;
162        }
163
164        if overlap_start <= region_start {
165            map[i].base = overlap_end;
166            map[i].size = region_end.saturating_sub(overlap_end);
167            i += 1;
168            continue;
169        }
170
171        if overlap_end >= region_end {
172            map[i].size = overlap_start.saturating_sub(region_start);
173            i += 1;
174            continue;
175        }
176
177        let left = boot::entry::MemoryRegion {
178            base: region_start,
179            size: overlap_start.saturating_sub(region_start),
180            kind: region.kind,
181        };
182        let right = boot::entry::MemoryRegion {
183            base: overlap_end,
184            size: region_end.saturating_sub(overlap_end),
185            kind: region.kind,
186        };
187
188        if *len + 1 > map.len() {
189            map[i] = left;
190            i += 1;
191            continue;
192        }
193
194        for j in (i + 1..*len).rev() {
195            map[j + 1] = map[j];
196        }
197        map[i] = left;
198        map[i + 1] = right;
199        *len += 1;
200        i += 2;
201    }
202}
203
204#[inline]
205fn count_free_like_regions(map: &[boot::entry::MemoryRegion], len: usize) -> usize {
206    map[..len]
207        .iter()
208        .filter(|region| {
209            matches!(
210                region.kind,
211                boot::entry::MemoryKind::Free | boot::entry::MemoryKind::Reclaim
212            )
213        })
214        .count()
215}
216
217/// Performs the region kind for addr operation.
218#[cfg(feature = "selftest")]
219fn region_kind_for_addr(
220    map: &[boot::entry::MemoryRegion],
221    len: usize,
222    addr: u64,
223) -> Option<boot::entry::MemoryKind> {
224    map.iter().take(len).find_map(|r| {
225        let start = r.base;
226        let end = r.base.saturating_add(r.size);
227        if addr >= start && addr < end {
228            Some(r.kind)
229        } else {
230            None
231        }
232    })
233}
234
235/// Kernel panic handler
236#[panic_handler]
237fn panic_handler(info: &PanicInfo) -> ! {
238    boot::panic::panic_handler(info)
239}
240
241/// Register a validated physical module with exactly one HHDM conversion.
242fn register_initfs_module(module: &strat9_abi::boot::ModuleEntry) {
243    let view = boot::modules::InitfsModule::from_physical(module, memory::hhdm_offset())
244        .unwrap_or_else(|error| panic!("Invalid initfs module: {}", error));
245    let path = view.name;
246    let base_virt = view.virtual_base as *const u8;
247    let len = view.len;
248    #[cfg(feature = "selftest")]
249    {
250        // Only peek small header bytes for debugging; no heap allocations.
251        let data = unsafe { core::slice::from_raw_parts(base_virt, len.min(4)) };
252        if data.len() == 4 {
253            serial_println!(
254                "[init] /initfs/{} source magic={:02x}{:02x}{:02x}{:02x} size={}",
255                path,
256                data[0],
257                data[1],
258                data[2],
259                data[3],
260                len
261            );
262        }
263    }
264
265    // Register the bootloader-provided module directly; keep it read-only.
266    if let Err(e) = vfs::register_initfs_file(path, base_virt, len) {
267        panic!("Failed to register /initfs/{}: {:?}", path, e);
268    } else {
269        serial_println!("[init] Registered /initfs/{} ({} bytes)", path, len);
270    }
271}
272
273/// Register modules from the validated bootloader module table.
274///
275/// Each module has a name, physical base address, and size.
276/// The kernel maps them into the VFS at /initfs/<name>.
277fn register_boot_modules(modules: &[strat9_abi::boot::ModuleEntry]) {
278    if modules.is_empty() {
279        serial_println!("[init] No modules provided by bootloader");
280        return;
281    }
282
283    serial_println!("[init] Bootloader provided {} modules:", modules.len());
284    for module in modules {
285        register_initfs_module(module);
286    }
287}
288
289/// Performs the register initfs module operation.
290#[cfg(feature = "selftest")]
291fn log_boot_module_magics(stage: &str) {
292    crate::serial_println!(
293        "[init] Module magic [{}]: (FAT32 module loader pending)",
294        stage
295    );
296}
297
298/// Performs the log boot module magics operation.
299#[cfg(not(feature = "selftest"))]
300fn log_boot_module_magics(_stage: &str) {}
301
302/// Main kernel initialization - called by bootloader entry points
303pub unsafe fn kernel_main(args: *const boot::entry::KernelArgs) -> ! {
304    // Earliest possible e9 mark : before any COM1 trace that might hang.
305    crate::e9_mark!(b'K');
306
307    // Raw COM1 traces hang when SERIAL_ENABLED=false (UART not initialized).
308    // Gate them behind SERIAL_ENABLED so we don't spin on a dead port.
309    if crate::debug_cfg::SERIAL_ENABLED {
310        // Raw COM1 trace - works before any subsystem is initialized.
311        {
312            let thr: u16 = 0x3F8;
313            let lsr: u16 = 0x3F8 + 5;
314            let msg = b"[km] kernel_main enter\r\n";
315            for &b in msg {
316                loop {
317                    let s: u8;
318                    core::arch::asm!("in al, dx", out("al") s, in("dx") lsr, options(nomem, nostack, preserves_flags));
319                    if s & 0x20 != 0 {
320                        break;
321                    }
322                }
323                core::arch::asm!("out dx, al", in("dx") thr, in("al") b, options(nomem, nostack, preserves_flags));
324            }
325        }
326    }
327
328    // Invariant: interrupts must stay disabled throughout kernel_main until the
329    // scheduler is ready and the APIC timer is started (Asterinas pattern:
330    // interrupts are only enabled once, at the very end of init).
331    debug_assert!(
332        !arch::interrupts_enabled(),
333        "interrupts must be disabled at boot entry"
334    );
335
336    // Trace: raw COM1 after debug_assert
337    if crate::debug_cfg::SERIAL_ENABLED {
338        let thr: u16 = 0x3F8;
339        let lsr: u16 = 0x3F8 + 5;
340        let msg = b"[km] after debug_assert\r\n";
341        for &b in msg {
342            loop {
343                let s: u8;
344                core::arch::asm!("in al, dx", out("al") s, in("dx") lsr, options(nomem, nostack, preserves_flags));
345                if s & 0x20 != 0 {
346                    break;
347                }
348            }
349            core::arch::asm!("out dx, al", in("dx") thr, in("al") b, options(nomem, nostack, preserves_flags));
350        }
351    }
352
353    // =============================================
354    // Phase 1: serial output (earliest debug output)
355    // =============================================
356    arch::x86_64::boot_timestamp::init();
357
358    // Trace: raw COM1 after boot_timestamp
359    if crate::debug_cfg::SERIAL_ENABLED {
360        let thr: u16 = 0x3F8;
361        let lsr: u16 = 0x3F8 + 5;
362        let msg = b"[km] after boot_timestamp\r\n";
363        for &b in msg {
364            loop {
365                let s: u8;
366                core::arch::asm!("in al, dx", out("al") s, in("dx") lsr, options(nomem, nostack, preserves_flags));
367                if s & 0x20 != 0 {
368                    break;
369                }
370            }
371            core::arch::asm!("out dx, al", in("dx") thr, in("al") b, options(nomem, nostack, preserves_flags));
372        }
373    }
374
375    // Skip e9_println! : it uses format_args! which may crash before
376    // the full kernel is initialized. Use raw COM1 trace instead.
377    //crate::e9_println!("B0 kernel_main");
378
379    // Trace before init_serial
380    if crate::debug_cfg::SERIAL_ENABLED {
381        let thr: u16 = 0x3F8;
382        let lsr: u16 = 0x3F8 + 5;
383        let msg = b"[km] before init_serial\r\n";
384        for &b in msg {
385            loop {
386                let s: u8;
387                core::arch::asm!("in al, dx", out("al") s, in("dx") lsr, options(nomem, nostack, preserves_flags));
388                if s & 0x20 != 0 {
389                    break;
390                }
391            }
392            core::arch::asm!("out dx, al", in("dx") thr, in("al") b, options(nomem, nostack, preserves_flags));
393        }
394    }
395
396    // init_serial() : temporarily disabled: #UD during uart_16550 init
397    //init_serial();
398
399    // Enable boot log prefix (timestamp) by default; can be disabled later if needed.
400    crate::e9_mark!(b'L');
401    arch::serial::set_boot_log_prefix_enabled(true);
402    crate::e9_mark!(b'M');
403
404    init_logger();
405    crate::e9_mark!(b'N');
406    //boot_milestone!("Kernel entry");
407    //arch::x86_64::speaker::beep_phase(1);
408
409    // =============================================
410    // Phase 1c: TSS + GDT + IDT
411    // =============================================
412    // TSS and GDT must be loaded before the IDT so that the kernel's
413    // CS selector (0x08) is valid when the first exception fires.
414    // Without a valid GDT entry, the IDT handler triple-faults.
415
416    e9_mark!(b'T');
417    arch::tss::init();
418    e9_mark!(b'G');
419    arch::gdt::init();
420    e9_mark!(b'I');
421    arch::x86_64::idt::init();
422    e9_mark!(b'i');
423    //serial_println!("[init] IDT initialized.");
424    //crate::e9_println!("B2 post-IDT");
425    //boot_milestone!("IDT initialized");
426    //crate::e9_println!("B3 milestone");
427
428    // Trace after IDT
429    if crate::debug_cfg::SERIAL_ENABLED {
430        let thr: u16 = 0x3F8;
431        let lsr: u16 = 0x3F8 + 5;
432        let msg = b"[km] IDT initialized\r\n";
433        for &b in msg {
434            loop {
435                let s: u8;
436                core::arch::asm!("in al, dx", out("al") s, in("dx") lsr, options(nomem, nostack, preserves_flags));
437                if s & 0x20 != 0 {
438                    break;
439                }
440            }
441            core::arch::asm!("out dx, al", in("dx") thr, in("al") b, options(nomem, nostack, preserves_flags));
442        }
443    }
444
445    debug_assert!(
446        !arch::interrupts_enabled(),
447        "interrupts must be disabled after IDT init"
448    );
449    e9_mark!(b'4');
450
451    // Detect CPU features (must happen before init_cpu_extensions)
452    e9_mark!(b'a');
453    crate::arch::x86_64::cpuid::init();
454    e9_mark!(b'b');
455
456    // Initialize FPU/SSE/XSAVE for the BSP
457    e9_mark!(b'c');
458    crate::arch::x86_64::init_cpu_extensions();
459    e9_mark!(b'd');
460
461    // Enable format_args-heavy logging paths now that SSE/XSAVE are initialized.
462    boot::logger::set_extensions_ready();
463
464    // Seed the kernel entropy pool from RDRAND (if available).
465    e9_mark!(b'e');
466    crate::entropy::seed_from_rdrand();
467    e9_mark!(b'f');
468
469    // Initialize KASLR offsets (requires entropy pool to be seeded).
470    e9_mark!(b'g');
471    crate::kaslr::init();
472    e9_mark!(b'h');
473
474    // Initialize crypto subsystem (trusted keys for module verification).
475    e9_mark!(b'i');
476    crate::crypto::init();
477    e9_mark!(b'j');
478
479    // Puts default panic hooks early to ensure
480    //we get useful info on any panics during init.
481    e9_mark!(b'k');
482    boot::panic::install_default_panic_hooks();
483    e9_mark!(b'l');
484    boot::symbols::init();
485    e9_mark!(b'm');
486
487    // Nice logo :D
488    // Disabled: serial_println with format_args creates nested Arguments requiring
489    // Display trait vtable pointers that resolve to identity-mapped addresses → #UD.
490    // TODO: replace with direct e9_mark! traces or fix the format_args vtable relocation.
491    /*
492    serial_println!();
493    serial_println!();
494    serial_println!(r"          __                 __   ________                         ");
495    serial_println!(r"  _______/  |_____________ _/  |_/   __   \           ____  ______ ");
496    serial_println!(r" /  ___/\   __\_  __ \__  \\   __\____    /  ______  /  _ \/  ___/ ");
497    serial_println!(r" \___ \  |  |  |  | \// __ \|  |    /    /  /_____/ (  <_> )___ \  ");
498    serial_println!(r"/____  > |__|  |__|  (____  /__|   /____/            \____/____  > ");
499    serial_println!(r"     \/                   \/                                   \/  ");
500    serial_println!();
501
502    serial_println!("");
503    serial_println!("=======================================================================================================");
504    serial_println!("  strat9-OS kernel v0.1.0 (Bedrock)");
505    serial_println!("  Copyright (c) 2024-26 Guillaume Gielly - GPLv3 License");
506    serial_println!("");
507    serial_println!("  This software is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY, without");
508    serial_println!(
509        "  even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE."
510    );
511    serial_println!("  See the GNU General Public License for more details.");
512    serial_println!("=======================================================================================================");
513    serial_println!();
514    */
515
516    // Validate arguments
517    e9_mark!(b'V');
518    if args.is_null() {
519        serial_println!("[CRIT] No KernelArgs provided. System will hang.");
520        loop {
521            arch::hlt();
522        }
523    }
524    e9_mark!(b'v');
525
526    let args = &*args;
527    e9_mark!(b'W');
528    serial_println!("[init] KernelArgs at {:p}", args);
529    e9_mark!(b'w');
530
531    // Store boot args globally so components can access them.
532    // SAFETY: written once here, read-only thereafter.
533    unsafe { BOOT_ARGS = Some(args) };
534    e9_mark!(b'X');
535
536    // SAFETY: KernelArgs is packed; read fields via addr_of! to avoid unaligned references.
537    let magic = unsafe { core::ptr::read_unaligned(core::ptr::addr_of!(args.magic)) };
538    let abi_version = unsafe { core::ptr::read_unaligned(core::ptr::addr_of!(args.abi_version)) };
539    e9_mark!(b'x');
540
541    if magic != strat9_abi::boot::STRAT9_BOOT_MAGIC {
542        serial_println!(
543            "[CRIT] Bad KernelArgs magic: 0x{:08x} (expected 0x{:08x})",
544            magic,
545            strat9_abi::boot::STRAT9_BOOT_MAGIC
546        );
547        loop {
548            arch::hlt();
549        }
550    }
551    if abi_version != strat9_abi::boot::STRAT9_BOOT_ABI_VERSION {
552        serial_println!(
553            "[CRIT] Unsupported boot ABI version: {} (kernel expects {})",
554            abi_version,
555            strat9_abi::boot::STRAT9_BOOT_ABI_VERSION
556        );
557        loop {
558            arch::hlt();
559        }
560    }
561    e9_mark!(b'Y');
562
563    // Parse kernel cmdline (early, for serial console config).
564    // SAFETY: cmdline_ptr is a valid null-terminated C string from the bootloader.
565    // NOTE: inlined strip_suffix caused #UD (Pattern trait function pointer at identity-mapped
566    // address 0x12002). Skip cmdline parsing for now.
567    e9_mark!(b'z');
568
569    // Le's go !
570    //
571    // =============================================
572    // Phase 1b : HHDM offset (must be set before any physical memory access)
573    // =============================================
574    let hhdm = args.hhdm_offset;
575    memory::set_hhdm_offset(hhdm);
576    #[cfg(target_arch = "x86_64")]
577    if args.env_get("loader.paging") == Some("wx-uc-v1") {
578        // No low EFI instruction is needed now. Revoke its executable alias
579        // before reclaiming loader pages for stacks, heaps or page tables.
580        unsafe { memory::paging::retire_uefi_identity_code() };
581    }
582    serial_println!("[init] HHDM offset: 0x{:x}", hhdm);
583
584    let memory_map_base =
585        unsafe { core::ptr::read_unaligned(core::ptr::addr_of!(args.memory_map_base)) };
586    let memory_map_size =
587        unsafe { core::ptr::read_unaligned(core::ptr::addr_of!(args.memory_map_size)) };
588    serial_println!(
589        "[init] Memory map: 0x{:x} ({} bytes)",
590        memory_map_base,
591        memory_map_size
592    );
593
594    log_boot_module_magics("pre-mm");
595
596    // =============================================
597    // Phase 2 : memory management (Buddy Allocator)
598    // =============================================
599    crate::e9_println!("MM pre-regions");
600    serial_println!("[init] Memory manager...");
601    serial_println!(
602        "[init] Memory map: 0x{:x} ({} bytes)",
603        memory_map_base,
604        memory_map_size
605    );
606    // SAFETY: the loader keeps these handoff buffers reserved and identity-mapped.
607    // Reject malformed extents/counts before any physical allocator consumes them.
608    let regions = unsafe { args.memory_regions() }
609        .unwrap_or_else(|error| panic!("Invalid boot memory map: {}", error));
610    if regions.is_empty() {
611        panic!("Boot memory map is empty");
612    }
613    let boot_modules = unsafe { args.modules() }
614        .unwrap_or_else(|error| panic!("Invalid boot module table: {}", error));
615    boot::modules::validate_modules(boot_modules, hhdm)
616        .unwrap_or_else(|error| panic!("Invalid boot modules: {}", error));
617    serial_println!("[init] Memory regions count: {}", regions.len());
618    if let Some(first) = regions.first() {
619        serial_println!(
620            "[init] First region: base={:#x} size={:#x} kind={:?}",
621            first.base,
622            first.size,
623            first.kind
624        );
625    }
626    // DEBUG: dump regions 0..6 on the E9 port (raw, no format_args).
627    {
628        let mut i = 0usize;
629        while i < regions.len().min(16) {
630            let r = &regions[i];
631            // kind: 0=Null 1=Free 2=Reclaim 3=Reserved
632            let k = (r.kind.0 as u8) + b'0';
633            let base = r.base;
634            let size = r.size;
635            unsafe {
636                core::arch::asm!(
637                    "out 0xe9, al",
638                    in("al") b'R', options(nomem, nostack)
639                );
640                // base nibbles (low 5 bytes enough)
641                let mut shift = 0i32;
642                while shift < 40 {
643                    let nib = ((base >> shift) & 0xF) as u8;
644                    let c = if nib < 10 {
645                        b'0' + nib
646                    } else {
647                        b'a' + nib - 10
648                    };
649                    core::arch::asm!("out 0xe9, al", in("al") c, options(nomem, nostack));
650                    shift += 4;
651                }
652                core::arch::asm!("out 0xe9, al", in("al") b'/', options(nomem, nostack));
653                shift = 0;
654                while shift < 40 {
655                    let nib = ((size >> shift) & 0xF) as u8;
656                    let c = if nib < 10 {
657                        b'0' + nib
658                    } else {
659                        b'a' + nib - 10
660                    };
661                    core::arch::asm!("out 0xe9, al", in("al") c, options(nomem, nostack));
662                    shift += 4;
663                }
664                core::arch::asm!("out 0xe9, al", in("al") b'/', options(nomem, nostack));
665                core::arch::asm!("out 0xe9, al", in("al") k, options(nomem, nostack));
666                core::arch::asm!("out 0xe9, al", in("al") b'\n', options(nomem, nostack));
667            }
668            i += 1;
669        }
670    }
671    crate::e9_println!("MM regions");
672    // Safety: single-threaded boot, no concurrent access
673    let mmap_work = unsafe { &mut *core::ptr::addr_of_mut!(MMAP_WORK) };
674    crate::e9_println!("MM work array");
675    let mmap_work_len = regions.len();
676    crate::e9_println!("MM len calc");
677    mmap_work[..mmap_work_len].copy_from_slice(regions);
678    crate::e9_println!("MM copy done");
679
680    // Modules are loaded from the FAT32 boot partition.
681    // Protected ranges will be set up after module loading is implemented in Phase 4.
682    let protected_ranges = [None; memory::boot_alloc::MAX_PROTECTED_RANGES];
683    crate::e9_println!("MM prot ranges");
684    memory::boot_alloc::set_protected_ranges(&protected_ranges);
685    crate::e9_println!("MM set prot done");
686
687    // Initialize the boot allocator before manually carving the working memory
688    // map. The allocator excludes the configured protected ranges itself, so
689    // it can still see the large original free extents that VMware exposes
690    // before module reservations fragment them.
691    crate::e9_println!("MM pre-init-boot-alloc");
692    memory::boot_alloc::init_boot_allocator(&mmap_work[..mmap_work_len]);
693    crate::e9_println!("MM post-init-boot-alloc before serial");
694    crate::e9_mark!(b'1');
695    serial_println!("[init] Boot allocator ready.");
696    crate::e9_mark!(b'2');
697    serial_println!("[init] Boot allocator ready.");
698    crate::e9_mark!(b'3');
699
700    let total_ram = mmap_work[..mmap_work_len]
701        .iter()
702        .filter(|region| {
703            matches!(
704                region.kind,
705                boot::entry::MemoryKind::Free | boot::entry::MemoryKind::Reclaim
706            )
707        })
708        .map(|region| region.base.saturating_add(region.size))
709        .max()
710        .unwrap_or(0);
711    crate::e9_mark!(b'4');
712    let free_like_regions = count_free_like_regions(&mmap_work[..mmap_work_len], mmap_work_len);
713    crate::e9_mark!(b'5');
714    let metadata_bytes = memory::frame::metadata_size_for(total_ram) as usize;
715    crate::e9_mark!(b'6');
716    let boot_stats = memory::boot_alloc::boot_allocator_stats();
717    crate::e9_mark!(b'7');
718    crate::e9_println!("M7 boot-stats-done");
719    serial_println!(
720        "[init] Frame metadata plan: total_ram={:#x} free_regions={} bytes={} boot_free={} largest_boot_region={}",
721        total_ram,
722        free_like_regions,
723        metadata_bytes,
724        boot_stats.total_free_bytes as usize,
725        boot_stats.largest_region_bytes as usize,
726    );
727    crate::e9_mark!(b'8');
728
729    {
730        crate::e9_mark!(b'9');
731        let mut boot_alloc = memory::boot_alloc::get_boot_allocator().lock();
732        crate::e9_mark!(b'A');
733        memory::frame::init_metadata_array(total_ram, &mut *boot_alloc);
734        crate::e9_mark!(b'B');
735    }
736    crate::e9_mark!(b'C');
737    serial_println!("[init] Frame metadata ready.");
738
739    // TODO: Phase 4 - Reserve module memory ranges when FAT32 loader is implemented
740    // For now, skip module reservation since we're using the custom bootloader + FAT32
741    crate::e9_mark!(b'D');
742
743    memory::buddy::init_buddy_allocator(&mmap_work[..mmap_work_len]);
744    crate::e9_mark!(b'E');
745
746    serial_println!("[init] Buddy allocator ready.");
747    crate::e9_mark!(b'F');
748
749    // =============================================
750    // Stack switch: migrate off the 8 KB bootstrap stack
751    // =============================================
752    // Inspired by Unikraft: allocate a proper kernel stack from the buddy
753    // allocator and switch to it. The bootstrap stack is abandoned after
754    // the switch (it remains allocated but unused).
755    {
756        use crate::boot::dtb_boot::KERNEL_STACK_SIZE;
757        let stack_phys = memory::boot_alloc::alloc_bytes_accessible(
758            KERNEL_STACK_SIZE,
759            16, // 16-byte alignment for SysV ABI
760        );
761        if let Some(phys) = stack_phys {
762            let stack_top = memory::phys_to_virt(phys.as_u64()) + KERNEL_STACK_SIZE as u64;
763            serial_println!(
764                "[init] Kernel stack allocated: {:#x} - {:#x} ({} KB)",
765                memory::phys_to_virt(phys.as_u64()),
766                stack_top,
767                KERNEL_STACK_SIZE / 1024
768            );
769
770            // Switch to the new stack via asm trampoline.
771            // This abandons the 8 KB bootstrap stack.
772            extern "C" {
773                fn switch_stack(new_rsp: u64, entry: extern "C" fn() -> !) -> !;
774            }
775            extern "C" fn stack_switch_entry() -> ! {
776                // We are now on the new 256 KB stack.
777                // The old bootstrap stack is abandoned.
778                // Continue with the rest of kernel_main.
779                // This is a noreturn function; we use a trick to continue
780                // execution after the stack switch.
781                //
782                // Actually, we can't easily continue kernel_main from here
783                // because the stack frame is different. Instead, we store
784                // the continuation address on the new stack and return to it.
785                //
786                // For now, we just halt : the real init continues on the
787                // bootstrap stack. The stack switch is a demonstration of
788                // the capability; full migration will happen when we restructure
789                // the init phases.
790                crate::serial_println!("[init] Stack switch: entered new stack, continuing...");
791                loop {
792                    unsafe {
793                        core::arch::asm!("hlt");
794                    }
795                }
796            }
797
798            // For now, log the allocation but don't actually switch.
799            // Full stack migration requires restructuring kernel_main into
800            // a two-phase init (early on bootstrap, late on real stack).
801            serial_println!("[init] Stack allocated (switch deferred to Phase 7)");
802        } else {
803            serial_println!(
804                "[init] WARNING: kernel stack alloc failed, staying on bootstrap stack"
805            );
806        }
807    }
808
809    // Apply kernel.toml configuration (must be after buddy allocator init,
810    // before VGA init so quiet_mode can suppress early debug output).
811    boot::config::apply_kernel_config();
812
813    // Initialize the vmalloc arena (VM-backed large heap allocations)
814    memory::vmalloc::init();
815    serial_println!("[init] Vmalloc arena ready.");
816
817    debug_assert!(
818        !arch::interrupts_enabled(),
819        "interrupts must be disabled after buddy allocator init"
820    );
821
822    boot_milestone!("Memory manager ready");
823    arch::speaker::beep_phase(2);
824    log_boot_module_magics("post-buddy");
825
826    // Sanity check: verify buddy allocator was initialized.
827    // If this fails, the memory subsystem is fatally broken.
828    if crate::memory::buddy::get_allocator().lock().is_none() {
829        serial_println!("[CRIT] Buddy allocator self-test FAILED: allocator not initialized");
830        serial_println!("[CRIT] System halted.");
831        loop {
832            arch::x86_64::hlt();
833        }
834    }
835
836    // =============================================
837    // Phase 2.5: paging / VMM (Must be before Console if FB is not already mapped)
838    // =============================================
839    crate::e9_println!("B5 pre-paging");
840    serial_println!("[init] Paging...");
841    memory::paging::init(hhdm);
842    crate::e9_println!("B6 post-paging");
843
844    // Map all RAM into HHDM to ensure buddy/heap allocations are accessible.
845    // VMware bootloader HHDM may be sparse, causing PF on new heap pages.
846    memory::paging::map_all_ram(&mmap_work[..mmap_work_len]);
847
848    // Framebuffer is often backed by MMIO memory outside RAM (e.g. around 0xFDxxxxxx),
849    // or sometimes at the very end of RAM that might be missed by the bootloader's initial map.
850    // Explicitly map its full range in HHDM for all later graphics access.
851    if args.framebuffer_addr != 0 && args.framebuffer_stride != 0 && args.framebuffer_height != 0 {
852        let fb_phys = if args.framebuffer_addr >= hhdm {
853            args.framebuffer_addr - hhdm
854        } else {
855            args.framebuffer_addr
856        };
857        let fb_size =
858            (args.framebuffer_stride as u64).saturating_mul(args.framebuffer_height as u64);
859        memory::paging::ensure_identity_map_range(fb_phys, fb_size);
860        serial_println!(
861            "[init] Framebuffer mapped: phys=0x{:x} size={} bytes",
862            fb_phys,
863            fb_size
864        );
865    }
866    serial_println!("[init] Paging initialized.");
867    boot_milestone!("Paging initialized");
868    arch::speaker::beep_phase(3);
869
870    // =============================================
871    // Phase 3: console output (VGA or serial fallback)
872    // =============================================
873    serial_println!("[init] Console...");
874    arch::vga::init(
875        args.framebuffer_addr,
876        args.framebuffer_width,
877        args.framebuffer_height,
878        args.framebuffer_stride,
879        args.framebuffer_bpp,
880        args.framebuffer_red_mask_size,
881        args.framebuffer_red_mask_shift,
882        args.framebuffer_green_mask_size,
883        args.framebuffer_green_mask_shift,
884        args.framebuffer_blue_mask_size,
885        args.framebuffer_blue_mask_shift,
886    );
887    // Flush any log lines buffered before VGA was available.
888    arch::vgabuf::vgabuf_flush_to_framebuffer();
889    vga_println!("[OK] Paging initialized");
890    vga_println!("[OK] Serial port initialized");
891    vga_println!("[OK] Memory manager active");
892
893    // =============================================
894    // Phase 4a : TSS + GDT (already initialized in Phase 1c)
895    // =============================================
896    serial_println!("[init] TSS+GDT already initialized.");
897
898    // =============================================
899    // Phase 4c: SYSCALL/SYSRET MSR configuration
900    // =============================================
901    serial_println!("[init] SYSCALL/SYSRET...");
902    vga_println!("[..] Initializing SYSCALL/SYSRET...");
903    arch::syscall::init();
904    serial_println!("[init] SYSCALL/SYSRET initialized.");
905    vga_println!("[OK] SYSCALL/SYSRET configured");
906
907    // =============================================
908    // Phase 4d: component system - Bootstrap stage
909    // =============================================
910    serial_println!("[init] Components (bootstrap)...");
911    vga_println!("[..] Initializing bootstrap components...");
912    if let Err(e) = component::init_all(component::InitStage::Bootstrap) {
913        serial_println!("[WARN] Some bootstrap components failed: {:?}", e);
914    }
915    serial_println!("[init] Bootstrap components initialized.");
916    vga_println!("[OK] Bootstrap components ready");
917
918    // =============================================
919    // Phase 5b: paging / VMM - (Moved earlier to prevent PF on VGA init)
920    // =============================================
921    log_boot_module_magics("post-paging");
922
923    // =============================================
924    // Phase 5c: kernel address space
925    // =============================================
926    serial_println!("[init] Kernel address space...");
927    vga_println!("[..] Initializing kernel address space...");
928    memory::address_space::init_kernel_address_space();
929    serial_println!("[init] Kernel address space initialized.");
930    debug_assert!(
931        !arch::interrupts_enabled(),
932        "interrupts must be disabled after kernel address space init"
933    );
934    vga_println!("[OK] Kernel address space initialized");
935    log_boot_module_magics("post-kas");
936
937    // =============================================
938    // Phase 5d: virtual file system
939    // =============================================
940    serial_println!("[init] VFS...");
941    vga_println!("[..] Initializing virtual file system...");
942
943    vfs::init();
944
945    serial_println!("[init] VFS initialized.");
946    vga_println!("[OK] VFS initialized");
947    register_boot_modules(boot_modules);
948
949    log_boot_module_magics("post-cow");
950
951    // =============================================
952    // Phase 6: ACPI + APIC (with PIC fallback)
953    // =============================================
954    serial_println!("[init] Interrupt controller...");
955    vga_println!("[..] Initializing interrupt controller...");
956
957    // Ensure RSDP is mapped (it might be in unmapped legacy region)
958    memory::paging::ensure_identity_map(args.acpi_rsdp_base);
959
960    let rsdp_virt = memory::phys_to_virt(args.acpi_rsdp_base);
961    let apic_active = init_apic_subsystem(rsdp_virt);
962
963    if !apic_active {
964        // Fallback: legacy PIC + PIT
965        serial_println!("[init] APIC unavailable, falling back to legacy PIC");
966        vga_println!("[..] Falling back to legacy PIC...");
967        arch::pic::init(arch::pic::PIC1_OFFSET, arch::pic::PIC2_OFFSET);
968        arch::pic::disable();
969        arch::pic::enable_irq(0); // Timer
970        arch::pic::enable_irq(1); // Keyboard
971        serial_println!("[init] Legacy PIC initialized.");
972        vga_println!("[OK] Legacy PIC initialized (IRQ0: timer, IRQ1: keyboard)");
973    } else {
974        serial_println!("[init] APIC subsystem initialized.");
975        vga_println!("[OK] APIC + I/O APIC + APIC timer active");
976    }
977
978    // Initialize TLB shootdown system (SMP safety for COW operations).
979    if apic_active {
980        arch::tlb::init();
981        serial_println!("[init] TLB shootdown system initialized.");
982        debug_assert!(
983            !arch::interrupts_enabled(),
984            "interrupts must be disabled after TLB init"
985        );
986    }
987
988    // ================================================
989    // Phase 6j: SMP bring-up (AP boot) + per-CPU data
990    // ================================================
991    if apic_active {
992        let bsp_apic_id = arch::apic::lapic_id();
993        arch::percpu::init_boot_cpu(bsp_apic_id);
994        arch::percpu::init_gs_base(0);
995        serial_println!("[init] SMP: booting secondary cores...");
996        vga_println!("[..] SMP: starting APs...");
997
998        match arch::smp::init() {
999            Ok(count) => {
1000                serial_println!("[init] SMP: {} core(s) online", count);
1001                vga_println!("[OK] SMP: {} core(s) online", count);
1002            }
1003            Err(e) => {
1004                serial_println!("[init] SMP init failed: {}", e);
1005                vga_println!("[WARN] SMP init failed: {}", e);
1006            }
1007        }
1008    } else {
1009        arch::percpu::init_boot_cpu(0);
1010    }
1011    boot_milestone!("APIC + SMP ready");
1012    arch::speaker::beep_phase(4);
1013
1014    arch::keyboard::init();
1015    serial_println!("[init] PS/2 keyboard controller initialized.");
1016
1017    // =============================================
1018    // Phase 6k: PS/2 mouse driver
1019    // =============================================
1020    if apic_active {
1021        let mouse_ok = arch::mouse::init();
1022
1023        if mouse_ok {
1024            serial_println!("[init] PS/2 mouse initialized.");
1025            vga_println!("[OK] PS/2 mouse ready");
1026        } else {
1027            serial_println!("[init] PS/2 mouse not found (optional).");
1028        }
1029    }
1030
1031    // =============================================
1032    // Phase 7: initialize scheduler
1033    // =============================================
1034    crate::e9_println!("B7 pre-sched");
1035    serial_println!("[init] Initializing scheduler...");
1036    vga_println!("[..] Setting up multitasking...");
1037    // Print struct layout for crash-site offset analysis (debug build only).
1038    crate::process::task::Task::debug_print_layout();
1039    process::init_scheduler();
1040    crate::e9_println!("B8 post-sched");
1041
1042    // Sanity check: verify scheduler is initialized.
1043    if crate::process::scheduler::GLOBAL_SCHED_STATE
1044        .lock()
1045        .is_none()
1046    {
1047        serial_println!("[CRIT] Scheduler init failed: GLOBAL_SCHED_STATE is None");
1048        serial_println!("[CRIT] System halted.");
1049        loop {
1050            arch::x86_64::hlt();
1051        }
1052    }
1053
1054    debug_assert!(
1055        !arch::interrupts_enabled(),
1056        "interrupts must be disabled after scheduler init"
1057    );
1058
1059    // =============================================
1060    // Phase 7+: Start timer
1061    // =============================================
1062    // The BSP timer only starts when the scheduler is ready to handle interrupts.
1063    // This is the last point where interrupts are guaranteed disabled on BSP.
1064    if apic_active {
1065        debug_assert!(
1066            !arch::interrupts_enabled(),
1067            "interrupts must be disabled before APIC timer start"
1068        );
1069        serial_println!("[init] Starting APIC timer on BSP...");
1070        arch::timer::start_apic_timer_cached();
1071    }
1072
1073    serial_println!("[init] Scheduler initialized.");
1074    serial_println!("[trace][bsp] after init_scheduler");
1075    boot_milestone!("Scheduler + timer ready");
1076    arch::speaker::beep_phase(5);
1077    vga_println!("[OK] Multitasking enabled");
1078
1079    // =============================================
1080    // Phase 7b: component system - Kthread stage
1081    // =============================================
1082    crate::e9_println!("B9 pre-kthread");
1083    serial_println!("[trace][bsp] before kthread init_all");
1084    serial_println!("[init] Components (kthread)...");
1085    vga_println!("[..] Initializing kthread components...");
1086
1087    if let Err(e) = component::init_all(component::InitStage::Kthread) {
1088        serial_println!("[WARN] Some kthread components failed: {:?}", e);
1089    }
1090
1091    serial_println!("[trace][bsp] after kthread init_all");
1092    serial_println!("[init] Kthread components initialized.");
1093    vga_println!("[OK] Kthread components ready");
1094
1095    // =============================================
1096    // Phase 7c: component system - Hardware stage
1097    // =============================================
1098    crate::e9_println!("BA pre-hardware");
1099    serial_println!("[init] Components (hardware)...");
1100    vga_println!("[..] Initializing hardware components...");
1101    if let Err(e) = component::init_all(component::InitStage::Hardware) {
1102        serial_println!("[WARN] Some hardware components failed: {:?}", e);
1103    }
1104    serial_println!("[init] Hardware components initialized.");
1105    vga_println!("[OK] Hardware components ready");
1106    boot_milestone!("Hardware drivers ready");
1107
1108    #[cfg(feature = "selftest")]
1109    {
1110        // =============================================
1111        // Phase 8a: runtime self-tests
1112        // =============================================
1113        serial_println!("[init] Creating self-test tasks...");
1114        vga_println!("[..] Adding self-test tasks...");
1115        process::selftest::create_selftest_tasks();
1116        serial_println!("[init] Self-test tasks created.");
1117        vga_println!("[OK] Self-test tasks added");
1118    }
1119
1120    // Ring3 smoke test task disabled in selftest mode: fork-test already
1121    // exercises Ring3 transitions and this extra task can interfere.
1122
1123    #[cfg(not(feature = "selftest"))]
1124    {
1125        // =============================================
1126        // Phase 8c: process components
1127        // =============================================
1128        let mut init_task_id: Option<crate::process::TaskId> = None;
1129
1130        crate::e9_println!("BB pre-process");
1131        serial_println!("[init] Components (process)...");
1132        vga_println!("[..] Initializing process components...");
1133        if let Err(e) = component::init_all(component::InitStage::Process) {
1134            serial_println!("[WARN] Some process components failed: {:?}", e);
1135        }
1136        serial_println!("[init] Process components initialized.");
1137        vga_println!("[OK] Process components ready");
1138
1139        // =============================================
1140        // Phase 8d: device enumeration and reporting
1141        // =============================================
1142        // Hardware drivers were initialized in the Hardware stage above.
1143        // This block reports which devices were found.
1144        crate::e9_println!("BD device-report");
1145        serial_println!("[init] Checking for devices...");
1146        vga_println!("[..] Checking for devices...");
1147
1148        if let Some(blk) = hardware::storage::virtio_block::get_device() {
1149            use hardware::storage::virtio_block::BlockDevice;
1150            serial_println!(
1151                "[INFO] VirtIO block device found. Capacity: {} sectors",
1152                blk.sector_count()
1153            );
1154            vga_println!("[OK] VirtIO block driver loaded");
1155        } else {
1156            serial_println!("[WARN] No VirtIO block device found");
1157            vga_println!("[WARN] No VirtIO block device found");
1158        }
1159
1160        if let Some(ahci) = hardware::storage::ahci::get_device() {
1161            serial_println!(
1162                "[INFO] AHCI SATA device found. Capacity: {} sectors ({} MiB)",
1163                ahci.sector_count(),
1164                (ahci.sector_count() * 512) / 1048576, // 1024*1024 bytes per MiB, 512 bytes per sector
1165            );
1166            vga_println!("[OK] AHCI SATA driver loaded");
1167        } else {
1168            serial_println!("[INFO] No AHCI SATA device found");
1169        }
1170
1171        if let Some(nvme) = hardware::storage::nvme::get_first_controller() {
1172            let nvme = nvme.lock();
1173            if let Some(ns) = nvme.get_namespace(0) {
1174                serial_println!(
1175                    "[INFO] NVMe device found. Namespace {} - {} blocks @ {} bytes ({} MiB)",
1176                    ns.nsid,
1177                    ns.size,
1178                    ns.block_size,
1179                    (ns.size * ns.block_size as u64) / 1048576, // 1024*1024 bytes per MiB
1180                );
1181                vga_println!("[OK] NVMe driver loaded");
1182            }
1183        } else {
1184            serial_println!("[INFO] No NVMe device found");
1185        }
1186
1187        // Report all registered network interfaces (E1000 + VirtIO)
1188        {
1189            let ifaces = hardware::nic::list_interfaces();
1190            if ifaces.is_empty() {
1191                serial_println!("[WARN] No network devices found");
1192                vga_println!("[WARN] No network devices found");
1193            } else {
1194                for name in &ifaces {
1195                    if let Some(dev) = hardware::nic::get_device(name) {
1196                        let mac = dev.mac_address();
1197                        serial_println!(
1198                            "[INFO] Network {} ({}) MAC {:02x}:{:02x}:{:02x}:{:02x}:{:02x}:{:02x} link={}",
1199                            name, dev.name(),
1200                            mac[0], mac[1], mac[2], mac[3], mac[4], mac[5],
1201                            if dev.link_up() { "up" } else { "down" },
1202                        );
1203                        vga_println!("[OK] Network {} ({}) loaded", name, dev.name());
1204                    }
1205                }
1206            }
1207        }
1208
1209        serial_println!("[init] Storage verification skipped (boot path)");
1210        vga_println!("[..] Storage verification skipped at boot");
1211
1212        // Launch init through the VFS populated from the boot module table.
1213        let mut init_loaded = false;
1214
1215        for init_path in ["/initfs/init", "/initfs/strate-init"] {
1216            if let Ok(fd) = vfs::open(init_path, vfs::OpenFlags::READ) {
1217                let data = vfs::read_all(fd);
1218                let _ = vfs::close(fd);
1219                let data = match data {
1220                    Ok(data) => data,
1221                    Err(error) => {
1222                        serial_println!("[init] Failed to read {}: {:?}", init_path, error);
1223                        continue;
1224                    }
1225                };
1226                let init_caps = [crate::silo::create_silo_admin_capability()];
1227                match process::elf::load_and_run_elf_with_caps(&data, "init", &init_caps) {
1228                    Ok(task_id) => {
1229                        init_task_id = Some(task_id);
1230                        init_loaded = true;
1231                        serial_println!("[init] ELF '{}' loaded as task 'init'.", init_path);
1232                        break;
1233                    }
1234                    Err(e) => {
1235                        serial_println!("[init] Failed to load init ELF: {}", e);
1236                    }
1237                }
1238            }
1239        }
1240
1241        // Try to load init from modules if not already loaded.
1242        // The initfs payload may be named "init" or "strate-init".
1243        if !init_loaded {
1244            // E9: report the module names so init-chain progress is visible
1245            // even when the formatted serial path is unavailable.
1246            for module in boot_modules {
1247                let raw = module.name_str();
1248                unsafe {
1249                    core::arch::asm!("out 0xe9, al", in("al") b'N', options(nomem, nostack));
1250                    for b in raw.as_bytes() {
1251                        core::arch::asm!("out 0xe9, al", in("al") *b, options(nomem, nostack));
1252                    }
1253                    core::arch::asm!("out 0xe9, al", in("al") b'\n', options(nomem, nostack));
1254                }
1255                let name = raw;
1256                if name == "init" || name == "strate-init" {
1257                    let view = boot::modules::InitfsModule::from_physical(module, hhdm)
1258                        .unwrap_or_else(|error| panic!("Invalid init module: {}", error));
1259                    let elf_data = unsafe {
1260                        core::slice::from_raw_parts(view.virtual_base as *const u8, view.len)
1261                    };
1262                    // E9: ELF magic check before handing to the loader.
1263                    unsafe {
1264                        let magic_ok = elf_data.len() >= 4
1265                            && elf_data[0] == 0x7F
1266                            && elf_data[1] == b'E'
1267                            && elf_data[2] == b'L'
1268                            && elf_data[3] == b'F';
1269                        core::arch::asm!("out 0xe9, al", in("al") b'!', options(nomem, nostack));
1270                        core::arch::asm!("out 0xe9, al", in("al") if magic_ok { b'Y' } else { b'N' }, options(nomem, nostack));
1271                        core::arch::asm!("out 0xe9, al", in("al") b'\n', options(nomem, nostack));
1272                    }
1273                    let init_caps = [crate::silo::create_silo_admin_capability()];
1274                    match process::elf::load_and_run_elf_with_caps(elf_data, "init", &init_caps) {
1275                        Ok(task_id) => {
1276                            init_task_id = Some(task_id);
1277                            crate::e9_mark!(b'I');
1278                            serial_println!(
1279                                "[init] ELF loaded as task 'init' (from module table)."
1280                            );
1281                        }
1282                        Err(e) => {
1283                            // E9: report failure code letter.
1284                            let code = e.as_bytes().first().copied().unwrap_or(b'?');
1285                            unsafe {
1286                                core::arch::asm!("out 0xe9, al", in("al") b'X', options(nomem, nostack));
1287                                core::arch::asm!("out 0xe9, al", in("al") code, options(nomem, nostack));
1288                                core::arch::asm!("out 0xe9, al", in("al") b'\n', options(nomem, nostack));
1289                            }
1290                            serial_println!("[init] Failed to load init ELF: {}", e);
1291                        }
1292                    }
1293                    break;
1294                }
1295            }
1296        }
1297        if let (Some(task_id), Some(device)) =
1298            (init_task_id, hardware::storage::virtio_block::get_device())
1299        {
1300            if let Some(task) = crate::process::get_task_by_id(task_id) {
1301                let cap = crate::capability::get_capability_manager().create_capability(
1302                    crate::capability::ResourceType::Volume,
1303                    device as *const _ as usize,
1304                    crate::capability::CapPermissions {
1305                        read: true,
1306                        write: true,
1307                        execute: false,
1308                        grant: true,
1309                        revoke: true,
1310                    },
1311                );
1312                unsafe { (&mut *task.process.capabilities.get()).insert(cap) };
1313                serial_println!("[init] Granted volume capability to init");
1314            }
1315        }
1316        crate::e9_mark!(b'W');
1317
1318        match process::Task::new_kernel_task_with_stack(
1319            shell::shell_main,
1320            "chevron-shell",
1321            process::TaskPriority::Normal,
1322            64 * 1024,
1323        ) {
1324            Ok(shell_task) => {
1325                process::add_task(shell_task);
1326                crate::e9_mark!(b'w');
1327                serial_println!("[init] Chevron shell ready.");
1328            }
1329            Err(e) => {
1330                serial_println!("[WARN] Failed to create shell task: {}", e);
1331            }
1332        }
1333        #[cfg(target_arch = "x86_64")]
1334        {
1335            match process::Task::new_kernel_task_with_stack(
1336                shell::mouse_task_main,
1337                "console-mouse",
1338                process::TaskPriority::Normal,
1339                64 * 1024,
1340            ) {
1341                Ok(task) => process::add_task(task),
1342                Err(err) => serial_println!("[WARN] Mouse task unavailable: {}", err),
1343            }
1344            match process::Task::new_kernel_task_with_stack(
1345                arch::x86_64::vgabuf::console_task_main,
1346                "console-render",
1347                process::TaskPriority::Normal,
1348                64 * 1024,
1349            ) {
1350                Ok(task) => process::add_task(task),
1351                Err(err) => serial_println!("[WARN] Console task unavailable: {}", err),
1352            }
1353            match process::Task::new_kernel_task_with_stack(
1354                arch::x86_64::serial::serial_task_main,
1355                "serial-output",
1356                process::TaskPriority::Low,
1357                32 * 1024,
1358            ) {
1359                Ok(task) => process::add_task(task),
1360                Err(err) => serial_println!("[WARN] Serial task unavailable: {}", err),
1361            }
1362        }
1363        crate::e9_mark!(b'Y');
1364        if let Ok(status_task) = process::Task::new_kernel_task_with_stack(
1365            arch::vga::status_line_task_main,
1366            "status-line",
1367            process::TaskPriority::Low,
1368            64 * 1024,
1369        ) {
1370            process::add_task(status_task);
1371            crate::e9_mark!(b'y');
1372            // Switch from live VGA debug output to buffered vgabuf path.
1373            // The console-render task drains vgabuf; status-line is a fallback.
1374            crate::debug_cfg::set_vga_debug_live(false);
1375        }
1376    }
1377    #[cfg(feature = "selftest")]
1378    {
1379        serial_println!("[init] Selftest mode: skipping process services and virtio drivers");
1380    }
1381
1382    // Initialize keyboard layout to French by default
1383    crate::arch::keyboard_layout::set_french_layout();
1384    crate::e9_mark!(b'F');
1385
1386    // =============================================
1387    // Boot complete : start preemptive multitasking
1388    // =============================================
1389    if apic_active {
1390        arch::smp::open_ap_scheduler_gate();
1391    }
1392    crate::e9_mark!(b'G');
1393    crate::e9_println!("BC pre-schedule");
1394    crate::e9_mark!(b'H');
1395    boot_milestone!("Boot complete ! Now entering in scheduler");
1396    arch::speaker::beep_startup();
1397    serial_println!("[init] Boot complete. Starting preemptive scheduler...");
1398    vga_println!("[OK] Starting multitasking (preemptive)");
1399    arch::serial::set_boot_log_prefix_enabled(false);
1400
1401    // Keep interrupts disabled on the init stack. `schedule_on_cpu()` enters
1402    // the first task with IF=0 and `task_entry_trampoline` executes `sti`
1403    // after the task context is fully installed.
1404
1405    // Start the scheduler - this will never return
1406    serial_force_println!("[trace][bsp] schedule start (never returns)");
1407    process::schedule();
1408}
1409
1410/// Initialize the APIC subsystem (Local APIC + I/O APIC + APIC Timer).
1411///
1412/// Returns `true` if APIC is active, `false` if we should fall back to PIC+PIT.
1413/// On failure at any step, logs a warning and returns `false`.
1414fn init_apic_subsystem(rsdp_vaddr: u64) -> bool {
1415    use arch::{apic, ioapic, pic, timer};
1416    use timer::TIMER_HZ;
1417
1418    // Step 6a: check CPUID for APIC support
1419    if !apic::is_present() {
1420        log::warn!("APIC: not present (CPUID)");
1421        return false;
1422    }
1423    serial_println!("[init]   6a. APIC present (CPUID)");
1424
1425    // Step 6b: initialize ACPI (validate RSDP)
1426    match acpi::init(rsdp_vaddr) {
1427        Ok(true) => {}
1428        Ok(false) => {
1429            log::warn!("APIC: no RSDP from bootloader");
1430            return false;
1431        }
1432        Err(e) => {
1433            log::warn!("APIC: ACPI init failed: {}", e);
1434            return false;
1435        }
1436    }
1437    serial_println!("[init]   6b. ACPI RSDP validated");
1438
1439    // Step 6c: Parse MADT
1440    let madt_info = match acpi::madt::parse_madt() {
1441        Some(info) => info,
1442        None => {
1443            log::warn!("APIC: MADT not found");
1444            return false;
1445        }
1446    };
1447    serial_println!("[init]   6c. MADT parsed");
1448
1449    if let Some(mcfg) = acpi::mcfg::parse_mcfg() {
1450        serial_println!(
1451            "[init]   6c+. MCFG parsed ({} segment(s))",
1452            mcfg.entries.len()
1453        );
1454        for entry in mcfg.entries.iter() {
1455            log::info!(
1456                "ACPI: MCFG seg={} ecam={:#x} buses={}..{} ({} bus(es))",
1457                entry.segment_group,
1458                entry.base_address,
1459                entry.start_bus,
1460                entry.end_bus,
1461                entry.bus_count()
1462            );
1463        }
1464    } else {
1465        serial_println!("[init]   6c+. MCFG not found");
1466    }
1467
1468    // Step 6c++: Parse IVRS (AMD IOMMU)
1469    if let Some(ivrs) = acpi::ivrs::Ivrs::get() {
1470        let dev_entry_count = unsafe {
1471            core::ptr::read_unaligned(core::ptr::addr_of!(ivrs.header().dev_entry_count))
1472        };
1473        serial_println!(
1474            "[init]   6c++. IVRS parsed (flags: draint={}, coherent={}, {} device entries)",
1475            ivrs.header().has_draint(),
1476            ivrs.header().is_coherent(),
1477            dev_entry_count,
1478        );
1479        ivrs.dump();
1480    } else {
1481        serial_println!("[init]   6c++. IVRS not found (no AMD IOMMU)");
1482    }
1483
1484    // Step 6d: initialize Local APIC
1485    // Ensure Local APIC MMIO is mapped
1486    memory::paging::ensure_identity_map(madt_info.local_apic_address);
1487    apic::init(madt_info.local_apic_address);
1488    serial_println!("[init]   6d. Local APIC initialized");
1489
1490    // Step 6e: initialize first I/O APIC
1491    if madt_info.io_apic_count == 0 {
1492        log::warn!("APIC: no I/O APIC in MADT");
1493        return false;
1494    }
1495    let Some(io_apic_entry) = madt_info.io_apics[0] else {
1496        log::warn!("APIC: MADT I/O APIC entry[0] missing");
1497        return false;
1498    };
1499    // Ensure I/O APIC MMIO is mapped
1500    memory::paging::ensure_identity_map(io_apic_entry.address as u64);
1501    ioapic::init(io_apic_entry.address, io_apic_entry.gsi_base);
1502    serial_println!("[init]   6e. I/O APIC initialized");
1503
1504    // Step 6f: remap PIC to 0x20+ then keep only PS/2 input IRQs enabled.
1505    // Must remap first to avoid stray interrupts at exception vectors (0-31).
1506    // On the current q35 + SMP path, LAPIC timer delivery is fine but legacy
1507    // PS/2 IRQs are not reliably arriving through the I/O APIC. Keep keyboard
1508    // and mouse on the remapped PIC while using APIC for the timer.
1509    pic::init(pic::PIC1_OFFSET, pic::PIC2_OFFSET);
1510    pic::disable_permanently();
1511    pic::enable_irq(1);
1512    pic::enable_irq(2);
1513    pic::enable_irq(12);
1514    serial_println!("[init]   6f. Legacy PIC remapped; PS/2 IRQ1/IRQ12 left enabled");
1515
1516    // Step 6g: route only the legacy timer IRQ via I/O APIC.
1517    // Keyboard (IRQ1) and mouse (IRQ12) stay on the remapped PIC path above.
1518    let lapic_id = apic::lapic_id();
1519    ioapic::route_legacy_irq(0, lapic_id, 0x20, &madt_info.overrides);
1520    ioapic::mask_legacy_irq(1, &madt_info.overrides);
1521    ioapic::mask_legacy_irq(12, &madt_info.overrides);
1522
1523    // Store overrides so PCI NIC drivers can route their IRQ later.
1524    ioapic::store_madt_overrides(&madt_info.overrides);
1525
1526    serial_println!("[init]   6g. IRQ0->vec 0x20 via IOAPIC; IRQ1/IRQ12 via PIC");
1527
1528    // Step 6h: calibrate APIC timer using PIT channel 2
1529    serial_println!("[init]   6h. Calibrating APIC timer using PIT channel 2...");
1530    serial_println!(
1531        "[timer] ================================ TIMER INIT ================================"
1532    );
1533
1534    let ticks_per_10ms = timer::calibrate_apic_timer();
1535
1536    if ticks_per_10ms == 0 {
1537        log::error!("APIC: timer calibration FAILED");
1538        log::warn!("Falling back to legacy PIT timer at 100Hz");
1539
1540        // Re-enable PIC since APIC timer failed
1541        // (Note: I/O APIC routing is still active for keyboard/timer via PIC vectors)
1542        serial_println!("[timer] APIC calibration failed, initializing PIT fallback...");
1543        timer::init_pit(TIMER_HZ as u32);
1544        serial_println!(
1545            "[timer] PIT initialized at {}Hz ({} ms/tick)",
1546            TIMER_HZ,
1547            1_000 / TIMER_HZ
1548        );
1549        serial_println!("[init]   6h. PIT timer initialized (fallback)");
1550
1551        serial_println!("[timer] ============================= TIMER INIT COMPLETE ============================");
1552        serial_println!("[timer] Mode: PIT (legacy fallback)");
1553        serial_println!("[timer] Frequency: {}Hz", TIMER_HZ);
1554        serial_println!("[timer] Interval: {} ms per tick", 1_000 / TIMER_HZ);
1555        serial_println!(
1556            "[timer] =========================================================================="
1557        );
1558
1559        // Continue with PIT - don't return false
1560        // return false;
1561    } else {
1562        serial_println!("[init]   6h. APIC timer calibrated successfully");
1563
1564        // Step 6i: DO NOT start APIC timer yet. (Asterinas style)
1565        // We will start it only after the scheduler is ready.
1566        // timer::start_apic_timer(ticks_per_10ms);
1567
1568        // Step 6i+: quench legacy PIT to prevent phantom timer interrupts.
1569        timer::stop_pit();
1570        ioapic::mask_legacy_irq(0, &madt_info.overrides);
1571        serial_println!("[init]   6i+. Legacy PIT stopped and masked in IOAPIC");
1572
1573        serial_println!("[timer] ============================= TIMER INIT COMPLETE ============================");
1574        serial_println!("[timer] Mode: APIC (native)");
1575        serial_println!("[timer] Frequency: {}Hz", TIMER_HZ);
1576        serial_println!("[timer] Interval: {} ms per tick", 1_000 / TIMER_HZ);
1577        serial_println!("[timer] Ticks per 10ms: {}", ticks_per_10ms);
1578        serial_println!(
1579            "[timer] =========================================================================="
1580        );
1581    }
1582
1583    true
1584}
1585
1586/// Boot-module lookup shim for non-x86 targets (always empty).
1587/// Boot-module lookup shim for non-x86 targets (always empty).
1588#[cfg(not(target_arch = "x86_64"))]
1589pub mod boot_limine_shim {
1590    pub fn kernel_elf_bytes() -> Option<&'static [u8]> {
1591        None
1592    }
1593    pub fn test_syscalls_module() -> Option<(u64, u64)> {
1594        None
1595    }
1596
1597    pub fn test_mem_module() -> Option<(u64, u64)> {
1598        None
1599    }
1600
1601    pub fn test_mem_stressed_module() -> Option<(u64, u64)> {
1602        None
1603    }
1604
1605    pub fn test_mem_region_module() -> Option<(u64, u64)> {
1606        None
1607    }
1608
1609    pub fn test_mem_region_proc_module() -> Option<(u64, u64)> {
1610        None
1611    }
1612
1613    pub fn test_exec_module() -> Option<(u64, u64)> {
1614        None
1615    }
1616
1617    pub fn test_exec_helper_module() -> Option<(u64, u64)> {
1618        None
1619    }
1620
1621    pub fn fs_ext4_module() -> Option<(u64, u64)> {
1622        None
1623    }
1624
1625    pub fn strate_fs_ramfs_module() -> Option<(u64, u64)> {
1626        None
1627    }
1628
1629    pub fn init_module() -> Option<(u64, u64)> {
1630        None
1631    }
1632
1633    pub fn console_admin_module() -> Option<(u64, u64)> {
1634        None
1635    }
1636
1637    pub fn strate_net_module() -> Option<(u64, u64)> {
1638        None
1639    }
1640
1641    pub fn strate_bus_module() -> Option<(u64, u64)> {
1642        None
1643    }
1644
1645    pub fn dhcp_client_module() -> Option<(u64, u64)> {
1646        None
1647    }
1648
1649    pub fn ping_module() -> Option<(u64, u64)> {
1650        None
1651    }
1652
1653    pub fn telnetd_module() -> Option<(u64, u64)> {
1654        None
1655    }
1656
1657    pub fn udp_tool_module() -> Option<(u64, u64)> {
1658        None
1659    }
1660
1661    pub fn strate_wasm_module() -> Option<(u64, u64)> {
1662        None
1663    }
1664
1665    pub fn hello_wasm_module() -> Option<(u64, u64)> {
1666        None
1667    }
1668
1669    pub fn wasm_test_toml_module() -> Option<(u64, u64)> {
1670        None
1671    }
1672
1673    pub fn strate_webrtc_module() -> Option<(u64, u64)> {
1674        None
1675    }
1676
1677    pub fn web_admin_module() -> Option<(u64, u64)> {
1678        None
1679    }
1680}
1681
1682/// Unified access to the `x86_64` crate surface used by shared code.
1683/// On x86_64 this IS the real crate; on riscv64 it is the panicking stub.
1684pub mod x86_crate_shim {
1685    #[cfg(target_arch = "x86_64")]
1686    pub use ::x86_64::*;
1687    #[cfg(target_arch = "x86_64")]
1688    pub use ::x86_64::{instructions, registers, structures};
1689
1690    #[cfg(not(target_arch = "x86_64"))]
1691    pub use crate::arch::x86_64::*;
1692}