arm: implement API to validate user buffer
Implement API to validate user buffer. This API will iterate all MPU regions to check if the given buffer is user accessible or not. For #3832. Signed-off-by: Chunlin Han <chunlin.han@linaro.org>
This commit is contained in:
parent
d952aae3f1
commit
d051740ee0
5 changed files with 174 additions and 0 deletions
|
@ -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
|
||||
|
|
|
@ -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 */
|
||||
|
|
|
@ -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 */
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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)
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue