use alloc::{
string::{String, ToString},
sync::Arc,
};
use ax_fs_ng::vfs::current_fs_context;
use ax_runtime::hal::cpu::user::UserContext;
use crate::{
file::{FD_TABLE, FileTable, new_file_table_scope},
mm::{MmHandle, load_user_app, new_user_image_builder},
namespace::NsProxy,
pseudofs::{self, dev::tty},
sync::{Mutex, RwLock},
task::{
PidReservation, PidReservationKind, Process, ProcessData, ProcessDataInit, ProcessImage,
ROOT_PID_NS, Tgid, Thread, Tid, TidNumber, UserThreadOptions, kernel_thread_builder,
new_user_task, prepare_user_thread, sleep, spawn_alarm_task,
},
tracepoint::tracepoint_init,
};
pub fn init(args: &[String], envs: &[String]) {
crate::rdrive_osal::init();
crate::stop_machine::init();
crate::trap::init_handlers();
static_keys::global_init();
crate::cgroup::init();
tracepoint_init().expect("Failed to initialize tracepoints");
crate::ebpf::init_ebpf();
crate::perf::perf_event_init();
crate::kmod::init_kmod();
pseudofs::mount_all().expect("Failed to mount pseudofs");
spawn_alarm_task();
crate::mm::spawn_reclaimer_task();
if ax_driver::cpufreq::calibrate_wanted() {
run_opp_calibration();
} else {
spawn_cpufreq_governor();
}
ax_driver::cpufreq::log_frequency_readout();
pseudofs::usbfs::start_event_pump();
ax_alloc::register_page_reclaim_fn(ax_fs_ng::vfs::page_cache_reclaim);
let loc = current_fs_context()
.lock()
.resolve(&args[0])
.expect("Failed to resolve executable path");
let path = loc
.absolute_path()
.expect("Failed to get executable absolute path");
let name = loc.name().into_owned();
let mut image_builder =
new_user_image_builder().expect("Failed to create unpublished user address space");
let loaded_image = load_user_app(
&mut image_builder,
loc,
&args[0],
args,
envs,
&crate::task::Cred::root(),
)
.unwrap_or_else(|error| panic!("Failed to load user app: {error}"));
let prepared_image = image_builder
.finish(loaded_image)
.expect("loaded init image token no longer matches its address space");
let (uspace, entry_vaddr, ustack_top, auxv) = prepared_image.into_parts();
let uctx = UserContext::new(entry_vaddr.into(), ustack_top, 0);
const INIT_PID: u32 = 1;
let reservation = PidReservation::reserve(&ROOT_PID_NS, PidReservationKind::ProcessLeader)
.expect("failed to reserve init PID identity");
let pid = reservation
.number_in(&ROOT_PID_NS)
.expect("init PID reservation has no root binding")
.get();
assert_eq!(pid, INIT_PID);
let identity = reservation.identity();
let tid_lease = identity
.acquire_role::<Tid>()
.expect("failed to acquire init TID role");
let tgid_lease = identity
.acquire_role::<Tgid>()
.expect("failed to acquire init TGID role");
let proc = Process::new_init(identity.clone()).expect("failed to prepare init process");
proc.add_thread(TidNumber::try_from(pid).expect("init TID must be non-zero"));
if let Err(error) = tty::bind_console_to(&proc) {
warn!("Failed to bind console tty: {error:?}");
}
let proc = ProcessData::new(
proc,
identity.clone(),
tgid_lease,
ProcessDataInit::new(
ProcessImage::new(
path.to_string(),
Arc::new(args.to_vec()),
Arc::new(envs.to_vec()),
auxv,
"/".to_string(),
"/".to_string(),
),
MmHandle::from_arc(Arc::new(Mutex::new(uspace)))
.expect("init MM identity must be unique"),
Arc::default(),
NsProxy::new_root(),
None,
TidNumber::try_from(pid).expect("init TID must be non-zero"),
),
);
crate::cgroup::attach_initial_process(&identity)
.expect("Failed to attach init process to cgroup root");
let mut scope = scope_local::Scope::new();
let mut fd_table = FileTable::new();
crate::file::add_stdio(&mut fd_table).expect("Failed to add stdio");
*FD_TABLE.scope_mut(&mut scope) = new_file_table_scope(Arc::new(RwLock::new(fd_table)));
let thr = Thread::new(
identity.clone(),
tid_lease,
proc,
None,
starry_signal::SignalSet::default(),
scope,
)
.expect("failed to prepare init thread state");
let prepared_task = prepare_user_thread(
new_user_task(
uctx,
0,
TidNumber::try_from(pid).expect("init TID must be non-zero"),
),
thr,
UserThreadOptions::new(&name).expect("failed to prepare init thread name"),
)
.expect("failed to prepare init task");
let staged_task = prepared_task.stage().expect("failed to stage init task");
let published_identity = reservation
.publish()
.expect("failed to publish init PID identity");
debug_assert!(Arc::ptr_eq(&published_identity, &identity));
staged_task.with_task(|task| task.as_thread().attach_pid_task(task));
tty::arm_console_irq();
let task = staged_task.activate();
let exit_code = task.join();
info!("Init process exited with code: {exit_code:?}");
let fs_context = current_fs_context();
let cx = fs_context.lock();
if let Err(err) = cx.root_dir().unmount_all() {
warn!("shutdown: unmount_all failed (best-effort): {err:?}");
}
cx.root_dir()
.filesystem()
.flush()
.expect("Failed to flush rootfs");
}
fn run_opp_calibration() {
info!("cpufreq: running OPP calibration sweep (governor disabled this boot)");
for &(cluster_idx, cpu) in &[(0usize, 0usize), (1, 4), (2, 6)] {
let mut affinity = ax_runtime::task::sched::CpuSet::empty(ax_runtime::hal::cpu_num());
let cpu_id =
u32::try_from(cpu).unwrap_or_else(|_| panic!("cpufreq CPU id {cpu} is out of range"));
assert!(
affinity.insert(ax_runtime::task::sched::CpuId::new(cpu_id)),
"cpufreq calibration CPU {cpu} is outside the runtime topology"
);
let task = kernel_thread_builder(String::from("cpufreq-cal"))
.affinity(affinity)
.spawn(move || ax_driver::cpufreq::calibrate_cluster(cluster_idx, cpu))
.expect("failed to spawn kernel thread");
let _exit_code = task.join().expect("failed to join kernel thread");
}
info!("cpufreq: OPP calibration sweep complete");
}
fn spawn_cpufreq_governor() {
if !ax_driver::cpufreq::governor_wanted() {
return;
}
info!("Initialize cpufreq ondemand governor...");
let _ = kernel_thread_builder(String::from("cpufreq-gov"))
.spawn(cpufreq_governor_loop)
.expect("failed to spawn kernel thread");
}
fn cpufreq_governor_loop() {
let period = core::time::Duration::from_millis(ax_driver::cpufreq::governor_period_ms());
loop {
sleep(period);
let mut busy = [0u64; 8];
for (cpu, slot) in busy.iter_mut().enumerate() {
*slot = ax_runtime::task::sched::cpu_busy_runtime_ns(
ax_runtime::task::sched::CpuId::new(cpu as u32),
)
.unwrap_or(0);
}
ax_driver::cpufreq::governor_poll(&busy);
}
}