Skip to main content

frozen_core/memmap/
page_size.rs

1//! Strongly-typed virtual memory page size definitions and platform validation
2
3use crate::{
4    error::{FrozenError, FrozenResult},
5    hints,
6    memmap::err,
7};
8
9/// Strongly-typed virtual memory page size representing hardware MMU translation granules
10///
11/// Across supported operating systems and CPU architectures, base virtual memory page sizes
12/// form a strictly bounded set corresponding to hardware translation granules:
13///
14/// - **4 KiB** (`4,096` bytes): Standard on x86/x86_64, Windows, and default Linux AArch64
15/// - **16 KiB** (`16,384` bytes): Apple Silicon macOS and 16k Linux kernels
16/// - **64 KiB** (`65,536` bytes): Enterprise Linux AArch64 (RHEL, CentOS Stream, Amazon Linux)
17///
18/// ## Example
19///
20/// ```
21/// use frozen_core::memmap::PageSize;
22///
23/// let ps = PageSize::try_from(0x1000).unwrap();
24/// assert_eq!(ps, PageSize::K4);
25/// assert_eq!(ps.as_usize(), 0x1000);
26/// assert_eq!(ps.as_u32(), 0x1000);
27/// assert!(ps.is_power_of_two());
28/// ```
29#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
30#[repr(usize)]
31pub enum PageSize {
32    /// 4 KiB (4,096 bytes) - Standard x86_64, Windows, and default Linux AArch64
33    K4 = 0x1000,
34
35    /// 16 KiB (16,384 bytes) - Native Apple Silicon macOS and 16k Linux kernels
36    K16 = 0x4000,
37
38    /// 64 KiB (65,536 bytes) - Enterprise Linux AArch64 (RHEL, CentOS, Amazon Linux)
39    K64 = 0x10000,
40}
41
42impl PageSize {
43    /// Returns the page size in bytes as `usize`
44    #[inline]
45    pub const fn as_usize(self) -> usize {
46        self as usize
47    }
48
49    /// Returns the page size in bytes as `u32`
50    #[inline]
51    pub const fn as_u32(self) -> u32 {
52        self as usize as u32
53    }
54
55    /// Returns `true` since all valid hardware page sizes are powers of two
56    #[inline]
57    pub const fn is_power_of_two(self) -> bool {
58        true
59    }
60
61    /// Validates whether this page size is supported on the active host operating system and
62    /// architecture
63    ///
64    /// ## Platform rules
65    ///
66    /// - **Windows**: Only `PageSize::K4` (4096 bytes) is valid
67    /// - **macOS**: `PageSize::K16` (16384 bytes) on Apple Silicon (`aarch64`);
68    ///   `PageSize::K4` (4096 bytes) on Intel (`x86_64`)
69    /// - **Linux**: `PageSize::K4` (4096 bytes) on `x86`/`x86_64`;
70    ///   `PageSize::K4`, `PageSize::K16`, or `PageSize::K64` on `aarch64`
71    pub fn validate_platform(self) -> FrozenResult<Self> {
72        Self::validate_target_platform(self, std::env::consts::OS, std::env::consts::ARCH)
73    }
74
75    pub(crate) fn validate_target_platform(self, os: &str, arch: &str) -> FrozenResult<Self> {
76        let valid = match (os, arch) {
77            ("windows", _) => self == Self::K4,
78            ("macos", "aarch64") => self == Self::K16,
79            ("macos", "x86_64") => self == Self::K4,
80            ("linux", "x86" | "x86_64") => self == Self::K4,
81            ("linux", "aarch64") => matches!(self, Self::K4 | Self::K16 | Self::K64),
82            _ => false,
83        };
84
85        if hints::likely(valid) {
86            Ok(self)
87        } else {
88            err::raw_error(
89                err::INV,
90                format!(
91                    "unsupported page size {} for target platform {os}/{arch}",
92                    self.as_usize()
93                ),
94            )
95        }
96    }
97}
98
99impl From<PageSize> for usize {
100    #[inline]
101    fn from(ps: PageSize) -> Self {
102        ps as usize
103    }
104}
105
106impl From<PageSize> for u32 {
107    #[inline]
108    fn from(ps: PageSize) -> Self {
109        ps as usize as u32
110    }
111}
112
113impl From<PageSize> for u64 {
114    #[inline]
115    fn from(ps: PageSize) -> Self {
116        ps as usize as u64
117    }
118}
119
120impl core::fmt::Display for PageSize {
121    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
122        write!(f, "{}", self.as_usize())
123    }
124}
125
126impl TryFrom<usize> for PageSize {
127    type Error = FrozenError;
128
129    fn try_from(size: usize) -> Result<Self, Self::Error> {
130        match size {
131            0x1000 => Ok(Self::K4),
132            0x4000 => Ok(Self::K16),
133            0x10000 => Ok(Self::K64),
134            other => err::raw_error(err::INV, format!("unsupported page size: {other}")),
135        }
136    }
137}
138
139#[cfg(test)]
140mod tests {
141    use super::*;
142
143    #[test]
144    fn ok_page_size_conversions() {
145        assert_eq!(PageSize::K4.as_usize(), 0x1000);
146        assert_eq!(PageSize::K16.as_usize(), 0x4000);
147        assert_eq!(PageSize::K64.as_usize(), 0x10000);
148
149        assert_eq!(PageSize::K4.as_u32(), 0x1000);
150        assert_eq!(PageSize::K16.as_u32(), 0x4000);
151        assert_eq!(PageSize::K64.as_u32(), 0x10000);
152
153        assert_eq!(usize::from(PageSize::K4), 0x1000);
154        assert_eq!(u32::from(PageSize::K16), 0x4000);
155        assert_eq!(u64::from(PageSize::K64), 0x10000);
156
157        assert_eq!(PageSize::try_from(0x1000).unwrap(), PageSize::K4);
158        assert_eq!(PageSize::try_from(0x4000).unwrap(), PageSize::K16);
159        assert_eq!(PageSize::try_from(0x10000).unwrap(), PageSize::K64);
160
161        assert_eq!(format!("{}", PageSize::K4), "4096");
162        assert_eq!(format!("{}", PageSize::K16), "16384");
163        assert_eq!(format!("{}", PageSize::K64), "65536");
164
165        assert!(PageSize::K4.is_power_of_two());
166        assert!(PageSize::K16.is_power_of_two());
167        assert!(PageSize::K64.is_power_of_two());
168    }
169
170    #[test]
171    fn err_page_size_invalid_values() {
172        assert!(PageSize::try_from(0x0).is_err());
173        assert!(PageSize::try_from(0x400).is_err());
174        assert!(PageSize::try_from(0x800).is_err());
175        assert!(PageSize::try_from(0x1388).is_err());
176        assert!(PageSize::try_from(0x2000).is_err());
177        assert!(PageSize::try_from(0x8000).is_err());
178        assert!(PageSize::try_from(0x20000).is_err());
179    }
180
181    #[test]
182    fn ok_platform_validation_matrix() {
183        // Windows allows only K4
184        assert!(PageSize::K4.validate_target_platform("windows", "x86_64").is_ok());
185        assert!(PageSize::K4.validate_target_platform("windows", "aarch64").is_ok());
186        assert!(PageSize::K16.validate_target_platform("windows", "x86_64").is_err());
187        assert!(PageSize::K64.validate_target_platform("windows", "x86_64").is_err());
188
189        // macOS Apple Silicon (aarch64) allows only K16
190        assert!(PageSize::K16.validate_target_platform("macos", "aarch64").is_ok());
191        assert!(PageSize::K4.validate_target_platform("macos", "aarch64").is_err());
192        assert!(PageSize::K64.validate_target_platform("macos", "aarch64").is_err());
193
194        // macOS Intel (x86_64) allows only K4
195        assert!(PageSize::K4.validate_target_platform("macos", "x86_64").is_ok());
196        assert!(PageSize::K16.validate_target_platform("macos", "x86_64").is_err());
197        assert!(PageSize::K64.validate_target_platform("macos", "x86_64").is_err());
198
199        // Linux x86 / x86_64 allows only K4
200        assert!(PageSize::K4.validate_target_platform("linux", "x86_64").is_ok());
201        assert!(PageSize::K4.validate_target_platform("linux", "x86").is_ok());
202        assert!(PageSize::K16.validate_target_platform("linux", "x86_64").is_err());
203        assert!(PageSize::K64.validate_target_platform("linux", "x86_64").is_err());
204
205        // Linux aarch64 allows K4, K16, K64
206        assert!(PageSize::K4.validate_target_platform("linux", "aarch64").is_ok());
207        assert!(PageSize::K16.validate_target_platform("linux", "aarch64").is_ok());
208        assert!(PageSize::K64.validate_target_platform("linux", "aarch64").is_ok());
209
210        // Unsupported platform
211        assert!(PageSize::K4.validate_target_platform("freebsd", "x86_64").is_err());
212        assert!(PageSize::K4.validate_target_platform("unknown", "unknown").is_err());
213    }
214
215    #[test]
216    fn ok_current_platform_validation() {
217        #[cfg(all(target_os = "linux", any(target_arch = "x86", target_arch = "x86_64")))]
218        assert!(PageSize::K4.validate_platform().is_ok());
219
220        #[cfg(all(target_os = "linux", target_arch = "aarch64"))]
221        {
222            assert!(PageSize::K4.validate_platform().is_ok());
223            assert!(PageSize::K16.validate_platform().is_ok());
224            assert!(PageSize::K64.validate_platform().is_ok());
225        }
226
227        #[cfg(all(target_os = "macos", target_arch = "aarch64"))]
228        {
229            assert!(PageSize::K16.validate_platform().is_ok());
230            assert!(PageSize::K4.validate_platform().is_err());
231        }
232
233        #[cfg(all(target_os = "macos", target_arch = "x86_64"))]
234        {
235            assert!(PageSize::K4.validate_platform().is_ok());
236            assert!(PageSize::K16.validate_platform().is_err());
237        }
238
239        #[cfg(target_os = "windows")]
240        {
241            assert!(PageSize::K4.validate_platform().is_ok());
242            assert!(PageSize::K16.validate_platform().is_err());
243        }
244    }
245}