|
| 1 | +/* |
| 2 | + * Copyright (c) 2021 Synopsys. |
| 3 | + * |
| 4 | + * SPDX-License-Identifier: Apache-2.0 |
| 5 | + */ |
| 6 | +#ifndef ZEPHYR_ARCH_ARC_CORE_MPU_ARC_MPU_COMMON_INTERNAL_H_ |
| 7 | +#define ZEPHYR_ARCH_ARC_CORE_MPU_ARC_MPU_COMMON_INTERNAL_H_ |
| 8 | + |
| 9 | +#if CONFIG_ARC_MPU_VER == 2 || CONFIG_ARC_MPU_VER == 3 |
| 10 | +#include "arc_mpu_v2_internal.h" |
| 11 | +#elif CONFIG_ARC_MPU_VER == 6 |
| 12 | +#include "arc_mpu_v6_internal.h" |
| 13 | +#else |
| 14 | +#error "Unsupported MPU version" |
| 15 | +#endif |
| 16 | + |
| 17 | +/** |
| 18 | + * @brief configure the base address and size for an MPU region |
| 19 | + * |
| 20 | + * @param type MPU region type |
| 21 | + * @param base base address in RAM |
| 22 | + * @param size size of the region |
| 23 | + */ |
| 24 | +static inline int _mpu_configure(uint8_t type, uint32_t base, uint32_t size) |
| 25 | +{ |
| 26 | + int32_t region_index = get_region_index_by_type(type); |
| 27 | + uint32_t region_attr = get_region_attr_by_type(type); |
| 28 | + |
| 29 | + LOG_DBG("Region info: 0x%x 0x%x", base, size); |
| 30 | + |
| 31 | + if (region_attr == 0U || region_index < 0) { |
| 32 | + return -EINVAL; |
| 33 | + } |
| 34 | + |
| 35 | + /* |
| 36 | + * For ARC MPU, MPU regions can be overlapped, smaller |
| 37 | + * region index has higher priority. |
| 38 | + */ |
| 39 | + _region_init(region_index, base, size, region_attr); |
| 40 | + |
| 41 | + return 0; |
| 42 | +} |
| 43 | + |
| 44 | +/* ARC Core MPU Driver API Implementation for ARC MP */ |
| 45 | + |
| 46 | +/** |
| 47 | + * @brief enable the MPU |
| 48 | + */ |
| 49 | +void arc_core_mpu_enable(void) |
| 50 | +{ |
| 51 | + /* Enable MPU */ |
| 52 | + z_arc_v2_aux_reg_write(_ARC_V2_MPU_EN, |
| 53 | + z_arc_v2_aux_reg_read(_ARC_V2_MPU_EN) | AUX_MPU_EN_ENABLE); |
| 54 | +} |
| 55 | + |
| 56 | +/** |
| 57 | + * @brief disable the MPU |
| 58 | + */ |
| 59 | +void arc_core_mpu_disable(void) |
| 60 | +{ |
| 61 | + /* Disable MPU */ |
| 62 | + z_arc_v2_aux_reg_write(_ARC_V2_MPU_EN, |
| 63 | + z_arc_v2_aux_reg_read(_ARC_V2_MPU_EN) & AUX_MPU_EN_DISABLE); |
| 64 | +} |
| 65 | + |
| 66 | +/** |
| 67 | + * @brief configure the thread's MPU regions |
| 68 | + * |
| 69 | + * @param thread the target thread |
| 70 | + */ |
| 71 | +void arc_core_mpu_configure_thread(struct k_thread *thread) |
| 72 | +{ |
| 73 | +#if defined(CONFIG_USERSPACE) |
| 74 | + /* configure stack region of user thread */ |
| 75 | + if (thread->base.user_options & K_USER) { |
| 76 | + LOG_DBG("configure user thread %p's stack", thread); |
| 77 | + if (_mpu_configure(THREAD_STACK_USER_REGION, |
| 78 | + (uint32_t)thread->stack_info.start, |
| 79 | + thread->stack_info.size) < 0) { |
| 80 | + LOG_ERR("user thread %p's stack failed", thread); |
| 81 | + return; |
| 82 | + } |
| 83 | + } |
| 84 | + |
| 85 | + LOG_DBG("configure thread %p's domain", thread); |
| 86 | + arc_core_mpu_configure_mem_domain(thread); |
| 87 | +#endif |
| 88 | +} |
| 89 | + |
| 90 | + |
| 91 | +/** |
| 92 | + * @brief configure the default region |
| 93 | + * |
| 94 | + * @param region_attr region attribute of default region |
| 95 | + */ |
| 96 | +void arc_core_mpu_default(uint32_t region_attr) |
| 97 | +{ |
| 98 | + uint32_t val = z_arc_v2_aux_reg_read(_ARC_V2_MPU_EN) & (~AUX_MPU_RDP_ATTR_MASK); |
| 99 | + |
| 100 | + region_attr &= AUX_MPU_RDP_ATTR_MASK; |
| 101 | + z_arc_v2_aux_reg_write(_ARC_V2_MPU_EN, region_attr | val); |
| 102 | +} |
| 103 | + |
| 104 | +/** |
| 105 | + * @brief configure the MPU region |
| 106 | + * |
| 107 | + * @param index MPU region index |
| 108 | + * @param base base address |
| 109 | + * @param region_attr region attribute |
| 110 | + */ |
| 111 | +int arc_core_mpu_region(uint32_t index, uint32_t base, uint32_t size, uint32_t region_attr) |
| 112 | +{ |
| 113 | + if (index >= get_num_regions()) { |
| 114 | + return -EINVAL; |
| 115 | + } |
| 116 | + |
| 117 | + region_attr &= AUX_MPU_RDP_ATTR_MASK; |
| 118 | + |
| 119 | + _region_init(index, base, size, region_attr); |
| 120 | + |
| 121 | + return 0; |
| 122 | +} |
| 123 | + |
| 124 | +#if defined(CONFIG_USERSPACE) |
| 125 | + |
| 126 | +/** |
| 127 | + * @brief configure MPU regions for the memory partitions of the memory domain |
| 128 | + * |
| 129 | + * @param thread the thread which has memory domain |
| 130 | + */ |
| 131 | +void arc_core_mpu_configure_mem_domain(struct k_thread *thread) |
| 132 | +{ |
| 133 | + int region_index = get_region_index_by_type(THREAD_DOMAIN_PARTITION_REGION); |
| 134 | + uint32_t num_partitions; |
| 135 | + struct k_mem_partition *pparts; |
| 136 | + struct k_mem_domain *mem_domain = NULL; |
| 137 | + |
| 138 | + if (thread) { |
| 139 | + mem_domain = thread->mem_domain_info.mem_domain; |
| 140 | + } |
| 141 | + |
| 142 | + if (mem_domain) { |
| 143 | + LOG_DBG("configure domain: %p", mem_domain); |
| 144 | + num_partitions = mem_domain->num_partitions; |
| 145 | + pparts = mem_domain->partitions; |
| 146 | + } else { |
| 147 | + LOG_DBG("disable domain partition regions"); |
| 148 | + num_partitions = 0U; |
| 149 | + pparts = NULL; |
| 150 | + } |
| 151 | + |
| 152 | + for (; region_index >= 0; region_index--) { |
| 153 | + if (num_partitions) { |
| 154 | + LOG_DBG("set region 0x%x 0x%lx 0x%x", |
| 155 | + region_index, pparts->start, pparts->size); |
| 156 | + _region_init(region_index, pparts->start, pparts->size, pparts->attr); |
| 157 | + num_partitions--; |
| 158 | + } else { |
| 159 | + /* clear the left mpu entries */ |
| 160 | + _region_init(region_index, 0, 0, 0); |
| 161 | + } |
| 162 | + pparts++; |
| 163 | + } |
| 164 | +} |
| 165 | + |
| 166 | +/** |
| 167 | + * @brief remove MPU regions for the memory partitions of the memory domain |
| 168 | + * |
| 169 | + * @param mem_domain the target memory domain |
| 170 | + */ |
| 171 | +void arc_core_mpu_remove_mem_domain(struct k_mem_domain *mem_domain) |
| 172 | +{ |
| 173 | + ARG_UNUSED(mem_domain); |
| 174 | + |
| 175 | + int region_index = get_region_index_by_type(THREAD_DOMAIN_PARTITION_REGION); |
| 176 | + |
| 177 | + for (; region_index >= 0; region_index--) { |
| 178 | + _region_init(region_index, 0, 0, 0); |
| 179 | + } |
| 180 | +} |
| 181 | + |
| 182 | +/** |
| 183 | + * @brief reset MPU region for a single memory partition |
| 184 | + * |
| 185 | + * @param domain the target memory domain |
| 186 | + * @param partition_id memory partition id |
| 187 | + */ |
| 188 | +void arc_core_mpu_remove_mem_partition(struct k_mem_domain *domain, uint32_t part_id) |
| 189 | +{ |
| 190 | + ARG_UNUSED(domain); |
| 191 | + |
| 192 | + int region_index = get_region_index_by_type(THREAD_DOMAIN_PARTITION_REGION); |
| 193 | + |
| 194 | + LOG_DBG("disable region 0x%x", region_index + part_id); |
| 195 | + /* Disable region */ |
| 196 | + _region_init(region_index + part_id, 0, 0, 0); |
| 197 | +} |
| 198 | + |
| 199 | +/** |
| 200 | + * @brief get the maximum number of free regions for memory domain partitions |
| 201 | + */ |
| 202 | +int arc_core_mpu_get_max_domain_partition_regions(void) |
| 203 | +{ |
| 204 | + return get_region_index_by_type(THREAD_DOMAIN_PARTITION_REGION) + 1; |
| 205 | +} |
| 206 | + |
| 207 | +/** |
| 208 | + * @brief validate the given buffer is user accessible or not |
| 209 | + */ |
| 210 | +int arc_core_mpu_buffer_validate(void *addr, size_t size, int write) |
| 211 | +{ |
| 212 | + /* |
| 213 | + * For ARC MPU, smaller region number takes priority. |
| 214 | + * we can stop the iteration immediately once we find the |
| 215 | + * matched region that grants permission or denies access. |
| 216 | + * |
| 217 | + */ |
| 218 | + for (int r_index = 0; r_index < get_num_regions(); r_index++) { |
| 219 | + if (!_is_enabled_region(r_index) || !_is_in_region(r_index, (uint32_t)addr, size)) { |
| 220 | + continue; |
| 221 | + } |
| 222 | + |
| 223 | + if (_is_user_accessible_region(r_index, write)) { |
| 224 | + return 0; |
| 225 | + } else { |
| 226 | + return -EPERM; |
| 227 | + } |
| 228 | + } |
| 229 | + |
| 230 | + return -EPERM; |
| 231 | +} |
| 232 | +#endif /* CONFIG_USERSPACE */ |
| 233 | + |
| 234 | +/* ARC MPU Driver Initial Setup */ |
| 235 | +/* |
| 236 | + * @brief MPU default initialization and configuration |
| 237 | + * |
| 238 | + * This function provides the default configuration mechanism for the Memory |
| 239 | + * Protection Unit (MPU). |
| 240 | + */ |
| 241 | +static int arc_mpu_init(const struct device *arg) |
| 242 | +{ |
| 243 | + ARG_UNUSED(arg); |
| 244 | + |
| 245 | + uint32_t num_regions = get_num_regions(); |
| 246 | + |
| 247 | + if (mpu_config.num_regions > num_regions) { |
| 248 | + __ASSERT(0, "Request to configure: %u regions (supported: %u)\n", |
| 249 | + mpu_config.num_regions, num_regions); |
| 250 | + return -EINVAL; |
| 251 | + } |
| 252 | + |
| 253 | + /* Disable MPU */ |
| 254 | + arc_core_mpu_disable(); |
| 255 | + |
| 256 | + /* |
| 257 | + * the MPU regions are filled in the reverse order. |
| 258 | + * According to ARCv2 ISA, the MPU region with smaller |
| 259 | + * index has higher priority. The static background MPU |
| 260 | + * regions in mpu_config will be in the bottom. Then |
| 261 | + * the special type regions will be above. |
| 262 | + */ |
| 263 | + int r_index = num_regions - mpu_config.num_regions; |
| 264 | + |
| 265 | + /* clear all the regions first */ |
| 266 | + for (uint32_t i = 0U; i < r_index; i++) { |
| 267 | + _region_init(i, 0, 0, 0); |
| 268 | + } |
| 269 | + |
| 270 | + /* configure the static regions */ |
| 271 | + for (uint32_t i = 0U; i < mpu_config.num_regions; i++) { |
| 272 | + _region_init(r_index, mpu_config.mpu_regions[i].base, |
| 273 | + mpu_config.mpu_regions[i].size, mpu_config.mpu_regions[i].attr); |
| 274 | + r_index++; |
| 275 | + } |
| 276 | + |
| 277 | + /* default region: no read, write and execute */ |
| 278 | + arc_core_mpu_default(0); |
| 279 | + |
| 280 | + /* Enable MPU */ |
| 281 | + arc_core_mpu_enable(); |
| 282 | + |
| 283 | + return 0; |
| 284 | +} |
| 285 | + |
| 286 | +SYS_INIT(arc_mpu_init, PRE_KERNEL_1, CONFIG_KERNEL_INIT_PRIORITY_DEFAULT); |
| 287 | + |
| 288 | +#endif /* ZEPHYR_ARCH_ARC_CORE_MPU_ARC_MPU_COMMON_INTERNAL_H_ */ |
0 commit comments