| /* |
| * Copyright (c) 2017 Synopsys. |
| * |
| * SPDX-License-Identifier: Apache-2.0 |
| */ |
| |
| #include <device.h> |
| #include <init.h> |
| #include <kernel.h> |
| #include <soc.h> |
| #include <arch/arc/v2/aux_regs.h> |
| #include <arch/arc/v2/mpu/arc_mpu.h> |
| #include <arch/arc/v2/mpu/arc_core_mpu.h> |
| #include <linker/linker-defs.h> |
| #include <logging/sys_log.h> |
| |
| |
| #define AUX_MPU_RDB_VALID_MASK (0x1) |
| #define AUX_MPU_EN_ENABLE (0x40000000) |
| #define AUX_MPU_EN_DISABLE (0xBFFFFFFF) |
| |
| #define AUX_MPU_RDP_REGION_SIZE(bits) \ |
| (((bits - 1) & 0x3) | (((bits - 1) & 0x1C) << 7)) |
| |
| #define AUX_MPU_RDP_ATTR_MASK (0xFFF) |
| |
| #define _ARC_V2_MPU_EN (0x409) |
| #define _ARC_V2_MPU_RDB0 (0x422) |
| #define _ARC_V2_MPU_RDP0 (0x423) |
| |
| /* aux regs added in MPU version 3 */ |
| #define _ARC_V2_MPU_INDEX (0x448) /* MPU index */ |
| #define _ARC_V2_MPU_RSTART (0x449) /* MPU region start address */ |
| #define _ARC_V2_MPU_REND (0x44A) /* MPU region end address */ |
| #define _ARC_V2_MPU_RPER (0x44B) /* MPU region permission register */ |
| #define _ARC_V2_MPU_PROBE (0x44C) /* MPU probe register */ |
| |
| /* For MPU version 2, the minimum protection region size is 2048 bytes */ |
| /* FOr MPU version 3, the minimum protection region size is 32 bytes */ |
| #if CONFIG_ARC_MPU_VER == 2 |
| #define ARC_FEATURE_MPU_ALIGNMENT_BITS 11 |
| #elif CONFIG_ARC_MPU_VER == 3 |
| #define ARC_FEATURE_MPU_ALIGNMENT_BITS 5 |
| #endif |
| |
| #define CALC_REGION_END_ADDR(start, size) \ |
| (start + size - (1 << ARC_FEATURE_MPU_ALIGNMENT_BITS)) |
| |
| |
| /** |
| * @brief Get the number of supported MPU regions |
| * |
| */ |
| static inline u8_t _get_num_regions(void) |
| { |
| u32_t num = _arc_v2_aux_reg_read(_ARC_V2_MPU_BUILD); |
| |
| num = (num & 0xFF00) >> 8; |
| |
| return (u8_t)num; |
| } |
| |
| /** |
| * This internal function is utilized by the MPU driver to parse the intent |
| * type (i.e. THREAD_STACK_REGION) and return the correct parameter set. |
| */ |
| static inline u32_t _get_region_attr_by_type(u32_t type) |
| { |
| switch (type) { |
| case THREAD_STACK_USER_REGION: |
| return REGION_RAM_ATTR; |
| case THREAD_STACK_REGION: |
| return AUX_MPU_RDP_KW | AUX_MPU_RDP_KR; |
| case THREAD_APP_DATA_REGION: |
| return REGION_RAM_ATTR; |
| case THREAD_STACK_GUARD_REGION: |
| /* no Write and Execute to guard region */ |
| return AUX_MPU_RDP_UR | AUX_MPU_RDP_KR; |
| default: |
| /* Size 0 region */ |
| return 0; |
| } |
| } |
| |
| static inline void _region_init(u32_t index, u32_t region_addr, u32_t size, |
| u32_t region_attr) |
| { |
| /* ARC MPU version 2 and version 3 have different aux reg interface */ |
| #if CONFIG_ARC_MPU_VER == 2 |
| u8_t bits = find_msb_set(size) - 1; |
| index = 2 * index; |
| |
| if (bits < ARC_FEATURE_MPU_ALIGNMENT_BITS) { |
| bits = ARC_FEATURE_MPU_ALIGNMENT_BITS; |
| } |
| |
| if ((1 << bits) < size) { |
| bits++; |
| } |
| |
| if (size > 0) { |
| region_attr |= AUX_MPU_RDP_REGION_SIZE(bits); |
| region_addr |= AUX_MPU_RDB_VALID_MASK; |
| } else { |
| region_addr = 0; |
| } |
| |
| _arc_v2_aux_reg_write(_ARC_V2_MPU_RDP0 + index, region_attr); |
| _arc_v2_aux_reg_write(_ARC_V2_MPU_RDB0 + index, region_addr); |
| |
| #elif CONFIG_ARC_MPU_VER == 3 |
| #define AUX_MPU_RPER_SID1 0x10000 |
| if (size < (1 << ARC_FEATURE_MPU_ALIGNMENT_BITS)) { |
| size = (1 << ARC_FEATURE_MPU_ALIGNMENT_BITS); |
| } |
| |
| /* all MPU regions SID are the same: 1, the default SID */ |
| if (region_attr) { |
| region_attr |= (AUX_MPU_RDB_VALID_MASK | AUX_MPU_RDP_S | |
| AUX_MPU_RPER_SID1); |
| } |
| |
| _arc_v2_aux_reg_write(_ARC_V2_MPU_INDEX, index); |
| _arc_v2_aux_reg_write(_ARC_V2_MPU_RSTART, region_addr); |
| _arc_v2_aux_reg_write(_ARC_V2_MPU_REND, |
| CALC_REGION_END_ADDR(region_addr, size)); |
| _arc_v2_aux_reg_write(_ARC_V2_MPU_RPER, region_attr); |
| #endif |
| } |
| |
| #if CONFIG_ARC_MPU_VER == 3 |
| static inline s32_t _mpu_probe(u32_t addr) |
| { |
| u32_t val; |
| |
| _arc_v2_aux_reg_write(_ARC_V2_MPU_PROBE, addr); |
| val = _arc_v2_aux_reg_read(_ARC_V2_MPU_INDEX); |
| |
| /* if no match or multiple regions match, return error */ |
| if (val & 0xC0000000) { |
| return -1; |
| } else { |
| return val; |
| } |
| } |
| #endif |
| |
| /** |
| * 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 u32_t _get_region_index_by_type(u32_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. |
| * |
| * For ARC MPU v3, each index has the same priority, so the index is |
| * allocated from small to big. Static regions start from 0, then |
| * thread related regions. |
| */ |
| switch (type) { |
| #if CONFIG_ARC_MPU_VER == 2 |
| 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_STACK_GUARD_REGION: |
| return _get_num_regions() - mpu_config.num_regions - type; |
| case THREAD_DOMAIN_PARTITION_REGION: |
| #if defined(CONFIG_MPU_STACK_GUARD) |
| return _get_num_regions() - mpu_config.num_regions - type; |
| #else |
| /* |
| * Start domain partition region from stack guard region |
| * since stack guard is not enabled. |
| */ |
| return _get_num_regions() - mpu_config.num_regions - type + 1; |
| #endif |
| #elif CONFIG_ARC_MPU_VER == 3 |
| case THREAD_STACK_USER_REGION: |
| return mpu_config.num_regions + THREAD_STACK_REGION - 1; |
| case THREAD_STACK_REGION: |
| case THREAD_APP_DATA_REGION: |
| case THREAD_STACK_GUARD_REGION: |
| return mpu_config.num_regions + type - 1; |
| case THREAD_DOMAIN_PARTITION_REGION: |
| #if defined(CONFIG_MPU_STACK_GUARD) |
| return mpu_config.num_regions + type - 1; |
| #else |
| /* |
| * Start domain partition region from stack guard region |
| * since stack guard is not enabled. |
| */ |
| return mpu_config.num_regions + type - 2; |
| #endif |
| #endif |
| default: |
| __ASSERT(0, "Unsupported type"); |
| return 0; |
| } |
| } |
| |
| /** |
| * This internal function checks if region is enabled or not |
| */ |
| static inline int _is_enabled_region(u32_t r_index) |
| { |
| #if CONFIG_ARC_MPU_VER == 2 |
| return ((_arc_v2_aux_reg_read(_ARC_V2_MPU_RDB0 + 2 * r_index) |
| & AUX_MPU_RDB_VALID_MASK) == AUX_MPU_RDB_VALID_MASK); |
| #elif CONFIG_ARC_MPU_VER == 3 |
| _arc_v2_aux_reg_write(_ARC_V2_MPU_INDEX, r_index); |
| return ((_arc_v2_aux_reg_read(_ARC_V2_MPU_RPER) & |
| AUX_MPU_RDB_VALID_MASK) == AUX_MPU_RDB_VALID_MASK); |
| #endif |
| } |
| |
| /** |
| * This internal function check if the given buffer in in the region |
| */ |
| static inline int _is_in_region(u32_t r_index, u32_t start, u32_t size) |
| { |
| #if CONFIG_ARC_MPU_VER == 2 |
| u32_t r_addr_start; |
| u32_t r_addr_end; |
| u32_t r_size_lshift; |
| |
| r_addr_start = _arc_v2_aux_reg_read(_ARC_V2_MPU_RDB0 + 2 * r_index) |
| & (~AUX_MPU_RDB_VALID_MASK); |
| r_size_lshift = _arc_v2_aux_reg_read(_ARC_V2_MPU_RDP0 + 2 * r_index) |
| & AUX_MPU_RDP_ATTR_MASK; |
| r_size_lshift = (r_size_lshift & 0x3) | ((r_size_lshift >> 7) & 0x1C); |
| r_addr_end = r_addr_start + (1 << (r_size_lshift + 1)); |
| |
| if (start >= r_addr_start && (start + size) < r_addr_end) { |
| return 1; |
| } |
| |
| #elif CONFIG_ARC_MPU_VER == 3 |
| |
| if ((r_index == _mpu_probe(start)) && |
| (r_index == _mpu_probe(start + size))) { |
| return 1; |
| } |
| #endif |
| |
| |
| |
| return 0; |
| } |
| |
| /** |
| * This internal function check if the region is user accessible or not |
| */ |
| static inline int _is_user_accessible_region(u32_t r_index, int write) |
| { |
| u32_t r_ap; |
| |
| #if CONFIG_ARC_MPU_VER == 2 |
| r_ap = _arc_v2_aux_reg_read(_ARC_V2_MPU_RDP0 + 2 * r_index); |
| #elif CONFIG_ARC_MPU_VER == 3 |
| _arc_v2_aux_reg_write(_ARC_V2_MPU_INDEX, r_index); |
| r_ap = _arc_v2_aux_reg_read(_ARC_V2_MPU_RPER); |
| #endif |
| r_ap &= AUX_MPU_RDP_ATTR_MASK; |
| |
| if (write) { |
| return ((r_ap & (AUX_MPU_RDP_UW | AUX_MPU_RDP_KW)) == |
| (AUX_MPU_RDP_UW | AUX_MPU_RDP_KW)); |
| } |
| |
| return ((r_ap & (AUX_MPU_RDP_UR | AUX_MPU_RDP_KR)) == |
| (AUX_MPU_RDP_UR | AUX_MPU_RDP_KR)); |
| } |
| |
| /* ARC Core MPU Driver API Implementation for ARC MPU */ |
| |
| /** |
| * @brief enable the MPU |
| */ |
| void arc_core_mpu_enable(void) |
| { |
| #if CONFIG_ARC_MPU_VER == 2 |
| /* Enable MPU */ |
| _arc_v2_aux_reg_write(_ARC_V2_MPU_EN, |
| _arc_v2_aux_reg_read(_ARC_V2_MPU_EN) | AUX_MPU_EN_ENABLE); |
| |
| /* MPU is always enabled, use default region to |
| * simulate MPU enable |
| */ |
| #elif CONFIG_ARC_MPU_VER == 3 |
| arc_core_mpu_default(0); |
| #endif |
| } |
| |
| /** |
| * @brief disable the MPU |
| */ |
| void arc_core_mpu_disable(void) |
| { |
| #if CONFIG_ARC_MPU_VER == 2 |
| /* Disable MPU */ |
| _arc_v2_aux_reg_write(_ARC_V2_MPU_EN, |
| _arc_v2_aux_reg_read(_ARC_V2_MPU_EN) & AUX_MPU_EN_DISABLE); |
| #elif CONFIG_ARC_MPU_VER == 3 |
| /* MPU is always enabled, use default region to |
| * simulate MPU disable |
| */ |
| arc_core_mpu_default(REGION_ALL_ATTR); |
| #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 |
| */ |
| void arc_core_mpu_configure(u8_t type, u32_t base, u32_t size) |
| { |
| u32_t region_index = _get_region_index_by_type(type); |
| u32_t region_attr = _get_region_attr_by_type(type); |
| |
| SYS_LOG_DBG("Region info: 0x%x 0x%x", base, size); |
| |
| if (region_attr == 0) { |
| return; |
| } |
| /* |
| * The new MPU regions are allocated per type before |
| * the statically configured regions. |
| */ |
| #if CONFIG_ARC_MPU_VER == 2 |
| /* |
| * For ARC MPU v2, MPU regions can be overlapped, smaller |
| * region index has higher priority. |
| */ |
| _region_init(region_index, base, size, region_attr); |
| #elif CONFIG_ARC_MPU_VER == 3 |
| static s32_t last_index; |
| s32_t index; |
| u32_t last_region = _get_num_regions() - 1; |
| |
| |
| /* use hardware probe to find the region maybe split. |
| * another way is to look up the mpu_config.mpu_regions |
| * in software, which is time consuming. |
| */ |
| index = _mpu_probe(base); |
| |
| /* ARC MPU version 3 doesn't support region overlap. |
| * So it can not be directly used for stack/stack guard protect |
| * One way to do this is splitting the ram region as follow: |
| * |
| * Take THREAD_STACK_GUARD_REGION as example: |
| * RAM region 0: the ram region before THREAD_STACK_GUARD_REGION, rw |
| * RAM THREAD_STACK_GUARD_REGION: RO |
| * RAM region 1: the region after THREAD_STACK_GUARD_REGION, same |
| * as region 0 |
| * if region_index == index, it means the same thread comes back, |
| * no need to split |
| */ |
| |
| if (index >= 0 && region_index != index) { |
| /* need to split, only 1 split is allowed */ |
| /* find the correct region to mpu_config.mpu_regions */ |
| if (index == last_region) { |
| /* already split */ |
| index = last_index; |
| } else { |
| /* new split */ |
| last_index = index; |
| } |
| |
| _region_init(index, |
| mpu_config.mpu_regions[index].base, |
| base - mpu_config.mpu_regions[index].base, |
| mpu_config.mpu_regions[index].attr); |
| |
| #if defined(CONFIG_MPU_STACK_GUARD) |
| if (type != THREAD_STACK_USER_REGION) |
| /* |
| * USER REGION is continuous with MPU_STACK_GUARD. |
| * In current implementation, THREAD_STACK_GUARD_REGION is |
| * configured before THREAD_STACK_USER_REGION |
| */ |
| #endif |
| _region_init(last_region, base + size, |
| (mpu_config.mpu_regions[index].base + |
| mpu_config.mpu_regions[index].size - base - size), |
| mpu_config.mpu_regions[index].attr); |
| } |
| |
| _region_init(region_index, base, size, region_attr); |
| #endif |
| } |
| |
| /** |
| * @brief configure the default region |
| * |
| * @param region_attr region attribute of default region |
| */ |
| void arc_core_mpu_default(u32_t region_attr) |
| { |
| u32_t val = _arc_v2_aux_reg_read(_ARC_V2_MPU_EN) & |
| (~AUX_MPU_RDP_ATTR_MASK); |
| |
| region_attr &= AUX_MPU_RDP_ATTR_MASK; |
| |
| _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 |
| */ |
| void arc_core_mpu_region(u32_t index, u32_t base, u32_t size, |
| u32_t region_attr) |
| { |
| if (index >= _get_num_regions()) { |
| return; |
| } |
| |
| region_attr &= AUX_MPU_RDP_ATTR_MASK; |
| |
| _region_init(index, base, size, region_attr); |
| } |
| |
| #if defined(CONFIG_USERSPACE) |
| void arc_core_mpu_configure_user_context(struct k_thread *thread) |
| { |
| u32_t base = (u32_t)thread->stack_obj; |
| u32_t size = thread->stack_info.size; |
| |
| /* for kernel threads, no need to configure user context */ |
| if (!thread->arch.priv_stack_start) { |
| return; |
| } |
| |
| arc_core_mpu_configure(THREAD_STACK_USER_REGION, base, size); |
| |
| /* configure app data portion */ |
| #ifdef CONFIG_APPLICATION_MEMORY |
| #if CONFIG_ARC_MPU_VER == 2 |
| /* |
| * _app_ram_size is guaranteed to be power of two, and |
| * _app_ram_start is guaranteed to be aligned _app_ram_size |
| * in linker template |
| */ |
| base = (u32_t)&__app_ram_start; |
| size = (u32_t)&__app_ram_size; |
| |
| /* set up app data region if exists, otherwise disable */ |
| if (size > 0) { |
| arc_core_mpu_configure(THREAD_APP_DATA_REGION, base, size); |
| } |
| #elif CONFIG_ARC_MPU_VER == 3 |
| /* |
| * ARC MPV v3 doesn't support MPU region overlap. |
| * Application memory should be a static memory, defined in mpu_config |
| */ |
| #endif |
| #endif |
| } |
| |
| /** |
| * @brief configure MPU regions for the memory partitions of the memory domain |
| * |
| * @param mem_domain memory domain that thread belongs to |
| */ |
| void arc_core_mpu_configure_mem_domain(struct k_mem_domain *mem_domain) |
| { |
| s32_t region_index = |
| _get_region_index_by_type(THREAD_DOMAIN_PARTITION_REGION); |
| u32_t num_partitions; |
| struct k_mem_partition *pparts; |
| |
| if (mem_domain) { |
| SYS_LOG_DBG("configure domain: %p", mem_domain); |
| num_partitions = mem_domain->num_partitions; |
| pparts = mem_domain->partitions; |
| } else { |
| SYS_LOG_DBG("disable domain partition regions"); |
| num_partitions = 0; |
| pparts = NULL; |
| } |
| #if CONFIG_ARC_MPU_VER == 2 |
| for (; region_index >= 0; region_index--) { |
| #elif CONFIG_ARC_MPU_VER == 3 |
| /* |
| * Note: For ARC MPU v3, overlapping is not allowed, so the following |
| * partitions/region may be overlapped with each other or regions in |
| * mpu_config. This will cause EV_MachineCheck exception (ECR = 0x030600). |
| * Although split mechanism is used for stack guard region to avoid this, |
| * it doesn't work for memory domain, because the dynamic region numbers. |
| * So be careful to avoid the overlap situation. |
| */ |
| for (; region_index < _get_num_regions() - 1; region_index++) { |
| #endif |
| if (num_partitions && pparts->size) { |
| SYS_LOG_DBG("set region 0x%x 0x%x 0x%x", |
| region_index, pparts->start, pparts->size); |
| _region_init(region_index, pparts->start, pparts->size, |
| pparts->attr); |
| num_partitions--; |
| } else { |
| SYS_LOG_DBG("disable region 0x%x", region_index); |
| /* Disable region */ |
| _region_init(region_index, 0, 0, 0); |
| } |
| pparts++; |
| } |
| } |
| |
| /** |
| * @brief configure MPU region for a single memory partition |
| * |
| * @param part_index memory partition index |
| * @param part memory partition info |
| */ |
| void arc_core_mpu_configure_mem_partition(u32_t part_index, |
| struct k_mem_partition *part) |
| { |
| u32_t region_index = |
| _get_region_index_by_type(THREAD_DOMAIN_PARTITION_REGION); |
| |
| SYS_LOG_DBG("configure partition index: %u", part_index); |
| |
| if (part) { |
| SYS_LOG_DBG("set region 0x%x 0x%x 0x%x", |
| region_index + part_index, part->start, part->size); |
| _region_init(region_index, part->start, part->size, |
| part->attr); |
| } else { |
| SYS_LOG_DBG("disable region 0x%x", region_index + part_index); |
| /* Disable region */ |
| _region_init(region_index + part_index, 0, 0, 0); |
| } |
| } |
| |
| /** |
| * @brief Reset MPU region for a single memory partition |
| * |
| * @param part_index memory partition index |
| */ |
| void arc_core_mpu_mem_partition_remove(u32_t part_index) |
| { |
| u32_t region_index = |
| _get_region_index_by_type(THREAD_DOMAIN_PARTITION_REGION); |
| |
| SYS_LOG_DBG("disable region 0x%x", region_index + part_index); |
| /* Disable region */ |
| _region_init(region_index + part_index, 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) |
| { |
| #if CONFIG_ARC_MPU_VER == 2 |
| return _get_region_index_by_type(THREAD_DOMAIN_PARTITION_REGION) + 1; |
| #elif CONFIG_ARC_MPU_VER == 3 |
| /* |
| * Subtract the start of domain partition regions and 1 reserved region |
| * from total regions will get the maximum number of free regions for |
| * memory domain partitions. |
| */ |
| 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) |
| { |
| s32_t 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 ARC MPU v3, overlapping is not supported. |
| * we can stop the iteration immediately once we find the |
| * matched region that grants permission or denies access. |
| */ |
| #if CONFIG_ARC_MPU_VER == 2 |
| for (r_index = 0; r_index < _get_num_regions(); r_index++) { |
| if (!_is_enabled_region(r_index) || |
| !_is_in_region(r_index, (u32_t)addr, size)) { |
| continue; |
| } |
| |
| if (_is_user_accessible_region(r_index, write)) { |
| return 0; |
| } else { |
| return -EPERM; |
| } |
| } |
| #elif CONFIG_ARC_MPU_VER == 3 |
| r_index = _mpu_probe((u32_t)addr); |
| /* match and the area is in one region */ |
| if (r_index >= 0 && r_index == _mpu_probe((u32_t)addr + size)) { |
| if (_is_user_accessible_region(r_index, write)) { |
| return 0; |
| } else { |
| return -EPERM; |
| } |
| } |
| #endif |
| |
| return -EPERM; |
| } |
| #endif /* CONFIG_USERSPACE */ |
| |
| /* ARC MPU Driver Initial Setup */ |
| |
| /* |
| * @brief MPU default configuration |
| * |
| * This function provides the default configuration mechanism for the Memory |
| * Protection Unit (MPU). |
| */ |
| static void _arc_mpu_config(void) |
| { |
| u32_t num_regions; |
| u32_t i; |
| |
| num_regions = _get_num_regions(); |
| |
| /* ARC MPU supports up to 16 Regions */ |
| if (mpu_config.num_regions > num_regions) { |
| return; |
| } |
| |
| /* Disable MPU */ |
| arc_core_mpu_disable(); |
| |
| #if CONFIG_ARC_MPU_VER == 2 |
| u32_t 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 = 0; i < r_index; i++) { |
| _region_init(i, 0, 0, 0); |
| } |
| |
| /* configure the static regions */ |
| for (i = 0; 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); |
| |
| #elif CONFIG_ARC_MPU_VER == 3 |
| for (i = 0; i < mpu_config.num_regions; i++) { |
| _region_init(i, |
| mpu_config.mpu_regions[i].base, |
| mpu_config.mpu_regions[i].size, |
| mpu_config.mpu_regions[i].attr); |
| } |
| |
| for (; i < num_regions; i++) { |
| _region_init(i, 0, 0, 0); |
| } |
| #endif |
| /* Enable MPU */ |
| arc_core_mpu_enable(); |
| } |
| |
| static int arc_mpu_init(struct device *arg) |
| { |
| ARG_UNUSED(arg); |
| |
| _arc_mpu_config(); |
| |
| return 0; |
| } |
| |
| SYS_INIT(arc_mpu_init, PRE_KERNEL_1, |
| CONFIG_KERNEL_INIT_PRIORITY_DEFAULT); |