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

armv8-m: making full SRAM run-time partitioning (MPU gap filling) optional #20152

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.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
23 changes: 23 additions & 0 deletions arch/Kconfig
Original file line number Diff line number Diff line change
Expand Up @@ -449,6 +449,29 @@ config MPU_REQUIRES_NON_OVERLAPPING_REGIONS
This option is enabled when the MPU requires the active (i.e. enabled)
MPU regions to be non-overlapping with each other.

config MPU_GAP_FILLING
bool "Force MPU to be filling in background memory regions"
depends on MPU_REQUIRES_NON_OVERLAPPING_REGIONS
depends on USERSPACE
help
This Kconfig option instructs the MPU driver to enforce
a full kernel SRAM partitioning, when it programs the
dynamic MPU regions (user thread stack, PRIV stack guard
and application memory domains) during context-switch. We
allow this to be a configurable option, in order to be able
to switch the option off and have an increased number of MPU
regions available for application memory domain programming.

Notes:
An increased number of MPU regions should only be required,
when building with USERSPACE support.

When the option is switched off, access to memory areas not
covered by explicit MPU regions is restricted to privileged
code on an ARCH-specific basis. Refer to ARCH-specific
documentation for more information on how this option is
used.

menuconfig FLOAT
bool "Floating point"
depends on CPU_HAS_FPU
Expand Down
29 changes: 26 additions & 3 deletions arch/arm/core/cortex_m/mpu/Kconfig
Original file line number Diff line number Diff line change
Expand Up @@ -14,10 +14,33 @@ config ARM_MPU
select MPU_REQUIRES_NON_OVERLAPPING_REGIONS if CPU_HAS_ARM_MPU && (ARMV8_M_BASELINE || ARMV8_M_MAINLINE)
help
MCU implements Memory Protection Unit.
Note that NXP MPU as well as ARMv8-M MPU does not require MPU regions

Notes:
The ARMv6-M and ARMv8-M MPU architecture requires a power-of-two
alignment of MPU region base address and size.

The NXP MPU as well as the ARMv8-M MPU do not require MPU regions
to have power-of-two alignment for base address and region size.
In addition to the above, ARMv8-M MPU requires the active MPU regions
be non-overlapping.

The ARMv8-M MPU requires the active MPU regions be non-overlapping.
As a result of this, the ARMv8-M MPU needs to fully partition the
memory map when programming dynamic memory regions (e.g. PRIV stack
guard, user thread stack, and application memory domains), if the
system requires PRIV access policy different from the access policy
of the ARMv8-M background memory map. The allication developer may
enforce full PRIV (kernel) memory partition by enabling the
CONFIG_MPU_GAP_FILLING option.
By not enforcing full partition, MPU may leave part of kernel
SRAM area covered only by the default ARMv8-M memory map. This
is fine for User Mode, since the background ARM map does not
allow nPRIV access at all. However, since the background map
policy allows instruction fetches by privileged code, forcing
this Kconfig option off prevents the system from directly
triggering MemManage exceptions upon accidental attempts to
execute code from SRAM in XIP builds.
Since this does not compromise User Mode, we make the skipping
of full partitioning the default behavior for the ARMv8-M MPU
driver.

config ARM_MPU_REGION_MIN_ALIGN_AND_SIZE
int
Expand Down
9 changes: 6 additions & 3 deletions arch/arm/core/cortex_m/mpu/arm_core_mpu_dev.h
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,8 @@ struct k_thread;
*
* @param mpu_regions_num the number of available HW MPU regions.
*/
#if defined(CONFIG_MPU_REQUIRES_NON_OVERLAPPING_REGIONS)
#if defined(CONFIG_MPU_REQUIRES_NON_OVERLAPPING_REGIONS) && \
defined(CONFIG_MPU_GAP_FILLING)
/*
* For ARM MPU architectures, where the domain partitions cannot be defined
* on top of the statically configured memory regions, the maximum number of
Expand All @@ -53,7 +54,8 @@ struct k_thread;
* @brief Maximum number of MPU regions required to configure a
* memory region for (user) Thread Stack.
*/
#if defined(CONFIG_MPU_REQUIRES_NON_OVERLAPPING_REGIONS)
#if defined(CONFIG_MPU_REQUIRES_NON_OVERLAPPING_REGIONS) && \
defined(CONFIG_MPU_GAP_FILLING)
/* When dynamic regions may not be defined on top of statically
* allocated memory regions, defining a region for a thread stack
* requires two additional MPU regions to be configured; one for
Expand All @@ -73,7 +75,8 @@ struct k_thread;
* @brief Maximum number of MPU regions required to configure a
* memory region for a (supervisor) Thread Stack Guard.
*/
#if defined(CONFIG_MPU_REQUIRES_NON_OVERLAPPING_REGIONS) \
#if (defined(CONFIG_MPU_REQUIRES_NON_OVERLAPPING_REGIONS) && \
defined(CONFIG_MPU_GAP_FILLING)) \
|| defined(CONFIG_CPU_HAS_NXP_MPU)
/*
* When dynamic regions may not be defined on top of statically
Expand Down
39 changes: 39 additions & 0 deletions arch/arm/core/cortex_m/mpu/arm_mpu.c
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,45 @@ static int mpu_configure_region(const u8_t index,
(const struct arm_mpu_region *)&region_conf);
}

#if !defined(CONFIG_MPU_REQUIRES_NON_OVERLAPPING_REGIONS) || \
!defined(CONFIG_MPU_GAP_FILLING)
/* This internal function programs a set of given MPU regions
* over a background memory area, optionally performing a
* sanity check of the memory regions to be programmed.
*/
static int mpu_configure_regions(const struct k_mem_partition
*regions[], u8_t regions_num, u8_t start_reg_index,
bool do_sanity_check)
{
int i;
int reg_index = start_reg_index;

for (i = 0; i < regions_num; i++) {
if (regions[i]->size == 0U) {
continue;
}
/* Non-empty region. */

if (do_sanity_check &&
(!mpu_partition_is_valid(regions[i]))) {
LOG_ERR("Partition %u: sanity check failed.", i);
return -EINVAL;
}

reg_index = mpu_configure_region(reg_index, regions[i]);

if (reg_index == -EINVAL) {
return reg_index;
}

/* Increment number of programmed MPU indices. */
reg_index++;
}

return reg_index;
}
#endif

/* ARM Core MPU Driver API Implementation for ARM MPU */

/**
Expand Down
34 changes: 1 addition & 33 deletions arch/arm/core/cortex_m/mpu/arm_mpu_v7_internal.h
Original file line number Diff line number Diff line change
Expand Up @@ -252,41 +252,9 @@ static inline int mpu_buffer_validate(void *addr, size_t size, int write)
static int mpu_configure_region(const u8_t index,
const struct k_mem_partition *new_region);

/* This internal function programs a set of given MPU regions
* over a background memory area, optionally performing a
* sanity check of the memory regions to be programmed.
*/
static int mpu_configure_regions(const struct k_mem_partition
*regions[], u8_t regions_num, u8_t start_reg_index,
bool do_sanity_check)
{
int i;
int reg_index = start_reg_index;

for (i = 0; i < regions_num; i++) {
if (regions[i]->size == 0U) {
continue;
}
/* Non-empty region. */

if (do_sanity_check &&
(!mpu_partition_is_valid(regions[i]))) {
LOG_ERR("Partition %u: sanity check failed.", i);
return -EINVAL;
}

reg_index = mpu_configure_region(reg_index, regions[i]);

if (reg_index == -EINVAL) {
return reg_index;
}

/* Increment number of programmed MPU indices. */
reg_index++;
}

return reg_index;
}
bool do_sanity_check);

/* This internal function programs the static MPU regions.
*
Expand Down
29 changes: 27 additions & 2 deletions arch/arm/core/cortex_m/mpu/arm_mpu_v8_internal.h
Original file line number Diff line number Diff line change
Expand Up @@ -318,11 +318,20 @@ static int region_allocate_and_init(const u8_t index,
static int mpu_configure_region(const u8_t index,
const struct k_mem_partition *new_region);

#if !defined(CONFIG_MPU_GAP_FILLING)
static int mpu_configure_regions(const struct k_mem_partition
*regions[], u8_t regions_num, u8_t start_reg_index,
bool do_sanity_check);
#endif

/* This internal function programs a set of given MPU regions
* over a background memory area, optionally performing a
* sanity check of the memory regions to be programmed.
*
* The function performs a full partition of the background memory
* area, effectively, leaving no space in this area uncovered by MPU.
*/
static int mpu_configure_regions(const struct k_mem_partition
static int mpu_configure_regions_and_partition(const struct k_mem_partition
*regions[], u8_t regions_num, u8_t start_reg_index,
bool do_sanity_check)
{
Expand Down Expand Up @@ -472,7 +481,7 @@ static int mpu_configure_static_mpu_regions(const struct k_mem_partition
ARG_UNUSED(background_area_base);
ARG_UNUSED(background_area_end);

mpu_reg_index = mpu_configure_regions(static_regions,
mpu_reg_index = mpu_configure_regions_and_partition(static_regions,
regions_num, mpu_reg_index, true);

static_regions_num = mpu_reg_index;
Expand Down Expand Up @@ -539,6 +548,7 @@ static int mpu_configure_dynamic_mpu_regions(const struct k_mem_partition
ARM_MPU_ClrRegion(i);
}

#if defined(CONFIG_MPU_GAP_FILLING)
/* Reset MPU regions inside which dynamic memory regions may
* be programmed.
*/
Expand All @@ -551,10 +561,25 @@ static int mpu_configure_dynamic_mpu_regions(const struct k_mem_partition
* forming a full partition of the background area, specified by the
* given boundaries.
*/
mpu_reg_index = mpu_configure_regions_and_partition(dynamic_regions,
regions_num, mpu_reg_index, true);
#else

/* We are going to skip the full partition of the background areas.
* So we can disable MPU regions inside which dynamic memroy regions
* may be programmed.
*/
for (int i = 0; i < MPU_DYNAMIC_REGION_AREAS_NUM; i++) {
ARM_MPU_ClrRegion(dyn_reg_info[i].index);
}

/* The dynamic regions are now programmed on top of
* existing SRAM region configuration.
*/
mpu_reg_index = mpu_configure_regions(dynamic_regions,
regions_num, mpu_reg_index, true);

#endif /* CONFIG_MPU_GAP_FILLING */
return mpu_reg_index;
}

Expand Down
6 changes: 6 additions & 0 deletions tests/kernel/mem_protect/mem_protect/testcase.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -4,3 +4,9 @@ tests:
filter: CONFIG_ARCH_HAS_USERSPACE
platform_exclude: twr_ke18f
tags: kernel security userspace ignore_faults
kernel.memory_protection.armv8m_gap_filling:
arch_whitelist: arm
filter: CONFIG_ARCH_HAS_USERSPACE and CONFIG_MPU_REQUIRES_NON_OVERLAPPING_REGIONS
extra_args: CONFIG_MPU_GAP_FILLING=y
min_ram: 32
tags: kernel security userspace ignore_faults
6 changes: 6 additions & 0 deletions tests/kernel/mem_protect/userspace/testcase.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -3,3 +3,9 @@ tests:
filter: CONFIG_ARCH_HAS_USERSPACE
tags: kernel security userspace ignore_faults
min_ram: 36
kernel.memory_protection.userspace.armv8m_gap_filling:
arch_whitelist: arm
filter: CONFIG_ARCH_HAS_USERSPACE and CONFIG_MPU_REQUIRES_NON_OVERLAPPING_REGIONS
extra_args: CONFIG_MPU_GAP_FILLING=y
tags: kernel security userspace ignore_faults
min_ram: 36