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

PMP and MPU Improvements #1873

Merged
merged 3 commits into from
Sep 10, 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
13 changes: 9 additions & 4 deletions arch/cortex-m3/src/mpu.rs
Original file line number Diff line number Diff line change
Expand Up @@ -363,7 +363,12 @@ impl CortexMRegion {
impl kernel::mpu::MPU for MPU {
type MpuConfig = CortexMConfig;

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

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

// Enable the MPU, disable it during HardFault/NMI handlers, and allow
Expand All @@ -372,9 +377,9 @@ impl kernel::mpu::MPU for MPU {
.write(Control::ENABLE::SET + Control::HFNMIENA::CLEAR + Control::PRIVDEFENA::SET);
}

fn disable_mpu(&self) {
let regs = &*self.registers;
regs.ctrl.write(Control::ENABLE::CLEAR);
fn disable_app_mpu(&self) {
// The MPU is not enabled for privileged mode, so we don't have to do
// anything
}

fn number_total_regions(&self) -> usize {
Expand Down
13 changes: 9 additions & 4 deletions arch/rv32i/src/pmp.rs
Original file line number Diff line number Diff line change
Expand Up @@ -244,9 +244,7 @@ impl PMPConfig {
impl kernel::mpu::MPU for PMP {
type MpuConfig = PMPConfig;

fn enable_mpu(&self) {}

fn disable_mpu(&self) {
fn clear_mpu(&self) {
// We want to disable all of the hardware entries, so we use `$x` here,
// and not `$x / 2`.
for x in 0..$x {
Expand Down Expand Up @@ -299,10 +297,17 @@ impl kernel::mpu::MPU for PMP {
csr::CSR.pmpcfg[0].modify(csr::pmpconfig::pmpcfg::w0::SET);
csr::CSR.pmpcfg[0].modify(csr::pmpconfig::pmpcfg::x0::SET);
csr::CSR.pmpcfg[0].modify(csr::pmpconfig::pmpcfg::a0::TOR);
// MPU is not configured for any process now
// PMP is not configured for any process now
self.last_configured_for.take();
}

fn enable_app_mpu(&self) {}

fn disable_app_mpu(&self) {
// PMP is not enabled for machine mode, so we don't have to do
// anything
}

fn number_total_regions(&self) -> usize {
$x / 2
}
Expand Down
2 changes: 1 addition & 1 deletion boards/nano33ble/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -266,7 +266,7 @@ pub unsafe fn reset_handler() {
CHIP = Some(chip);

// Need to disable the MPU because the bootloader seems to set it up.
chip.mpu().disable_mpu();
chip.mpu().clear_mpu();

// Configure the USB stack to enable a serial port over CDC-ACM.
cdc.enable();
Expand Down
104 changes: 94 additions & 10 deletions kernel/src/platform/mpu.rs
Original file line number Diff line number Diff line change
Expand Up @@ -93,20 +93,29 @@ pub trait MPU {
/// current state to help with debugging.
type MpuConfig: Default + Display = MpuConfigDefault;

/// Enables the MPU.
/// Clears the MPU.
///
/// This function will clear any access control enforced by the
/// MPU where possible.
/// On some hardware it is impossible to reset the MPU after it has
/// been locked, in this case this function wont change those regions.
fn clear_mpu(&self) {}

/// Enables the MPU for userspace apps.
///
/// This function must enable the permission restrictions on the various
/// regions protected by the MPU.
fn enable_mpu(&self) {}
fn enable_app_mpu(&self) {}

/// Disables the MPU.
/// Disables the MPU for userspace apps.
///
/// This function must completely disable any access control enforced by the
/// MPU. This will be called before the kernel starts to execute as on some
/// This function must disable any access control that was previously setup
/// for an app if it will interfere with the kernel.
alistair23 marked this conversation as resolved.
Show resolved Hide resolved
/// This will be called before the kernel starts to execute as on some
/// platforms the MPU rules apply to privileged code as well, and therefore
/// the MPU must be completely disabled for the kernel to effectively manage
/// processes.
fn disable_mpu(&self) {}
/// some of the MPU configuration must be disabled for the kernel to effectively
/// manage processes.
fn disable_app_mpu(&self) {}

/// Returns the maximum number of regions supported by the MPU.
fn number_total_regions(&self) -> usize {
Expand Down Expand Up @@ -186,7 +195,7 @@ pub trait MPU {
/// This function returns the start address and the size of the memory block
/// chosen for the process. If it is infeasible to find a memory block or
/// allocate the MPU region, or if the function has already been called,
/// returns None.
/// returns None. If None is returned no changes are made.
#[allow(unused_variables)]
fn allocate_app_memory_region(
&self,
Expand Down Expand Up @@ -225,7 +234,8 @@ pub trait MPU {
/// # Return Value
///
/// Returns an error if it is infeasible to update the MPU region, or if it
/// was never created.
/// was never created. If an error is returned no changes are made to the
/// configuration.
#[allow(unused_variables)]
fn update_app_memory_region(
&self,
Expand Down Expand Up @@ -257,3 +267,77 @@ pub trait MPU {

/// Implement default MPU trait for unit.
impl MPU for () {}

/// The generic trait that particular kernel level memory protection unit
/// implementations need to implement.
///
/// This trait provides generic functionality to extend the MPU trait above
/// to also allow the kernel to protect itself. It is expected that only a
/// limited number of SoCs can support this, which is why it is a seperate
/// implementation.
pub trait KernelMPU {
/// MPU-specific state that defines a particular configuration for the kernel
/// MPU.
/// That is, this should contain all of the required state such that the
/// implementation can be passed an object of this type and it should be
/// able to correctly and entirely configure the MPU.
///
/// It is `Default` so we can create empty state when the kernel is
/// created, and `Display` so that the `panic!()` output can display the
/// current state to help with debugging.
type KernelMpuConfig: Default + Display = MpuConfigDefault;

/// Mark a region of memory that the Tock kernel owns.
///
/// This function will optionally set the MPU to enforce the specified
/// constraints for all accessess (even from the kernel).
/// This should be used to mark read/write/execute areas of the Tock
/// kernel to have the hardware enforce those permissions.
///
/// If the KernelMPU trait is supported a board should use this function
/// to set permissions for all areas of memory the kernel will use.
/// Once all regions of memory have been allocated, the board must call
/// enable_kernel_mpu(). After enable_kernel_mpu() is called no changes
/// to kernel level code permissions can be made.
///
/// Note that kernel level permissions also apply to apps, although apps
/// will have more constraints applied on top of the kernel ones as
/// specified by the `MPU` trait.
///
/// Not all architectures support this, so don't assume this will be
/// implemented.
alistair23 marked this conversation as resolved.
Show resolved Hide resolved
///
/// # Arguments
///
/// - `memory_start`: start of memory region
/// - `memory_size`: size of unallocated memory
/// - `permissions`: permissions for the region
/// - `config`: MPU region configuration
///
/// # Return Value
///
/// Returns the start and size of the requested memory region. If it is
/// infeasible to allocate the MPU region, returns None. If None is
/// returned no changes are made.
#[allow(unused_variables)]
fn allocate_kernel_region(
&self,
memory_start: *const u8,
memory_size: usize,
permissions: Permissions,
config: &mut Self::KernelMpuConfig,
) -> Option<Region>;

/// Enables the MPU for the kernel.
///
/// This function must enable the permission restrictions on the various
alistair23 marked this conversation as resolved.
Show resolved Hide resolved
/// kernel regions specified by `allocate_kernel_region()` protected by
/// the MPU.
///
/// It is expected that this function is called in `reset_handler()`.
///
/// Once enabled this cannot be disabled. It is expected there won't be any
/// changes to the kernel regions after this is enabled.
#[allow(unused_variables)]
fn enable_kernel_mpu(&self, config: &mut Self::KernelMpuConfig);
}
4 changes: 2 additions & 2 deletions kernel/src/sched.rs
Original file line number Diff line number Diff line change
Expand Up @@ -610,11 +610,11 @@ impl Kernel {
// generate an interrupt when the timeslice has expired. The
// underlying timer is not affected.
process.setup_mpu();
chip.mpu().enable_mpu();
chip.mpu().enable_app_mpu();
scheduler_timer.arm();
let context_switch_reason = process.switch_to();
scheduler_timer.disarm();
chip.mpu().disable_mpu();
chip.mpu().disable_app_mpu();

// Now the process has returned back to the kernel. Check
// why and handle the process as appropriate.
Expand Down