xref: /openbmc/qemu/target/xtensa/mmu_helper.c (revision ae3c12a0)
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"
29 #include "qemu/main-loop.h"
30 #include "qemu/qemu-print.h"
31 #include "qemu/units.h"
32 #include "cpu.h"
33 #include "exec/helper-proto.h"
34 #include "qemu/host-utils.h"
35 #include "exec/exec-all.h"
36 #include "exec/cpu_ldst.h"
37 
38 void HELPER(itlb_hit_test)(CPUXtensaState *env, uint32_t vaddr)
39 {
40     /*
41      * Attempt the memory load; we don't care about the result but
42      * only the side-effects (ie any MMU or other exception)
43      */
44     cpu_ldub_code_ra(env, vaddr, GETPC());
45 }
46 
47 void HELPER(wsr_rasid)(CPUXtensaState *env, uint32_t v)
48 {
49     XtensaCPU *cpu = xtensa_env_get_cpu(env);
50 
51     v = (v & 0xffffff00) | 0x1;
52     if (v != env->sregs[RASID]) {
53         env->sregs[RASID] = v;
54         tlb_flush(CPU(cpu));
55     }
56 }
57 
58 static uint32_t get_page_size(const CPUXtensaState *env,
59                               bool dtlb, uint32_t way)
60 {
61     uint32_t tlbcfg = env->sregs[dtlb ? DTLBCFG : ITLBCFG];
62 
63     switch (way) {
64     case 4:
65         return (tlbcfg >> 16) & 0x3;
66 
67     case 5:
68         return (tlbcfg >> 20) & 0x1;
69 
70     case 6:
71         return (tlbcfg >> 24) & 0x1;
72 
73     default:
74         return 0;
75     }
76 }
77 
78 /*!
79  * Get bit mask for the virtual address bits translated by the TLB way
80  */
81 uint32_t xtensa_tlb_get_addr_mask(const CPUXtensaState *env,
82                                   bool dtlb, uint32_t way)
83 {
84     if (xtensa_option_enabled(env->config, XTENSA_OPTION_MMU)) {
85         bool varway56 = dtlb ?
86             env->config->dtlb.varway56 :
87             env->config->itlb.varway56;
88 
89         switch (way) {
90         case 4:
91             return 0xfff00000 << get_page_size(env, dtlb, way) * 2;
92 
93         case 5:
94             if (varway56) {
95                 return 0xf8000000 << get_page_size(env, dtlb, way);
96             } else {
97                 return 0xf8000000;
98             }
99 
100         case 6:
101             if (varway56) {
102                 return 0xf0000000 << (1 - get_page_size(env, dtlb, way));
103             } else {
104                 return 0xf0000000;
105             }
106 
107         default:
108             return 0xfffff000;
109         }
110     } else {
111         return REGION_PAGE_MASK;
112     }
113 }
114 
115 /*!
116  * Get bit mask for the 'VPN without index' field.
117  * See ISA, 4.6.5.6, data format for RxTLB0
118  */
119 static uint32_t get_vpn_mask(const CPUXtensaState *env, bool dtlb, uint32_t way)
120 {
121     if (way < 4) {
122         bool is32 = (dtlb ?
123                 env->config->dtlb.nrefillentries :
124                 env->config->itlb.nrefillentries) == 32;
125         return is32 ? 0xffff8000 : 0xffffc000;
126     } else if (way == 4) {
127         return xtensa_tlb_get_addr_mask(env, dtlb, way) << 2;
128     } else if (way <= 6) {
129         uint32_t mask = xtensa_tlb_get_addr_mask(env, dtlb, way);
130         bool varway56 = dtlb ?
131             env->config->dtlb.varway56 :
132             env->config->itlb.varway56;
133 
134         if (varway56) {
135             return mask << (way == 5 ? 2 : 3);
136         } else {
137             return mask << 1;
138         }
139     } else {
140         return 0xfffff000;
141     }
142 }
143 
144 /*!
145  * Split virtual address into VPN (with index) and entry index
146  * for the given TLB way
147  */
148 void split_tlb_entry_spec_way(const CPUXtensaState *env, uint32_t v, bool dtlb,
149         uint32_t *vpn, uint32_t wi, uint32_t *ei)
150 {
151     bool varway56 = dtlb ?
152         env->config->dtlb.varway56 :
153         env->config->itlb.varway56;
154 
155     if (!dtlb) {
156         wi &= 7;
157     }
158 
159     if (wi < 4) {
160         bool is32 = (dtlb ?
161                 env->config->dtlb.nrefillentries :
162                 env->config->itlb.nrefillentries) == 32;
163         *ei = (v >> 12) & (is32 ? 0x7 : 0x3);
164     } else {
165         switch (wi) {
166         case 4:
167             {
168                 uint32_t eibase = 20 + get_page_size(env, dtlb, wi) * 2;
169                 *ei = (v >> eibase) & 0x3;
170             }
171             break;
172 
173         case 5:
174             if (varway56) {
175                 uint32_t eibase = 27 + get_page_size(env, dtlb, wi);
176                 *ei = (v >> eibase) & 0x3;
177             } else {
178                 *ei = (v >> 27) & 0x1;
179             }
180             break;
181 
182         case 6:
183             if (varway56) {
184                 uint32_t eibase = 29 - get_page_size(env, dtlb, wi);
185                 *ei = (v >> eibase) & 0x7;
186             } else {
187                 *ei = (v >> 28) & 0x1;
188             }
189             break;
190 
191         default:
192             *ei = 0;
193             break;
194         }
195     }
196     *vpn = v & xtensa_tlb_get_addr_mask(env, dtlb, wi);
197 }
198 
199 /*!
200  * Split TLB address into TLB way, entry index and VPN (with index).
201  * See ISA, 4.6.5.5 - 4.6.5.8 for the TLB addressing format
202  */
203 static void split_tlb_entry_spec(CPUXtensaState *env, uint32_t v, bool dtlb,
204         uint32_t *vpn, uint32_t *wi, uint32_t *ei)
205 {
206     if (xtensa_option_enabled(env->config, XTENSA_OPTION_MMU)) {
207         *wi = v & (dtlb ? 0xf : 0x7);
208         split_tlb_entry_spec_way(env, v, dtlb, vpn, *wi, ei);
209     } else {
210         *vpn = v & REGION_PAGE_MASK;
211         *wi = 0;
212         *ei = (v >> 29) & 0x7;
213     }
214 }
215 
216 static xtensa_tlb_entry *get_tlb_entry(CPUXtensaState *env,
217         uint32_t v, bool dtlb, uint32_t *pwi)
218 {
219     uint32_t vpn;
220     uint32_t wi;
221     uint32_t ei;
222 
223     split_tlb_entry_spec(env, v, dtlb, &vpn, &wi, &ei);
224     if (pwi) {
225         *pwi = wi;
226     }
227     return xtensa_tlb_get_entry(env, dtlb, wi, ei);
228 }
229 
230 uint32_t HELPER(rtlb0)(CPUXtensaState *env, uint32_t v, uint32_t dtlb)
231 {
232     if (xtensa_option_enabled(env->config, XTENSA_OPTION_MMU)) {
233         uint32_t wi;
234         const xtensa_tlb_entry *entry = get_tlb_entry(env, v, dtlb, &wi);
235         return (entry->vaddr & get_vpn_mask(env, dtlb, wi)) | entry->asid;
236     } else {
237         return v & REGION_PAGE_MASK;
238     }
239 }
240 
241 uint32_t HELPER(rtlb1)(CPUXtensaState *env, uint32_t v, uint32_t dtlb)
242 {
243     const xtensa_tlb_entry *entry = get_tlb_entry(env, v, dtlb, NULL);
244     return entry->paddr | entry->attr;
245 }
246 
247 void HELPER(itlb)(CPUXtensaState *env, uint32_t v, uint32_t dtlb)
248 {
249     if (xtensa_option_enabled(env->config, XTENSA_OPTION_MMU)) {
250         uint32_t wi;
251         xtensa_tlb_entry *entry = get_tlb_entry(env, v, dtlb, &wi);
252         if (entry->variable && entry->asid) {
253             tlb_flush_page(CPU(xtensa_env_get_cpu(env)), entry->vaddr);
254             entry->asid = 0;
255         }
256     }
257 }
258 
259 uint32_t HELPER(ptlb)(CPUXtensaState *env, uint32_t v, uint32_t dtlb)
260 {
261     if (xtensa_option_enabled(env->config, XTENSA_OPTION_MMU)) {
262         uint32_t wi;
263         uint32_t ei;
264         uint8_t ring;
265         int res = xtensa_tlb_lookup(env, v, dtlb, &wi, &ei, &ring);
266 
267         switch (res) {
268         case 0:
269             if (ring >= xtensa_get_ring(env)) {
270                 return (v & 0xfffff000) | wi | (dtlb ? 0x10 : 0x8);
271             }
272             break;
273 
274         case INST_TLB_MULTI_HIT_CAUSE:
275         case LOAD_STORE_TLB_MULTI_HIT_CAUSE:
276             HELPER(exception_cause_vaddr)(env, env->pc, res, v);
277             break;
278         }
279         return 0;
280     } else {
281         return (v & REGION_PAGE_MASK) | 0x1;
282     }
283 }
284 
285 void xtensa_tlb_set_entry_mmu(const CPUXtensaState *env,
286                               xtensa_tlb_entry *entry, bool dtlb,
287                               unsigned wi, unsigned ei, uint32_t vpn,
288                               uint32_t pte)
289 {
290     entry->vaddr = vpn;
291     entry->paddr = pte & xtensa_tlb_get_addr_mask(env, dtlb, wi);
292     entry->asid = (env->sregs[RASID] >> ((pte >> 1) & 0x18)) & 0xff;
293     entry->attr = pte & 0xf;
294 }
295 
296 void xtensa_tlb_set_entry(CPUXtensaState *env, bool dtlb,
297                           unsigned wi, unsigned ei, uint32_t vpn, uint32_t pte)
298 {
299     XtensaCPU *cpu = xtensa_env_get_cpu(env);
300     CPUState *cs = CPU(cpu);
301     xtensa_tlb_entry *entry = xtensa_tlb_get_entry(env, dtlb, wi, ei);
302 
303     if (xtensa_option_enabled(env->config, XTENSA_OPTION_MMU)) {
304         if (entry->variable) {
305             if (entry->asid) {
306                 tlb_flush_page(cs, entry->vaddr);
307             }
308             xtensa_tlb_set_entry_mmu(env, entry, dtlb, wi, ei, vpn, pte);
309             tlb_flush_page(cs, entry->vaddr);
310         } else {
311             qemu_log_mask(LOG_GUEST_ERROR,
312                           "%s %d, %d, %d trying to set immutable entry\n",
313                           __func__, dtlb, wi, ei);
314         }
315     } else {
316         tlb_flush_page(cs, entry->vaddr);
317         if (xtensa_option_enabled(env->config,
318                     XTENSA_OPTION_REGION_TRANSLATION)) {
319             entry->paddr = pte & REGION_PAGE_MASK;
320         }
321         entry->attr = pte & 0xf;
322     }
323 }
324 
325 void HELPER(wtlb)(CPUXtensaState *env, uint32_t p, uint32_t v, uint32_t dtlb)
326 {
327     uint32_t vpn;
328     uint32_t wi;
329     uint32_t ei;
330     split_tlb_entry_spec(env, v, dtlb, &vpn, &wi, &ei);
331     xtensa_tlb_set_entry(env, dtlb, wi, ei, vpn, p);
332 }
333 
334 hwaddr xtensa_cpu_get_phys_page_debug(CPUState *cs, vaddr addr)
335 {
336     XtensaCPU *cpu = XTENSA_CPU(cs);
337     uint32_t paddr;
338     uint32_t page_size;
339     unsigned access;
340 
341     if (xtensa_get_physical_addr(&cpu->env, false, addr, 0, 0,
342                 &paddr, &page_size, &access) == 0) {
343         return paddr;
344     }
345     if (xtensa_get_physical_addr(&cpu->env, false, addr, 2, 0,
346                 &paddr, &page_size, &access) == 0) {
347         return paddr;
348     }
349     return ~0;
350 }
351 
352 static void reset_tlb_mmu_all_ways(CPUXtensaState *env,
353                                    const xtensa_tlb *tlb,
354                                    xtensa_tlb_entry entry[][MAX_TLB_WAY_SIZE])
355 {
356     unsigned wi, ei;
357 
358     for (wi = 0; wi < tlb->nways; ++wi) {
359         for (ei = 0; ei < tlb->way_size[wi]; ++ei) {
360             entry[wi][ei].asid = 0;
361             entry[wi][ei].variable = true;
362         }
363     }
364 }
365 
366 static void reset_tlb_mmu_ways56(CPUXtensaState *env,
367                                  const xtensa_tlb *tlb,
368                                  xtensa_tlb_entry entry[][MAX_TLB_WAY_SIZE])
369 {
370     if (!tlb->varway56) {
371         static const xtensa_tlb_entry way5[] = {
372             {
373                 .vaddr = 0xd0000000,
374                 .paddr = 0,
375                 .asid = 1,
376                 .attr = 7,
377                 .variable = false,
378             }, {
379                 .vaddr = 0xd8000000,
380                 .paddr = 0,
381                 .asid = 1,
382                 .attr = 3,
383                 .variable = false,
384             }
385         };
386         static const xtensa_tlb_entry way6[] = {
387             {
388                 .vaddr = 0xe0000000,
389                 .paddr = 0xf0000000,
390                 .asid = 1,
391                 .attr = 7,
392                 .variable = false,
393             }, {
394                 .vaddr = 0xf0000000,
395                 .paddr = 0xf0000000,
396                 .asid = 1,
397                 .attr = 3,
398                 .variable = false,
399             }
400         };
401         memcpy(entry[5], way5, sizeof(way5));
402         memcpy(entry[6], way6, sizeof(way6));
403     } else {
404         uint32_t ei;
405         for (ei = 0; ei < 8; ++ei) {
406             entry[6][ei].vaddr = ei << 29;
407             entry[6][ei].paddr = ei << 29;
408             entry[6][ei].asid = 1;
409             entry[6][ei].attr = 3;
410         }
411     }
412 }
413 
414 static void reset_tlb_region_way0(CPUXtensaState *env,
415                                   xtensa_tlb_entry entry[][MAX_TLB_WAY_SIZE])
416 {
417     unsigned ei;
418 
419     for (ei = 0; ei < 8; ++ei) {
420         entry[0][ei].vaddr = ei << 29;
421         entry[0][ei].paddr = ei << 29;
422         entry[0][ei].asid = 1;
423         entry[0][ei].attr = 2;
424         entry[0][ei].variable = true;
425     }
426 }
427 
428 void reset_mmu(CPUXtensaState *env)
429 {
430     if (xtensa_option_enabled(env->config, XTENSA_OPTION_MMU)) {
431         env->sregs[RASID] = 0x04030201;
432         env->sregs[ITLBCFG] = 0;
433         env->sregs[DTLBCFG] = 0;
434         env->autorefill_idx = 0;
435         reset_tlb_mmu_all_ways(env, &env->config->itlb, env->itlb);
436         reset_tlb_mmu_all_ways(env, &env->config->dtlb, env->dtlb);
437         reset_tlb_mmu_ways56(env, &env->config->itlb, env->itlb);
438         reset_tlb_mmu_ways56(env, &env->config->dtlb, env->dtlb);
439     } else {
440         reset_tlb_region_way0(env, env->itlb);
441         reset_tlb_region_way0(env, env->dtlb);
442     }
443 }
444 
445 static unsigned get_ring(const CPUXtensaState *env, uint8_t asid)
446 {
447     unsigned i;
448     for (i = 0; i < 4; ++i) {
449         if (((env->sregs[RASID] >> i * 8) & 0xff) == asid) {
450             return i;
451         }
452     }
453     return 0xff;
454 }
455 
456 /*!
457  * Lookup xtensa TLB for the given virtual address.
458  * See ISA, 4.6.2.2
459  *
460  * \param pwi: [out] way index
461  * \param pei: [out] entry index
462  * \param pring: [out] access ring
463  * \return 0 if ok, exception cause code otherwise
464  */
465 int xtensa_tlb_lookup(const CPUXtensaState *env, uint32_t addr, bool dtlb,
466                       uint32_t *pwi, uint32_t *pei, uint8_t *pring)
467 {
468     const xtensa_tlb *tlb = dtlb ?
469         &env->config->dtlb : &env->config->itlb;
470     const xtensa_tlb_entry (*entry)[MAX_TLB_WAY_SIZE] = dtlb ?
471         env->dtlb : env->itlb;
472 
473     int nhits = 0;
474     unsigned wi;
475 
476     for (wi = 0; wi < tlb->nways; ++wi) {
477         uint32_t vpn;
478         uint32_t ei;
479         split_tlb_entry_spec_way(env, addr, dtlb, &vpn, wi, &ei);
480         if (entry[wi][ei].vaddr == vpn && entry[wi][ei].asid) {
481             unsigned ring = get_ring(env, entry[wi][ei].asid);
482             if (ring < 4) {
483                 if (++nhits > 1) {
484                     return dtlb ?
485                         LOAD_STORE_TLB_MULTI_HIT_CAUSE :
486                         INST_TLB_MULTI_HIT_CAUSE;
487                 }
488                 *pwi = wi;
489                 *pei = ei;
490                 *pring = ring;
491             }
492         }
493     }
494     return nhits ? 0 :
495         (dtlb ? LOAD_STORE_TLB_MISS_CAUSE : INST_TLB_MISS_CAUSE);
496 }
497 
498 /*!
499  * Convert MMU ATTR to PAGE_{READ,WRITE,EXEC} mask.
500  * See ISA, 4.6.5.10
501  */
502 static unsigned mmu_attr_to_access(uint32_t attr)
503 {
504     unsigned access = 0;
505 
506     if (attr < 12) {
507         access |= PAGE_READ;
508         if (attr & 0x1) {
509             access |= PAGE_EXEC;
510         }
511         if (attr & 0x2) {
512             access |= PAGE_WRITE;
513         }
514 
515         switch (attr & 0xc) {
516         case 0:
517             access |= PAGE_CACHE_BYPASS;
518             break;
519 
520         case 4:
521             access |= PAGE_CACHE_WB;
522             break;
523 
524         case 8:
525             access |= PAGE_CACHE_WT;
526             break;
527         }
528     } else if (attr == 13) {
529         access |= PAGE_READ | PAGE_WRITE | PAGE_CACHE_ISOLATE;
530     }
531     return access;
532 }
533 
534 /*!
535  * Convert region protection ATTR to PAGE_{READ,WRITE,EXEC} mask.
536  * See ISA, 4.6.3.3
537  */
538 static unsigned region_attr_to_access(uint32_t attr)
539 {
540     static const unsigned access[16] = {
541          [0] = PAGE_READ | PAGE_WRITE             | PAGE_CACHE_WT,
542          [1] = PAGE_READ | PAGE_WRITE | PAGE_EXEC | PAGE_CACHE_WT,
543          [2] = PAGE_READ | PAGE_WRITE | PAGE_EXEC | PAGE_CACHE_BYPASS,
544          [3] =                          PAGE_EXEC | PAGE_CACHE_WB,
545          [4] = PAGE_READ | PAGE_WRITE | PAGE_EXEC | PAGE_CACHE_WB,
546          [5] = PAGE_READ | PAGE_WRITE | PAGE_EXEC | PAGE_CACHE_WB,
547         [14] = PAGE_READ | PAGE_WRITE             | PAGE_CACHE_ISOLATE,
548     };
549 
550     return access[attr & 0xf];
551 }
552 
553 /*!
554  * Convert cacheattr to PAGE_{READ,WRITE,EXEC} mask.
555  * See ISA, A.2.14 The Cache Attribute Register
556  */
557 static unsigned cacheattr_attr_to_access(uint32_t attr)
558 {
559     static const unsigned access[16] = {
560          [0] = PAGE_READ | PAGE_WRITE             | PAGE_CACHE_WT,
561          [1] = PAGE_READ | PAGE_WRITE | PAGE_EXEC | PAGE_CACHE_WT,
562          [2] = PAGE_READ | PAGE_WRITE | PAGE_EXEC | PAGE_CACHE_BYPASS,
563          [3] =                          PAGE_EXEC | PAGE_CACHE_WB,
564          [4] = PAGE_READ | PAGE_WRITE | PAGE_EXEC | PAGE_CACHE_WB,
565         [14] = PAGE_READ | PAGE_WRITE             | PAGE_CACHE_ISOLATE,
566     };
567 
568     return access[attr & 0xf];
569 }
570 
571 static bool is_access_granted(unsigned access, int is_write)
572 {
573     switch (is_write) {
574     case 0:
575         return access & PAGE_READ;
576 
577     case 1:
578         return access & PAGE_WRITE;
579 
580     case 2:
581         return access & PAGE_EXEC;
582 
583     default:
584         return 0;
585     }
586 }
587 
588 static bool get_pte(CPUXtensaState *env, uint32_t vaddr, uint32_t *pte);
589 
590 static int get_physical_addr_mmu(CPUXtensaState *env, bool update_tlb,
591                                  uint32_t vaddr, int is_write, int mmu_idx,
592                                  uint32_t *paddr, uint32_t *page_size,
593                                  unsigned *access, bool may_lookup_pt)
594 {
595     bool dtlb = is_write != 2;
596     uint32_t wi;
597     uint32_t ei;
598     uint8_t ring;
599     uint32_t vpn;
600     uint32_t pte;
601     const xtensa_tlb_entry *entry = NULL;
602     xtensa_tlb_entry tmp_entry;
603     int ret = xtensa_tlb_lookup(env, vaddr, dtlb, &wi, &ei, &ring);
604 
605     if ((ret == INST_TLB_MISS_CAUSE || ret == LOAD_STORE_TLB_MISS_CAUSE) &&
606         may_lookup_pt && get_pte(env, vaddr, &pte)) {
607         ring = (pte >> 4) & 0x3;
608         wi = 0;
609         split_tlb_entry_spec_way(env, vaddr, dtlb, &vpn, wi, &ei);
610 
611         if (update_tlb) {
612             wi = ++env->autorefill_idx & 0x3;
613             xtensa_tlb_set_entry(env, dtlb, wi, ei, vpn, pte);
614             env->sregs[EXCVADDR] = vaddr;
615             qemu_log_mask(CPU_LOG_MMU, "%s: autorefill(%08x): %08x -> %08x\n",
616                           __func__, vaddr, vpn, pte);
617         } else {
618             xtensa_tlb_set_entry_mmu(env, &tmp_entry, dtlb, wi, ei, vpn, pte);
619             entry = &tmp_entry;
620         }
621         ret = 0;
622     }
623     if (ret != 0) {
624         return ret;
625     }
626 
627     if (entry == NULL) {
628         entry = xtensa_tlb_get_entry(env, dtlb, wi, ei);
629     }
630 
631     if (ring < mmu_idx) {
632         return dtlb ?
633             LOAD_STORE_PRIVILEGE_CAUSE :
634             INST_FETCH_PRIVILEGE_CAUSE;
635     }
636 
637     *access = mmu_attr_to_access(entry->attr) &
638         ~(dtlb ? PAGE_EXEC : PAGE_READ | PAGE_WRITE);
639     if (!is_access_granted(*access, is_write)) {
640         return dtlb ?
641             (is_write ?
642              STORE_PROHIBITED_CAUSE :
643              LOAD_PROHIBITED_CAUSE) :
644             INST_FETCH_PROHIBITED_CAUSE;
645     }
646 
647     *paddr = entry->paddr | (vaddr & ~xtensa_tlb_get_addr_mask(env, dtlb, wi));
648     *page_size = ~xtensa_tlb_get_addr_mask(env, dtlb, wi) + 1;
649 
650     return 0;
651 }
652 
653 static bool get_pte(CPUXtensaState *env, uint32_t vaddr, uint32_t *pte)
654 {
655     CPUState *cs = CPU(xtensa_env_get_cpu(env));
656     uint32_t paddr;
657     uint32_t page_size;
658     unsigned access;
659     uint32_t pt_vaddr =
660         (env->sregs[PTEVADDR] | (vaddr >> 10)) & 0xfffffffc;
661     int ret = get_physical_addr_mmu(env, false, pt_vaddr, 0, 0,
662                                     &paddr, &page_size, &access, false);
663 
664     if (ret == 0) {
665         qemu_log_mask(CPU_LOG_MMU,
666                       "%s: autorefill(%08x): PTE va = %08x, pa = %08x\n",
667                       __func__, vaddr, pt_vaddr, paddr);
668     } else {
669         qemu_log_mask(CPU_LOG_MMU,
670                       "%s: autorefill(%08x): PTE va = %08x, failed (%d)\n",
671                       __func__, vaddr, pt_vaddr, ret);
672     }
673 
674     if (ret == 0) {
675         MemTxResult result;
676 
677         *pte = address_space_ldl(cs->as, paddr, MEMTXATTRS_UNSPECIFIED,
678                                  &result);
679         if (result != MEMTX_OK) {
680             qemu_log_mask(CPU_LOG_MMU,
681                           "%s: couldn't load PTE: transaction failed (%u)\n",
682                           __func__, (unsigned)result);
683             ret = 1;
684         }
685     }
686     return ret == 0;
687 }
688 
689 static int get_physical_addr_region(CPUXtensaState *env,
690                                     uint32_t vaddr, int is_write, int mmu_idx,
691                                     uint32_t *paddr, uint32_t *page_size,
692                                     unsigned *access)
693 {
694     bool dtlb = is_write != 2;
695     uint32_t wi = 0;
696     uint32_t ei = (vaddr >> 29) & 0x7;
697     const xtensa_tlb_entry *entry =
698         xtensa_tlb_get_entry(env, dtlb, wi, ei);
699 
700     *access = region_attr_to_access(entry->attr);
701     if (!is_access_granted(*access, is_write)) {
702         return dtlb ?
703             (is_write ?
704              STORE_PROHIBITED_CAUSE :
705              LOAD_PROHIBITED_CAUSE) :
706             INST_FETCH_PROHIBITED_CAUSE;
707     }
708 
709     *paddr = entry->paddr | (vaddr & ~REGION_PAGE_MASK);
710     *page_size = ~REGION_PAGE_MASK + 1;
711 
712     return 0;
713 }
714 
715 /*!
716  * Convert virtual address to physical addr.
717  * MMU may issue pagewalk and change xtensa autorefill TLB way entry.
718  *
719  * \return 0 if ok, exception cause code otherwise
720  */
721 int xtensa_get_physical_addr(CPUXtensaState *env, bool update_tlb,
722                              uint32_t vaddr, int is_write, int mmu_idx,
723                              uint32_t *paddr, uint32_t *page_size,
724                              unsigned *access)
725 {
726     if (xtensa_option_enabled(env->config, XTENSA_OPTION_MMU)) {
727         return get_physical_addr_mmu(env, update_tlb,
728                                      vaddr, is_write, mmu_idx, paddr,
729                                      page_size, access, true);
730     } else if (xtensa_option_bits_enabled(env->config,
731                 XTENSA_OPTION_BIT(XTENSA_OPTION_REGION_PROTECTION) |
732                 XTENSA_OPTION_BIT(XTENSA_OPTION_REGION_TRANSLATION))) {
733         return get_physical_addr_region(env, vaddr, is_write, mmu_idx,
734                                         paddr, page_size, access);
735     } else {
736         *paddr = vaddr;
737         *page_size = TARGET_PAGE_SIZE;
738         *access = cacheattr_attr_to_access(env->sregs[CACHEATTR] >>
739                                            ((vaddr & 0xe0000000) >> 27));
740         return 0;
741     }
742 }
743 
744 static void dump_tlb(CPUXtensaState *env, bool dtlb)
745 {
746     unsigned wi, ei;
747     const xtensa_tlb *conf =
748         dtlb ? &env->config->dtlb : &env->config->itlb;
749     unsigned (*attr_to_access)(uint32_t) =
750         xtensa_option_enabled(env->config, XTENSA_OPTION_MMU) ?
751         mmu_attr_to_access : region_attr_to_access;
752 
753     for (wi = 0; wi < conf->nways; ++wi) {
754         uint32_t sz = ~xtensa_tlb_get_addr_mask(env, dtlb, wi) + 1;
755         const char *sz_text;
756         bool print_header = true;
757 
758         if (sz >= 0x100000) {
759             sz /= MiB;
760             sz_text = "MB";
761         } else {
762             sz /= KiB;
763             sz_text = "KB";
764         }
765 
766         for (ei = 0; ei < conf->way_size[wi]; ++ei) {
767             const xtensa_tlb_entry *entry =
768                 xtensa_tlb_get_entry(env, dtlb, wi, ei);
769 
770             if (entry->asid) {
771                 static const char * const cache_text[8] = {
772                     [PAGE_CACHE_BYPASS >> PAGE_CACHE_SHIFT] = "Bypass",
773                     [PAGE_CACHE_WT >> PAGE_CACHE_SHIFT] = "WT",
774                     [PAGE_CACHE_WB >> PAGE_CACHE_SHIFT] = "WB",
775                     [PAGE_CACHE_ISOLATE >> PAGE_CACHE_SHIFT] = "Isolate",
776                 };
777                 unsigned access = attr_to_access(entry->attr);
778                 unsigned cache_idx = (access & PAGE_CACHE_MASK) >>
779                     PAGE_CACHE_SHIFT;
780 
781                 if (print_header) {
782                     print_header = false;
783                     qemu_printf("Way %u (%d %s)\n", wi, sz, sz_text);
784                     qemu_printf("\tVaddr       Paddr       ASID  Attr RWX Cache\n"
785                                 "\t----------  ----------  ----  ---- --- -------\n");
786                 }
787                 qemu_printf("\t0x%08x  0x%08x  0x%02x  0x%02x %c%c%c %-7s\n",
788                             entry->vaddr,
789                             entry->paddr,
790                             entry->asid,
791                             entry->attr,
792                             (access & PAGE_READ) ? 'R' : '-',
793                             (access & PAGE_WRITE) ? 'W' : '-',
794                             (access & PAGE_EXEC) ? 'X' : '-',
795                             cache_text[cache_idx] ?
796                             cache_text[cache_idx] : "Invalid");
797             }
798         }
799     }
800 }
801 
802 void dump_mmu(CPUXtensaState *env)
803 {
804     if (xtensa_option_bits_enabled(env->config,
805                 XTENSA_OPTION_BIT(XTENSA_OPTION_REGION_PROTECTION) |
806                 XTENSA_OPTION_BIT(XTENSA_OPTION_REGION_TRANSLATION) |
807                 XTENSA_OPTION_BIT(XTENSA_OPTION_MMU))) {
808 
809         qemu_printf("ITLB:\n");
810         dump_tlb(env, false);
811         qemu_printf("\nDTLB:\n");
812         dump_tlb(env, true);
813     } else {
814         qemu_printf("No TLB for this CPU core\n");
815     }
816 }
817