xref: /openbmc/qemu/target/ppc/mmu-hash32.c (revision 7d87775f)
1 /*
2  *  PowerPC MMU, TLB and BAT emulation helpers for QEMU.
3  *
4  *  Copyright (c) 2003-2007 Jocelyn Mayer
5  *  Copyright (c) 2013 David Gibson, IBM Corporation
6  *
7  * This library is free software; you can redistribute it and/or
8  * modify it under the terms of the GNU Lesser General Public
9  * License as published by the Free Software Foundation; either
10  * version 2.1 of the License, or (at your option) any later version.
11  *
12  * This library is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15  * Lesser General Public License for more details.
16  *
17  * You should have received a copy of the GNU Lesser General Public
18  * License along with this library; if not, see <http://www.gnu.org/licenses/>.
19  */
20 
21 #include "qemu/osdep.h"
22 #include "cpu.h"
23 #include "exec/exec-all.h"
24 #include "exec/page-protection.h"
25 #include "sysemu/kvm.h"
26 #include "kvm_ppc.h"
27 #include "internal.h"
28 #include "mmu-hash32.h"
29 #include "mmu-books.h"
30 #include "exec/log.h"
31 
32 /* #define DEBUG_BATS */
33 
34 #ifdef DEBUG_BATS
35 #  define LOG_BATS(...) qemu_log_mask(CPU_LOG_MMU, __VA_ARGS__)
36 #else
37 #  define LOG_BATS(...) do { } while (0)
38 #endif
39 
40 static target_ulong hash32_bat_size(int mmu_idx,
41                                     target_ulong batu, target_ulong batl)
42 {
43     if ((mmuidx_pr(mmu_idx) && !(batu & BATU32_VP))
44         || (!mmuidx_pr(mmu_idx) && !(batu & BATU32_VS))) {
45         return 0;
46     }
47 
48     return BATU32_BEPI & ~((batu & BATU32_BL) << 15);
49 }
50 
51 static hwaddr ppc_hash32_bat_lookup(PowerPCCPU *cpu, target_ulong ea,
52                                     MMUAccessType access_type, int *prot,
53                                     int mmu_idx)
54 {
55     CPUPPCState *env = &cpu->env;
56     target_ulong *BATlt, *BATut;
57     bool ifetch = access_type == MMU_INST_FETCH;
58     int i;
59 
60     LOG_BATS("%s: %cBAT v " TARGET_FMT_lx "\n", __func__,
61              ifetch ? 'I' : 'D', ea);
62     if (ifetch) {
63         BATlt = env->IBAT[1];
64         BATut = env->IBAT[0];
65     } else {
66         BATlt = env->DBAT[1];
67         BATut = env->DBAT[0];
68     }
69     for (i = 0; i < env->nb_BATs; i++) {
70         target_ulong batu = BATut[i];
71         target_ulong batl = BATlt[i];
72         target_ulong mask;
73 
74         mask = hash32_bat_size(mmu_idx, batu, batl);
75         LOG_BATS("%s: %cBAT%d v " TARGET_FMT_lx " BATu " TARGET_FMT_lx
76                  " BATl " TARGET_FMT_lx "\n", __func__,
77                  ifetch ? 'I' : 'D', i, ea, batu, batl);
78 
79         if (mask && ((ea & mask) == (batu & BATU32_BEPI))) {
80             hwaddr raddr = (batl & mask) | (ea & ~mask);
81 
82             *prot = ppc_hash32_bat_prot(batu, batl);
83 
84             return raddr & TARGET_PAGE_MASK;
85         }
86     }
87 
88     /* No hit */
89 #if defined(DEBUG_BATS)
90     if (qemu_log_enabled()) {
91         target_ulong *BATu, *BATl;
92         target_ulong BEPIl, BEPIu, bl;
93 
94         LOG_BATS("no BAT match for " TARGET_FMT_lx ":\n", ea);
95         for (i = 0; i < 4; i++) {
96             BATu = &BATut[i];
97             BATl = &BATlt[i];
98             BEPIu = *BATu & BATU32_BEPIU;
99             BEPIl = *BATu & BATU32_BEPIL;
100             bl = (*BATu & 0x00001FFC) << 15;
101             LOG_BATS("%s: %cBAT%d v " TARGET_FMT_lx " BATu " TARGET_FMT_lx
102                      " BATl " TARGET_FMT_lx "\n\t" TARGET_FMT_lx " "
103                      TARGET_FMT_lx " " TARGET_FMT_lx "\n",
104                      __func__, ifetch ? 'I' : 'D', i, ea,
105                      *BATu, *BATl, BEPIu, BEPIl, bl);
106         }
107     }
108 #endif
109 
110     return -1;
111 }
112 
113 static bool ppc_hash32_direct_store(PowerPCCPU *cpu, target_ulong sr,
114                                     target_ulong eaddr,
115                                     MMUAccessType access_type,
116                                     hwaddr *raddr, int *prot, int mmu_idx,
117                                     bool guest_visible)
118 {
119     CPUState *cs = CPU(cpu);
120     CPUPPCState *env = &cpu->env;
121 
122     qemu_log_mask(CPU_LOG_MMU, "direct store...\n");
123 
124     if (access_type == MMU_INST_FETCH) {
125         /* No code fetch is allowed in direct-store areas */
126         if (guest_visible) {
127             cs->exception_index = POWERPC_EXCP_ISI;
128             env->error_code = 0x10000000;
129         }
130         return false;
131     }
132 
133     /*
134      * From ppc_cpu_get_phys_page_debug, env->access_type is not set.
135      * Assume ACCESS_INT for that case.
136      */
137     switch (guest_visible ? env->access_type : ACCESS_INT) {
138     case ACCESS_INT:
139         /* Integer load/store : only access allowed */
140         break;
141     case ACCESS_FLOAT:
142         /* Floating point load/store */
143         cs->exception_index = POWERPC_EXCP_ALIGN;
144         env->error_code = POWERPC_EXCP_ALIGN_FP;
145         env->spr[SPR_DAR] = eaddr;
146         return false;
147     case ACCESS_RES:
148         /* lwarx, ldarx or srwcx. */
149         env->error_code = 0;
150         env->spr[SPR_DAR] = eaddr;
151         if (access_type == MMU_DATA_STORE) {
152             env->spr[SPR_DSISR] = 0x06000000;
153         } else {
154             env->spr[SPR_DSISR] = 0x04000000;
155         }
156         return false;
157     case ACCESS_CACHE:
158         /*
159          * dcba, dcbt, dcbtst, dcbf, dcbi, dcbst, dcbz, or icbi
160          *
161          * Should make the instruction do no-op.  As it already do
162          * no-op, it's quite easy :-)
163          */
164         *raddr = eaddr;
165         return true;
166     case ACCESS_EXT:
167         /* eciwx or ecowx */
168         cs->exception_index = POWERPC_EXCP_DSI;
169         env->error_code = 0;
170         env->spr[SPR_DAR] = eaddr;
171         if (access_type == MMU_DATA_STORE) {
172             env->spr[SPR_DSISR] = 0x06100000;
173         } else {
174             env->spr[SPR_DSISR] = 0x04100000;
175         }
176         return false;
177     default:
178         cpu_abort(cs, "ERROR: insn should not need address translation\n");
179     }
180 
181     if (ppc_hash32_key(mmuidx_pr(mmu_idx), sr)) {
182         *prot = PAGE_READ | PAGE_WRITE;
183     } else {
184         *prot = PAGE_READ;
185     }
186     if (check_prot_access_type(*prot, access_type)) {
187         *raddr = eaddr;
188         return true;
189     }
190 
191     if (guest_visible) {
192         cs->exception_index = POWERPC_EXCP_DSI;
193         env->error_code = 0;
194         env->spr[SPR_DAR] = eaddr;
195         if (access_type == MMU_DATA_STORE) {
196             env->spr[SPR_DSISR] = 0x0a000000;
197         } else {
198             env->spr[SPR_DSISR] = 0x08000000;
199         }
200     }
201     return false;
202 }
203 
204 static hwaddr ppc_hash32_pteg_search(PowerPCCPU *cpu, hwaddr pteg_off,
205                                      bool secondary, target_ulong ptem,
206                                      ppc_hash_pte32_t *pte)
207 {
208     hwaddr pte_offset = pteg_off;
209     target_ulong pte0, pte1;
210     int i;
211 
212     for (i = 0; i < HPTES_PER_GROUP; i++) {
213         pte0 = ppc_hash32_load_hpte0(cpu, pte_offset);
214         /*
215          * pte0 contains the valid bit and must be read before pte1,
216          * otherwise we might see an old pte1 with a new valid bit and
217          * thus an inconsistent hpte value
218          */
219         smp_rmb();
220         pte1 = ppc_hash32_load_hpte1(cpu, pte_offset);
221 
222         if ((pte0 & HPTE32_V_VALID)
223             && (secondary == !!(pte0 & HPTE32_V_SECONDARY))
224             && HPTE32_V_COMPARE(pte0, ptem)) {
225             pte->pte0 = pte0;
226             pte->pte1 = pte1;
227             return pte_offset;
228         }
229 
230         pte_offset += HASH_PTE_SIZE_32;
231     }
232 
233     return -1;
234 }
235 
236 static void ppc_hash32_set_r(PowerPCCPU *cpu, hwaddr pte_offset, uint32_t pte1)
237 {
238     target_ulong base = ppc_hash32_hpt_base(cpu);
239     hwaddr offset = pte_offset + 6;
240 
241     /* The HW performs a non-atomic byte update */
242     stb_phys(CPU(cpu)->as, base + offset, ((pte1 >> 8) & 0xff) | 0x01);
243 }
244 
245 static void ppc_hash32_set_c(PowerPCCPU *cpu, hwaddr pte_offset, uint64_t pte1)
246 {
247     target_ulong base = ppc_hash32_hpt_base(cpu);
248     hwaddr offset = pte_offset + 7;
249 
250     /* The HW performs a non-atomic byte update */
251     stb_phys(CPU(cpu)->as, base + offset, (pte1 & 0xff) | 0x80);
252 }
253 
254 static hwaddr ppc_hash32_htab_lookup(PowerPCCPU *cpu,
255                                      target_ulong sr, target_ulong eaddr,
256                                      ppc_hash_pte32_t *pte)
257 {
258     hwaddr pteg_off, pte_offset;
259     hwaddr hash;
260     uint32_t vsid, pgidx, ptem;
261 
262     vsid = sr & SR32_VSID;
263     pgidx = (eaddr & ~SEGMENT_MASK_256M) >> TARGET_PAGE_BITS;
264     hash = vsid ^ pgidx;
265     ptem = (vsid << 7) | (pgidx >> 10);
266 
267     /* Page address translation */
268     qemu_log_mask(CPU_LOG_MMU, "htab_base " HWADDR_FMT_plx
269             " htab_mask " HWADDR_FMT_plx
270             " hash " HWADDR_FMT_plx "\n",
271             ppc_hash32_hpt_base(cpu), ppc_hash32_hpt_mask(cpu), hash);
272 
273     /* Primary PTEG lookup */
274     qemu_log_mask(CPU_LOG_MMU, "0 htab=" HWADDR_FMT_plx "/" HWADDR_FMT_plx
275             " vsid=%" PRIx32 " ptem=%" PRIx32
276             " hash=" HWADDR_FMT_plx "\n",
277             ppc_hash32_hpt_base(cpu), ppc_hash32_hpt_mask(cpu),
278             vsid, ptem, hash);
279     pteg_off = get_pteg_offset32(cpu, hash);
280     pte_offset = ppc_hash32_pteg_search(cpu, pteg_off, 0, ptem, pte);
281     if (pte_offset == -1) {
282         /* Secondary PTEG lookup */
283         qemu_log_mask(CPU_LOG_MMU, "1 htab=" HWADDR_FMT_plx "/" HWADDR_FMT_plx
284                 " vsid=%" PRIx32 " api=%" PRIx32
285                 " hash=" HWADDR_FMT_plx "\n", ppc_hash32_hpt_base(cpu),
286                 ppc_hash32_hpt_mask(cpu), vsid, ptem, ~hash);
287         pteg_off = get_pteg_offset32(cpu, ~hash);
288         pte_offset = ppc_hash32_pteg_search(cpu, pteg_off, 1, ptem, pte);
289     }
290 
291     return pte_offset;
292 }
293 
294 bool ppc_hash32_xlate(PowerPCCPU *cpu, vaddr eaddr, MMUAccessType access_type,
295                       hwaddr *raddrp, int *psizep, int *protp, int mmu_idx,
296                       bool guest_visible)
297 {
298     CPUState *cs = CPU(cpu);
299     CPUPPCState *env = &cpu->env;
300     target_ulong sr;
301     hwaddr pte_offset, raddr;
302     ppc_hash_pte32_t pte;
303     bool key;
304     int prot;
305 
306     /* There are no hash32 large pages. */
307     *psizep = TARGET_PAGE_BITS;
308 
309     /* 1. Handle real mode accesses */
310     if (mmuidx_real(mmu_idx)) {
311         /* Translation is off */
312         *raddrp = eaddr;
313         *protp = PAGE_READ | PAGE_WRITE | PAGE_EXEC;
314         return true;
315     }
316 
317     /* 2. Check Block Address Translation entries (BATs) */
318     if (env->nb_BATs != 0) {
319         raddr = ppc_hash32_bat_lookup(cpu, eaddr, access_type, protp, mmu_idx);
320         if (raddr != -1) {
321             if (!check_prot_access_type(*protp, access_type)) {
322                 if (guest_visible) {
323                     if (access_type == MMU_INST_FETCH) {
324                         cs->exception_index = POWERPC_EXCP_ISI;
325                         env->error_code = 0x08000000;
326                     } else {
327                         cs->exception_index = POWERPC_EXCP_DSI;
328                         env->error_code = 0;
329                         env->spr[SPR_DAR] = eaddr;
330                         if (access_type == MMU_DATA_STORE) {
331                             env->spr[SPR_DSISR] = 0x0a000000;
332                         } else {
333                             env->spr[SPR_DSISR] = 0x08000000;
334                         }
335                     }
336                 }
337                 return false;
338             }
339             *raddrp = raddr;
340             return true;
341         }
342     }
343 
344     /* 3. Look up the Segment Register */
345     sr = env->sr[eaddr >> 28];
346 
347     /* 4. Handle direct store segments */
348     if (sr & SR32_T) {
349         return ppc_hash32_direct_store(cpu, sr, eaddr, access_type,
350                                        raddrp, protp, mmu_idx, guest_visible);
351     }
352 
353     /* 5. Check for segment level no-execute violation */
354     if (access_type == MMU_INST_FETCH && (sr & SR32_NX)) {
355         if (guest_visible) {
356             cs->exception_index = POWERPC_EXCP_ISI;
357             env->error_code = 0x10000000;
358         }
359         return false;
360     }
361 
362     /* 6. Locate the PTE in the hash table */
363     pte_offset = ppc_hash32_htab_lookup(cpu, sr, eaddr, &pte);
364     if (pte_offset == -1) {
365         if (guest_visible) {
366             if (access_type == MMU_INST_FETCH) {
367                 cs->exception_index = POWERPC_EXCP_ISI;
368                 env->error_code = 0x40000000;
369             } else {
370                 cs->exception_index = POWERPC_EXCP_DSI;
371                 env->error_code = 0;
372                 env->spr[SPR_DAR] = eaddr;
373                 if (access_type == MMU_DATA_STORE) {
374                     env->spr[SPR_DSISR] = 0x42000000;
375                 } else {
376                     env->spr[SPR_DSISR] = 0x40000000;
377                 }
378             }
379         }
380         return false;
381     }
382     qemu_log_mask(CPU_LOG_MMU,
383                 "found PTE at offset %08" HWADDR_PRIx "\n", pte_offset);
384 
385     /* 7. Check access permissions */
386     key = ppc_hash32_key(mmuidx_pr(mmu_idx), sr);
387     prot = ppc_hash32_prot(key, pte.pte1 & HPTE32_R_PP, sr & SR32_NX);
388 
389     if (!check_prot_access_type(prot, access_type)) {
390         /* Access right violation */
391         qemu_log_mask(CPU_LOG_MMU, "PTE access rejected\n");
392         if (guest_visible) {
393             if (access_type == MMU_INST_FETCH) {
394                 cs->exception_index = POWERPC_EXCP_ISI;
395                 env->error_code = 0x08000000;
396             } else {
397                 cs->exception_index = POWERPC_EXCP_DSI;
398                 env->error_code = 0;
399                 env->spr[SPR_DAR] = eaddr;
400                 if (access_type == MMU_DATA_STORE) {
401                     env->spr[SPR_DSISR] = 0x0a000000;
402                 } else {
403                     env->spr[SPR_DSISR] = 0x08000000;
404                 }
405             }
406         }
407         return false;
408     }
409 
410     qemu_log_mask(CPU_LOG_MMU, "PTE access granted !\n");
411 
412     /* 8. Update PTE referenced and changed bits if necessary */
413 
414     if (!(pte.pte1 & HPTE32_R_R)) {
415         ppc_hash32_set_r(cpu, pte_offset, pte.pte1);
416     }
417     if (!(pte.pte1 & HPTE32_R_C)) {
418         if (access_type == MMU_DATA_STORE) {
419             ppc_hash32_set_c(cpu, pte_offset, pte.pte1);
420         } else {
421             /*
422              * Treat the page as read-only for now, so that a later write
423              * will pass through this function again to set the C bit
424              */
425             prot &= ~PAGE_WRITE;
426         }
427     }
428     *protp = prot;
429 
430     /* 9. Determine the real address from the PTE */
431     *raddrp = pte.pte1 & HPTE32_R_RPN;
432     *raddrp &= TARGET_PAGE_MASK;
433     *raddrp |= eaddr & ~TARGET_PAGE_MASK;
434     return true;
435 }
436