Skip to main content

aarch32_cpu/
pmsav7.rs

1//! Support for the PMSAv7 MPU
2//!
3//! See Chapter 14: Protected Memory System Architecture in [Arm
4//! Architecture Reference Manual ARMv7-A and ARMv7-R edition][armv7]
5//!
6//! [armv7]: https://developer.arm.com/documentation/ddi0406/latest
7
8use core::marker::PhantomData;
9
10use crate::register;
11
12use arbitrary_int::{u2, u3};
13
14#[doc(inline)]
15pub use register::drsr::RegionSize;
16
17/// Ways this API can fail
18#[derive(Debug, Clone, PartialEq, Eq)]
19#[cfg_attr(feature = "defmt", derive(defmt::Format))]
20pub enum Error {
21    /// Found too many regions
22    TooManyRegions,
23    /// Found a region with invalid alignment
24    UnalignedRegion(*mut u8),
25}
26
27/// Represents our PMSAv7 MPU
28///
29/// This type is not [Send] because it is a per-core type and should not be moved across
30/// cores on an SMP system.
31pub struct Mpu {
32    _phantom: PhantomData<*const u8>,
33}
34
35impl Mpu {
36    /// Create an MPU handle
37    ///
38    /// # Safety
39    ///
40    /// Only create one of these at any given time, as they access shared
41    /// mutable state within the processor and do read-modify-writes on that state.
42    pub unsafe fn new() -> Mpu {
43        Mpu {
44            _phantom: PhantomData,
45        }
46    }
47
48    /// How many MPU instruction regions are there?
49    pub fn num_iregions(&self) -> u8 {
50        register::Mpuir::read().iregions()
51    }
52
53    /// How many MPU data/unified regions are there?
54    pub fn num_dregions(&self) -> u8 {
55        register::Mpuir::read().dregions()
56    }
57
58    /// Do we have a unified MPU?
59    pub fn is_unified(&self) -> bool {
60        !register::Mpuir::read().non_unified()
61    }
62
63    /// Get an instruction region
64    pub fn get_iregion(&mut self, idx: u8) -> Option<Region> {
65        if idx >= self.num_iregions() {
66            return None;
67        }
68        register::Rgnr::write(register::Rgnr(idx as u32));
69        let base = register::Irbar::read().0 as *mut u8;
70        let rsr = register::Irsr::read();
71        let racr = register::Iracr::read();
72
73        let mem_attr_bits = MemAttrBits {
74            tex: racr.tex(),
75            c: racr.c(),
76            b: racr.b(),
77            s: racr.s(),
78        };
79
80        let mem_attr = mem_attr_bits.decode()?;
81        let access_perms = AccessPerms::from_bits(racr.ap())?;
82
83        Some(Region {
84            base,
85            size: rsr.region_size(),
86            subregion_mask: rsr.subregion_mask(),
87            enabled: rsr.enabled(),
88            no_exec: racr.nx(),
89            mem_attr,
90            access_perms,
91        })
92    }
93
94    /// Get a data/unified region
95    pub fn get_dregion(&mut self, idx: u8) -> Option<Region> {
96        if idx >= self.num_dregions() {
97            return None;
98        }
99        register::Rgnr::write(register::Rgnr(idx as u32));
100        let base = register::Drbar::read().0 as *mut u8;
101        let rsr = register::Drsr::read();
102        let racr = register::Dracr::read();
103
104        let mem_attr_bits = MemAttrBits {
105            tex: racr.tex(),
106            c: racr.c(),
107            b: racr.b(),
108            s: racr.s(),
109        };
110        let mem_attr = mem_attr_bits.decode()?;
111        let access_perms = AccessPerms::from_bits(racr.ap())?;
112
113        Some(Region {
114            base,
115            size: rsr.region_size(),
116            subregion_mask: rsr.subregion_mask(),
117            enabled: rsr.enabled(),
118            no_exec: racr.nx(),
119            mem_attr,
120            access_perms,
121        })
122    }
123
124    /// Configure the EL1 MPU
125    pub fn configure(&mut self, config: &Config) -> Result<(), Error> {
126        if config.iregions.len() > self.num_iregions() as usize {
127            return Err(Error::TooManyRegions);
128        }
129        if config.dregions.len() > self.num_dregions() as usize {
130            return Err(Error::TooManyRegions);
131        }
132        for (idx, region) in config.iregions.iter().enumerate() {
133            register::Rgnr::write(register::Rgnr(idx as u32));
134            if !region.size.is_aligned(region.base) {
135                return Err(Error::UnalignedRegion(region.base));
136            }
137            register::Irbar::write(register::Irbar(region.base as u32));
138            register::Irsr::write({
139                let mut out = register::Irsr::new_with_raw_value(0);
140                out.set_enabled(region.enabled);
141                out.set_region_size(region.size);
142                out.set_subregion_mask(region.subregion_mask);
143                out
144            });
145            register::Iracr::write({
146                let mut out = register::Iracr::new_with_raw_value(0);
147                let mem_attr_bits = region.mem_attr.to_bits();
148                out.set_tex(mem_attr_bits.tex);
149                out.set_c(mem_attr_bits.c);
150                out.set_b(mem_attr_bits.b);
151                out.set_s(mem_attr_bits.s);
152                out.set_nx(region.no_exec);
153                out.set_ap(region.access_perms.to_bits());
154                out
155            });
156        }
157        for (idx, region) in config.dregions.iter().enumerate() {
158            if !region.size.is_aligned(region.base) {
159                return Err(Error::UnalignedRegion(region.base));
160            }
161            register::Rgnr::write(register::Rgnr(idx as u32));
162            register::Drbar::write(register::Drbar(region.base as u32));
163            register::Drsr::write({
164                let mut out = register::Drsr::new_with_raw_value(0);
165                out.set_enabled(region.enabled);
166                out.set_region_size(region.size);
167                out.set_subregion_mask(region.subregion_mask);
168                out
169            });
170            register::Dracr::write({
171                let mut out = register::Dracr::new_with_raw_value(0);
172                let mem_attr_bits = region.mem_attr.to_bits();
173                out.set_tex(mem_attr_bits.tex);
174                out.set_c(mem_attr_bits.c);
175                out.set_b(mem_attr_bits.b);
176                out.set_s(mem_attr_bits.s);
177                out.set_nx(region.no_exec);
178                out.set_ap(region.access_perms.to_bits());
179                out
180            });
181        }
182        register::Sctlr::modify(|r| {
183            r.set_br(config.background_config);
184        });
185        Ok(())
186    }
187
188    /// Enable the MPU
189    pub fn enable(&mut self) {
190        register::Sctlr::modify(|r| {
191            r.set_m(true);
192        });
193    }
194
195    /// Disable the MPU
196    pub fn disable(&mut self) {
197        register::Sctlr::modify(|r| {
198            r.set_m(false);
199        });
200    }
201}
202
203/// Configuration for the PMSAv7 MPU
204#[derive(Clone, Debug, PartialEq, Eq)]
205#[cfg_attr(feature = "defmt", derive(defmt::Format))]
206pub struct Config<'a> {
207    /// Background Config Enable
208    ///
209    /// If true, use the default MMU config if no other region matches an address
210    pub background_config: bool,
211    /// List of instruction regions
212    pub iregions: &'a [Region],
213    /// List of data/unified regions
214    pub dregions: &'a [Region],
215}
216
217/// Configuration for a region in the PMSAv7 MPU
218#[derive(Clone, Debug, PartialEq, Eq)]
219#[cfg_attr(feature = "defmt", derive(defmt::Format))]
220pub struct Region {
221    /// The base address of this region.
222    ///
223    /// Must be aligned to the size of the region.
224    pub base: *mut u8,
225    /// The size of this region
226    pub size: RegionSize,
227    /// Sub-region bitmask
228    ///
229    /// The region is divided into exactly eight equal sized subregions.
230    /// Subregion 0 is the subregion at the least significant address.
231    ///
232    /// A 1 bit means that sub-region is disabled.
233    ///
234    /// Only applies to regions sized 256 bytes or larger.
235    pub subregion_mask: u8,
236    /// Is this region enabled?
237    pub enabled: bool,
238    /// No-Execute in this region
239    pub no_exec: bool,
240    /// Attributes for this region
241    pub mem_attr: MemAttr,
242    /// Access permissions for this region
243    pub access_perms: AccessPerms,
244}
245
246// Creating a static Region is fine - the pointers within it
247// only go to the MPU and aren't accessed via Rust code
248unsafe impl Sync for Region {}
249
250/// Describes the memory ordering and cacheability of a region
251#[derive(Debug, Clone, PartialEq, Eq)]
252#[cfg_attr(feature = "defmt", derive(defmt::Format))]
253pub enum MemAttr {
254    /// Strongly-ordered memory
255    StronglyOrdered,
256    /// Device (shareable or non-shareable)
257    Device {
258        /// Is device shareable (across multiple CPUs)
259        shareable: bool,
260    },
261    /// Normal Memory, Outer and Inner Cache are Write-Through, no Write-Allocate
262    WriteThroughNoWriteAlloc {
263        /// Is memory shareable (across multiple CPUs)
264        shareable: bool,
265    },
266    /// Normal Memory, Outer and Inner Cache are Write-Back, no Write-Allocate
267    WriteBackNoWriteAlloc {
268        /// Is memory shareable (across multiple CPUs)
269        shareable: bool,
270    },
271    /// Normal Memory, Non-cacheable in Outer and Inner Caches
272    NonCacheable {
273        /// Is memory shareable (across multiple CPUs)
274        shareable: bool,
275    },
276    /// Implementation Defined
277    ImplementationDefined {
278        /// Is memory shareable (across multiple CPUs)
279        shareable: bool,
280    },
281    /// Outer and Inner Write-Back, Write-Allocate
282    WriteBackWriteAlloc {
283        /// Is memory shareable (across multiple CPUs)
284        shareable: bool,
285    },
286    /// Normal Memory, where Inner and Outer cache have different settings
287    Cacheable {
288        /// Settings for the Outer Cache
289        outer: CachePolicy,
290        /// Settings for the Inner Cache
291        inner: CachePolicy,
292        /// Is memory shareable (across multiple CPUs)
293        shareable: bool,
294    },
295}
296
297impl MemAttr {
298    /// Convert this memory attribute to an 8-bit value we can write to RACR
299    pub const fn to_bits(&self) -> MemAttrBits {
300        match self {
301            MemAttr::StronglyOrdered => MemAttrBits {
302                tex: u3::from_u8(0b000),
303                c: false,
304                b: false,
305                s: true,
306            },
307            MemAttr::Device { shareable: true } => MemAttrBits {
308                tex: u3::from_u8(0b000),
309                c: false,
310                b: true,
311                s: true,
312            },
313            MemAttr::Device { shareable: false } => MemAttrBits {
314                tex: u3::from_u8(0b010),
315                c: false,
316                b: false,
317                s: false,
318            },
319            MemAttr::WriteThroughNoWriteAlloc { shareable } => MemAttrBits {
320                tex: u3::from_u8(0b000),
321                c: true,
322                b: false,
323                s: *shareable,
324            },
325            MemAttr::WriteBackNoWriteAlloc { shareable } => MemAttrBits {
326                tex: u3::from_u8(0b000),
327                c: true,
328                b: true,
329                s: *shareable,
330            },
331            MemAttr::NonCacheable { shareable } => MemAttrBits {
332                tex: u3::from_u8(0b001),
333                c: false,
334                b: false,
335                s: *shareable,
336            },
337            MemAttr::ImplementationDefined { shareable } => MemAttrBits {
338                tex: u3::from_u8(0b001),
339                c: true,
340                b: false,
341                s: *shareable,
342            },
343            MemAttr::WriteBackWriteAlloc { shareable } => MemAttrBits {
344                tex: u3::from_u8(0b001),
345                c: true,
346                b: true,
347                s: *shareable,
348            },
349            MemAttr::Cacheable {
350                outer,
351                inner,
352                shareable,
353            } => {
354                let outer = *outer as u8;
355                let inner = *inner as u8;
356                MemAttrBits {
357                    tex: u3::from_u8(0b100 | outer),
358                    c: (inner & 0b10) != 0,
359                    b: (inner & 0b01) != 0,
360                    s: *shareable,
361                }
362            }
363        }
364    }
365}
366
367/// A representation of Memory Attributes suitable for sticking into the RACR register
368#[derive(Debug, Clone, PartialEq, Eq)]
369#[cfg_attr(feature = "defmt", derive(defmt::Format))]
370pub struct MemAttrBits {
371    /// TEX attribute, defining the outer cache attribute
372    pub tex: u3,
373    /// C bit, defining the inner cache attribute
374    pub c: bool,
375    /// B bit, defining the inner cache attribute
376    pub b: bool,
377    /// S bit, shareable bit for normal memory regions
378    pub s: bool,
379}
380
381impl MemAttrBits {
382    const fn decode(&self) -> Option<MemAttr> {
383        match (self.tex.value(), self.c, self.b) {
384            (0b000, false, false) => Some(MemAttr::StronglyOrdered),
385            (0b000, false, true) => Some(MemAttr::Device { shareable: true }),
386            (0b000, true, false) => Some(MemAttr::WriteThroughNoWriteAlloc { shareable: self.s }),
387            (0b000, true, true) => Some(MemAttr::WriteBackNoWriteAlloc { shareable: self.s }),
388            (0b001, false, false) => Some(MemAttr::NonCacheable { shareable: self.s }),
389            (0b001, true, false) => Some(MemAttr::ImplementationDefined { shareable: self.s }),
390            (0b001, true, true) => Some(MemAttr::WriteBackWriteAlloc { shareable: self.s }),
391            (0b010, false, false) => Some(MemAttr::Device { shareable: false }),
392            (tex, c, b) if tex >= 0b100 => {
393                let outer = tex & 0b11;
394                let inner = ((c as u8) << 1) | (b as u8);
395                Some(MemAttr::Cacheable {
396                    outer: CachePolicy::new_with_raw_value(u2::from_u8(outer)),
397                    inner: CachePolicy::new_with_raw_value(u2::from_u8(inner)),
398                    shareable: self.s,
399                })
400            }
401            _ => {
402                // failed to decode
403                None
404            }
405        }
406    }
407}
408
409/// Whether/how a region is cacheable
410#[bitbybit::bitenum(u2, exhaustive = true)]
411#[cfg_attr(feature = "defmt", derive(defmt::Format))]
412#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
413#[derive(Debug, PartialEq, Eq)]
414pub enum CachePolicy {
415    /// Non-cacheable
416    NonCacheable = 0b00,
417    /// Write-Back Cacheable, Write-Allocate
418    WriteBackWriteAlloc = 0b01,
419    /// Write-Through Cacheable
420    WriteThroughNoWriteAlloc = 0b10,
421    /// Write-Back Cacheable, no Write-Allocate
422    WriteBackNoWriteAlloc = 0b11,
423}
424
425/// Describes the access permissions of a memory region
426///
427/// Access permission bits control read and write access to the corresponding memory region.
428/// Permissions are specified for priviledged accesses (PL1) and for user accesses (PL0).
429/// Accesses to a memory region without the required permissions generate a Permission fault.
430///
431/// Note that the execute-never (XN) attribute provides an additional permission check on memory
432/// regions, see [Region::no_exec].
433#[derive(Debug, Clone, PartialEq, Eq)]
434#[cfg_attr(feature = "defmt", derive(defmt::Format))]
435pub enum AccessPerms {
436    /// All accesses generate a Permission fault
437    /// - PL1: No access
438    /// - PL0: No access
439    NoAccess,
440    /// All write accesses generate Permission faults
441    /// - PL1: Read-only
442    /// - PL0: Read-only
443    ReadOnly,
444    /// Full access
445    /// - PL1: Read/Write
446    /// - PL0: Read/Write
447    ReadWrite,
448    /// PL1 read-only, all other accesses generate Permission faults
449    /// - PL1: Read-only
450    /// - PL0: No access
451    PrivReadOnly,
452    /// All unprivileged accesses generate Permission faults
453    /// - PL1: Read/Write
454    /// - PL0: No access
455    PrivReadWrite,
456    /// User mode write accesses generate Permission faults
457    /// - PL1: Read/Write
458    /// - PL0: Read-only
459    PrivReadWriteUserReadOnly,
460}
461impl AccessPerms {
462    /// Convert these access permissions to a 3-bit value we can write to RACR
463    pub const fn to_bits(&self) -> u3 {
464        u3::new(match self {
465            Self::NoAccess => 0b000,
466            Self::PrivReadWrite => 0b001,
467            Self::PrivReadWriteUserReadOnly => 0b010,
468            Self::ReadWrite => 0b011,
469            Self::PrivReadOnly => 0b101,
470            Self::ReadOnly => 0b110,
471        })
472    }
473    const fn from_bits(ap: u3) -> Option<Self> {
474        match ap.value() {
475            0b000 => Some(Self::NoAccess),
476            0b001 => Some(Self::PrivReadWrite),
477            0b010 => Some(Self::PrivReadWriteUserReadOnly),
478            0b011 => Some(Self::ReadWrite),
479            0b101 => Some(Self::PrivReadOnly),
480            0b110 => Some(Self::ReadOnly),
481            _ => None,
482        }
483    }
484}
485
486#[cfg(test)]
487mod test {
488    use super::*;
489
490    #[test]
491    fn mem_attr_strong() {
492        let mem_attr = MemAttr::StronglyOrdered;
493        let mem_attr_bits = mem_attr.to_bits();
494        assert_eq!(
495            mem_attr_bits,
496            MemAttrBits {
497                tex: u3::from_u8(0),
498                c: false,
499                b: false,
500                s: true
501            }
502        );
503        let mem_attr2 = mem_attr_bits.decode();
504        assert_eq!(Some(mem_attr), mem_attr2);
505    }
506
507    #[test]
508    fn mem_attr_complex() {
509        let mem_attr = MemAttr::Cacheable {
510            // 0b01
511            outer: CachePolicy::WriteBackWriteAlloc,
512            // 0b10
513            inner: CachePolicy::WriteThroughNoWriteAlloc,
514            shareable: true,
515        };
516        let mem_attr_bits = mem_attr.to_bits();
517        assert_eq!(
518            mem_attr_bits,
519            MemAttrBits {
520                tex: u3::from_u8(0b101),
521                c: true,
522                b: false,
523                s: true
524            }
525        );
526        let mem_attr2 = mem_attr_bits.decode();
527        assert_eq!(Some(mem_attr), mem_attr2);
528    }
529}