From eb14e21d18ae19d659ffdb65b657c874558c63e5 Mon Sep 17 00:00:00 2001 From: Yuguo Zou Date: Tue, 24 Aug 2021 20:42:46 +0800 Subject: [PATCH] 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 --- arch/arc/core/mpu/Kconfig | 9 +- arch/arc/core/mpu/arc_mpu.c | 6 +- arch/arc/core/mpu/arc_mpu_common_internal.h | 288 +++++++++++++++++ arch/arc/core/mpu/arc_mpu_v2_internal.h | 338 ++------------------ arch/arc/core/mpu/arc_mpu_v6_internal.h | 205 ++++++++++++ include/arch/arc/arch.h | 2 +- include/arch/arc/v2/linker.ld | 2 +- 7 files changed, 536 insertions(+), 314 deletions(-) create mode 100644 arch/arc/core/mpu/arc_mpu_common_internal.h create mode 100644 arch/arc/core/mpu/arc_mpu_v6_internal.h diff --git a/arch/arc/core/mpu/Kconfig b/arch/arc/core/mpu/Kconfig index 70a98504a4b..38b9c58542f 100644 --- a/arch/arc/core/mpu/Kconfig +++ b/arch/arc/core/mpu/Kconfig @@ -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) diff --git a/arch/arc/core/mpu/arc_mpu.c b/arch/arc/core/mpu/arc_mpu.c index 9fbe49d84a2..111bc0f6088 100644 --- a/arch/arc/core/mpu/arc_mpu.c +++ b/arch/arc/core/mpu/arc_mpu.c @@ -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 diff --git a/arch/arc/core/mpu/arc_mpu_common_internal.h b/arch/arc/core/mpu/arc_mpu_common_internal.h new file mode 100644 index 00000000000..e6e2174a598 --- /dev/null +++ b/arch/arc/core/mpu/arc_mpu_common_internal.h @@ -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_ */ diff --git a/arch/arc/core/mpu/arc_mpu_v2_internal.h b/arch/arc/core/mpu/arc_mpu_v2_internal.h index 12b2c6468d4..f59b1e9184c 100644 --- a/arch/arc/core/mpu/arc_mpu_v2_internal.h +++ b/arch/arc/core/mpu/arc_mpu_v2_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_ */ diff --git a/arch/arc/core/mpu/arc_mpu_v6_internal.h b/arch/arc/core/mpu/arc_mpu_v6_internal.h new file mode 100644 index 00000000000..1dbd50bdc84 --- /dev/null +++ b/arch/arc/core/mpu/arc_mpu_v6_internal.h @@ -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_ */ diff --git a/include/arch/arc/arch.h b/include/arch/arc/arch.h index f5765136fc1..a7441f8345c 100644 --- a/include/arch/arc/arch.h +++ b/include/arch/arc/arch.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" diff --git a/include/arch/arc/v2/linker.ld b/include/arch/arc/v2/linker.ld index 13245c70baf..89f3c96be41 100644 --- a/include/arch/arc/v2/linker.ld +++ b/include/arch/arc/v2/linker.ld @@ -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);