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