/* * 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(const 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(void) { 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_ */