Skip to main content

ax_runtime/
bootstrap.rs

1//! Primary CPU boot orchestration.
2
3use core::sync::atomic::Ordering;
4
5const LOGO: &str = r#"
6       d8888                            .d88888b.   .d8888b.
7      d88888                           d88P" "Y88b d88P  Y88b
8     d88P888                           888     888 Y88b.
9    d88P 888 888d888  .d8888b  .d88b.  888     888  "Y888b.
10   d88P  888 888P"   d88P"    d8P  Y8b 888     888     "Y88b.
11  d88P   888 888     888      88888888 888     888       "888
12 d8888888888 888     Y88b.    Y8b.     Y88b. .d88P Y88b  d88P
13d88P     888 888      "Y8888P  "Y8888   "Y88888P"   "Y8888P"
14"#;
15
16#[cfg(feature = "paging")]
17fn runtime_page_fault_handler(
18    addr: ax_memory_addr::VirtAddr,
19    flags: ax_hal::trap::PageFaultFlags,
20) -> bool {
21    #[cfg(feature = "stack-guard-page")]
22    if crate::diagnostics::diagnose_current_stack_guard_page_fault(addr) {
23        return false;
24    }
25
26    crate::kernel_mapping::handle_kernel_page_fault(addr, flags)
27}
28
29/// Establishes scheduler ownership before code that may use task services.
30///
31/// Linux establishes the boot runqueue/current/idle relationship in
32/// `sched_init()` before it starts device initcalls. Keep the same ordering at
33/// this boundary: once platform late-init starts, task identity and scheduler
34/// CPU-local state must already have one authoritative owner.
35pub(super) fn initialize_scheduler_before_platform<E>(
36    initialize_scheduler: impl FnOnce() -> Result<(), E>,
37    initialize_platform: impl FnOnce(),
38) -> Result<(), E> {
39    initialize_scheduler()?;
40    initialize_platform();
41    Ok(())
42}
43
44fn initialize_primary_platform(cpu_id: usize, arg: usize) {
45    info!("Initialize platform devices...");
46    ax_hal::init_later(cpu_id, arg);
47    if rdrive::is_initialized() {
48        crate::registers::append_linker_registers();
49        ax_hal::irq::init_boot_irqs(cpu_id)
50            .unwrap_or_else(|error| panic!("failed to initialize boot IRQs: {error:?}"));
51    } else {
52        warn!("rdrive is not initialized; skip pre-kernel driver probe");
53    }
54}
55
56/// The main entry point of the ArceOS runtime.
57///
58/// It is called from the bootstrapping code in the specific platform crate
59/// (see [`ax_plat::main`]).
60///
61/// `cpu_id` is the logic ID of the current CPU, and `arg` is passed from the
62/// bootloader (typically the device tree blob address).
63///
64/// In multi-core environment, this function is called on the primary core, and
65/// secondary cores call [`crate::rust_main_secondary`].
66#[cfg_attr(not(test), ax_plat::main)]
67pub fn rust_main(cpu_id: usize, arg: usize) -> ! {
68    ax_hal::percpu::init_primary(cpu_id);
69    crate::guard::assert_boot_preemption_held();
70    // After per-CPU init, before scheduler/IPI/IRQ paths can allocate.
71    // This is a no-op for allocator backends that do not need per-CPU state.
72    ax_alloc::init_percpu_slab(cpu_id);
73    ax_hal::init_early(cpu_id, arg);
74    let log_level = option_env!("AX_LOG").unwrap_or("info");
75
76    ax_println!("{}", LOGO);
77    ax_println!(
78        indoc::indoc! {"
79            arch = {}
80            platform = {}
81            target = {}
82            build_mode = {}
83            log_level = {}
84            backtrace = {}
85            smp = {}
86        "},
87        crate::build_info::ARCH,
88        crate::hal::platform_name(),
89        crate::build_info::TARGET,
90        crate::build_info::MODE,
91        log_level,
92        axbacktrace::is_enabled(),
93        ax_hal::cpu_num()
94    );
95
96    ax_log::init();
97    ax_log::set_max_level(log_level); // no effect if set `log-level-*` features
98    info!("Logging is enabled.");
99    info!("Primary CPU {cpu_id} started, arg = {arg:#x}.");
100
101    info!("Found physcial memory regions:");
102    for region in ax_hal::mem::memory_regions() {
103        info!(
104            "  [{:x?}, {:x?}) {} ({:?})",
105            region.paddr,
106            region.paddr + region.size,
107            region.name,
108            region.flags
109        );
110    }
111
112    crate::boot_memory::init_allocator();
113
114    #[cfg(feature = "std-compat")]
115    crate::panic_output::install_std_hook();
116
117    #[cfg(feature = "tls")]
118    crate::thread::initialize_early_bootstrap_tls()
119        .expect("failed to initialize primary bootstrap TLS");
120
121    let layout = ax_hal::mem::virtual_address_space()
122        .expect("platform virtual-address layout must be supported");
123    let kernel_space_start = layout.kernel().start;
124    let kernel_space_size = layout.kernel().size();
125
126    {
127        use core::ops::Range;
128
129        unsafe extern "C" {
130            safe static _stext: [u8; 0];
131            safe static _etext: [u8; 0];
132        }
133
134        let fp_range_start = kernel_space_start.as_usize();
135        let fp_range_end = fp_range_start.saturating_add(kernel_space_size);
136        axbacktrace::init(
137            Range {
138                start: _stext.as_ptr() as usize,
139                end: _etext.as_ptr() as usize,
140            },
141            Range {
142                start: fp_range_start,
143                end: fp_range_end,
144            },
145        );
146    }
147
148    info!(
149        "kernel aspace: [{:#x?}, {:#x?})",
150        kernel_space_start,
151        kernel_space_start + kernel_space_size,
152    );
153
154    #[cfg(feature = "paging")]
155    {
156        ax_mm::init_memory_management();
157        ax_hal::trap::set_page_fault_handler(runtime_page_fault_handler);
158    }
159    initialize_scheduler_before_platform(
160        || crate::thread::initialize_primary(cpu_id),
161        || initialize_primary_platform(cpu_id, arg),
162    )
163    .expect("failed to initialize primary task scheduler");
164
165    #[cfg(any(feature = "ipi", feature = "wake-ipi"))]
166    {
167        ax_ipi::init();
168        #[cfg(feature = "ipi")]
169        ax_hal::irq::set_run_on_cpu_sync(crate::ipi_delivery::run_on_cpu_sync);
170    }
171    {
172        info!("Initialize interrupt handlers...");
173        crate::interrupt_bootstrap::init_current_cpu();
174    }
175
176    #[cfg(feature = "paging")]
177    let tlb_preparation = ax_hal::cache::prepare_current_cpu_tlb()
178        .expect("primary CPU failed to prepare TLB capability");
179
180    // Linux enables the local IPI endpoint before publishing the CPU online to
181    // the scheduler. Once scheduler work is visible, any safe point may need a
182    // physical self-doorbell, including the bootstrap scheduling pass below.
183    #[cfg(any(feature = "ipi", feature = "wake-ipi"))]
184    ax_ipi::mark_current_cpu_ready();
185    let online_cpu = crate::thread::publish_current_cpu_online()
186        .expect("failed to publish primary scheduler CPU");
187    crate::thread::start_current_ktimer_service().expect("failed to create primary ktimer service");
188    crate::clock_event_runtime::enable_irqs_after_scheduler_online(online_cpu);
189    #[cfg(feature = "paging")]
190    ax_hal::cache::publish_current_cpu_tlb_ready(tlb_preparation)
191        .expect("primary CPU failed to publish TLB readiness");
192    crate::guard::release_bootstrap_preemption();
193    crate::thread::start_deferred_task_work_service()
194        .expect("failed to start deferred scheduler task-work service");
195
196    crate::devices::probe_all_devices();
197    crate::serial::init(cpu_id);
198    match crate::console::activate_before_smp() {
199        crate::console::ConsoleActivation::Active {
200            runtime_index,
201            tty_number,
202        } => info!("runtime console active: serial{runtime_index}, ttyS{tty_number}"),
203        crate::console::ConsoleActivation::RawHal(reason) => {
204            info!("no runtime console selected; keeping the HAL console: {reason:?}")
205        }
206        crate::console::ConsoleActivation::FailedClosed(reason) => {
207            warn!("runtime console unavailable; early console failed closed: {reason:?}")
208        }
209    }
210
211    #[cfg(feature = "rtc")]
212    ax_println!(
213        "Boot at {}\n",
214        chrono::DateTime::from_timestamp_nanos(ax_hal::time::wall_time_nanos() as _),
215    );
216
217    crate::fs::init(ax_hal::boot::bootargs());
218
219    #[cfg(feature = "display")]
220    crate::devices::init_display();
221
222    #[cfg(feature = "input")]
223    crate::devices::init_input();
224
225    #[cfg(feature = "net")]
226    crate::devices::init_net();
227
228    #[cfg(feature = "vsock")]
229    crate::devices::init_vsock();
230
231    #[cfg(feature = "smp")]
232    crate::mp::start_secondary_cpus(cpu_id);
233
234    ax_ctor_bare::call_ctors();
235
236    info!("Primary CPU {cpu_id} init OK.");
237    crate::INITED_CPUS.fetch_add(1, Ordering::Release);
238
239    while !crate::is_init_ok() {
240        core::hint::spin_loop();
241    }
242
243    #[cfg(any(feature = "ipi", feature = "wake-ipi"))]
244    ax_ipi::wait_for_all_cpus_ready();
245
246    #[cfg(all(feature = "smp", feature = "ipi"))]
247    crate::fs::online_smp();
248
249    crate::ax_app_entry();
250    crate::terminate();
251}
252
253#[cfg(test)]
254mod tests {
255    use alloc::vec::Vec;
256    use core::cell::RefCell;
257
258    use super::initialize_scheduler_before_platform;
259
260    #[derive(Clone, Copy, Debug, Eq, PartialEq)]
261    enum BootEvent {
262        SchedulerOwnerPublished,
263        PlatformLateInit,
264    }
265
266    #[test]
267    fn scheduler_owner_is_published_before_platform_late_init() {
268        let events = RefCell::new(Vec::new());
269        initialize_scheduler_before_platform(
270            || {
271                events.borrow_mut().push(BootEvent::SchedulerOwnerPublished);
272                Ok::<(), ()>(())
273            },
274            || events.borrow_mut().push(BootEvent::PlatformLateInit),
275        )
276        .unwrap();
277
278        assert_eq!(
279            events.into_inner(),
280            [
281                BootEvent::SchedulerOwnerPublished,
282                BootEvent::PlatformLateInit,
283            ]
284        );
285    }
286}