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#![feature(doc_auto_cfg)]
32
33#[allow(unused_imports)]
34#[macro_use]
35extern crate log;
36
37#[allow(unused_imports)]
38#[macro_use]
39extern crate axlog;
40
41#[allow(unused_imports)]
42#[macro_use]
43extern crate memory_addr;
44
45cfg_if::cfg_if! {
46 if #[cfg(feature = "myplat")] {
47 // link the custom platform crate in your application.
48 } else if #[cfg(target_os = "none")] {
49 #[cfg(target_arch = "x86_64")]
50 extern crate axplat_x86_pc;
51 #[cfg(target_arch = "aarch64")]
52 extern crate axplat_aarch64_qemu_virt;
53 #[cfg(target_arch = "riscv64")]
54 extern crate axplat_riscv64_qemu_virt;
55 #[cfg(target_arch = "loongarch64")]
56 extern crate axplat_loongarch64_qemu_virt;
57 } else {
58 // Link the dummy platform implementation to pass cargo test.
59 mod dummy;
60 }
61}
62
63pub mod mem;
64pub mod percpu;
65pub mod time;
66
67#[cfg(feature = "tls")]
68pub mod tls;
69
70#[cfg(feature = "irq")]
71pub mod irq;
72
73#[cfg(feature = "paging")]
74pub mod paging;
75
76/// Console input and output.
77pub mod console {
78 pub use axplat::console::{read_bytes, write_bytes};
79}
80
81/// CPU power management.
82pub mod power {
83 #[cfg(feature = "smp")]
84 pub use axplat::power::cpu_boot;
85 pub use axplat::power::system_off;
86}
87
88/// Trap handling.
89pub mod trap {
90 #[cfg(feature = "uspace")]
91 pub use axcpu::trap::SYSCALL;
92 pub use axcpu::trap::{IRQ, PAGE_FAULT};
93 pub use axcpu::trap::{PageFaultFlags, register_trap_handler};
94}
95
96/// CPU register states for context switching.
97///
98/// There are three types of context:
99///
100/// - [`TaskContext`][axcpu::TaskContext]: The context of a task.
101/// - [`TrapFrame`][axcpu::TrapFrame]: The context of an interrupt or an exception.
102/// - [`UspaceContext`][axcpu::uspace::UspaceContext]: The context for user/kernel mode switching.
103pub mod context {
104 #[cfg(feature = "uspace")]
105 pub use axcpu::uspace::UspaceContext;
106 pub use axcpu::{TaskContext, TrapFrame};
107}
108
109pub use axcpu::asm;
110
111#[cfg(feature = "smp")]
112pub use axplat::init::{init_early_secondary, init_later_secondary};
113#[cfg(feature = "smp")]
114use core::sync::atomic::{AtomicUsize, Ordering};
115
116/// Initializes CPU-local data structures for the primary core.
117///
118/// This function should be called as early as possible, as other initializations
119/// may acess the CPU-local data.
120pub fn init_percpu(cpu_id: usize) {
121 self::percpu::init_primary(cpu_id);
122}
123
124/// Initializes CPU-local data structures for secondary cores.
125///
126/// This function should be called as early as possible, as other initializations
127/// may acess the CPU-local data.
128#[cfg(feature = "smp")]
129pub fn init_percpu_secondary(cpu_id: usize) {
130 self::percpu::init_secondary(cpu_id);
131}
132
133/// Initializes the platform and boot argument.
134/// This function should be called as early as possible.
135pub fn init_early(cpu_id: usize, arg: usize) {
136 BOOT_ARG.init_once(arg);
137 axplat::init::init_early(cpu_id, arg);
138}
139
140/// Initializes the platform later stage.
141pub fn init_later(cpu_id: usize, arg: usize) {
142 axplat::init::init_later(cpu_id, arg);
143 init_cpu_num();
144}
145
146use lazyinit::LazyInit;
147
148static BOOT_ARG: LazyInit<usize> = LazyInit::new();
149
150/// Returns the boot argument.
151/// This is typically the device tree blob address passed from the bootloader.
152pub fn get_bootarg() -> usize {
153 *BOOT_ARG
154}
155
156/// The number of CPUs in the system. Based on the number declared by the
157/// platform crate and limited by the configured maximum CPU number.
158#[cfg(feature = "smp")]
159static CPU_NUM: AtomicUsize = AtomicUsize::new(1);
160
161/// Gets the number of CPUs running in the system.
162///
163/// When SMP is disabled, this function always returns 1.
164///
165/// When SMP is enabled, it's the smaller one between the platform-declared CPU
166/// number [`axplat::power::cpu_num`] and the configured maximum CPU number
167/// `axconfig::plat::MAX_CPU_NUM`.
168///
169/// This value is determined during the BSP initialization phase.
170pub fn cpu_num() -> usize {
171 #[cfg(feature = "smp")]
172 {
173 // The BSP will always see the correct value because `CPU_NUM` is set by
174 // itself.
175 //
176 // All APs will see the correct value because it is written with
177 // `Ordering::Release` and read with `Ordering::Acquire`, ensuring
178 // memory visibility.
179 //
180 // Acquire may result in a performance penalty, but this function is not
181 // expected to be called frequently in normal operation.
182 CPU_NUM.load(Ordering::Acquire)
183 }
184 #[cfg(not(feature = "smp"))]
185 {
186 1
187 }
188}
189
190/// Initializes the CPU number information.
191fn init_cpu_num() {
192 #[cfg(feature = "smp")]
193 {
194 let plat_cpu_num = axplat::power::cpu_num();
195 let max_cpu_num = axconfig::plat::MAX_CPU_NUM;
196 let cpu_num = plat_cpu_num.min(max_cpu_num);
197
198 info!("CPU number: max = {max_cpu_num}, platform = {plat_cpu_num}, use = {cpu_num}",);
199 ax_println!("smp = {}", cpu_num); // for test purposes
200
201 if plat_cpu_num > max_cpu_num {
202 warn!(
203 "platform declares more CPUs ({plat_cpu_num}) than configured max ({max_cpu_num}), \
204 only the first {max_cpu_num} CPUs will be used."
205 );
206 }
207
208 CPU_NUM.store(cpu_num, Ordering::Release);
209 }
210 #[cfg(not(feature = "smp"))]
211 {
212 ax_println!("smp = 1"); // for test purposes
213 }
214}