diff --git a/arch/arm/core/cortex_m/mpu/arm_core_mpu.c b/arch/arm/core/cortex_m/mpu/arm_core_mpu.c index f21a5a5ed39..e03019622b5 100644 --- a/arch/arm/core/cortex_m/mpu/arm_core_mpu.c +++ b/arch/arm/core/cortex_m/mpu/arm_core_mpu.c @@ -79,4 +79,12 @@ void _arch_mem_domain_destroy(struct k_mem_domain *domain) arm_core_mpu_enable(); } +/* + * Validate the given buffer is user accessible or not + */ +int _arch_buffer_validate(void *addr, size_t size, int write) +{ + return arm_core_mpu_buffer_validate(addr, size, write); +} + #endif diff --git a/arch/arm/core/cortex_m/mpu/arm_mpu.c b/arch/arm/core/cortex_m/mpu/arm_mpu.c index a9abbc1b316..c59ffca2e32 100644 --- a/arch/arm/core/cortex_m/mpu/arm_mpu.c +++ b/arch/arm/core/cortex_m/mpu/arm_mpu.c @@ -140,6 +140,56 @@ static inline u32_t _size_to_mpu_rasr_size(u32_t size) return (find_msb_set(size) - 2) << 1; } +/** + * This internal function check if region is enabled or not + */ +static inline int _is_enabled_region(u32_t r_index) +{ + ARM_MPU_DEV->rnr = r_index; + + return ARM_MPU_DEV->rasr & REGION_ENABLE_MASK; +} + +/** + * This internal function check if the given buffer in in the region + */ +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; + + ARM_MPU_DEV->rnr = r_index; + r_addr_start = ARM_MPU_DEV->rbar & REGION_BASE_ADDR_MASK; + r_size_lshift = ((ARM_MPU_DEV->rasr & REGION_SIZE_MASK) >> + REGION_SIZE_OFFSET) + 1; + r_addr_end = r_addr_start + (1 << r_size_lshift) - 1; + + if (start >= r_addr_start && (start + size - 1) <= r_addr_end) { + return 1; + } + + return 0; +} + +/** + * This internal function check if the region is user accessible or not + */ +static inline int _is_user_accessible_region(u32_t r_index, int write) +{ + u32_t r_ap; + + ARM_MPU_DEV->rnr = r_index; + r_ap = ARM_MPU_DEV->rasr & ACCESS_PERMS_MASK; + + if (write) { + return r_ap == P_RW_U_RW; + } + + /* For all user accessible permissions, their AP[1] bit is l */ + return r_ap & (0x2 << ACCESS_PERMS_OFFSET); +} + /* ARM Core MPU Driver API Implementation for ARM MPU */ /** @@ -292,6 +342,35 @@ int arm_core_mpu_get_max_domain_partition_regions(void) return _get_num_regions() - _get_region_index_by_type(THREAD_DOMAIN_PARTITION_REGION); } + +/** + * @brief validate the given buffer is user accessible or not + */ +int arm_core_mpu_buffer_validate(void *addr, size_t size, int write) +{ + u32_t r_index; + + /* Iterate all mpu regions in reversed order */ + for (r_index = _get_num_regions() + 1; r_index-- > 0;) { + 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 */ /* ARM MPU Driver Initial Setup */ diff --git a/arch/arm/core/cortex_m/mpu/nxp_mpu.c b/arch/arm/core/cortex_m/mpu/nxp_mpu.c index 7d5e5b25c0f..857dfbd7a8b 100644 --- a/arch/arm/core/cortex_m/mpu/nxp_mpu.c +++ b/arch/arm/core/cortex_m/mpu/nxp_mpu.c @@ -112,6 +112,46 @@ static inline u32_t _get_region_index_by_type(u32_t type) } } +/** + * This internal function check if region is enabled or not + */ +static inline int _is_enabled_region(u32_t r_index) +{ + return SYSMPU->WORD[r_index][3] & SYSMPU_WORD_VLD_MASK; +} + +/** + * This internal function check if the given buffer in in the region + */ +static inline int _is_in_region(u32_t r_index, u32_t start, u32_t size) +{ + u32_t r_addr_start; + u32_t r_addr_end; + + r_addr_start = SYSMPU->WORD[r_index][0]; + r_addr_end = SYSMPU->WORD[r_index][1]; + + if (start >= r_addr_start && (start + size - 1) <= r_addr_end) { + return 1; + } + + return 0; +} + +/** + * This internal function check if the region is user accessible or not + */ +static inline int _is_user_accessible_region(u32_t r_index, int write) +{ + u32_t r_ap = SYSMPU->WORD[r_index][2]; + + if (write) { + return (r_ap & MPU_REGION_WRITE) == MPU_REGION_WRITE; + } + + return (r_ap & MPU_REGION_READ) == MPU_REGION_READ; +} + /* ARM Core MPU Driver API Implementation for NXP MPU */ /** @@ -322,6 +362,33 @@ int arm_core_mpu_get_max_domain_partition_regions(void) return _get_num_regions() - _get_region_index_by_type(THREAD_DOMAIN_PARTITION_REGION) - 1; } + +/** + * @brief validate the given buffer is user accessible or not + */ +int arm_core_mpu_buffer_validate(void *addr, size_t size, int write) +{ + u32_t r_index; + + /* Iterate all mpu regions */ + for (r_index = 0; r_index < _get_num_regions(); r_index++) { + if (!_is_enabled_region(r_index) || + !_is_in_region(r_index, (u32_t)addr, size)) { + continue; + } + + /* For NXP MPU, priority given to granting permission over + * denying access for overlapping region. + * So we can stop the iteration immediately once we find the + * matched region that grants permission. + */ + if (_is_user_accessible_region(r_index, write)) { + return 0; + } + } + + return -EPERM; +} #endif /* CONFIG_USERSPACE */ /* NXP MPU Driver Initial Setup */ diff --git a/include/arch/arm/cortex_m/mpu/arm_core_mpu_dev.h b/include/arch/arm/cortex_m/mpu/arm_core_mpu_dev.h index 56af5cab1d0..6d9588d15d2 100644 --- a/include/arch/arm/cortex_m/mpu/arm_core_mpu_dev.h +++ b/include/arch/arm/cortex_m/mpu/arm_core_mpu_dev.h @@ -92,6 +92,11 @@ void arm_core_mpu_mem_partition_remove(u32_t part_index); */ int arm_core_mpu_get_max_domain_partition_regions(void); +/** + * @brief validate the given buffer is user accessible or not + */ +int arm_core_mpu_buffer_validate(void *addr, size_t size, int write); + #endif /* CONFIG_ARM_CORE_MPU */ #ifdef __cplusplus diff --git a/include/arch/arm/cortex_m/mpu/arm_mpu.h b/include/arch/arm/cortex_m/mpu/arm_mpu.h index b557b4a1be4..668efb539b6 100644 --- a/include/arch/arm/cortex_m/mpu/arm_mpu.h +++ b/include/arch/arm/cortex_m/mpu/arm_mpu.h @@ -48,6 +48,21 @@ struct arm_mpu { /* Region base address mask */ #define REGION_BASE_ADDR_MASK 0xFFFFFFE0 +/* ARM MPU RASR Register */ +/* Region enable bit offset */ +#define REGION_ENABLE_OFFSET (0) +/* Region enable bit mask */ +#define REGION_ENABLE_MASK (0x1 << REGION_ENABLE_OFFSET) +/* Region size bit offset */ +#define REGION_SIZE_OFFSET (1) +/* Region size bit mask */ +#define REGION_SIZE_MASK (0x1F << REGION_SIZE_OFFSET) +/* Access permissions bit offset */ +#define ACCESS_PERMS_OFFSET (24) +/* Access permissions bit mask */ +#define ACCESS_PERMS_MASK (0x7 << ACCESS_PERMS_OFFSET) + + /* eXecute Never */ #define NOT_EXEC (0x1 << 28)