arch: arc: add support of mpu v6
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 <yuguo.zou@synopsys.com>
This commit is contained in:
parent
333501e871
commit
eb14e21d18
7 changed files with 536 additions and 314 deletions
|
@ -5,11 +5,12 @@
|
|||
|
||||
config ARC_MPU_VER
|
||||
int "ARC MPU version"
|
||||
range 2 4
|
||||
range 2 6
|
||||
default 2
|
||||
help
|
||||
ARC MPU has several versions. For MPU v2, the minimum region is 2048 bytes;
|
||||
For MPU v3 and v4, the minimum region is 32 bytes
|
||||
For other versions, the minimum region is 32 bytes; v4 has secure features,
|
||||
v6 supports up to 32 regions.
|
||||
|
||||
config ARC_CORE_MPU
|
||||
bool "ARC Core MPU functionalities"
|
||||
|
@ -31,8 +32,8 @@ config ARC_MPU
|
|||
select SRAM_REGION_PERMISSIONS
|
||||
select ARC_CORE_MPU
|
||||
select THREAD_STACK_INFO
|
||||
select GEN_PRIV_STACKS if ARC_MPU_VER = 2
|
||||
select MPU_REQUIRES_POWER_OF_TWO_ALIGNMENT if ARC_MPU_VER = 2
|
||||
select GEN_PRIV_STACKS if ARC_MPU_VER != 4
|
||||
select MPU_REQUIRES_POWER_OF_TWO_ALIGNMENT if ARC_MPU_VER !=4
|
||||
select MPU_REQUIRES_NON_OVERLAPPING_REGIONS if ARC_MPU_VER = 4
|
||||
help
|
||||
Target has ARC MPU (currently only works for EMSK 2.2/2.3 ARCEM7D)
|
||||
|
|
|
@ -52,8 +52,8 @@ static inline uint32_t get_region_attr_by_type(uint32_t type)
|
|||
}
|
||||
}
|
||||
|
||||
#if CONFIG_ARC_MPU_VER == 2
|
||||
#include "arc_mpu_v2_internal.h"
|
||||
#elif CONFIG_ARC_MPU_VER == 4
|
||||
#if CONFIG_ARC_MPU_VER == 4
|
||||
#include "arc_mpu_v4_internal.h"
|
||||
#else
|
||||
#include "arc_mpu_common_internal.h"
|
||||
#endif
|
||||
|
|
288
arch/arc/core/mpu/arc_mpu_common_internal.h
Normal file
288
arch/arc/core/mpu/arc_mpu_common_internal.h
Normal file
|
@ -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_ */
|
|
@ -6,15 +6,36 @@
|
|||
#ifndef ZEPHYR_ARCH_ARC_CORE_MPU_ARC_MPU_V2_INTERNAL_H_
|
||||
#define ZEPHYR_ARCH_ARC_CORE_MPU_ARC_MPU_V2_INTERNAL_H_
|
||||
|
||||
#define AUX_MPU_RDB_VALID_MASK (0x1)
|
||||
#define AUX_MPU_EN_ENABLE (0x40000000)
|
||||
#define AUX_MPU_EN_DISABLE (0xBFFFFFFF)
|
||||
#define AUX_MPU_EN_ENABLE BIT(30)
|
||||
#define AUX_MPU_EN_DISABLE ~BIT(30)
|
||||
|
||||
#define AUX_MPU_RDP_REGION_SIZE(bits) \
|
||||
(((bits - 1) & 0x3) | (((bits - 1) & 0x1C) << 7))
|
||||
/*
|
||||
* The size of the region is a 5-bit field, the three MSB bits are
|
||||
* represented in [11:9] and the two LSB bits are represented in [1:0].
|
||||
* Together these fields specify the size of the region in bytes:
|
||||
* 00000-00011 Reserved
|
||||
* 0x4 32 0x5 64 0x6 128 0x7 256
|
||||
* 0x8 512 0x9 1k 0xA 2K 0xB 4K
|
||||
* 0xC 8K 0xD 16K 0xE 32K 0xF 64K
|
||||
* 0x10 128K 0x11 256K 0x12 512K 0x13 1M
|
||||
* 0x14 2M 0x15 4M 0x16 8M 0x17 16M
|
||||
* 0x18 32M 0x19 64M 0x1A 128M 0x1B 256M
|
||||
* 0x1C 512M 0x1D 1G 0x1E 2G 0x1F 4G
|
||||
*
|
||||
* Bit ... 12 11 10 9 8 3 2 1 0
|
||||
* ------+------------+------+---+-----------+
|
||||
* ... | SIZE[11:9] | ATTR | R | SIZE[1:0] |
|
||||
* ------+------------+------+---+-----------+
|
||||
*/
|
||||
/* arrange size into proper bit field in RDP aux reg*/
|
||||
#define AUX_MPU_RDP_REGION_SIZE(size) (((size - 1) & BIT_MASK(2)) | \
|
||||
(((size - 1) & (BIT_MASK(3) << 2)) << 7))
|
||||
/* recover size from bit fields in RDP aux reg*/
|
||||
#define AUX_MPU_RDP_SIZE_SHIFT(rdp) ((rdp & BIT_MASK(2)) | (((rdp >> 9) & BIT_MASK(3)) << 2))
|
||||
|
||||
#define AUX_MPU_RDP_ATTR_MASK (0x1FC)
|
||||
#define AUX_MPU_RDP_SIZE_MASK (0xE03)
|
||||
#define AUX_MPU_RDB_VALID_MASK BIT(0)
|
||||
#define AUX_MPU_RDP_ATTR_MASK (BIT_MASK(6) << 3)
|
||||
#define AUX_MPU_RDP_SIZE_MASK ((BIT_MASK(3) << 9) | BIT_MASK(2))
|
||||
|
||||
/* For MPU version 2, the minimum protection region size is 2048 bytes */
|
||||
#if CONFIG_ARC_MPU_VER == 2
|
||||
|
@ -39,7 +60,7 @@ static inline void _region_init(uint32_t index, uint32_t region_addr, uint32_t s
|
|||
bits = ARC_FEATURE_MPU_ALIGNMENT_BITS;
|
||||
}
|
||||
|
||||
if ((1 << bits) < size) {
|
||||
if (BIT(bits) < size) {
|
||||
bits++;
|
||||
}
|
||||
|
||||
|
@ -72,8 +93,7 @@ static inline int get_region_index_by_type(uint32_t type)
|
|||
*/
|
||||
switch (type) {
|
||||
case THREAD_STACK_USER_REGION:
|
||||
return get_num_regions() - mpu_config.num_regions
|
||||
- THREAD_STACK_REGION;
|
||||
return get_num_regions() - mpu_config.num_regions - THREAD_STACK_REGION;
|
||||
case THREAD_STACK_REGION:
|
||||
case THREAD_APP_DATA_REGION:
|
||||
case THREAD_DOMAIN_PARTITION_REGION:
|
||||
|
@ -110,14 +130,14 @@ static inline bool _is_in_region(uint32_t r_index, uint32_t start, uint32_t size
|
|||
& (~AUX_MPU_RDB_VALID_MASK);
|
||||
r_size_lshift = z_arc_v2_aux_reg_read(_ARC_V2_MPU_RDP0 + r_index * 2U)
|
||||
& AUX_MPU_RDP_SIZE_MASK;
|
||||
r_size_lshift = (r_size_lshift & 0x3) | ((r_size_lshift >> 7) & 0x1C);
|
||||
r_size_lshift = AUX_MPU_RDP_SIZE_SHIFT(r_size_lshift);
|
||||
r_addr_end = r_addr_start + (1 << (r_size_lshift + 1));
|
||||
|
||||
if (start >= r_addr_start && (start + size) <= r_addr_end) {
|
||||
return 1;
|
||||
return true;
|
||||
}
|
||||
|
||||
return 0;
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -140,296 +160,4 @@ static inline bool _is_user_accessible_region(uint32_t r_index, int write)
|
|||
(AUX_MPU_ATTR_UR | AUX_MPU_ATTR_KR));
|
||||
}
|
||||
|
||||
/**
|
||||
* @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 v2, 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 MPUv2 */
|
||||
|
||||
/**
|
||||
* @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)
|
||||
{
|
||||
int r_index;
|
||||
|
||||
/*
|
||||
* For ARC MPU v2, smaller region number takes priority.
|
||||
* we can stop the iteration immediately once we find the
|
||||
* matched region that grants permission or denies access.
|
||||
*
|
||||
*/
|
||||
for (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;
|
||||
uint32_t i;
|
||||
|
||||
num_regions = get_num_regions();
|
||||
|
||||
/* ARC MPU supports up to 16 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();
|
||||
|
||||
int r_index;
|
||||
/*
|
||||
* 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.
|
||||
*
|
||||
*/
|
||||
r_index = num_regions - mpu_config.num_regions;
|
||||
|
||||
/* clear all the regions first */
|
||||
for (i = 0U; i < r_index; i++) {
|
||||
_region_init(i, 0, 0, 0);
|
||||
}
|
||||
|
||||
/* configure the static regions */
|
||||
for (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_V2_INTERNAL_H_ */
|
||||
|
|
205
arch/arc/core/mpu/arc_mpu_v6_internal.h
Normal file
205
arch/arc/core/mpu/arc_mpu_v6_internal.h
Normal file
|
@ -0,0 +1,205 @@
|
|||
/*
|
||||
* Copyright (c) 2021 Synopsys.
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
#ifndef ZEPHYR_ARCH_ARC_CORE_MPU_ARC_MPU_V6_INTERNAL_H_
|
||||
#define ZEPHYR_ARCH_ARC_CORE_MPU_ARC_MPU_V6_INTERNAL_H_
|
||||
|
||||
#define AUX_MPU_EN_BANK_MASK BIT(0)
|
||||
#define AUX_MPU_EN_IC BIT(12)
|
||||
#define AUX_MPU_EN_DC BIT(13)
|
||||
#define AUX_MPU_EN_ENABLE BIT(30)
|
||||
#define AUX_MPU_EN_DISABLE ~BIT(30)
|
||||
|
||||
/*
|
||||
* The size of the region is a 5-bit field, the three MSB bits are
|
||||
* represented in [11:9] and the two LSB bits are represented in [1:0].
|
||||
* Together these fields specify the size of the region in bytes:
|
||||
* 00000-00011 Reserved
|
||||
* 0x4 32 0x5 64 0x6 128 0x7 256
|
||||
* 0x8 512 0x9 1k 0xA 2K 0xB 4K
|
||||
* 0xC 8K 0xD 16K 0xE 32K 0xF 64K
|
||||
* 0x10 128K 0x11 256K 0x12 512K 0x13 1M
|
||||
* 0x14 2M 0x15 4M 0x16 8M 0x17 16M
|
||||
* 0x18 32M 0x19 64M 0x1A 128M 0x1B 256M
|
||||
* 0x1C 512M 0x1D 1G 0x1E 2G 0x1F 4G
|
||||
*
|
||||
* Bit ... 12 11 10 9 8 3 2 1 0
|
||||
* ------+------------+------+---+-----------+
|
||||
* ... | SIZE[11:9] | ATTR | R | SIZE[1:0] |
|
||||
* ------+------------+------+---+-----------+
|
||||
*/
|
||||
/* arrange size into proper bit field in RDP aux reg*/
|
||||
#define AUX_MPU_RDP_REGION_SIZE(size) (((size - 1) & BIT_MASK(2)) | \
|
||||
(((size - 1) & (BIT_MASK(3) << 2)) << 7))
|
||||
/* recover size from bit fields in RDP aux reg*/
|
||||
#define AUX_MPU_RDP_SIZE_SHIFT(rdp) ((rdp & BIT_MASK(2)) | (((rdp >> 9) & BIT_MASK(3)) << 2))
|
||||
|
||||
#define AUX_MPU_RDB_VALID_MASK BIT(0)
|
||||
#define AUX_MPU_RDP_ATTR_MASK (BIT_MASK(6) << 3)
|
||||
#define AUX_MPU_RDP_SIZE_MASK ((BIT_MASK(3) << 9) | BIT_MASK(2))
|
||||
/* Global code cacheability that applies to a region
|
||||
* 0x0: (Default) Code is cacheable in all levels of the cache hierarchy
|
||||
* 0x1: Code is not cacheable in any level of the cache hierarchy
|
||||
*/
|
||||
#define AUX_MPU_RDB_IC BIT(12)
|
||||
/* Global data cacheability that applies to a region
|
||||
* 0x0: (Default) Data is cacheable in all levels of the cache hierarchy
|
||||
* 0x1: Data is not cacheable in any level of the cache hierarchy
|
||||
*/
|
||||
#define AUX_MPU_RDB_DC BIT(13)
|
||||
/* Define a MPU region as non-volatile
|
||||
* 0x0: (Default) The memory space for this MPU region is treated as a volatile uncached space.
|
||||
* 0x1: The memory space for this MPU region is non-volatile
|
||||
*/
|
||||
#define AUX_MPU_RDB_NV BIT(14)
|
||||
|
||||
/* For MPU version 6, the minimum protection region size is 32 bytes */
|
||||
#define ARC_FEATURE_MPU_ALIGNMENT_BITS 5
|
||||
#define ARC_FEATURE_MPU_BANK_SIZE 16
|
||||
|
||||
/**
|
||||
* This internal function select a MPU bank
|
||||
*/
|
||||
static inline void _bank_select(uint32_t bank)
|
||||
{
|
||||
uint32_t val;
|
||||
|
||||
val = z_arc_v2_aux_reg_read(_ARC_V2_MPU_EN) & (~AUX_MPU_EN_BANK_MASK);
|
||||
z_arc_v2_aux_reg_write(_ARC_V2_MPU_EN, val | bank);
|
||||
}
|
||||
/**
|
||||
* This internal function initializes a MPU region
|
||||
*/
|
||||
static inline void _region_init(uint32_t index, uint32_t region_addr,
|
||||
uint32_t size, uint32_t region_attr)
|
||||
{
|
||||
uint32_t bank = index / ARC_FEATURE_MPU_BANK_SIZE;
|
||||
|
||||
index = (index % ARC_FEATURE_MPU_BANK_SIZE) * 2U;
|
||||
|
||||
if (size > 0) {
|
||||
uint8_t bits = find_msb_set(size) - 1;
|
||||
|
||||
if (bits < ARC_FEATURE_MPU_ALIGNMENT_BITS) {
|
||||
bits = ARC_FEATURE_MPU_ALIGNMENT_BITS;
|
||||
}
|
||||
|
||||
if (BIT(bits) < size) {
|
||||
bits++;
|
||||
}
|
||||
|
||||
/* Clear size bits and IC, DC bits, and set NV bit
|
||||
* The default value of NV bit is 0 which means the region is volatile and uncached.
|
||||
* Setting the NV bit here has no effect on mpu v6 but is for the
|
||||
* forward compatibility to mpu v7. Currently we do not allow to toggle these bits
|
||||
* until we implement the control of these region properties
|
||||
* TODO: support uncacheable regions and volatile uncached regions
|
||||
*/
|
||||
region_attr &= ~(AUX_MPU_RDP_SIZE_MASK | AUX_MPU_RDB_IC | AUX_MPU_RDB_DC);
|
||||
region_attr |= AUX_MPU_RDP_REGION_SIZE(bits) | AUX_MPU_RDB_NV;
|
||||
region_addr |= AUX_MPU_RDB_VALID_MASK;
|
||||
} else {
|
||||
region_addr = 0U;
|
||||
}
|
||||
|
||||
_bank_select(bank);
|
||||
z_arc_v2_aux_reg_write(_ARC_V2_MPU_RDP0 + index, region_attr);
|
||||
z_arc_v2_aux_reg_write(_ARC_V2_MPU_RDB0 + index, region_addr);
|
||||
}
|
||||
|
||||
/**
|
||||
* This internal function is utilized by the MPU driver to parse the intent
|
||||
* type (i.e. THREAD_STACK_REGION) and return the correct region index.
|
||||
*/
|
||||
static inline int get_region_index_by_type(uint32_t type)
|
||||
{
|
||||
/*
|
||||
* The new MPU regions are allocated per type after the statically
|
||||
* configured regions. The type is one-indexed rather than
|
||||
* zero-indexed.
|
||||
*
|
||||
* For ARC MPU v6, the smaller index has higher priority, so the
|
||||
* index is allocated in reverse order. Static regions start from
|
||||
* the biggest index, then thread related regions.
|
||||
*
|
||||
*/
|
||||
switch (type) {
|
||||
case THREAD_STACK_USER_REGION:
|
||||
return get_num_regions() - mpu_config.num_regions - THREAD_STACK_REGION;
|
||||
case THREAD_STACK_REGION:
|
||||
case THREAD_APP_DATA_REGION:
|
||||
case THREAD_DOMAIN_PARTITION_REGION:
|
||||
/*
|
||||
* Start domain partition region from stack guard region
|
||||
* since stack guard is not supported.
|
||||
*/
|
||||
return get_num_regions() - mpu_config.num_regions - type + 1;
|
||||
default:
|
||||
__ASSERT(0, "Unsupported type");
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* This internal function checks if region is enabled or not
|
||||
*/
|
||||
static inline bool _is_enabled_region(uint32_t r_index)
|
||||
{
|
||||
uint32_t bank = r_index / ARC_FEATURE_MPU_BANK_SIZE;
|
||||
uint32_t index = (r_index % ARC_FEATURE_MPU_BANK_SIZE) * 2U;
|
||||
|
||||
_bank_select(bank);
|
||||
return ((z_arc_v2_aux_reg_read(_ARC_V2_MPU_RDB0 + index)
|
||||
& AUX_MPU_RDB_VALID_MASK) == AUX_MPU_RDB_VALID_MASK);
|
||||
}
|
||||
|
||||
/**
|
||||
* This internal function check if the given buffer in in the region
|
||||
*/
|
||||
static inline bool _is_in_region(uint32_t r_index, uint32_t start, uint32_t size)
|
||||
{
|
||||
uint32_t r_addr_start;
|
||||
uint32_t r_addr_end;
|
||||
uint32_t r_size_lshift;
|
||||
uint32_t bank = r_index / ARC_FEATURE_MPU_BANK_SIZE;
|
||||
uint32_t index = (r_index % ARC_FEATURE_MPU_BANK_SIZE) * 2U;
|
||||
|
||||
_bank_select(bank);
|
||||
r_addr_start = z_arc_v2_aux_reg_read(_ARC_V2_MPU_RDB0 + index) & (~AUX_MPU_RDB_VALID_MASK);
|
||||
r_size_lshift = z_arc_v2_aux_reg_read(_ARC_V2_MPU_RDP0 + index) & AUX_MPU_RDP_SIZE_MASK;
|
||||
r_size_lshift = AUX_MPU_RDP_SIZE_SHIFT(r_size_lshift);
|
||||
r_addr_end = r_addr_start + (1 << (r_size_lshift + 1));
|
||||
|
||||
if (start >= r_addr_start && (start + size) <= r_addr_end) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* This internal function check if the region is user accessible or not
|
||||
*/
|
||||
static inline bool _is_user_accessible_region(uint32_t r_index, int write)
|
||||
{
|
||||
uint32_t r_ap;
|
||||
uint32_t bank = r_index / ARC_FEATURE_MPU_BANK_SIZE;
|
||||
uint32_t index = (r_index % ARC_FEATURE_MPU_BANK_SIZE) * 2U;
|
||||
|
||||
_bank_select(bank);
|
||||
r_ap = z_arc_v2_aux_reg_read(_ARC_V2_MPU_RDP0 + index);
|
||||
|
||||
r_ap &= AUX_MPU_RDP_ATTR_MASK;
|
||||
|
||||
if (write) {
|
||||
return ((r_ap & (AUX_MPU_ATTR_UW | AUX_MPU_ATTR_KW)) ==
|
||||
(AUX_MPU_ATTR_UW | AUX_MPU_ATTR_KW));
|
||||
}
|
||||
|
||||
return ((r_ap & (AUX_MPU_ATTR_UR | AUX_MPU_ATTR_KR)) ==
|
||||
(AUX_MPU_ATTR_UR | AUX_MPU_ATTR_KR));
|
||||
}
|
||||
|
||||
#endif /* ZEPHYR_ARCH_ARC_CORE_MPU_ARC_MPU_V6_INTERNAL_H_ */
|
|
@ -66,7 +66,7 @@ extern "C" {
|
|||
#ifdef CONFIG_ARC_CORE_MPU
|
||||
#if CONFIG_ARC_MPU_VER == 2
|
||||
#define Z_ARC_MPU_ALIGN 2048
|
||||
#elif (CONFIG_ARC_MPU_VER == 3) || (CONFIG_ARC_MPU_VER == 4)
|
||||
#elif (CONFIG_ARC_MPU_VER == 3) || (CONFIG_ARC_MPU_VER == 4) || (CONFIG_ARC_MPU_VER == 6)
|
||||
#define Z_ARC_MPU_ALIGN 32
|
||||
#else
|
||||
#error "Unsupported MPU version"
|
||||
|
|
|
@ -30,7 +30,7 @@
|
|||
#ifdef CONFIG_ARC_MPU_ENABLE
|
||||
#if CONFIG_ARC_MPU_VER == 2
|
||||
#define MPU_MIN_SIZE 2048
|
||||
#elif (CONFIG_ARC_MPU_VER == 3) || (CONFIG_ARC_MPU_VER == 4)
|
||||
#elif (CONFIG_ARC_MPU_VER == 3) || (CONFIG_ARC_MPU_VER == 4) || (CONFIG_ARC_MPU_VER == 6)
|
||||
#define MPU_MIN_SIZE 32
|
||||
#endif
|
||||
#define MPU_MIN_SIZE_ALIGN . = ALIGN(MPU_MIN_SIZE);
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue