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    fn main();
47}
48
49struct LogIfImpl;
50
51#[crate_interface::impl_interface]
52impl axlog::LogIf for LogIfImpl {
53    fn console_write_str(s: &str) {
54        axhal::console::write_bytes(s.as_bytes());
55    }
56
57    fn current_time() -> core::time::Duration {
58        axhal::time::monotonic_time()
59    }
60
61    fn current_cpu_id() -> Option<usize> {
62        #[cfg(feature = "smp")]
63        if is_init_ok() {
64            Some(axhal::cpu::this_cpu_id())
65        } else {
66            None
67        }
68        #[cfg(not(feature = "smp"))]
69        Some(0)
70    }
71
72    fn current_task_id() -> Option<u64> {
73        if is_init_ok() {
74            #[cfg(feature = "multitask")]
75            {
76                axtask::current_may_uninit().map(|curr| curr.id().as_u64())
77            }
78            #[cfg(not(feature = "multitask"))]
79            None
80        } else {
81            None
82        }
83    }
84}
85
86use core::sync::atomic::{AtomicUsize, Ordering};
87
88static INITED_CPUS: AtomicUsize = AtomicUsize::new(0);
89
90fn is_init_ok() -> bool {
91    INITED_CPUS.load(Ordering::Acquire) == axconfig::SMP
92}
93
94/// The main entry point of the ArceOS runtime.
95///
96/// It is called from the bootstrapping code in [axhal]. `cpu_id` is the ID of
97/// the current CPU, and `dtb` is the address of the device tree blob. It
98/// finally calls the application's `main` function after all initialization
99/// work is done.
100///
101/// In multi-core environment, this function is called on the primary CPU,
102/// and the secondary CPUs call [`rust_main_secondary`].
103#[cfg_attr(not(test), unsafe(no_mangle))]
104pub extern "C" fn rust_main(cpu_id: usize, dtb: usize) -> ! {
105    ax_println!("{}", LOGO);
106    ax_println!(
107        "\
108        arch = {}\n\
109        platform = {}\n\
110        target = {}\n\
111        build_mode = {}\n\
112        log_level = {}\n\
113        smp = {}\n\
114        ",
115        axconfig::ARCH,
116        axconfig::PLATFORM,
117        option_env!("AX_TARGET").unwrap_or(""),
118        option_env!("AX_MODE").unwrap_or(""),
119        option_env!("AX_LOG").unwrap_or(""),
120        axconfig::SMP,
121    );
122    #[cfg(feature = "rtc")]
123    ax_println!(
124        "Boot at {}\n",
125        chrono::DateTime::from_timestamp_nanos(axhal::time::wall_time_nanos() as _),
126    );
127
128    axlog::init();
129    axlog::set_max_level(option_env!("AX_LOG").unwrap_or("")); // no effect if set `log-level-*` features
130    info!("Logging is enabled.");
131    info!("Primary CPU {} started, dtb = {:#x}.", cpu_id, dtb);
132
133    info!("Found physcial memory regions:");
134    for r in axhal::mem::memory_regions() {
135        info!(
136            "  [{:x?}, {:x?}) {} ({:?})",
137            r.paddr,
138            r.paddr + r.size,
139            r.name,
140            r.flags
141        );
142    }
143
144    #[cfg(feature = "alloc")]
145    init_allocator();
146
147    #[cfg(feature = "paging")]
148    axmm::init_memory_management();
149
150    info!("Initialize platform devices...");
151    axhal::platform_init();
152
153    #[cfg(feature = "multitask")]
154    axtask::init_scheduler();
155
156    #[cfg(any(feature = "fs", feature = "net", feature = "display"))]
157    {
158        #[allow(unused_variables)]
159        let all_devices = axdriver::init_drivers();
160
161        #[cfg(feature = "fs")]
162        axfs::init_filesystems(all_devices.block);
163
164        #[cfg(feature = "net")]
165        axnet::init_network(all_devices.net);
166
167        #[cfg(feature = "display")]
168        axdisplay::init_display(all_devices.display);
169    }
170
171    #[cfg(feature = "smp")]
172    self::mp::start_secondary_cpus(cpu_id);
173
174    #[cfg(feature = "irq")]
175    {
176        info!("Initialize interrupt handlers...");
177        init_interrupt();
178    }
179
180    #[cfg(all(feature = "tls", not(feature = "multitask")))]
181    {
182        info!("Initialize thread local storage...");
183        init_tls();
184    }
185
186    ctor_bare::call_ctors();
187
188    info!("Primary CPU {} init OK.", cpu_id);
189    INITED_CPUS.fetch_add(1, Ordering::Relaxed);
190
191    while !is_init_ok() {
192        core::hint::spin_loop();
193    }
194
195    unsafe { main() };
196
197    #[cfg(feature = "multitask")]
198    axtask::exit(0);
199    #[cfg(not(feature = "multitask"))]
200    {
201        debug!("main task exited: exit_code={}", 0);
202        axhal::misc::terminate();
203    }
204}
205
206#[cfg(feature = "alloc")]
207fn init_allocator() {
208    use axhal::mem::{MemRegionFlags, memory_regions, phys_to_virt};
209
210    info!("Initialize global memory allocator...");
211    info!("  use {} allocator.", axalloc::global_allocator().name());
212
213    let mut max_region_size = 0;
214    let mut max_region_paddr = 0.into();
215    for r in memory_regions() {
216        if r.flags.contains(MemRegionFlags::FREE) && r.size > max_region_size {
217            max_region_size = r.size;
218            max_region_paddr = r.paddr;
219        }
220    }
221    for r in memory_regions() {
222        if r.flags.contains(MemRegionFlags::FREE) && r.paddr == max_region_paddr {
223            axalloc::global_init(phys_to_virt(r.paddr).as_usize(), r.size);
224            break;
225        }
226    }
227    for r in memory_regions() {
228        if r.flags.contains(MemRegionFlags::FREE) && r.paddr != max_region_paddr {
229            axalloc::global_add_memory(phys_to_virt(r.paddr).as_usize(), r.size)
230                .expect("add heap memory region failed");
231        }
232    }
233}
234
235#[cfg(feature = "irq")]
236fn init_interrupt() {
237    use axhal::time::TIMER_IRQ_NUM;
238
239    // Setup timer interrupt handler
240    const PERIODIC_INTERVAL_NANOS: u64 =
241        axhal::time::NANOS_PER_SEC / axconfig::TICKS_PER_SEC as u64;
242
243    #[percpu::def_percpu]
244    static NEXT_DEADLINE: u64 = 0;
245
246    fn update_timer() {
247        let now_ns = axhal::time::monotonic_time_nanos();
248        // Safety: we have disabled preemption in IRQ handler.
249        let mut deadline = unsafe { NEXT_DEADLINE.read_current_raw() };
250        if now_ns >= deadline {
251            deadline = now_ns + PERIODIC_INTERVAL_NANOS;
252        }
253        unsafe { NEXT_DEADLINE.write_current_raw(deadline + PERIODIC_INTERVAL_NANOS) };
254        axhal::time::set_oneshot_timer(deadline);
255    }
256
257    axhal::irq::register_handler(TIMER_IRQ_NUM, || {
258        update_timer();
259        #[cfg(feature = "multitask")]
260        axtask::on_timer_tick();
261    });
262
263    // Enable IRQs before starting app
264    axhal::arch::enable_irqs();
265}
266
267#[cfg(all(feature = "tls", not(feature = "multitask")))]
268fn init_tls() {
269    let main_tls = axhal::tls::TlsArea::alloc();
270    unsafe { axhal::arch::write_thread_pointer(main_tls.tls_ptr() as usize) };
271    core::mem::forget(main_tls);
272}