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
21#[macro_use]
22extern crate axlog;
23
24#[cfg(all(target_os = "none", not(test)))]
25mod lang_items;
26
27#[cfg(feature = "smp")]
28mod mp;
29
30#[cfg(feature = "smp")]
31pub use self::mp::rust_main_secondary;
32
33const LOGO: &str = r#"
34       d8888                            .d88888b.   .d8888b.
35      d88888                           d88P" "Y88b d88P  Y88b
36     d88P888                           888     888 Y88b.
37    d88P 888 888d888  .d8888b  .d88b.  888     888  "Y888b.
38   d88P  888 888P"   d88P"    d8P  Y8b 888     888     "Y88b.
39  d88P   888 888     888      88888888 888     888       "888
40 d8888888888 888     Y88b.    Y8b.     Y88b. .d88P Y88b  d88P
41d88P     888 888      "Y8888P  "Y8888   "Y88888P"   "Y8888P"
42"#;
43
44unsafe extern "C" {
45    /// Application's entry point.
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::percpu::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
88/// Number of CPUs that have completed initialization.
89static INITED_CPUS: AtomicUsize = AtomicUsize::new(0);
90
91fn is_init_ok() -> bool {
92    INITED_CPUS.load(Ordering::Acquire) == axhal::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        ",
120        axconfig::ARCH,
121        axconfig::PLATFORM,
122        option_env!("AX_TARGET").unwrap_or(""),
123        option_env!("AX_MODE").unwrap_or(""),
124        option_env!("AX_LOG").unwrap_or(""),
125    );
126    #[cfg(feature = "rtc")]
127    ax_println!(
128        "Boot at {}\n",
129        chrono::DateTime::from_timestamp_nanos(axhal::time::wall_time_nanos() as _),
130    );
131
132    axlog::init();
133    axlog::set_max_level(option_env!("AX_LOG").unwrap_or("")); // no effect if set `log-level-*` features
134    info!("Logging is enabled.");
135    info!("Primary CPU {} started, arg = {:#x}.", cpu_id, arg);
136
137    axhal::mem::init();
138    info!("Found physcial memory regions:");
139    for r in axhal::mem::memory_regions() {
140        info!(
141            "  [{:x?}, {:x?}) {} ({:?})",
142            r.paddr,
143            r.paddr + r.size,
144            r.name,
145            r.flags
146        );
147    }
148
149    #[cfg(feature = "alloc")]
150    init_allocator();
151
152    #[cfg(feature = "paging")]
153    axmm::init_memory_management();
154
155    info!("Initialize platform devices...");
156    axhal::init_later(cpu_id, arg);
157
158    #[cfg(feature = "multitask")]
159    axtask::init_scheduler();
160
161    #[cfg(any(feature = "fs", feature = "net", feature = "display"))]
162    {
163        #[allow(unused_variables)]
164        let all_devices = axdriver::init_drivers();
165
166        #[cfg(feature = "fs")]
167        axfs::init_filesystems(all_devices.block);
168
169        #[cfg(feature = "net")]
170        axnet::init_network(all_devices.net);
171
172        #[cfg(feature = "display")]
173        axdisplay::init_display(all_devices.display);
174    }
175
176    #[cfg(feature = "smp")]
177    self::mp::start_secondary_cpus(cpu_id);
178
179    #[cfg(feature = "irq")]
180    {
181        info!("Initialize interrupt handlers...");
182        init_interrupt();
183    }
184
185    #[cfg(all(feature = "tls", not(feature = "multitask")))]
186    {
187        info!("Initialize thread local storage...");
188        init_tls();
189    }
190
191    ctor_bare::call_ctors();
192
193    info!("Primary CPU {} init OK.", cpu_id);
194    INITED_CPUS.fetch_add(1, Ordering::Release);
195
196    while !is_init_ok() {
197        core::hint::spin_loop();
198    }
199
200    unsafe { main() };
201
202    #[cfg(feature = "multitask")]
203    axtask::exit(0);
204    #[cfg(not(feature = "multitask"))]
205    {
206        debug!("main task exited: exit_code={}", 0);
207        axhal::power::system_off();
208    }
209}
210
211#[cfg(feature = "alloc")]
212fn init_allocator() {
213    use axhal::mem::{MemRegionFlags, memory_regions, phys_to_virt};
214
215    info!("Initialize global memory allocator...");
216    info!("  use {} allocator.", axalloc::global_allocator().name());
217
218    let mut max_region_size = 0;
219    let mut max_region_paddr = 0.into();
220    for r in memory_regions() {
221        if r.flags.contains(MemRegionFlags::FREE) && r.size > max_region_size {
222            max_region_size = r.size;
223            max_region_paddr = r.paddr;
224        }
225    }
226    for r in memory_regions() {
227        if r.flags.contains(MemRegionFlags::FREE) && r.paddr == max_region_paddr {
228            axalloc::global_init(phys_to_virt(r.paddr).as_usize(), r.size);
229            break;
230        }
231    }
232    for r in memory_regions() {
233        if r.flags.contains(MemRegionFlags::FREE) && r.paddr != max_region_paddr {
234            axalloc::global_add_memory(phys_to_virt(r.paddr).as_usize(), r.size)
235                .expect("add heap memory region failed");
236        }
237    }
238}
239
240#[cfg(feature = "irq")]
241fn init_interrupt() {
242    // Setup timer interrupt handler
243    const PERIODIC_INTERVAL_NANOS: u64 =
244        axhal::time::NANOS_PER_SEC / axconfig::TICKS_PER_SEC as u64;
245
246    #[percpu::def_percpu]
247    static NEXT_DEADLINE: u64 = 0;
248
249    fn update_timer() {
250        let now_ns = axhal::time::monotonic_time_nanos();
251        // Safety: we have disabled preemption in IRQ handler.
252        let mut deadline = unsafe { NEXT_DEADLINE.read_current_raw() };
253        if now_ns >= deadline {
254            deadline = now_ns + PERIODIC_INTERVAL_NANOS;
255        }
256        unsafe { NEXT_DEADLINE.write_current_raw(deadline + PERIODIC_INTERVAL_NANOS) };
257        axhal::time::set_oneshot_timer(deadline);
258    }
259
260    axhal::irq::register(axconfig::devices::TIMER_IRQ, || {
261        update_timer();
262        #[cfg(feature = "multitask")]
263        axtask::on_timer_tick();
264    });
265
266    #[cfg(feature = "ipi")]
267    axhal::irq::register(axhal::irq::IPI_IRQ, || {
268        axipi::ipi_handler();
269    });
270
271    // Enable IRQs before starting app
272    axhal::asm::enable_irqs();
273}
274
275#[cfg(all(feature = "tls", not(feature = "multitask")))]
276fn init_tls() {
277    let main_tls = axhal::tls::TlsArea::alloc();
278    unsafe { axhal::asm::write_thread_pointer(main_tls.tls_ptr() as usize) };
279    core::mem::forget(main_tls);
280}