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}