| /* |
| * Copyright (c) 2019 Synopsys. |
| * |
| * SPDX-License-Identifier: Apache-2.0 |
| */ |
| #ifndef ZEPHYR_ARCH_ARC_CORE_MPU_ARC_MPU_V4_INTERNAL_H_ |
| #define ZEPHYR_ARCH_ARC_CORE_MPU_ARC_MPU_V4_INTERNAL_H_ |
| |
| #define AUX_MPU_RPER_SID1 0x10000 |
| /* valid mask: SID1+secure+valid */ |
| #define AUX_MPU_RPER_VALID_MASK ((0x1) | AUX_MPU_RPER_SID1 | AUX_MPU_ATTR_S) |
| |
| #define AUX_MPU_RPER_ATTR_MASK (0x1FF) |
| |
| /* For MPU version 4, the minimum protection region size is 32 bytes */ |
| #define ARC_FEATURE_MPU_ALIGNMENT_BITS 5 |
| |
| #define CALC_REGION_END_ADDR(start, size) \ |
| (start + size - (1 << ARC_FEATURE_MPU_ALIGNMENT_BITS)) |
| |
| /* ARC MPU version 4 does not support mpu region overlap in hardware |
| * so if we want to allocate MPU region dynamically, e.g. thread stack, |
| * memory domain from a background region, a dynamic region splitting |
| * approach is designed. pls see comments in |
| * _dynamic_region_allocate_and_init |
| * But this approach has an impact on performance of thread switch. |
| * As a trade off, we can use the default mpu region as the background region |
| * to avoid the dynamic region splitting. This will give more privilege to |
| * codes in kernel mode which can access the memory region not covered by |
| * explicit mpu entry. Considering memory protection is mainly used to |
| * isolate malicious codes in user mode, it makes sense to get better |
| * thread switch performance through default mpu region. |
| * CONFIG_MPU_GAP_FILLING is used to turn this on/off. |
| * |
| */ |
| #if defined(CONFIG_MPU_GAP_FILLING) |
| |
| #if defined(CONFIG_USERSPACE) && defined(CONFIG_MPU_STACK_GUARD) |
| /* 1 for stack guard , 1 for user thread, 1 for split */ |
| #define MPU_REGION_NUM_FOR_THREAD 3 |
| #elif defined(CONFIG_USERSPACE) || defined(CONFIG_MPU_STACK_GUARD) |
| /* 1 for stack guard or user thread stack , 1 for split */ |
| #define MPU_REGION_NUM_FOR_THREAD 2 |
| #else |
| #define MPU_REGION_NUM_FOR_THREAD 0 |
| #endif |
| |
| #define MPU_DYNAMIC_REGION_AREAS_NUM 2 |
| |
| /** |
| * @brief internal structure holding information of |
| * memory areas where dynamic MPU programming is allowed. |
| */ |
| struct dynamic_region_info { |
| uint8_t index; |
| uint32_t base; |
| uint32_t size; |
| uint32_t attr; |
| }; |
| |
| static uint8_t dynamic_regions_num; |
| static uint8_t dynamic_region_index; |
| |
| /** |
| * Global array, holding the MPU region index of |
| * the memory region inside which dynamic memory |
| * regions may be configured. |
| */ |
| static struct dynamic_region_info dyn_reg_info[MPU_DYNAMIC_REGION_AREAS_NUM]; |
| #endif /* CONFIG_MPU_GAP_FILLING */ |
| |
| static uint8_t static_regions_num; |
| |
| #ifdef CONFIG_ARC_NORMAL_FIRMWARE |
| /* \todo through secure service to access mpu */ |
| static inline void _region_init(uint32_t index, uint32_t region_addr, uint32_t size, |
| uint32_t region_attr) |
| { |
| } |
| |
| static inline void _region_set_attr(uint32_t index, uint32_t attr) |
| { |
| |
| } |
| |
| static inline uint32_t _region_get_attr(uint32_t index) |
| { |
| return 0; |
| } |
| |
| static inline uint32_t _region_get_start(uint32_t index) |
| { |
| return 0; |
| } |
| |
| static inline void _region_set_start(uint32_t index, uint32_t start) |
| { |
| |
| } |
| |
| static inline uint32_t _region_get_end(uint32_t index) |
| { |
| return 0; |
| } |
| |
| static inline void _region_set_end(uint32_t index, uint32_t end) |
| { |
| } |
| |
| /** |
| * This internal function probes the given addr's MPU index.if not |
| * in MPU, returns error |
| */ |
| static inline int _mpu_probe(uint32_t addr) |
| { |
| return -EINVAL; |
| } |
| |
| /** |
| * This internal function checks if MPU region is enabled or not |
| */ |
| static inline bool _is_enabled_region(uint32_t r_index) |
| { |
| 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) |
| { |
| return false; |
| } |
| #else /* CONFIG_ARC_NORMAL_FIRMWARE */ |
| /* the following functions are prepared for SECURE_FIRMWARE */ |
| static inline void _region_init(uint32_t index, uint32_t region_addr, uint32_t size, |
| uint32_t region_attr) |
| { |
| if (size < (1 << ARC_FEATURE_MPU_ALIGNMENT_BITS)) { |
| size = (1 << ARC_FEATURE_MPU_ALIGNMENT_BITS); |
| } |
| |
| if (region_attr) { |
| region_attr &= AUX_MPU_RPER_ATTR_MASK; |
| region_attr |= AUX_MPU_RPER_VALID_MASK; |
| } |
| |
| z_arc_v2_aux_reg_write(_ARC_V2_MPU_INDEX, index); |
| z_arc_v2_aux_reg_write(_ARC_V2_MPU_RSTART, region_addr); |
| z_arc_v2_aux_reg_write(_ARC_V2_MPU_REND, |
| CALC_REGION_END_ADDR(region_addr, size)); |
| z_arc_v2_aux_reg_write(_ARC_V2_MPU_RPER, region_attr); |
| } |
| |
| static inline void _region_set_attr(uint32_t index, uint32_t attr) |
| { |
| z_arc_v2_aux_reg_write(_ARC_V2_MPU_INDEX, index); |
| z_arc_v2_aux_reg_write(_ARC_V2_MPU_RPER, attr | |
| AUX_MPU_RPER_VALID_MASK); |
| } |
| |
| static inline uint32_t _region_get_attr(uint32_t index) |
| { |
| z_arc_v2_aux_reg_write(_ARC_V2_MPU_INDEX, index); |
| |
| return z_arc_v2_aux_reg_read(_ARC_V2_MPU_RPER); |
| } |
| |
| static inline uint32_t _region_get_start(uint32_t index) |
| { |
| z_arc_v2_aux_reg_write(_ARC_V2_MPU_INDEX, index); |
| |
| return z_arc_v2_aux_reg_read(_ARC_V2_MPU_RSTART); |
| } |
| |
| static inline void _region_set_start(uint32_t index, uint32_t start) |
| { |
| z_arc_v2_aux_reg_write(_ARC_V2_MPU_INDEX, index); |
| z_arc_v2_aux_reg_write(_ARC_V2_MPU_RSTART, start); |
| } |
| |
| static inline uint32_t _region_get_end(uint32_t index) |
| { |
| z_arc_v2_aux_reg_write(_ARC_V2_MPU_INDEX, index); |
| |
| return z_arc_v2_aux_reg_read(_ARC_V2_MPU_REND) + |
| (1 << ARC_FEATURE_MPU_ALIGNMENT_BITS); |
| } |
| |
| static inline void _region_set_end(uint32_t index, uint32_t end) |
| { |
| z_arc_v2_aux_reg_write(_ARC_V2_MPU_INDEX, index); |
| z_arc_v2_aux_reg_write(_ARC_V2_MPU_REND, end - |
| (1 << ARC_FEATURE_MPU_ALIGNMENT_BITS)); |
| } |
| |
| /** |
| * This internal function probes the given addr's MPU index.if not |
| * in MPU, returns error |
| */ |
| static inline int _mpu_probe(uint32_t addr) |
| { |
| uint32_t val; |
| |
| z_arc_v2_aux_reg_write(_ARC_V2_MPU_PROBE, addr); |
| val = z_arc_v2_aux_reg_read(_ARC_V2_MPU_INDEX); |
| |
| /* if no match or multiple regions match, return error */ |
| if (val & 0xC0000000) { |
| return -EINVAL; |
| } else { |
| return val; |
| } |
| } |
| |
| /** |
| * This internal function checks if MPU region is enabled or not |
| */ |
| static inline bool _is_enabled_region(uint32_t r_index) |
| { |
| z_arc_v2_aux_reg_write(_ARC_V2_MPU_INDEX, r_index); |
| return ((z_arc_v2_aux_reg_read(_ARC_V2_MPU_RPER) & |
| AUX_MPU_RPER_VALID_MASK) == AUX_MPU_RPER_VALID_MASK); |
| } |
| |
| /** |
| * 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; |
| |
| z_arc_v2_aux_reg_write(_ARC_V2_MPU_INDEX, r_index); |
| r_ap = z_arc_v2_aux_reg_read(_ARC_V2_MPU_RPER); |
| r_ap &= AUX_MPU_RPER_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 /* CONFIG_ARC_NORMAL_FIRMWARE */ |
| |
| /** |
| * This internal function checks the area given by (start, size) |
| * and returns the index if the area match one MPU entry |
| */ |
| static inline int _get_region_index(uint32_t start, uint32_t size) |
| { |
| int index = _mpu_probe(start); |
| |
| if (index > 0 && index == _mpu_probe(start + size - 1)) { |
| return index; |
| } |
| |
| return -EINVAL; |
| } |
| |
| #if defined(CONFIG_MPU_GAP_FILLING) |
| /** |
| * This internal function allocates a dynamic MPU region and returns |
| * the index or error |
| */ |
| static inline int _dynamic_region_allocate_index(void) |
| { |
| if (dynamic_region_index >= get_num_regions()) { |
| LOG_ERR("no enough mpu entries %d", dynamic_region_index); |
| return -EINVAL; |
| } |
| |
| return dynamic_region_index++; |
| } |
| |
| /* @brief allocate and init a dynamic MPU region |
| * |
| * This internal function performs the allocation and initialization of |
| * a dynamic MPU region |
| * |
| * @param base region base |
| * @param size region size |
| * @param attr region attribute |
| * @return <0 failure, >0 allocated dynamic region index |
| */ |
| static int _dynamic_region_allocate_and_init(uint32_t base, uint32_t size, |
| uint32_t attr) |
| { |
| int u_region_index = _get_region_index(base, size); |
| int region_index; |
| |
| LOG_DBG("Region info: base 0x%x size 0x%x attr 0x%x", base, size, attr); |
| |
| if (u_region_index == -EINVAL) { |
| /* no underlying region */ |
| |
| region_index = _dynamic_region_allocate_index(); |
| |
| if (region_index > 0) { |
| /* a new region */ |
| _region_init(region_index, base, size, attr); |
| } |
| |
| return region_index; |
| } |
| |
| /* |
| * The new memory region is to be placed inside the underlying |
| * region, possibly splitting the underlying region into two. |
| */ |
| |
| uint32_t u_region_start = _region_get_start(u_region_index); |
| uint32_t u_region_end = _region_get_end(u_region_index); |
| uint32_t u_region_attr = _region_get_attr(u_region_index); |
| uint32_t end = base + size; |
| |
| |
| if ((base == u_region_start) && (end == u_region_end)) { |
| /* The new region overlaps entirely with the |
| * underlying region. In this case we simply |
| * update the partition attributes of the |
| * underlying region with those of the new |
| * region. |
| */ |
| _region_init(u_region_index, base, size, attr); |
| region_index = u_region_index; |
| } else if (base == u_region_start) { |
| /* The new region starts exactly at the start of the |
| * underlying region; the start of the underlying |
| * region needs to be set to the end of the new region. |
| */ |
| _region_set_start(u_region_index, base + size); |
| _region_set_attr(u_region_index, u_region_attr); |
| |
| region_index = _dynamic_region_allocate_index(); |
| |
| if (region_index > 0) { |
| _region_init(region_index, base, size, attr); |
| } |
| |
| } else if (end == u_region_end) { |
| /* The new region ends exactly at the end of the |
| * underlying region; the end of the underlying |
| * region needs to be set to the start of the |
| * new region. |
| */ |
| _region_set_end(u_region_index, base); |
| _region_set_attr(u_region_index, u_region_attr); |
| |
| region_index = _dynamic_region_allocate_index(); |
| |
| if (region_index > 0) { |
| _region_init(region_index, base, size, attr); |
| } |
| |
| } else { |
| /* The new region lies strictly inside the |
| * underlying region, which needs to split |
| * into two regions. |
| */ |
| |
| _region_set_end(u_region_index, base); |
| _region_set_attr(u_region_index, u_region_attr); |
| |
| region_index = _dynamic_region_allocate_index(); |
| |
| if (region_index > 0) { |
| _region_init(region_index, base, size, attr); |
| |
| region_index = _dynamic_region_allocate_index(); |
| |
| if (region_index > 0) { |
| _region_init(region_index, base + size, |
| u_region_end - end, u_region_attr); |
| } |
| } |
| } |
| |
| return region_index; |
| } |
| |
| /* @brief reset the dynamic MPU regions |
| * |
| * This internal function performs the reset of dynamic MPU regions |
| */ |
| static void _mpu_reset_dynamic_regions(void) |
| { |
| uint32_t i; |
| uint32_t num_regions = get_num_regions(); |
| |
| for (i = static_regions_num; i < num_regions; i++) { |
| _region_init(i, 0, 0, 0); |
| } |
| |
| for (i = 0U; i < dynamic_regions_num; i++) { |
| _region_init( |
| dyn_reg_info[i].index, |
| dyn_reg_info[i].base, |
| dyn_reg_info[i].size, |
| dyn_reg_info[i].attr); |
| } |
| |
| /* dynamic regions are after static regions */ |
| dynamic_region_index = static_regions_num; |
| } |
| |
| /** |
| * @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) |
| { |
| uint32_t region_attr = get_region_attr_by_type(type); |
| |
| return _dynamic_region_allocate_and_init(base, size, region_attr); |
| } |
| #else |
| /** |
| * 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 v2, 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 static_regions_num + THREAD_STACK_REGION; |
| case THREAD_STACK_REGION: |
| case THREAD_APP_DATA_REGION: |
| case THREAD_STACK_GUARD_REGION: |
| return static_regions_num + type; |
| case THREAD_DOMAIN_PARTITION_REGION: |
| #if defined(CONFIG_MPU_STACK_GUARD) |
| return static_regions_num + type; |
| #else |
| /* |
| * Start domain partition region from stack guard region |
| * since stack guard is not enabled. |
| */ |
| return static_regions_num + type - 1; |
| #endif |
| default: |
| __ASSERT(0, "Unsupported type"); |
| return -EINVAL; |
| } |
| } |
| |
| /** |
| * @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) |
| { |
| int 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; |
| } |
| |
| _region_init(region_index, base, size, region_attr); |
| |
| return 0; |
| } |
| #endif |
| |
| /* ARC Core MPU Driver API Implementation for ARC MPUv3 */ |
| |
| /** |
| * @brief enable the MPU |
| */ |
| void arc_core_mpu_enable(void) |
| { |
| #ifdef CONFIG_ARC_SECURE_FIRMWARE |
| /* the default region: |
| * secure:0x8000, SID:0x10000, KW:0x100 KR:0x80 |
| */ |
| #define MPU_ENABLE_ATTR 0x18180 |
| #else |
| #define MPU_ENABLE_ATTR 0 |
| #endif |
| arc_core_mpu_default(MPU_ENABLE_ATTR); |
| } |
| |
| /** |
| * @brief disable the MPU |
| */ |
| void arc_core_mpu_disable(void) |
| { |
| /* MPU is always enabled, use default region to |
| * simulate MPU disable |
| */ |
| arc_core_mpu_default(REGION_ALL_ATTR | AUX_MPU_ATTR_S | |
| AUX_MPU_RPER_SID1); |
| } |
| |
| /** |
| * @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_MPU_GAP_FILLING) |
| /* the mpu entries of ARC MPUv4 are divided into 2 parts: |
| * static entries: global mpu entries, not changed in context switch |
| * dynamic entries: MPU entries changed in context switch and |
| * memory domain configure, including: |
| * MPU entries for user thread stack |
| * MPU entries for stack guard |
| * MPU entries for mem domain |
| * MPU entries for other thread specific regions |
| * before configuring thread specific mpu entries, need to reset dynamic |
| * entries |
| */ |
| _mpu_reset_dynamic_regions(); |
| #endif |
| #if defined(CONFIG_MPU_STACK_GUARD) |
| uint32_t guard_start; |
| |
| /* Set location of guard area when the thread is running in |
| * supervisor mode. For a supervisor thread, this is just low |
| * memory in the stack buffer. For a user thread, it only runs |
| * in supervisor mode when handling a system call on the privilege |
| * elevation stack. |
| */ |
| #if defined(CONFIG_USERSPACE) |
| if ((thread->base.user_options & K_USER) != 0U) { |
| guard_start = thread->arch.priv_stack_start; |
| } else |
| #endif |
| { |
| guard_start = thread->stack_info.start; |
| } |
| guard_start -= Z_ARC_STACK_GUARD_SIZE; |
| |
| if (_mpu_configure(THREAD_STACK_GUARD_REGION, guard_start, |
| Z_ARC_STACK_GUARD_SIZE) < 0) { |
| LOG_ERR("thread %p's stack guard failed", thread); |
| return; |
| } |
| #endif /* CONFIG_MPU_STACK_GUARD */ |
| |
| #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("thread %p's stack failed", thread); |
| return; |
| } |
| } |
| |
| #if defined(CONFIG_MPU_GAP_FILLING) |
| uint32_t num_partitions; |
| struct k_mem_partition *pparts; |
| struct k_mem_domain *mem_domain = thread->mem_domain_info.mem_domain; |
| |
| /* configure thread's memory domain */ |
| if (mem_domain) { |
| LOG_DBG("configure thread %p's domain: %p", |
| thread, mem_domain); |
| num_partitions = mem_domain->num_partitions; |
| pparts = mem_domain->partitions; |
| } else { |
| num_partitions = 0U; |
| pparts = NULL; |
| } |
| |
| for (uint32_t i = 0; i < num_partitions; i++) { |
| if (pparts->size) { |
| if (_dynamic_region_allocate_and_init(pparts->start, |
| pparts->size, pparts->attr) < 0) { |
| LOG_ERR( |
| "thread %p's mem region: %p failed", |
| thread, pparts); |
| return; |
| } |
| } |
| pparts++; |
| } |
| #else |
| arc_core_mpu_configure_mem_domain(thread); |
| #endif |
| #endif |
| } |
| |
| /** |
| * @brief configure the default region |
| * |
| * @param region_attr region attribute of default region |
| */ |
| void arc_core_mpu_default(uint32_t region_attr) |
| { |
| #ifdef CONFIG_ARC_NORMAL_FIRMWARE |
| /* \todo through secure service to access mpu */ |
| #else |
| z_arc_v2_aux_reg_write(_ARC_V2_MPU_EN, region_attr); |
| #endif |
| } |
| |
| /** |
| * @brief configure the MPU region |
| * |
| * @param index MPU region index |
| * @param base base address |
| * @param size region size |
| * @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_RPER_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 |
| */ |
| #if defined(CONFIG_MPU_GAP_FILLING) |
| void arc_core_mpu_configure_mem_domain(struct k_thread *thread) |
| { |
| arc_core_mpu_configure_thread(thread); |
| } |
| #else |
| void arc_core_mpu_configure_mem_domain(struct k_thread *thread) |
| { |
| uint32_t region_index; |
| uint32_t num_partitions; |
| uint32_t num_regions; |
| 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; |
| } |
| |
| num_regions = get_num_regions(); |
| region_index = get_region_index_by_type(THREAD_DOMAIN_PARTITION_REGION); |
| |
| while (num_partitions && region_index < num_regions) { |
| if (pparts->size > 0) { |
| 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); |
| region_index++; |
| } |
| pparts++; |
| num_partitions--; |
| } |
| |
| while (region_index < num_regions) { |
| /* clear the left mpu entries */ |
| _region_init(region_index, 0, 0, 0); |
| region_index++; |
| } |
| } |
| #endif |
| |
| /** |
| * @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) |
| { |
| uint32_t num_partitions; |
| struct k_mem_partition *pparts; |
| int index; |
| |
| 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 (uint32_t i = 0; i < num_partitions; i++) { |
| if (pparts->size) { |
| index = _get_region_index(pparts->start, |
| pparts->size); |
| if (index > 0) { |
| #if defined(CONFIG_MPU_GAP_FILLING) |
| _region_set_attr(index, |
| REGION_KERNEL_RAM_ATTR); |
| #else |
| _region_init(index, 0, 0, 0); |
| #endif |
| } |
| } |
| pparts++; |
| } |
| } |
| |
| /** |
| * @brief reset MPU region for a single memory partition |
| * |
| * @param partition_id memory partition id |
| */ |
| void arc_core_mpu_remove_mem_partition(struct k_mem_domain *domain, |
| uint32_t partition_id) |
| { |
| struct k_mem_partition *partition = &domain->partitions[partition_id]; |
| |
| int region_index = _get_region_index(partition->start, |
| partition->size); |
| |
| if (region_index < 0) { |
| return; |
| } |
| |
| LOG_DBG("remove region 0x%x", region_index); |
| #if defined(CONFIG_MPU_GAP_FILLING) |
| _region_set_attr(region_index, REGION_KERNEL_RAM_ATTR); |
| #else |
| _region_init(region_index, 0, 0, 0); |
| #endif |
| } |
| |
| /** |
| * @brief get the maximum number of free regions for memory domain partitions |
| */ |
| int arc_core_mpu_get_max_domain_partition_regions(void) |
| { |
| #if defined(CONFIG_MPU_GAP_FILLING) |
| /* consider the worst case: each partition requires split */ |
| return (get_num_regions() - MPU_REGION_NUM_FOR_THREAD) / 2; |
| #else |
| return get_num_regions() - |
| get_region_index_by_type(THREAD_DOMAIN_PARTITION_REGION) - 1; |
| #endif |
| } |
| |
| /** |
| * @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; |
| int key = arch_irq_lock(); |
| |
| /* |
| * For ARC MPU v4, overlapping is not supported. |
| * we can stop the iteration immediately once we find the |
| * matched region that grants permission or denies access. |
| */ |
| r_index = _mpu_probe((uint32_t)addr); |
| /* match and the area is in one region */ |
| if (r_index >= 0 && r_index == _mpu_probe((uint32_t)addr + (size - 1))) { |
| if (_is_user_accessible_region(r_index, write)) { |
| r_index = 0; |
| } else { |
| r_index = -EPERM; |
| } |
| } else { |
| r_index = -EPERM; |
| } |
| |
| arch_irq_unlock(key); |
| |
| return r_index; |
| } |
| #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(void) |
| { |
| 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; |
| } |
| |
| static_regions_num = 0U; |
| |
| /* Disable MPU */ |
| arc_core_mpu_disable(); |
| |
| for (i = 0U; i < mpu_config.num_regions; i++) { |
| /* skip empty region */ |
| if (mpu_config.mpu_regions[i].size == 0) { |
| continue; |
| } |
| #if defined(CONFIG_MPU_GAP_FILLING) |
| _region_init(static_regions_num, |
| mpu_config.mpu_regions[i].base, |
| mpu_config.mpu_regions[i].size, |
| mpu_config.mpu_regions[i].attr); |
| |
| /* record the static region which can be split */ |
| if (mpu_config.mpu_regions[i].attr & REGION_DYNAMIC) { |
| if (dynamic_regions_num >= |
| MPU_DYNAMIC_REGION_AREAS_NUM) { |
| LOG_ERR("not enough dynamic regions %d", |
| dynamic_regions_num); |
| return -EINVAL; |
| } |
| |
| dyn_reg_info[dynamic_regions_num].index = i; |
| dyn_reg_info[dynamic_regions_num].base = |
| mpu_config.mpu_regions[i].base; |
| dyn_reg_info[dynamic_regions_num].size = |
| mpu_config.mpu_regions[i].size; |
| dyn_reg_info[dynamic_regions_num].attr = |
| mpu_config.mpu_regions[i].attr; |
| |
| dynamic_regions_num++; |
| } |
| static_regions_num++; |
| #else |
| /* dynamic region will be covered by default mpu setting |
| * no need to configure |
| */ |
| if (!(mpu_config.mpu_regions[i].attr & REGION_DYNAMIC)) { |
| _region_init(static_regions_num, |
| mpu_config.mpu_regions[i].base, |
| mpu_config.mpu_regions[i].size, |
| mpu_config.mpu_regions[i].attr); |
| static_regions_num++; |
| } |
| #endif |
| } |
| |
| for (i = static_regions_num; i < num_regions; i++) { |
| _region_init(i, 0, 0, 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_V4_INTERNAL_H_ */ |