// SPDX-License-Identifier: MPL-2.0 //! Kernel initialization. use component::InitStage; use ostd::{ arch::qemu::{exit_qemu, QemuExitCode}, boot::boot_info, cpu::CpuId, util::id_set::Id, }; use spin::once::Once; use crate::{ fs::{fs_resolver::FsResolver, path::MountNamespace}, kcmdline::KCmdlineArg, prelude::*, process::{spawn_init_process, Process}, sched::SchedPolicy, thread::kernel_thread::ThreadOptions, }; pub(super) fn main() { // Initialize the global states for all CPUs. ostd::early_println!("[kernel] OSTD initialized. Preparing components."); component::init_all(InitStage::Bootstrap, component::parse_metadata!()).unwrap(); init(); // Initialize the per-CPU states for BSP. init_on_each_cpu(); // Enable APs. ostd::boot::smp::register_ap_entry(ap_init); // Give the control of the BSP to the idle thread. ThreadOptions::new(bsp_idle_loop) .cpu_affinity(CpuId::bsp().into()) .sched_policy(SchedPolicy::Idle) .spawn(); } fn init() { crate::thread::init(); crate::util::random::init(); crate::driver::init(); crate::time::init(); crate::net::init(); crate::sched::init(); crate::process::init(); crate::fs::init(); crate::security::init(); } fn init_on_each_cpu() { crate::sched::init_on_each_cpu(); crate::process::init_on_each_cpu(); crate::fs::init_on_each_cpu(); crate::time::init_on_each_cpu(); } fn ap_init() { // Initialize the per-CPU states for AP. init_on_each_cpu(); ThreadOptions::new(ap_idle_loop) // No races because `ap_init` runs on a certain AP. .cpu_affinity(CpuId::current_racy().into()) .sched_policy(SchedPolicy::Idle) .spawn(); } //-------------------------------------------------------------------------- // Per-CPU idle threads //-------------------------------------------------------------------------- // Note: Keep the code in the idle loop to the bare minimum. // // We do not want the idle loop to // rely on the APIs of other kernel subsystems for two reasons. // First, the idle task must never sleep or block. // This property is relied upon by the scheduler. // Second, the idle task is spawned before the kernel is fully initialized. // So other subsystems may not be ready, yet. // // In addition, // doing more work in the idle task may have negative impact on // the latency to switching from the idle task to a useful, runnable one. fn bsp_idle_loop() { log::info!("[kernel] Idle thread for CPU #0 started"); // Spawn the first non-idle kernel thread on BSP. ThreadOptions::new(first_kthread) .cpu_affinity(CpuId::bsp().into()) .sched_policy(SchedPolicy::default()) .spawn(); // Wait till the init process is spawned. let init_process = loop { if let Some(init_process) = INIT_PROCESS.get() { break init_process; }; ostd::task::halt_cpu(); }; // Wait till the init process becomes zombie. while !init_process.status().is_zombie() { ostd::task::halt_cpu(); } // TODO: exit via qemu isa debug device should not be the only way. let exit_code = if init_process.status().exit_code() == 0 { QemuExitCode::Success } else { QemuExitCode::Failed }; exit_qemu(exit_code); } fn ap_idle_loop() { log::info!( "[kernel] Idle thread for CPU #{} started", // No races because this function runs on a certain AP. CpuId::current_racy().as_usize(), ); loop { ostd::task::halt_cpu(); } } //-------------------------------------------------------------------------- // The first kernel thread //-------------------------------------------------------------------------- // The main function of the first (non-idle) kernel thread fn first_kthread() { println!("[kernel] Spawn the first kernel thread"); let init_mnt_ns = MountNamespace::get_init_singleton(); let fs_resolver = init_mnt_ns.new_fs_resolver(); init_in_first_kthread(&fs_resolver); print_banner(); INIT_PROCESS.call_once(|| { let karg: KCmdlineArg = boot_info().kernel_cmdline.as_str().into(); spawn_init_process( karg.get_initproc_path().unwrap(), karg.get_initproc_argv().to_vec(), karg.get_initproc_envp().to_vec(), ) .expect("Run init process failed.") }); } static INIT_PROCESS: Once> = Once::new(); fn init_in_first_kthread(fs_resolver: &FsResolver) { component::init_all(InitStage::Kthread, component::parse_metadata!()).unwrap(); // Work queue should be initialized before interrupt is enabled, // in case any irq handler uses work queue as bottom half crate::thread::work_queue::init_in_first_kthread(); crate::device::init_in_first_kthread(); crate::net::init_in_first_kthread(); crate::fs::init_in_first_kthread(fs_resolver); crate::ipc::init_in_first_kthread(); #[cfg(any(target_arch = "x86_64", target_arch = "riscv64"))] crate::vdso::init_in_first_kthread(); } fn print_banner() { println!(""); println!("{}", logo_ascii_art::get_gradient_color_version()); } pub(crate) fn on_first_process_startup(ctx: &Context) { component::init_all(InitStage::Process, component::parse_metadata!()).unwrap(); crate::device::init_in_first_process(ctx).unwrap(); crate::fs::init_in_first_process(ctx); crate::process::init_in_first_process(ctx); }