183 lines
5.4 KiB
Rust
183 lines
5.4 KiB
Rust
// 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<Arc<Process>> = 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::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);
|
|
}
|