Skip to content

Commit d051740

Browse files
Chunlin HanAnas Nashif
authored andcommitted
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 <[email protected]>
1 parent d952aae commit d051740

File tree

5 files changed

+174
-0
lines changed

5 files changed

+174
-0
lines changed

arch/arm/core/cortex_m/mpu/arm_core_mpu.c

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -79,4 +79,12 @@ void _arch_mem_domain_destroy(struct k_mem_domain *domain)
7979
arm_core_mpu_enable();
8080
}
8181

82+
/*
83+
* Validate the given buffer is user accessible or not
84+
*/
85+
int _arch_buffer_validate(void *addr, size_t size, int write)
86+
{
87+
return arm_core_mpu_buffer_validate(addr, size, write);
88+
}
89+
8290
#endif

arch/arm/core/cortex_m/mpu/arm_mpu.c

Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -140,6 +140,56 @@ static inline u32_t _size_to_mpu_rasr_size(u32_t size)
140140
return (find_msb_set(size) - 2) << 1;
141141
}
142142

143+
/**
144+
* This internal function check if region is enabled or not
145+
*/
146+
static inline int _is_enabled_region(u32_t r_index)
147+
{
148+
ARM_MPU_DEV->rnr = r_index;
149+
150+
return ARM_MPU_DEV->rasr & REGION_ENABLE_MASK;
151+
}
152+
153+
/**
154+
* This internal function check if the given buffer in in the region
155+
*/
156+
static inline int _is_in_region(u32_t r_index, u32_t start, u32_t size)
157+
{
158+
u32_t r_addr_start;
159+
u32_t r_size_lshift;
160+
u32_t r_addr_end;
161+
162+
ARM_MPU_DEV->rnr = r_index;
163+
r_addr_start = ARM_MPU_DEV->rbar & REGION_BASE_ADDR_MASK;
164+
r_size_lshift = ((ARM_MPU_DEV->rasr & REGION_SIZE_MASK) >>
165+
REGION_SIZE_OFFSET) + 1;
166+
r_addr_end = r_addr_start + (1 << r_size_lshift) - 1;
167+
168+
if (start >= r_addr_start && (start + size - 1) <= r_addr_end) {
169+
return 1;
170+
}
171+
172+
return 0;
173+
}
174+
175+
/**
176+
* This internal function check if the region is user accessible or not
177+
*/
178+
static inline int _is_user_accessible_region(u32_t r_index, int write)
179+
{
180+
u32_t r_ap;
181+
182+
ARM_MPU_DEV->rnr = r_index;
183+
r_ap = ARM_MPU_DEV->rasr & ACCESS_PERMS_MASK;
184+
185+
if (write) {
186+
return r_ap == P_RW_U_RW;
187+
}
188+
189+
/* For all user accessible permissions, their AP[1] bit is l */
190+
return r_ap & (0x2 << ACCESS_PERMS_OFFSET);
191+
}
192+
143193
/* ARM Core MPU Driver API Implementation for ARM MPU */
144194

145195
/**
@@ -292,6 +342,35 @@ int arm_core_mpu_get_max_domain_partition_regions(void)
292342
return _get_num_regions() -
293343
_get_region_index_by_type(THREAD_DOMAIN_PARTITION_REGION);
294344
}
345+
346+
/**
347+
* @brief validate the given buffer is user accessible or not
348+
*/
349+
int arm_core_mpu_buffer_validate(void *addr, size_t size, int write)
350+
{
351+
u32_t r_index;
352+
353+
/* Iterate all mpu regions in reversed order */
354+
for (r_index = _get_num_regions() + 1; r_index-- > 0;) {
355+
if (!_is_enabled_region(r_index) ||
356+
!_is_in_region(r_index, (u32_t)addr, size)) {
357+
continue;
358+
}
359+
360+
/* For ARM MPU, higher region number takes priority.
361+
* Since we iterate all mpu regions in reversed order, so
362+
* we can stop the iteration immediately once we find the
363+
* matched region that grants permission or denies access.
364+
*/
365+
if (_is_user_accessible_region(r_index, write)) {
366+
return 0;
367+
} else {
368+
return -EPERM;
369+
}
370+
}
371+
372+
return -EPERM;
373+
}
295374
#endif /* CONFIG_USERSPACE */
296375

297376
/* ARM MPU Driver Initial Setup */

arch/arm/core/cortex_m/mpu/nxp_mpu.c

Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -112,6 +112,46 @@ static inline u32_t _get_region_index_by_type(u32_t type)
112112
}
113113
}
114114

115+
/**
116+
* This internal function check if region is enabled or not
117+
*/
118+
static inline int _is_enabled_region(u32_t r_index)
119+
{
120+
return SYSMPU->WORD[r_index][3] & SYSMPU_WORD_VLD_MASK;
121+
}
122+
123+
/**
124+
* This internal function check if the given buffer in in the region
125+
*/
126+
static inline int _is_in_region(u32_t r_index, u32_t start, u32_t size)
127+
{
128+
u32_t r_addr_start;
129+
u32_t r_addr_end;
130+
131+
r_addr_start = SYSMPU->WORD[r_index][0];
132+
r_addr_end = SYSMPU->WORD[r_index][1];
133+
134+
if (start >= r_addr_start && (start + size - 1) <= r_addr_end) {
135+
return 1;
136+
}
137+
138+
return 0;
139+
}
140+
141+
/**
142+
* This internal function check if the region is user accessible or not
143+
*/
144+
static inline int _is_user_accessible_region(u32_t r_index, int write)
145+
{
146+
u32_t r_ap = SYSMPU->WORD[r_index][2];
147+
148+
if (write) {
149+
return (r_ap & MPU_REGION_WRITE) == MPU_REGION_WRITE;
150+
}
151+
152+
return (r_ap & MPU_REGION_READ) == MPU_REGION_READ;
153+
}
154+
115155
/* ARM Core MPU Driver API Implementation for NXP MPU */
116156

117157
/**
@@ -322,6 +362,33 @@ int arm_core_mpu_get_max_domain_partition_regions(void)
322362
return _get_num_regions() -
323363
_get_region_index_by_type(THREAD_DOMAIN_PARTITION_REGION) - 1;
324364
}
365+
366+
/**
367+
* @brief validate the given buffer is user accessible or not
368+
*/
369+
int arm_core_mpu_buffer_validate(void *addr, size_t size, int write)
370+
{
371+
u32_t r_index;
372+
373+
/* Iterate all mpu regions */
374+
for (r_index = 0; r_index < _get_num_regions(); r_index++) {
375+
if (!_is_enabled_region(r_index) ||
376+
!_is_in_region(r_index, (u32_t)addr, size)) {
377+
continue;
378+
}
379+
380+
/* For NXP MPU, priority given to granting permission over
381+
* denying access for overlapping region.
382+
* So we can stop the iteration immediately once we find the
383+
* matched region that grants permission.
384+
*/
385+
if (_is_user_accessible_region(r_index, write)) {
386+
return 0;
387+
}
388+
}
389+
390+
return -EPERM;
391+
}
325392
#endif /* CONFIG_USERSPACE */
326393

327394
/* NXP MPU Driver Initial Setup */

include/arch/arm/cortex_m/mpu/arm_core_mpu_dev.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -92,6 +92,11 @@ void arm_core_mpu_mem_partition_remove(u32_t part_index);
9292
*/
9393
int arm_core_mpu_get_max_domain_partition_regions(void);
9494

95+
/**
96+
* @brief validate the given buffer is user accessible or not
97+
*/
98+
int arm_core_mpu_buffer_validate(void *addr, size_t size, int write);
99+
95100
#endif /* CONFIG_ARM_CORE_MPU */
96101

97102
#ifdef __cplusplus

include/arch/arm/cortex_m/mpu/arm_mpu.h

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,21 @@ struct arm_mpu {
4848
/* Region base address mask */
4949
#define REGION_BASE_ADDR_MASK 0xFFFFFFE0
5050

51+
/* ARM MPU RASR Register */
52+
/* Region enable bit offset */
53+
#define REGION_ENABLE_OFFSET (0)
54+
/* Region enable bit mask */
55+
#define REGION_ENABLE_MASK (0x1 << REGION_ENABLE_OFFSET)
56+
/* Region size bit offset */
57+
#define REGION_SIZE_OFFSET (1)
58+
/* Region size bit mask */
59+
#define REGION_SIZE_MASK (0x1F << REGION_SIZE_OFFSET)
60+
/* Access permissions bit offset */
61+
#define ACCESS_PERMS_OFFSET (24)
62+
/* Access permissions bit mask */
63+
#define ACCESS_PERMS_MASK (0x7 << ACCESS_PERMS_OFFSET)
64+
65+
5166
/* eXecute Never */
5267
#define NOT_EXEC (0x1 << 28)
5368

0 commit comments

Comments
 (0)