Skip to main content

ostd/cpu/local/
static_cpu_local.rs

1// SPDX-License-Identifier: MPL-2.0
2
3//! Statically-allocated CPU-local objects.
4
5#![cfg_attr(
6    any(
7        target_arch = "riscv64",
8        target_arch = "loongarch64",
9        target_arch = "aarch64"
10    ),
11    expect(dead_code)
12)]
13
14use core::marker::PhantomData;
15
16use super::{__cpu_local_end, __cpu_local_start, AnyStorage, CpuLocal};
17use crate::{arch, cpu::CpuId, irq::DisabledLocalIrqGuard, util::id_set::Id};
18
19/// Defines a statically-allocated CPU-local variable.
20///
21/// The accessors of the CPU-local variables are defined with [`CpuLocal`].
22///
23/// You can get the reference to the inner object on one CPU by calling
24/// [`CpuLocal::get_on_cpu`]. Also if you intend to access the inner object
25/// on the current CPU, you can use [`CpuLocal::get_with`]. The latter
26/// accessors can be used even if the inner object is not `Sync`.
27///
28/// # Example
29///
30/// ```rust
31/// use ostd::{cpu_local, cpu::PinCurrentCpu, task::disable_preempt, trap};
32/// use core::{sync::atomic::{AtomicU32, Ordering}, cell::Cell};
33///
34/// cpu_local! {
35///     static FOO: AtomicU32 = AtomicU32::new(1);
36///     pub static BAR: Cell<usize> = Cell::new(2);
37/// }
38///
39/// fn not_an_atomic_function() {
40///     let preempt_guard = disable_preempt();
41///     let ref_of_foo = FOO.get_on_cpu(preempt_guard.current_cpu());
42///     let val_of_foo = ref_of_foo.load(Ordering::Relaxed);
43///     println!("FOO VAL: {}", val_of_foo);
44///
45///     let irq_guard = irq::disable_local();
46///     let bar_guard = BAR.get_with(&irq_guard);
47///     let val_of_bar = bar_guard.get();
48///     println!("BAR VAL: {}", val_of_bar);
49/// }
50/// ```
51#[macro_export]
52macro_rules! cpu_local {
53    ($( $(#[$attr:meta])* $vis:vis static $name:ident: $t:ty = $init:expr; )*) => {
54        $(
55            // SAFETY: This is properly handled in the linker script.
56            #[unsafe(link_section = ".cpu_local")]
57            $(#[$attr])* $vis static $name: $crate::cpu::local::StaticCpuLocal<$t> = {
58                let val = $init;
59                // SAFETY: The per-CPU variable instantiated is statically
60                // stored in the special `.cpu_local` section.
61                unsafe {
62                    $crate::cpu::local::CpuLocal::__new_static(val)
63                }
64            };
65        )*
66    };
67}
68
69/// A static storage for a CPU-local variable of type `T`.
70///
71/// Such a CPU-local storage is not intended to be allocated directly.
72/// Use the `cpu_local` macro instead.
73pub struct StaticStorage<T: 'static>(T);
74
75impl<T: 'static> StaticStorage<T> {
76    /// Gets access to the underlying value through a raw pointer.
77    ///
78    /// This method is safe, but using the returned pointer will be unsafe.
79    fn as_ptr(&self) -> *const T {
80        super::is_used::debug_set_true();
81
82        let offset = self.get_offset();
83
84        let local_base = arch::cpu::local::get_base() as usize;
85        let local_va = local_base + offset;
86
87        // A sanity check about the alignment.
88        debug_assert_eq!(local_va % align_of::<T>(), 0);
89
90        local_va as *const T
91    }
92
93    /// Gets the offset of the CPU-local object in the CPU-local area.
94    fn get_offset(&self) -> usize {
95        let bsp_va = self as *const _ as usize;
96        let bsp_base = __cpu_local_start as *const () as usize;
97        // The implementation should ensure that the CPU-local object resides in the `.cpu_local`.
98        debug_assert!(bsp_va + size_of::<T>() <= __cpu_local_end as *const () as usize);
99
100        bsp_va - bsp_base
101    }
102}
103
104unsafe impl<T: 'static> AnyStorage<T> for StaticStorage<T> {
105    fn get_ptr_on_current(&self, _guard: &DisabledLocalIrqGuard) -> *const T {
106        self.as_ptr()
107    }
108
109    fn get_ptr_on_target(&self, cpu_id: CpuId) -> *const T {
110        super::is_used::debug_set_true();
111
112        let cpu_id = cpu_id.as_usize();
113
114        // If on the BSP, just use the statically linked storage.
115        if cpu_id == 0 {
116            return &self.0 as *const T;
117        }
118
119        let base = {
120            // SAFETY: At this time we have a non-BSP `CpuId`, which means that
121            // `init_cpu_nums` must have been called, so `copy_bsp_for_ap` must
122            // also have been called (see the implementation of `cpu::init_on_bsp`),
123            // so `CPU_LOCAL_STORAGES` must already be initialized.
124            let storages = unsafe { super::CPU_LOCAL_STORAGES.get_unchecked() };
125            // SAFETY: `cpu_id` is guaranteed to be in range because the type
126            // invariant of `CpuId`.
127            let storage = unsafe { *storages.get_unchecked(cpu_id - 1) };
128            crate::mm::paddr_to_vaddr(storage)
129        };
130
131        let offset = self.get_offset();
132        (base + offset) as *const T
133    }
134
135    fn get_mut_ptr_on_target(&mut self, _: CpuId) -> *mut T {
136        // `StaticStorage<T>` does not support `get_mut_ptr_on_target`, because
137        // statically-allocated CPU-local objects do not require per-CPU initialization.
138        panic!("Can't get the mutable pointer of StaticStorage<T> on a target CPU.");
139    }
140}
141
142impl<T: 'static> CpuLocal<T, StaticStorage<T>> {
143    /// Creates a new statically-allocated CPU-local object.
144    ///
145    /// Please do not call this function directly. Instead, use the
146    /// `cpu_local!` macro.
147    ///
148    /// # Safety
149    ///
150    /// The caller should ensure that the object initialized by this
151    /// function resides in the `.cpu_local` section. Otherwise the
152    /// behavior is undefined.
153    #[doc(hidden)]
154    pub const unsafe fn __new_static(val: T) -> Self {
155        Self {
156            storage: StaticStorage(val),
157            phantom: PhantomData,
158        }
159    }
160
161    /// Gets access to the underlying value through a raw pointer.
162    ///
163    /// This method is safe, but using the returned pointer will be unsafe.
164    pub(crate) fn as_ptr(&self) -> *const T {
165        self.storage.as_ptr()
166    }
167}