// SPDX-License-Identifier: MPL-2.0 //! Aster-nix is the Asterinas kernel, a safe, efficient unix-like //! operating system kernel built on top of OSTD and OSDK. #![no_std] #![no_main] #![deny(unsafe_code)] #![feature(btree_cursors)] #![feature(btree_extract_if)] #![feature(debug_closure_helpers)] #![feature(extract_if)] #![feature(format_args_nl)] #![feature(integer_sign_cast)] #![feature(let_chains)] #![feature(linked_list_cursors)] #![feature(linked_list_retain)] #![feature(negative_impls)] #![feature(panic_can_unwind)] #![feature(register_tool)] #![feature(min_specialization)] #![feature(trait_alias)] #![feature(trait_upcasting)] #![feature(associated_type_defaults)] #![register_tool(component_access_control)] use component::InitStage; use kcmdline::KCmdlineArg; use ostd::{ arch::qemu::{exit_qemu, QemuExitCode}, boot::boot_info, cpu::CpuId, util::id_set::Id, }; use process::{spawn_init_process, Process}; use sched::SchedPolicy; use crate::{ fs::{fs_resolver::FsResolver, path::MountNamespace}, prelude::*, thread::kernel_thread::ThreadOptions, }; extern crate alloc; extern crate lru; #[macro_use] extern crate controlled; #[macro_use] extern crate getset; #[cfg_attr(target_arch = "x86_64", path = "arch/x86/mod.rs")] #[cfg_attr(target_arch = "riscv64", path = "arch/riscv/mod.rs")] #[cfg_attr(target_arch = "loongarch64", path = "arch/loongarch/mod.rs")] mod arch; mod context; mod cpu; mod device; mod driver; mod error; mod events; mod fs; mod ipc; mod kcmdline; mod net; mod prelude; mod process; mod sched; mod security; mod syscall; mod thread; mod time; mod util; // TODO: Add vDSO support for other architectures. #[cfg(any(target_arch = "x86_64", target_arch = "riscv64"))] mod vdso; mod vm; #[ostd::main] #[controlled] fn main() { ostd::early_println!("[kernel] OSTD initialized. Preparing components."); component::init_all(InitStage::Bootstrap, component::parse_metadata!()).unwrap(); init(); // Spawn all AP idle threads. ostd::boot::smp::register_ap_entry(ap_init); init_on_each_cpu(); // Spawn the first kernel thread on BSP. ThreadOptions::new(first_kthread) .cpu_affinity(CpuId::bsp().into()) .sched_policy(SchedPolicy::Idle) .spawn(); } fn init() { thread::init(); util::random::init(); driver::init(); time::init(); net::init(); sched::init(); process::init(); fs::init(); security::init(); } fn init_on_each_cpu() { sched::init_on_each_cpu(); process::init_on_each_cpu(); fs::init_on_each_cpu(); time::init_on_each_cpu(); } 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 thread::work_queue::init_in_first_kthread(); net::init_in_first_kthread(); fs::init_in_first_kthread(fs_resolver); ipc::init_in_first_kthread(); #[cfg(any(target_arch = "x86_64", target_arch = "riscv64"))] vdso::init_in_first_kthread(); } fn init_in_first_process(ctx: &Context) { component::init_all(InitStage::Process, component::parse_metadata!()).unwrap(); device::init_in_first_process(ctx).unwrap(); fs::init_in_first_process(ctx); process::init_in_first_process(ctx); } fn ap_init() { init_on_each_cpu(); fn ap_idle_thread() { log::info!( "Kernel idle thread for CPU #{} started.", // No races because `ap_idle_thread` runs on a certain AP. CpuId::current_racy().as_usize(), ); loop { ostd::task::halt_cpu(); } } ThreadOptions::new(ap_idle_thread) // No races because `ap_init` runs on a certain AP. .cpu_affinity(CpuId::current_racy().into()) .sched_policy(SchedPolicy::Idle) .spawn(); } fn first_kthread() { println!("[kernel] Spawn init 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(); let karg: KCmdlineArg = boot_info().kernel_cmdline.as_str().into(); let initproc = 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."); // Wait till initproc become zombie. while !initproc.status().is_zombie() { ostd::task::halt_cpu(); } // TODO: exit via qemu isa debug device should not be the only way. let exit_code = if initproc.status().exit_code() == 0 { QemuExitCode::Success } else { QemuExitCode::Failed }; exit_qemu(exit_code); } fn print_banner() { println!(""); println!("{}", logo_ascii_art::get_gradient_color_version()); }