From e93b9d59c3951c6ee6dd4eb63d90fdbaf291e909 Mon Sep 17 00:00:00 2001 From: Ioannis Glaropoulos Date: Tue, 4 Dec 2018 16:44:21 +0100 Subject: [PATCH] arch: arm: introduce/implement API to configure dynamic MPU regions This commit introduces an ARM API that allows the user to program a set of dynamic MPU regions at run-time. The API function is invoked every time the memory map needs to be re-programmed (for example at thread context-switch). The functionality is implementated in arm_core_mpu.c. Signed-off-by: Ioannis Glaropoulos --- arch/arm/core/cortex_m/mpu/arm_core_mpu.c | 124 +++++++++++++++++++ arch/arm/core/swap_helper.S | 25 ++-- arch/arm/core/userspace.S | 20 +-- arch/arm/include/kernel_arch_func.h | 10 +- include/arch/arm/cortex_m/mpu/arm_core_mpu.h | 13 ++ 5 files changed, 155 insertions(+), 37 deletions(-) diff --git a/arch/arm/core/cortex_m/mpu/arm_core_mpu.c b/arch/arm/core/cortex_m/mpu/arm_core_mpu.c index 0e92ed8aa76..3d1256d17be 100644 --- a/arch/arm/core/cortex_m/mpu/arm_core_mpu.c +++ b/arch/arm/core/cortex_m/mpu/arm_core_mpu.c @@ -16,7 +16,27 @@ #include LOG_MODULE_REGISTER(mpu); +/* + * Maximum number of dynamic memory partitions that may be supplied to the MPU + * driver for programming during run-time. Note that the actual number of the + * available MPU regions for dynamic programming depends on the number of the + * static MPU regions currently being programmed, and the total number of HW- + * available MPU regions. This macro is only used internally in function + * _arch_configure_dynamic_mpu_regions(), to reserve sufficient area for the + * array of dynamic regions passed to the underlying driver. + */ +#if defined(CONFIG_USERSPACE) +#define _MAX_DYNAMIC_MPU_REGIONS_NUM \ + CONFIG_MAX_DOMAIN_PARTITIONS + /* User thread stack */ 1 + \ + (IS_ENABLED(CONFIG_MPU_STACK_GUARD) ? 1 : 0) +#else +#define _MAX_DYNAMIC_MPU_REGIONS_NUM \ + (IS_ENABLED(CONFIG_MPU_STACK_GUARD) ? 1 : 0) +#endif /* CONFIG_USERSPACE */ +/* Convenience macros to denote the start address and the size of the system + * memory area, where dynamic memory regions may be programmed at run-time. + */ #if defined(CONFIG_APP_SHARED_MEM) #define _MPU_DYNAMIC_REGIONS_AREA_START ((u32_t)&_app_smem_start) #else @@ -102,6 +122,110 @@ void _arch_configure_static_mpu_regions(void) #endif /* CONFIG_MPU_REQUIRES_NON_OVERLAPPING_REGIONS */ } +/** + * @brief Use the HW-specific MPU driver to program + * the dynamic MPU regions. + * + * Program the dynamic MPU regions using the HW-specific MPU + * driver. This function is meant to be invoked every time the + * memory map is to be re-programmed, e.g during thread context + * switch, entering user mode, reconfiguring memory domain, etc. + * + * For some MPU architectures, such as the unmodified ARMv8-M MPU, + * the function must execute with MPU enabled. + */ +void _arch_configure_dynamic_mpu_regions(struct k_thread *thread) +{ + /* Define an array of k_mem_partition objects to hold the configuration + * of the respective dynamic MPU regions to be programmed for + * the given thread. The array of partitions (along with its + * actual size) will be supplied to the underlying MPU driver. + */ + struct k_mem_partition dynamic_regions[_MAX_DYNAMIC_MPU_REGIONS_NUM]; + + u8_t region_num = 0; + +#if defined(CONFIG_USERSPACE) + /* Memory domain */ + LOG_DBG("configure thread %p's domain", thread); + struct k_mem_domain *mem_domain = thread->mem_domain_info.mem_domain; + + if (mem_domain) { + LOG_DBG("configure domain: %p", mem_domain); + u32_t num_partitions = mem_domain->num_partitions; + struct k_mem_partition partition; + int i; + + LOG_DBG("configure domain: %p", mem_domain); + + for (i = 0; i < CONFIG_MAX_DOMAIN_PARTITIONS; i++) { + partition = mem_domain->partitions[i]; + if (partition.size == 0) { + /* Zero size indicates a non-existing + * memory partition. + */ + continue; + } + LOG_DBG("set region 0x%x 0x%x", + partition.start, partition.size); + __ASSERT(region_num < _MAX_DYNAMIC_MPU_REGIONS_NUM, + "Out-of-bounds error for dynamic region map."); + dynamic_regions[region_num] = partition; + + region_num++; + num_partitions--; + if (num_partitions == 0) { + break; + } + } + } + /* Thread user stack */ + LOG_DBG("configure user thread %p's context", thread); + if (thread->arch.priv_stack_start) { + u32_t base = (u32_t)thread->stack_obj; + u32_t size = thread->stack_info.size; +#if !defined(CONFIG_MPU_REQUIRES_POWER_OF_TWO_ALIGNMENT) + /* In user-mode the thread stack will include the (optional) + * guard area. For MPUs with arbitrary base address and limit + * it is essential to include this size increase, to avoid + * MPU faults. + */ + size += thread->stack_info.start - base; +#endif + __ASSERT(region_num < _MAX_DYNAMIC_MPU_REGIONS_NUM, + "Out-of-bounds error for dynamic region map."); + dynamic_regions[region_num] = (const struct k_mem_partition) + {base, size, K_MEM_PARTITION_P_RW_U_RW}; + + region_num++; + } +#endif /* CONFIG_USERSPACE */ + +#if defined(CONFIG_MPU_STACK_GUARD) + /* Privileged stack guard */ +#if defined(CONFIG_USERSPACE) + u32_t guard_start = thread->arch.priv_stack_start ? + (u32_t)thread->arch.priv_stack_start : + (u32_t)thread->stack_obj; +#else + u32_t guard_start = thread->stack_info.start; +#endif + __ASSERT(region_num < _MAX_DYNAMIC_MPU_REGIONS_NUM, + "Out-of-bounds error for dynamic region map."); + dynamic_regions[region_num] = (const struct k_mem_partition) + { + guard_start, + MPU_GUARD_ALIGN_AND_SIZE, + K_MEM_PARTITION_P_RO_U_NA + }; + + region_num++; +#endif /* CONFIG_MPU_STACK_GUARD */ + + /* Configure the dynamic MPU regions */ + arm_core_mpu_configure_dynamic_mpu_regions(dynamic_regions, + region_num); +} #if defined(CONFIG_MPU_STACK_GUARD) /* diff --git a/arch/arm/core/swap_helper.S b/arch/arm/core/swap_helper.S index 9ba857e2188..d8d0d3dfdd1 100644 --- a/arch/arm/core/swap_helper.S +++ b/arch/arm/core/swap_helper.S @@ -182,13 +182,14 @@ _thread_irq_disabled: vldmia r0, {s16-s31} #endif -#ifdef CONFIG_MPU_STACK_GUARD - /* r2 contains k_thread */ - add r0, r2, #0 - push {r2, lr} - blx configure_mpu_stack_guard - pop {r2, lr} -#endif /* CONFIG_MPU_STACK_GUARD */ +#if defined (CONFIG_ARM_MPU) + /* Re-program dynamic memory map */ + push {r2,lr} + ldr r0, =_kernel + ldr r0, [r0, #_kernel_offset_to_current] + bl _arch_configure_dynamic_mpu_regions + pop {r2,lr} +#endif #ifdef CONFIG_USERSPACE /* restore mode */ @@ -204,16 +205,6 @@ _thread_irq_disabled: */ isb - /* r2 contains k_thread */ - add r0, r2, #0 - push {r2, lr} - blx configure_mpu_mem_domain - pop {r2, lr} - - add r0, r2, #0 - push {r2, lr} - blx configure_mpu_user_context - pop {r2, lr} #endif /* load callee-saved + psp from thread */ diff --git a/arch/arm/core/userspace.S b/arch/arm/core/userspace.S index 6648df98f4f..300775a5a11 100644 --- a/arch/arm/core/userspace.S +++ b/arch/arm/core/userspace.S @@ -40,13 +40,13 @@ SECTION_FUNC(TEXT,_arm_userspace_enter) /* move user_entry to lr */ mov lr, r0 -#if defined (CONFIG_MPU_STACK_GUARD) - /* Re-program MPU to guard the privileged stack. */ - push {r1,r2,r3,lr} +#if defined (CONFIG_ARM_MPU) + /* Re-program dynamic memory map */ + push {r1,r2,r3,ip,lr} ldr r0, =_kernel ldr r0, [r0, #_kernel_offset_to_current] - bl configure_mpu_stack_guard - pop {r1,r2,r3,lr} + bl _arch_configure_dynamic_mpu_regions + pop {r1,r2,r3,ip,lr} #endif /* set stack to privileged stack */ @@ -97,16 +97,6 @@ SECTION_FUNC(TEXT,_arm_userspace_enter) #endif bl memset - /* setup arguments to configure_mpu_mem_domain */ - ldr r0, =_kernel - ldr r0, [r0, #_kernel_offset_to_current] - bl configure_mpu_mem_domain - - /* setup arguments to configure_mpu_user_context */ - ldr r0, =_kernel - ldr r0, [r0, #_kernel_offset_to_current] - bl configure_mpu_user_context - pop {r0,r1,r2,r3,ip,lr} /* r0 contains user stack start, ip contains user stack size */ diff --git a/arch/arm/include/kernel_arch_func.h b/arch/arm/include/kernel_arch_func.h index ca34776b888..29d8b4a018e 100644 --- a/arch/arm/include/kernel_arch_func.h +++ b/arch/arm/include/kernel_arch_func.h @@ -104,14 +104,14 @@ _arch_switch_to_main_thread(struct k_thread *main_thread, #error Unknown ARM architecture #endif /* CONFIG_ARMV6_M_ARMV8_M_BASELINE */ -#ifdef CONFIG_MPU_STACK_GUARD +#ifdef CONFIG_ARM_MPU /* - * if guard is enabled, make sure to set it before jumping to thread - * entry function - */ + * If stack protection is enabled, make sure to set it + * before jumping to thread entry function + */ "mov %%r0, %3 \t\n" "push {r2, lr} \t\n" - "blx configure_mpu_stack_guard \t\n" + "blx _arch_configure_dynamic_mpu_regions \t\n" "pop {r2, lr} \t\n" #endif /* branch to _thread_entry(_main, 0, 0, 0) */ diff --git a/include/arch/arm/cortex_m/mpu/arm_core_mpu.h b/include/arch/arm/cortex_m/mpu/arm_core_mpu.h index 154603e8392..79e759eec9f 100644 --- a/include/arch/arm/cortex_m/mpu/arm_core_mpu.h +++ b/include/arch/arm/cortex_m/mpu/arm_core_mpu.h @@ -24,6 +24,19 @@ extern "C" { */ void _arch_configure_static_mpu_regions(void); +/** + * @brief Use the HW-specific MPU driver to program + * the dynamic MPU regions. + * + * Program the dynamic MPU regions using the HW-specific MPU + * driver. This function is meant to be invoked every time the + * memory map is to be re-programmed, e.g during thread context + * switch, entering user mode, reconfiguring memory domain, etc. + * + * @param thread pointer to the current k_thread context + */ +void _arch_configure_dynamic_mpu_regions(struct k_thread *thread); + #if defined(CONFIG_MPU_STACK_GUARD) /** * @brief Configure MPU stack guard