-
Notifications
You must be signed in to change notification settings - Fork 7k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
arm: clean up MPU code for ARM and NXP #7902
arm: clean up MPU code for ARM and NXP #7902
Conversation
Codecov Report
@@ Coverage Diff @@
## master #7902 +/- ##
=======================================
Coverage 63.95% 63.95%
=======================================
Files 429 429
Lines 41281 41281
Branches 6957 6957
=======================================
Hits 26403 26403
Misses 11720 11720
Partials 3158 3158 Continue to review full report at Codecov.
|
f7e6895
to
3ceee05
Compare
Passes my testing on sam_e70_xplained (ARM MPU) and frdm_k64f (NXP MPU) |
3ceee05
to
cb28c0a
Compare
Maybe, change the commit message, so it starts with "arch: arm:" :) |
} | ||
u32_t region_index; | ||
|
||
__ASSERT(type < THREAD_MPU_REGION_LAST, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looks like, the first __ASSERT needed for the case when "type" is invalid, but region_index is valid. So, most likely, for wrap-around, correct?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
this assert is to detect if an unknown value of type is passed in.
the second one is to determine if the computed region index exceeds the number of available regions.
arch/arm/core/cortex_m/mpu/arm_mpu.c
Outdated
@@ -377,8 +329,33 @@ int arm_core_mpu_get_max_domain_partition_regions(void) | |||
_get_region_index_by_type(THREAD_DOMAIN_PARTITION_REGION); | |||
} | |||
|
|||
/** | |||
* This internal function check if the region is user accessible or not |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
check --> checks
Good clean-up (reviewed all but the NXP part) |
arch/arm/core/cortex_m/mpu/arm_mpu.c
Outdated
{ | ||
u32_t r_index; | ||
|
||
/* ARM MPU supports up to 16 Regions */ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This was probably wrong info: AFAIK, Cortex-M4 MPU supports up to 8 regions.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
i saw this in a document somewhere. You can have more than 8 regions, it's not capped to 8 but it is implementation specific.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@agross-linaro I think you're right, ARMv7-m allows more regions. Didn't find the info that limits this. MPU_TYPE.DREGION is 8-b long, so it needs to be at most 256.
However, Cortex-M3,4 implement 8, so the comment was a bit confusing, for most of developers that work on M4 and M4
31efbb1
to
8ba8056
Compare
arch/arm/core/cortex_m/mpu/nxp_mpu.c
Outdated
@@ -44,6 +48,19 @@ static inline u8_t _get_num_regions(void) | |||
return FSL_FEATURE_SYSMPU_DESCRIPTOR_COUNT; | |||
} | |||
|
|||
static inline u8_t _get_num_usable_regions(void) | |||
{ | |||
int max = _get_num_regions(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
_get_num_regions() returns an u8_t; please use it for the type of max
to avoid confusions with signed/unsigned values, and implicit conversions between byte and int
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
done
arch/arm/core/cortex_m/mpu/nxp_mpu.c
Outdated
@@ -408,7 +390,7 @@ 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++) { | |||
for (r_index = 0; r_index < _get_num_usable_regions(); r_index++) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
_get_num_regions() or _get_num_usable_regions() unfortunately can't be marked with attribute((pure)), so the compiler will most likely keep calling them. Wouldn't be better to cache their value in an u8_t
, and define r_index
as an u8_t
as well so that they're of the same type?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
done
8ba8056
to
4b98cf7
Compare
Should we try to get this in, for the upcoming release? |
Looks like a massive change for a release which should happen in 2 days... |
I'm OK with pushing to 1.13. |
arch/arm/core/cortex_m/mpu/arm_mpu.c
Outdated
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) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why this condition is needed? Will r_ap == P_RW_U_RW
check fail there?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This might be a throwback to when the stack region index was the same for the privileged and unprivileged stack. The permissions on those are different and I believe this check would fail because it was stricter for the privileged stack. With the changes done to split out the privileged stack, we don't have to worry about the locality of the stack as it's in a completely different memory region and any user mode access to that memory would fail.
Is this function ever called when the thread is running privileged mode?
Could you please rebase this, @andrewboie , and see what is left of this PR? I think it overlaps in part with several PRs recently merged in master. |
* We are now *much* better at not reserving unnecessary system MPU regions based on configuration. The #defines for intent are now an enumerated type. As a bonus, the implementation of _get_region_index_by_type() is much simpler. Previously we were wasting regions for stack guard and application memory if they were not configured. * NXP MPU doesn't reserve the last region if HW stack protection isn't enabled. * Certain parts of the MPU code are now properly ifdef'd based on configuration. * THREAD_STACK_REGION and THREAD_STACK_USER_REGION was a confusing construction and has now been replaced with just THREAD_STACK_REGION, which represents the MPU region for a user mode thread stack. Supervisor mode stacks do not require an MPU region. * The bounds of CONFIG_APPLICATION_MEMORY never changes and we just do it once during initialization instead of every context switch. * Assertions have been added to catch out-of-bounds cases. Fixes: zephyrproject-rtos#7384 Signed-off-by: Andrew Boie <andrew.p.boie@intel.com>
4b98cf7
to
7230593
Compare
Thank you, @andrewboie , I suggest we try to merge this one before #8467, #8554. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nice clean-up, I left some suggestions in arm_mpu.c, but same comments apply for the nxp_mpu.c
@@ -60,21 +60,27 @@ static inline u32_t _size_to_mpu_rasr_size(u32_t size) | |||
*/ | |||
static inline u32_t _get_region_attr_by_type(u32_t type, u32_t size) | |||
{ | |||
#if defined(CONFIG_USERSPACE) || defined(CONFIG_MPU_STACK_GUARD) || \ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
As far as I can see, _get_region_attr_by_type(..)
can be placed, entirely, within this composite #if guard.
It is used in arm_mpu_configure_xxxx(..)
functions, which depend on the same directives (e.g. CONFIG_USERSPACE
, CONFIG_APPLICATION_MEMORY
).
Note that: arm_core_mpu_configure(..)
appears to be implemented unconditionally, but, in fact, it is used only if the CONFIG_MPU_STACK_GUARD
is defined. So, that function can (and should) also be placed in the same #if guards.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
can we make this additional refinement in another PR
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes
@@ -122,35 +128,18 @@ static void _region_init(u32_t index, u32_t region_addr, | |||
*/ | |||
static inline u32_t _get_region_index_by_type(u32_t type) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This function is greatly simplified.
r_ap = ARM_MPU_DEV->rasr & MPU_RASR_AP_Msk; | ||
|
||
/* always return true if this is the thread stack region */ | ||
if (_get_region_index_by_type(THREAD_STACK_REGION) == r_index) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Here the answer is "by design"
} | ||
|
||
if (write) { | ||
return r_ap == P_RW_U_RW; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Here the answer is "by checking hardware at runtime"
} | ||
|
||
/* For all user accessible permissions, their AP[1] bit is l */ | ||
return r_ap & (0x2 << MPU_RASR_AP_Pos); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Here the answer is "by checking hardware at runtime"
* Note: | ||
* The caller must provide a valid region number. | ||
*/ | ||
static inline int _is_user_accessible_region(u32_t r_index, int write) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I know that this change was just due to the relocation of the function, however, it give me the opportunity to bring up a question: _is_user_accessible_region(..)
appears to mix up design with run-time hardware status, explained inline in following comments. So I wonder whether we could have a unified behavior, instead.
Basically, this boils down to the following question: Since the type of region determines the permissions, why do we need to ask the hardware in runtime?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
can this change be done in another PR
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes
/** | ||
* @brief validate the given buffer is user accessible or not | ||
* | ||
* Presumes the background mapping is NOT user accessible. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is true by design in ARM MPU (@agross-linaro please correct if I am wrong), when the MPU is enabled. But I guess, we always enable MPU when we compile-in this file.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nobody is calling this function without the MPU turned on
@@ -459,9 +437,11 @@ static void _arm_mpu_config(void) | |||
mpu_config.num_regions, | |||
_get_num_regions() | |||
); | |||
return; | |||
return -1; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Isn't it better to use some standard Zephyr error code here, e.g. -EINVAL or some other?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't see the point as this gets called by init code which just checks success/failure
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That's right
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Approving this. Some suggested changes can be tracked on future PR
We are now much better at not reserving unnecessary
system MPU regions based on configuration. The #defines
for intent are now an enumerated type. As a bonus, the
implementation of _get_region_index_by_type() is much
simpler. Previously we were wasting regions for stack guard
and application memory if they were not configured.
Certain parts of the MPU code are now properly ifdef'd
based on configuration.
THREAD_STACK_REGION and THREAD_STACK_USER_REGION was a
confusing construction and has now been replaced with
just THREAD_STACK_REGION, which represents the MPU region
for a user mode thread stack. Supervisor mode stacks
do not require an MPU region.
The bounds of CONFIG_APPLICATION_MEMORY never changes
and we just do it once during initialization instead of
every context switch.
Assertions have been added to catch out-of-bounds cases.
Fixes: #7384
Signed-off-by: Andrew Boie andrew.p.boie@intel.com