axruntime/
lib.rs

1//! Runtime library of [ArceOS](https://github.com/arceos-org/arceos).
2//!
3//! Any application uses ArceOS should link this library. It does some
4//! initialization work before entering the application's `main` function.
5//!
6//! # Cargo Features
7//!
8//! - `alloc`: Enable global memory allocator.
9//! - `paging`: Enable page table manipulation support.
10//! - `irq`: Enable interrupt handling support.
11//! - `multitask`: Enable multi-threading support.
12//! - `smp`: Enable SMP (symmetric multiprocessing) support.
13//! - `fs`: Enable filesystem support.
14//! - `net`: Enable networking support.
15//! - `display`: Enable graphics support.
16//!
17//! All the features are optional and disabled by default.
18
19#![cfg_attr(not(test), no_std)]
20#![feature(doc_auto_cfg)]
21
22#[macro_use]
23extern crate axlog;
24
25#[cfg(all(target_os = "none", not(test)))]
26mod lang_items;
27
28#[cfg(feature = "smp")]
29mod mp;
30
31#[cfg(feature = "smp")]
32pub use self::mp::rust_main_secondary;
33
34const LOGO: &str = r#"
35       d8888                            .d88888b.   .d8888b.
36      d88888                           d88P" "Y88b d88P  Y88b
37     d88P888                           888     888 Y88b.
38    d88P 888 888d888  .d8888b  .d88b.  888     888  "Y888b.
39   d88P  888 888P"   d88P"    d8P  Y8b 888     888     "Y88b.
40  d88P   888 888     888      88888888 888     888       "888
41 d8888888888 888     Y88b.    Y8b.     Y88b. .d88P Y88b  d88P
42d88P     888 888      "Y8888P  "Y8888   "Y88888P"   "Y8888P"
43"#;
44
45unsafe extern "C" {
46    /// Application's entry point.
47    fn main();
48}
49
50struct LogIfImpl;
51
52#[crate_interface::impl_interface]
53impl axlog::LogIf for LogIfImpl {
54    fn console_write_str(s: &str) {
55        axhal::console::write_bytes(s.as_bytes());
56    }
57
58    fn current_time() -> core::time::Duration {
59        axhal::time::monotonic_time()
60    }
61
62    fn current_cpu_id() -> Option<usize> {
63        #[cfg(feature = "smp")]
64        if is_init_ok() {
65            Some(axhal::percpu::this_cpu_id())
66        } else {
67            None
68        }
69        #[cfg(not(feature = "smp"))]
70        Some(0)
71    }
72
73    fn current_task_id() -> Option<u64> {
74        if is_init_ok() {
75            #[cfg(feature = "multitask")]
76            {
77                axtask::current_may_uninit().map(|curr| curr.id().as_u64())
78            }
79            #[cfg(not(feature = "multitask"))]
80            None
81        } else {
82            None
83        }
84    }
85}
86
87use core::sync::atomic::{AtomicUsize, Ordering};
88
89static INITED_CPUS: AtomicUsize = AtomicUsize::new(0);
90
91fn is_init_ok() -> bool {
92    INITED_CPUS.load(Ordering::Acquire) == axconfig::plat::CPU_NUM
93}
94
95/// The main entry point of the ArceOS runtime.
96///
97/// It is called from the bootstrapping code in the specific platform crate (see
98/// [`axplat::main`]).
99///
100/// `cpu_id` is the logic ID of the current CPU, and `arg` is passed from the
101/// bootloader (typically the device tree blob address).
102///
103/// In multi-core environment, this function is called on the primary core, and
104/// secondary cores call [`rust_main_secondary`].
105#[cfg_attr(not(test), axplat::main)]
106pub fn rust_main(cpu_id: usize, arg: usize) -> ! {
107    unsafe { axhal::mem::clear_bss() };
108    axhal::init_percpu(cpu_id);
109    axhal::init_early(cpu_id, arg);
110
111    ax_println!("{}", LOGO);
112    ax_println!(
113        "\
114        arch = {}\n\
115        platform = {}\n\
116        target = {}\n\
117        build_mode = {}\n\
118        log_level = {}\n\
119        smp = {}\n\
120        ",
121        axconfig::ARCH,
122        axconfig::PLATFORM,
123        option_env!("AX_TARGET").unwrap_or(""),
124        option_env!("AX_MODE").unwrap_or(""),
125        option_env!("AX_LOG").unwrap_or(""),
126        axconfig::plat::CPU_NUM,
127    );
128    #[cfg(feature = "rtc")]
129    ax_println!(
130        "Boot at {}\n",
131        chrono::DateTime::from_timestamp_nanos(axhal::time::wall_time_nanos() as _),
132    );
133
134    axlog::init();
135    axlog::set_max_level(option_env!("AX_LOG").unwrap_or("")); // no effect if set `log-level-*` features
136    info!("Logging is enabled.");
137    info!("Primary CPU {} started, arg = {:#x}.", cpu_id, arg);
138
139    axhal::mem::init();
140    info!("Found physcial memory regions:");
141    for r in axhal::mem::memory_regions() {
142        info!(
143            "  [{:x?}, {:x?}) {} ({:?})",
144            r.paddr,
145            r.paddr + r.size,
146            r.name,
147            r.flags
148        );
149    }
150
151    #[cfg(feature = "alloc")]
152    init_allocator();
153
154    #[cfg(feature = "paging")]
155    axmm::init_memory_management();
156
157    info!("Initialize platform devices...");
158    axhal::init_later(cpu_id, arg);
159
160    #[cfg(feature = "multitask")]
161    axtask::init_scheduler();
162
163    #[cfg(any(feature = "fs", feature = "net", feature = "display"))]
164    {
165        #[allow(unused_variables)]
166        let all_devices = axdriver::init_drivers();
167
168        #[cfg(feature = "fs")]
169        axfs::init_filesystems(all_devices.block);
170
171        #[cfg(feature = "net")]
172        axnet::init_network(all_devices.net);
173
174        #[cfg(feature = "display")]
175        axdisplay::init_display(all_devices.display);
176    }
177
178    #[cfg(feature = "smp")]
179    self::mp::start_secondary_cpus(cpu_id);
180
181    #[cfg(feature = "irq")]
182    {
183        info!("Initialize interrupt handlers...");
184        init_interrupt();
185    }
186
187    #[cfg(all(feature = "tls", not(feature = "multitask")))]
188    {
189        info!("Initialize thread local storage...");
190        init_tls();
191    }
192
193    ctor_bare::call_ctors();
194
195    info!("Primary CPU {} init OK.", cpu_id);
196    INITED_CPUS.fetch_add(1, Ordering::Release);
197
198    while !is_init_ok() {
199        core::hint::spin_loop();
200    }
201
202    unsafe { main() };
203
204    #[cfg(feature = "multitask")]
205    axtask::exit(0);
206    #[cfg(not(feature = "multitask"))]
207    {
208        debug!("main task exited: exit_code={}", 0);
209        axhal::power::system_off();
210    }
211}
212
213#[cfg(feature = "alloc")]
214fn init_allocator() {
215    use axhal::mem::{MemRegionFlags, memory_regions, phys_to_virt};
216
217    info!("Initialize global memory allocator...");
218    info!("  use {} allocator.", axalloc::global_allocator().name());
219
220    let mut max_region_size = 0;
221    let mut max_region_paddr = 0.into();
222    for r in memory_regions() {
223        if r.flags.contains(MemRegionFlags::FREE) && r.size > max_region_size {
224            max_region_size = r.size;
225            max_region_paddr = r.paddr;
226        }
227    }
228    for r in memory_regions() {
229        if r.flags.contains(MemRegionFlags::FREE) && r.paddr == max_region_paddr {
230            axalloc::global_init(phys_to_virt(r.paddr).as_usize(), r.size);
231            break;
232        }
233    }
234    for r in memory_regions() {
235        if r.flags.contains(MemRegionFlags::FREE) && r.paddr != max_region_paddr {
236            axalloc::global_add_memory(phys_to_virt(r.paddr).as_usize(), r.size)
237                .expect("add heap memory region failed");
238        }
239    }
240}
241
242#[cfg(feature = "irq")]
243fn init_interrupt() {
244    // Setup timer interrupt handler
245    const PERIODIC_INTERVAL_NANOS: u64 =
246        axhal::time::NANOS_PER_SEC / axconfig::TICKS_PER_SEC as u64;
247
248    #[percpu::def_percpu]
249    static NEXT_DEADLINE: u64 = 0;
250
251    fn update_timer() {
252        let now_ns = axhal::time::monotonic_time_nanos();
253        // Safety: we have disabled preemption in IRQ handler.
254        let mut deadline = unsafe { NEXT_DEADLINE.read_current_raw() };
255        if now_ns >= deadline {
256            deadline = now_ns + PERIODIC_INTERVAL_NANOS;
257        }
258        unsafe { NEXT_DEADLINE.write_current_raw(deadline + PERIODIC_INTERVAL_NANOS) };
259        axhal::time::set_oneshot_timer(deadline);
260    }
261
262    axhal::irq::register(axconfig::devices::TIMER_IRQ, || {
263        update_timer();
264        #[cfg(feature = "multitask")]
265        axtask::on_timer_tick();
266    });
267
268    // Enable IRQs before starting app
269    axhal::asm::enable_irqs();
270}
271
272#[cfg(all(feature = "tls", not(feature = "multitask")))]
273fn init_tls() {
274    let main_tls = axhal::tls::TlsArea::alloc();
275    unsafe { axhal::asm::write_thread_pointer(main_tls.tls_ptr() as usize) };
276    core::mem::forget(main_tls);
277}