Skip to main content

ax_runtime/
lib.rs

1// Copyright 2025 The Axvisor Team
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7//     http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15//! Runtime library of [ArceOS](https://github.com/arceos-org/arceos).
16//!
17//! Any application uses ArceOS should link this library. It does some
18//! initialization work before entering the application's `main` function.
19//!
20//! # Cargo Features
21//!
22//! - `alloc`: Enable global memory allocator.
23//! - `paging`: Enable page table manipulation support.
24//! - `irq`: Enable interrupt handling support.
25//! - `multitask`: Enable multi-threading support.
26//! - `smp`: Enable SMP (symmetric multiprocessing) support.
27//! - `fs`: Enable filesystem support.
28//! - `net`: Enable networking support.
29//! - `display`: Enable graphics support.
30//!
31//! All the features are optional and disabled by default.
32
33#![feature(extern_item_impls)]
34#![cfg_attr(not(test), no_std)]
35#![allow(missing_abi)]
36
37#[macro_use]
38extern crate ax_log;
39
40#[cfg(all(target_os = "none", not(test)))]
41mod lang_items;
42
43#[cfg(feature = "smp")]
44mod mp;
45
46#[cfg(feature = "paging")]
47mod klib;
48
49#[cfg(feature = "smp")]
50pub use self::mp::rust_main_secondary;
51
52const LOGO: &str = r#"
53       d8888                            .d88888b.   .d8888b.
54      d88888                           d88P" "Y88b d88P  Y88b
55     d88P888                           888     888 Y88b.
56    d88P 888 888d888  .d8888b  .d88b.  888     888  "Y888b.
57   d88P  888 888P"   d88P"    d8P  Y8b 888     888     "Y88b.
58  d88P   888 888     888      88888888 888     888       "888
59 d8888888888 888     Y88b.    Y8b.     Y88b. .d88P Y88b  d88P
60d88P     888 888      "Y8888P  "Y8888   "Y88888P"   "Y8888P"
61"#;
62
63#[eii]
64fn ax_app_entry() {
65    #[cfg(not(test))]
66    unsafe extern "C" {
67        /// Legacy application's entry point.
68        safe fn main();
69    }
70    // Default implementation
71    #[cfg(not(test))]
72    main();
73}
74
75struct LogIfImpl;
76
77#[ax_crate_interface::impl_interface]
78impl ax_log::LogIf for LogIfImpl {
79    fn console_write_str(s: &str) {
80        ax_hal::console::write_text_bytes(s.as_bytes());
81    }
82
83    fn current_time() -> core::time::Duration {
84        ax_hal::time::monotonic_time()
85    }
86
87    fn current_cpu_id() -> Option<usize> {
88        #[cfg(feature = "smp")]
89        if is_init_ok() {
90            Some(ax_hal::percpu::this_cpu_id())
91        } else {
92            None
93        }
94        #[cfg(not(feature = "smp"))]
95        Some(0)
96    }
97
98    fn current_task_id() -> Option<u64> {
99        if is_init_ok() {
100            #[cfg(feature = "multitask")]
101            {
102                ax_task::current_may_uninit().map(|curr| curr.id().as_u64())
103            }
104            #[cfg(not(feature = "multitask"))]
105            None
106        } else {
107            None
108        }
109    }
110}
111
112use core::sync::atomic::{AtomicUsize, Ordering};
113
114/// Number of CPUs that have completed initialization.
115static INITED_CPUS: AtomicUsize = AtomicUsize::new(0);
116
117fn is_init_ok() -> bool {
118    INITED_CPUS.load(Ordering::Acquire) == ax_hal::cpu_num()
119}
120
121/// The main entry point of the ArceOS runtime.
122///
123/// It is called from the bootstrapping code in the specific platform crate (see
124/// [`ax_plat::main`]).
125///
126/// `cpu_id` is the logic ID of the current CPU, and `arg` is passed from the
127/// bootloader (typically the device tree blob address).
128///
129/// In multi-core environment, this function is called on the primary core, and
130/// secondary cores call [`rust_main_secondary`].
131#[cfg_attr(not(test), ax_plat::main)]
132pub fn rust_main(cpu_id: usize, arg: usize) -> ! {
133    #[cfg(not(feature = "plat-dyn"))]
134    unsafe {
135        ax_hal::mem::clear_bss()
136    };
137    ax_hal::percpu::init_primary(cpu_id);
138    #[cfg(all(feature = "alloc", feature = "buddy-slab"))]
139    ax_alloc::init_percpu_slab(cpu_id);
140    ax_hal::init_early(cpu_id, arg);
141    let log_level = option_env!("AX_LOG").unwrap_or("info");
142
143    ax_println!("{}", LOGO);
144    ax_println!(
145        indoc::indoc! {"
146            arch = {}
147            platform = {}
148            target = {}
149            build_mode = {}
150            log_level = {}
151            backtrace = {}
152            smp = {}
153        "},
154        ax_config::ARCH,
155        ax_config::PLATFORM,
156        option_env!("AX_TARGET").unwrap_or(""),
157        option_env!("AX_MODE").unwrap_or(""),
158        log_level,
159        axbacktrace::is_enabled(),
160        ax_hal::cpu_num()
161    );
162
163    #[cfg(feature = "rtc")]
164    ax_println!(
165        "Boot at {}\n",
166        chrono::DateTime::from_timestamp_nanos(ax_hal::time::wall_time_nanos() as _),
167    );
168
169    ax_log::init();
170    ax_log::set_max_level(log_level); // no effect if set `log-level-*` features
171    info!("Logging is enabled.");
172    info!("Primary CPU {cpu_id} started, arg = {arg:#x}.");
173
174    info!("Found physcial memory regions:");
175    for r in ax_hal::mem::memory_regions() {
176        info!(
177            "  [{:x?}, {:x?}) {} ({:?})",
178            r.paddr,
179            r.paddr + r.size,
180            r.name,
181            r.flags
182        );
183    }
184
185    #[cfg(feature = "alloc")]
186    init_allocator();
187
188    {
189        use core::ops::Range;
190
191        unsafe extern "C" {
192            safe static _stext: [u8; 0];
193            safe static _etext: [u8; 0];
194            safe static _edata: [u8; 0];
195        }
196
197        axbacktrace::init(
198            Range {
199                start: _stext.as_ptr() as usize,
200                end: _etext.as_ptr() as usize,
201            },
202            Range {
203                start: _edata.as_ptr() as usize,
204                end: usize::MAX,
205            },
206        );
207    }
208
209    let (kernel_space_start, kernel_space_size) = ax_hal::mem::kernel_aspace();
210
211    info!(
212        "kernel aspace: [{:#x?}, {:#x?})",
213        kernel_space_start,
214        kernel_space_start + kernel_space_size,
215    );
216
217    #[cfg(feature = "paging")]
218    ax_mm::init_memory_management();
219
220    // #[cfg(feature = "plat-dyn")]
221    // ax_driver::setup(arg);
222
223    info!("Initialize platform devices...");
224    ax_hal::init_later(cpu_id, arg);
225
226    #[cfg(feature = "multitask")]
227    ax_task::init_scheduler();
228
229    #[cfg(feature = "ipi")]
230    ax_ipi::init();
231
232    #[cfg(feature = "ax-driver")]
233    {
234        #[allow(unused_variables)]
235        let all_devices = ax_driver::init_drivers();
236
237        cfg_if::cfg_if! {
238            if #[cfg(feature = "fs-ng")] {
239                ax_fs_ng::init_filesystems(all_devices.block, ax_hal::dtb::get_chosen_bootargs());
240            } else
241            if #[cfg(feature = "fs")] {
242                ax_fs::init_filesystems(all_devices.block, ax_hal::dtb::get_chosen_bootargs());
243            }
244        }
245
246        cfg_if::cfg_if! {
247            if #[cfg(feature = "net-ng")] {
248                ax_net_ng::init_network(all_devices.net);
249
250                #[cfg(feature = "vsock")]
251                ax_net_ng::init_vsock(all_devices.vsock);
252            } else if #[cfg(feature = "net")] {
253                ax_net::init_network(all_devices.net);
254            }
255        }
256
257        #[cfg(feature = "display")]
258        ax_display::init_display(all_devices.display);
259
260        #[cfg(feature = "input")]
261        ax_input::init_input(all_devices.input);
262    }
263
264    #[cfg(feature = "smp")]
265    self::mp::start_secondary_cpus(cpu_id);
266
267    #[cfg(feature = "irq")]
268    {
269        info!("Initialize interrupt handlers...");
270        init_interrupt();
271    }
272
273    #[cfg(all(feature = "tls", not(feature = "multitask")))]
274    {
275        info!("Initialize thread local storage...");
276        init_tls();
277    }
278
279    ax_ctor_bare::call_ctors();
280
281    info!("Primary CPU {cpu_id} init OK.");
282    INITED_CPUS.fetch_add(1, Ordering::Release);
283
284    while !is_init_ok() {
285        core::hint::spin_loop();
286    }
287
288    ax_app_entry();
289
290    #[cfg(feature = "multitask")]
291    ax_task::exit(0);
292    #[cfg(not(feature = "multitask"))]
293    {
294        debug!("main task exited: exit_code={}", 0);
295        ax_hal::power::system_off();
296    }
297}
298
299#[cfg(feature = "alloc")]
300fn init_allocator() {
301    use ax_hal::mem::{MemRegionFlags, memory_regions, phys_to_virt};
302
303    info!("Initialize global memory allocator...");
304    info!("  use {} allocator.", ax_alloc::global_allocator().name());
305
306    let mut max_region_size = 0;
307    let mut max_region_paddr = 0.into();
308    let mut use_next_free = false;
309
310    for r in memory_regions() {
311        if r.name == ".bss" {
312            use_next_free = true;
313        } else if r.flags.contains(MemRegionFlags::FREE) {
314            if use_next_free {
315                max_region_paddr = r.paddr;
316                break;
317            } else if r.size > max_region_size {
318                max_region_size = r.size;
319                max_region_paddr = r.paddr;
320            }
321        }
322    }
323
324    for r in memory_regions() {
325        if r.flags.contains(MemRegionFlags::FREE) && r.paddr == max_region_paddr {
326            ax_alloc::global_init(phys_to_virt(r.paddr).as_usize(), r.size)
327                .expect("initialize global allocator failed");
328            break;
329        }
330    }
331
332    for r in memory_regions() {
333        if r.flags.contains(MemRegionFlags::FREE) && r.paddr != max_region_paddr {
334            ax_alloc::global_add_memory(phys_to_virt(r.paddr).as_usize(), r.size)
335                .expect("add heap memory region failed");
336        }
337    }
338}
339
340#[cfg(feature = "irq")]
341fn init_interrupt() {
342    // Setup timer interrupt handler
343    const PERIODIC_INTERVAL_NANOS: u64 =
344        ax_hal::time::NANOS_PER_SEC / ax_config::TICKS_PER_SEC as u64;
345
346    #[ax_percpu::def_percpu]
347    static NEXT_DEADLINE: u64 = 0;
348
349    fn update_timer(_irq_num: usize) {
350        let now_ns = ax_hal::time::monotonic_time_nanos();
351        // Safety: we have disabled preemption in IRQ handler.
352        let mut deadline = unsafe { NEXT_DEADLINE.read_current_raw() };
353        if now_ns >= deadline {
354            deadline = now_ns + PERIODIC_INTERVAL_NANOS;
355        }
356        unsafe { NEXT_DEADLINE.write_current_raw(deadline + PERIODIC_INTERVAL_NANOS) };
357        ax_hal::time::set_oneshot_timer(deadline);
358    }
359
360    #[cfg(target_arch = "loongarch64")]
361    ax_hal::irq::init_common_irq_handler();
362
363    ax_hal::irq::register(ax_hal::time::irq_num(), |irq_num| {
364        update_timer(irq_num);
365        #[cfg(feature = "multitask")]
366        ax_task::on_timer_tick();
367    });
368
369    #[cfg(feature = "ipi")]
370    ax_hal::irq::register(ax_hal::irq::IPI_IRQ, |_irq_num| {
371        ax_ipi::ipi_handler();
372    });
373
374    // Arm the first one-shot timer on the primary CPU. Otherwise the timer
375    // handler may never get the first chance to re-program subsequent ticks.
376    update_timer(ax_hal::time::irq_num());
377
378    // Enable IRQs before starting app
379    ax_hal::asm::enable_irqs();
380}
381
382#[cfg(all(feature = "tls", not(feature = "multitask")))]
383fn init_tls() {
384    let main_tls = ax_hal::tls::TlsArea::alloc();
385    unsafe { ax_hal::asm::write_thread_pointer(main_tls.tls_ptr() as usize) };
386    core::mem::forget(main_tls);
387}