From 0b49de6746de3548c20aa7c1091834f1ee7b0732 Mon Sep 17 00:00:00 2001 From: Ioannis Glaropoulos Date: Thu, 24 Oct 2019 15:17:09 +0200 Subject: [PATCH 1/4] arch: arm: mpu: introduce option to skip background MPU region fill We introduce MPU_GAP_FILLING Kconfig option that instructs the MPU driver to enforce a full SRAM partitioning, when it programs the dynamic MPU regions (user thread stack, PRIV stack guard and application memory domains) at context-switch. We allow this to be configurable, in order to increase the number of MPU regions available for application memory domain programming. This option is introduced in arch/Kconfig, as it is expected to serve as a cross-ARCH symbol. The option can be set by the user during build configuration. By not enforcing full partition, we may leave part of kernel SRAM area covered only by the default ARM memory map. This is fine for User Mode, since the background ARM map does not allow nPRIV access at all. The difference is that kernel code will be able to attempt fetching instructions from kernel SRAM area without this leading directly to a MemManage exception. Since this does not compromize User Mode, we make the skipping of full partitioning the default behavior for the ARMv8-M MPU driver. The application developer may be able to overwrite this. In the wake of this change we update the macro definitions in arm_core_mpu_dev.h that derive the maximum number of MPU regions for application memory domains. Signed-off-by: Ioannis Glaropoulos --- arch/Kconfig | 23 +++++++++++++++ arch/arm/core/cortex_m/mpu/Kconfig | 29 +++++++++++++++++-- arch/arm/core/cortex_m/mpu/arm_core_mpu_dev.h | 9 ++++-- 3 files changed, 55 insertions(+), 6 deletions(-) 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..4b4df5ddc84462 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_SKIP_BACKGROUND_SRAM_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_SKIP_BACKGROUND_SRAM_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_SKIP_BACKGROUND_SRAM_FILLING)) \ || defined(CONFIG_CPU_HAS_NXP_MPU) /* * When dynamic regions may not be defined on top of statically From 9063f8651cb337aa60d94d5d0b62f5dd15f73fea Mon Sep 17 00:00:00 2001 From: Ioannis Glaropoulos Date: Fri, 25 Oct 2019 14:25:29 +0200 Subject: [PATCH 2/4] arch: arm: mpu: move mpu_configure_regions(.) in arm_mpu.c This commit moves the function mpu_configure_regions(.) from arm_mpu_v7_internal.h to arm_mpu.c. The function is to be used by the both ARMv7-M MPU driver, as well as the ARMv8-M MPU driver (when it behaves like the ARMv7-M driver). Signed-off-by: Ioannis Glaropoulos --- arch/arm/core/cortex_m/mpu/arm_mpu.c | 39 +++++++++++++++++++ .../core/cortex_m/mpu/arm_mpu_v7_internal.h | 34 +--------------- 2 files changed, 40 insertions(+), 33 deletions(-) diff --git a/arch/arm/core/cortex_m/mpu/arm_mpu.c b/arch/arm/core/cortex_m/mpu/arm_mpu.c index b847450f7f6563..6d2ef6839ec9dc 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_SRAM_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. * From 07bbd4e7add333fe810578e0f3f681413a9e68e3 Mon Sep 17 00:00:00 2001 From: Ioannis Glaropoulos Date: Fri, 25 Oct 2019 14:45:49 +0200 Subject: [PATCH 3/4] arch: arm: mpu: allow optional region gap filling in the ARMv8-M MPU We allow the run-time, full paritioning of the SRAM space by the ARMv8-M MPU driver to be an optional feature. Signed-off-by: Ioannis Glaropoulos --- arch/arm/core/cortex_m/mpu/arm_core_mpu_dev.h | 6 ++-- arch/arm/core/cortex_m/mpu/arm_mpu.c | 2 +- .../core/cortex_m/mpu/arm_mpu_v8_internal.h | 29 +++++++++++++++++-- 3 files changed, 31 insertions(+), 6 deletions(-) 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 4b4df5ddc84462..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 @@ -29,7 +29,7 @@ struct k_thread; * @param mpu_regions_num the number of available HW MPU regions. */ #if defined(CONFIG_MPU_REQUIRES_NON_OVERLAPPING_REGIONS) && \ - !defined(CONFIG_MPU_SKIP_BACKGROUND_SRAM_FILLING) + 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 @@ -55,7 +55,7 @@ struct k_thread; * memory region for (user) Thread Stack. */ #if defined(CONFIG_MPU_REQUIRES_NON_OVERLAPPING_REGIONS) && \ - !defined(CONFIG_MPU_SKIP_BACKGROUND_SRAM_FILLING) + 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 @@ -76,7 +76,7 @@ struct k_thread; * memory region for a (supervisor) Thread Stack Guard. */ #if (defined(CONFIG_MPU_REQUIRES_NON_OVERLAPPING_REGIONS) && \ - !defined(CONFIG_MPU_SKIP_BACKGROUND_SRAM_FILLING)) \ + 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 6d2ef6839ec9dc..2705a0e0ebfef9 100644 --- a/arch/arm/core/cortex_m/mpu/arm_mpu.c +++ b/arch/arm/core/cortex_m/mpu/arm_mpu.c @@ -101,7 +101,7 @@ static int mpu_configure_region(const u8_t index, } #if !defined(CONFIG_MPU_REQUIRES_NON_OVERLAPPING_REGIONS) || \ - !defined(CONFIG_MPU_SRAM_GAP_FILLING) + !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. 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; } From 17e251f1d480c02018f1f6589f15584fab457f4f Mon Sep 17 00:00:00 2001 From: Ioannis Glaropoulos Date: Mon, 28 Oct 2019 06:54:54 +0100 Subject: [PATCH 4/4] tests: kernel: mem_protect: run tests with ARMV8-M MPU gap filling We add a new test-case for the mem_protect and userspace tests, to test the ARMv8-M MPU driver without the skipping of full SRAM partitioning (i.e. gap filling). Signed-off-by: Ioannis Glaropoulos --- tests/kernel/mem_protect/mem_protect/testcase.yaml | 6 ++++++ tests/kernel/mem_protect/userspace/testcase.yaml | 6 ++++++ 2 files changed, 12 insertions(+) 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