pub struct EarlGreyEPMP<const HANDOVER_CONFIG_CHECK: bool, DBG: EPMPDebugConfig> { /* private fields */ }
Expand description
RISC-V ePMP memory protection implementation for the EarlGrey SoC.
The EarlGrey ePMP implementation hard-codes many assumptions about the behavior and state of the underlying hardware, to reduce complexity of this codebase, and improve its security, reliability and auditability.
Namely, it makes and checks assumptions about the machine security policy
prior to its initialization routine, locks down the hardware through a
static set of PMP configuration steps, and then exposes a subset of regions
for user-mode protection through the PMPUserMPU
trait.
The EarlGrey ePMP implementation supports JTAG debug-port access through the
integrated RISC-V Debug Manger (RVDM) core, which requires R/W/X-access to a
given region of memory in machine-mode and user-mode. The EarlGreyEPMP
struct accepts a generic EPMPDebugConfig
implementation, which either
enables (in the case of EPMPDebugEnable
) or disables
(EPMPDebugDisable
) the debug-port access. However, enabling debug-port
access can potentially weaken the system’s security by not enabling
machine-mode lockdown (MML), and uses an additional PMP region otherwise
available to userspace. See the documentation of EPMPDebugConfig
for
more information on this.
§ePMP Region Layout & Configuration (EPMPDebugDisable
mode)
Because of the machine-mode lockdown (MML) mode, no region can have R/W/X permissions. The machine-mode whitelist policy (MMWP) further requires all memory accessed by machine-mode to have a corresponding locked PMP entry defined. Lower-indexed PMP entires have precedence over entries with higher indices. Under MML mode, a non-locked (user-mode) entry prevents machine-mode access to that memory. Thus, the ePMP is to be configured in a “sandwiched” layout (with decreasing precedence):
-
High-priority machine-mode “lockdown” entries.
These entries are only accessible to machine mode. Once locked, they can only be changed through a hart reset. Examples for such memory sections can be the kernel’s
.text
or certain RAM (e.g. stack) sections. -
Tock’s user-mode “MPU”
This section defines entries corresponding to memory sections made accessible to user-mode. These entires are exposed through the implementation of the
TORUserPMP
trait.Effectively, this is Tock’s “MPU” sandwiched in between the high-priority and low-priority PMP sections.
These entires are not locked and must be turned off prior to the kernel being able to access them.
This section must take precende over the lower kernel-mode entries, as these entries are aliased by the lower kernel-mode entries. Having a locked machine-mode entry take precende over an alias a user-space one prevents user-mode from accessing the aliased memory.
-
Low-priority machine-mode “accessability” entires.
These entires provide the kernel access to memory regions which are (partially) aliased by user-mode regions above. This allows for implementing memory sharing between userspace and the kernel (moving acccess to user-mode by turning on a region above, and falling back onto these rules when turning the user-mode region off).
These regions can be granular (e.g. grant R/W on the entire RAM), but should not provide any excess permissions where not required (e.g. avoid granting R/X on flash-memory where only R is required, because the kernel-text is already marked as R/X in the high-priority regions above.
Because the ROM_EXT and test ROM set up different ePMP configs, there are
separate initialization routines (new
and new_test_rom
) for those
environments.
new
(only available when the debug-port is disabled) attempts to set up
the following memory protection rules and layout:
-
msseccfg
CSR:|-----+-----------------------------------------------------------+-------| | BIT | LABEL | STATE | |-----+-----------------------------------------------------------+-------| | 0 | Machine-Mode Lockdown (MML) | 1 | | 1 | Machine-Mode Whitelist Policy (MMWP) | 1 | | 2 | Rule-Lock Bypass (RLB) | 0 | |-----+-----------------------------------------------------------+-------|
-
pmpcfgX
/pmpaddrX
CSRs:|-------+----------------------------------------+-----------+---+-------| | ENTRY | REGION / ADDR | MODE | L | PERMS | |-------+----------------------------------------+-----------+---+-------| | 0 | Locked by the ROM_EXT or unused | NAPOT/OFF | X | | | | | | | | | 1 | Locked by the ROM_EXT or unused | NAPOT/OFF | X | | | | | | | | | 2 | -------------------------------------- | OFF | X | ----- | | 3 | Kernel .text section | TOR | X | R/X | | | | | | | | 4 | / \ | OFF | | | | 5 | \ Userspace TOR region #0 / | TOR | | ????? | | | | | | | | 6 | / \ | OFF | | | | 7 | \ Userspace TOR region #1 / | TOR | | ????? | | | | | | | | 8 | / \ | OFF | | | | 9 | \ Userspace TOR region #2 / | TOR | | ????? | | | | | | | | 10 | / \ | OFF | | | | 11 | \ Userspace TOR region #3 / | TOR | | ????? | | | | | | | | 12 | FLASH (spanning kernel & apps) | NAPOT | X | R | | | | | | | | 13 | -------------------------------------- | OFF | X | ----- | | | | | | | | 14 | RAM (spanning kernel & apps) | NAPOT | X | R/W | | | | | | | | 15 | MMIO | NAPOT | X | R/W | |-------+----------------------------------------+-----------+---+-------|
new_test_rom
(only available when the debug-port is disabled) attempts to
set up the following memory protection rules and layout:
-
msseccfg
CSR:|-----+-----------------------------------------------------------+-------| | BIT | LABEL | STATE | |-----+-----------------------------------------------------------+-------| | 0 | Machine-Mode Lockdown (MML) | 1 | | 1 | Machine-Mode Whitelist Policy (MMWP) | 1 | | 2 | Rule-Lock Bypass (RLB) | 0 | |-----+-----------------------------------------------------------+-------|
-
pmpcfgX
/pmpaddrX
CSRs:|-------+---------------------------------------------+-------+---+-------| | ENTRY | REGION / ADDR | MODE | L | PERMS | |-------+---------------------------------------------+-------+---+-------| | 0 | ------------------------------------------- | OFF | X | ----- | | 1 | Kernel .text section | TOR | X | R/X | | | | | | | | 2 | ------------------------------------------- | OFF | X | | | | | | | | | 3 | ------------------------------------------- | OFF | X | | | | | | | | | 4 | / \ | OFF | | | | 5 | \ Userspace TOR region #0 / | TOR | | ????? | | | | | | | | 6 | / \ | OFF | | | | 7 | \ Userspace TOR region #1 / | TOR | | ????? | | | | | | | | 8 | / \ | OFF | | | | 9 | \ Userspace TOR region #2 / | TOR | | ????? | | | | | | | | 10 | / \ | OFF | | | | 11 | \ Userspace TOR region #3 / | TOR | | ????? | | | | | | | | 12 | ------------------------------------------- | OFF | X | ----- | | | | | | | | 13 | FLASH (spanning kernel & apps) | NAPOT | X | R | | | | | | | | 14 | RAM (spanning kernel & apps) | NAPOT | X | R/W | | | | | | | | 15 | MMIO | NAPOT | X | R/W | |-------+---------------------------------------------+-------+---+-------|
§ePMP Region Layout & Configuration (EPMPDebugEnable
mode)
When enabling the RISC-V Debug Manager (JTAG debug port), the ePMP must be
configured differently. This is because the RVDM
requires a memory section
to be mapped with read-write-execute privileges, which is not possible under
the machine-mode lockdown (MML) mode. However, when simply disabling MML in
the above policy, it would grant userspace access to kernel memory through
the locked PMP entires. We still need to define locked PMP entries to grant
the kernel (machine-mode) access to its required memory regions, as the
machine-mode whitelist policy (MMWP) is enabled.
Thus we split the PMP entires into three parts, as outlined in the following:
-
Tock’s user-mode “MPU”
This section defines entries corresponding to memory sections made accessible to user-mode. These entires are exposed through the implementation of the
TORUserPMP
trait.These entires are not locked. Because the machine-mode lockdown (MML) mode is not enabled, non-locked regions are ignored in machine-mode. The kernel does not have to disable these entires prior to being able to access them.
This section must take precende over the lower kernel-mode entries, as these entries are aliased by the lower kernel-mode entries. Having a locked machine-mode entry take precende over an alias a user-space one prevents user-mode from accessing the aliased memory.
-
User-mode “deny-all” rule.
Without machine-mode lockdown (MML) mode, locked regions apply to both user- and kernel-mode. Because the machine-mode whitelist policy (MMWP) is enabled, the kernel must be granted explicit permission to access memory (default-deny policy). This means that we must prevent any user-mode access from “falling through” to kernel-mode regions. For this purpose, we insert a non-locked “deny-all” rule which disallows all user-mode accesses to the entire address space, if no other higher-priority user-mode rule matches.
-
Machine-mode “accessability” entires.
These entires provide the kernel access to certain memory regions, as required by the machine-mode whitelist policy (MMWP).
new_debug
(only available when the debug-port is enabled) attempts to set
up the following memory protection rules and layout:
-
msseccfg
CSR:|-----+-----------------------------------------------------------+-------| | BIT | LABEL | STATE | |-----+-----------------------------------------------------------+-------| | 0 | Machine-Mode Lockdown (MML) | 0 | | 1 | Machine-Mode Whitelist Policy (MMWP) | 1 | | 2 | Rule-Lock Bypass (RLB) | 0 | |-----+-----------------------------------------------------------+-------|
-
pmpcfgX
/pmpaddrX
CSRs:|-------+---------------------------------------------+-------+---+-------| | ENTRY | REGION / ADDR | MODE | L | PERMS | |-------+---------------------------------------------+-------+---+-------| | 0 | / \ | OFF | | | | 1 | \ Userspace TOR region #0 / | TOR | | ????? | | | | | | | | 2 | / \ | OFF | | | | 3 | \ Userspace TOR region #1 / | TOR | | ????? | | | | | | | | 4 | / \ | OFF | | | | 5 | \ Userspace TOR region #2 / | TOR | | ????? | | | | | | | | 6 | / \ | OFF | | | | 7 | \ Userspace TOR region #3 / | TOR | | ????? | | | | | | | | 8 | ------------------------------------------- | OFF | | ----- | | | | | | | | 9 | "Deny-all" user-mode rule (all memory) | NAPOT | | ----- | | | | | | | | 10 | ------------------------------------------- | OFF | X | ----- | | 11 | Kernel .text section | TOR | X | R/X | | | | | | | | 12 | RVDM Debug Core Memory | NAPOT | X | R/W/X | | | | | | | | 13 | FLASH (spanning kernel & apps) | NAPOT | X | R | | | | | | | | 14 | RAM (spanning kernel & apps) | NAPOT | X | R/W | | | | | | | | 15 | MMIO | NAPOT | X | R/W | |-------+---------------------------------------------+-------+---+-------|
Implementations§
Source§impl<const HANDOVER_CONFIG_CHECK: bool> EarlGreyEPMP<HANDOVER_CONFIG_CHECK, EPMPDebugDisable>
impl<const HANDOVER_CONFIG_CHECK: bool> EarlGreyEPMP<HANDOVER_CONFIG_CHECK, EPMPDebugDisable>
pub unsafe fn new( flash: FlashRegion, ram: RAMRegion, mmio: MMIORegion, kernel_text: KernelTextRegion, ) -> Result<Self, EarlGreyEPMPError>
pub unsafe fn new_test_rom( flash: FlashRegion, ram: RAMRegion, mmio: MMIORegion, kernel_text: KernelTextRegion, ) -> Result<Self, EarlGreyEPMPError>
Source§impl<const HANDOVER_CONFIG_CHECK: bool> EarlGreyEPMP<HANDOVER_CONFIG_CHECK, EPMPDebugEnable>
impl<const HANDOVER_CONFIG_CHECK: bool> EarlGreyEPMP<HANDOVER_CONFIG_CHECK, EPMPDebugEnable>
pub unsafe fn new_debug( flash: FlashRegion, ram: RAMRegion, mmio: MMIORegion, kernel_text: KernelTextRegion, debug_memory: RVDMRegion, ) -> Result<Self, EarlGreyEPMPError>
Trait Implementations§
Source§impl<const HANDOVER_CONFIG_CHECK: bool, DBG: EPMPDebugConfig> Display for EarlGreyEPMP<HANDOVER_CONFIG_CHECK, DBG>
impl<const HANDOVER_CONFIG_CHECK: bool, DBG: EPMPDebugConfig> Display for EarlGreyEPMP<HANDOVER_CONFIG_CHECK, DBG>
Source§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_DISABLE }> for EarlGreyEPMP<HANDOVER_CONFIG_CHECK, EPMPDebugDisable>
Source§const CONST_ASSERT_CHECK: () = ()
const CONST_ASSERT_CHECK: () = ()
PMPUserMPU::new
. This can be used to, for instance, assert that the
number of userspace regions does not exceed the number of hardware
regions.Source§fn available_regions(&self) -> usize
fn available_regions(&self) -> usize
[0; MAX_REGIONS]
. Read moreSource§fn configure_pmp(
&self,
regions: &[(TORUserPMPCFG, *const u8, *const u8); 4],
) -> Result<(), ()>
fn configure_pmp( &self, regions: &[(TORUserPMPCFG, *const u8, *const u8); 4], ) -> Result<(), ()>
Source§fn disable_user_pmp(&self)
fn disable_user_pmp(&self)
Source§impl<const HANDOVER_CONFIG_CHECK: bool> TORUserPMP<{ TOR_USER_REGIONS_DEBUG_ENABLE }> for EarlGreyEPMP<HANDOVER_CONFIG_CHECK, EPMPDebugEnable>
impl<const HANDOVER_CONFIG_CHECK: bool> TORUserPMP<{ TOR_USER_REGIONS_DEBUG_ENABLE }> for EarlGreyEPMP<HANDOVER_CONFIG_CHECK, EPMPDebugEnable>
Source§const CONST_ASSERT_CHECK: () = ()
const CONST_ASSERT_CHECK: () = ()
PMPUserMPU::new
. This can be used to, for instance, assert that the
number of userspace regions does not exceed the number of hardware
regions.Source§fn available_regions(&self) -> usize
fn available_regions(&self) -> usize
[0; MAX_REGIONS]
. Read more