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#![feature(offset_of)]
17
18extern crate alloc;
19
20// OSTD-like abstraction layer (minimal unsafe TCB)
21pub mod ostd;
22
23pub mod acpi;
24pub mod arch;
25pub mod audit;
26pub mod boot;
27pub mod capability;
28pub mod components;
29pub mod debug;
30pub mod debug_cfg;
31
32pub mod async_io;
33pub mod dma;
34pub mod entropy;
35pub mod framebuffer;
36pub mod hardware;
37pub mod ipc;
38pub mod memory;
39pub mod namespace;
40pub mod process;
41pub mod shell;
42pub mod silo;
43pub mod sync;
44pub mod syscall;
45pub mod trace;
46pub mod vfs;
47
48// Re-export boot::limine::kmain as the main entry point
49pub use boot::limine::kmain;
50
51// serial_print! and serial_println! macros are #[macro_export]'ed
52// from arch::x86_64::serial and available at crate root automatically.
53
54/// Initialize serial output
55pub fn init_serial() {
56    arch::x86_64::serial::init();
57}
58
59/// Initialize the logger (uses serial)
60pub fn init_logger() {
61    boot::logger::init();
62}
63
64/// Initialize kernel components using the component system
65///
66/// This function initializes all kernel components in the correct order
67/// based on their dependencies and priorities.
68pub fn init_components(stage: component::InitStage) -> Result<(), component::ComponentInitError> {
69    component::init_all(stage)
70}
71
72use core::panic::PanicInfo;
73
74const PAGE_SIZE: u64 = 4096;
75const MAX_BOOT_MMAP_REGIONS_WORK: usize = 1024;
76
77/// Performs the null region operation.
78const fn null_region() -> boot::entry::MemoryRegion {
79    boot::entry::MemoryRegion {
80        base: 0,
81        size: 0,
82        kind: boot::entry::MemoryKind::Reserved,
83    }
84}
85
86/// Performs the align down operation.
87#[inline]
88const fn align_down(value: u64, align: u64) -> u64 {
89    value & !(align - 1)
90}
91
92/// Performs the align up operation.
93#[inline]
94const fn align_up(value: u64, align: u64) -> u64 {
95    (value + align - 1) & !(align - 1)
96}
97
98/// Performs the virt or phys to phys operation.
99#[inline]
100const fn virt_or_phys_to_phys(addr: u64, hhdm: u64) -> u64 {
101    if hhdm != 0 && addr >= hhdm {
102        addr - hhdm
103    } else {
104        addr
105    }
106}
107
108/// Performs the reserve range in map operation.
109fn reserve_range_in_map(
110    map: &mut [boot::entry::MemoryRegion],
111    len: &mut usize,
112    reserve_start: u64,
113    reserve_end: u64,
114) {
115    if reserve_start >= reserve_end {
116        return;
117    }
118
119    let mut i = 0usize;
120    while i < *len {
121        let region = map[i];
122        if !matches!(
123            region.kind,
124            boot::entry::MemoryKind::Free | boot::entry::MemoryKind::Reclaim
125        ) {
126            i += 1;
127            continue;
128        }
129
130        let region_start = region.base;
131        let region_end = region.base.saturating_add(region.size);
132        if reserve_end <= region_start || reserve_start >= region_end {
133            i += 1;
134            continue;
135        }
136
137        let overlap_start = core::cmp::max(region_start, reserve_start);
138        let overlap_end = core::cmp::min(region_end, reserve_end);
139
140        if overlap_start <= region_start && overlap_end >= region_end {
141            map[i].kind = boot::entry::MemoryKind::Reserved;
142            i += 1;
143            continue;
144        }
145
146        if overlap_start <= region_start {
147            map[i].base = overlap_end;
148            map[i].size = region_end.saturating_sub(overlap_end);
149            i += 1;
150            continue;
151        }
152
153        if overlap_end >= region_end {
154            map[i].size = overlap_start.saturating_sub(region_start);
155            i += 1;
156            continue;
157        }
158
159        let left = boot::entry::MemoryRegion {
160            base: region_start,
161            size: overlap_start.saturating_sub(region_start),
162            kind: region.kind,
163        };
164        let right = boot::entry::MemoryRegion {
165            base: overlap_end,
166            size: region_end.saturating_sub(overlap_end),
167            kind: region.kind,
168        };
169
170        if *len + 1 > map.len() {
171            map[i] = left;
172            i += 1;
173            continue;
174        }
175
176        for j in (i + 1..*len).rev() {
177            map[j + 1] = map[j];
178        }
179        map[i] = left;
180        map[i + 1] = right;
181        *len += 1;
182        i += 2;
183    }
184}
185
186#[inline]
187fn count_free_like_regions(map: &[boot::entry::MemoryRegion], len: usize) -> usize {
188    map[..len]
189        .iter()
190        .filter(|region| {
191            matches!(
192                region.kind,
193                boot::entry::MemoryKind::Free | boot::entry::MemoryKind::Reclaim
194            )
195        })
196        .count()
197}
198
199/// Performs the region kind for addr operation.
200#[cfg(feature = "selftest")]
201fn region_kind_for_addr(
202    map: &[boot::entry::MemoryRegion],
203    len: usize,
204    addr: u64,
205) -> Option<boot::entry::MemoryKind> {
206    map.iter().take(len).find_map(|r| {
207        let start = r.base;
208        let end = r.base.saturating_add(r.size);
209        if addr >= start && addr < end {
210            Some(r.kind)
211        } else {
212            None
213        }
214    })
215}
216
217/// Kernel panic handler
218#[panic_handler]
219fn panic_handler(info: &PanicInfo) -> ! {
220    boot::panic::panic_handler(info)
221}
222
223/// Performs the register initfs module operation.
224fn register_initfs_module(path: &str, module: Option<(u64, u64)>) {
225    let Some((base, size)) = module else {
226        return;
227    };
228    if base == 0 || size == 0 {
229        return;
230    }
231
232    let base_virt = memory::phys_to_virt(base) as *const u8;
233    let len = size as usize;
234    #[cfg(feature = "selftest")]
235    {
236        // Only peek small header bytes for debugging; no heap allocations.
237        let data = unsafe { core::slice::from_raw_parts(base_virt, len.min(4)) };
238        if data.len() == 4 {
239            serial_println!(
240                "[init] /initfs/{} source magic={:02x}{:02x}{:02x}{:02x} size={}",
241                path,
242                data[0],
243                data[1],
244                data[2],
245                data[3],
246                size
247            );
248        }
249    }
250
251    // Register the bootloader-provided module directly; keep it read-only.
252    if let Err(e) = vfs::register_initfs_file(path, base_virt, len) {
253        serial_println!("[init] Failed to register /initfs/{}: {:?}", path, e);
254    } else {
255        serial_println!("[init] Registered /initfs/{} ({} bytes)", path, size);
256    }
257}
258
259/// Performs the register boot initfs modules operation.
260fn register_boot_initfs_modules(initfs_base: u64, initfs_size: u64) {
261    let boot_test_pid = if initfs_base != 0 && initfs_size != 0 {
262        Some((initfs_base, initfs_size))
263    } else {
264        None
265    };
266    let initfs_modules = [
267        ("test_pid", boot_test_pid),
268        ("test_syscalls", crate::boot::limine::test_syscalls_module()),
269        ("test_mem", crate::boot::limine::test_mem_module()),
270        (
271            "test_mem_stressed",
272            crate::boot::limine::test_mem_stressed_module(),
273        ),
274        (
275            "test_mem_region",
276            crate::boot::limine::test_mem_region_module(),
277        ),
278        (
279            "test_mem_region_proc",
280            crate::boot::limine::test_mem_region_proc_module(),
281        ),
282        ("test_exec", crate::boot::limine::test_exec_module()),
283        (
284            "test_exec_helper",
285            crate::boot::limine::test_exec_helper_module(),
286        ),
287        ("fs-ext4", crate::boot::limine::fs_ext4_module()),
288        (
289            "strate-fs-ramfs",
290            crate::boot::limine::strate_fs_ramfs_module(),
291        ),
292        ("init", crate::boot::limine::init_module()),
293        ("console-admin", crate::boot::limine::console_admin_module()),
294        ("strate-net", crate::boot::limine::strate_net_module()),
295        ("strate-bus", crate::boot::limine::strate_bus_module()),
296        ("bin/dhcp-client", crate::boot::limine::dhcp_client_module()),
297        ("bin/ping", crate::boot::limine::ping_module()),
298        ("bin/telnetd", crate::boot::limine::telnetd_module()),
299        ("bin/udp-tool", crate::boot::limine::udp_tool_module()),
300        ("bin/web-admin", crate::boot::limine::web_admin_module()),
301        ("strate-wasm", crate::boot::limine::strate_wasm_module()),
302        ("strate-webrtc", crate::boot::limine::strate_webrtc_module()),
303        ("bin/hello.wasm", crate::boot::limine::hello_wasm_module()),
304        (
305            "wasm-test.toml",
306            crate::boot::limine::wasm_test_toml_module(),
307        ),
308    ];
309    for (path, module) in initfs_modules {
310        register_initfs_module(path, module);
311    }
312}
313
314/// Performs the boot module slice operation.
315#[inline]
316fn boot_module_slice(base: u64, size: u64) -> &'static [u8] {
317    let base_virt = memory::phys_to_virt(base);
318    unsafe { core::slice::from_raw_parts(base_virt as *const u8, size as usize) }
319}
320
321/// Performs the log boot module magics operation.
322#[cfg(feature = "selftest")]
323fn log_boot_module_magics(stage: &str) {
324    let modules = [
325        ("init", crate::boot::limine::init_module()),
326        ("console-admin", crate::boot::limine::console_admin_module()),
327        ("strate-net", crate::boot::limine::strate_net_module()),
328        ("strate-bus", crate::boot::limine::strate_bus_module()),
329        ("bin/dhcp-client", crate::boot::limine::dhcp_client_module()),
330        ("bin/ping", crate::boot::limine::ping_module()),
331        ("bin/telnetd", crate::boot::limine::telnetd_module()),
332        ("bin/udp-tool", crate::boot::limine::udp_tool_module()),
333        ("bin/web-admin", crate::boot::limine::web_admin_module()),
334        ("strate-wasm", crate::boot::limine::strate_wasm_module()),
335        ("strate-webrtc", crate::boot::limine::strate_webrtc_module()),
336        ("bin/hello.wasm", crate::boot::limine::hello_wasm_module()),
337        (
338            "wasm-test.toml",
339            crate::boot::limine::wasm_test_toml_module(),
340        ),
341    ];
342    for (name, module) in modules {
343        let Some((base, size)) = module else {
344            continue;
345        };
346        if size < 4 {
347            continue;
348        }
349        let ptr = memory::phys_to_virt(base) as *const u8;
350        let m0 = unsafe { core::ptr::read_volatile(ptr) };
351        let m1 = unsafe { core::ptr::read_volatile(ptr.add(1)) };
352        let m2 = unsafe { core::ptr::read_volatile(ptr.add(2)) };
353        let m3 = unsafe { core::ptr::read_volatile(ptr.add(3)) };
354        serial_println!(
355            "[init] Module magic [{}]: {} phys=0x{:x} magic={:02x}{:02x}{:02x}{:02x} size={}",
356            stage,
357            name,
358            base,
359            m0,
360            m1,
361            m2,
362            m3,
363            size
364        );
365    }
366}
367
368/// Performs the log boot module magics operation.
369#[cfg(not(feature = "selftest"))]
370fn log_boot_module_magics(_stage: &str) {}
371
372/// Main kernel initialization - called by bootloader entry points
373pub unsafe fn kernel_main(args: *const boot::entry::KernelArgs) -> ! {
374    // Invariant: interrupts must stay disabled throughout kernel_main until the
375    // scheduler is ready and the APIC timer is started (Asterinas pattern:
376    // interrupts are only enabled once, at the very end of init).
377    debug_assert!(
378        !arch::x86_64::interrupts_enabled(),
379        "interrupts must be disabled at boot entry"
380    );
381
382    // =============================================
383    // Phase 1: serial output (earliest debug output)
384    // =============================================
385    arch::x86_64::boot_timestamp::init();
386    crate::e9_println!("B0 kernel_main");
387    init_serial();
388
389    // Enable boot log prefix (timestamp) by default; can be disabled later if needed.
390    arch::x86_64::serial::set_boot_log_prefix_enabled(true);
391
392    init_logger();
393    boot_milestone!("Kernel entry");
394    arch::x86_64::speaker::beep_phase(1);
395
396    // =============================================
397    // Phase 1c: IDT (Interrupt Descriptor Table)
398    // =============================================
399    // We initialize the IDT as early as possible to catch any exceptions
400    // during the early memory management and hardware initialization phases.
401    crate::e9_println!("B1 pre-IDT");
402    serial_println!("[init] IDT (early)...");
403    arch::x86_64::idt::init();
404    serial_println!("[init] IDT initialized.");
405    crate::e9_println!("B2 post-IDT");
406    boot_milestone!("IDT initialized");
407
408    debug_assert!(
409        !arch::x86_64::interrupts_enabled(),
410        "interrupts must be disabled after IDT init"
411    );
412
413    // Detect CPU features (must happen before init_cpu_extensions)
414    crate::arch::x86_64::cpuid::init();
415
416    // Initialize FPU/SSE/XSAVE for the BSP
417    crate::e9_println!("B2a pre-cpu-extensions");
418    crate::arch::x86_64::init_cpu_extensions();
419    crate::e9_println!("B2b post-cpu-extensions");
420
421    // Seed the kernel entropy pool from RDRAND (if available).
422    crate::e9_println!("B2c pre-entropy");
423    crate::entropy::seed_from_rdrand();
424    crate::e9_println!("B2d post-entropy");
425
426    // Puts default panic hooks early to ensure
427    //we get useful info on any panics during init.
428    crate::e9_println!("B2e pre-panic-hooks");
429    boot::panic::install_default_panic_hooks();
430    crate::e9_println!("B2f post-panic-hooks");
431
432    // Nice logo :D
433    serial_println!();
434    serial_println!();
435    serial_println!(r"          __                 __   ________                         ");
436    serial_println!(r"  _______/  |_____________ _/  |_/   __   \           ____  ______ ");
437    serial_println!(r" /  ___/\   __\_  __ \__  \\   __\____    /  ______  /  _ \/  ___/ ");
438    serial_println!(r" \___ \  |  |  |  | \// __ \|  |    /    /  /_____/ (  <_> )___ \  ");
439    serial_println!(r"/____  > |__|  |__|  (____  /__|   /____/            \____/____  > ");
440    serial_println!(r"     \/                   \/                                   \/  ");
441    serial_println!();
442
443    serial_println!("");
444    serial_println!("=======================================================================================================");
445    serial_println!("  strat9-OS kernel v0.1.0 (Bedrock)");
446    serial_println!("  Copyright (c) 2024-26 Guillaume Gielly - GPLv3 License");
447    serial_println!("");
448    serial_println!("  This software is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY, without");
449    serial_println!(
450        "  even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE."
451    );
452    serial_println!("  See the GNU General Public License for more details.");
453    serial_println!("=======================================================================================================");
454    serial_println!();
455
456    // Validate arguments
457    if args.is_null() {
458        serial_println!("[CRIT] No KernelArgs provided. System will hang.");
459        loop {
460            arch::x86_64::hlt();
461        }
462    }
463
464    let args = &*args;
465    serial_println!("[init] KernelArgs at {:p}", args);
466
467    if args.magic != strat9_abi::boot::STRAT9_BOOT_MAGIC {
468        serial_println!(
469            "[CRIT] Bad KernelArgs magic: 0x{:08x} (expected 0x{:08x})",
470            args.magic,
471            strat9_abi::boot::STRAT9_BOOT_MAGIC
472        );
473        loop {
474            arch::x86_64::hlt();
475        }
476    }
477    if args.abi_version != strat9_abi::boot::STRAT9_BOOT_ABI_VERSION {
478        serial_println!(
479            "[CRIT] Unsupported boot ABI version: {} (kernel expects {})",
480            args.abi_version,
481            strat9_abi::boot::STRAT9_BOOT_ABI_VERSION
482        );
483        loop {
484            arch::x86_64::hlt();
485        }
486    }
487
488    // Parse kernel cmdline from Limine (early, for serial console config).
489    if args.cmdline_ptr != 0 && args.cmdline_len != 0 {
490        // SAFETY: cmdline_ptr is a valid null-terminated C string from Limine bootloader.
491        unsafe { arch::x86_64::serial::parse_cmdline(args.cmdline_ptr, args.cmdline_len) };
492    } else {
493        serial_println!("[init] No kernel cmdline provided");
494    }
495
496    // Le's go !
497    //
498    // =============================================
499    // Phase 1b : HHDM offset (must be set before any physical memory access)
500    // =============================================
501    let hhdm = args.hhdm_offset;
502    memory::set_hhdm_offset(hhdm);
503    serial_println!("[init] HHDM offset: 0x{:x}", hhdm);
504
505    serial_println!(
506        "[init] Memory map: 0x{:x} ({} bytes)",
507        args.memory_map_base,
508        args.memory_map_size
509    );
510
511    log_boot_module_magics("pre-mm");
512
513    // =============================================
514    // Phase 2 : memory management (Buddy Allocator)
515    // =============================================
516    serial_println!("[init] Memory manager...");
517    let mmap_ptr = args.memory_map_base as *const boot::entry::MemoryRegion;
518    let mmap_len =
519        args.memory_map_size as usize / core::mem::size_of::<boot::entry::MemoryRegion>();
520    let mmap = core::slice::from_raw_parts(mmap_ptr, mmap_len);
521    let mut mmap_work = [null_region(); MAX_BOOT_MMAP_REGIONS_WORK];
522    let mut mmap_work_len = core::cmp::min(mmap.len(), mmap_work.len());
523    for (dst, src) in mmap_work.iter_mut().zip(mmap.iter()).take(mmap_work_len) {
524        *dst = *src;
525    }
526
527    let reserve_modules = [
528        (
529            "test_pid",
530            if args.initfs_base != 0 && args.initfs_size != 0 {
531                Some((args.initfs_base, args.initfs_size))
532            } else {
533                None
534            },
535        ),
536        ("test_syscalls", crate::boot::limine::test_syscalls_module()),
537        ("test_mem", crate::boot::limine::test_mem_module()),
538        (
539            "test_mem_stressed",
540            crate::boot::limine::test_mem_stressed_module(),
541        ),
542        ("fs-ext4", crate::boot::limine::fs_ext4_module()),
543        (
544            "strate-fs-ramfs",
545            crate::boot::limine::strate_fs_ramfs_module(),
546        ),
547        ("init", crate::boot::limine::init_module()),
548        ("console-admin", crate::boot::limine::console_admin_module()),
549        ("strate-net", crate::boot::limine::strate_net_module()),
550        ("strate-bus", crate::boot::limine::strate_bus_module()),
551        ("bin/dhcp-client", crate::boot::limine::dhcp_client_module()),
552        ("bin/ping", crate::boot::limine::ping_module()),
553        ("bin/telnetd", crate::boot::limine::telnetd_module()),
554        ("bin/udp-tool", crate::boot::limine::udp_tool_module()),
555        ("strate-wasm", crate::boot::limine::strate_wasm_module()),
556        ("bin/hello.wasm", crate::boot::limine::hello_wasm_module()),
557        (
558            "wasm-test.toml",
559            crate::boot::limine::wasm_test_toml_module(),
560        ),
561    ];
562
563    let mut protected_ranges = [None; memory::boot_alloc::MAX_PROTECTED_RANGES];
564    for (idx, (_name, module)) in reserve_modules.iter().enumerate() {
565        if idx >= protected_ranges.len() {
566            break;
567        }
568        protected_ranges[idx] = *module;
569    }
570    memory::boot_alloc::set_protected_ranges(&protected_ranges);
571
572    // Initialize the boot allocator before manually carving the working memory
573    // map. The allocator excludes the configured protected ranges itself, so
574    // it can still see the large original free extents that VMware exposes
575    // before module reservations fragment them.
576    memory::boot_alloc::init_boot_allocator(&mmap_work[..mmap_work_len]);
577    serial_println!("[init] Boot allocator ready.");
578
579    let total_ram = mmap_work[..mmap_work_len]
580        .iter()
581        .filter(|region| {
582            matches!(
583                region.kind,
584                boot::entry::MemoryKind::Free | boot::entry::MemoryKind::Reclaim
585            )
586        })
587        .map(|region| region.base.saturating_add(region.size))
588        .max()
589        .unwrap_or(0);
590    let free_like_regions = count_free_like_regions(&mmap_work, mmap_work_len);
591    let metadata_bytes = memory::frame::metadata_size_for(total_ram) as usize;
592    let boot_stats = memory::boot_alloc::boot_allocator_stats();
593    serial_println!(
594        "[init] Frame metadata plan: total_ram={:#x} free_regions={} bytes={} boot_free={} largest_boot_region={}",
595        total_ram,
596        free_like_regions,
597        metadata_bytes,
598        boot_stats.total_free_bytes as usize,
599        boot_stats.largest_region_bytes as usize,
600    );
601
602    {
603        let mut boot_alloc = memory::boot_alloc::get_boot_allocator().lock();
604        memory::frame::init_metadata_array(total_ram, &mut *boot_alloc);
605    }
606    serial_println!("[init] Frame metadata ready.");
607
608    for (_name, module) in reserve_modules {
609        let Some((base, size)) = module else {
610            continue;
611        };
612        if size == 0 {
613            continue;
614        }
615        let phys = virt_or_phys_to_phys(base, hhdm);
616        let reserve_start = align_down(phys, PAGE_SIZE);
617        let reserve_end = align_up(phys.saturating_add(size), PAGE_SIZE);
618        let before_regions = count_free_like_regions(&mmap_work, mmap_work_len);
619        reserve_range_in_map(
620            &mut mmap_work,
621            &mut mmap_work_len,
622            reserve_start,
623            reserve_end,
624        );
625        let after_regions = count_free_like_regions(&mmap_work, mmap_work_len);
626        serial_println!(
627            "[init] reserve_range_in_map: {} phys=0x{:x}..0x{:x} free_regions {} -> {}",
628            _name,
629            reserve_start,
630            reserve_end,
631            before_regions,
632            after_regions
633        );
634        #[cfg(feature = "selftest")]
635        {
636            serial_println!(
637                "[init] Reserved module pages: {} phys=0x{:x}..0x{:x}",
638                _name,
639                reserve_start,
640                reserve_end
641            );
642            let kind = region_kind_for_addr(&mmap_work, mmap_work_len, reserve_start);
643            serial_println!(
644                "[init] Module map kind: {} @0x{:x} => {:?}",
645                _name,
646                reserve_start,
647                kind
648            );
649        }
650    }
651
652    memory::buddy::init_buddy_allocator(&mmap_work[..mmap_work_len]);
653
654    serial_println!("[init] Buddy allocator ready.");
655
656    // Initialize the vmalloc arena (VM-backed large heap allocations)
657    memory::vmalloc::init();
658    serial_println!("[init] Vmalloc arena ready.");
659
660    debug_assert!(
661        !arch::x86_64::interrupts_enabled(),
662        "interrupts must be disabled after buddy allocator init"
663    );
664
665    boot_milestone!("Memory manager ready");
666    arch::x86_64::speaker::beep_phase(2);
667    log_boot_module_magics("post-buddy");
668
669    // =============================================
670    // Phase 2.5: paging / VMM (Must be before Console if FB is not already mapped)
671    // =============================================
672    crate::e9_println!("B5 pre-paging");
673    serial_println!("[init] Paging...");
674    memory::paging::init(hhdm);
675    crate::e9_println!("B6 post-paging");
676
677    // Map all RAM into HHDM to ensure buddy/heap allocations are accessible.
678    // VMware Limine HHDM may be sparse, causing PF on new heap pages.
679    memory::paging::map_all_ram(&mmap_work[..mmap_work_len]);
680
681    // Framebuffer is often backed by MMIO memory outside RAM (e.g. around 0xFDxxxxxx),
682    // or sometimes at the very end of RAM that might be missed by the bootloader's initial map.
683    // Explicitly map its full range in HHDM for all later graphics access.
684    if args.framebuffer_addr != 0 && args.framebuffer_stride != 0 && args.framebuffer_height != 0 {
685        let fb_phys = if args.framebuffer_addr >= hhdm {
686            args.framebuffer_addr - hhdm
687        } else {
688            args.framebuffer_addr
689        };
690        let fb_size =
691            (args.framebuffer_stride as u64).saturating_mul(args.framebuffer_height as u64);
692        memory::paging::ensure_identity_map_range(fb_phys, fb_size);
693        serial_println!(
694            "[init] Framebuffer mapped: phys=0x{:x} size={} bytes",
695            fb_phys,
696            fb_size
697        );
698    }
699    serial_println!("[init] Paging initialized.");
700    boot_milestone!("Paging initialized");
701    arch::x86_64::speaker::beep_phase(3);
702
703    // =============================================
704    // Phase 3: console output (VGA or serial fallback)
705    // =============================================
706    serial_println!("[init] Console...");
707    arch::x86_64::vga::init(
708        args.framebuffer_addr,
709        args.framebuffer_width,
710        args.framebuffer_height,
711        args.framebuffer_stride,
712        args.framebuffer_bpp,
713        args.framebuffer_red_mask_size,
714        args.framebuffer_red_mask_shift,
715        args.framebuffer_green_mask_size,
716        args.framebuffer_green_mask_shift,
717        args.framebuffer_blue_mask_size,
718        args.framebuffer_blue_mask_shift,
719    );
720    // Flush any log lines buffered before VGA was available.
721    arch::x86_64::vgabuf::vgabuf_flush_to_framebuffer();
722    vga_println!("[OK] Paging initialized");
723    vga_println!("[OK] Serial port initialized");
724    vga_println!("[OK] Memory manager active");
725
726    // =============================================
727    // Phase 4a : TSS (Task State Segment)
728    // =============================================
729    serial_println!("[init] TSS...");
730    vga_println!("[..] Initializing TSS...");
731    arch::x86_64::tss::init();
732    serial_println!("[init] TSS initialized.");
733    vga_println!("[OK] TSS initialized");
734
735    // =============================================
736    // Phase 4b : GDT (global Descriptor Table)
737    // =============================================
738    serial_println!("[init] GDT...");
739    vga_println!("[..] Initializing GDT...");
740    arch::x86_64::gdt::init();
741    serial_println!("[init] GDT initialized.");
742    vga_println!("[OK] GDT loaded (with TSS)");
743
744    // =============================================
745    // Phase 4c: SYSCALL/SYSRET MSR configuration
746    // =============================================
747    serial_println!("[init] SYSCALL/SYSRET...");
748    vga_println!("[..] Initializing SYSCALL/SYSRET...");
749    arch::x86_64::syscall::init();
750    serial_println!("[init] SYSCALL/SYSRET initialized.");
751    vga_println!("[OK] SYSCALL/SYSRET configured");
752
753    // =============================================
754    // Phase 4d: component system - Bootstrap stage
755    // =============================================
756    serial_println!("[init] Components (bootstrap)...");
757    vga_println!("[..] Initializing bootstrap components...");
758    if let Err(e) = component::init_all(component::InitStage::Bootstrap) {
759        serial_println!("[WARN] Some bootstrap components failed: {:?}", e);
760    }
761    serial_println!("[init] Bootstrap components initialized.");
762    vga_println!("[OK] Bootstrap components ready");
763
764    // =============================================
765    // Phase 5: IDT (Interrupt Descriptor Table) - ALREADY INITIALIZED EARLY
766    // =============================================
767    // arch::x86_64::idt::init();
768
769    // =============================================
770    // Phase 5b: paging / VMM - (Moved earlier to prevent PF on VGA init)
771    // =============================================
772    log_boot_module_magics("post-paging");
773
774    // =============================================
775    // Phase 5c: kernel address space
776    // =============================================
777    serial_println!("[init] Kernel address space...");
778    vga_println!("[..] Initializing kernel address space...");
779    memory::address_space::init_kernel_address_space();
780    serial_println!("[init] Kernel address space initialized.");
781    debug_assert!(
782        !arch::x86_64::interrupts_enabled(),
783        "interrupts must be disabled after kernel address space init"
784    );
785    vga_println!("[OK] Kernel address space initialized");
786    log_boot_module_magics("post-kas");
787
788    // =============================================
789    // Phase 5d: virtual file system
790    // =============================================
791    serial_println!("[init] VFS...");
792    vga_println!("[..] Initializing virtual file system...");
793
794    vfs::init();
795
796    serial_println!("[init] VFS initialized.");
797    vga_println!("[OK] VFS initialized");
798    register_boot_initfs_modules(args.initfs_base, args.initfs_size);
799
800    log_boot_module_magics("post-cow");
801
802    // =============================================
803    // Phase 6: ACPI + APIC (with PIC fallback)
804    // =============================================
805    serial_println!("[init] Interrupt controller...");
806    vga_println!("[..] Initializing interrupt controller...");
807
808    // Ensure RSDP is mapped (it might be in unmapped legacy region)
809    memory::paging::ensure_identity_map(args.acpi_rsdp_base);
810
811    let rsdp_virt = memory::phys_to_virt(args.acpi_rsdp_base);
812    let apic_active = init_apic_subsystem(rsdp_virt);
813
814    if !apic_active {
815        // Fallback: legacy PIC + PIT
816        serial_println!("[init] APIC unavailable, falling back to legacy PIC");
817        vga_println!("[..] Falling back to legacy PIC...");
818        arch::x86_64::pic::init(
819            arch::x86_64::pic::PIC1_OFFSET,
820            arch::x86_64::pic::PIC2_OFFSET,
821        );
822        arch::x86_64::pic::disable();
823        arch::x86_64::pic::enable_irq(0); // Timer
824        arch::x86_64::pic::enable_irq(1); // Keyboard
825        serial_println!("[init] Legacy PIC initialized.");
826        vga_println!("[OK] Legacy PIC initialized (IRQ0: timer, IRQ1: keyboard)");
827    } else {
828        serial_println!("[init] APIC subsystem initialized.");
829        vga_println!("[OK] APIC + I/O APIC + APIC timer active");
830    }
831
832    // Initialize TLB shootdown system (SMP safety for COW operations).
833    if apic_active {
834        arch::x86_64::tlb::init();
835        serial_println!("[init] TLB shootdown system initialized.");
836        debug_assert!(
837            !arch::x86_64::interrupts_enabled(),
838            "interrupts must be disabled after TLB init"
839        );
840    }
841
842    // ================================================
843    // Phase 6j: SMP bring-up (AP boot) + per-CPU data
844    // ================================================
845    if apic_active {
846        let bsp_apic_id = arch::x86_64::apic::lapic_id();
847        arch::x86_64::percpu::init_boot_cpu(bsp_apic_id);
848        arch::x86_64::percpu::init_gs_base(0);
849        serial_println!("[init] SMP: booting secondary cores...");
850        vga_println!("[..] SMP: starting APs...");
851
852        match arch::x86_64::smp::init() {
853            Ok(count) => {
854                serial_println!("[init] SMP: {} core(s) online", count);
855                vga_println!("[OK] SMP: {} core(s) online", count);
856            }
857            Err(e) => {
858                serial_println!("[init] SMP init failed: {}", e);
859                vga_println!("[WARN] SMP init failed: {}", e);
860            }
861        }
862    } else {
863        arch::x86_64::percpu::init_boot_cpu(0);
864    }
865    boot_milestone!("APIC + SMP ready");
866    arch::x86_64::speaker::beep_phase(4);
867
868    arch::x86_64::keyboard::init();
869    serial_println!("[init] PS/2 keyboard controller initialized.");
870
871    // =============================================
872    // Phase 6k: PS/2 mouse driver
873    // =============================================
874    if apic_active {
875        let mouse_ok = arch::x86_64::mouse::init();
876
877        if mouse_ok {
878            serial_println!("[init] PS/2 mouse initialized.");
879            vga_println!("[OK] PS/2 mouse ready");
880        } else {
881            serial_println!("[init] PS/2 mouse not found (optional).");
882        }
883    }
884
885    // =============================================
886    // Phase 7: initialize scheduler
887    // =============================================
888    crate::e9_println!("B7 pre-sched");
889    serial_println!("[init] Initializing scheduler...");
890    vga_println!("[..] Setting up multitasking...");
891    // Print struct layout for crash-site offset analysis (debug build only).
892    crate::process::task::Task::debug_print_layout();
893    process::init_scheduler();
894    crate::e9_println!("B8 post-sched");
895
896    debug_assert!(
897        !arch::x86_64::interrupts_enabled(),
898        "interrupts must be disabled after scheduler init"
899    );
900
901    // =============================================
902    // Phase 7+: Start timer
903    // =============================================
904    // The BSP timer only starts when the scheduler is ready to handle interrupts.
905    // This is the last point where interrupts are guaranteed disabled on BSP.
906    if apic_active {
907        debug_assert!(
908            !arch::x86_64::interrupts_enabled(),
909            "interrupts must be disabled before APIC timer start"
910        );
911        serial_println!("[init] Starting APIC timer on BSP...");
912        arch::x86_64::timer::start_apic_timer_cached();
913    }
914
915    serial_println!("[init] Scheduler initialized.");
916    serial_println!("[trace][bsp] after init_scheduler");
917    boot_milestone!("Scheduler + timer ready");
918    arch::x86_64::speaker::beep_phase(5);
919    vga_println!("[OK] Multitasking enabled");
920
921    // =============================================
922    // Phase 7b: component system - Kthread stage
923    // =============================================
924    crate::e9_println!("B9 pre-kthread");
925    serial_println!("[trace][bsp] before kthread init_all");
926    serial_println!("[init] Components (kthread)...");
927    vga_println!("[..] Initializing kthread components...");
928
929    if let Err(e) = component::init_all(component::InitStage::Kthread) {
930        serial_println!("[WARN] Some kthread components failed: {:?}", e);
931    }
932
933    serial_println!("[trace][bsp] after kthread init_all");
934    serial_println!("[init] Kthread components initialized.");
935    vga_println!("[OK] Kthread components ready");
936
937    #[cfg(feature = "selftest")]
938    {
939        // =============================================
940        // Phase 8a: runtime self-tests
941        // =============================================
942        serial_println!("[init] Creating self-test tasks...");
943        vga_println!("[..] Adding self-test tasks...");
944        process::selftest::create_selftest_tasks();
945        serial_println!("[init] Self-test tasks created.");
946        vga_println!("[OK] Self-test tasks added");
947    }
948
949    // Ring3 smoke test task disabled in selftest mode: fork-test already
950    // exercises Ring3 transitions and this extra task can interfere.
951
952    #[cfg(not(feature = "selftest"))]
953    {
954        // =============================================
955        // Phase 8c: process components
956        // =============================================
957        let mut init_task_id: Option<crate::process::TaskId> = None;
958
959        crate::e9_println!("BB pre-process");
960        serial_println!("[init] Components (process)...");
961        vga_println!("[..] Initializing process components...");
962        if let Err(e) = component::init_all(component::InitStage::Process) {
963            serial_println!("[WARN] Some process components failed: {:?}", e);
964        }
965        serial_println!("[init] Process components initialized.");
966        vga_println!("[OK] Process components ready");
967
968        // =============================================
969        // Phase 8d: VirtIO + hardware drivers
970        // =============================================
971        // Blocking launch order note:
972        // PCI consumers now rely on /bus/pci/* exposed by userspace strate-bus.
973        // The userspace boot sequence must start silo "bus" before PCI-dependent
974        // silos, otherwise early probes can legitimately return no device.
975        crate::e9_println!("BG pre-hardware");
976        serial_println!("[init] Loading hardware drivers...");
977        vga_println!("[..] Initializing hardware drivers...");
978        hardware::init();
979        crate::e9_println!("BH post-hardware");
980        boot_milestone!("Hardware drivers ready");
981        arch::x86_64::speaker::beep_phase(15);
982        arch::x86_64::speaker::beep_phase(6);
983
984        serial_println!("[init] Initializing timers...");
985        vga_println!("[..] Initializing HPET and RTC...");
986        hardware::timer::init();
987        serial_println!("[init] Timers initialized.");
988        vga_println!("[OK] HPET/RTC initialized");
989        arch::x86_64::speaker::beep_phase(16); // Timers
990
991        serial_println!("[init] Initializing USB...");
992        vga_println!("[..] Looking for USB controllers...");
993        hardware::usb::init();
994        serial_println!("[init] USB initialized.");
995        vga_println!("[OK] USB xHCI/EHCI/UHCI initialized");
996        arch::x86_64::speaker::beep_phase(17); // USB
997
998        serial_println!("[init] Initializing VirtIO block...");
999        vga_println!("[..] Looking for VirtIO block device...");
1000        hardware::storage::virtio_block::init();
1001        serial_println!("[init] VirtIO block initialized.");
1002        vga_println!("[OK] VirtIO block driver initialized");
1003        arch::x86_64::speaker::beep_phase(18); // VirtIO block
1004
1005        serial_println!("[init] Initializing AHCI...");
1006        vga_println!("[..] Looking for AHCI SATA controller...");
1007        // Debug: check BSP stack usage before AHCI init
1008        {
1009            let dummy = 0u64;
1010            let rsp = &dummy as *const u64 as u64;
1011            serial_println!("[DEBUG] BSP stack before AHCI: rsp={:#x}", rsp);
1012            // Stack grows downward from high address. Check distance from typical low addresses.
1013            // With 512KB stack at 0x80000, top is around 0x80000 + 0x80000 = 0x100000
1014            // If rsp is below 0xC0000, we've used more than 256KB
1015            if rsp < 0xC0000 {
1016                serial_println!("[WARN] BSP stack usage high! rsp={:#x}", rsp);
1017            }
1018        }
1019        hardware::storage::ahci::init();
1020        serial_println!("[init] AHCI probe done.");
1021        vga_println!("[OK] AHCI probe done");
1022        arch::x86_64::speaker::beep_phase(19); // AHCI
1023
1024        serial_println!("[init] Initializing ATA/IDE...");
1025        vga_println!("[..] Looking for ATA/IDE devices...");
1026        hardware::storage::ata_legacy::init();
1027        serial_println!("[init] ATA/IDE probe done.");
1028        vga_println!("[OK] ATA/IDE probe done");
1029        arch::x86_64::speaker::beep_phase(20); // ATA
1030
1031        serial_println!("[init] Initializing NVMe...");
1032        vga_println!("[..] Looking for NVMe controllers...");
1033        hardware::storage::nvme::init();
1034        serial_println!("[init] NVMe probe done.");
1035        vga_println!("[OK] NVMe probe done");
1036        arch::x86_64::speaker::beep_phase(21); // NVMe
1037
1038        serial_println!("[init] Initializing VirtIO net...");
1039        vga_println!("[..] Looking for VirtIO net device...");
1040        hardware::nic::virtio_net::init();
1041        serial_println!("[init] VirtIO net initialized.");
1042        vga_println!("[OK] VirtIO net driver initialized");
1043        arch::x86_64::speaker::beep_phase(22); // VirtIO net
1044
1045        serial_println!("[init] Initializing VirtIO RNG...");
1046        vga_println!("[..] Looking for VirtIO RNG device...");
1047        crate::hardware::virtio::rng::init();
1048        serial_println!("[init] VirtIO RNG initialized.");
1049        vga_println!("[OK] VirtIO RNG driver initialized");
1050        arch::x86_64::speaker::beep_phase(23); // VirtIO RNG
1051
1052        serial_println!("[init] Initializing VirtIO Console...");
1053        vga_println!("[..] Looking for VirtIO Console device...");
1054        crate::hardware::virtio::console::init();
1055        serial_println!("[init] VirtIO Console initialized.");
1056        vga_println!("[OK] VirtIO Console driver initialized");
1057        arch::x86_64::speaker::beep_phase(24); // VirtIO Console
1058
1059        // VirtIO GPU + framebuffer are initialized in hardware::init()
1060
1061        serial_println!("[init] Checking for devices...");
1062        vga_println!("[..] Checking for devices...");
1063
1064        if let Some(blk) = hardware::storage::virtio_block::get_device() {
1065            use hardware::storage::virtio_block::BlockDevice;
1066            serial_println!(
1067                "[INFO] VirtIO block device found. Capacity: {} sectors",
1068                blk.sector_count()
1069            );
1070            vga_println!("[OK] VirtIO block driver loaded");
1071        } else {
1072            serial_println!("[WARN] No VirtIO block device found");
1073            vga_println!("[WARN] No VirtIO block device found");
1074        }
1075
1076        if let Some(ahci) = hardware::storage::ahci::get_device() {
1077            serial_println!(
1078                "[INFO] AHCI SATA device found. Capacity: {} sectors ({} MiB)",
1079                ahci.sector_count(),
1080                (ahci.sector_count() * 512) / 1048576, // 1024*1024 bytes per MiB, 512 bytes per sector
1081            );
1082            vga_println!("[OK] AHCI SATA driver loaded");
1083        } else {
1084            serial_println!("[INFO] No AHCI SATA device found");
1085        }
1086
1087        if let Some(nvme) = hardware::storage::nvme::get_first_controller() {
1088            let nvme = nvme.lock();
1089            if let Some(ns) = nvme.get_namespace(0) {
1090                serial_println!(
1091                    "[INFO] NVMe device found. Namespace {} - {} blocks @ {} bytes ({} MiB)",
1092                    ns.nsid,
1093                    ns.size,
1094                    ns.block_size,
1095                    (ns.size * ns.block_size as u64) / 1048576, // 1024*1024 bytes per MiB
1096                );
1097                vga_println!("[OK] NVMe driver loaded");
1098            }
1099        } else {
1100            serial_println!("[INFO] No NVMe device found");
1101        }
1102
1103        // Report all registered network interfaces (E1000 + VirtIO)
1104        {
1105            let ifaces = hardware::nic::list_interfaces();
1106            if ifaces.is_empty() {
1107                serial_println!("[WARN] No network devices found");
1108                vga_println!("[WARN] No network devices found");
1109            } else {
1110                for name in &ifaces {
1111                    if let Some(dev) = hardware::nic::get_device(name) {
1112                        let mac = dev.mac_address();
1113                        serial_println!(
1114                            "[INFO] Network {} ({}) MAC {:02x}:{:02x}:{:02x}:{:02x}:{:02x}:{:02x} link={}",
1115                            name, dev.name(),
1116                            mac[0], mac[1], mac[2], mac[3], mac[4], mac[5],
1117                            if dev.link_up() { "up" } else { "down" },
1118                        );
1119                        vga_println!("[OK] Network {} ({}) loaded", name, dev.name());
1120                    }
1121                }
1122            }
1123        }
1124
1125        serial_println!("[init] Storage verification skipped (boot path)");
1126        vga_println!("[..] Storage verification skipped at boot");
1127
1128        // Launch the init process: prefer /initfs/init, fall back to /initfs/test_pid.
1129        // The fallback is tried both when the primary module is absent AND when it
1130        // is present but contains an invalid ELF (corrupt / wrong arch).
1131        let mut init_loaded = false;
1132
1133        if let Some((base, size)) = crate::boot::limine::init_module() {
1134            let elf_data = boot_module_slice(base, size);
1135            let init_caps = [crate::silo::create_silo_admin_capability()];
1136            match process::elf::load_and_run_elf_with_caps(elf_data, "init", &init_caps) {
1137                Ok(task_id) => {
1138                    init_task_id = Some(task_id);
1139                    init_loaded = true;
1140                    serial_println!("[init] ELF '/initfs/init' loaded as task 'init'.");
1141                }
1142                Err(e) => {
1143                    serial_println!("[init] Failed to load init ELF: {}; trying fallback.", e);
1144                }
1145            }
1146        }
1147
1148        if !init_loaded && args.initfs_base != 0 && args.initfs_size != 0 {
1149            let elf_data = boot_module_slice(args.initfs_base, args.initfs_size);
1150            let init_caps = [crate::silo::create_silo_admin_capability()];
1151            match process::elf::load_and_run_elf_with_caps(elf_data, "init", &init_caps) {
1152                Ok(task_id) => {
1153                    init_task_id = Some(task_id);
1154                    serial_println!(
1155                        "[init] ELF '/initfs/test_pid' loaded as task 'init' (fallback)."
1156                    );
1157                }
1158                Err(e) => {
1159                    serial_println!("[init] Failed to load test_pid ELF: {}", e);
1160                }
1161            }
1162        }
1163        if let Some((base, size)) = crate::boot::limine::strate_fs_ramfs_module() {
1164            let ram_data = boot_module_slice(base, size);
1165            match process::elf::load_elf_task_with_caps(ram_data, "strate-fs-ramfs", &[]) {
1166                Ok(task) => {
1167                    let task_id = task.id;
1168                    crate::serial_force_println!(
1169                        "[trace][boot] before register_boot_strate_task tid={} label=ramfs-default",
1170                        task_id.as_u64()
1171                    );
1172                    let reg_res = crate::silo::register_boot_strate_task(task_id, "ramfs-default");
1173                    crate::serial_force_println!(
1174                        "[trace][boot] marker: returned from register_boot_strate_task"
1175                    );
1176                    if let Err(e) = reg_res {
1177                        crate::serial_force_println!(
1178                            "[trace][boot] register_boot_strate_task failed tid={} err={:?}",
1179                            task_id.as_u64(),
1180                            e
1181                        );
1182                    } else {
1183                        crate::serial_force_println!(
1184                            "[trace][boot] register_boot_strate_task ok tid={}",
1185                            task_id.as_u64()
1186                        );
1187                    }
1188                    crate::serial_force_println!(
1189                        "[trace][boot] before add_task tid={} name=strate-fs-ramfs",
1190                        task_id.as_u64()
1191                    );
1192                    process::add_task(task);
1193                    crate::serial_force_println!(
1194                        "[trace][boot] after add_task tid={} name=strate-fs-ramfs",
1195                        task_id.as_u64()
1196                    );
1197                    serial_println!("[init] Component 'strate-fs-ramfs' loaded.");
1198                }
1199                Err(e) => serial_println!("[init] Failed to load strate-fs-ramfs component: {}", e),
1200            }
1201        }
1202        if let Some((base, size)) = crate::boot::limine::fs_ext4_module() {
1203            let ext4_data = boot_module_slice(base, size);
1204            match process::elf::load_elf_task_with_caps(ext4_data, "strate-fs-ext4", &[]) {
1205                Ok(task) => {
1206                    let task_id = task.id;
1207                    crate::serial_force_println!(
1208                        "[trace][boot] before register_boot_strate_task tid={} label=ext4-default",
1209                        task_id.as_u64()
1210                    );
1211                    if let Err(e) = crate::silo::register_boot_strate_task(task_id, "ext4-default")
1212                    {
1213                        crate::serial_force_println!(
1214                            "[trace][boot] register_boot_strate_task failed tid={} err={:?}",
1215                            task_id.as_u64(),
1216                            e
1217                        );
1218                    } else {
1219                        crate::serial_force_println!(
1220                            "[trace][boot] register_boot_strate_task ok tid={}",
1221                            task_id.as_u64()
1222                        );
1223                    }
1224                    crate::serial_force_println!(
1225                        "[trace][boot] before add_task tid={} name=strate-fs-ext4",
1226                        task_id.as_u64()
1227                    );
1228                    process::add_task(task);
1229                    crate::serial_force_println!(
1230                        "[trace][boot] after add_task tid={} name=strate-fs-ext4",
1231                        task_id.as_u64()
1232                    );
1233                    serial_println!("[init] Component 'strate-fs-ext4' loaded.");
1234                }
1235                Err(e) => serial_println!("[init] Failed to load strate-fs-ext4 component: {}", e),
1236            }
1237        }
1238        if let (Some(task_id), Some(device)) =
1239            (init_task_id, hardware::storage::virtio_block::get_device())
1240        {
1241            if let Some(task) = crate::process::get_task_by_id(task_id) {
1242                let cap = crate::capability::get_capability_manager().create_capability(
1243                    crate::capability::ResourceType::Volume,
1244                    device as *const _ as usize,
1245                    crate::capability::CapPermissions {
1246                        read: true,
1247                        write: true,
1248                        execute: false,
1249                        grant: true,
1250                        revoke: true,
1251                    },
1252                );
1253                unsafe { (&mut *task.process.capabilities.get()).insert(cap) };
1254                serial_println!("[init] Granted volume capability to init");
1255            }
1256        }
1257
1258        match process::Task::new_kernel_task_with_stack(
1259            shell::shell_main,
1260            "chevron-shell",
1261            process::TaskPriority::Normal,
1262            64 * 1024,
1263        ) {
1264            Ok(shell_task) => {
1265                process::add_task(shell_task);
1266                serial_println!("[init] Chevron shell ready.");
1267            }
1268            Err(e) => {
1269                serial_println!("[WARN] Failed to create shell task: {}", e);
1270            }
1271        }
1272        if let Ok(status_task) = process::Task::new_kernel_task_with_stack(
1273            arch::x86_64::vga::status_line_task_main,
1274            "status-line",
1275            process::TaskPriority::Low,
1276            64 * 1024,
1277        ) {
1278            process::add_task(status_task);
1279            // Switch from live VGA debug output to buffered vgabuf path.
1280            // The status_line_task will flush vgabuf to the framebuffer.
1281            crate::debug_cfg::set_vga_debug_live(false);
1282        }
1283    }
1284    #[cfg(feature = "selftest")]
1285    {
1286        serial_println!("[init] Selftest mode: skipping process services and virtio drivers");
1287    }
1288
1289    // Initialize keyboard layout to French by default
1290    crate::arch::x86_64::keyboard_layout::set_french_layout();
1291
1292    // =============================================
1293    // Boot complete : start preemptive multitasking
1294    // =============================================
1295    if apic_active {
1296        arch::x86_64::smp::open_ap_scheduler_gate();
1297    }
1298    crate::e9_println!("BC pre-schedule");
1299    boot_milestone!("Boot complete ! Now entering in scheduler");
1300    arch::x86_64::speaker::beep_startup();
1301    serial_println!("[init] Boot complete. Starting preemptive scheduler...");
1302    vga_println!("[OK] Starting multitasking (preemptive)");
1303    arch::x86_64::serial::set_boot_log_prefix_enabled(false);
1304
1305    // Keep interrupts disabled on the init stack. `schedule_on_cpu()` enters
1306    // the first task with IF=0 and `task_entry_trampoline` executes `sti`
1307    // after the task context is fully installed.
1308
1309    // Start the scheduler - this will never return
1310    serial_force_println!("[trace][bsp] schedule start (never returns)");
1311    process::schedule();
1312}
1313
1314/// Initialize the APIC subsystem (Local APIC + I/O APIC + APIC Timer).
1315///
1316/// Returns `true` if APIC is active, `false` if we should fall back to PIC+PIT.
1317/// On failure at any step, logs a warning and returns `false`.
1318fn init_apic_subsystem(rsdp_vaddr: u64) -> bool {
1319    use arch::x86_64::{apic, ioapic, pic, timer};
1320    use timer::TIMER_HZ;
1321
1322    // Step 6a: check CPUID for APIC support
1323    if !apic::is_present() {
1324        log::warn!("APIC: not present (CPUID)");
1325        return false;
1326    }
1327    serial_println!("[init]   6a. APIC present (CPUID)");
1328
1329    // Step 6b: initialize ACPI (validate RSDP)
1330    match acpi::init(rsdp_vaddr) {
1331        Ok(true) => {}
1332        Ok(false) => {
1333            log::warn!("APIC: no RSDP from bootloader");
1334            return false;
1335        }
1336        Err(e) => {
1337            log::warn!("APIC: ACPI init failed: {}", e);
1338            return false;
1339        }
1340    }
1341    serial_println!("[init]   6b. ACPI RSDP validated");
1342
1343    // Step 6c: Parse MADT
1344    let madt_info = match acpi::madt::parse_madt() {
1345        Some(info) => info,
1346        None => {
1347            log::warn!("APIC: MADT not found");
1348            return false;
1349        }
1350    };
1351    serial_println!("[init]   6c. MADT parsed");
1352
1353    if let Some(mcfg) = acpi::mcfg::parse_mcfg() {
1354        serial_println!(
1355            "[init]   6c+. MCFG parsed ({} segment(s))",
1356            mcfg.entries.len()
1357        );
1358        for entry in mcfg.entries.iter() {
1359            log::info!(
1360                "ACPI: MCFG seg={} ecam={:#x} buses={}..{} ({} bus(es))",
1361                entry.segment_group,
1362                entry.base_address,
1363                entry.start_bus,
1364                entry.end_bus,
1365                entry.bus_count()
1366            );
1367        }
1368    } else {
1369        serial_println!("[init]   6c+. MCFG not found");
1370    }
1371
1372    // Step 6d: initialize Local APIC
1373    // Ensure Local APIC MMIO is mapped
1374    memory::paging::ensure_identity_map(madt_info.local_apic_address as u64);
1375    apic::init(madt_info.local_apic_address);
1376    serial_println!("[init]   6d. Local APIC initialized");
1377
1378    // Step 6e: initialize first I/O APIC
1379    if madt_info.io_apic_count == 0 {
1380        log::warn!("APIC: no I/O APIC in MADT");
1381        return false;
1382    }
1383    let Some(io_apic_entry) = madt_info.io_apics[0] else {
1384        log::warn!("APIC: MADT I/O APIC entry[0] missing");
1385        return false;
1386    };
1387    // Ensure I/O APIC MMIO is mapped
1388    memory::paging::ensure_identity_map(io_apic_entry.address as u64);
1389    ioapic::init(io_apic_entry.address, io_apic_entry.gsi_base);
1390    serial_println!("[init]   6e. I/O APIC initialized");
1391
1392    // Step 6f: remap PIC to 0x20+ then keep only PS/2 input IRQs enabled.
1393    // Must remap first to avoid stray interrupts at exception vectors (0-31).
1394    // On the current q35 + SMP path, LAPIC timer delivery is fine but legacy
1395    // PS/2 IRQs are not reliably arriving through the I/O APIC. Keep keyboard
1396    // and mouse on the remapped PIC while using APIC for the timer.
1397    pic::init(pic::PIC1_OFFSET, pic::PIC2_OFFSET);
1398    pic::disable_permanently();
1399    pic::enable_irq(1);
1400    pic::enable_irq(2);
1401    pic::enable_irq(12);
1402    serial_println!("[init]   6f. Legacy PIC remapped; PS/2 IRQ1/IRQ12 left enabled");
1403
1404    // Step 6g: route only the legacy timer IRQ via I/O APIC.
1405    // Keyboard (IRQ1) and mouse (IRQ12) stay on the remapped PIC path above.
1406    let lapic_id = apic::lapic_id();
1407    ioapic::route_legacy_irq(0, lapic_id, 0x20, &madt_info.overrides);
1408    ioapic::mask_legacy_irq(1, &madt_info.overrides);
1409    ioapic::mask_legacy_irq(12, &madt_info.overrides);
1410
1411    // Store overrides so PCI NIC drivers can route their IRQ later.
1412    ioapic::store_madt_overrides(&madt_info.overrides);
1413
1414    serial_println!("[init]   6g. IRQ0->vec 0x20 via IOAPIC; IRQ1/IRQ12 via PIC");
1415
1416    // Step 6h: calibrate APIC timer using PIT channel 2
1417    serial_println!("[init]   6h. Calibrating APIC timer using PIT channel 2...");
1418    serial_println!(
1419        "[timer] ================================ TIMER INIT ================================"
1420    );
1421
1422    let ticks_per_10ms = timer::calibrate_apic_timer();
1423
1424    if ticks_per_10ms == 0 {
1425        log::error!("APIC: timer calibration FAILED");
1426        log::warn!("Falling back to legacy PIT timer at 100Hz");
1427
1428        // Re-enable PIC since APIC timer failed
1429        // (Note: I/O APIC routing is still active for keyboard/timer via PIC vectors)
1430        serial_println!("[timer] APIC calibration failed, initializing PIT fallback...");
1431        timer::init_pit(TIMER_HZ as u32);
1432        serial_println!(
1433            "[timer] PIT initialized at {}Hz ({} ms/tick)",
1434            TIMER_HZ,
1435            1_000 / TIMER_HZ
1436        );
1437        serial_println!("[init]   6h. PIT timer initialized (fallback)");
1438
1439        serial_println!("[timer] ============================= TIMER INIT COMPLETE ============================");
1440        serial_println!("[timer] Mode: PIT (legacy fallback)");
1441        serial_println!("[timer] Frequency: {}Hz", TIMER_HZ);
1442        serial_println!("[timer] Interval: {} ms per tick", 1_000 / TIMER_HZ);
1443        serial_println!(
1444            "[timer] =========================================================================="
1445        );
1446
1447        // Continue with PIT - don't return false
1448        // return false;
1449    } else {
1450        serial_println!("[init]   6h. APIC timer calibrated successfully");
1451
1452        // Step 6i: DO NOT start APIC timer yet. (Asterinas style)
1453        // We will start it only after the scheduler is ready.
1454        // timer::start_apic_timer(ticks_per_10ms);
1455
1456        // Step 6i+: quench legacy PIT to prevent phantom timer interrupts.
1457        timer::stop_pit();
1458        ioapic::mask_legacy_irq(0, &madt_info.overrides);
1459        serial_println!("[init]   6i+. Legacy PIT stopped and masked in IOAPIC");
1460
1461        serial_println!("[timer] ============================= TIMER INIT COMPLETE ============================");
1462        serial_println!("[timer] Mode: APIC (native)");
1463        serial_println!("[timer] Frequency: {}Hz", TIMER_HZ);
1464        serial_println!("[timer] Interval: {} ms per tick", 1_000 / TIMER_HZ);
1465        serial_println!("[timer] Ticks per 10ms: {}", ticks_per_10ms);
1466        serial_println!(
1467            "[timer] =========================================================================="
1468        );
1469    }
1470
1471    true
1472}