use core::{marker::Sync, ops::Deref};
use super::{__cpu_local_end, __cpu_local_start};
use crate::{arch, trap::DisabledLocalIrqGuard};
#[macro_export]
macro_rules! cpu_local {
($( $(#[$attr:meta])* $vis:vis static $name:ident: $t:ty = $init:expr; )*) => {
$(
#[link_section = ".cpu_local"]
$(#[$attr])* $vis static $name: $crate::cpu::local::CpuLocal<$t> = {
let val = $init;
// SAFETY: The per-CPU variable instantiated is statically
// stored in the special `.cpu_local` section.
unsafe {
$crate::cpu::local::CpuLocal::__new(val)
}
};
)*
};
}
pub struct CpuLocal<T: 'static>(T);
impl<T: 'static> CpuLocal<T> {
#[doc(hidden)]
pub const unsafe fn __new(val: T) -> Self {
Self(val)
}
pub fn get_with<'a>(
&'static self,
guard: &'a DisabledLocalIrqGuard,
) -> CpuLocalDerefGuard<'a, T> {
CpuLocalDerefGuard {
cpu_local: self,
guard,
}
}
unsafe fn as_ptr(&'static self) -> *const T {
super::has_init::assert_true();
let offset = self.get_offset();
let local_base = arch::cpu::local::get_base() as usize;
let local_va = local_base + offset;
debug_assert_eq!(local_va % core::mem::align_of::<T>(), 0);
local_va as *mut T
}
fn get_offset(&'static self) -> usize {
let bsp_va = self as *const _ as usize;
let bsp_base = __cpu_local_start as usize;
debug_assert!(bsp_va + core::mem::size_of::<T>() <= __cpu_local_end as usize);
bsp_va - bsp_base
}
}
impl<T: 'static + Sync> CpuLocal<T> {
pub fn get_on_cpu(&'static self, cpu_id: u32) -> &'static T {
super::has_init::assert_true();
if cpu_id == 0 {
return &self.0;
}
let base = unsafe {
super::CPU_LOCAL_STORAGES
.get_unchecked()
.get(cpu_id as usize - 1)
.unwrap()
.start_paddr()
};
let base = crate::mm::paddr_to_vaddr(base);
let offset = self.get_offset();
let ptr = (base + offset) as *const T;
unsafe { &*ptr }
}
}
unsafe impl<T: 'static> Sync for CpuLocal<T> {}
impl<T: 'static> !Copy for CpuLocal<T> {}
impl<T: 'static> !Clone for CpuLocal<T> {}
impl<T: 'static> !Send for CpuLocal<T> {}
#[must_use]
pub struct CpuLocalDerefGuard<'a, T: 'static> {
cpu_local: &'static CpuLocal<T>,
#[allow(dead_code)]
guard: &'a DisabledLocalIrqGuard,
}
impl<T: 'static> Deref for CpuLocalDerefGuard<'_, T> {
type Target = T;
fn deref(&self) -> &Self::Target {
unsafe { &*self.cpu_local.as_ptr() }
}
}