Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

kernel: configure MPU only if switching to a different process #1822

Merged
merged 8 commits into from
Jun 4, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
61 changes: 47 additions & 14 deletions arch/cortex-m3/src/mpu.rs
Original file line number Diff line number Diff line change
@@ -1,13 +1,16 @@
//! Implementation of the memory protection unit for the Cortex-M3 and
//! Cortex-M4.

use core::cell::Cell;
use core::cmp;
use core::fmt;
use kernel;
use kernel::common::cells::OptionalCell;
use kernel::common::math;
use kernel::common::registers::{register_bitfields, FieldValue, ReadOnly, ReadWrite};
use kernel::common::StaticRef;
use kernel::mpu;
use kernel::AppId;

/// MPU Registers for the Cortex-M3 and Cortex-M4 families
/// Described in section 4.5 of
Expand Down Expand Up @@ -120,19 +123,39 @@ register_bitfields![u32,
const MPU_BASE_ADDRESS: StaticRef<MpuRegisters> =
unsafe { StaticRef::new(0xE000ED90 as *const MpuRegisters) };

/// Constructor field is private to limit who can create a new MPU
pub struct MPU(StaticRef<MpuRegisters>);
/// State related to the real physical MPU.
///
/// There should only be one instantiation of this object as it represents
/// real hardware.
pub struct MPU {
/// MMIO reference to MPU registers.
registers: StaticRef<MpuRegisters>,
/// Optimization logic. This is used to indicate which application the MPU
/// is currently configured for so that the MPU can skip updating when the
/// kernel returns to the same app.
hardware_is_configured_for: OptionalCell<AppId>,
}

impl MPU {
pub const unsafe fn new() -> MPU {
MPU(MPU_BASE_ADDRESS)
MPU {
registers: MPU_BASE_ADDRESS,
hardware_is_configured_for: OptionalCell::empty(),
}
}
}

/// Struct storing region configuration for the Cortex-M MPU.
#[derive(Copy, Clone)]
/// Per-process struct storing MPU configuration for cortex-m MPUs.
///
/// The cortex-m MPU has eight regions, all of which must be configured (though
/// unused regions may be configured as disabled). This struct caches the result
/// of region configuration calculation
pub struct CortexMConfig {
/// The computed region configuration for this process.
regions: [CortexMRegion; 8],
/// Has the configuration changed since the last time the this process
/// configuration was written to hardware?
is_dirty: Cell<bool>,
}

const APP_MEMORY_REGION_NUM: usize = 0;
Expand All @@ -150,6 +173,7 @@ impl Default for CortexMConfig {
CortexMRegion::empty(6),
CortexMRegion::empty(7),
],
is_dirty: Cell::new(true),
}
}
}
Expand Down Expand Up @@ -340,7 +364,7 @@ impl kernel::mpu::MPU for MPU {
type MpuConfig = CortexMConfig;

fn enable_mpu(&self) {
let regs = &*self.0;
let regs = &*self.registers;

// Enable the MPU, disable it during HardFault/NMI handlers, and allow
// privileged code access to all unprotected memory.
Expand All @@ -349,12 +373,12 @@ impl kernel::mpu::MPU for MPU {
}

fn disable_mpu(&self) {
let regs = &*self.0;
let regs = &*self.registers;
regs.ctrl.write(Control::ENABLE::CLEAR);
}

fn number_total_regions(&self) -> usize {
let regs = &*self.0;
let regs = &*self.registers;
regs.mpu_type.read(Type::DREGION) as usize
}

Expand Down Expand Up @@ -480,6 +504,7 @@ impl kernel::mpu::MPU for MPU {
);

config.regions[region_num] = region;
config.is_dirty.set(true);

Some(mpu::Region::new(start as *const u8, size))
}
Expand Down Expand Up @@ -586,6 +611,7 @@ impl kernel::mpu::MPU for MPU {
);

config.regions[APP_MEMORY_REGION_NUM] = region;
config.is_dirty.set(true);

Some((region_start as *const u8, region_size))
}
Expand Down Expand Up @@ -645,17 +671,24 @@ impl kernel::mpu::MPU for MPU {
);

config.regions[APP_MEMORY_REGION_NUM] = region;
config.is_dirty.set(true);

Ok(())
}

fn configure_mpu(&self, config: &Self::MpuConfig) {
let regs = &*self.0;
fn configure_mpu(&self, config: &Self::MpuConfig, app_id: &AppId) {
// If the hardware is already configured for this app and the app's MPU
// configuration has not changed, then skip the hardware update.
if !self.hardware_is_configured_for.contains(app_id) || config.is_dirty.get() {
let regs = &*self.registers;

// Set MPU regions
for region in config.regions.iter() {
regs.rbar.write(region.base_address());
regs.rasr.write(region.attributes());
// Set MPU regions
for region in config.regions.iter() {
regs.rbar.write(region.base_address());
regs.rasr.write(region.attributes());
}
self.hardware_is_configured_for.set(*app_id);
config.is_dirty.set(false);
}
}
}