1 /*
2  * Copyright (c) 2023 Intel Corporation
3  *
4  * SPDX-License-Identifier: Apache-2.0
5  */
6 
7 #ifndef ZEPHYR_ARCH_XTENSA_XTENSA_MPU_PRIV_H_
8 #define ZEPHYR_ARCH_XTENSA_XTENSA_MPU_PRIV_H_
9 
10 #include <stdint.h>
11 
12 #include <zephyr/toolchain.h>
13 #include <zephyr/arch/xtensa/mpu.h>
14 #include <zephyr/sys/util_macro.h>
15 
16 #include <xtensa/config/core-isa.h>
17 
18 /**
19  * @defgroup xtensa_mpu_internal_apis Xtensa Memory Protection Unit (MPU) Internal APIs
20  * @ingroup xtensa_mpu_apis
21  * @{
22  */
23 
24 /**
25  * @name Bit shifts and masks for MPU entry registers.
26  *
27  * @{
28  */
29 
30 /**
31  * Number of bits to shift for start address in MPU entry register.
32  *
33  * This is only used for aligning the value to the MPU entry register,
34  * and is different than the hardware alignment requirement.
35  */
36 #define XTENSA_MPU_ENTRY_REG_START_ADDR_SHIFT		5U
37 
38 /**
39  * Bit mask of start address in MPU entry register.
40  *
41  * This is only used for aligning the value to the MPU entry register,
42  * and is different than the hardware alignment requirement.
43  */
44 #define XTENSA_MPU_ENTRY_REG_START_ADDR_MASK		0xFFFFFFE0U
45 
46 /** Number of bits to shift for enable bit in MPU entry register. */
47 #define XTENSA_MPU_ENTRY_REG_ENABLE_SHIFT		0U
48 
49 /** Bit mask of enable bit in MPU entry register. */
50 #define XTENSA_MPU_ENTRY_REG_ENABLE_MASK		BIT(XTENSA_MPU_ENTRY_ENABLE_SHIFT)
51 
52 /** Number of bits to shift for lock bit in MPU entry register. */
53 #define XTENSA_MPU_ENTRY_REG_LOCK_SHIFT			1U
54 
55 /** Bit mask of lock bit in MPU entry register. */
56 #define XTENSA_MPU_ENTRY_REG_LOCK_MASK			BIT(XTENSA_MPU_ENTRY_LOCK_SHIFT)
57 
58 /** Number of bits to shift for access rights in MPU entry register. */
59 #define XTENSA_MPU_ENTRY_REG_ACCESS_RIGHTS_SHIFT	8U
60 
61 /** Bit mask of access rights in MPU entry register. */
62 #define XTENSA_MPU_ENTRY_REG_ACCESS_RIGHTS_MASK		\
63 	(0xFU << XTENSA_MPU_ENTRY_REG_ACCESS_RIGHTS_SHIFT)
64 
65 /** Number of bits to shift for memory type in MPU entry register. */
66 #define XTENSA_MPU_ENTRY_REG_MEMORY_TYPE_SHIFT		12U
67 
68 /** Bit mask of memory type in MPU entry register. */
69 #define XTENSA_MPU_ENTRY_REG_MEMORY_TYPE_MASK		\
70 	(0x1FFU << XTENSA_MPU_ENTRY_REG_MEMORY_TYPE_SHIFT)
71 
72 /** Bit mask for foreground entry returned by probing. */
73 #define XTENSA_MPU_PROBE_IS_FG_ENTRY_MASK		BIT(31)
74 
75 /** Bit mask for background entry returned by probing. */
76 #define XTENSA_MPU_PROBE_IS_BG_ENTRY_MASK		BIT(30)
77 
78 /** Bit mask used to determine if entry is valid returned by probing. */
79 #define XTENSA_MPU_PROBE_VALID_ENTRY_MASK		\
80 	(XTENSA_MPU_PROBE_IS_FG_ENTRY_MASK | XTENSA_MPU_PROBE_IS_BG_ENTRY_MASK)
81 
82 /**
83  * @}
84  */
85 
86 /**
87  * @name Bit shifts and masks for MPU PPTLB return value.
88  *
89  * @{
90  */
91 
92 /** Bit shift for segment value. */
93 #define XTENSA_MPU_PPTLB_ACCESS_RIGHTS_SHIFT		8U
94 
95 /** Mask for segment value. */
96 #define XTENSA_MPU_PPTLB_ACCESS_RIGHTS_MASK		0x00000F00U
97 
98 /**
99  * @}
100  */
101 
102 
103 /**
104  * Define one MPU entry of type struct xtensa_mpu_entry.
105  *
106  * @note This needs a comma at the end if used in array declaration.
107  *
108  * @param saddr Start address.
109  * @param en Enable bit
110  * @param rights Access rights.
111  * @param memtype Memory type.
112  */
113 #define XTENSA_MPU_ENTRY(saddr, en, rights, memtype) \
114 	{ \
115 		.as.p.enable = en, \
116 		.as.p.lock = 0, \
117 		.as.p.mbz = 0, \
118 		.as.p.start_addr = (saddr >> XTENSA_MPU_ENTRY_START_ADDR_SHIFT), \
119 		.at.p.segment = 0, \
120 		.at.p.mbz1 = 0, \
121 		.at.p.access_rights = rights, \
122 		.at.p.memory_type = memtype, \
123 		.at.p.mbz2 = 0, \
124 	}
125 
126 /**
127  * @brief Read MPUCFG register.
128  *
129  * This returns the bitmask of enabled MPU entries (foreground segments).
130  *
131  * @return Value of MPUCFG register.
132  */
xtensa_mpu_mpucfg_read(void)133 static ALWAYS_INLINE uint32_t xtensa_mpu_mpucfg_read(void)
134 {
135 	uint32_t mpucfg;
136 
137 	__asm__ __volatile__("rsr.mpucfg %0" : "=a" (mpucfg));
138 
139 	return mpucfg;
140 }
141 
142 /**
143  * @brief Read MPUENB register.
144  *
145  * This returns the enable bits for MPU entries.
146  *
147  * @return Value of MPUENB register.
148  */
xtensa_mpu_mpuenb_read(void)149 static ALWAYS_INLINE uint32_t xtensa_mpu_mpuenb_read(void)
150 {
151 	uint32_t mpuenb;
152 
153 	__asm__ __volatile__("rsr.mpuenb %0" : "=a" (mpuenb));
154 
155 	return mpuenb;
156 }
157 
158 /**
159  * @brief Write MPUENB register.
160  *
161  * This writes the enable bits for MPU entries.
162  *
163  * @param mpuenb Value to be written.
164  */
xtensa_mpu_mpuenb_write(uint32_t mpuenb)165 static ALWAYS_INLINE void xtensa_mpu_mpuenb_write(uint32_t mpuenb)
166 {
167 	__asm__ __volatile__("wsr.mpuenb %0" : : "a"(mpuenb));
168 }
169 
170 /**
171  * @brief Probe for protection TLB entry from an address.
172  *
173  * @param addr Probe address.
174  *
175  * @return Return of the PPTLB instruction.
176  */
xtensa_pptlb_probe(uintptr_t addr)177 static ALWAYS_INLINE uint32_t xtensa_pptlb_probe(uintptr_t addr)
178 {
179 	uint32_t ret;
180 
181 	__asm__ __volatile__("pptlb  %0, %1\n\t" : "=a"(ret) : "a"(addr));
182 	return ret;
183 }
184 
185 /**
186  * @name MPU entry internal helper functions.
187  *
188  * @{
189  */
190 
191 /**
192  * @brief Return the start address encoded in the MPU entry.
193  *
194  * @param entry Pointer to the MPU entry.
195  *
196  * @return Start address.
197  */
198 static ALWAYS_INLINE
xtensa_mpu_entry_start_address_get(const struct xtensa_mpu_entry * entry)199 uintptr_t xtensa_mpu_entry_start_address_get(const struct xtensa_mpu_entry *entry)
200 {
201 	return (entry->as.p.start_addr << XTENSA_MPU_ENTRY_REG_START_ADDR_SHIFT);
202 }
203 
204 /**
205  * @brief Set the start address encoded in the MPU entry.
206  *
207  * @param entry Pointer to the MPU entry.
208  * @param addr Start address.
209  */
210 static ALWAYS_INLINE
xtensa_mpu_entry_start_address_set(struct xtensa_mpu_entry * entry,uintptr_t addr)211 void xtensa_mpu_entry_start_address_set(struct xtensa_mpu_entry *entry, uintptr_t addr)
212 {
213 	entry->as.p.start_addr = addr >> XTENSA_MPU_ENTRY_REG_START_ADDR_SHIFT;
214 }
215 
216 /**
217  * @brief Return the lock bit encoded in the MPU entry.
218  *
219  * @param entry Pointer to the MPU entry.
220  *
221  * @retval True Lock bit is set.
222  * @retval False Lock bit is not set.
223  */
224 static ALWAYS_INLINE
xtensa_mpu_entry_lock_get(const struct xtensa_mpu_entry * entry)225 bool xtensa_mpu_entry_lock_get(const struct xtensa_mpu_entry *entry)
226 {
227 	return entry->as.p.lock != 0;
228 }
229 
230 /**
231  * @brief Set the lock bit encoded in the MPU entry.
232  *
233  * @param entry Pointer to the MPU entry.
234  * @param lock True if to lock the MPU entry.
235  */
236 static ALWAYS_INLINE
xtensa_mpu_entry_lock_set(struct xtensa_mpu_entry * entry,bool lock)237 void xtensa_mpu_entry_lock_set(struct xtensa_mpu_entry *entry, bool lock)
238 {
239 	entry->as.p.lock = lock ? 1 : 0;
240 }
241 
242 /**
243  * @brief Return the enable bit encoded in the MPU entry.
244  *
245  * @param entry Pointer to the MPU entry.
246  *
247  * @retval True Enable bit is set.
248  * @retval False Enable bit is not set.
249  */
250 static ALWAYS_INLINE
xtensa_mpu_entry_enable_get(const struct xtensa_mpu_entry * entry)251 bool xtensa_mpu_entry_enable_get(const struct xtensa_mpu_entry *entry)
252 {
253 	return entry->as.p.enable != 0;
254 }
255 
256 /**
257  * @brief Set the enable bit encoded in the MPU entry.
258  *
259  * @param entry Pointer to the MPU entry.
260  * @param en True if to enable the MPU entry.
261  */
262 static ALWAYS_INLINE
xtensa_mpu_entry_enable_set(struct xtensa_mpu_entry * entry,bool en)263 void xtensa_mpu_entry_enable_set(struct xtensa_mpu_entry *entry, bool en)
264 {
265 	entry->as.p.enable = en ? 1 : 0;
266 }
267 
268 /**
269  * @brief Return the access rights encoded in the MPU entry.
270  *
271  * @param entry Pointer to the MPU entry.
272  *
273  * @return Access right value.
274  */
275 static ALWAYS_INLINE
xtensa_mpu_entry_access_rights_get(const struct xtensa_mpu_entry * entry)276 uint8_t xtensa_mpu_entry_access_rights_get(const struct xtensa_mpu_entry *entry)
277 {
278 	return entry->at.p.access_rights;
279 }
280 
281 /**
282  * @brief Set the lock bit encoded in the MPU entry.
283  *
284  * @param entry Pointer to the MPU entry.
285  * @param access_rights Access rights to be set.
286  */
287 static ALWAYS_INLINE
xtensa_mpu_entry_access_rights_set(struct xtensa_mpu_entry * entry,uint8_t access_rights)288 void xtensa_mpu_entry_access_rights_set(struct xtensa_mpu_entry *entry, uint8_t access_rights)
289 {
290 	entry->at.p.access_rights = access_rights;
291 }
292 
293 /**
294  * @brief Return the memory type encoded in the MPU entry.
295  *
296  * @param entry Pointer to the MPU entry.
297  *
298  * @return Memory type value.
299  */
300 static ALWAYS_INLINE
xtensa_mpu_entry_memory_type_get(const struct xtensa_mpu_entry * entry)301 uint16_t xtensa_mpu_entry_memory_type_get(const struct xtensa_mpu_entry *entry)
302 {
303 	return entry->at.p.memory_type;
304 }
305 
306 /**
307  * @brief Set the memory type in the MPU entry.
308  *
309  * @param entry Pointer to the MPU entry.
310  * @param memory_type Memory type to be set.
311  */
312 static ALWAYS_INLINE
xtensa_mpu_entry_memory_type_set(struct xtensa_mpu_entry * entry,uint16_t memory_type)313 void xtensa_mpu_entry_memory_type_set(struct xtensa_mpu_entry *entry, uint16_t memory_type)
314 {
315 	entry->at.p.memory_type = memory_type;
316 }
317 
318 /**
319  * @brief Set both access rights and memory type of a MPU entry.
320  *
321  * @param entry Pointer to the MPU entry.
322  * @param access_rights Access rights value.
323  * @param memory_type Memory type value.
324  */
325 static inline
xtensa_mpu_entry_attributes_set(struct xtensa_mpu_entry * entry,uint8_t access_rights,uint16_t memory_type)326 void xtensa_mpu_entry_attributes_set(struct xtensa_mpu_entry *entry,
327 				     uint8_t access_rights, uint16_t memory_type)
328 {
329 	xtensa_mpu_entry_access_rights_set(entry, access_rights);
330 	xtensa_mpu_entry_memory_type_set(entry, memory_type);
331 }
332 
333 /**
334  * @brief Set fields in MPU entry so it will be functional.
335  *
336  * This sets the starting address, enable bit, access rights and memory type
337  * of an entry.
338  *
339  * Note that this preserves the valud of the segment field.
340  *
341  * @param entry Pointer to the entry to be manipulated.
342  * @param start_address Start address to be set.
343  * @param enable Whether this entry should be enabled.
344  * @param access_rights Access rights for the entry.
345  * @param memory_type Memory type for the entry.
346  */
347 static inline
xtensa_mpu_entry_set(struct xtensa_mpu_entry * entry,uintptr_t start_address,bool enable,uint8_t access_rights,uint16_t memory_type)348 void xtensa_mpu_entry_set(struct xtensa_mpu_entry *entry, uintptr_t start_address,
349 			  bool enable, uint8_t access_rights, uint16_t memory_type)
350 {
351 	uint8_t segment = entry->at.p.segment;
352 
353 	/* Clear out the fields, and make sure MBZ fields are zero. */
354 	entry->as.raw = 0;
355 	entry->at.raw = 0;
356 
357 	xtensa_mpu_entry_start_address_set(entry, start_address);
358 	xtensa_mpu_entry_enable_set(entry, enable);
359 	xtensa_mpu_entry_access_rights_set(entry, access_rights);
360 	xtensa_mpu_entry_memory_type_set(entry, memory_type);
361 
362 	entry->at.p.segment = segment;
363 }
364 
365 /**
366  * @brief Test if two MPU entries have same access rights.
367  *
368  * @param entry1 MPU entry #1
369  * @param entry2 MPU entry #2.
370  *
371  * @return True if access rights are the same, false otherwise.
372  */
373 static inline
xtensa_mpu_entries_has_same_access_rights(const struct xtensa_mpu_entry * entry1,const struct xtensa_mpu_entry * entry2)374 bool xtensa_mpu_entries_has_same_access_rights(const struct xtensa_mpu_entry *entry1,
375 					       const struct xtensa_mpu_entry *entry2)
376 {
377 	return entry1->at.p.access_rights == entry2->at.p.access_rights;
378 }
379 
380 /**
381  * @brief Test if two MPU entries have same memory types.
382  *
383  * @param entry1 MPU entry #1.
384  * @param entry2 MPU entry #2.
385  *
386  * @return True if memory types are the same, false otherwise.
387  */
388 static inline
xtensa_mpu_entries_has_same_memory_type(const struct xtensa_mpu_entry * entry1,const struct xtensa_mpu_entry * entry2)389 bool xtensa_mpu_entries_has_same_memory_type(const struct xtensa_mpu_entry *entry1,
390 					     const struct xtensa_mpu_entry *entry2)
391 {
392 	return entry1->at.p.memory_type == entry2->at.p.memory_type;
393 }
394 
395 /**
396  * @brief Test if two MPU entries have same access rights and memory types.
397  *
398  * @param entry1 MPU entry #1.
399  * @param entry2 MPU entry #2.
400  *
401  * @return True if access rights and memory types are the same, false otherwise.
402  */
403 static inline
xtensa_mpu_entries_has_same_attributes(const struct xtensa_mpu_entry * entry1,const struct xtensa_mpu_entry * entry2)404 bool xtensa_mpu_entries_has_same_attributes(const struct xtensa_mpu_entry *entry1,
405 					    const struct xtensa_mpu_entry *entry2)
406 {
407 	return xtensa_mpu_entries_has_same_access_rights(entry1, entry2) &&
408 	       xtensa_mpu_entries_has_same_memory_type(entry1, entry2);
409 }
410 
411 /**
412  * @brief Test if two entries has the same addresses.
413  *
414  * @param entry1 MPU entry #1.
415  * @param entry2 MPU entry #2.
416  *
417  * @return True if they have the same address, false otherwise.
418  */
419 static inline
xtensa_mpu_entries_has_same_address(const struct xtensa_mpu_entry * entry1,const struct xtensa_mpu_entry * entry2)420 bool xtensa_mpu_entries_has_same_address(const struct xtensa_mpu_entry *entry1,
421 					 const struct xtensa_mpu_entry *entry2)
422 {
423 	return xtensa_mpu_entry_start_address_get(entry1)
424 	       == xtensa_mpu_entry_start_address_get(entry2);
425 }
426 
427 /**
428  * @}
429  */
430 
431 /**
432  * @name MPU access rights helper functions.
433  *
434  * @{
435  */
436 
437 /**
438  * @brief Test if the access rights is valid.
439  *
440  * @param access_rights Access rights value.
441  *
442  * @return True if access rights is valid, false otherwise.
443  */
xtensa_mpu_access_rights_is_valid(uint8_t access_rights)444 static ALWAYS_INLINE bool xtensa_mpu_access_rights_is_valid(uint8_t access_rights)
445 {
446 	return (access_rights != 1) && (access_rights <= 15);
447 }
448 
449 /**
450  * @}
451  */
452 
453 /**
454  * @}
455  */
456 
457 #endif /* ZEPHYR_ARCH_XTENSA_XTENSA_MPU_PRIV_H_ */
458