1 /*
2  * Copyright (c) 2021 Synopsys.
3  *
4  * SPDX-License-Identifier: Apache-2.0
5  */
6 #ifndef ZEPHYR_ARCH_ARC_CORE_MPU_ARC_MPU_V6_INTERNAL_H_
7 #define ZEPHYR_ARCH_ARC_CORE_MPU_ARC_MPU_V6_INTERNAL_H_
8 
9 #define AUX_MPU_EN_BANK_MASK BIT(0)
10 #define AUX_MPU_EN_IC		BIT(12)
11 #define AUX_MPU_EN_DC		BIT(13)
12 #define AUX_MPU_EN_ENABLE   BIT(30)
13 #define AUX_MPU_EN_DISABLE  ~BIT(30)
14 
15 /*
16  * The size of the region is a 5-bit field, the three MSB bits are
17  * represented in [11:9] and the two LSB bits are represented in [1:0].
18  * Together these fields specify the size of the region in bytes:
19  * 00000-00011	Reserved
20  * 0x4  32		0x5  64		0x6  128	0x7 256
21  * 0x8  512		0x9  1k		0xA  2K		0xB 4K
22  * 0xC  8K		0xD  16K	0xE  32K	0xF 64K
23  * 0x10 128K	0x11 256K	0x12 512K	0x13 1M
24  * 0x14 2M		0x15 4M		0x16 8M		0x17 16M
25  * 0x18 32M		0x19 64M	0x1A 128M	0x1B 256M
26  * 0x1C 512M	0x1D 1G		0x1E 2G		0x1F 4G
27  *
28  * Bit ... 12 11   10    9 8    3  2  1         0
29  *     ------+------------+------+---+-----------+
30  *     ...   | SIZE[11:9] | ATTR | R | SIZE[1:0] |
31  *     ------+------------+------+---+-----------+
32  */
33 /* arrange size into proper bit field in RDP aux reg*/
34 #define AUX_MPU_RDP_REGION_SIZE(size)  (((size - 1) & BIT_MASK(2)) | \
35 					(((size - 1) & (BIT_MASK(3) << 2)) << 7))
36 /* recover size from bit fields in RDP aux reg*/
37 #define AUX_MPU_RDP_SIZE_SHIFT(rdp)     ((rdp & BIT_MASK(2)) | (((rdp >> 9) & BIT_MASK(3)) << 2))
38 
39 #define AUX_MPU_RDB_VALID_MASK BIT(0)
40 #define AUX_MPU_RDP_ATTR_MASK  (BIT_MASK(6) << 3)
41 #define AUX_MPU_RDP_SIZE_MASK  ((BIT_MASK(3) << 9) | BIT_MASK(2))
42 /* Global code cacheability that applies to a region
43  * 0x0: (Default) Code is cacheable in all levels of the cache hierarchy
44  * 0x1: Code is not cacheable in any level of the cache hierarchy
45  */
46 #define AUX_MPU_RDB_IC		BIT(12)
47 /* Global data cacheability that applies to a region
48  * 0x0: (Default) Data is cacheable in all levels of the cache hierarchy
49  * 0x1: Data is not cacheable in any level of the cache hierarchy
50  */
51 #define AUX_MPU_RDB_DC		BIT(13)
52 /* Define a MPU region as non-volatile
53  * 0x0: (Default) The memory space for this MPU region is treated as a volatile uncached space.
54  * 0x1: The memory space for this MPU region is non-volatile
55  */
56 #define AUX_MPU_RDB_NV		BIT(14)
57 
58 /* For MPU version 6, the minimum protection region size is 32 bytes */
59 #define ARC_FEATURE_MPU_ALIGNMENT_BITS 5
60 #define ARC_FEATURE_MPU_BANK_SIZE      16
61 
62 /**
63  * This internal function select a MPU bank
64  */
_bank_select(uint32_t bank)65 static inline void _bank_select(uint32_t bank)
66 {
67 	uint32_t val;
68 
69 	val = z_arc_v2_aux_reg_read(_ARC_V2_MPU_EN) & (~AUX_MPU_EN_BANK_MASK);
70 	z_arc_v2_aux_reg_write(_ARC_V2_MPU_EN, val | bank);
71 }
72 /**
73  * This internal function initializes a MPU region
74  */
_region_init(uint32_t index,uint32_t region_addr,uint32_t size,uint32_t region_attr)75 static inline void _region_init(uint32_t index, uint32_t region_addr,
76 				uint32_t size, uint32_t region_attr)
77 {
78 	uint32_t bank = index / ARC_FEATURE_MPU_BANK_SIZE;
79 
80 	index = (index % ARC_FEATURE_MPU_BANK_SIZE) * 2U;
81 
82 	if (size > 0) {
83 		uint8_t bits = find_msb_set(size) - 1;
84 
85 		if (bits < ARC_FEATURE_MPU_ALIGNMENT_BITS) {
86 			bits = ARC_FEATURE_MPU_ALIGNMENT_BITS;
87 		}
88 
89 		if (BIT(bits) < size) {
90 			bits++;
91 		}
92 
93 		/* Clear size bits and IC, DC bits, and set NV bit
94 		 * The default value of NV bit is 0 which means the region is volatile and uncached.
95 		 * Setting the NV bit here has no effect on mpu v6 but is for the
96 		 * forward compatibility to mpu v7. Currently we do not allow to toggle these bits
97 		 * until we implement the control of these region properties
98 		 * TODO: support uncacheable regions and volatile uncached regions
99 		 */
100 		region_attr &= ~(AUX_MPU_RDP_SIZE_MASK | AUX_MPU_RDB_IC | AUX_MPU_RDB_DC);
101 		region_attr |= AUX_MPU_RDP_REGION_SIZE(bits) | AUX_MPU_RDB_NV;
102 		region_addr |= AUX_MPU_RDB_VALID_MASK;
103 	} else {
104 		region_addr = 0U;
105 	}
106 
107 	_bank_select(bank);
108 	z_arc_v2_aux_reg_write(_ARC_V2_MPU_RDP0 + index, region_attr);
109 	z_arc_v2_aux_reg_write(_ARC_V2_MPU_RDB0 + index, region_addr);
110 }
111 
112 /**
113  * This internal function is utilized by the MPU driver to parse the intent
114  * type (i.e. THREAD_STACK_REGION) and return the correct region index.
115  */
get_region_index_by_type(uint32_t type)116 static inline int get_region_index_by_type(uint32_t type)
117 {
118 	/*
119 	 * The new MPU regions are allocated per type after the statically
120 	 * configured regions. The type is one-indexed rather than
121 	 * zero-indexed.
122 	 *
123 	 * For ARC MPU v6, the smaller index has higher priority, so the
124 	 * index is allocated in reverse order. Static regions start from
125 	 * the biggest index, then thread related regions.
126 	 *
127 	 */
128 	switch (type) {
129 	case THREAD_STACK_USER_REGION:
130 		return get_num_regions() - mpu_config.num_regions - THREAD_STACK_REGION;
131 	case THREAD_STACK_REGION:
132 	case THREAD_APP_DATA_REGION:
133 	case THREAD_DOMAIN_PARTITION_REGION:
134 		/*
135 		 * Start domain partition region from stack guard region
136 		 * since stack guard is not supported.
137 		 */
138 		return get_num_regions() - mpu_config.num_regions - type + 1;
139 	default:
140 		__ASSERT(0, "Unsupported type");
141 		return -EINVAL;
142 	}
143 }
144 
145 /**
146  * This internal function checks if region is enabled or not
147  */
_is_enabled_region(uint32_t r_index)148 static inline bool _is_enabled_region(uint32_t r_index)
149 {
150 	uint32_t bank = r_index / ARC_FEATURE_MPU_BANK_SIZE;
151 	uint32_t index = (r_index % ARC_FEATURE_MPU_BANK_SIZE) * 2U;
152 
153 	_bank_select(bank);
154 	return ((z_arc_v2_aux_reg_read(_ARC_V2_MPU_RDB0 + index)
155 		 & AUX_MPU_RDB_VALID_MASK) == AUX_MPU_RDB_VALID_MASK);
156 }
157 
158 /**
159  * This internal function check if the given buffer is in the region
160  */
_is_in_region(uint32_t r_index,uint32_t start,uint32_t size)161 static inline bool _is_in_region(uint32_t r_index, uint32_t start, uint32_t size)
162 {
163 	uint32_t r_addr_start;
164 	uint32_t r_addr_end;
165 	uint32_t r_size_lshift;
166 	uint32_t bank = r_index / ARC_FEATURE_MPU_BANK_SIZE;
167 	uint32_t index = (r_index % ARC_FEATURE_MPU_BANK_SIZE) * 2U;
168 
169 	_bank_select(bank);
170 	r_addr_start = z_arc_v2_aux_reg_read(_ARC_V2_MPU_RDB0 + index) & (~AUX_MPU_RDB_VALID_MASK);
171 	r_size_lshift = z_arc_v2_aux_reg_read(_ARC_V2_MPU_RDP0 + index) & AUX_MPU_RDP_SIZE_MASK;
172 	r_size_lshift = AUX_MPU_RDP_SIZE_SHIFT(r_size_lshift);
173 	r_addr_end = r_addr_start  + (1 << (r_size_lshift + 1));
174 
175 	if (start >= r_addr_start && (start + size) <= r_addr_end) {
176 		return true;
177 	}
178 
179 	return false;
180 }
181 
182 /**
183  * This internal function check if the region is user accessible or not
184  */
_is_user_accessible_region(uint32_t r_index,int write)185 static inline bool _is_user_accessible_region(uint32_t r_index, int write)
186 {
187 	uint32_t r_ap;
188 	uint32_t bank = r_index / ARC_FEATURE_MPU_BANK_SIZE;
189 	uint32_t index = (r_index % ARC_FEATURE_MPU_BANK_SIZE) * 2U;
190 
191 	_bank_select(bank);
192 	r_ap = z_arc_v2_aux_reg_read(_ARC_V2_MPU_RDP0 + index);
193 
194 	r_ap &= AUX_MPU_RDP_ATTR_MASK;
195 
196 	if (write) {
197 		return ((r_ap & (AUX_MPU_ATTR_UW | AUX_MPU_ATTR_KW)) ==
198 			(AUX_MPU_ATTR_UW | AUX_MPU_ATTR_KW));
199 	}
200 
201 	return ((r_ap & (AUX_MPU_ATTR_UR | AUX_MPU_ATTR_KR)) ==
202 		(AUX_MPU_ATTR_UR | AUX_MPU_ATTR_KR));
203 }
204 
205 #endif /* ZEPHYR_ARCH_ARC_CORE_MPU_ARC_MPU_V6_INTERNAL_H_ */
206