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:
parent
0e528ec11b
commit
e93b9d59c3
5 changed files with 155 additions and 37 deletions
|
@ -16,7 +16,27 @@
|
|||
#include <logging/log.h>
|
||||
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)
|
||||
/*
|
||||
|
|
|
@ -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 */
|
||||
|
|
|
@ -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 */
|
||||
|
|
|
@ -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) */
|
||||
|
|
|
@ -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
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue