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 <Ioannis.Glaropoulos@nordicsemi.no>
This commit is contained in:
Ioannis Glaropoulos 2018-12-04 16:44:21 +01:00 committed by Andrew Boie
commit e93b9d59c3
5 changed files with 155 additions and 37 deletions

View file

@ -16,7 +16,27 @@
#include <logging/log.h> #include <logging/log.h>
LOG_MODULE_REGISTER(mpu); 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) #if defined(CONFIG_APP_SHARED_MEM)
#define _MPU_DYNAMIC_REGIONS_AREA_START ((u32_t)&_app_smem_start) #define _MPU_DYNAMIC_REGIONS_AREA_START ((u32_t)&_app_smem_start)
#else #else
@ -102,6 +122,110 @@ void _arch_configure_static_mpu_regions(void)
#endif /* CONFIG_MPU_REQUIRES_NON_OVERLAPPING_REGIONS */ #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) #if defined(CONFIG_MPU_STACK_GUARD)
/* /*

View file

@ -182,13 +182,14 @@ _thread_irq_disabled:
vldmia r0, {s16-s31} vldmia r0, {s16-s31}
#endif #endif
#ifdef CONFIG_MPU_STACK_GUARD #if defined (CONFIG_ARM_MPU)
/* r2 contains k_thread */ /* Re-program dynamic memory map */
add r0, r2, #0 push {r2,lr}
push {r2, lr} ldr r0, =_kernel
blx configure_mpu_stack_guard ldr r0, [r0, #_kernel_offset_to_current]
pop {r2, lr} bl _arch_configure_dynamic_mpu_regions
#endif /* CONFIG_MPU_STACK_GUARD */ pop {r2,lr}
#endif
#ifdef CONFIG_USERSPACE #ifdef CONFIG_USERSPACE
/* restore mode */ /* restore mode */
@ -204,16 +205,6 @@ _thread_irq_disabled:
*/ */
isb 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 #endif
/* load callee-saved + psp from thread */ /* load callee-saved + psp from thread */

View file

@ -40,13 +40,13 @@ SECTION_FUNC(TEXT,_arm_userspace_enter)
/* move user_entry to lr */ /* move user_entry to lr */
mov lr, r0 mov lr, r0
#if defined (CONFIG_MPU_STACK_GUARD) #if defined (CONFIG_ARM_MPU)
/* Re-program MPU to guard the privileged stack. */ /* Re-program dynamic memory map */
push {r1,r2,r3,lr} push {r1,r2,r3,ip,lr}
ldr r0, =_kernel ldr r0, =_kernel
ldr r0, [r0, #_kernel_offset_to_current] ldr r0, [r0, #_kernel_offset_to_current]
bl configure_mpu_stack_guard bl _arch_configure_dynamic_mpu_regions
pop {r1,r2,r3,lr} pop {r1,r2,r3,ip,lr}
#endif #endif
/* set stack to privileged stack */ /* set stack to privileged stack */
@ -97,16 +97,6 @@ SECTION_FUNC(TEXT,_arm_userspace_enter)
#endif #endif
bl memset 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} pop {r0,r1,r2,r3,ip,lr}
/* r0 contains user stack start, ip contains user stack size */ /* r0 contains user stack start, ip contains user stack size */

View file

@ -104,14 +104,14 @@ _arch_switch_to_main_thread(struct k_thread *main_thread,
#error Unknown ARM architecture #error Unknown ARM architecture
#endif /* CONFIG_ARMV6_M_ARMV8_M_BASELINE */ #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 * If stack protection is enabled, make sure to set it
* entry function * before jumping to thread entry function
*/ */
"mov %%r0, %3 \t\n" "mov %%r0, %3 \t\n"
"push {r2, lr} \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" "pop {r2, lr} \t\n"
#endif #endif
/* branch to _thread_entry(_main, 0, 0, 0) */ /* branch to _thread_entry(_main, 0, 0, 0) */

View file

@ -24,6 +24,19 @@ extern "C" {
*/ */
void _arch_configure_static_mpu_regions(void); 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) #if defined(CONFIG_MPU_STACK_GUARD)
/** /**
* @brief Configure MPU stack guard * @brief Configure MPU stack guard