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