Skip to content

Commit f1d8ad3

Browse files
Merge pull request #62 from korken89/el2-mpu
EL2 MPU support
2 parents 5ff05d8 + fcbb674 commit f1d8ad3

File tree

10 files changed

+526
-83
lines changed

10 files changed

+526
-83
lines changed

cortex-ar/src/pmsav8.rs

Lines changed: 227 additions & 16 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(),
@@ -67,7 +69,7 @@ impl El1Mpu {
6769
///
6870
/// ## Arguments
6971
///
70-
/// - `region`: The [Region] object containing the configuration for the MPU region.
72+
/// - `region`: The [El1Region] object containing the configuration for the MPU region.
7173
/// - `idx`: The index of the region to be configured.
7274
///
7375
/// ## Errors
@@ -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 {
@@ -114,11 +116,11 @@ impl El1Mpu {
114116
/// ## Arguments
115117
///
116118
/// - `regions_starting_idx`: The starting index for the regions to be reconfigured.
117-
/// - `regions`: A slice of [Region] objects that will overwrite the previous regions starting from `regions_starting_idx`.
119+
/// - `regions`: A slice of [El1Region] objects that will overwrite the previous regions starting from `regions_starting_idx`.
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
{
@@ -161,8 +163,9 @@ impl El1Mpu {
161163

162164
/// Configure the EL1 MPU
163165
///
164-
/// Write regions, attributes and enable/disable the background region with a single [Config] struct.
165-
pub fn configure(&mut self, config: &Config) -> Result<(), Error> {
166+
/// Write regions, attributes and enable/disable the background region
167+
/// with a single [El1Config] struct.
168+
pub fn configure(&mut self, config: &El1Config) -> Result<(), Error> {
166169
self.set_regions(0, config.regions)?;
167170

168171
self.set_attributes(config.memory_attributes);
@@ -187,17 +190,180 @@ impl El1Mpu {
187190
}
188191
}
189192

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

207373
/// Configuration for the PMSAv8-32 MPU
208374
#[derive(Clone, Debug, PartialEq, Eq)]
209-
pub struct Region {
375+
pub struct El1Region {
210376
/// The range of the region
211377
///
212378
/// * The first address must be a multiple of 32.
213379
/// * The length must be a multiple of 32.
214380
pub range: core::ops::RangeInclusive<*mut u8>,
215381
/// Shareability of the region
216-
pub shareability: Shareability,
382+
pub shareability: El1Shareability,
217383
/// Access for the region
218-
pub access: AccessPerms,
384+
pub access: El1AccessPerms,
219385
/// Is region no-exec?
220386
pub no_exec: bool,
221387
/// Which Memory Attribute applies here?
@@ -230,7 +396,52 @@ pub struct Region {
230396

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

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

cortex-ar/src/register/armv8r/hmpuir.rs

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,13 @@
33
use crate::register::{SysReg, SysRegRead};
44

55
/// HMPUIR (*Hyp MPU Type Register*)
6-
pub struct Hmpuir(pub u32);
6+
#[bitbybit::bitfield(u32)]
7+
pub struct Hmpuir {
8+
/// The number of EL2 MPU regions implemented
9+
#[bits(0..=7, r)]
10+
region: u8,
11+
}
12+
713
impl SysReg for Hmpuir {
814
const CP: u32 = 15;
915
const CRN: u32 = 0;
@@ -16,6 +22,6 @@ impl Hmpuir {
1622
#[inline]
1723
/// Reads HMPUIR (*Hyp MPU Type Register*)
1824
pub fn read() -> Hmpuir {
19-
unsafe { Self(<Self as SysRegRead>::read_raw()) }
25+
unsafe { Self::new_with_raw_value(<Self as SysRegRead>::read_raw()) }
2026
}
2127
}

0 commit comments

Comments
 (0)