Trait TORUserPMP

Source
pub trait TORUserPMP<const MAX_REGIONS: usize> {
    const CONST_ASSERT_CHECK: ();

    // Required methods
    fn available_regions(&self) -> usize;
    fn configure_pmp(
        &self,
        regions: &[(TORUserPMPCFG, *const u8, *const u8); MAX_REGIONS],
    ) -> Result<(), ()>;
    fn enable_user_pmp(&self) -> Result<(), ()>;
    fn disable_user_pmp(&self);
}
Expand description

A RISC-V PMP implementation exposing a number of TOR memory protection regions to the PMPUserMPU.

The RISC-V PMP is complex and can be used to enforce memory protection in various modes (Machine, Supervisor and User mode). Depending on the exact extension set present (e.g., ePMP) and the machine’s security configuration bits, it may expose a vastly different set of constraints and application semantics.

Because we can’t possibly capture all of this in a single readable, maintainable and efficient implementation, we implement a two-layer system:

  • a TORUserPMP is a simple abstraction over some underlying PMP hardware implementation, which exposes an interface to configure regions that are active (enforced) in user-mode and can be configured for arbitrary addresses on a 4-byte granularity.

  • the PMPUserMPU takes this abstraction and implements the Tock kernel’s mpu::MPU trait. It worries about re-configuring memory protection when switching processes, allocating memory regions of an appropriate size, etc.

Implementors of a chip are free to define their own TORUserPMP implementations, adhering to their specific PMP layout & constraints, provided they implement this trait.

The MAX_REGIONS const generic is used to indicate the maximum number of TOR PMP regions available to the PMPUserMPU. The PMP implementation may provide less regions than indicated through MAX_REGIONS, for instance when entries are enforced (locked) in machine mode. The number of available regions may change at runtime. The current number of regions available to the PMPUserMPU is indicated by the TORUserPMP::available_regions method. However, when it is known that a number of regions are not available for userspace protection, MAX_REGIONS can be used to reduce the memory footprint allocated by stored PMP configurations, as well as the re-configuration overhead.

Required Associated Constants§

Source

const CONST_ASSERT_CHECK: ()

A placeholder to define const-assertions which are evaluated in PMPUserMPU::new. This can be used to, for instance, assert that the number of userspace regions does not exceed the number of hardware regions.

Required Methods§

Source

fn available_regions(&self) -> usize

The number of TOR regions currently available for userspace memory protection. Within [0; MAX_REGIONS].

The PMP implementation may provide less regions than indicated through MAX_REGIONS, for instance when entries are enforced (locked) in machine mode. The number of available regions may change at runtime. The implementation is free to map these regions to arbitrary PMP entries (and change this mapping at runtime), provided that they are enforced when the hart is in user-mode, and other memory regions are generally inaccessible when in user-mode.

When allocating regions for kernel-mode protection, and thus reducing the number of regions available to userspace, re-configuring the PMP may fail. This is allowed behavior. However, the PMP must not remove any regions from the user-mode current configuration while it is active (TORUserPMP::enable_user_pmp has been called, and it has not been disabled through TORUserPMP::disable_user_pmp).

Source

fn configure_pmp( &self, regions: &[(TORUserPMPCFG, *const u8, *const u8); MAX_REGIONS], ) -> Result<(), ()>

Configure the user-mode memory protection.

This method configures the user-mode memory protection, to be enforced on a call to TORUserPMP::enable_user_pmp.

PMP implementations where configured regions are only enforced in user-mode may re-configure the PMP on this function invocation and implement TORUserPMP::enable_user_pmp as a no-op. If configured regions are enforced in machine-mode (for instance when using an ePMP with the machine-mode whitelist policy), the new configuration rules must not apply until TORUserPMP::enable_user_pmp.

The tuples as passed in the regions parameter are defined as follows:

  • first value (TORUserPMPCFG): the memory protection mode as enforced on the region. A TORUserPMPCFG can be created from the mpu::Permissions type. It is in a format compatible to the pmpcfgX register, guaranteed to not have the lock (L) bit set, and configured either as a TOR region (A = 0b01), or disabled (all bits set to 0).

  • second value (*const u8): the region’s start addres. As a PMP TOR region has a 4-byte address granularity, this address is rounded down to the next 4-byte boundary.

  • third value (*const u8): the region’s end addres. As a PMP TOR region has a 4-byte address granularity, this address is rounded down to the next 4-byte boundary.

To disable a region, set its configuration to TORUserPMPCFG::OFF. In this case, the start and end addresses are ignored and can be set to arbitrary values.

Source

fn enable_user_pmp(&self) -> Result<(), ()>

Enable the user-mode memory protection.

Enables the memory protection for user-mode, as configured through TORUserPMP::configure_pmp. Enabling the PMP for user-mode may make the user-mode accessible regions inaccessible to the kernel. For PMP implementations where configured regions are only enforced in user-mode, this method may be implemented as a no-op.

If enabling the current configuration is not possible (e.g., because regions have been allocated to the kernel), this function must return Err(()). Otherwise, this function returns Ok(()).

Source

fn disable_user_pmp(&self)

Disable the user-mode memory protection.

Disables the memory protection for user-mode. If enabling the user-mode memory protetion made user-mode accessible regions inaccessible to machine-mode, this method should make these regions accessible again.

For PMP implementations where configured regions are only enforced in user-mode, this method may be implemented as a no-op. This method is not responsible for making regions inaccessible to user-mode. If previously configured regions must be made inaccessible, TORUserPMP::configure_pmp must be used to re-configure the PMP accordingly.

Dyn Compatibility§

This trait is not dyn compatible.

In older versions of Rust, dyn compatibility was called "object safety", so this trait is not object safe.

Implementors§

Source§

impl<const AVAILABLE_ENTRIES: usize, const MPU_REGIONS: usize> TORUserPMP<MPU_REGIONS> for KernelProtectionPMP<AVAILABLE_ENTRIES>

Source§

impl<const AVAILABLE_ENTRIES: usize, const MPU_REGIONS: usize> TORUserPMP<MPU_REGIONS> for KernelProtectionMMLEPMP<AVAILABLE_ENTRIES, MPU_REGIONS>

Source§

impl<const AVAILABLE_ENTRIES: usize, const MPU_REGIONS: usize> TORUserPMP<MPU_REGIONS> for SimplePMP<AVAILABLE_ENTRIES>

impl<const HANDOVER_CONFIG_CHECK: bool> TORUserPMP<{ TOR_USER_REGIONS_DEBUG_DISABLE }> for EarlGreyEPMP<HANDOVER_CONFIG_CHECK, EPMPDebugDisable>

impl<const HANDOVER_CONFIG_CHECK: bool> TORUserPMP<{ TOR_USER_REGIONS_DEBUG_ENABLE }> for EarlGreyEPMP<HANDOVER_CONFIG_CHECK, EPMPDebugEnable>