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>
|
#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)
|
||||||
/*
|
/*
|
||||||
|
|
|
@ -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}
|
||||||
blx configure_mpu_stack_guard
|
ldr r0, =_kernel
|
||||||
|
ldr r0, [r0, #_kernel_offset_to_current]
|
||||||
|
bl _arch_configure_dynamic_mpu_regions
|
||||||
pop {r2,lr}
|
pop {r2,lr}
|
||||||
#endif /* CONFIG_MPU_STACK_GUARD */
|
#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 */
|
||||||
|
|
|
@ -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 */
|
||||||
|
|
|
@ -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) */
|
||||||
|
|
|
@ -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
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue