1use synth_core::{Error, Result};
4
5#[derive(Debug, Clone, Copy, PartialEq, Eq)]
10pub enum MPUSize {
11 Size32B = 5,
13 Size64B = 6,
15 Size128B = 7,
17 Size256B = 8,
19 Size512B = 9,
21 Size1KB = 10,
23 Size2KB = 11,
25 Size4KB = 12,
27 Size8KB = 13,
29 Size16KB = 14,
31 Size32KB = 15,
33 Size64KB = 16,
35 Size128KB = 17,
37 Size256KB = 18,
39 Size512KB = 19,
41 Size1MB = 20,
43 Size2MB = 21,
45 Size4MB = 22,
47 Size8MB = 23,
49 Size16MB = 24,
51 Size32MB = 25,
53 Size64MB = 26,
55 Size128MB = 27,
57 Size256MB = 28,
59 Size512MB = 29,
61 Size1GB = 30,
63 Size2GB = 31,
65 Size4GB = 32,
67}
68
69impl MPUSize {
70 pub fn bytes(&self) -> u64 {
72 1u64 << (*self as u8)
73 }
74
75 pub fn from_bytes(bytes: u64) -> Result<Self> {
77 if bytes < 32 {
78 return Err(Error::HardwareProtectionError(
79 "MPU minimum region size is 32 bytes".to_string(),
80 ));
81 }
82 if bytes > (1u64 << 32) {
83 return Err(Error::HardwareProtectionError(
84 "MPU maximum region size is 4GB".to_string(),
85 ));
86 }
87
88 let bit_pos = 64 - bytes.leading_zeros() - 1;
90 let power = if bytes == (1u64 << bit_pos) {
91 bit_pos
92 } else {
93 bit_pos + 1
94 };
95
96 match power {
97 5 => Ok(MPUSize::Size32B),
98 6 => Ok(MPUSize::Size64B),
99 7 => Ok(MPUSize::Size128B),
100 8 => Ok(MPUSize::Size256B),
101 9 => Ok(MPUSize::Size512B),
102 10 => Ok(MPUSize::Size1KB),
103 11 => Ok(MPUSize::Size2KB),
104 12 => Ok(MPUSize::Size4KB),
105 13 => Ok(MPUSize::Size8KB),
106 14 => Ok(MPUSize::Size16KB),
107 15 => Ok(MPUSize::Size32KB),
108 16 => Ok(MPUSize::Size64KB),
109 17 => Ok(MPUSize::Size128KB),
110 18 => Ok(MPUSize::Size256KB),
111 19 => Ok(MPUSize::Size512KB),
112 20 => Ok(MPUSize::Size1MB),
113 21 => Ok(MPUSize::Size2MB),
114 22 => Ok(MPUSize::Size4MB),
115 23 => Ok(MPUSize::Size8MB),
116 24 => Ok(MPUSize::Size16MB),
117 25 => Ok(MPUSize::Size32MB),
118 26 => Ok(MPUSize::Size64MB),
119 27 => Ok(MPUSize::Size128MB),
120 28 => Ok(MPUSize::Size256MB),
121 29 => Ok(MPUSize::Size512MB),
122 30 => Ok(MPUSize::Size1GB),
123 31 => Ok(MPUSize::Size2GB),
124 32 => Ok(MPUSize::Size4GB),
125 _ => unreachable!(),
126 }
127 }
128}
129
130#[derive(Debug, Clone, Copy, PartialEq, Eq)]
132pub enum MPUPermissions {
133 NoAccess = 0,
135 PrivilegedRW = 1,
137 PrivilegedRWUserRO = 2,
139 FullRW = 3,
141 PrivilegedRO = 5,
143 FullRO = 6,
145}
146
147#[derive(Debug, Clone, Copy, PartialEq, Eq)]
149pub struct MPUAttributes {
150 pub shareable: bool,
152 pub cacheable: bool,
154 pub bufferable: bool,
156 pub execute_never: bool,
158}
159
160impl MPUAttributes {
161 pub fn normal() -> Self {
163 Self {
164 shareable: false,
165 cacheable: true,
166 bufferable: true,
167 execute_never: false,
168 }
169 }
170
171 pub fn device() -> Self {
176 Self {
177 shareable: true,
178 cacheable: false,
179 bufferable: true,
180 execute_never: true,
181 }
182 }
183
184 pub fn strongly_ordered() -> Self {
186 Self {
187 shareable: true,
188 cacheable: false,
189 bufferable: false,
190 execute_never: false,
191 }
192 }
193}
194
195#[derive(Debug, Clone)]
197pub struct MPURegion {
198 pub number: u8,
200
201 pub base_address: u32,
203
204 pub size: MPUSize,
206
207 pub permissions: MPUPermissions,
209
210 pub attributes: MPUAttributes,
212
213 pub subregion_disable: u8,
215
216 pub enabled: bool,
218}
219
220impl MPURegion {
221 pub fn new(number: u8, base_address: u32, size: MPUSize) -> Self {
223 Self {
224 number,
225 base_address,
226 size,
227 permissions: MPUPermissions::FullRW,
228 attributes: MPUAttributes::normal(),
229 subregion_disable: 0,
230 enabled: true,
231 }
232 }
233
234 pub fn validate(&self) -> Result<()> {
236 let size_bytes = self.size.bytes();
238 if size_bytes > u32::MAX as u64 {
239 return Err(Error::HardwareProtectionError(
240 "Region size exceeds 32-bit address space".to_string(),
241 ));
242 }
243
244 let alignment = size_bytes as u32;
245 #[allow(clippy::manual_is_multiple_of)]
246 if alignment == 0 || self.base_address % alignment != 0 {
248 return Err(Error::HardwareProtectionError(format!(
249 "Base address 0x{:08X} not aligned to region size {} bytes",
250 self.base_address, size_bytes
251 )));
252 }
253
254 Ok(())
255 }
256
257 pub fn rasr(&self) -> u32 {
259 let mut rasr = 0u32;
260
261 if self.enabled {
263 rasr |= 1 << 0;
264 }
265
266 rasr |= ((self.size as u32) << 1) & 0x3E;
268
269 rasr |= (self.subregion_disable as u32) << 8;
271
272 if self.attributes.bufferable {
274 rasr |= 1 << 16;
275 }
276 if self.attributes.cacheable {
277 rasr |= 1 << 17;
278 }
279 if self.attributes.shareable {
280 rasr |= 1 << 18;
281 }
282
283 rasr |= (self.permissions as u32) << 24;
287
288 if self.attributes.execute_never {
290 rasr |= 1 << 28;
291 }
292
293 rasr
294 }
295
296 pub fn rbar(&self) -> u32 {
298 let mut rbar = self.base_address & 0xFFFFFFE0; rbar |= (self.number as u32) & 0xF; rbar |= 1 << 4; rbar
302 }
303}
304
305#[cfg(test)]
306mod tests {
307 use super::*;
308
309 #[test]
310 fn test_mpu_size_from_bytes() {
311 assert_eq!(MPUSize::from_bytes(32).unwrap(), MPUSize::Size32B);
312 assert_eq!(MPUSize::from_bytes(64).unwrap(), MPUSize::Size64B);
313 assert_eq!(MPUSize::from_bytes(100).unwrap(), MPUSize::Size128B); assert_eq!(MPUSize::from_bytes(1024).unwrap(), MPUSize::Size1KB);
315 assert_eq!(MPUSize::from_bytes(65536).unwrap(), MPUSize::Size64KB);
316 }
317
318 #[test]
319 fn test_mpu_size_bytes() {
320 assert_eq!(MPUSize::Size32B.bytes(), 32);
321 assert_eq!(MPUSize::Size1KB.bytes(), 1024);
322 assert_eq!(MPUSize::Size64KB.bytes(), 65536);
323 assert_eq!(MPUSize::Size1MB.bytes(), 1048576);
324 }
325
326 #[test]
327 fn test_mpu_region_alignment() {
328 let region = MPURegion::new(0, 0x20000000, MPUSize::Size64KB);
329 assert!(region.validate().is_ok());
330
331 let misaligned = MPURegion::new(0, 0x20000100, MPUSize::Size64KB);
332 assert!(misaligned.validate().is_err());
333 }
334
335 #[test]
336 fn test_mpu_rasr_generation() {
337 let region = MPURegion::new(0, 0x20000000, MPUSize::Size64KB);
338 let rasr = region.rasr();
339
340 assert_eq!(rasr & 0x1, 1);
342
343 let size_field = (rasr >> 1) & 0x1F;
345 assert_eq!(size_field, MPUSize::Size64KB as u32);
346 }
347}