]>
Commit | Line | Data |
---|---|---|
7e5e5a63 MF |
1 | /* |
2 | * Copyright (c) 2011 - 2019, Max Filippov, Open Source and Linux Lab. | |
3 | * All rights reserved. | |
4 | * | |
5 | * Redistribution and use in source and binary forms, with or without | |
6 | * modification, are permitted provided that the following conditions are met: | |
7 | * * Redistributions of source code must retain the above copyright | |
8 | * notice, this list of conditions and the following disclaimer. | |
9 | * * Redistributions in binary form must reproduce the above copyright | |
10 | * notice, this list of conditions and the following disclaimer in the | |
11 | * documentation and/or other materials provided with the distribution. | |
12 | * * Neither the name of the Open Source and Linux Lab nor the | |
13 | * names of its contributors may be used to endorse or promote products | |
14 | * derived from this software without specific prior written permission. | |
15 | * | |
16 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" | |
17 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE | |
18 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE | |
19 | * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY | |
20 | * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES | |
21 | * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; | |
22 | * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND | |
23 | * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | |
24 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS | |
25 | * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | |
26 | */ | |
27 | ||
28 | #include "qemu/osdep.h" | |
cd617484 | 29 | #include "qemu/log.h" |
7e5e5a63 | 30 | #include "qemu/main-loop.h" |
fad866da | 31 | #include "qemu/qemu-print.h" |
7e5e5a63 MF |
32 | #include "qemu/units.h" |
33 | #include "cpu.h" | |
34 | #include "exec/helper-proto.h" | |
35 | #include "qemu/host-utils.h" | |
36 | #include "exec/exec-all.h" | |
37 | #include "exec/cpu_ldst.h" | |
38 | ||
4d04ea35 MF |
39 | #define XTENSA_MPU_SEGMENT_MASK 0x0000001f |
40 | #define XTENSA_MPU_ACC_RIGHTS_MASK 0x00000f00 | |
41 | #define XTENSA_MPU_ACC_RIGHTS_SHIFT 8 | |
42 | #define XTENSA_MPU_MEM_TYPE_MASK 0x001ff000 | |
43 | #define XTENSA_MPU_MEM_TYPE_SHIFT 12 | |
44 | #define XTENSA_MPU_ATTR_MASK 0x001fff00 | |
45 | ||
46 | #define XTENSA_MPU_PROBE_B 0x40000000 | |
47 | #define XTENSA_MPU_PROBE_V 0x80000000 | |
48 | ||
49 | #define XTENSA_MPU_SYSTEM_TYPE_DEVICE 0x0001 | |
50 | #define XTENSA_MPU_SYSTEM_TYPE_NC 0x0002 | |
51 | #define XTENSA_MPU_SYSTEM_TYPE_C 0x0003 | |
52 | #define XTENSA_MPU_SYSTEM_TYPE_MASK 0x0003 | |
53 | ||
54 | #define XTENSA_MPU_TYPE_SYS_C 0x0010 | |
55 | #define XTENSA_MPU_TYPE_SYS_W 0x0020 | |
56 | #define XTENSA_MPU_TYPE_SYS_R 0x0040 | |
57 | #define XTENSA_MPU_TYPE_CPU_C 0x0100 | |
58 | #define XTENSA_MPU_TYPE_CPU_W 0x0200 | |
59 | #define XTENSA_MPU_TYPE_CPU_R 0x0400 | |
60 | #define XTENSA_MPU_TYPE_CPU_CACHE 0x0800 | |
61 | #define XTENSA_MPU_TYPE_B 0x1000 | |
62 | #define XTENSA_MPU_TYPE_INT 0x2000 | |
63 | ||
7e5e5a63 MF |
64 | void HELPER(itlb_hit_test)(CPUXtensaState *env, uint32_t vaddr) |
65 | { | |
66 | /* | |
ecd3571e | 67 | * Probe the memory; we don't care about the result but |
7e5e5a63 MF |
68 | * only the side-effects (ie any MMU or other exception) |
69 | */ | |
ecd3571e RH |
70 | probe_access(env, vaddr, 1, MMU_INST_FETCH, |
71 | cpu_mmu_index(env, true), GETPC()); | |
7e5e5a63 MF |
72 | } |
73 | ||
74 | void HELPER(wsr_rasid)(CPUXtensaState *env, uint32_t v) | |
75 | { | |
7e5e5a63 MF |
76 | v = (v & 0xffffff00) | 0x1; |
77 | if (v != env->sregs[RASID]) { | |
78 | env->sregs[RASID] = v; | |
92fddfbd | 79 | tlb_flush(env_cpu(env)); |
7e5e5a63 MF |
80 | } |
81 | } | |
82 | ||
83 | static uint32_t get_page_size(const CPUXtensaState *env, | |
84 | bool dtlb, uint32_t way) | |
85 | { | |
86 | uint32_t tlbcfg = env->sregs[dtlb ? DTLBCFG : ITLBCFG]; | |
87 | ||
88 | switch (way) { | |
89 | case 4: | |
90 | return (tlbcfg >> 16) & 0x3; | |
91 | ||
92 | case 5: | |
93 | return (tlbcfg >> 20) & 0x1; | |
94 | ||
95 | case 6: | |
96 | return (tlbcfg >> 24) & 0x1; | |
97 | ||
98 | default: | |
99 | return 0; | |
100 | } | |
101 | } | |
102 | ||
103 | /*! | |
104 | * Get bit mask for the virtual address bits translated by the TLB way | |
105 | */ | |
5f7f36d0 MF |
106 | static uint32_t xtensa_tlb_get_addr_mask(const CPUXtensaState *env, |
107 | bool dtlb, uint32_t way) | |
7e5e5a63 MF |
108 | { |
109 | if (xtensa_option_enabled(env->config, XTENSA_OPTION_MMU)) { | |
110 | bool varway56 = dtlb ? | |
111 | env->config->dtlb.varway56 : | |
112 | env->config->itlb.varway56; | |
113 | ||
114 | switch (way) { | |
115 | case 4: | |
116 | return 0xfff00000 << get_page_size(env, dtlb, way) * 2; | |
117 | ||
118 | case 5: | |
119 | if (varway56) { | |
120 | return 0xf8000000 << get_page_size(env, dtlb, way); | |
121 | } else { | |
122 | return 0xf8000000; | |
123 | } | |
124 | ||
125 | case 6: | |
126 | if (varway56) { | |
127 | return 0xf0000000 << (1 - get_page_size(env, dtlb, way)); | |
128 | } else { | |
129 | return 0xf0000000; | |
130 | } | |
131 | ||
132 | default: | |
133 | return 0xfffff000; | |
134 | } | |
135 | } else { | |
136 | return REGION_PAGE_MASK; | |
137 | } | |
138 | } | |
139 | ||
140 | /*! | |
141 | * Get bit mask for the 'VPN without index' field. | |
142 | * See ISA, 4.6.5.6, data format for RxTLB0 | |
143 | */ | |
144 | static uint32_t get_vpn_mask(const CPUXtensaState *env, bool dtlb, uint32_t way) | |
145 | { | |
146 | if (way < 4) { | |
147 | bool is32 = (dtlb ? | |
148 | env->config->dtlb.nrefillentries : | |
149 | env->config->itlb.nrefillentries) == 32; | |
150 | return is32 ? 0xffff8000 : 0xffffc000; | |
151 | } else if (way == 4) { | |
152 | return xtensa_tlb_get_addr_mask(env, dtlb, way) << 2; | |
153 | } else if (way <= 6) { | |
154 | uint32_t mask = xtensa_tlb_get_addr_mask(env, dtlb, way); | |
155 | bool varway56 = dtlb ? | |
156 | env->config->dtlb.varway56 : | |
157 | env->config->itlb.varway56; | |
158 | ||
159 | if (varway56) { | |
160 | return mask << (way == 5 ? 2 : 3); | |
161 | } else { | |
162 | return mask << 1; | |
163 | } | |
164 | } else { | |
165 | return 0xfffff000; | |
166 | } | |
167 | } | |
168 | ||
169 | /*! | |
170 | * Split virtual address into VPN (with index) and entry index | |
171 | * for the given TLB way | |
172 | */ | |
5f7f36d0 MF |
173 | static void split_tlb_entry_spec_way(const CPUXtensaState *env, uint32_t v, |
174 | bool dtlb, uint32_t *vpn, | |
175 | uint32_t wi, uint32_t *ei) | |
7e5e5a63 MF |
176 | { |
177 | bool varway56 = dtlb ? | |
178 | env->config->dtlb.varway56 : | |
179 | env->config->itlb.varway56; | |
180 | ||
181 | if (!dtlb) { | |
182 | wi &= 7; | |
183 | } | |
184 | ||
185 | if (wi < 4) { | |
186 | bool is32 = (dtlb ? | |
187 | env->config->dtlb.nrefillentries : | |
188 | env->config->itlb.nrefillentries) == 32; | |
189 | *ei = (v >> 12) & (is32 ? 0x7 : 0x3); | |
190 | } else { | |
191 | switch (wi) { | |
192 | case 4: | |
193 | { | |
194 | uint32_t eibase = 20 + get_page_size(env, dtlb, wi) * 2; | |
195 | *ei = (v >> eibase) & 0x3; | |
196 | } | |
197 | break; | |
198 | ||
199 | case 5: | |
200 | if (varway56) { | |
201 | uint32_t eibase = 27 + get_page_size(env, dtlb, wi); | |
202 | *ei = (v >> eibase) & 0x3; | |
203 | } else { | |
204 | *ei = (v >> 27) & 0x1; | |
205 | } | |
206 | break; | |
207 | ||
208 | case 6: | |
209 | if (varway56) { | |
210 | uint32_t eibase = 29 - get_page_size(env, dtlb, wi); | |
211 | *ei = (v >> eibase) & 0x7; | |
212 | } else { | |
213 | *ei = (v >> 28) & 0x1; | |
214 | } | |
215 | break; | |
216 | ||
217 | default: | |
218 | *ei = 0; | |
219 | break; | |
220 | } | |
221 | } | |
222 | *vpn = v & xtensa_tlb_get_addr_mask(env, dtlb, wi); | |
223 | } | |
224 | ||
225 | /*! | |
226 | * Split TLB address into TLB way, entry index and VPN (with index). | |
227 | * See ISA, 4.6.5.5 - 4.6.5.8 for the TLB addressing format | |
228 | */ | |
229 | static void split_tlb_entry_spec(CPUXtensaState *env, uint32_t v, bool dtlb, | |
230 | uint32_t *vpn, uint32_t *wi, uint32_t *ei) | |
231 | { | |
232 | if (xtensa_option_enabled(env->config, XTENSA_OPTION_MMU)) { | |
233 | *wi = v & (dtlb ? 0xf : 0x7); | |
234 | split_tlb_entry_spec_way(env, v, dtlb, vpn, *wi, ei); | |
235 | } else { | |
236 | *vpn = v & REGION_PAGE_MASK; | |
237 | *wi = 0; | |
238 | *ei = (v >> 29) & 0x7; | |
239 | } | |
240 | } | |
241 | ||
5f7f36d0 MF |
242 | static xtensa_tlb_entry *xtensa_tlb_get_entry(CPUXtensaState *env, bool dtlb, |
243 | unsigned wi, unsigned ei) | |
244 | { | |
245 | return dtlb ? | |
246 | env->dtlb[wi] + ei : | |
247 | env->itlb[wi] + ei; | |
248 | } | |
249 | ||
7e5e5a63 MF |
250 | static xtensa_tlb_entry *get_tlb_entry(CPUXtensaState *env, |
251 | uint32_t v, bool dtlb, uint32_t *pwi) | |
252 | { | |
253 | uint32_t vpn; | |
254 | uint32_t wi; | |
255 | uint32_t ei; | |
256 | ||
257 | split_tlb_entry_spec(env, v, dtlb, &vpn, &wi, &ei); | |
258 | if (pwi) { | |
259 | *pwi = wi; | |
260 | } | |
261 | return xtensa_tlb_get_entry(env, dtlb, wi, ei); | |
262 | } | |
263 | ||
5f7f36d0 MF |
264 | static void xtensa_tlb_set_entry_mmu(const CPUXtensaState *env, |
265 | xtensa_tlb_entry *entry, bool dtlb, | |
266 | unsigned wi, unsigned ei, uint32_t vpn, | |
267 | uint32_t pte) | |
7e5e5a63 MF |
268 | { |
269 | entry->vaddr = vpn; | |
270 | entry->paddr = pte & xtensa_tlb_get_addr_mask(env, dtlb, wi); | |
271 | entry->asid = (env->sregs[RASID] >> ((pte >> 1) & 0x18)) & 0xff; | |
272 | entry->attr = pte & 0xf; | |
273 | } | |
274 | ||
5f7f36d0 MF |
275 | static void xtensa_tlb_set_entry(CPUXtensaState *env, bool dtlb, |
276 | unsigned wi, unsigned ei, | |
277 | uint32_t vpn, uint32_t pte) | |
7e5e5a63 | 278 | { |
92fddfbd | 279 | CPUState *cs = env_cpu(env); |
7e5e5a63 MF |
280 | xtensa_tlb_entry *entry = xtensa_tlb_get_entry(env, dtlb, wi, ei); |
281 | ||
282 | if (xtensa_option_enabled(env->config, XTENSA_OPTION_MMU)) { | |
283 | if (entry->variable) { | |
284 | if (entry->asid) { | |
285 | tlb_flush_page(cs, entry->vaddr); | |
286 | } | |
287 | xtensa_tlb_set_entry_mmu(env, entry, dtlb, wi, ei, vpn, pte); | |
288 | tlb_flush_page(cs, entry->vaddr); | |
289 | } else { | |
290 | qemu_log_mask(LOG_GUEST_ERROR, | |
291 | "%s %d, %d, %d trying to set immutable entry\n", | |
292 | __func__, dtlb, wi, ei); | |
293 | } | |
294 | } else { | |
295 | tlb_flush_page(cs, entry->vaddr); | |
296 | if (xtensa_option_enabled(env->config, | |
297 | XTENSA_OPTION_REGION_TRANSLATION)) { | |
298 | entry->paddr = pte & REGION_PAGE_MASK; | |
299 | } | |
300 | entry->attr = pte & 0xf; | |
301 | } | |
302 | } | |
303 | ||
7e5e5a63 MF |
304 | hwaddr xtensa_cpu_get_phys_page_debug(CPUState *cs, vaddr addr) |
305 | { | |
306 | XtensaCPU *cpu = XTENSA_CPU(cs); | |
307 | uint32_t paddr; | |
308 | uint32_t page_size; | |
309 | unsigned access; | |
310 | ||
311 | if (xtensa_get_physical_addr(&cpu->env, false, addr, 0, 0, | |
312 | &paddr, &page_size, &access) == 0) { | |
313 | return paddr; | |
314 | } | |
315 | if (xtensa_get_physical_addr(&cpu->env, false, addr, 2, 0, | |
316 | &paddr, &page_size, &access) == 0) { | |
317 | return paddr; | |
318 | } | |
319 | return ~0; | |
320 | } | |
321 | ||
322 | static void reset_tlb_mmu_all_ways(CPUXtensaState *env, | |
323 | const xtensa_tlb *tlb, | |
324 | xtensa_tlb_entry entry[][MAX_TLB_WAY_SIZE]) | |
325 | { | |
326 | unsigned wi, ei; | |
327 | ||
328 | for (wi = 0; wi < tlb->nways; ++wi) { | |
329 | for (ei = 0; ei < tlb->way_size[wi]; ++ei) { | |
330 | entry[wi][ei].asid = 0; | |
331 | entry[wi][ei].variable = true; | |
332 | } | |
333 | } | |
334 | } | |
335 | ||
336 | static void reset_tlb_mmu_ways56(CPUXtensaState *env, | |
337 | const xtensa_tlb *tlb, | |
338 | xtensa_tlb_entry entry[][MAX_TLB_WAY_SIZE]) | |
339 | { | |
340 | if (!tlb->varway56) { | |
341 | static const xtensa_tlb_entry way5[] = { | |
342 | { | |
343 | .vaddr = 0xd0000000, | |
344 | .paddr = 0, | |
345 | .asid = 1, | |
346 | .attr = 7, | |
347 | .variable = false, | |
348 | }, { | |
349 | .vaddr = 0xd8000000, | |
350 | .paddr = 0, | |
351 | .asid = 1, | |
352 | .attr = 3, | |
353 | .variable = false, | |
354 | } | |
355 | }; | |
356 | static const xtensa_tlb_entry way6[] = { | |
357 | { | |
358 | .vaddr = 0xe0000000, | |
359 | .paddr = 0xf0000000, | |
360 | .asid = 1, | |
361 | .attr = 7, | |
362 | .variable = false, | |
363 | }, { | |
364 | .vaddr = 0xf0000000, | |
365 | .paddr = 0xf0000000, | |
366 | .asid = 1, | |
367 | .attr = 3, | |
368 | .variable = false, | |
369 | } | |
370 | }; | |
371 | memcpy(entry[5], way5, sizeof(way5)); | |
372 | memcpy(entry[6], way6, sizeof(way6)); | |
373 | } else { | |
374 | uint32_t ei; | |
375 | for (ei = 0; ei < 8; ++ei) { | |
376 | entry[6][ei].vaddr = ei << 29; | |
377 | entry[6][ei].paddr = ei << 29; | |
378 | entry[6][ei].asid = 1; | |
379 | entry[6][ei].attr = 3; | |
380 | } | |
381 | } | |
382 | } | |
383 | ||
384 | static void reset_tlb_region_way0(CPUXtensaState *env, | |
385 | xtensa_tlb_entry entry[][MAX_TLB_WAY_SIZE]) | |
386 | { | |
387 | unsigned ei; | |
388 | ||
389 | for (ei = 0; ei < 8; ++ei) { | |
390 | entry[0][ei].vaddr = ei << 29; | |
391 | entry[0][ei].paddr = ei << 29; | |
392 | entry[0][ei].asid = 1; | |
393 | entry[0][ei].attr = 2; | |
394 | entry[0][ei].variable = true; | |
395 | } | |
396 | } | |
397 | ||
398 | void reset_mmu(CPUXtensaState *env) | |
399 | { | |
400 | if (xtensa_option_enabled(env->config, XTENSA_OPTION_MMU)) { | |
401 | env->sregs[RASID] = 0x04030201; | |
402 | env->sregs[ITLBCFG] = 0; | |
403 | env->sregs[DTLBCFG] = 0; | |
404 | env->autorefill_idx = 0; | |
405 | reset_tlb_mmu_all_ways(env, &env->config->itlb, env->itlb); | |
406 | reset_tlb_mmu_all_ways(env, &env->config->dtlb, env->dtlb); | |
407 | reset_tlb_mmu_ways56(env, &env->config->itlb, env->itlb); | |
408 | reset_tlb_mmu_ways56(env, &env->config->dtlb, env->dtlb); | |
4d04ea35 MF |
409 | } else if (xtensa_option_enabled(env->config, XTENSA_OPTION_MPU)) { |
410 | unsigned i; | |
411 | ||
412 | env->sregs[MPUENB] = 0; | |
413 | env->sregs[MPUCFG] = env->config->n_mpu_fg_segments; | |
414 | env->sregs[CACHEADRDIS] = 0; | |
415 | assert(env->config->n_mpu_bg_segments > 0 && | |
416 | env->config->mpu_bg[0].vaddr == 0); | |
417 | for (i = 1; i < env->config->n_mpu_bg_segments; ++i) { | |
418 | assert(env->config->mpu_bg[i].vaddr >= | |
419 | env->config->mpu_bg[i - 1].vaddr); | |
420 | } | |
7e5e5a63 | 421 | } else { |
4d04ea35 | 422 | env->sregs[CACHEATTR] = 0x22222222; |
7e5e5a63 MF |
423 | reset_tlb_region_way0(env, env->itlb); |
424 | reset_tlb_region_way0(env, env->dtlb); | |
425 | } | |
426 | } | |
427 | ||
428 | static unsigned get_ring(const CPUXtensaState *env, uint8_t asid) | |
429 | { | |
430 | unsigned i; | |
431 | for (i = 0; i < 4; ++i) { | |
432 | if (((env->sregs[RASID] >> i * 8) & 0xff) == asid) { | |
433 | return i; | |
434 | } | |
435 | } | |
436 | return 0xff; | |
437 | } | |
438 | ||
439 | /*! | |
440 | * Lookup xtensa TLB for the given virtual address. | |
441 | * See ISA, 4.6.2.2 | |
442 | * | |
443 | * \param pwi: [out] way index | |
444 | * \param pei: [out] entry index | |
445 | * \param pring: [out] access ring | |
446 | * \return 0 if ok, exception cause code otherwise | |
447 | */ | |
5f7f36d0 MF |
448 | static int xtensa_tlb_lookup(const CPUXtensaState *env, |
449 | uint32_t addr, bool dtlb, | |
450 | uint32_t *pwi, uint32_t *pei, uint8_t *pring) | |
7e5e5a63 MF |
451 | { |
452 | const xtensa_tlb *tlb = dtlb ? | |
453 | &env->config->dtlb : &env->config->itlb; | |
454 | const xtensa_tlb_entry (*entry)[MAX_TLB_WAY_SIZE] = dtlb ? | |
455 | env->dtlb : env->itlb; | |
456 | ||
457 | int nhits = 0; | |
458 | unsigned wi; | |
459 | ||
460 | for (wi = 0; wi < tlb->nways; ++wi) { | |
461 | uint32_t vpn; | |
462 | uint32_t ei; | |
463 | split_tlb_entry_spec_way(env, addr, dtlb, &vpn, wi, &ei); | |
464 | if (entry[wi][ei].vaddr == vpn && entry[wi][ei].asid) { | |
465 | unsigned ring = get_ring(env, entry[wi][ei].asid); | |
466 | if (ring < 4) { | |
467 | if (++nhits > 1) { | |
468 | return dtlb ? | |
469 | LOAD_STORE_TLB_MULTI_HIT_CAUSE : | |
470 | INST_TLB_MULTI_HIT_CAUSE; | |
471 | } | |
472 | *pwi = wi; | |
473 | *pei = ei; | |
474 | *pring = ring; | |
475 | } | |
476 | } | |
477 | } | |
478 | return nhits ? 0 : | |
479 | (dtlb ? LOAD_STORE_TLB_MISS_CAUSE : INST_TLB_MISS_CAUSE); | |
480 | } | |
481 | ||
5f7f36d0 MF |
482 | uint32_t HELPER(rtlb0)(CPUXtensaState *env, uint32_t v, uint32_t dtlb) |
483 | { | |
484 | if (xtensa_option_enabled(env->config, XTENSA_OPTION_MMU)) { | |
485 | uint32_t wi; | |
486 | const xtensa_tlb_entry *entry = get_tlb_entry(env, v, dtlb, &wi); | |
487 | return (entry->vaddr & get_vpn_mask(env, dtlb, wi)) | entry->asid; | |
488 | } else { | |
489 | return v & REGION_PAGE_MASK; | |
490 | } | |
491 | } | |
492 | ||
493 | uint32_t HELPER(rtlb1)(CPUXtensaState *env, uint32_t v, uint32_t dtlb) | |
494 | { | |
495 | const xtensa_tlb_entry *entry = get_tlb_entry(env, v, dtlb, NULL); | |
496 | return entry->paddr | entry->attr; | |
497 | } | |
498 | ||
499 | void HELPER(itlb)(CPUXtensaState *env, uint32_t v, uint32_t dtlb) | |
500 | { | |
501 | if (xtensa_option_enabled(env->config, XTENSA_OPTION_MMU)) { | |
502 | uint32_t wi; | |
503 | xtensa_tlb_entry *entry = get_tlb_entry(env, v, dtlb, &wi); | |
504 | if (entry->variable && entry->asid) { | |
92fddfbd | 505 | tlb_flush_page(env_cpu(env), entry->vaddr); |
5f7f36d0 MF |
506 | entry->asid = 0; |
507 | } | |
508 | } | |
509 | } | |
510 | ||
511 | uint32_t HELPER(ptlb)(CPUXtensaState *env, uint32_t v, uint32_t dtlb) | |
512 | { | |
513 | if (xtensa_option_enabled(env->config, XTENSA_OPTION_MMU)) { | |
514 | uint32_t wi; | |
515 | uint32_t ei; | |
516 | uint8_t ring; | |
517 | int res = xtensa_tlb_lookup(env, v, dtlb, &wi, &ei, &ring); | |
518 | ||
519 | switch (res) { | |
520 | case 0: | |
521 | if (ring >= xtensa_get_ring(env)) { | |
522 | return (v & 0xfffff000) | wi | (dtlb ? 0x10 : 0x8); | |
523 | } | |
524 | break; | |
525 | ||
526 | case INST_TLB_MULTI_HIT_CAUSE: | |
527 | case LOAD_STORE_TLB_MULTI_HIT_CAUSE: | |
528 | HELPER(exception_cause_vaddr)(env, env->pc, res, v); | |
529 | break; | |
530 | } | |
531 | return 0; | |
532 | } else { | |
533 | return (v & REGION_PAGE_MASK) | 0x1; | |
534 | } | |
535 | } | |
536 | ||
537 | void HELPER(wtlb)(CPUXtensaState *env, uint32_t p, uint32_t v, uint32_t dtlb) | |
538 | { | |
539 | uint32_t vpn; | |
540 | uint32_t wi; | |
541 | uint32_t ei; | |
542 | split_tlb_entry_spec(env, v, dtlb, &vpn, &wi, &ei); | |
543 | xtensa_tlb_set_entry(env, dtlb, wi, ei, vpn, p); | |
544 | } | |
545 | ||
7e5e5a63 MF |
546 | /*! |
547 | * Convert MMU ATTR to PAGE_{READ,WRITE,EXEC} mask. | |
548 | * See ISA, 4.6.5.10 | |
549 | */ | |
550 | static unsigned mmu_attr_to_access(uint32_t attr) | |
551 | { | |
552 | unsigned access = 0; | |
553 | ||
554 | if (attr < 12) { | |
555 | access |= PAGE_READ; | |
556 | if (attr & 0x1) { | |
557 | access |= PAGE_EXEC; | |
558 | } | |
559 | if (attr & 0x2) { | |
560 | access |= PAGE_WRITE; | |
561 | } | |
562 | ||
563 | switch (attr & 0xc) { | |
564 | case 0: | |
565 | access |= PAGE_CACHE_BYPASS; | |
566 | break; | |
567 | ||
568 | case 4: | |
569 | access |= PAGE_CACHE_WB; | |
570 | break; | |
571 | ||
572 | case 8: | |
573 | access |= PAGE_CACHE_WT; | |
574 | break; | |
575 | } | |
576 | } else if (attr == 13) { | |
577 | access |= PAGE_READ | PAGE_WRITE | PAGE_CACHE_ISOLATE; | |
578 | } | |
579 | return access; | |
580 | } | |
581 | ||
582 | /*! | |
583 | * Convert region protection ATTR to PAGE_{READ,WRITE,EXEC} mask. | |
584 | * See ISA, 4.6.3.3 | |
585 | */ | |
586 | static unsigned region_attr_to_access(uint32_t attr) | |
587 | { | |
588 | static const unsigned access[16] = { | |
589 | [0] = PAGE_READ | PAGE_WRITE | PAGE_CACHE_WT, | |
590 | [1] = PAGE_READ | PAGE_WRITE | PAGE_EXEC | PAGE_CACHE_WT, | |
591 | [2] = PAGE_READ | PAGE_WRITE | PAGE_EXEC | PAGE_CACHE_BYPASS, | |
592 | [3] = PAGE_EXEC | PAGE_CACHE_WB, | |
593 | [4] = PAGE_READ | PAGE_WRITE | PAGE_EXEC | PAGE_CACHE_WB, | |
594 | [5] = PAGE_READ | PAGE_WRITE | PAGE_EXEC | PAGE_CACHE_WB, | |
595 | [14] = PAGE_READ | PAGE_WRITE | PAGE_CACHE_ISOLATE, | |
596 | }; | |
597 | ||
598 | return access[attr & 0xf]; | |
599 | } | |
600 | ||
601 | /*! | |
602 | * Convert cacheattr to PAGE_{READ,WRITE,EXEC} mask. | |
603 | * See ISA, A.2.14 The Cache Attribute Register | |
604 | */ | |
605 | static unsigned cacheattr_attr_to_access(uint32_t attr) | |
606 | { | |
607 | static const unsigned access[16] = { | |
608 | [0] = PAGE_READ | PAGE_WRITE | PAGE_CACHE_WT, | |
609 | [1] = PAGE_READ | PAGE_WRITE | PAGE_EXEC | PAGE_CACHE_WT, | |
610 | [2] = PAGE_READ | PAGE_WRITE | PAGE_EXEC | PAGE_CACHE_BYPASS, | |
611 | [3] = PAGE_EXEC | PAGE_CACHE_WB, | |
612 | [4] = PAGE_READ | PAGE_WRITE | PAGE_EXEC | PAGE_CACHE_WB, | |
613 | [14] = PAGE_READ | PAGE_WRITE | PAGE_CACHE_ISOLATE, | |
614 | }; | |
615 | ||
616 | return access[attr & 0xf]; | |
617 | } | |
618 | ||
4d04ea35 MF |
619 | struct attr_pattern { |
620 | uint32_t mask; | |
621 | uint32_t value; | |
622 | }; | |
623 | ||
624 | static int attr_pattern_match(uint32_t attr, | |
625 | const struct attr_pattern *pattern, | |
626 | size_t n) | |
627 | { | |
628 | size_t i; | |
629 | ||
630 | for (i = 0; i < n; ++i) { | |
631 | if ((attr & pattern[i].mask) == pattern[i].value) { | |
632 | return 1; | |
633 | } | |
634 | } | |
635 | return 0; | |
636 | } | |
637 | ||
638 | static unsigned mpu_attr_to_cpu_cache(uint32_t attr) | |
639 | { | |
640 | static const struct attr_pattern cpu_c[] = { | |
641 | { .mask = 0x18f, .value = 0x089 }, | |
642 | { .mask = 0x188, .value = 0x080 }, | |
643 | { .mask = 0x180, .value = 0x180 }, | |
644 | }; | |
645 | ||
646 | unsigned type = 0; | |
647 | ||
648 | if (attr_pattern_match(attr, cpu_c, ARRAY_SIZE(cpu_c))) { | |
649 | type |= XTENSA_MPU_TYPE_CPU_CACHE; | |
650 | if (attr & 0x10) { | |
651 | type |= XTENSA_MPU_TYPE_CPU_C; | |
652 | } | |
653 | if (attr & 0x20) { | |
654 | type |= XTENSA_MPU_TYPE_CPU_W; | |
655 | } | |
656 | if (attr & 0x40) { | |
657 | type |= XTENSA_MPU_TYPE_CPU_R; | |
658 | } | |
659 | } | |
660 | return type; | |
661 | } | |
662 | ||
663 | static unsigned mpu_attr_to_type(uint32_t attr) | |
664 | { | |
665 | static const struct attr_pattern device_type[] = { | |
666 | { .mask = 0x1f6, .value = 0x000 }, | |
667 | { .mask = 0x1f6, .value = 0x006 }, | |
668 | }; | |
669 | static const struct attr_pattern sys_nc_type[] = { | |
670 | { .mask = 0x1fe, .value = 0x018 }, | |
671 | { .mask = 0x1fe, .value = 0x01e }, | |
672 | { .mask = 0x18f, .value = 0x089 }, | |
673 | }; | |
674 | static const struct attr_pattern sys_c_type[] = { | |
675 | { .mask = 0x1f8, .value = 0x010 }, | |
676 | { .mask = 0x188, .value = 0x080 }, | |
677 | { .mask = 0x1f0, .value = 0x030 }, | |
678 | { .mask = 0x180, .value = 0x180 }, | |
679 | }; | |
680 | static const struct attr_pattern b[] = { | |
681 | { .mask = 0x1f7, .value = 0x001 }, | |
682 | { .mask = 0x1f7, .value = 0x007 }, | |
683 | { .mask = 0x1ff, .value = 0x019 }, | |
684 | { .mask = 0x1ff, .value = 0x01f }, | |
685 | }; | |
686 | ||
687 | unsigned type = 0; | |
688 | ||
689 | attr = (attr & XTENSA_MPU_MEM_TYPE_MASK) >> XTENSA_MPU_MEM_TYPE_SHIFT; | |
690 | if (attr_pattern_match(attr, device_type, ARRAY_SIZE(device_type))) { | |
691 | type |= XTENSA_MPU_SYSTEM_TYPE_DEVICE; | |
692 | if (attr & 0x80) { | |
693 | type |= XTENSA_MPU_TYPE_INT; | |
694 | } | |
695 | } | |
696 | if (attr_pattern_match(attr, sys_nc_type, ARRAY_SIZE(sys_nc_type))) { | |
697 | type |= XTENSA_MPU_SYSTEM_TYPE_NC; | |
698 | } | |
699 | if (attr_pattern_match(attr, sys_c_type, ARRAY_SIZE(sys_c_type))) { | |
700 | type |= XTENSA_MPU_SYSTEM_TYPE_C; | |
701 | if (attr & 0x1) { | |
702 | type |= XTENSA_MPU_TYPE_SYS_C; | |
703 | } | |
704 | if (attr & 0x2) { | |
705 | type |= XTENSA_MPU_TYPE_SYS_W; | |
706 | } | |
707 | if (attr & 0x4) { | |
708 | type |= XTENSA_MPU_TYPE_SYS_R; | |
709 | } | |
710 | } | |
711 | if (attr_pattern_match(attr, b, ARRAY_SIZE(b))) { | |
712 | type |= XTENSA_MPU_TYPE_B; | |
713 | } | |
714 | type |= mpu_attr_to_cpu_cache(attr); | |
715 | ||
716 | return type; | |
717 | } | |
718 | ||
719 | static unsigned mpu_attr_to_access(uint32_t attr, unsigned ring) | |
720 | { | |
721 | static const unsigned access[2][16] = { | |
722 | [0] = { | |
723 | [4] = PAGE_READ, | |
724 | [5] = PAGE_READ | PAGE_EXEC, | |
725 | [6] = PAGE_READ | PAGE_WRITE, | |
726 | [7] = PAGE_READ | PAGE_WRITE | PAGE_EXEC, | |
727 | [8] = PAGE_WRITE, | |
728 | [9] = PAGE_READ | PAGE_WRITE, | |
729 | [10] = PAGE_READ | PAGE_WRITE, | |
730 | [11] = PAGE_READ | PAGE_WRITE | PAGE_EXEC, | |
731 | [12] = PAGE_READ, | |
732 | [13] = PAGE_READ | PAGE_EXEC, | |
733 | [14] = PAGE_READ | PAGE_WRITE, | |
734 | [15] = PAGE_READ | PAGE_WRITE | PAGE_EXEC, | |
735 | }, | |
736 | [1] = { | |
737 | [8] = PAGE_WRITE, | |
738 | [9] = PAGE_READ | PAGE_WRITE | PAGE_EXEC, | |
739 | [10] = PAGE_READ, | |
740 | [11] = PAGE_READ | PAGE_EXEC, | |
741 | [12] = PAGE_READ, | |
742 | [13] = PAGE_READ | PAGE_EXEC, | |
743 | [14] = PAGE_READ | PAGE_WRITE, | |
744 | [15] = PAGE_READ | PAGE_WRITE | PAGE_EXEC, | |
745 | }, | |
746 | }; | |
747 | unsigned rv; | |
748 | unsigned type; | |
749 | ||
750 | type = mpu_attr_to_cpu_cache(attr); | |
751 | rv = access[ring != 0][(attr & XTENSA_MPU_ACC_RIGHTS_MASK) >> | |
752 | XTENSA_MPU_ACC_RIGHTS_SHIFT]; | |
753 | ||
754 | if (type & XTENSA_MPU_TYPE_CPU_CACHE) { | |
755 | rv |= (type & XTENSA_MPU_TYPE_CPU_C) ? PAGE_CACHE_WB : PAGE_CACHE_WT; | |
756 | } else { | |
757 | rv |= PAGE_CACHE_BYPASS; | |
758 | } | |
759 | return rv; | |
760 | } | |
761 | ||
7e5e5a63 MF |
762 | static bool is_access_granted(unsigned access, int is_write) |
763 | { | |
764 | switch (is_write) { | |
765 | case 0: | |
766 | return access & PAGE_READ; | |
767 | ||
768 | case 1: | |
769 | return access & PAGE_WRITE; | |
770 | ||
771 | case 2: | |
772 | return access & PAGE_EXEC; | |
773 | ||
774 | default: | |
775 | return 0; | |
776 | } | |
777 | } | |
778 | ||
779 | static bool get_pte(CPUXtensaState *env, uint32_t vaddr, uint32_t *pte); | |
780 | ||
781 | static int get_physical_addr_mmu(CPUXtensaState *env, bool update_tlb, | |
782 | uint32_t vaddr, int is_write, int mmu_idx, | |
783 | uint32_t *paddr, uint32_t *page_size, | |
784 | unsigned *access, bool may_lookup_pt) | |
785 | { | |
786 | bool dtlb = is_write != 2; | |
787 | uint32_t wi; | |
788 | uint32_t ei; | |
789 | uint8_t ring; | |
790 | uint32_t vpn; | |
791 | uint32_t pte; | |
792 | const xtensa_tlb_entry *entry = NULL; | |
793 | xtensa_tlb_entry tmp_entry; | |
794 | int ret = xtensa_tlb_lookup(env, vaddr, dtlb, &wi, &ei, &ring); | |
795 | ||
796 | if ((ret == INST_TLB_MISS_CAUSE || ret == LOAD_STORE_TLB_MISS_CAUSE) && | |
797 | may_lookup_pt && get_pte(env, vaddr, &pte)) { | |
798 | ring = (pte >> 4) & 0x3; | |
799 | wi = 0; | |
800 | split_tlb_entry_spec_way(env, vaddr, dtlb, &vpn, wi, &ei); | |
801 | ||
802 | if (update_tlb) { | |
803 | wi = ++env->autorefill_idx & 0x3; | |
804 | xtensa_tlb_set_entry(env, dtlb, wi, ei, vpn, pte); | |
805 | env->sregs[EXCVADDR] = vaddr; | |
806 | qemu_log_mask(CPU_LOG_MMU, "%s: autorefill(%08x): %08x -> %08x\n", | |
807 | __func__, vaddr, vpn, pte); | |
808 | } else { | |
809 | xtensa_tlb_set_entry_mmu(env, &tmp_entry, dtlb, wi, ei, vpn, pte); | |
810 | entry = &tmp_entry; | |
811 | } | |
812 | ret = 0; | |
813 | } | |
814 | if (ret != 0) { | |
815 | return ret; | |
816 | } | |
817 | ||
818 | if (entry == NULL) { | |
819 | entry = xtensa_tlb_get_entry(env, dtlb, wi, ei); | |
820 | } | |
821 | ||
822 | if (ring < mmu_idx) { | |
823 | return dtlb ? | |
824 | LOAD_STORE_PRIVILEGE_CAUSE : | |
825 | INST_FETCH_PRIVILEGE_CAUSE; | |
826 | } | |
827 | ||
828 | *access = mmu_attr_to_access(entry->attr) & | |
829 | ~(dtlb ? PAGE_EXEC : PAGE_READ | PAGE_WRITE); | |
830 | if (!is_access_granted(*access, is_write)) { | |
831 | return dtlb ? | |
832 | (is_write ? | |
833 | STORE_PROHIBITED_CAUSE : | |
834 | LOAD_PROHIBITED_CAUSE) : | |
835 | INST_FETCH_PROHIBITED_CAUSE; | |
836 | } | |
837 | ||
838 | *paddr = entry->paddr | (vaddr & ~xtensa_tlb_get_addr_mask(env, dtlb, wi)); | |
839 | *page_size = ~xtensa_tlb_get_addr_mask(env, dtlb, wi) + 1; | |
840 | ||
841 | return 0; | |
842 | } | |
843 | ||
844 | static bool get_pte(CPUXtensaState *env, uint32_t vaddr, uint32_t *pte) | |
845 | { | |
92fddfbd | 846 | CPUState *cs = env_cpu(env); |
7e5e5a63 MF |
847 | uint32_t paddr; |
848 | uint32_t page_size; | |
849 | unsigned access; | |
850 | uint32_t pt_vaddr = | |
851 | (env->sregs[PTEVADDR] | (vaddr >> 10)) & 0xfffffffc; | |
852 | int ret = get_physical_addr_mmu(env, false, pt_vaddr, 0, 0, | |
853 | &paddr, &page_size, &access, false); | |
854 | ||
855 | if (ret == 0) { | |
856 | qemu_log_mask(CPU_LOG_MMU, | |
857 | "%s: autorefill(%08x): PTE va = %08x, pa = %08x\n", | |
858 | __func__, vaddr, pt_vaddr, paddr); | |
859 | } else { | |
860 | qemu_log_mask(CPU_LOG_MMU, | |
861 | "%s: autorefill(%08x): PTE va = %08x, failed (%d)\n", | |
862 | __func__, vaddr, pt_vaddr, ret); | |
863 | } | |
864 | ||
865 | if (ret == 0) { | |
866 | MemTxResult result; | |
867 | ||
868 | *pte = address_space_ldl(cs->as, paddr, MEMTXATTRS_UNSPECIFIED, | |
869 | &result); | |
870 | if (result != MEMTX_OK) { | |
871 | qemu_log_mask(CPU_LOG_MMU, | |
872 | "%s: couldn't load PTE: transaction failed (%u)\n", | |
873 | __func__, (unsigned)result); | |
874 | ret = 1; | |
875 | } | |
876 | } | |
877 | return ret == 0; | |
878 | } | |
879 | ||
880 | static int get_physical_addr_region(CPUXtensaState *env, | |
881 | uint32_t vaddr, int is_write, int mmu_idx, | |
882 | uint32_t *paddr, uint32_t *page_size, | |
883 | unsigned *access) | |
884 | { | |
885 | bool dtlb = is_write != 2; | |
886 | uint32_t wi = 0; | |
887 | uint32_t ei = (vaddr >> 29) & 0x7; | |
888 | const xtensa_tlb_entry *entry = | |
889 | xtensa_tlb_get_entry(env, dtlb, wi, ei); | |
890 | ||
891 | *access = region_attr_to_access(entry->attr); | |
892 | if (!is_access_granted(*access, is_write)) { | |
893 | return dtlb ? | |
894 | (is_write ? | |
895 | STORE_PROHIBITED_CAUSE : | |
896 | LOAD_PROHIBITED_CAUSE) : | |
897 | INST_FETCH_PROHIBITED_CAUSE; | |
898 | } | |
899 | ||
900 | *paddr = entry->paddr | (vaddr & ~REGION_PAGE_MASK); | |
901 | *page_size = ~REGION_PAGE_MASK + 1; | |
902 | ||
903 | return 0; | |
904 | } | |
905 | ||
4d04ea35 MF |
906 | static int xtensa_mpu_lookup(const xtensa_mpu_entry *entry, unsigned n, |
907 | uint32_t vaddr, unsigned *segment) | |
908 | { | |
909 | unsigned nhits = 0; | |
910 | unsigned i; | |
911 | ||
912 | for (i = 0; i < n; ++i) { | |
913 | if (vaddr >= entry[i].vaddr && | |
914 | (i == n - 1 || vaddr < entry[i + 1].vaddr)) { | |
915 | if (nhits++) { | |
916 | break; | |
917 | } | |
918 | *segment = i; | |
919 | } | |
920 | } | |
921 | return nhits; | |
922 | } | |
923 | ||
924 | void HELPER(wsr_mpuenb)(CPUXtensaState *env, uint32_t v) | |
925 | { | |
4d04ea35 MF |
926 | v &= (2u << (env->config->n_mpu_fg_segments - 1)) - 1; |
927 | ||
928 | if (v != env->sregs[MPUENB]) { | |
929 | env->sregs[MPUENB] = v; | |
92fddfbd | 930 | tlb_flush(env_cpu(env)); |
4d04ea35 MF |
931 | } |
932 | } | |
933 | ||
934 | void HELPER(wptlb)(CPUXtensaState *env, uint32_t p, uint32_t v) | |
935 | { | |
936 | unsigned segment = p & XTENSA_MPU_SEGMENT_MASK; | |
937 | ||
938 | if (segment < env->config->n_mpu_fg_segments) { | |
939 | env->mpu_fg[segment].vaddr = v & -env->config->mpu_align; | |
940 | env->mpu_fg[segment].attr = p & XTENSA_MPU_ATTR_MASK; | |
941 | env->sregs[MPUENB] = deposit32(env->sregs[MPUENB], segment, 1, v); | |
92fddfbd | 942 | tlb_flush(env_cpu(env)); |
4d04ea35 MF |
943 | } |
944 | } | |
945 | ||
946 | uint32_t HELPER(rptlb0)(CPUXtensaState *env, uint32_t s) | |
947 | { | |
948 | unsigned segment = s & XTENSA_MPU_SEGMENT_MASK; | |
949 | ||
950 | if (segment < env->config->n_mpu_fg_segments) { | |
951 | return env->mpu_fg[segment].vaddr | | |
952 | extract32(env->sregs[MPUENB], segment, 1); | |
953 | } else { | |
954 | return 0; | |
955 | } | |
956 | } | |
957 | ||
958 | uint32_t HELPER(rptlb1)(CPUXtensaState *env, uint32_t s) | |
959 | { | |
960 | unsigned segment = s & XTENSA_MPU_SEGMENT_MASK; | |
961 | ||
962 | if (segment < env->config->n_mpu_fg_segments) { | |
963 | return env->mpu_fg[segment].attr; | |
964 | } else { | |
965 | return 0; | |
966 | } | |
967 | } | |
968 | ||
969 | uint32_t HELPER(pptlb)(CPUXtensaState *env, uint32_t v) | |
970 | { | |
971 | unsigned nhits; | |
972 | unsigned segment = XTENSA_MPU_PROBE_B; | |
973 | unsigned bg_segment; | |
974 | ||
975 | nhits = xtensa_mpu_lookup(env->mpu_fg, env->config->n_mpu_fg_segments, | |
976 | v, &segment); | |
977 | if (nhits > 1) { | |
978 | HELPER(exception_cause_vaddr)(env, env->pc, | |
979 | LOAD_STORE_TLB_MULTI_HIT_CAUSE, v); | |
980 | } else if (nhits == 1 && (env->sregs[MPUENB] & (1u << segment))) { | |
981 | return env->mpu_fg[segment].attr | segment | XTENSA_MPU_PROBE_V; | |
982 | } else { | |
983 | xtensa_mpu_lookup(env->config->mpu_bg, | |
984 | env->config->n_mpu_bg_segments, | |
985 | v, &bg_segment); | |
986 | return env->config->mpu_bg[bg_segment].attr | segment; | |
987 | } | |
988 | } | |
989 | ||
990 | static int get_physical_addr_mpu(CPUXtensaState *env, | |
991 | uint32_t vaddr, int is_write, int mmu_idx, | |
992 | uint32_t *paddr, uint32_t *page_size, | |
993 | unsigned *access) | |
994 | { | |
995 | unsigned nhits; | |
996 | unsigned segment; | |
997 | uint32_t attr; | |
998 | ||
999 | nhits = xtensa_mpu_lookup(env->mpu_fg, env->config->n_mpu_fg_segments, | |
1000 | vaddr, &segment); | |
1001 | if (nhits > 1) { | |
1002 | return is_write < 2 ? | |
1003 | LOAD_STORE_TLB_MULTI_HIT_CAUSE : | |
1004 | INST_TLB_MULTI_HIT_CAUSE; | |
1005 | } else if (nhits == 1 && (env->sregs[MPUENB] & (1u << segment))) { | |
1006 | attr = env->mpu_fg[segment].attr; | |
1007 | } else { | |
1008 | xtensa_mpu_lookup(env->config->mpu_bg, | |
1009 | env->config->n_mpu_bg_segments, | |
1010 | vaddr, &segment); | |
1011 | attr = env->config->mpu_bg[segment].attr; | |
1012 | } | |
1013 | ||
1014 | *access = mpu_attr_to_access(attr, mmu_idx); | |
1015 | if (!is_access_granted(*access, is_write)) { | |
1016 | return is_write < 2 ? | |
1017 | (is_write ? | |
1018 | STORE_PROHIBITED_CAUSE : | |
1019 | LOAD_PROHIBITED_CAUSE) : | |
1020 | INST_FETCH_PROHIBITED_CAUSE; | |
1021 | } | |
1022 | *paddr = vaddr; | |
1023 | *page_size = env->config->mpu_align; | |
1024 | return 0; | |
1025 | } | |
1026 | ||
7e5e5a63 MF |
1027 | /*! |
1028 | * Convert virtual address to physical addr. | |
1029 | * MMU may issue pagewalk and change xtensa autorefill TLB way entry. | |
1030 | * | |
1031 | * \return 0 if ok, exception cause code otherwise | |
1032 | */ | |
1033 | int xtensa_get_physical_addr(CPUXtensaState *env, bool update_tlb, | |
1034 | uint32_t vaddr, int is_write, int mmu_idx, | |
1035 | uint32_t *paddr, uint32_t *page_size, | |
1036 | unsigned *access) | |
1037 | { | |
1038 | if (xtensa_option_enabled(env->config, XTENSA_OPTION_MMU)) { | |
1039 | return get_physical_addr_mmu(env, update_tlb, | |
1040 | vaddr, is_write, mmu_idx, paddr, | |
1041 | page_size, access, true); | |
1042 | } else if (xtensa_option_bits_enabled(env->config, | |
1043 | XTENSA_OPTION_BIT(XTENSA_OPTION_REGION_PROTECTION) | | |
1044 | XTENSA_OPTION_BIT(XTENSA_OPTION_REGION_TRANSLATION))) { | |
1045 | return get_physical_addr_region(env, vaddr, is_write, mmu_idx, | |
1046 | paddr, page_size, access); | |
4d04ea35 MF |
1047 | } else if (xtensa_option_enabled(env->config, XTENSA_OPTION_MPU)) { |
1048 | return get_physical_addr_mpu(env, vaddr, is_write, mmu_idx, | |
1049 | paddr, page_size, access); | |
7e5e5a63 MF |
1050 | } else { |
1051 | *paddr = vaddr; | |
1052 | *page_size = TARGET_PAGE_SIZE; | |
1053 | *access = cacheattr_attr_to_access(env->sregs[CACHEATTR] >> | |
1054 | ((vaddr & 0xe0000000) >> 27)); | |
1055 | return 0; | |
1056 | } | |
1057 | } | |
1058 | ||
fad866da | 1059 | static void dump_tlb(CPUXtensaState *env, bool dtlb) |
7e5e5a63 MF |
1060 | { |
1061 | unsigned wi, ei; | |
1062 | const xtensa_tlb *conf = | |
1063 | dtlb ? &env->config->dtlb : &env->config->itlb; | |
1064 | unsigned (*attr_to_access)(uint32_t) = | |
1065 | xtensa_option_enabled(env->config, XTENSA_OPTION_MMU) ? | |
1066 | mmu_attr_to_access : region_attr_to_access; | |
1067 | ||
1068 | for (wi = 0; wi < conf->nways; ++wi) { | |
1069 | uint32_t sz = ~xtensa_tlb_get_addr_mask(env, dtlb, wi) + 1; | |
1070 | const char *sz_text; | |
1071 | bool print_header = true; | |
1072 | ||
1073 | if (sz >= 0x100000) { | |
1074 | sz /= MiB; | |
1075 | sz_text = "MB"; | |
1076 | } else { | |
1077 | sz /= KiB; | |
1078 | sz_text = "KB"; | |
1079 | } | |
1080 | ||
1081 | for (ei = 0; ei < conf->way_size[wi]; ++ei) { | |
1082 | const xtensa_tlb_entry *entry = | |
1083 | xtensa_tlb_get_entry(env, dtlb, wi, ei); | |
1084 | ||
1085 | if (entry->asid) { | |
1086 | static const char * const cache_text[8] = { | |
1087 | [PAGE_CACHE_BYPASS >> PAGE_CACHE_SHIFT] = "Bypass", | |
1088 | [PAGE_CACHE_WT >> PAGE_CACHE_SHIFT] = "WT", | |
1089 | [PAGE_CACHE_WB >> PAGE_CACHE_SHIFT] = "WB", | |
1090 | [PAGE_CACHE_ISOLATE >> PAGE_CACHE_SHIFT] = "Isolate", | |
1091 | }; | |
1092 | unsigned access = attr_to_access(entry->attr); | |
1093 | unsigned cache_idx = (access & PAGE_CACHE_MASK) >> | |
1094 | PAGE_CACHE_SHIFT; | |
1095 | ||
1096 | if (print_header) { | |
1097 | print_header = false; | |
fad866da MA |
1098 | qemu_printf("Way %u (%d %s)\n", wi, sz, sz_text); |
1099 | qemu_printf("\tVaddr Paddr ASID Attr RWX Cache\n" | |
7e5e5a63 MF |
1100 | "\t---------- ---------- ---- ---- --- -------\n"); |
1101 | } | |
61848717 | 1102 | qemu_printf("\t0x%08x 0x%08x 0x%02x 0x%02x %c%c%c %s\n", |
7e5e5a63 MF |
1103 | entry->vaddr, |
1104 | entry->paddr, | |
1105 | entry->asid, | |
1106 | entry->attr, | |
1107 | (access & PAGE_READ) ? 'R' : '-', | |
1108 | (access & PAGE_WRITE) ? 'W' : '-', | |
1109 | (access & PAGE_EXEC) ? 'X' : '-', | |
1110 | cache_text[cache_idx] ? | |
1111 | cache_text[cache_idx] : "Invalid"); | |
1112 | } | |
1113 | } | |
1114 | } | |
1115 | } | |
1116 | ||
4d04ea35 MF |
1117 | static void dump_mpu(CPUXtensaState *env, |
1118 | const xtensa_mpu_entry *entry, unsigned n) | |
1119 | { | |
1120 | unsigned i; | |
1121 | ||
1122 | qemu_printf("\t%s Vaddr Attr Ring0 Ring1 System Type CPU cache\n" | |
1123 | "\t%s ---------- ---------- ----- ----- ------------- ---------\n", | |
1124 | env ? "En" : " ", | |
1125 | env ? "--" : " "); | |
1126 | ||
1127 | for (i = 0; i < n; ++i) { | |
1128 | uint32_t attr = entry[i].attr; | |
1129 | unsigned access0 = mpu_attr_to_access(attr, 0); | |
1130 | unsigned access1 = mpu_attr_to_access(attr, 1); | |
1131 | unsigned type = mpu_attr_to_type(attr); | |
1132 | char cpu_cache = (type & XTENSA_MPU_TYPE_CPU_CACHE) ? '-' : ' '; | |
1133 | ||
1134 | qemu_printf("\t %c 0x%08x 0x%08x %c%c%c %c%c%c ", | |
1135 | env ? | |
1136 | ((env->sregs[MPUENB] & (1u << i)) ? '+' : '-') : ' ', | |
1137 | entry[i].vaddr, attr, | |
1138 | (access0 & PAGE_READ) ? 'R' : '-', | |
1139 | (access0 & PAGE_WRITE) ? 'W' : '-', | |
1140 | (access0 & PAGE_EXEC) ? 'X' : '-', | |
1141 | (access1 & PAGE_READ) ? 'R' : '-', | |
1142 | (access1 & PAGE_WRITE) ? 'W' : '-', | |
1143 | (access1 & PAGE_EXEC) ? 'X' : '-'); | |
1144 | ||
1145 | switch (type & XTENSA_MPU_SYSTEM_TYPE_MASK) { | |
1146 | case XTENSA_MPU_SYSTEM_TYPE_DEVICE: | |
1147 | qemu_printf("Device %cB %3s\n", | |
1148 | (type & XTENSA_MPU_TYPE_B) ? ' ' : 'n', | |
1149 | (type & XTENSA_MPU_TYPE_INT) ? "int" : ""); | |
1150 | break; | |
1151 | case XTENSA_MPU_SYSTEM_TYPE_NC: | |
1152 | qemu_printf("Sys NC %cB %c%c%c\n", | |
1153 | (type & XTENSA_MPU_TYPE_B) ? ' ' : 'n', | |
1154 | (type & XTENSA_MPU_TYPE_CPU_R) ? 'r' : cpu_cache, | |
1155 | (type & XTENSA_MPU_TYPE_CPU_W) ? 'w' : cpu_cache, | |
1156 | (type & XTENSA_MPU_TYPE_CPU_C) ? 'c' : cpu_cache); | |
1157 | break; | |
1158 | case XTENSA_MPU_SYSTEM_TYPE_C: | |
1159 | qemu_printf("Sys C %c%c%c %c%c%c\n", | |
1160 | (type & XTENSA_MPU_TYPE_SYS_R) ? 'R' : '-', | |
1161 | (type & XTENSA_MPU_TYPE_SYS_W) ? 'W' : '-', | |
1162 | (type & XTENSA_MPU_TYPE_SYS_C) ? 'C' : '-', | |
1163 | (type & XTENSA_MPU_TYPE_CPU_R) ? 'r' : cpu_cache, | |
1164 | (type & XTENSA_MPU_TYPE_CPU_W) ? 'w' : cpu_cache, | |
1165 | (type & XTENSA_MPU_TYPE_CPU_C) ? 'c' : cpu_cache); | |
1166 | break; | |
1167 | default: | |
1168 | qemu_printf("Unknown\n"); | |
1169 | break; | |
1170 | } | |
1171 | } | |
1172 | } | |
1173 | ||
fad866da | 1174 | void dump_mmu(CPUXtensaState *env) |
7e5e5a63 MF |
1175 | { |
1176 | if (xtensa_option_bits_enabled(env->config, | |
1177 | XTENSA_OPTION_BIT(XTENSA_OPTION_REGION_PROTECTION) | | |
1178 | XTENSA_OPTION_BIT(XTENSA_OPTION_REGION_TRANSLATION) | | |
1179 | XTENSA_OPTION_BIT(XTENSA_OPTION_MMU))) { | |
1180 | ||
fad866da MA |
1181 | qemu_printf("ITLB:\n"); |
1182 | dump_tlb(env, false); | |
1183 | qemu_printf("\nDTLB:\n"); | |
1184 | dump_tlb(env, true); | |
4d04ea35 MF |
1185 | } else if (xtensa_option_enabled(env->config, XTENSA_OPTION_MPU)) { |
1186 | qemu_printf("Foreground map:\n"); | |
1187 | dump_mpu(env, env->mpu_fg, env->config->n_mpu_fg_segments); | |
1188 | qemu_printf("\nBackground map:\n"); | |
1189 | dump_mpu(NULL, env->config->mpu_bg, env->config->n_mpu_bg_segments); | |
7e5e5a63 | 1190 | } else { |
fad866da | 1191 | qemu_printf("No TLB for this CPU core\n"); |
7e5e5a63 MF |
1192 | } |
1193 | } |