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 */
_mpu_configure(uint8_t type,uint32_t base,uint32_t size)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 */
arc_core_mpu_enable(void)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 */
arc_core_mpu_disable(void)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 */
arc_core_mpu_configure_thread(struct k_thread * thread)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 */
arc_core_mpu_default(uint32_t region_attr)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 */
arc_core_mpu_region(uint32_t index,uint32_t base,uint32_t size,uint32_t region_attr)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 */
arc_core_mpu_configure_mem_domain(struct k_thread * thread)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 */
arc_core_mpu_remove_mem_domain(struct k_mem_domain * mem_domain)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 */
arc_core_mpu_remove_mem_partition(struct k_mem_domain * domain,uint32_t part_id)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 */
arc_core_mpu_get_max_domain_partition_regions(void)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 */
arc_core_mpu_buffer_validate(const void * addr,size_t size,int write)210 int arc_core_mpu_buffer_validate(const 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 */
arc_mpu_init(void)241 void arc_mpu_init(void)
242 {
243
244 uint32_t num_regions = get_num_regions();
245
246 if (mpu_config.num_regions > num_regions) {
247 __ASSERT(0, "Request to configure: %u regions (supported: %u)\n",
248 mpu_config.num_regions, num_regions);
249 }
250
251 /* Disable MPU */
252 arc_core_mpu_disable();
253
254 /*
255 * the MPU regions are filled in the reverse order.
256 * According to ARCv2 ISA, the MPU region with smaller
257 * index has higher priority. The static background MPU
258 * regions in mpu_config will be in the bottom. Then
259 * the special type regions will be above.
260 */
261 int r_index = num_regions - mpu_config.num_regions;
262
263 /* clear all the regions first */
264 for (uint32_t i = 0U; i < r_index; i++) {
265 _region_init(i, 0, 0, 0);
266 }
267
268 /* configure the static regions */
269 for (uint32_t i = 0U; i < mpu_config.num_regions; i++) {
270 _region_init(r_index, mpu_config.mpu_regions[i].base,
271 mpu_config.mpu_regions[i].size, mpu_config.mpu_regions[i].attr);
272 r_index++;
273 }
274
275 /* default region: no read, write and execute */
276 arc_core_mpu_default(0);
277
278 /* Enable MPU */
279 arc_core_mpu_enable();
280 }
281
282
283 #endif /* ZEPHYR_ARCH_ARC_CORE_MPU_ARC_MPU_COMMON_INTERNAL_H_ */
284