1use core::mem::size_of;
2
3use cpu_local::{CpuAreaPrefix, CpuIndex};
4
5use crate::{PerCpuArea, PerCpuError, PerCpuRegion};
6
7static INSTALLED_LAYOUT: ax_lazyinit::OnceLock<PerCpuLayout> = ax_lazyinit::OnceLock::new();
8
9#[derive(Clone, Copy, Debug, Eq, PartialEq)]
11pub struct PerCpuLayout {
12 region: PerCpuRegion,
13 template_base: usize,
14 area_size: usize,
15 required_alignment: usize,
16}
17
18unsafe impl Send for PerCpuLayout {}
21unsafe impl Sync for PerCpuLayout {}
23
24impl PerCpuLayout {
25 pub(crate) fn validate(region: PerCpuRegion) -> Result<Self, PerCpuError> {
26 let area_size = crate::template_size();
27 let required_alignment = crate::required_area_alignment()?;
28 if area_size < size_of::<CpuAreaPrefix>() {
29 return Err(PerCpuError::AreaTooSmall {
30 actual: area_size,
31 minimum: size_of::<CpuAreaPrefix>(),
32 });
33 }
34 let runtime_base = region.runtime_base();
35 if !runtime_base.is_multiple_of(required_alignment) {
36 return Err(PerCpuError::MisalignedRuntimeBase {
37 base: runtime_base,
38 alignment: required_alignment,
39 });
40 }
41 if region.area_stride() < area_size {
42 return Err(PerCpuError::StrideTooSmall {
43 stride: region.area_stride(),
44 area_size,
45 });
46 }
47 if !region.area_stride().is_multiple_of(required_alignment) {
48 return Err(PerCpuError::MisalignedStride {
49 stride: region.area_stride(),
50 alignment: required_alignment,
51 });
52 }
53 let template_base = crate::template_base();
54 if !template_base.is_multiple_of(required_alignment) {
55 return Err(PerCpuError::MisalignedTemplateBase {
56 base: template_base,
57 alignment: required_alignment,
58 });
59 }
60 let prefix_address = cpu_local::cpu_area_template_base();
61 if template_base != prefix_address {
62 return Err(PerCpuError::PrefixPlacement {
63 template_base,
64 prefix_address,
65 });
66 }
67 let last_index = region.area_count().get() as usize - 1;
68 let last_offset = region
69 .area_stride()
70 .checked_mul(last_index)
71 .ok_or(PerCpuError::AddressOverflow)?;
72 runtime_base
73 .checked_add(last_offset)
74 .and_then(|base| base.checked_add(area_size))
75 .ok_or(PerCpuError::AddressOverflow)?;
76 Ok(Self {
77 region,
78 template_base,
79 area_size,
80 required_alignment,
81 })
82 }
83
84 pub const fn region(&self) -> PerCpuRegion {
86 self.region
87 }
88
89 pub fn runtime_base(&self) -> usize {
91 self.region.runtime_base()
92 }
93
94 pub const fn area_stride(&self) -> usize {
96 self.region.area_stride()
97 }
98
99 pub const fn area_count(&self) -> u32 {
101 self.region.area_count().get()
102 }
103
104 pub const fn area_size(&self) -> usize {
106 self.area_size
107 }
108
109 pub fn area(&self, cpu_index: CpuIndex) -> Result<PerCpuArea, PerCpuError> {
111 if cpu_index.as_u32() >= self.area_count() {
112 return Err(PerCpuError::CpuOutOfRange {
113 cpu_index,
114 area_count: self.area_count(),
115 });
116 }
117 let offset = self
118 .area_stride()
119 .checked_mul(cpu_index.as_usize())
120 .ok_or(PerCpuError::AddressOverflow)?;
121 let runtime_base = self
122 .runtime_base()
123 .checked_add(offset)
124 .ok_or(PerCpuError::AddressOverflow)?;
125 Ok(PerCpuArea::new(cpu_index, runtime_base, self.area_size))
126 }
127
128 pub(crate) const fn template_base(&self) -> usize {
129 self.template_base
130 }
131
132 pub(crate) const fn required_alignment(&self) -> usize {
133 self.required_alignment
134 }
135}
136
137pub(crate) fn installed_layout() -> Result<&'static PerCpuLayout, PerCpuError> {
138 INSTALLED_LAYOUT
139 .get()
140 .ok_or(PerCpuError::LayoutNotInstalled)
141}
142
143pub(crate) fn freeze_initialized_layout(layout: PerCpuLayout) -> &'static PerCpuLayout {
144 assert!(
145 INSTALLED_LAYOUT.get().is_none(),
146 "per-CPU layout publication must occur exactly once after initialization"
147 );
148 INSTALLED_LAYOUT.call_once(|| layout)
149}