Skip to main content

ax_percpu/
initialization.rs

1//! One-shot construction of typed values at their final runtime addresses.
2
3use core::{
4    mem::size_of,
5    sync::atomic::{AtomicU8, Ordering, compiler_fence},
6};
7
8use cpu_local::{CpuAreaPrefix, CpuIndex};
9
10use crate::{
11    PerCpuArea, PerCpuError, PerCpuLayout, PerCpuRegion,
12    descriptor::{PerCpuInitRecord, PerCpuInitRegistration, init_registrations},
13    layout::freeze_initialized_layout,
14};
15
16const UNINITIALIZED: u8 = 0;
17const INITIALIZING: u8 = 1;
18const INITIALIZED: u8 = 2;
19
20static INITIALIZATION_STATE: AtomicU8 = AtomicU8::new(UNINITIALIZED);
21
22/// Validates and constructs all values in raw runtime areas exactly once.
23///
24/// Every descriptor and overlap is checked before the first destination write.
25/// The layout becomes globally visible only after all prefixes and objects are
26/// fully initialized.
27///
28/// # Safety
29///
30/// `region` must name exclusively owned, writable, correctly aligned storage
31/// that remains mapped until shutdown. No CPU may bind or access an area until
32/// this function succeeds, and the bytes must not contain live Rust values.
33pub unsafe fn initialize_layout(
34    region: PerCpuRegion,
35) -> Result<&'static PerCpuLayout, PerCpuError> {
36    let candidate = PerCpuLayout::validate(region)?;
37    begin_initialization()?;
38
39    let registrations = match init_registrations() {
40        Ok(registrations) => registrations,
41        Err(error) => return reset_initialization(error),
42    };
43    if let Err(error) = validate_prefixes(&candidate)
44        .and_then(|()| validate_init_records(registrations, &candidate))
45    {
46        return reset_initialization(error);
47    }
48
49    for cpu_raw in 0..candidate.area_count() {
50        let cpu_index = CpuIndex::from_u32(cpu_raw)
51            .expect("nonzero u32 area count must retain representable indices");
52        let area = candidate
53            .area(cpu_index)
54            .expect("validated layout area must remain addressable");
55        // SAFETY: caller ownership and complete preflight validation cover this
56        // unique destination area. Each area is visited exactly once.
57        unsafe { initialize_area(area, candidate.template_base(), registrations) };
58    }
59
60    compiler_fence(Ordering::Release);
61    let installed = freeze_initialized_layout(candidate);
62    INITIALIZATION_STATE.store(INITIALIZED, Ordering::Release);
63    Ok(installed)
64}
65
66fn reset_initialization<T>(error: PerCpuError) -> Result<T, PerCpuError> {
67    INITIALIZATION_STATE.store(UNINITIALIZED, Ordering::Release);
68    Err(error)
69}
70
71fn begin_initialization() -> Result<(), PerCpuError> {
72    match INITIALIZATION_STATE.compare_exchange(
73        UNINITIALIZED,
74        INITIALIZING,
75        Ordering::AcqRel,
76        Ordering::Acquire,
77    ) {
78        Ok(_) => Ok(()),
79        Err(INITIALIZING) => Err(PerCpuError::LayoutInitializationInProgress),
80        Err(INITIALIZED) => Err(PerCpuError::LayoutAlreadyInitialized),
81        Err(_) => unreachable!("per-CPU initialization state must remain valid"),
82    }
83}
84
85fn validate_prefixes(layout: &PerCpuLayout) -> Result<(), PerCpuError> {
86    for cpu_raw in 0..layout.area_count() {
87        let cpu_index = CpuIndex::from_u32(cpu_raw)
88            .expect("validated area count must retain representable indices");
89        let area = layout.area(cpu_index)?;
90        CpuAreaPrefix::initialize(cpu_index, area.runtime_base())?;
91    }
92    Ok(())
93}
94
95fn validate_init_records(
96    registrations: &[PerCpuInitRegistration],
97    layout: &PerCpuLayout,
98) -> Result<(), PerCpuError> {
99    for (index, registration) in registrations.iter().copied().enumerate() {
100        let record = registration.record(index, layout.template_base())?;
101        validate_init_record(index, record, layout)?;
102        for (relative, other) in registrations[index + 1..].iter().copied().enumerate() {
103            let other_index = index + 1 + relative;
104            let other_record = other.record(other_index, layout.template_base())?;
105            if record.overlaps(other_record)? {
106                return Err(PerCpuError::OverlappingInitRecords {
107                    first_offset: record.offset,
108                    second_offset: other_record.offset,
109                });
110            }
111        }
112    }
113    Ok(())
114}
115
116fn validate_init_record(
117    index: usize,
118    record: PerCpuInitRecord,
119    layout: &PerCpuLayout,
120) -> Result<(), PerCpuError> {
121    let end = record.end()?;
122    if record.alignment == 0
123        || !record.alignment.is_power_of_two()
124        || record.offset < size_of::<CpuAreaPrefix>()
125        || end > layout.area_size()
126        || !record.offset.is_multiple_of(record.alignment)
127        || record.alignment > layout.required_alignment()
128    {
129        return Err(PerCpuError::MalformedInitRecord {
130            index,
131            offset: record.offset,
132            size: record.size,
133            alignment: record.alignment,
134        });
135    }
136    Ok(())
137}
138
139unsafe fn initialize_area(
140    area: PerCpuArea,
141    template_base: usize,
142    registrations: &[PerCpuInitRegistration],
143) {
144    let prefix = CpuAreaPrefix::initialize(area.cpu_index(), area.runtime_base())
145        .expect("preflighted prefix facts must remain valid");
146    // SAFETY: caller owns this raw area and preflight reserves the prefix.
147    unsafe { area.prefix_ptr().write(prefix) };
148    for (index, registration) in registrations.iter().copied().enumerate() {
149        let record = registration
150            .record(index, template_base)
151            .expect("validated descriptor must remain stable during initialization");
152        // SAFETY: every non-overlapping record was validated before any write.
153        let destination = unsafe { area.runtime_ptr().add(record.offset) };
154        unsafe { (record.initialize)(destination) };
155    }
156}
157
158#[cfg(test)]
159mod tests {
160    use super::*;
161    use crate::descriptor::PerCpuInitRecord;
162
163    unsafe extern "C" fn initialize_nothing(_destination: *mut u8) {}
164
165    #[test]
166    fn overlap_check_handles_empty_and_wrapping_records() {
167        let record = |offset, size| PerCpuInitRecord {
168            offset,
169            size,
170            alignment: 8,
171            initialize: initialize_nothing,
172        };
173        assert_eq!(record(0x100, 0x20).overlaps(record(0x110, 8)), Ok(true));
174        assert_eq!(record(0x100, 0x20).overlaps(record(0x110, 0)), Ok(false));
175        assert_eq!(
176            record(usize::MAX - 3, 8).end(),
177            Err(PerCpuError::AddressOverflow)
178        );
179    }
180}