kernel: add memory domain APIs
Add the following application-facing memory domain APIs: k_mem_domain_init() - to initialize a memory domain k_mem_domain_destroy() - to destroy a memory domain k_mem_domain_add_partition() - to add a partition into a domain k_mem_domain_remove_partition() - to remove a partition from a domain k_mem_domain_add_thread() - to add a thread into a domain k_mem_domain_remove_thread() - to remove a thread from a domain A memory domain would contain some number of memory partitions. A memory partition is a memory region (might be RAM, peripheral registers, flash...) with specific attributes (access permission, e.g. privileged read/write, unprivileged read-only, execute never...). Memory partitions would be defined by set of MPU regions or MMU tables underneath. A thread could only belong to a single memory domain any point in time but a memory domain could contain multiple threads. Threads in the same memory domain would have the same access permission to the memory partitions belong to the memory domain. The memory domain APIs are used by unprivileged threads to share data to the threads in the same memory and protect sensitive data from threads outside their domain. It is not only for improving the security but also useful for debugging (unexpected access would cause exception). Jira: ZEP-2281 Signed-off-by: Chunlin Han <chunlin.han@linaro.org>
This commit is contained in:
parent
de85fdedf9
commit
e9c9702818
17 changed files with 928 additions and 15 deletions
|
@ -10,6 +10,7 @@
|
|||
#include <soc.h>
|
||||
#include <arch/arm/cortex_m/cmsis.h>
|
||||
#include <arch/arm/cortex_m/mpu/arm_core_mpu.h>
|
||||
#include <logging/sys_log.h>
|
||||
|
||||
#if defined(CONFIG_MPU_STACK_GUARD)
|
||||
/*
|
||||
|
@ -29,3 +30,26 @@ void configure_mpu_stack_guard(struct k_thread *thread)
|
|||
arm_core_mpu_enable();
|
||||
}
|
||||
#endif
|
||||
|
||||
#if defined(CONFIG_USERSPACE)
|
||||
/*
|
||||
* @brief Configure MPU memory domain
|
||||
*
|
||||
* This function configures per thread memory domain reprogramming the MPU.
|
||||
* The functionality is meant to be used during context switch.
|
||||
*
|
||||
* @param thread thread info data structure.
|
||||
*/
|
||||
void configure_mpu_mem_domain(struct k_thread *thread)
|
||||
{
|
||||
SYS_LOG_DBG("configure thread %p's domain", thread);
|
||||
arm_core_mpu_disable();
|
||||
arm_core_mpu_configure_mem_domain(thread->mem_domain_info.mem_domain);
|
||||
arm_core_mpu_enable();
|
||||
}
|
||||
|
||||
int _arch_mem_domain_max_partitions_get(void)
|
||||
{
|
||||
return arm_core_mpu_get_max_domain_partition_regions();
|
||||
}
|
||||
#endif
|
||||
|
|
|
@ -69,6 +69,77 @@ static void _region_init(u32_t index, u32_t region_addr,
|
|||
ARM_MPU_DEV->rasr = region_attr | REGION_ENABLE;
|
||||
}
|
||||
|
||||
/**
|
||||
* This internal function is utilized by the MPU driver to parse the intent
|
||||
* type (i.e. THREAD_STACK_REGION) and return the correct region index.
|
||||
*/
|
||||
static inline u32_t _get_region_index_by_type(u32_t type)
|
||||
{
|
||||
/*
|
||||
* The new MPU regions are allocated per type after the statically
|
||||
* configured regions. The type is one-indexed rather than
|
||||
* zero-indexed, therefore we need to subtract by one to get the region
|
||||
* index.
|
||||
*/
|
||||
switch (type) {
|
||||
case THREAD_STACK_REGION:
|
||||
return mpu_config.num_regions + type - 1;
|
||||
case THREAD_STACK_GUARD_REGION:
|
||||
return mpu_config.num_regions + type - 1;
|
||||
case THREAD_DOMAIN_PARTITION_REGION:
|
||||
#if defined(CONFIG_MPU_STACK_GUARD)
|
||||
return mpu_config.num_regions + type - 1;
|
||||
#else
|
||||
/*
|
||||
* Start domain partition region from stack guard region
|
||||
* since stack guard is not enabled.
|
||||
*/
|
||||
return mpu_config.num_regions + type - 2;
|
||||
#endif
|
||||
default:
|
||||
__ASSERT(0, "Unsupported type");
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
static inline u32_t round_up_to_next_power_of_two(u32_t v)
|
||||
{
|
||||
v--;
|
||||
v |= v >> 1;
|
||||
v |= v >> 2;
|
||||
v |= v >> 4;
|
||||
v |= v >> 8;
|
||||
v |= v >> 16;
|
||||
v++;
|
||||
|
||||
return v;
|
||||
}
|
||||
|
||||
/**
|
||||
* This internal function converts the region size to
|
||||
* the SIZE field value of MPU_RASR.
|
||||
*/
|
||||
static inline u32_t _size_to_mpu_rasr_size(u32_t size)
|
||||
{
|
||||
/* The minimal supported region size is 32 bytes */
|
||||
if (size <= 32) {
|
||||
return REGION_32B;
|
||||
}
|
||||
|
||||
/*
|
||||
* A size value greater than 2^31 could not be handled by
|
||||
* round_up_to_next_power_of_two() properly. We handle
|
||||
* it separately here.
|
||||
*/
|
||||
if (size > (1 << 31)) {
|
||||
return REGION_4G;
|
||||
}
|
||||
|
||||
size = round_up_to_next_power_of_two(size);
|
||||
|
||||
return (find_msb_set(size) - 2) << 1;
|
||||
}
|
||||
|
||||
/* ARM Core MPU Driver API Implementation for ARM MPU */
|
||||
|
||||
/**
|
||||
|
@ -107,13 +178,7 @@ void arm_core_mpu_disable(void)
|
|||
void arm_core_mpu_configure(u8_t type, u32_t base, u32_t size)
|
||||
{
|
||||
SYS_LOG_DBG("Region info: 0x%x 0x%x", base, size);
|
||||
/*
|
||||
* The new MPU regions are allocated per type after the statically
|
||||
* configured regions. The type is one-indexed rather than
|
||||
* zero-indexed, therefore we need to subtract by one to get the region
|
||||
* index.
|
||||
*/
|
||||
u32_t region_index = mpu_config.num_regions + type - 1;
|
||||
u32_t region_index = _get_region_index_by_type(type);
|
||||
u32_t region_attr = _get_region_attr_by_type(type, size);
|
||||
|
||||
/* ARM MPU supports up to 16 Regions */
|
||||
|
@ -124,6 +189,94 @@ void arm_core_mpu_configure(u8_t type, u32_t base, u32_t size)
|
|||
_region_init(region_index, base, region_attr);
|
||||
}
|
||||
|
||||
#if defined(CONFIG_USERSPACE)
|
||||
/**
|
||||
* @brief configure MPU regions for the memory partitions of the memory domain
|
||||
*
|
||||
* @param mem_domain memory domain that thread belongs to
|
||||
*/
|
||||
void arm_core_mpu_configure_mem_domain(struct k_mem_domain *mem_domain)
|
||||
{
|
||||
u32_t region_index =
|
||||
_get_region_index_by_type(THREAD_DOMAIN_PARTITION_REGION);
|
||||
u32_t region_attr;
|
||||
u32_t num_partitions;
|
||||
struct k_mem_partition *pparts;
|
||||
|
||||
if (mem_domain) {
|
||||
SYS_LOG_DBG("configure domain: %p", mem_domain);
|
||||
num_partitions = mem_domain->num_partitions;
|
||||
pparts = mem_domain->partitions;
|
||||
} else {
|
||||
SYS_LOG_DBG("disable domain partition regions");
|
||||
num_partitions = 0;
|
||||
pparts = NULL;
|
||||
}
|
||||
|
||||
for (; region_index < _get_num_regions(); region_index++) {
|
||||
if (num_partitions && pparts->size) {
|
||||
SYS_LOG_DBG("set region 0x%x 0x%x 0x%x",
|
||||
region_index, pparts->start, pparts->size);
|
||||
region_attr = pparts->attr |
|
||||
_size_to_mpu_rasr_size(pparts->size);
|
||||
_region_init(region_index, pparts->start, region_attr);
|
||||
num_partitions--;
|
||||
} else {
|
||||
SYS_LOG_DBG("disable region 0x%x", region_index);
|
||||
/* Disable region */
|
||||
ARM_MPU_DEV->rnr = region_index;
|
||||
ARM_MPU_DEV->rbar = 0;
|
||||
ARM_MPU_DEV->rasr = 0;
|
||||
}
|
||||
pparts++;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief configure MPU region for a single memory partition
|
||||
*
|
||||
* @param part_index memory partition index
|
||||
* @param part memory partition info
|
||||
*/
|
||||
void arm_core_mpu_configure_mem_partition(u32_t part_index,
|
||||
struct k_mem_partition *part)
|
||||
{
|
||||
u32_t region_index =
|
||||
_get_region_index_by_type(THREAD_DOMAIN_PARTITION_REGION);
|
||||
u32_t region_attr;
|
||||
|
||||
SYS_LOG_DBG("configure partition index: %u", part_index);
|
||||
|
||||
if (part) {
|
||||
SYS_LOG_DBG("set region 0x%x 0x%x 0x%x",
|
||||
region_index + part_index, part->start, part->size);
|
||||
region_attr = part->attr | _size_to_mpu_rasr_size(part->size);
|
||||
_region_init(region_index + part_index, part->start,
|
||||
region_attr);
|
||||
} else {
|
||||
SYS_LOG_DBG("disable region 0x%x", region_index + part_index);
|
||||
/* Disable region */
|
||||
ARM_MPU_DEV->rnr = region_index + part_index;
|
||||
ARM_MPU_DEV->rbar = 0;
|
||||
ARM_MPU_DEV->rasr = 0;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief get the maximum number of free regions for memory domain partitions
|
||||
*/
|
||||
int arm_core_mpu_get_max_domain_partition_regions(void)
|
||||
{
|
||||
/*
|
||||
* Subtract the start of domain partition regions from total regions
|
||||
* will get the maximum number of free regions for memory domain
|
||||
* partitions.
|
||||
*/
|
||||
return _get_num_regions() -
|
||||
_get_region_index_by_type(THREAD_DOMAIN_PARTITION_REGION);
|
||||
}
|
||||
#endif /* CONFIG_USERSPACE */
|
||||
|
||||
/* ARM MPU Driver Initial Setup */
|
||||
|
||||
/*
|
||||
|
|
|
@ -79,6 +79,39 @@ static void _region_init(u32_t index, u32_t region_base,
|
|||
SYSMPU->WORD[index][3]);
|
||||
}
|
||||
|
||||
/**
|
||||
* This internal function is utilized by the MPU driver to parse the intent
|
||||
* type (i.e. THREAD_STACK_REGION) and return the correct region index.
|
||||
*/
|
||||
static inline u32_t _get_region_index_by_type(u32_t type)
|
||||
{
|
||||
/*
|
||||
* The new MPU regions are allocated per type after the statically
|
||||
* configured regions. The type is one-indexed rather than
|
||||
* zero-indexed, therefore we need to subtract by one to get the region
|
||||
* index.
|
||||
*/
|
||||
switch (type) {
|
||||
case THREAD_STACK_REGION:
|
||||
return mpu_config.num_regions + type - 1;
|
||||
case THREAD_STACK_GUARD_REGION:
|
||||
return mpu_config.num_regions + type - 1;
|
||||
case THREAD_DOMAIN_PARTITION_REGION:
|
||||
#if defined(CONFIG_MPU_STACK_GUARD)
|
||||
return mpu_config.num_regions + type - 1;
|
||||
#else
|
||||
/*
|
||||
* Start domain partition region from stack guard region
|
||||
* since stack guard is not enabled.
|
||||
*/
|
||||
return mpu_config.num_regions + type - 2;
|
||||
#endif
|
||||
default:
|
||||
__ASSERT(0, "Unsupported type");
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
/* ARM Core MPU Driver API Implementation for NXP MPU */
|
||||
|
||||
/**
|
||||
|
@ -119,13 +152,7 @@ void arm_core_mpu_disable(void)
|
|||
void arm_core_mpu_configure(u8_t type, u32_t base, u32_t size)
|
||||
{
|
||||
SYS_LOG_DBG("Region info: 0x%x 0x%x", base, size);
|
||||
/*
|
||||
* The new MPU regions are allocated per type after the statically
|
||||
* configured regions. The type is one-indexed rather than
|
||||
* zero-indexed, therefore we need to subtract by one to get the region
|
||||
* index.
|
||||
*/
|
||||
u32_t region_index = mpu_config.num_regions + type - 1;
|
||||
u32_t region_index = _get_region_index_by_type(type);
|
||||
u32_t region_attr = _get_region_attr_by_type(type);
|
||||
u32_t last_region = _get_num_regions() - 1;
|
||||
|
||||
|
@ -181,6 +208,104 @@ void arm_core_mpu_configure(u8_t type, u32_t base, u32_t size)
|
|||
|
||||
}
|
||||
|
||||
#if defined(CONFIG_USERSPACE)
|
||||
/**
|
||||
* @brief configure MPU regions for the memory partitions of the memory domain
|
||||
*
|
||||
* @param mem_domain memory domain that thread belongs to
|
||||
*/
|
||||
void arm_core_mpu_configure_mem_domain(struct k_mem_domain *mem_domain)
|
||||
{
|
||||
u32_t region_index =
|
||||
_get_region_index_by_type(THREAD_DOMAIN_PARTITION_REGION);
|
||||
u32_t region_attr;
|
||||
u32_t num_partitions;
|
||||
struct k_mem_partition *pparts;
|
||||
|
||||
if (mem_domain) {
|
||||
SYS_LOG_DBG("configure domain: %p", mem_domain);
|
||||
num_partitions = mem_domain->num_partitions;
|
||||
pparts = mem_domain->partitions;
|
||||
} else {
|
||||
SYS_LOG_DBG("disable domain partition regions");
|
||||
num_partitions = 0;
|
||||
pparts = NULL;
|
||||
}
|
||||
|
||||
/*
|
||||
* Don't touch the last region, it is reserved for SRAM_1 region.
|
||||
* See comments in arm_core_mpu_configure().
|
||||
*/
|
||||
for (; region_index < _get_num_regions() - 1; region_index++) {
|
||||
if (num_partitions && pparts->size) {
|
||||
SYS_LOG_DBG("set region 0x%x 0x%x 0x%x",
|
||||
region_index, pparts->start, pparts->size);
|
||||
region_attr = pparts->attr;
|
||||
_region_init(region_index, pparts->start,
|
||||
ENDADDR_ROUND(pparts->start+pparts->size),
|
||||
region_attr);
|
||||
num_partitions--;
|
||||
} else {
|
||||
SYS_LOG_DBG("disable region 0x%x", region_index);
|
||||
/* Disable region */
|
||||
SYSMPU->WORD[region_index][0] = 0;
|
||||
SYSMPU->WORD[region_index][1] = 0;
|
||||
SYSMPU->WORD[region_index][2] = 0;
|
||||
SYSMPU->WORD[region_index][3] = 0;
|
||||
}
|
||||
pparts++;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief configure MPU region for a single memory partition
|
||||
*
|
||||
* @param part_index memory partition index
|
||||
* @param part memory partition info
|
||||
*/
|
||||
void arm_core_mpu_configure_mem_partition(u32_t part_index,
|
||||
struct k_mem_partition *part)
|
||||
{
|
||||
u32_t region_index =
|
||||
_get_region_index_by_type(THREAD_DOMAIN_PARTITION_REGION);
|
||||
u32_t region_attr;
|
||||
|
||||
SYS_LOG_DBG("configure partition index: %u", part_index);
|
||||
|
||||
if (part) {
|
||||
SYS_LOG_DBG("set region 0x%x 0x%x 0x%x",
|
||||
region_index + part_index, part->start, part->size);
|
||||
region_attr = part->attr;
|
||||
_region_init(region_index + part_index, part->start,
|
||||
ENDADDR_ROUND(part->start + part->size),
|
||||
region_attr);
|
||||
} else {
|
||||
SYS_LOG_DBG("disable region 0x%x", region_index);
|
||||
/* Disable region */
|
||||
SYSMPU->WORD[region_index + part_index][0] = 0;
|
||||
SYSMPU->WORD[region_index + part_index][1] = 0;
|
||||
SYSMPU->WORD[region_index + part_index][2] = 0;
|
||||
SYSMPU->WORD[region_index + part_index][3] = 0;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief get the maximum number of free regions for memory domain partitions
|
||||
*/
|
||||
int arm_core_mpu_get_max_domain_partition_regions(void)
|
||||
{
|
||||
/*
|
||||
* Subtract the start of domain partition regions from total regions
|
||||
* should get the maximum number of free regions for memory domain
|
||||
* partitions. But we need to consume an extra 1 region to make
|
||||
* stack/stack guard protection work properly.
|
||||
* See the comments in arm_core_mpu_configure().
|
||||
*/
|
||||
return _get_num_regions() -
|
||||
_get_region_index_by_type(THREAD_DOMAIN_PARTITION_REGION) - 1;
|
||||
}
|
||||
#endif /* CONFIG_USERSPACE */
|
||||
|
||||
/* NXP MPU Driver Initial Setup */
|
||||
|
||||
/*
|
||||
|
|
|
@ -175,6 +175,14 @@ _thread_irq_disabled:
|
|||
pop {r2, lr}
|
||||
#endif /* CONFIG_MPU_STACK_GUARD */
|
||||
|
||||
#ifdef CONFIG_USERSPACE
|
||||
/* r2 contains k_thread */
|
||||
add r0, r2, #0
|
||||
push {r2, lr}
|
||||
blx configure_mpu_mem_domain
|
||||
pop {r2, lr}
|
||||
#endif /* CONFIG_USERSPACE */
|
||||
|
||||
/* load callee-saved + psp from thread */
|
||||
add r0, r2, #_thread_offset_to_callee_saved
|
||||
ldmia r0, {v1-v8, ip}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue