use core::{marker::PhantomData, ptr::NonNull};
use crate::{CpuAreaRef, CpuLocalError, register};
#[must_use = "CPU-local access is valid only while this pin remains in scope"]
#[derive(Debug)]
pub struct CpuPin<'scope> {
area: CpuAreaRef,
_scope: PhantomData<&'scope mut &'scope ()>,
_not_send_or_sync: PhantomData<*mut ()>,
}
impl CpuPin<'_> {
pub const fn area(&self) -> CpuAreaRef {
self.area
}
}
#[must_use = "mutable CPU-local access is valid only while this token remains in scope"]
#[derive(Debug)]
pub struct ExclusiveCpu<'pin> {
area: CpuAreaRef,
_scope: PhantomData<&'pin mut &'pin ()>,
_not_send_or_sync: PhantomData<*mut ()>,
}
#[doc(hidden)]
#[must_use = "current CPU-area access is valid only while this token remains in scope"]
#[derive(Debug)]
pub struct CurrentCpuArea<'scope> {
area_base: usize,
_scope: PhantomData<&'scope mut &'scope ()>,
_not_send_or_sync: PhantomData<*mut ()>,
}
impl CurrentCpuArea<'_> {
#[doc(hidden)]
pub unsafe fn symbol_ptr<T>(&self, offset: usize) -> Result<NonNull<T>, CpuLocalError> {
let address = self
.area_base
.checked_add(offset)
.ok_or(CpuLocalError::AddressOverflow)?;
NonNull::new(address as *mut T).ok_or(CpuLocalError::InvalidAreaBase { base: address })
}
}
impl ExclusiveCpu<'_> {
pub const fn area(&self) -> CpuAreaRef {
self.area
}
}
pub unsafe fn with_cpu_pin<R>(
operation: impl for<'scope> FnOnce(&CpuPin<'scope>) -> R,
) -> Result<R, CpuLocalError> {
let area = register::current_area()?;
let pin = CpuPin {
area,
_scope: PhantomData,
_not_send_or_sync: PhantomData,
};
register::current_context(&pin)?;
Ok(operation(&pin))
}
pub unsafe fn with_exclusive_cpu<R>(
pin: &CpuPin<'_>,
operation: impl for<'exclusive> FnOnce(&ExclusiveCpu<'exclusive>) -> R,
) -> R {
let exclusive = ExclusiveCpu {
area: pin.area,
_scope: PhantomData,
_not_send_or_sync: PhantomData,
};
operation(&exclusive)
}
#[doc(hidden)]
pub unsafe fn with_current_cpu_area<R>(
operation: impl for<'scope> FnOnce(&CurrentCpuArea<'scope>) -> R,
) -> Result<R, CpuLocalError> {
let area_base = unsafe { register::current_cpu_area_base()? };
let area = CurrentCpuArea {
area_base,
_scope: PhantomData,
_not_send_or_sync: PhantomData,
};
Ok(operation(&area))
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn current_cpu_area_rejects_a_symbol_address_overflow() {
let area = CurrentCpuArea {
area_base: usize::MAX,
_scope: PhantomData,
_not_send_or_sync: PhantomData,
};
assert_eq!(
unsafe { area.symbol_ptr::<u8>(1) },
Err(CpuLocalError::AddressOverflow),
);
}
}