forked from zephyrproject-rtos/zephyr
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add support of ARC mpu v6 * minimal region size down to 32 bytes * maximal region number up to 32 * not support uncacheable region and volatile uncached region * clean up mpu code for better readablity Signed-off-by: Yuguo Zou <[email protected]>
- Loading branch information
Showing
7 changed files
with
536 additions
and
314 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,288 @@ | ||
/* | ||
* Copyright (c) 2021 Synopsys. | ||
* | ||
* SPDX-License-Identifier: Apache-2.0 | ||
*/ | ||
#ifndef ZEPHYR_ARCH_ARC_CORE_MPU_ARC_MPU_COMMON_INTERNAL_H_ | ||
#define ZEPHYR_ARCH_ARC_CORE_MPU_ARC_MPU_COMMON_INTERNAL_H_ | ||
|
||
#if CONFIG_ARC_MPU_VER == 2 || CONFIG_ARC_MPU_VER == 3 | ||
#include "arc_mpu_v2_internal.h" | ||
#elif CONFIG_ARC_MPU_VER == 6 | ||
#include "arc_mpu_v6_internal.h" | ||
#else | ||
#error "Unsupported MPU version" | ||
#endif | ||
|
||
/** | ||
* @brief configure the base address and size for an MPU region | ||
* | ||
* @param type MPU region type | ||
* @param base base address in RAM | ||
* @param size size of the region | ||
*/ | ||
static inline int _mpu_configure(uint8_t type, uint32_t base, uint32_t size) | ||
{ | ||
int32_t region_index = get_region_index_by_type(type); | ||
uint32_t region_attr = get_region_attr_by_type(type); | ||
|
||
LOG_DBG("Region info: 0x%x 0x%x", base, size); | ||
|
||
if (region_attr == 0U || region_index < 0) { | ||
return -EINVAL; | ||
} | ||
|
||
/* | ||
* For ARC MPU, MPU regions can be overlapped, smaller | ||
* region index has higher priority. | ||
*/ | ||
_region_init(region_index, base, size, region_attr); | ||
|
||
return 0; | ||
} | ||
|
||
/* ARC Core MPU Driver API Implementation for ARC MP */ | ||
|
||
/** | ||
* @brief enable the MPU | ||
*/ | ||
void arc_core_mpu_enable(void) | ||
{ | ||
/* Enable MPU */ | ||
z_arc_v2_aux_reg_write(_ARC_V2_MPU_EN, | ||
z_arc_v2_aux_reg_read(_ARC_V2_MPU_EN) | AUX_MPU_EN_ENABLE); | ||
} | ||
|
||
/** | ||
* @brief disable the MPU | ||
*/ | ||
void arc_core_mpu_disable(void) | ||
{ | ||
/* Disable MPU */ | ||
z_arc_v2_aux_reg_write(_ARC_V2_MPU_EN, | ||
z_arc_v2_aux_reg_read(_ARC_V2_MPU_EN) & AUX_MPU_EN_DISABLE); | ||
} | ||
|
||
/** | ||
* @brief configure the thread's MPU regions | ||
* | ||
* @param thread the target thread | ||
*/ | ||
void arc_core_mpu_configure_thread(struct k_thread *thread) | ||
{ | ||
#if defined(CONFIG_USERSPACE) | ||
/* configure stack region of user thread */ | ||
if (thread->base.user_options & K_USER) { | ||
LOG_DBG("configure user thread %p's stack", thread); | ||
if (_mpu_configure(THREAD_STACK_USER_REGION, | ||
(uint32_t)thread->stack_info.start, | ||
thread->stack_info.size) < 0) { | ||
LOG_ERR("user thread %p's stack failed", thread); | ||
return; | ||
} | ||
} | ||
|
||
LOG_DBG("configure thread %p's domain", thread); | ||
arc_core_mpu_configure_mem_domain(thread); | ||
#endif | ||
} | ||
|
||
|
||
/** | ||
* @brief configure the default region | ||
* | ||
* @param region_attr region attribute of default region | ||
*/ | ||
void arc_core_mpu_default(uint32_t region_attr) | ||
{ | ||
uint32_t val = z_arc_v2_aux_reg_read(_ARC_V2_MPU_EN) & (~AUX_MPU_RDP_ATTR_MASK); | ||
|
||
region_attr &= AUX_MPU_RDP_ATTR_MASK; | ||
z_arc_v2_aux_reg_write(_ARC_V2_MPU_EN, region_attr | val); | ||
} | ||
|
||
/** | ||
* @brief configure the MPU region | ||
* | ||
* @param index MPU region index | ||
* @param base base address | ||
* @param region_attr region attribute | ||
*/ | ||
int arc_core_mpu_region(uint32_t index, uint32_t base, uint32_t size, uint32_t region_attr) | ||
{ | ||
if (index >= get_num_regions()) { | ||
return -EINVAL; | ||
} | ||
|
||
region_attr &= AUX_MPU_RDP_ATTR_MASK; | ||
|
||
_region_init(index, base, size, region_attr); | ||
|
||
return 0; | ||
} | ||
|
||
#if defined(CONFIG_USERSPACE) | ||
|
||
/** | ||
* @brief configure MPU regions for the memory partitions of the memory domain | ||
* | ||
* @param thread the thread which has memory domain | ||
*/ | ||
void arc_core_mpu_configure_mem_domain(struct k_thread *thread) | ||
{ | ||
int region_index = get_region_index_by_type(THREAD_DOMAIN_PARTITION_REGION); | ||
uint32_t num_partitions; | ||
struct k_mem_partition *pparts; | ||
struct k_mem_domain *mem_domain = NULL; | ||
|
||
if (thread) { | ||
mem_domain = thread->mem_domain_info.mem_domain; | ||
} | ||
|
||
if (mem_domain) { | ||
LOG_DBG("configure domain: %p", mem_domain); | ||
num_partitions = mem_domain->num_partitions; | ||
pparts = mem_domain->partitions; | ||
} else { | ||
LOG_DBG("disable domain partition regions"); | ||
num_partitions = 0U; | ||
pparts = NULL; | ||
} | ||
|
||
for (; region_index >= 0; region_index--) { | ||
if (num_partitions) { | ||
LOG_DBG("set region 0x%x 0x%lx 0x%x", | ||
region_index, pparts->start, pparts->size); | ||
_region_init(region_index, pparts->start, pparts->size, pparts->attr); | ||
num_partitions--; | ||
} else { | ||
/* clear the left mpu entries */ | ||
_region_init(region_index, 0, 0, 0); | ||
} | ||
pparts++; | ||
} | ||
} | ||
|
||
/** | ||
* @brief remove MPU regions for the memory partitions of the memory domain | ||
* | ||
* @param mem_domain the target memory domain | ||
*/ | ||
void arc_core_mpu_remove_mem_domain(struct k_mem_domain *mem_domain) | ||
{ | ||
ARG_UNUSED(mem_domain); | ||
|
||
int region_index = get_region_index_by_type(THREAD_DOMAIN_PARTITION_REGION); | ||
|
||
for (; region_index >= 0; region_index--) { | ||
_region_init(region_index, 0, 0, 0); | ||
} | ||
} | ||
|
||
/** | ||
* @brief reset MPU region for a single memory partition | ||
* | ||
* @param domain the target memory domain | ||
* @param partition_id memory partition id | ||
*/ | ||
void arc_core_mpu_remove_mem_partition(struct k_mem_domain *domain, uint32_t part_id) | ||
{ | ||
ARG_UNUSED(domain); | ||
|
||
int region_index = get_region_index_by_type(THREAD_DOMAIN_PARTITION_REGION); | ||
|
||
LOG_DBG("disable region 0x%x", region_index + part_id); | ||
/* Disable region */ | ||
_region_init(region_index + part_id, 0, 0, 0); | ||
} | ||
|
||
/** | ||
* @brief get the maximum number of free regions for memory domain partitions | ||
*/ | ||
int arc_core_mpu_get_max_domain_partition_regions(void) | ||
{ | ||
return get_region_index_by_type(THREAD_DOMAIN_PARTITION_REGION) + 1; | ||
} | ||
|
||
/** | ||
* @brief validate the given buffer is user accessible or not | ||
*/ | ||
int arc_core_mpu_buffer_validate(void *addr, size_t size, int write) | ||
{ | ||
/* | ||
* For ARC MPU, smaller region number takes priority. | ||
* we can stop the iteration immediately once we find the | ||
* matched region that grants permission or denies access. | ||
* | ||
*/ | ||
for (int r_index = 0; r_index < get_num_regions(); r_index++) { | ||
if (!_is_enabled_region(r_index) || !_is_in_region(r_index, (uint32_t)addr, size)) { | ||
continue; | ||
} | ||
|
||
if (_is_user_accessible_region(r_index, write)) { | ||
return 0; | ||
} else { | ||
return -EPERM; | ||
} | ||
} | ||
|
||
return -EPERM; | ||
} | ||
#endif /* CONFIG_USERSPACE */ | ||
|
||
/* ARC MPU Driver Initial Setup */ | ||
/* | ||
* @brief MPU default initialization and configuration | ||
* | ||
* This function provides the default configuration mechanism for the Memory | ||
* Protection Unit (MPU). | ||
*/ | ||
static int arc_mpu_init(const struct device *arg) | ||
{ | ||
ARG_UNUSED(arg); | ||
|
||
uint32_t num_regions = get_num_regions(); | ||
|
||
if (mpu_config.num_regions > num_regions) { | ||
__ASSERT(0, "Request to configure: %u regions (supported: %u)\n", | ||
mpu_config.num_regions, num_regions); | ||
return -EINVAL; | ||
} | ||
|
||
/* Disable MPU */ | ||
arc_core_mpu_disable(); | ||
|
||
/* | ||
* the MPU regions are filled in the reverse order. | ||
* According to ARCv2 ISA, the MPU region with smaller | ||
* index has higher priority. The static background MPU | ||
* regions in mpu_config will be in the bottom. Then | ||
* the special type regions will be above. | ||
*/ | ||
int r_index = num_regions - mpu_config.num_regions; | ||
|
||
/* clear all the regions first */ | ||
for (uint32_t i = 0U; i < r_index; i++) { | ||
_region_init(i, 0, 0, 0); | ||
} | ||
|
||
/* configure the static regions */ | ||
for (uint32_t i = 0U; i < mpu_config.num_regions; i++) { | ||
_region_init(r_index, mpu_config.mpu_regions[i].base, | ||
mpu_config.mpu_regions[i].size, mpu_config.mpu_regions[i].attr); | ||
r_index++; | ||
} | ||
|
||
/* default region: no read, write and execute */ | ||
arc_core_mpu_default(0); | ||
|
||
/* Enable MPU */ | ||
arc_core_mpu_enable(); | ||
|
||
return 0; | ||
} | ||
|
||
SYS_INIT(arc_mpu_init, PRE_KERNEL_1, CONFIG_KERNEL_INIT_PRIORITY_DEFAULT); | ||
|
||
#endif /* ZEPHYR_ARCH_ARC_CORE_MPU_ARC_MPU_COMMON_INTERNAL_H_ */ |
Oops, something went wrong.