ax_percpu/
initialization.rs1use 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
22pub 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 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 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 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}