axhal/
cpu.rs

1//! CPU-related operations.
2
3#[percpu::def_percpu]
4static CPU_ID: usize = 0;
5
6#[percpu::def_percpu]
7static IS_BSP: bool = false;
8
9#[percpu::def_percpu]
10static CURRENT_TASK_PTR: usize = 0;
11
12/// Returns the ID of the current CPU.
13#[inline]
14pub fn this_cpu_id() -> usize {
15    CPU_ID.read_current()
16}
17
18/// Returns whether the current CPU is the primary CPU (aka the bootstrap
19/// processor or BSP)
20#[inline]
21pub fn this_cpu_is_bsp() -> bool {
22    IS_BSP.read_current()
23}
24
25/// Stores the pointer to the current task in the SP_EL0 register.
26///
27/// In aarch64 architecture, we use `SP_EL0` as the read cache for
28/// the current task pointer. And this function will update this cache.
29#[cfg(target_arch = "aarch64")]
30pub(crate) unsafe fn cache_current_task_ptr() {
31    use tock_registers::interfaces::Writeable;
32    aarch64_cpu::registers::SP_EL0.set(CURRENT_TASK_PTR.read_current_raw() as u64);
33}
34
35/// Gets the pointer to the current task with preemption-safety.
36///
37/// Preemption may be enabled when calling this function. This function will
38/// guarantee the correctness even the current task is preempted.
39#[inline]
40pub fn current_task_ptr<T>() -> *const T {
41    #[cfg(target_arch = "x86_64")]
42    unsafe {
43        // on x86, only one instruction is needed to read the per-CPU task pointer from `gs:[off]`.
44        CURRENT_TASK_PTR.read_current_raw() as _
45    }
46    #[cfg(any(
47        target_arch = "riscv32",
48        target_arch = "riscv64",
49        target_arch = "loongarch64"
50    ))]
51    unsafe {
52        // on RISC-V and LA64, reading `CURRENT_TASK_PTR` requires multiple instruction, so we disable local IRQs.
53        let _guard = kernel_guard::IrqSave::new();
54        CURRENT_TASK_PTR.read_current_raw() as _
55    }
56    #[cfg(target_arch = "aarch64")]
57    {
58        // on ARM64, we use `SP_EL0` to store the task pointer.
59        // `SP_EL0` is equivalent to the cache of CURRENT_TASK_PTR here.
60        use tock_registers::interfaces::Readable;
61        aarch64_cpu::registers::SP_EL0.get() as _
62    }
63}
64
65/// Sets the pointer to the current task with preemption-safety.
66///
67/// Preemption may be enabled when calling this function. This function will
68/// guarantee the correctness even the current task is preempted.
69///
70/// # Safety
71///
72/// The given `ptr` must be pointed to a valid task structure.
73#[inline]
74pub unsafe fn set_current_task_ptr<T>(ptr: *const T) {
75    #[cfg(target_arch = "x86_64")]
76    {
77        unsafe { CURRENT_TASK_PTR.write_current_raw(ptr as usize) }
78    }
79    #[cfg(any(
80        target_arch = "riscv32",
81        target_arch = "riscv64",
82        target_arch = "loongarch64"
83    ))]
84    {
85        let _guard = kernel_guard::IrqSave::new();
86        unsafe { CURRENT_TASK_PTR.write_current_raw(ptr as usize) }
87    }
88    #[cfg(target_arch = "aarch64")]
89    {
90        let _guard = kernel_guard::IrqSave::new();
91        CURRENT_TASK_PTR.write_current_raw(ptr as usize);
92        cache_current_task_ptr();
93    }
94}
95
96#[allow(dead_code)]
97pub(crate) fn init_primary(cpu_id: usize) {
98    percpu::init();
99    percpu::init_percpu_reg(cpu_id);
100    unsafe {
101        CPU_ID.write_current_raw(cpu_id);
102        IS_BSP.write_current_raw(true);
103    }
104    crate::arch::cpu_init();
105}
106
107#[allow(dead_code)]
108pub(crate) fn init_secondary(cpu_id: usize) {
109    percpu::init_percpu_reg(cpu_id);
110    unsafe {
111        CPU_ID.write_current_raw(cpu_id);
112        IS_BSP.write_current_raw(false);
113    }
114    crate::arch::cpu_init();
115}