blob: 79ee9ddca10b879d20c651fc1b44c430d906d609 [file] [log] [blame]
/*
* Copyright (c) 2017 Linaro Limited.
* Copyright (c) 2018 Nordic Semiconductor ASA.
*
* SPDX-License-Identifier: Apache-2.0
*/
/* Global MPU configuration at system initialization. */
static void _mpu_init(void)
{
/* No specific configuration at init for ARMv7-M MPU. */
}
/* This internal function performs MPU region initialization.
*
* Note:
* The caller must provide a valid region index.
*/
static void _region_init(u32_t index, struct arm_mpu_region *region_conf)
{
/* Select the region you want to access */
MPU->RNR = index;
/* Configure the region */
MPU->RBAR = (region_conf->base & MPU_RBAR_ADDR_Msk)
| MPU_RBAR_VALID_Msk | index;
MPU->RASR = region_conf->attr.rasr | MPU_RASR_ENABLE_Msk;
SYS_LOG_DBG("[%d] 0x%08x 0x%08x",
index, region_conf->base, region_conf->attr.rasr);
}
#if defined(CONFIG_USERSPACE) || defined(CONFIG_MPU_STACK_GUARD) || \
defined(CONFIG_APPLICATION_MEMORY)
static inline u8_t _get_num_regions(void);
static inline u32_t _get_region_index_by_type(u32_t type);
/**
* This internal function converts the region size to
* the SIZE field value of MPU_RASR.
*
* Note: If size is not a power-of-two, it is rounded-up to the next
* power-of-two value, and the returned SIZE field value corresponds
* to that power-of-two value.
*/
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 > (1UL << 31)) {
return REGION_4G;
}
return ((32 - __builtin_clz(size - 1) - 2 + 1) << MPU_RASR_SIZE_Pos) &
MPU_RASR_SIZE_Msk;
}
/**
* Generate the value of the MPU Region Attribute and Size Register
* (MPU_RASR) that corresponds to the supplied MPU region attributes.
* This function is internal to the driver.
*/
static inline u32_t _get_region_attr(u32_t xn, u32_t ap, u32_t tex,
u32_t c, u32_t b, u32_t s,
u32_t srd, u32_t region_size)
{
int size = _size_to_mpu_rasr_size(region_size);
return (((xn << MPU_RASR_XN_Pos) & MPU_RASR_XN_Msk)
| ((ap << MPU_RASR_AP_Pos) & MPU_RASR_AP_Msk)
| ((tex << MPU_RASR_TEX_Pos) & MPU_RASR_TEX_Msk)
| ((s << MPU_RASR_S_Pos) & MPU_RASR_S_Msk)
| ((c << MPU_RASR_C_Pos) & MPU_RASR_C_Msk)
| ((b << MPU_RASR_B_Pos) & MPU_RASR_B_Msk)
| ((srd << MPU_RASR_SRD_Pos) & MPU_RASR_SRD_Msk)
| (size));
}
/**
* This internal function allocates default RAM cache-ability, share-ability,
* and execution allowance attributes along with the requested access
* permissions and size.
*/
static inline void _get_mpu_ram_region_attr(arm_mpu_region_attr_t *p_attr,
u32_t ap, u32_t base, u32_t size)
{
/* in ARMv7-M MPU the base address is not required
* to determine region attributes.
*/
(void) base;
p_attr->rasr = _get_region_attr(1, ap, 1, 1, 1, 1, 0, size);
}
/**
* This internal function is utilized by the MPU driver to combine a given
* MPU RAM attribute configuration and region size and return the correct
* parameter set.
*/
static inline void _get_ram_region_attr_by_conf(arm_mpu_region_attr_t *p_attr,
u32_t attr, u32_t base, u32_t size)
{
/* in ARMv7-M MPU the base address is not required
* to determine region attributes
*/
(void) base;
p_attr->rasr = attr | _size_to_mpu_rasr_size(size);
}
/**
* This internal function checks if region is enabled or not.
*
* Note:
* The caller must provide a valid region number.
*/
static inline int _is_enabled_region(u32_t r_index)
{
MPU->RNR = r_index;
return MPU->RASR & MPU_RASR_ENABLE_Msk;
}
/**
* This internal function checks if the given buffer is in the region.
*
* Note:
* The caller must provide a valid region number.
*/
static inline int _is_in_region(u32_t r_index, u32_t start, u32_t size)
{
u32_t r_addr_start;
u32_t r_size_lshift;
u32_t r_addr_end;
MPU->RNR = r_index;
r_addr_start = MPU->RBAR & MPU_RBAR_ADDR_Msk;
r_size_lshift = ((MPU->RASR & MPU_RASR_SIZE_Msk) >>
MPU_RASR_SIZE_Pos) + 1;
r_addr_end = r_addr_start + (1UL << r_size_lshift) - 1;
if (start >= r_addr_start && (start + size - 1) <= r_addr_end) {
return 1;
}
return 0;
}
/**
* This internal function returns the access permissions of an MPU region
* specified by its region index.
*
* Note:
* The caller must provide a valid region number.
*/
static inline u32_t _get_region_ap(u32_t r_index)
{
MPU->RNR = r_index;
return (MPU->RASR & MPU_RASR_AP_Msk) >> MPU_RASR_AP_Pos;
}
/* Only a single bit is set for all user accessible permissions.
* In ARMv7-M MPU this is bit AP[1].
*/
#define MPU_USER_READ_ACCESSIBLE_Msk (P_RW_U_RO & P_RW_U_RW & P_RO_U_RO & RO)
#if defined(CONFIG_USERSPACE)
/**
* This internal function checks if the region is user accessible or not.
*
* Note:
* The caller must provide a valid region number.
*/
static inline int _is_user_accessible_region(u32_t r_index, int write)
{
u32_t r_ap = _get_region_ap(r_index);
/* 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;
}
return r_ap & MPU_USER_READ_ACCESSIBLE_Msk;
}
/**
* This internal function validates whether a given memory buffer
* is user accessible or not.
*/
static inline int _mpu_buffer_validate(void *addr, size_t size, int write)
{
s32_t r_index;
/* Iterate all mpu regions in reversed order */
for (r_index = _get_num_regions() - 1; r_index >= 0; r_index--) {
if (!_is_enabled_region(r_index) ||
!_is_in_region(r_index, (u32_t)addr, size)) {
continue;
}
/* For ARM MPU, higher region number takes priority.
* Since we iterate all mpu regions in reversed order, so
* we can stop the iteration immediately once we find the
* matched region that grants permission or denies access.
*/
if (_is_user_accessible_region(r_index, write)) {
return 0;
} else {
return -EPERM;
}
}
return -EPERM;
}
#endif /* CONFIG_USERSPACE */
#endif /* USERSPACE || MPU_STACK_GUARD || APPLICATION_MEMORY */