axhal/
lib.rs

1//! [ArceOS] hardware abstraction layer, provides unified APIs for
2//! platform-specific operations.
3//!
4//! It does the bootstrapping and initialization process for the specified
5//! platform, and provides useful operations on the hardware.
6//!
7//! Currently supported platforms (specify by cargo features):
8//!
9//! - `x86-pc`: Standard PC with x86_64 ISA.
10//! - `riscv64-qemu-virt`: QEMU virt machine with RISC-V ISA.
11//! - `aarch64-qemu-virt`: QEMU virt machine with AArch64 ISA.
12//! - `aarch64-raspi`: Raspberry Pi with AArch64 ISA.
13//! - `dummy`: If none of the above platform is selected, the dummy platform
14//!   will be used. In this platform, most of the operations are no-op or
15//!   `unimplemented!()`. This platform is mainly used for [cargo test].
16//!
17//! # Cargo Features
18//!
19//! - `smp`: Enable SMP (symmetric multiprocessing) support.
20//! - `fp-simd`: Enable floating-point and SIMD support.
21//! - `paging`: Enable page table manipulation.
22//! - `irq`: Enable interrupt handling support.
23//! - `tls`: Enable kernel space thread-local storage support.
24//! - `rtc`: Enable real-time clock support.
25//! - `uspace`: Enable user space support.
26//!
27//! [ArceOS]: https://github.com/arceos-org/arceos
28//! [cargo test]: https://doc.rust-lang.org/cargo/guide/tests.html
29
30#![no_std]
31
32#[allow(unused_imports)]
33#[macro_use]
34extern crate log;
35
36#[allow(unused_imports)]
37#[macro_use]
38extern crate axlog;
39
40#[allow(unused_imports)]
41#[macro_use]
42extern crate memory_addr;
43
44cfg_if::cfg_if! {
45    if #[cfg(feature = "myplat")] {
46        // link the custom platform crate in your application.
47    } else if #[cfg(target_os = "none")] {
48        #[cfg(target_arch = "x86_64")]
49        extern crate axplat_x86_pc;
50        #[cfg(target_arch = "aarch64")]
51        extern crate axplat_aarch64_qemu_virt;
52        #[cfg(target_arch = "arm")]
53        extern crate axplat_arm_qemu_virt;
54        #[cfg(target_arch = "riscv64")]
55        extern crate axplat_riscv64_qemu_virt;
56        #[cfg(target_arch = "loongarch64")]
57        extern crate axplat_loongarch64_qemu_virt;
58    } else {
59        // Link the dummy platform implementation to pass cargo test.
60        mod dummy;
61    }
62}
63
64pub mod mem;
65pub mod percpu;
66pub mod time;
67
68#[cfg(feature = "tls")]
69pub mod tls;
70
71#[cfg(feature = "irq")]
72pub mod irq;
73
74#[cfg(feature = "paging")]
75pub mod paging;
76
77/// Console input and output.
78pub mod console {
79    pub use axplat::console::{read_bytes, write_bytes};
80}
81
82/// CPU power management.
83pub mod power {
84    #[cfg(feature = "smp")]
85    pub use axplat::power::cpu_boot;
86    pub use axplat::power::system_off;
87}
88
89/// Trap handling.
90pub mod trap {
91    #[cfg(feature = "uspace")]
92    pub use axcpu::trap::SYSCALL;
93    pub use axcpu::trap::{IRQ, PAGE_FAULT};
94    pub use axcpu::trap::{PageFaultFlags, register_trap_handler};
95}
96
97/// CPU register states for context switching.
98///
99/// There are three types of context:
100///
101/// - [`TaskContext`][axcpu::TaskContext]: The context of a task.
102/// - [`TrapFrame`][axcpu::TrapFrame]: The context of an interrupt or an exception.
103/// - [`UspaceContext`][axcpu::uspace::UspaceContext]: The context for user/kernel mode switching.
104pub mod context {
105    #[cfg(feature = "uspace")]
106    pub use axcpu::uspace::UspaceContext;
107    pub use axcpu::{TaskContext, TrapFrame};
108}
109
110pub use axcpu::asm;
111
112#[cfg(feature = "smp")]
113pub use axplat::init::{init_early_secondary, init_later_secondary};
114#[cfg(feature = "smp")]
115use core::sync::atomic::{AtomicUsize, Ordering};
116
117/// Initializes CPU-local data structures for the primary core.
118///
119/// This function should be called as early as possible, as other initializations
120/// may acess the CPU-local data.
121pub fn init_percpu(cpu_id: usize) {
122    self::percpu::init_primary(cpu_id);
123}
124
125/// Initializes CPU-local data structures for secondary cores.
126///
127/// This function should be called as early as possible, as other initializations
128/// may acess the CPU-local data.
129#[cfg(feature = "smp")]
130pub fn init_percpu_secondary(cpu_id: usize) {
131    self::percpu::init_secondary(cpu_id);
132}
133
134/// Initializes the platform and boot argument.
135/// This function should be called as early as possible.
136pub fn init_early(cpu_id: usize, arg: usize) {
137    BOOT_ARG.init_once(arg);
138    axplat::init::init_early(cpu_id, arg);
139}
140
141/// Initializes the platform later stage.
142pub fn init_later(cpu_id: usize, arg: usize) {
143    axplat::init::init_later(cpu_id, arg);
144    init_cpu_num();
145}
146
147use lazyinit::LazyInit;
148
149static BOOT_ARG: LazyInit<usize> = LazyInit::new();
150
151/// Returns the boot argument.
152/// This is typically the device tree blob address passed from the bootloader.
153pub fn get_bootarg() -> usize {
154    *BOOT_ARG
155}
156
157/// The number of CPUs in the system. Based on the number declared by the
158/// platform crate and limited by the configured maximum CPU number.
159#[cfg(feature = "smp")]
160static CPU_NUM: AtomicUsize = AtomicUsize::new(1);
161
162/// Gets the number of CPUs running in the system.
163///
164/// When SMP is disabled, this function always returns 1.
165///
166/// When SMP is enabled, it's the smaller one between the platform-declared CPU
167/// number [`axplat::power::cpu_num`] and the configured maximum CPU number
168/// `axconfig::plat::MAX_CPU_NUM`.
169///
170/// This value is determined during the BSP initialization phase.
171pub fn cpu_num() -> usize {
172    #[cfg(feature = "smp")]
173    {
174        // The BSP will always see the correct value because `CPU_NUM` is set by
175        // itself.
176        //
177        // All APs will see the correct value because it is written with
178        // `Ordering::Release` and read with `Ordering::Acquire`, ensuring
179        // memory visibility.
180        //
181        // Acquire may result in a performance penalty, but this function is not
182        // expected to be called frequently in normal operation.
183        CPU_NUM.load(Ordering::Acquire)
184    }
185    #[cfg(not(feature = "smp"))]
186    {
187        1
188    }
189}
190
191/// Initializes the CPU number information.
192fn init_cpu_num() {
193    #[cfg(feature = "smp")]
194    {
195        let plat_cpu_num = axplat::power::cpu_num();
196        let max_cpu_num = axconfig::plat::MAX_CPU_NUM;
197        let cpu_num = plat_cpu_num.min(max_cpu_num);
198
199        info!("CPU number: max = {max_cpu_num}, platform = {plat_cpu_num}, use = {cpu_num}",);
200        ax_println!("smp = {}", cpu_num); // for test purposes
201
202        if plat_cpu_num > max_cpu_num {
203            warn!(
204                "platform declares more CPUs ({plat_cpu_num}) than configured max ({max_cpu_num}), \
205                only the first {max_cpu_num} CPUs will be used."
206            );
207        }
208
209        CPU_NUM.store(cpu_num, Ordering::Release);
210    }
211    #[cfg(not(feature = "smp"))]
212    {
213        ax_println!("smp = 1"); // for test purposes
214    }
215}