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