use core::mem::{align_of, size_of};
use crate::PerCpuError;
pub(crate) type PerCpuInitializer = unsafe extern "C" fn(*mut u8);
type PerCpuDescriptorThunk = unsafe extern "C" fn() -> PerCpuInitDescriptor;
#[derive(Clone, Copy, Debug)]
#[repr(C)]
pub struct PerCpuInitDescriptor {
storage_address: usize,
size: usize,
alignment: usize,
initialize: PerCpuInitializer,
}
impl PerCpuInitDescriptor {
#[doc(hidden)]
pub const unsafe fn new(
storage_address: usize,
size: usize,
alignment: usize,
initialize: PerCpuInitializer,
) -> Self {
Self {
storage_address,
size,
alignment,
initialize,
}
}
fn resolve(self, index: usize, template_base: usize) -> Result<PerCpuInitRecord, PerCpuError> {
let offset = self.storage_address.checked_sub(template_base).ok_or(
PerCpuError::MalformedInitRecord {
index,
offset: self.storage_address,
size: self.size,
alignment: self.alignment,
},
)?;
Ok(PerCpuInitRecord {
offset,
size: self.size,
alignment: self.alignment,
initialize: self.initialize,
})
}
}
#[derive(Clone, Copy, Debug)]
pub(crate) struct PerCpuInitRecord {
pub(crate) offset: usize,
pub(crate) size: usize,
pub(crate) alignment: usize,
pub(crate) initialize: PerCpuInitializer,
}
impl PerCpuInitRecord {
pub(crate) fn end(self) -> Result<usize, PerCpuError> {
self.offset
.checked_add(self.size)
.ok_or(PerCpuError::AddressOverflow)
}
pub(crate) fn overlaps(self, other: Self) -> Result<bool, PerCpuError> {
if self.size == 0 || other.size == 0 {
return Ok(false);
}
Ok(self.offset < other.end()? && other.offset < self.end()?)
}
}
#[derive(Clone, Copy)]
#[repr(transparent)]
pub struct PerCpuInitRegistration {
describe: PerCpuDescriptorThunk,
}
impl PerCpuInitRegistration {
#[doc(hidden)]
pub const unsafe fn new(describe: PerCpuDescriptorThunk) -> Self {
Self { describe }
}
pub(crate) fn record(
self,
index: usize,
template_base: usize,
) -> Result<PerCpuInitRecord, PerCpuError> {
unsafe { (self.describe)() }.resolve(index, template_base)
}
}
#[cfg(not(target_os = "macos"))]
pub(crate) fn init_registrations() -> Result<&'static [PerCpuInitRegistration], PerCpuError> {
unsafe extern "C" {
static __PERCPU_INIT_START: PerCpuInitRegistration;
static __PERCPU_INIT_END: PerCpuInitRegistration;
}
let start = core::ptr::addr_of!(__PERCPU_INIT_START) as usize;
let end = core::ptr::addr_of!(__PERCPU_INIT_END) as usize;
let byte_len = end
.checked_sub(start)
.ok_or(PerCpuError::MalformedInitTable { start, end })?;
if !start.is_multiple_of(align_of::<PerCpuInitRegistration>())
|| !byte_len.is_multiple_of(size_of::<PerCpuInitRegistration>())
{
return Err(PerCpuError::MalformedInitTable { start, end });
}
Ok(unsafe {
core::slice::from_raw_parts(
start as *const PerCpuInitRegistration,
byte_len / size_of::<PerCpuInitRegistration>(),
)
})
}
#[cfg(target_os = "macos")]
pub(crate) fn init_registrations() -> Result<&'static [PerCpuInitRegistration], PerCpuError> {
Err(PerCpuError::InitializerTableUnavailable)
}