diff --git a/arch/Kconfig b/arch/Kconfig index a801946ed828b9..6cf29205dfcee1 100644 --- a/arch/Kconfig +++ b/arch/Kconfig @@ -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 diff --git a/arch/arm/core/cortex_m/mpu/Kconfig b/arch/arm/core/cortex_m/mpu/Kconfig index f55604edc7e515..c0dbae36e6cb73 100644 --- a/arch/arm/core/cortex_m/mpu/Kconfig +++ b/arch/arm/core/cortex_m/mpu/Kconfig @@ -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 diff --git a/arch/arm/core/cortex_m/mpu/arm_core_mpu_dev.h b/arch/arm/core/cortex_m/mpu/arm_core_mpu_dev.h index d55a06a9366ed0..88f2392e98b3b8 100644 --- a/arch/arm/core/cortex_m/mpu/arm_core_mpu_dev.h +++ b/arch/arm/core/cortex_m/mpu/arm_core_mpu_dev.h @@ -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 @@ -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 @@ -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 diff --git a/arch/arm/core/cortex_m/mpu/arm_mpu.c b/arch/arm/core/cortex_m/mpu/arm_mpu.c index b847450f7f6563..2705a0e0ebfef9 100644 --- a/arch/arm/core/cortex_m/mpu/arm_mpu.c +++ b/arch/arm/core/cortex_m/mpu/arm_mpu.c @@ -100,6 +100,45 @@ static int mpu_configure_region(const u8_t index, (const struct arm_mpu_region *)®ion_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 */ /** diff --git a/arch/arm/core/cortex_m/mpu/arm_mpu_v7_internal.h b/arch/arm/core/cortex_m/mpu/arm_mpu_v7_internal.h index d44a46efec3d4e..49a702ec6d2508 100644 --- a/arch/arm/core/cortex_m/mpu/arm_mpu_v7_internal.h +++ b/arch/arm/core/cortex_m/mpu/arm_mpu_v7_internal.h @@ -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. * diff --git a/arch/arm/core/cortex_m/mpu/arm_mpu_v8_internal.h b/arch/arm/core/cortex_m/mpu/arm_mpu_v8_internal.h index f26060cf91ceea..31ddca6b309602 100644 --- a/arch/arm/core/cortex_m/mpu/arm_mpu_v8_internal.h +++ b/arch/arm/core/cortex_m/mpu/arm_mpu_v8_internal.h @@ -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) { @@ -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; @@ -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. */ @@ -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; } diff --git a/tests/kernel/mem_protect/mem_protect/testcase.yaml b/tests/kernel/mem_protect/mem_protect/testcase.yaml index 36e8e2e5f682ce..d6b4375897ef74 100644 --- a/tests/kernel/mem_protect/mem_protect/testcase.yaml +++ b/tests/kernel/mem_protect/mem_protect/testcase.yaml @@ -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 diff --git a/tests/kernel/mem_protect/userspace/testcase.yaml b/tests/kernel/mem_protect/userspace/testcase.yaml index f907b014253538..9d01b40cd4d81a 100644 --- a/tests/kernel/mem_protect/userspace/testcase.yaml +++ b/tests/kernel/mem_protect/userspace/testcase.yaml @@ -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