Skip to content

Commit

Permalink
arm: mpu: Enable userspace support for NXP and ARM
Browse files Browse the repository at this point in the history
This patch set implements the APIs and changed required to support
the user mode thread support.

Signed-off-by: Andy Gross <andy.gross@linaro.org>
  • Loading branch information
Andy Gross authored and andrewboie committed Feb 13, 2018
1 parent 1c047c9 commit f7ec62e
Show file tree
Hide file tree
Showing 6 changed files with 179 additions and 113 deletions.
1 change: 1 addition & 0 deletions arch/arm/core/cortex_m/mpu/Kconfig
Expand Up @@ -27,6 +27,7 @@ config ARM_MPU
depends on SOC_FAMILY_ARM || SOC_FAMILY_STM32 || SOC_FAMILY_NRF5 || SOC_FAMILY_IMX
select ARM_CORE_MPU
select ARCH_HAS_EXECUTABLE_PAGE_BIT
select MPU_REQUIRES_POWER_OF_TWO_ALIGNMENT
default n
help
MCU has ARM MPU
Expand Down
119 changes: 74 additions & 45 deletions arch/arm/core/cortex_m/mpu/arm_mpu.c
Expand Up @@ -12,6 +12,7 @@
#include <arch/arm/cortex_m/mpu/arm_mpu.h>
#include <arch/arm/cortex_m/mpu/arm_core_mpu.h>
#include <logging/sys_log.h>
#include <linker/linker-defs.h>

#define ARM_MPU_DEV ((volatile struct arm_mpu *) ARM_MPU_BASE)

Expand All @@ -31,18 +32,52 @@ static inline u32_t _get_region_attr(u32_t xn, u32_t ap, u32_t tex,
| (c << 17) | (b << 16) | (srd << 5) | (size));
}

/**
* 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 = 1 << (32 - __builtin_clz(size - 1));
return (32 - __builtin_clz(size) - 2) << 1;
}


/**
* This internal function is utilized by the MPU driver to parse the intent
* type (i.e. THREAD_STACK_REGION) and return the correct parameter set.
*/
static inline u32_t _get_region_attr_by_type(u32_t type, u32_t size)
{
int region_size = _size_to_mpu_rasr_size(size);

switch (type) {
case THREAD_STACK_USER_REGION:
return _get_region_attr(1, P_RW_U_RW, 0, 1, 0,
1, 0, region_size);
case THREAD_STACK_REGION:
return 0;
return _get_region_attr(1, P_RW_U_RW, 0, 1, 0,
1, 0, region_size);
case THREAD_STACK_GUARD_REGION:
return _get_region_attr(1, P_RO_U_RO, 0, 1, 0,
1, 0, REGION_32B);
return _get_region_attr(1, P_RO_U_NA, 0, 1, 0,
1, 0, region_size);
case THREAD_APP_DATA_REGION:
return _get_region_attr(1, P_RW_U_RW, 0, 1, 0,
1, 0, region_size);
default:
/* Size 0 region */
return 0;
Expand All @@ -67,6 +102,7 @@ static void _region_init(u32_t index, u32_t region_addr,
ARM_MPU_DEV->rbar = (region_addr & REGION_BASE_ADDR_MASK)
| REGION_VALID | index;
ARM_MPU_DEV->rasr = region_attr | REGION_ENABLE;
SYS_LOG_DBG("[%d] 0x%08x 0x%08x", index, region_addr, region_attr);
}

/**
Expand All @@ -82,64 +118,30 @@ static inline u32_t _get_region_index_by_type(u32_t type)
* index.
*/
switch (type) {
case THREAD_STACK_USER_REGION:
return mpu_config.num_regions + THREAD_STACK_REGION - 1;
case THREAD_STACK_REGION:
return mpu_config.num_regions + type - 1;
case THREAD_STACK_GUARD_REGION:
case THREAD_APP_DATA_REGION:
return mpu_config.num_regions + type - 1;
case THREAD_DOMAIN_PARTITION_REGION:
#if defined(CONFIG_MPU_STACK_GUARD)
#if defined(CONFIG_USERSPACE)
return mpu_config.num_regions + type - 1;
#elif defined(CONFIG_MPU_STACK_GUARD)
return mpu_config.num_regions + type - 2;
#else
/*
* Start domain partition region from stack guard region
* since stack guard is not enabled.
*/
return mpu_config.num_regions + type - 2;
return mpu_config.num_regions + type - 3;
#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;
}

/**
* This internal function check if region is enabled or not
*/
Expand Down Expand Up @@ -182,6 +184,11 @@ static inline int _is_user_accessible_region(u32_t r_index, int write)
ARM_MPU_DEV->rnr = r_index;
r_ap = ARM_MPU_DEV->rasr & ACCESS_PERMS_MASK;

/* always return true if this is the thread stack region */
if (_get_region_index_by_type(THREAD_STACK_REGION) == r_index) {
return 1;
}

if (write) {
return r_ap == P_RW_U_RW;
}
Expand Down Expand Up @@ -242,7 +249,29 @@ void arm_core_mpu_configure(u8_t type, u32_t base, u32_t size)
#if defined(CONFIG_USERSPACE)
void arm_core_mpu_configure_user_context(struct k_thread *thread)
{
return 0;
u32_t base = (u32_t)thread->stack_obj;
u32_t size = thread->stack_info.size;
u32_t index = _get_region_index_by_type(THREAD_STACK_USER_REGION);
u32_t region_attr = _get_region_attr_by_type(THREAD_STACK_USER_REGION,
size);

if (!thread->arch.priv_stack_start) {
ARM_MPU_DEV->rnr = index;
ARM_MPU_DEV->rbar = 0;
ARM_MPU_DEV->rasr = 0;
return;
}
/* configure stack */
_region_init(index, base, region_attr);

#if defined(CONFIG_APPLICATION_MEMORY)
/* configure app data portion */
index = _get_region_index_by_type(THREAD_APP_DATA_REGION);
size = (u32_t)&__app_ram_end - (u32_t)&__app_ram_start;
region_attr = _get_region_attr_by_type(THREAD_APP_DATA_REGION, size);
if (size > 0)
_region_init(index, (u32_t)&__app_ram_start, region_attr);
#endif
}

/**
Expand Down
136 changes: 84 additions & 52 deletions arch/arm/core/cortex_m/mpu/nxp_mpu.c
Expand Up @@ -12,8 +12,7 @@
#include <arch/arm/cortex_m/mpu/nxp_mpu.h>
#include <logging/sys_log.h>
#include <misc/__assert.h>

#define STACK_GUARD_REGION_SIZE 32
#include <linker/linker-defs.h>

/* NXP MPU Enabled state */
static u8_t nxp_mpu_enabled;
Expand All @@ -25,11 +24,15 @@ static u8_t nxp_mpu_enabled;
static inline u32_t _get_region_attr_by_type(u32_t type)
{
switch (type) {
case THREAD_STACK_USER_REGION:
return REGION_USER_MODE_ATTR;
case THREAD_STACK_REGION:
return 0;
return REGION_RAM_ATTR;
case THREAD_STACK_GUARD_REGION:
/* The stack guard region has to be not accessible */
return REGION_RO_ATTR;
case THREAD_APP_DATA_REGION:
return REGION_USER_MODE_ATTR;
default:
/* Size 0 region */
return 0;
Expand Down Expand Up @@ -92,19 +95,23 @@ static inline u32_t _get_region_index_by_type(u32_t type)
* index.
*/
switch (type) {
case THREAD_STACK_USER_REGION:
return mpu_config.num_regions + THREAD_STACK_REGION - 1;
case THREAD_STACK_REGION:
return mpu_config.num_regions + type - 1;
case THREAD_STACK_GUARD_REGION:
case THREAD_APP_DATA_REGION:
return mpu_config.num_regions + type - 1;
case THREAD_DOMAIN_PARTITION_REGION:
#if defined(CONFIG_MPU_STACK_GUARD)
#if defined(CONFIG_USERSPACE)
return mpu_config.num_regions + type - 1;
#elif defined(CONFIG_MPU_STACK_GUARD)
return mpu_config.num_regions + type - 2;
#else
/*
* Start domain partition region from stack guard region
* since stack guard is not enabled.
*/
return mpu_config.num_regions + type - 2;
return mpu_config.num_regions + type - 3;
#endif
default:
__ASSERT(0, "Unsupported type");
Expand Down Expand Up @@ -145,13 +152,56 @@ static inline int _is_user_accessible_region(u32_t r_index, int write)
{
u32_t r_ap = SYSMPU->WORD[r_index][2];

/* always return true if this is the thread stack region */
if (_get_region_index_by_type(THREAD_STACK_REGION) == r_index) {
return 1;
}

if (write) {
return (r_ap & MPU_REGION_WRITE) == MPU_REGION_WRITE;
}

return (r_ap & MPU_REGION_READ) == MPU_REGION_READ;
}

static void nxp_mpu_setup_sram_region(u32_t base, u32_t size)
{
u32_t last_region = _get_num_regions() - 1;

/*
* The NXP MPU manages the permissions of the overlapping regions
* doing the logic OR in between them, hence they can't be used
* for stack/stack guard protection. For this reason the last region of
* the MPU will be reserved.
*
* A consequence of this is that the SRAM is split in different
* regions. In example if THREAD_STACK_GUARD_REGION is selected:
* - SRAM before THREAD_STACK_GUARD_REGION: RW
* - SRAM THREAD_STACK_GUARD_REGION: RO
* - SRAM after THREAD_STACK_GUARD_REGION: RW
*/

/* Configure SRAM_0 region
*
* The mpu_config.sram_region contains the index of the region in
* the mpu_config.mpu_regions array but the region 0 on the NXP MPU
* is the background region, so on this MPU the regions are mapped
* starting from 1, hence the mpu_config.sram_region on the data
* structure is mapped on the mpu_config.sram_region + 1 region of
* the MPU.
*/
_region_init(mpu_config.sram_region,
mpu_config.mpu_regions[mpu_config.sram_region].base,
ENDADDR_ROUND(base),
mpu_config.mpu_regions[mpu_config.sram_region].attr);

/* Configure SRAM_1 region */
_region_init(last_region, base + size,
ENDADDR_ROUND(mpu_config.mpu_regions[mpu_config.sram_region].end),
mpu_config.mpu_regions[mpu_config.sram_region].attr);

}

/* ARM Core MPU Driver API Implementation for NXP MPU */

/**
Expand Down Expand Up @@ -194,64 +244,46 @@ 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);
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;

/*
* The NXP MPU manages the permissions of the overlapping regions
* doing the logic OR in between them, hence they can't be used
* for stack/stack guard protection. For this reason the last region of
* the MPU will be reserved.
*
* A consequence of this is that the SRAM is splitted in different
* regions. In example if THREAD_STACK_GUARD_REGION is selected:
* - SRAM before THREAD_STACK_GUARD_REGION: RW
* - SRAM THREAD_STACK_GUARD_REGION: RO
* - SRAM after THREAD_STACK_GUARD_REGION: RW
*/
/* NXP MPU supports up to 16 Regions */
if (region_index > _get_num_regions() - 2) {
return;
}

/* Configure SRAM_0 region */
/*
* The mpu_config.sram_region contains the index of the region in
* the mpu_config.mpu_regions array but the region 0 on the NXP MPU
* is the background region, so on this MPU the regions are mapped
* starting from 1, hence the mpu_config.sram_region on the data
* structure is mapped on the mpu_config.sram_region + 1 region of
* the MPU.
*/
_region_init(mpu_config.sram_region,
mpu_config.mpu_regions[mpu_config.sram_region].base,
ENDADDR_ROUND(base),
mpu_config.mpu_regions[mpu_config.sram_region].attr);

switch (type) {
case THREAD_STACK_REGION:
break;
case THREAD_STACK_GUARD_REGION:
_region_init(region_index, base,
ENDADDR_ROUND(base + STACK_GUARD_REGION_SIZE),
region_attr);
break;
default:
break;
_region_init(region_index, base,
ENDADDR_ROUND(base + size),
region_attr);
if (type == THREAD_STACK_GUARD_REGION) {
nxp_mpu_setup_sram_region(base, size);
}

/* Configure SRAM_1 region */
_region_init(last_region,
base + STACK_GUARD_REGION_SIZE,
ENDADDR_ROUND(
mpu_config.mpu_regions[mpu_config.sram_region].end),
mpu_config.mpu_regions[mpu_config.sram_region].attr);

}

#if defined(CONFIG_USERSPACE)
void arm_core_mpu_configure_user_context(struct k_thread *thread)
{
return 0;
u32_t base = (u32_t)thread->stack_info.start;
u32_t size = thread->stack_info.size;
u32_t index = _get_region_index_by_type(THREAD_STACK_USER_REGION);
u32_t region_attr = _get_region_attr_by_type(THREAD_STACK_USER_REGION);

/* configure stack */
_region_init(index, base, ENDADDR_ROUND(base + size), region_attr);

#if defined(CONFIG_APPLICATION_MEMORY)
/* configure app data portion */
index = _get_region_index_by_type(THREAD_APP_DATA_REGION);
region_attr = _get_region_attr_by_type(THREAD_APP_DATA_REGION);
base = (u32_t)&__app_ram_start;
size = (u32_t)&__app_ram_end - (u32_t)&__app_ram_start;

/* set up app data region if exists, otherwise disable */
if (size > 0) {
_region_init(index, base, ENDADDR_ROUND(base + size),
region_attr);
} else {
SYSMPU->WORD[index][3] = 0;
}
#endif
}

/**
Expand Down

0 comments on commit f7ec62e

Please sign in to comment.