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
89/// Number of CPUs that have completed initialization.
90static INITED_CPUS: AtomicUsize = AtomicUsize::new(0);
91
92fn is_init_ok() -> bool {
93    INITED_CPUS.load(Ordering::Acquire) == axhal::cpu_num()
94}
95
96/// The main entry point of the ArceOS runtime.
97///
98/// It is called from the bootstrapping code in the specific platform crate (see
99/// [`axplat::main`]).
100///
101/// `cpu_id` is the logic ID of the current CPU, and `arg` is passed from the
102/// bootloader (typically the device tree blob address).
103///
104/// In multi-core environment, this function is called on the primary core, and
105/// secondary cores call [`rust_main_secondary`].
106#[cfg_attr(not(test), axplat::main)]
107pub fn rust_main(cpu_id: usize, arg: usize) -> ! {
108    unsafe { axhal::mem::clear_bss() };
109    axhal::init_percpu(cpu_id);
110    axhal::init_early(cpu_id, arg);
111
112    ax_println!("{}", LOGO);
113    ax_println!(
114        "\
115        arch = {}\n\
116        platform = {}\n\
117        target = {}\n\
118        build_mode = {}\n\
119        log_level = {}\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    );
127    #[cfg(feature = "rtc")]
128    ax_println!(
129        "Boot at {}\n",
130        chrono::DateTime::from_timestamp_nanos(axhal::time::wall_time_nanos() as _),
131    );
132
133    axlog::init();
134    axlog::set_max_level(option_env!("AX_LOG").unwrap_or("")); // no effect if set `log-level-*` features
135    info!("Logging is enabled.");
136    info!("Primary CPU {} started, arg = {:#x}.", cpu_id, arg);
137
138    axhal::mem::init();
139    info!("Found physcial memory regions:");
140    for r in axhal::mem::memory_regions() {
141        info!(
142            "  [{:x?}, {:x?}) {} ({:?})",
143            r.paddr,
144            r.paddr + r.size,
145            r.name,
146            r.flags
147        );
148    }
149
150    #[cfg(feature = "alloc")]
151    init_allocator();
152
153    #[cfg(feature = "paging")]
154    axmm::init_memory_management();
155
156    info!("Initialize platform devices...");
157    axhal::init_later(cpu_id, arg);
158
159    #[cfg(feature = "multitask")]
160    axtask::init_scheduler();
161
162    #[cfg(any(feature = "fs", feature = "net", feature = "display"))]
163    {
164        #[allow(unused_variables)]
165        let all_devices = axdriver::init_drivers();
166
167        #[cfg(feature = "fs")]
168        axfs::init_filesystems(all_devices.block);
169
170        #[cfg(feature = "net")]
171        axnet::init_network(all_devices.net);
172
173        #[cfg(feature = "display")]
174        axdisplay::init_display(all_devices.display);
175    }
176
177    #[cfg(feature = "smp")]
178    self::mp::start_secondary_cpus(cpu_id);
179
180    #[cfg(feature = "irq")]
181    {
182        info!("Initialize interrupt handlers...");
183        init_interrupt();
184    }
185
186    #[cfg(all(feature = "tls", not(feature = "multitask")))]
187    {
188        info!("Initialize thread local storage...");
189        init_tls();
190    }
191
192    ctor_bare::call_ctors();
193
194    info!("Primary CPU {} init OK.", cpu_id);
195    INITED_CPUS.fetch_add(1, Ordering::Release);
196
197    while !is_init_ok() {
198        core::hint::spin_loop();
199    }
200
201    unsafe { main() };
202
203    #[cfg(feature = "multitask")]
204    axtask::exit(0);
205    #[cfg(not(feature = "multitask"))]
206    {
207        debug!("main task exited: exit_code={}", 0);
208        axhal::power::system_off();
209    }
210}
211
212#[cfg(feature = "alloc")]
213fn init_allocator() {
214    use axhal::mem::{MemRegionFlags, memory_regions, phys_to_virt};
215
216    info!("Initialize global memory allocator...");
217    info!("  use {} allocator.", axalloc::global_allocator().name());
218
219    let mut max_region_size = 0;
220    let mut max_region_paddr = 0.into();
221    for r in memory_regions() {
222        if r.flags.contains(MemRegionFlags::FREE) && r.size > max_region_size {
223            max_region_size = r.size;
224            max_region_paddr = r.paddr;
225        }
226    }
227    for r in memory_regions() {
228        if r.flags.contains(MemRegionFlags::FREE) && r.paddr == max_region_paddr {
229            axalloc::global_init(phys_to_virt(r.paddr).as_usize(), r.size);
230            break;
231        }
232    }
233    for r in memory_regions() {
234        if r.flags.contains(MemRegionFlags::FREE) && r.paddr != max_region_paddr {
235            axalloc::global_add_memory(phys_to_virt(r.paddr).as_usize(), r.size)
236                .expect("add heap memory region failed");
237        }
238    }
239}
240
241#[cfg(feature = "irq")]
242fn init_interrupt() {
243    // Setup timer interrupt handler
244    const PERIODIC_INTERVAL_NANOS: u64 =
245        axhal::time::NANOS_PER_SEC / axconfig::TICKS_PER_SEC as u64;
246
247    #[percpu::def_percpu]
248    static NEXT_DEADLINE: u64 = 0;
249
250    fn update_timer() {
251        let now_ns = axhal::time::monotonic_time_nanos();
252        // Safety: we have disabled preemption in IRQ handler.
253        let mut deadline = unsafe { NEXT_DEADLINE.read_current_raw() };
254        if now_ns >= deadline {
255            deadline = now_ns + PERIODIC_INTERVAL_NANOS;
256        }
257        unsafe { NEXT_DEADLINE.write_current_raw(deadline + PERIODIC_INTERVAL_NANOS) };
258        axhal::time::set_oneshot_timer(deadline);
259    }
260
261    axhal::irq::register(axconfig::devices::TIMER_IRQ, || {
262        update_timer();
263        #[cfg(feature = "multitask")]
264        axtask::on_timer_tick();
265    });
266
267    #[cfg(feature = "ipi")]
268    axhal::irq::register(axhal::irq::IPI_IRQ, || {
269        axipi::ipi_handler();
270    });
271
272    // Enable IRQs before starting app
273    axhal::asm::enable_irqs();
274}
275
276#[cfg(all(feature = "tls", not(feature = "multitask")))]
277fn init_tls() {
278    let main_tls = axhal::tls::TlsArea::alloc();
279    unsafe { axhal::asm::write_thread_pointer(main_tls.tls_ptr() as usize) };
280    core::mem::forget(main_tls);
281}