Skip to content

Commit 2056011

Browse files
committed
Add El2Mpu implementation for PMSAv8-32 EL2 MPU support
1 parent e1e7ada commit 2056011

File tree

3 files changed

+261
-50
lines changed

3 files changed

+261
-50
lines changed

cortex-ar/src/pmsav8.rs

Lines changed: 223 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,9 @@ use arbitrary_int::{u26, u3};
1111
use crate::register;
1212

1313
#[doc(inline)]
14-
pub use register::prbar::{AccessPerms, Shareability};
14+
pub use register::hprbar::{AccessPerms as El2AccessPerms, Shareability as El2Shareability};
15+
#[doc(inline)]
16+
pub use register::prbar::{AccessPerms as El1AccessPerms, Shareability as El1Shareability};
1517

1618
/// Ways this API can fail
1719
#[derive(Debug, Clone, PartialEq, Eq)]
@@ -44,7 +46,7 @@ impl El1Mpu {
4446
}
4547

4648
/// Access the current state of a region
47-
pub fn get_region(&mut self, idx: u8) -> Option<Region> {
49+
pub fn get_region(&mut self, idx: u8) -> Option<El1Region> {
4850
if idx >= self.num_regions() {
4951
return None;
5052
}
@@ -53,7 +55,7 @@ impl El1Mpu {
5355
let prlar = register::Prlar::read();
5456
let start_addr = (prbar.base().value() << 6) as *mut u8;
5557
let end_addr = ((prlar.limit().value() << 6) | 0x3F) as *mut u8;
56-
Some(Region {
58+
Some(El1Region {
5759
range: start_addr..=end_addr,
5860
shareability: prbar.shareability(),
5961
access: prbar.access_perms(),
@@ -76,7 +78,7 @@ impl El1Mpu {
7678
/// - [Error::UnalignedRegion] if the region's start address is not 64-byte aligned.
7779
/// - [Error::UnalignedRegion] if the region's end address is not 63-byte aligned.
7880
/// - [Error::InvalidMair] if the region's MAIR index is invalid (greater than 7).
79-
pub fn set_region(&mut self, idx: u8, region: &Region) -> Result<(), Error> {
81+
pub fn set_region(&mut self, idx: u8, region: &El1Region) -> Result<(), Error> {
8082
let start = *(region.range.start()) as usize as u32;
8183
// Check for 64-byte alignment (0x3F is six bits)
8284
if start & 0x3F != 0 {
@@ -118,7 +120,7 @@ impl El1Mpu {
118120
pub fn set_regions(
119121
&mut self,
120122
regions_starting_idx: u8,
121-
regions: &[Region],
123+
regions: &[El1Region],
122124
) -> Result<(), Error> {
123125
if regions.len().saturating_add(regions_starting_idx as usize) > self.num_regions() as usize
124126
{
@@ -162,7 +164,7 @@ impl El1Mpu {
162164
/// Configure the EL1 MPU
163165
///
164166
/// Write regions, attributes and enable/disable the background region with a single [Config] struct.
165-
pub fn configure(&mut self, config: &Config) -> Result<(), Error> {
167+
pub fn configure(&mut self, config: &El1Config) -> Result<(), Error> {
166168
self.set_regions(0, config.regions)?;
167169

168170
self.set_attributes(config.memory_attributes);
@@ -187,17 +189,180 @@ impl El1Mpu {
187189
}
188190
}
189191

190-
/// Configuration for the PMSAv8-32 MPU
192+
/// Represents our PMSAv8-32 EL2 MPU
193+
pub struct El2Mpu();
194+
195+
impl El2Mpu {
196+
/// Create an EL2 MPU handle
197+
///
198+
/// # Safety
199+
///
200+
/// Only create one of these at any given time, as they access shared
201+
/// mutable state within the processor and do read-modify-writes on that state.
202+
pub unsafe fn new() -> El2Mpu {
203+
El2Mpu()
204+
}
205+
206+
/// How many EL2 MPU regions are there?
207+
pub fn num_regions(&self) -> u8 {
208+
register::Hmpuir::read().region()
209+
}
210+
211+
/// Access the current state of a region
212+
pub fn get_region(&mut self, idx: u8) -> Option<El2Region> {
213+
if idx >= self.num_regions() {
214+
return None;
215+
}
216+
register::Hprselr::write(register::Hprselr(idx as u32));
217+
let hprbar = register::Hprbar::read();
218+
let hprlar = register::Hprlar::read();
219+
let start_addr = (hprbar.base().value() << 6) as *mut u8;
220+
let end_addr = ((hprlar.limit().value() << 6) | 0x3F) as *mut u8;
221+
Some(El2Region {
222+
range: start_addr..=end_addr,
223+
shareability: hprbar.shareability(),
224+
access: hprbar.access_perms(),
225+
no_exec: hprbar.nx(),
226+
mair: hprlar.mair().value(),
227+
enable: hprlar.enabled(),
228+
})
229+
}
230+
231+
/// Write a single region to the EL2 MPU
232+
///
233+
/// ## Arguments
234+
///
235+
/// - `region`: The [El2Region] object containing the configuration for the MPU region.
236+
/// - `idx`: The index of the region to be configured.
237+
///
238+
/// ## Errors
239+
///
240+
/// Returns:
241+
/// - [Error::UnalignedRegion] if the region's start address is not 64-byte aligned.
242+
/// - [Error::UnalignedRegion] if the region's end address is not 63-byte aligned.
243+
/// - [Error::InvalidMair] if the region's MAIR index is invalid (greater than 7).
244+
pub fn set_region(&mut self, idx: u8, region: &El2Region) -> Result<(), Error> {
245+
let start = *(region.range.start()) as usize as u32;
246+
// Check for 64-byte alignment (0x3F is six bits)
247+
if start & 0x3F != 0 {
248+
return Err(Error::UnalignedRegion(region.range.clone()));
249+
}
250+
let end = *(region.range.end()) as usize as u32;
251+
if end & 0x3F != 0x3F {
252+
return Err(Error::UnalignedRegion(region.range.clone()));
253+
}
254+
if region.mair > 7 {
255+
return Err(Error::InvalidMair(region.mair));
256+
}
257+
register::Hprselr::write(register::Hprselr(idx as u32));
258+
register::Hprbar::write({
259+
let mut bar = register::Hprbar::new_with_raw_value(0);
260+
bar.set_base(u26::from_u32(start >> 6));
261+
bar.set_access_perms(region.access);
262+
bar.set_nx(region.no_exec);
263+
bar.set_shareability(region.shareability);
264+
bar
265+
});
266+
register::Hprlar::write({
267+
let mut lar = register::Hprlar::new_with_raw_value(0);
268+
lar.set_limit(u26::from_u32(end >> 6));
269+
lar.set_enabled(region.enable);
270+
lar.set_mair(u3::from_u8(region.mair));
271+
lar
272+
});
273+
274+
Ok(())
275+
}
276+
277+
/// Writes a subset of EL2 MPU regions starting from a specified index.
278+
///
279+
/// ## Arguments
280+
///
281+
/// - `regions_starting_idx`: The starting index for the regions to be reconfigured.
282+
/// - `regions`: A slice of [El2Region] objects that will overwrite the previous regions starting from `regions_starting_idx`.
283+
pub fn set_regions(
284+
&mut self,
285+
regions_starting_idx: u8,
286+
regions: &[El2Region],
287+
) -> Result<(), Error> {
288+
if regions.len().saturating_add(regions_starting_idx as usize) > self.num_regions() as usize
289+
{
290+
return Err(Error::TooManyRegions);
291+
}
292+
293+
for (idx, region) in regions.iter().enumerate() {
294+
self.set_region(idx as u8 + regions_starting_idx, region)?;
295+
}
296+
297+
Ok(())
298+
}
299+
300+
/// Set the memory attributes to HMAIR0 and HMAIR1
301+
pub fn set_attributes(&mut self, memattrs: &[MemAttr]) {
302+
let mem_attr0 = memattrs.get(0).map(|m| m.to_bits()).unwrap_or(0) as u32;
303+
let mem_attr1 = memattrs.get(1).map(|m| m.to_bits()).unwrap_or(0) as u32;
304+
let mem_attr2 = memattrs.get(2).map(|m| m.to_bits()).unwrap_or(0) as u32;
305+
let mem_attr3 = memattrs.get(3).map(|m| m.to_bits()).unwrap_or(0) as u32;
306+
let hmair0 = mem_attr3 << 24 | mem_attr2 << 16 | mem_attr1 << 8 | mem_attr0;
307+
unsafe {
308+
register::Hmair0::write(register::Hmair0(hmair0));
309+
}
310+
let mem_attr0 = memattrs.get(4).map(|m| m.to_bits()).unwrap_or(0) as u32;
311+
let mem_attr1 = memattrs.get(5).map(|m| m.to_bits()).unwrap_or(0) as u32;
312+
let mem_attr2 = memattrs.get(6).map(|m| m.to_bits()).unwrap_or(0) as u32;
313+
let mem_attr3 = memattrs.get(7).map(|m| m.to_bits()).unwrap_or(0) as u32;
314+
let hmair1 = mem_attr3 << 24 | mem_attr2 << 16 | mem_attr1 << 8 | mem_attr0;
315+
unsafe {
316+
register::Hmair1::write(register::Hmair1(hmair1));
317+
}
318+
}
319+
320+
/// Enable or disable the background region
321+
pub fn background_region_enable(&mut self, enable: bool) {
322+
register::Hsctlr::modify(|r| {
323+
r.set_br(enable);
324+
});
325+
}
326+
327+
/// Configure the EL2 MPU
328+
///
329+
/// Write regions, attributes and enable/disable the background region with a single [El2Config] struct.
330+
pub fn configure(&mut self, config: &El2Config) -> Result<(), Error> {
331+
self.set_regions(0, config.regions)?;
332+
333+
self.set_attributes(config.memory_attributes);
334+
335+
self.background_region_enable(config.background_config);
336+
337+
Ok(())
338+
}
339+
340+
/// Enable the EL2 MPU
341+
pub fn enable(&mut self) {
342+
register::Hsctlr::modify(|r| {
343+
r.set_m(true);
344+
});
345+
}
346+
347+
/// Disable the EL2 MPU
348+
pub fn disable(&mut self) {
349+
register::Hsctlr::modify(|r| {
350+
r.set_m(false);
351+
});
352+
}
353+
}
354+
355+
/// Configuration for the PMSAv8-32 EL1 MPU
191356
#[derive(Clone, Debug, PartialEq, Eq)]
192-
pub struct Config<'a> {
357+
pub struct El1Config<'a> {
193358
/// Background Config Enable
194359
///
195360
/// If true, use the default MMU config if no other region matches an address
196361
pub background_config: bool,
197362
/// Information about each Region.
198363
///
199364
/// If you pass more regions than the MPU supports, you get an error.
200-
pub regions: &'a [Region],
365+
pub regions: &'a [El1Region],
201366
/// Information about each Memory Attribute
202367
///
203368
/// If you pass more MemAttrs than the MPU supports (8), you get an error.
@@ -206,16 +371,16 @@ pub struct Config<'a> {
206371

207372
/// Configuration for the PMSAv8-32 MPU
208373
#[derive(Clone, Debug, PartialEq, Eq)]
209-
pub struct Region {
374+
pub struct El1Region {
210375
/// The range of the region
211376
///
212377
/// * The first address must be a multiple of 32.
213378
/// * The length must be a multiple of 32.
214379
pub range: core::ops::RangeInclusive<*mut u8>,
215380
/// Shareability of the region
216-
pub shareability: Shareability,
381+
pub shareability: El1Shareability,
217382
/// Access for the region
218-
pub access: AccessPerms,
383+
pub access: El1AccessPerms,
219384
/// Is region no-exec?
220385
pub no_exec: bool,
221386
/// Which Memory Attribute applies here?
@@ -230,7 +395,52 @@ pub struct Region {
230395

231396
// Creating a static Region is fine - the pointers within it
232397
// only go to the MPU and aren't accessed via Rust code
233-
unsafe impl Sync for Region {}
398+
unsafe impl Sync for El1Region {}
399+
400+
/// Configuration for the PMSAv8-32 EL2 MPU
401+
#[derive(Clone, Debug, PartialEq, Eq)]
402+
pub struct El2Config<'a> {
403+
/// Background Config Enable
404+
///
405+
/// If true, use the default MMU config if no other region matches an address
406+
pub background_config: bool,
407+
/// Information about each Region.
408+
///
409+
/// If you pass more regions than the MPU supports, you get an error.
410+
pub regions: &'a [El2Region],
411+
/// Information about each Memory Attribute
412+
///
413+
/// If you pass more MemAttrs than the MPU supports (8), you get an error.
414+
pub memory_attributes: &'a [MemAttr],
415+
}
416+
417+
/// Configuration for the PMSAv8-32 EL2 MPU
418+
#[derive(Clone, Debug, PartialEq, Eq)]
419+
pub struct El2Region {
420+
/// The range of the region
421+
///
422+
/// * The first address must be a multiple of 32.
423+
/// * The length must be a multiple of 32.
424+
pub range: core::ops::RangeInclusive<*mut u8>,
425+
/// Shareability of the region
426+
pub shareability: El2Shareability,
427+
/// Access for the region
428+
pub access: El2AccessPerms,
429+
/// Is region no-exec?
430+
pub no_exec: bool,
431+
/// Which Memory Attribute applies here?
432+
///
433+
/// Selects from the eight attributes in {HMAIR0, HMAIR1}.
434+
///
435+
/// Only values 0..=7 are valid here.
436+
pub mair: u8,
437+
/// Is this region enabled?
438+
pub enable: bool,
439+
}
440+
441+
// Creating a static El2Region is fine - the pointers within it
442+
// only go to the MPU and aren't accessed via Rust code
443+
unsafe impl Sync for El2Region {}
234444

235445
/// Describes the memory ordering and cacheability of a region
236446
#[derive(Debug, Clone, PartialEq, Eq)]

0 commit comments

Comments
 (0)