1 | /** |
---|
2 | * @file |
---|
3 | * |
---|
4 | * @ingroup QorIQMMU |
---|
5 | * |
---|
6 | * @brief MMU implementation. |
---|
7 | */ |
---|
8 | |
---|
9 | /* |
---|
10 | * Copyright (c) 2011, 2018 embedded brains GmbH. All rights reserved. |
---|
11 | * |
---|
12 | * embedded brains GmbH |
---|
13 | * Dornierstr. 4 |
---|
14 | * 82178 Puchheim |
---|
15 | * Germany |
---|
16 | * <rtems@embedded-brains.de> |
---|
17 | * |
---|
18 | * The license and distribution terms for this file may be |
---|
19 | * found in the file LICENSE in this distribution or at |
---|
20 | * http://www.rtems.org/license/LICENSE. |
---|
21 | */ |
---|
22 | |
---|
23 | #include <bsp/mmu.h> |
---|
24 | #include <libcpu/powerpc-utility.h> |
---|
25 | |
---|
26 | #define TEXT __attribute__((section(".bsp_start_text"))) |
---|
27 | |
---|
28 | static uintptr_t TEXT power_of_two(uintptr_t val) |
---|
29 | { |
---|
30 | uintptr_t test_power = QORIQ_MMU_MIN_POWER; |
---|
31 | uintptr_t power = test_power; |
---|
32 | uintptr_t alignment = 1U << test_power; |
---|
33 | |
---|
34 | while (test_power <= QORIQ_MMU_MAX_POWER && (val & (alignment - 1)) == 0) { |
---|
35 | power = test_power; |
---|
36 | alignment <<= QORIQ_MMU_POWER_STEP; |
---|
37 | test_power += QORIQ_MMU_POWER_STEP; |
---|
38 | } |
---|
39 | |
---|
40 | return power; |
---|
41 | } |
---|
42 | |
---|
43 | static uintptr_t TEXT max_power_of_two(uintptr_t val) |
---|
44 | { |
---|
45 | uintptr_t test_power = QORIQ_MMU_MIN_POWER; |
---|
46 | uintptr_t power = test_power; |
---|
47 | uintptr_t max = 1U << test_power; |
---|
48 | |
---|
49 | do { |
---|
50 | power = test_power; |
---|
51 | max <<= QORIQ_MMU_POWER_STEP; |
---|
52 | test_power += QORIQ_MMU_POWER_STEP; |
---|
53 | } while (test_power <= QORIQ_MMU_MAX_POWER && max <= val); |
---|
54 | |
---|
55 | return power; |
---|
56 | } |
---|
57 | |
---|
58 | void TEXT qoriq_mmu_context_init(qoriq_mmu_context *self) |
---|
59 | { |
---|
60 | int *cur = (int *) self; |
---|
61 | const int *end = cur + sizeof(*self) / sizeof(*cur); |
---|
62 | |
---|
63 | while (cur != end) { |
---|
64 | *cur = 0; |
---|
65 | ++cur; |
---|
66 | } |
---|
67 | } |
---|
68 | |
---|
69 | static void TEXT sort(qoriq_mmu_context *self) |
---|
70 | { |
---|
71 | qoriq_mmu_entry *entries = self->entries; |
---|
72 | int n = self->count; |
---|
73 | int i = 0; |
---|
74 | |
---|
75 | for (i = 1; i < n; ++i) { |
---|
76 | qoriq_mmu_entry key = entries [i]; |
---|
77 | int j = 0; |
---|
78 | |
---|
79 | for (j = i - 1; j >= 0 && entries [j].begin > key.begin; --j) { |
---|
80 | entries [j + 1] = entries [j]; |
---|
81 | } |
---|
82 | |
---|
83 | entries [j + 1] = key; |
---|
84 | } |
---|
85 | } |
---|
86 | |
---|
87 | static bool TEXT mas_compatible(const qoriq_mmu_entry *a, const qoriq_mmu_entry *b) |
---|
88 | { |
---|
89 | uint32_t m = FSL_EIS_MAS2_M; |
---|
90 | |
---|
91 | return (a->mas2 & ~m) == (b->mas2 & ~m); |
---|
92 | } |
---|
93 | |
---|
94 | static bool TEXT can_merge(const qoriq_mmu_entry *prev, const qoriq_mmu_entry *cur) |
---|
95 | { |
---|
96 | return mas_compatible(prev, cur) |
---|
97 | && (prev->begin == cur->begin || prev->last >= cur->begin - 1); |
---|
98 | } |
---|
99 | |
---|
100 | static void TEXT merge(qoriq_mmu_context *self) |
---|
101 | { |
---|
102 | qoriq_mmu_entry *entries = self->entries; |
---|
103 | int n = self->count; |
---|
104 | int i = 0; |
---|
105 | |
---|
106 | for (i = 1; i < n; ++i) { |
---|
107 | qoriq_mmu_entry *prev = &entries [i - 1]; |
---|
108 | qoriq_mmu_entry *cur = &entries [i]; |
---|
109 | |
---|
110 | if (can_merge(prev, cur)) { |
---|
111 | int j = 0; |
---|
112 | |
---|
113 | prev->mas1 |= cur->mas1; |
---|
114 | prev->mas2 |= cur->mas2; |
---|
115 | prev->mas3 |= cur->mas3; |
---|
116 | |
---|
117 | if (cur->last > prev->last) { |
---|
118 | prev->last = cur->last; |
---|
119 | } |
---|
120 | |
---|
121 | for (j = i + 1; j < n; ++j) { |
---|
122 | entries [j - 1] = entries [j]; |
---|
123 | } |
---|
124 | |
---|
125 | --i; |
---|
126 | --n; |
---|
127 | } |
---|
128 | } |
---|
129 | |
---|
130 | self->count = n; |
---|
131 | } |
---|
132 | |
---|
133 | static void TEXT compact(qoriq_mmu_context *self) |
---|
134 | { |
---|
135 | sort(self); |
---|
136 | merge(self); |
---|
137 | } |
---|
138 | |
---|
139 | static bool TEXT can_expand_down( |
---|
140 | const qoriq_mmu_context *self, |
---|
141 | const qoriq_mmu_entry *cur, |
---|
142 | int i, |
---|
143 | uintptr_t new_begin |
---|
144 | ) |
---|
145 | { |
---|
146 | int j; |
---|
147 | |
---|
148 | for (j = 0; j < i; ++j) { |
---|
149 | const qoriq_mmu_entry *before = &self->entries[j]; |
---|
150 | |
---|
151 | if ( |
---|
152 | before->begin <= new_begin |
---|
153 | && new_begin <= before->last |
---|
154 | && !mas_compatible(before, cur) |
---|
155 | ) { |
---|
156 | return false; |
---|
157 | } |
---|
158 | } |
---|
159 | |
---|
160 | return true; |
---|
161 | } |
---|
162 | |
---|
163 | static bool TEXT can_expand_up( |
---|
164 | const qoriq_mmu_context *self, |
---|
165 | const qoriq_mmu_entry *cur, |
---|
166 | int i, |
---|
167 | int n, |
---|
168 | uintptr_t new_last |
---|
169 | ) |
---|
170 | { |
---|
171 | int j; |
---|
172 | |
---|
173 | for (j = i + 1; j < n; ++j) { |
---|
174 | const qoriq_mmu_entry *after = &self->entries[j]; |
---|
175 | |
---|
176 | if ( |
---|
177 | after->begin <= new_last |
---|
178 | && new_last <= after->last |
---|
179 | && !mas_compatible(after, cur) |
---|
180 | ) { |
---|
181 | return false; |
---|
182 | } |
---|
183 | } |
---|
184 | |
---|
185 | return true; |
---|
186 | } |
---|
187 | |
---|
188 | static void TEXT align(qoriq_mmu_context *self, uintptr_t alignment) |
---|
189 | { |
---|
190 | int n = self->count; |
---|
191 | int i; |
---|
192 | |
---|
193 | for (i = 0; i < n; ++i) { |
---|
194 | qoriq_mmu_entry *cur = &self->entries[i]; |
---|
195 | uintptr_t new_begin = cur->begin & ~(alignment - 1); |
---|
196 | uintptr_t new_last = alignment + (cur->last & ~(alignment - 1)) - 1; |
---|
197 | |
---|
198 | if ( |
---|
199 | can_expand_down(self, cur, i, new_begin) |
---|
200 | && can_expand_up(self, cur, i, n, new_last) |
---|
201 | ) { |
---|
202 | cur->begin = new_begin; |
---|
203 | cur->last = new_last; |
---|
204 | } |
---|
205 | } |
---|
206 | } |
---|
207 | |
---|
208 | static bool TEXT is_full(qoriq_mmu_context *self) |
---|
209 | { |
---|
210 | return self->count >= QORIQ_TLB1_ENTRY_COUNT; |
---|
211 | } |
---|
212 | |
---|
213 | static void TEXT append(qoriq_mmu_context *self, const qoriq_mmu_entry *new_entry) |
---|
214 | { |
---|
215 | self->entries [self->count] = *new_entry; |
---|
216 | ++self->count; |
---|
217 | } |
---|
218 | |
---|
219 | bool TEXT qoriq_mmu_add( |
---|
220 | qoriq_mmu_context *self, |
---|
221 | uintptr_t begin, |
---|
222 | uintptr_t last, |
---|
223 | uint32_t mas1, |
---|
224 | uint32_t mas2, |
---|
225 | uint32_t mas3, |
---|
226 | uint32_t mas7 |
---|
227 | ) |
---|
228 | { |
---|
229 | bool ok = true; |
---|
230 | |
---|
231 | if (is_full(self)) { |
---|
232 | compact(self); |
---|
233 | } |
---|
234 | |
---|
235 | if (!is_full(self)) { |
---|
236 | if (begin < last) { |
---|
237 | qoriq_mmu_entry new_entry = { |
---|
238 | .begin = begin, |
---|
239 | .last = last, |
---|
240 | .mas1 = mas1, |
---|
241 | .mas2 = mas2, |
---|
242 | .mas3 = mas3, |
---|
243 | .mas7 = mas7 |
---|
244 | }; |
---|
245 | append(self, &new_entry); |
---|
246 | } else { |
---|
247 | ok = false; |
---|
248 | } |
---|
249 | } else { |
---|
250 | ok = false; |
---|
251 | } |
---|
252 | |
---|
253 | return ok; |
---|
254 | } |
---|
255 | |
---|
256 | static uintptr_t TEXT min(uintptr_t a, uintptr_t b) |
---|
257 | { |
---|
258 | return a < b ? a : b; |
---|
259 | } |
---|
260 | |
---|
261 | static bool TEXT split(qoriq_mmu_context *self, qoriq_mmu_entry *cur) |
---|
262 | { |
---|
263 | bool again = false; |
---|
264 | uintptr_t begin = cur->begin; |
---|
265 | uintptr_t end = cur->last + 1; |
---|
266 | uintptr_t size = end - begin; |
---|
267 | uintptr_t begin_power = power_of_two(begin); |
---|
268 | uintptr_t size_power = max_power_of_two(size); |
---|
269 | uintptr_t power = min(begin_power, size_power); |
---|
270 | uintptr_t split_size = power < 32 ? (1U << power) : 0; |
---|
271 | uintptr_t split_pos = begin + split_size; |
---|
272 | |
---|
273 | if (split_pos != end && !is_full(self)) { |
---|
274 | qoriq_mmu_entry new_entry = *cur; |
---|
275 | cur->begin = split_pos; |
---|
276 | new_entry.last = split_pos - 1; |
---|
277 | append(self, &new_entry); |
---|
278 | again = true; |
---|
279 | } |
---|
280 | |
---|
281 | return again; |
---|
282 | } |
---|
283 | |
---|
284 | static void TEXT split_all(qoriq_mmu_context *self) |
---|
285 | { |
---|
286 | qoriq_mmu_entry *entries = self->entries; |
---|
287 | int n = self->count; |
---|
288 | int i = 0; |
---|
289 | |
---|
290 | for (i = 0; i < n; ++i) { |
---|
291 | qoriq_mmu_entry *cur = &entries [i]; |
---|
292 | |
---|
293 | while (split(self, cur)) { |
---|
294 | /* Repeat */ |
---|
295 | } |
---|
296 | } |
---|
297 | } |
---|
298 | |
---|
299 | static TEXT void partition(qoriq_mmu_context *self) |
---|
300 | { |
---|
301 | compact(self); |
---|
302 | split_all(self); |
---|
303 | sort(self); |
---|
304 | } |
---|
305 | |
---|
306 | void TEXT qoriq_mmu_partition(qoriq_mmu_context *self, int max_count) |
---|
307 | { |
---|
308 | uintptr_t alignment = 4096; |
---|
309 | |
---|
310 | sort(self); |
---|
311 | |
---|
312 | do { |
---|
313 | align(self, alignment); |
---|
314 | partition(self); |
---|
315 | alignment *= 4; |
---|
316 | } while (self->count > max_count); |
---|
317 | } |
---|
318 | |
---|
319 | void TEXT qoriq_mmu_write_to_tlb1(qoriq_mmu_context *self, int first_tlb) |
---|
320 | { |
---|
321 | qoriq_mmu_entry *entries = self->entries; |
---|
322 | int n = self->count; |
---|
323 | int i = 0; |
---|
324 | |
---|
325 | for (i = 0; i < n; ++i) { |
---|
326 | qoriq_mmu_entry *cur = &entries [i]; |
---|
327 | uintptr_t ea = cur->begin; |
---|
328 | uintptr_t size = cur->last - ea + 1; |
---|
329 | uintptr_t tsize = (power_of_two(size) - 10) / 2; |
---|
330 | int tlb = first_tlb + i; |
---|
331 | |
---|
332 | qoriq_tlb1_write( |
---|
333 | tlb, |
---|
334 | cur->mas1, |
---|
335 | cur->mas2, |
---|
336 | cur->mas3, |
---|
337 | cur->mas7, |
---|
338 | ea, |
---|
339 | (int) tsize |
---|
340 | ); |
---|
341 | } |
---|
342 | } |
---|
343 | |
---|
344 | void qoriq_mmu_change_perm(uint32_t test, uint32_t set, uint32_t clear) |
---|
345 | { |
---|
346 | int i = 0; |
---|
347 | |
---|
348 | for (i = 0; i < 16; ++i) { |
---|
349 | uint32_t mas0 = FSL_EIS_MAS0_TLBSEL | FSL_EIS_MAS0_ESEL(i); |
---|
350 | uint32_t mas1 = 0; |
---|
351 | |
---|
352 | PPC_SET_SPECIAL_PURPOSE_REGISTER(FSL_EIS_MAS0, mas0); |
---|
353 | asm volatile ("tlbre"); |
---|
354 | |
---|
355 | mas1 = PPC_SPECIAL_PURPOSE_REGISTER(FSL_EIS_MAS1); |
---|
356 | if ((mas1 & FSL_EIS_MAS1_V) != 0) { |
---|
357 | uint32_t mask = 0x3ff; |
---|
358 | uint32_t mas3 = PPC_SPECIAL_PURPOSE_REGISTER(FSL_EIS_MAS3); |
---|
359 | |
---|
360 | if ((mas3 & mask) == test) { |
---|
361 | mas3 &= ~(clear & mask); |
---|
362 | mas3 |= set & mask; |
---|
363 | PPC_SET_SPECIAL_PURPOSE_REGISTER(FSL_EIS_MAS3, mas3); |
---|
364 | asm volatile ("isync; msync; tlbwe; isync" : : : "memory"); |
---|
365 | } |
---|
366 | } |
---|
367 | } |
---|
368 | } |
---|