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