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