xref: /openbmc/qemu/target/ppc/mmu-hash32.c (revision 51806b545834e0902dd2d17d1f66c7a2d83422f3)
1fcf5ef2aSThomas Huth /*
2fcf5ef2aSThomas Huth  *  PowerPC MMU, TLB and BAT emulation helpers for QEMU.
3fcf5ef2aSThomas Huth  *
4fcf5ef2aSThomas Huth  *  Copyright (c) 2003-2007 Jocelyn Mayer
5fcf5ef2aSThomas Huth  *  Copyright (c) 2013 David Gibson, IBM Corporation
6fcf5ef2aSThomas Huth  *
7fcf5ef2aSThomas Huth  * This library is free software; you can redistribute it and/or
8fcf5ef2aSThomas Huth  * modify it under the terms of the GNU Lesser General Public
9fcf5ef2aSThomas Huth  * License as published by the Free Software Foundation; either
106bd039cdSChetan Pant  * version 2.1 of the License, or (at your option) any later version.
11fcf5ef2aSThomas Huth  *
12fcf5ef2aSThomas Huth  * This library is distributed in the hope that it will be useful,
13fcf5ef2aSThomas Huth  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14fcf5ef2aSThomas Huth  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15fcf5ef2aSThomas Huth  * Lesser General Public License for more details.
16fcf5ef2aSThomas Huth  *
17fcf5ef2aSThomas Huth  * You should have received a copy of the GNU Lesser General Public
18fcf5ef2aSThomas Huth  * License along with this library; if not, see <http://www.gnu.org/licenses/>.
19fcf5ef2aSThomas Huth  */
20fcf5ef2aSThomas Huth 
21fcf5ef2aSThomas Huth #include "qemu/osdep.h"
22fcf5ef2aSThomas Huth #include "cpu.h"
23fcf5ef2aSThomas Huth #include "exec/exec-all.h"
24fcf5ef2aSThomas Huth #include "sysemu/kvm.h"
25fcf5ef2aSThomas Huth #include "kvm_ppc.h"
26182357dbSRichard Henderson #include "internal.h"
27fcf5ef2aSThomas Huth #include "mmu-hash32.h"
28fcf5ef2aSThomas Huth #include "exec/log.h"
29fcf5ef2aSThomas Huth 
30596e3ca8SDavid Gibson /* #define DEBUG_BAT */
31fcf5ef2aSThomas Huth 
32fcf5ef2aSThomas Huth #ifdef DEBUG_BATS
33fcf5ef2aSThomas Huth #  define LOG_BATS(...) qemu_log_mask(CPU_LOG_MMU, __VA_ARGS__)
34fcf5ef2aSThomas Huth #else
35fcf5ef2aSThomas Huth #  define LOG_BATS(...) do { } while (0)
36fcf5ef2aSThomas Huth #endif
37fcf5ef2aSThomas Huth 
38fcf5ef2aSThomas Huth struct mmu_ctx_hash32 {
39fcf5ef2aSThomas Huth     hwaddr raddr;      /* Real address              */
40fcf5ef2aSThomas Huth     int prot;                      /* Protection bits           */
41fcf5ef2aSThomas Huth     int key;                       /* Access key                */
42fcf5ef2aSThomas Huth };
43fcf5ef2aSThomas Huth 
44fcf5ef2aSThomas Huth static int ppc_hash32_pp_prot(int key, int pp, int nx)
45fcf5ef2aSThomas Huth {
46fcf5ef2aSThomas Huth     int prot;
47fcf5ef2aSThomas Huth 
48fcf5ef2aSThomas Huth     if (key == 0) {
49fcf5ef2aSThomas Huth         switch (pp) {
50fcf5ef2aSThomas Huth         case 0x0:
51fcf5ef2aSThomas Huth         case 0x1:
52fcf5ef2aSThomas Huth         case 0x2:
53fcf5ef2aSThomas Huth             prot = PAGE_READ | PAGE_WRITE;
54fcf5ef2aSThomas Huth             break;
55fcf5ef2aSThomas Huth 
56fcf5ef2aSThomas Huth         case 0x3:
57fcf5ef2aSThomas Huth             prot = PAGE_READ;
58fcf5ef2aSThomas Huth             break;
59fcf5ef2aSThomas Huth 
60fcf5ef2aSThomas Huth         default:
61fcf5ef2aSThomas Huth             abort();
62fcf5ef2aSThomas Huth         }
63fcf5ef2aSThomas Huth     } else {
64fcf5ef2aSThomas Huth         switch (pp) {
65fcf5ef2aSThomas Huth         case 0x0:
66fcf5ef2aSThomas Huth             prot = 0;
67fcf5ef2aSThomas Huth             break;
68fcf5ef2aSThomas Huth 
69fcf5ef2aSThomas Huth         case 0x1:
70fcf5ef2aSThomas Huth         case 0x3:
71fcf5ef2aSThomas Huth             prot = PAGE_READ;
72fcf5ef2aSThomas Huth             break;
73fcf5ef2aSThomas Huth 
74fcf5ef2aSThomas Huth         case 0x2:
75fcf5ef2aSThomas Huth             prot = PAGE_READ | PAGE_WRITE;
76fcf5ef2aSThomas Huth             break;
77fcf5ef2aSThomas Huth 
78fcf5ef2aSThomas Huth         default:
79fcf5ef2aSThomas Huth             abort();
80fcf5ef2aSThomas Huth         }
81fcf5ef2aSThomas Huth     }
82fcf5ef2aSThomas Huth     if (nx == 0) {
83fcf5ef2aSThomas Huth         prot |= PAGE_EXEC;
84fcf5ef2aSThomas Huth     }
85fcf5ef2aSThomas Huth 
86fcf5ef2aSThomas Huth     return prot;
87fcf5ef2aSThomas Huth }
88fcf5ef2aSThomas Huth 
89fcf5ef2aSThomas Huth static int ppc_hash32_pte_prot(PowerPCCPU *cpu,
90fcf5ef2aSThomas Huth                                target_ulong sr, ppc_hash_pte32_t pte)
91fcf5ef2aSThomas Huth {
92fcf5ef2aSThomas Huth     CPUPPCState *env = &cpu->env;
93fcf5ef2aSThomas Huth     unsigned pp, key;
94fcf5ef2aSThomas Huth 
95fcf5ef2aSThomas Huth     key = !!(msr_pr ? (sr & SR32_KP) : (sr & SR32_KS));
96fcf5ef2aSThomas Huth     pp = pte.pte1 & HPTE32_R_PP;
97fcf5ef2aSThomas Huth 
98fcf5ef2aSThomas Huth     return ppc_hash32_pp_prot(key, pp, !!(sr & SR32_NX));
99fcf5ef2aSThomas Huth }
100fcf5ef2aSThomas Huth 
101fcf5ef2aSThomas Huth static target_ulong hash32_bat_size(PowerPCCPU *cpu,
102fcf5ef2aSThomas Huth                                     target_ulong batu, target_ulong batl)
103fcf5ef2aSThomas Huth {
104fcf5ef2aSThomas Huth     CPUPPCState *env = &cpu->env;
105fcf5ef2aSThomas Huth 
106fcf5ef2aSThomas Huth     if ((msr_pr && !(batu & BATU32_VP))
107fcf5ef2aSThomas Huth         || (!msr_pr && !(batu & BATU32_VS))) {
108fcf5ef2aSThomas Huth         return 0;
109fcf5ef2aSThomas Huth     }
110fcf5ef2aSThomas Huth 
111fcf5ef2aSThomas Huth     return BATU32_BEPI & ~((batu & BATU32_BL) << 15);
112fcf5ef2aSThomas Huth }
113fcf5ef2aSThomas Huth 
114fcf5ef2aSThomas Huth static int hash32_bat_prot(PowerPCCPU *cpu,
115fcf5ef2aSThomas Huth                            target_ulong batu, target_ulong batl)
116fcf5ef2aSThomas Huth {
117fcf5ef2aSThomas Huth     int pp, prot;
118fcf5ef2aSThomas Huth 
119fcf5ef2aSThomas Huth     prot = 0;
120fcf5ef2aSThomas Huth     pp = batl & BATL32_PP;
121fcf5ef2aSThomas Huth     if (pp != 0) {
122fcf5ef2aSThomas Huth         prot = PAGE_READ | PAGE_EXEC;
123fcf5ef2aSThomas Huth         if (pp == 0x2) {
124fcf5ef2aSThomas Huth             prot |= PAGE_WRITE;
125fcf5ef2aSThomas Huth         }
126fcf5ef2aSThomas Huth     }
127fcf5ef2aSThomas Huth     return prot;
128fcf5ef2aSThomas Huth }
129fcf5ef2aSThomas Huth 
130fcf5ef2aSThomas Huth static target_ulong hash32_bat_601_size(PowerPCCPU *cpu,
131fcf5ef2aSThomas Huth                                 target_ulong batu, target_ulong batl)
132fcf5ef2aSThomas Huth {
133fcf5ef2aSThomas Huth     if (!(batl & BATL32_601_V)) {
134fcf5ef2aSThomas Huth         return 0;
135fcf5ef2aSThomas Huth     }
136fcf5ef2aSThomas Huth 
137fcf5ef2aSThomas Huth     return BATU32_BEPI & ~((batl & BATL32_601_BL) << 17);
138fcf5ef2aSThomas Huth }
139fcf5ef2aSThomas Huth 
140fcf5ef2aSThomas Huth static int hash32_bat_601_prot(PowerPCCPU *cpu,
141fcf5ef2aSThomas Huth                                target_ulong batu, target_ulong batl)
142fcf5ef2aSThomas Huth {
143fcf5ef2aSThomas Huth     CPUPPCState *env = &cpu->env;
144fcf5ef2aSThomas Huth     int key, pp;
145fcf5ef2aSThomas Huth 
146fcf5ef2aSThomas Huth     pp = batu & BATU32_601_PP;
147fcf5ef2aSThomas Huth     if (msr_pr == 0) {
148fcf5ef2aSThomas Huth         key = !!(batu & BATU32_601_KS);
149fcf5ef2aSThomas Huth     } else {
150fcf5ef2aSThomas Huth         key = !!(batu & BATU32_601_KP);
151fcf5ef2aSThomas Huth     }
152fcf5ef2aSThomas Huth     return ppc_hash32_pp_prot(key, pp, 0);
153fcf5ef2aSThomas Huth }
154fcf5ef2aSThomas Huth 
15531fa64ecSRichard Henderson static hwaddr ppc_hash32_bat_lookup(PowerPCCPU *cpu, target_ulong ea,
15631fa64ecSRichard Henderson                                     MMUAccessType access_type, int *prot)
157fcf5ef2aSThomas Huth {
158fcf5ef2aSThomas Huth     CPUPPCState *env = &cpu->env;
159fcf5ef2aSThomas Huth     target_ulong *BATlt, *BATut;
16031fa64ecSRichard Henderson     bool ifetch = access_type == MMU_INST_FETCH;
161fcf5ef2aSThomas Huth     int i;
162fcf5ef2aSThomas Huth 
163fcf5ef2aSThomas Huth     LOG_BATS("%s: %cBAT v " TARGET_FMT_lx "\n", __func__,
16431fa64ecSRichard Henderson              ifetch ? 'I' : 'D', ea);
16531fa64ecSRichard Henderson     if (ifetch) {
166fcf5ef2aSThomas Huth         BATlt = env->IBAT[1];
167fcf5ef2aSThomas Huth         BATut = env->IBAT[0];
168fcf5ef2aSThomas Huth     } else {
169fcf5ef2aSThomas Huth         BATlt = env->DBAT[1];
170fcf5ef2aSThomas Huth         BATut = env->DBAT[0];
171fcf5ef2aSThomas Huth     }
172fcf5ef2aSThomas Huth     for (i = 0; i < env->nb_BATs; i++) {
173fcf5ef2aSThomas Huth         target_ulong batu = BATut[i];
174fcf5ef2aSThomas Huth         target_ulong batl = BATlt[i];
175fcf5ef2aSThomas Huth         target_ulong mask;
176fcf5ef2aSThomas Huth 
177fcf5ef2aSThomas Huth         if (unlikely(env->mmu_model == POWERPC_MMU_601)) {
178fcf5ef2aSThomas Huth             mask = hash32_bat_601_size(cpu, batu, batl);
179fcf5ef2aSThomas Huth         } else {
180fcf5ef2aSThomas Huth             mask = hash32_bat_size(cpu, batu, batl);
181fcf5ef2aSThomas Huth         }
182fcf5ef2aSThomas Huth         LOG_BATS("%s: %cBAT%d v " TARGET_FMT_lx " BATu " TARGET_FMT_lx
183fcf5ef2aSThomas Huth                  " BATl " TARGET_FMT_lx "\n", __func__,
18431fa64ecSRichard Henderson                  ifetch ? 'I' : 'D', i, ea, batu, batl);
185fcf5ef2aSThomas Huth 
186fcf5ef2aSThomas Huth         if (mask && ((ea & mask) == (batu & BATU32_BEPI))) {
187fcf5ef2aSThomas Huth             hwaddr raddr = (batl & mask) | (ea & ~mask);
188fcf5ef2aSThomas Huth 
189fcf5ef2aSThomas Huth             if (unlikely(env->mmu_model == POWERPC_MMU_601)) {
190fcf5ef2aSThomas Huth                 *prot = hash32_bat_601_prot(cpu, batu, batl);
191fcf5ef2aSThomas Huth             } else {
192fcf5ef2aSThomas Huth                 *prot = hash32_bat_prot(cpu, batu, batl);
193fcf5ef2aSThomas Huth             }
194fcf5ef2aSThomas Huth 
195fcf5ef2aSThomas Huth             return raddr & TARGET_PAGE_MASK;
196fcf5ef2aSThomas Huth         }
197fcf5ef2aSThomas Huth     }
198fcf5ef2aSThomas Huth 
199fcf5ef2aSThomas Huth     /* No hit */
200fcf5ef2aSThomas Huth #if defined(DEBUG_BATS)
201fcf5ef2aSThomas Huth     if (qemu_log_enabled()) {
202fcf5ef2aSThomas Huth         LOG_BATS("no BAT match for " TARGET_FMT_lx ":\n", ea);
203fcf5ef2aSThomas Huth         for (i = 0; i < 4; i++) {
204fcf5ef2aSThomas Huth             BATu = &BATut[i];
205fcf5ef2aSThomas Huth             BATl = &BATlt[i];
206fcf5ef2aSThomas Huth             BEPIu = *BATu & BATU32_BEPIU;
207fcf5ef2aSThomas Huth             BEPIl = *BATu & BATU32_BEPIL;
208fcf5ef2aSThomas Huth             bl = (*BATu & 0x00001FFC) << 15;
209fcf5ef2aSThomas Huth             LOG_BATS("%s: %cBAT%d v " TARGET_FMT_lx " BATu " TARGET_FMT_lx
210fcf5ef2aSThomas Huth                      " BATl " TARGET_FMT_lx "\n\t" TARGET_FMT_lx " "
211fcf5ef2aSThomas Huth                      TARGET_FMT_lx " " TARGET_FMT_lx "\n",
21231fa64ecSRichard Henderson                      __func__, ifetch ? 'I' : 'D', i, ea,
213fcf5ef2aSThomas Huth                      *BATu, *BATl, BEPIu, BEPIl, bl);
214fcf5ef2aSThomas Huth         }
215fcf5ef2aSThomas Huth     }
216fcf5ef2aSThomas Huth #endif
217fcf5ef2aSThomas Huth 
218fcf5ef2aSThomas Huth     return -1;
219fcf5ef2aSThomas Huth }
220fcf5ef2aSThomas Huth 
2216c3c873cSRichard Henderson static bool ppc_hash32_direct_store(PowerPCCPU *cpu, target_ulong sr,
22231fa64ecSRichard Henderson                                     target_ulong eaddr,
22331fa64ecSRichard Henderson                                     MMUAccessType access_type,
2246c3c873cSRichard Henderson                                     hwaddr *raddr, int *prot,
2256c3c873cSRichard Henderson                                     bool guest_visible)
226fcf5ef2aSThomas Huth {
227fcf5ef2aSThomas Huth     CPUState *cs = CPU(cpu);
228fcf5ef2aSThomas Huth     CPUPPCState *env = &cpu->env;
229fcf5ef2aSThomas Huth     int key = !!(msr_pr ? (sr & SR32_KP) : (sr & SR32_KS));
230fcf5ef2aSThomas Huth 
231fcf5ef2aSThomas Huth     qemu_log_mask(CPU_LOG_MMU, "direct store...\n");
232fcf5ef2aSThomas Huth 
233fcf5ef2aSThomas Huth     if ((sr & 0x1FF00000) >> 20 == 0x07f) {
234596e3ca8SDavid Gibson         /*
235596e3ca8SDavid Gibson          * Memory-forced I/O controller interface access
236596e3ca8SDavid Gibson          *
237596e3ca8SDavid Gibson          * If T=1 and BUID=x'07F', the 601 performs a memory access
238fcf5ef2aSThomas Huth          * to SR[28-31] LA[4-31], bypassing all protection mechanisms.
239fcf5ef2aSThomas Huth          */
240fcf5ef2aSThomas Huth         *raddr = ((sr & 0xF) << 28) | (eaddr & 0x0FFFFFFF);
241fcf5ef2aSThomas Huth         *prot = PAGE_READ | PAGE_WRITE | PAGE_EXEC;
2426c3c873cSRichard Henderson         return true;
243fcf5ef2aSThomas Huth     }
244fcf5ef2aSThomas Huth 
24531fa64ecSRichard Henderson     if (access_type == MMU_INST_FETCH) {
246fcf5ef2aSThomas Huth         /* No code fetch is allowed in direct-store areas */
2476c3c873cSRichard Henderson         if (guest_visible) {
248fcf5ef2aSThomas Huth             cs->exception_index = POWERPC_EXCP_ISI;
249fcf5ef2aSThomas Huth             env->error_code = 0x10000000;
2506c3c873cSRichard Henderson         }
2516c3c873cSRichard Henderson         return false;
252fcf5ef2aSThomas Huth     }
253fcf5ef2aSThomas Huth 
2546c3c873cSRichard Henderson     /*
2556c3c873cSRichard Henderson      * From ppc_cpu_get_phys_page_debug, env->access_type is not set.
2566c3c873cSRichard Henderson      * Assume ACCESS_INT for that case.
2576c3c873cSRichard Henderson      */
2586c3c873cSRichard Henderson     switch (guest_visible ? env->access_type : ACCESS_INT) {
259fcf5ef2aSThomas Huth     case ACCESS_INT:
260fcf5ef2aSThomas Huth         /* Integer load/store : only access allowed */
261fcf5ef2aSThomas Huth         break;
262fcf5ef2aSThomas Huth     case ACCESS_FLOAT:
263fcf5ef2aSThomas Huth         /* Floating point load/store */
264fcf5ef2aSThomas Huth         cs->exception_index = POWERPC_EXCP_ALIGN;
265fcf5ef2aSThomas Huth         env->error_code = POWERPC_EXCP_ALIGN_FP;
266fcf5ef2aSThomas Huth         env->spr[SPR_DAR] = eaddr;
2676c3c873cSRichard Henderson         return false;
268fcf5ef2aSThomas Huth     case ACCESS_RES:
269fcf5ef2aSThomas Huth         /* lwarx, ldarx or srwcx. */
270fcf5ef2aSThomas Huth         env->error_code = 0;
271fcf5ef2aSThomas Huth         env->spr[SPR_DAR] = eaddr;
27231fa64ecSRichard Henderson         if (access_type == MMU_DATA_STORE) {
273fcf5ef2aSThomas Huth             env->spr[SPR_DSISR] = 0x06000000;
274fcf5ef2aSThomas Huth         } else {
275fcf5ef2aSThomas Huth             env->spr[SPR_DSISR] = 0x04000000;
276fcf5ef2aSThomas Huth         }
2776c3c873cSRichard Henderson         return false;
278fcf5ef2aSThomas Huth     case ACCESS_CACHE:
279596e3ca8SDavid Gibson         /*
280596e3ca8SDavid Gibson          * dcba, dcbt, dcbtst, dcbf, dcbi, dcbst, dcbz, or icbi
281596e3ca8SDavid Gibson          *
282596e3ca8SDavid Gibson          * Should make the instruction do no-op.  As it already do
283596e3ca8SDavid Gibson          * no-op, it's quite easy :-)
284fcf5ef2aSThomas Huth          */
285fcf5ef2aSThomas Huth         *raddr = eaddr;
2866c3c873cSRichard Henderson         return true;
287fcf5ef2aSThomas Huth     case ACCESS_EXT:
288fcf5ef2aSThomas Huth         /* eciwx or ecowx */
289fcf5ef2aSThomas Huth         cs->exception_index = POWERPC_EXCP_DSI;
290fcf5ef2aSThomas Huth         env->error_code = 0;
291fcf5ef2aSThomas Huth         env->spr[SPR_DAR] = eaddr;
29231fa64ecSRichard Henderson         if (access_type == MMU_DATA_STORE) {
293fcf5ef2aSThomas Huth             env->spr[SPR_DSISR] = 0x06100000;
294fcf5ef2aSThomas Huth         } else {
295fcf5ef2aSThomas Huth             env->spr[SPR_DSISR] = 0x04100000;
296fcf5ef2aSThomas Huth         }
2976c3c873cSRichard Henderson         return false;
298fcf5ef2aSThomas Huth     default:
2996c3c873cSRichard Henderson         cpu_abort(cs, "ERROR: insn should not need address translation\n");
300fcf5ef2aSThomas Huth     }
3016c3c873cSRichard Henderson 
3026c3c873cSRichard Henderson     *prot = key ? PAGE_READ | PAGE_WRITE : PAGE_READ;
3036c3c873cSRichard Henderson     if (*prot & prot_for_access_type(access_type)) {
304fcf5ef2aSThomas Huth         *raddr = eaddr;
3056c3c873cSRichard Henderson         return true;
3066c3c873cSRichard Henderson     }
3076c3c873cSRichard Henderson 
3086c3c873cSRichard Henderson     if (guest_visible) {
309fcf5ef2aSThomas Huth         cs->exception_index = POWERPC_EXCP_DSI;
310fcf5ef2aSThomas Huth         env->error_code = 0;
311fcf5ef2aSThomas Huth         env->spr[SPR_DAR] = eaddr;
31231fa64ecSRichard Henderson         if (access_type == MMU_DATA_STORE) {
313fcf5ef2aSThomas Huth             env->spr[SPR_DSISR] = 0x0a000000;
314fcf5ef2aSThomas Huth         } else {
315fcf5ef2aSThomas Huth             env->spr[SPR_DSISR] = 0x08000000;
316fcf5ef2aSThomas Huth         }
317fcf5ef2aSThomas Huth     }
3186c3c873cSRichard Henderson     return false;
319fcf5ef2aSThomas Huth }
320fcf5ef2aSThomas Huth 
321fcf5ef2aSThomas Huth hwaddr get_pteg_offset32(PowerPCCPU *cpu, hwaddr hash)
322fcf5ef2aSThomas Huth {
32336778660SDavid Gibson     target_ulong mask = ppc_hash32_hpt_mask(cpu);
324fcf5ef2aSThomas Huth 
32536778660SDavid Gibson     return (hash * HASH_PTEG_SIZE_32) & mask;
326fcf5ef2aSThomas Huth }
327fcf5ef2aSThomas Huth 
328fcf5ef2aSThomas Huth static hwaddr ppc_hash32_pteg_search(PowerPCCPU *cpu, hwaddr pteg_off,
329fcf5ef2aSThomas Huth                                      bool secondary, target_ulong ptem,
330fcf5ef2aSThomas Huth                                      ppc_hash_pte32_t *pte)
331fcf5ef2aSThomas Huth {
332fcf5ef2aSThomas Huth     hwaddr pte_offset = pteg_off;
333fcf5ef2aSThomas Huth     target_ulong pte0, pte1;
334fcf5ef2aSThomas Huth     int i;
335fcf5ef2aSThomas Huth 
336fcf5ef2aSThomas Huth     for (i = 0; i < HPTES_PER_GROUP; i++) {
337fcf5ef2aSThomas Huth         pte0 = ppc_hash32_load_hpte0(cpu, pte_offset);
3383054b0caSBenjamin Herrenschmidt         /*
3393054b0caSBenjamin Herrenschmidt          * pte0 contains the valid bit and must be read before pte1,
3403054b0caSBenjamin Herrenschmidt          * otherwise we might see an old pte1 with a new valid bit and
3413054b0caSBenjamin Herrenschmidt          * thus an inconsistent hpte value
3423054b0caSBenjamin Herrenschmidt          */
3433054b0caSBenjamin Herrenschmidt         smp_rmb();
344fcf5ef2aSThomas Huth         pte1 = ppc_hash32_load_hpte1(cpu, pte_offset);
345fcf5ef2aSThomas Huth 
346fcf5ef2aSThomas Huth         if ((pte0 & HPTE32_V_VALID)
347fcf5ef2aSThomas Huth             && (secondary == !!(pte0 & HPTE32_V_SECONDARY))
348fcf5ef2aSThomas Huth             && HPTE32_V_COMPARE(pte0, ptem)) {
349fcf5ef2aSThomas Huth             pte->pte0 = pte0;
350fcf5ef2aSThomas Huth             pte->pte1 = pte1;
351fcf5ef2aSThomas Huth             return pte_offset;
352fcf5ef2aSThomas Huth         }
353fcf5ef2aSThomas Huth 
354fcf5ef2aSThomas Huth         pte_offset += HASH_PTE_SIZE_32;
355fcf5ef2aSThomas Huth     }
356fcf5ef2aSThomas Huth 
357fcf5ef2aSThomas Huth     return -1;
358fcf5ef2aSThomas Huth }
359fcf5ef2aSThomas Huth 
3606e8a65abSBenjamin Herrenschmidt static void ppc_hash32_set_r(PowerPCCPU *cpu, hwaddr pte_offset, uint32_t pte1)
3616e8a65abSBenjamin Herrenschmidt {
3626e8a65abSBenjamin Herrenschmidt     target_ulong base = ppc_hash32_hpt_base(cpu);
3636e8a65abSBenjamin Herrenschmidt     hwaddr offset = pte_offset + 6;
3646e8a65abSBenjamin Herrenschmidt 
3656e8a65abSBenjamin Herrenschmidt     /* The HW performs a non-atomic byte update */
3666e8a65abSBenjamin Herrenschmidt     stb_phys(CPU(cpu)->as, base + offset, ((pte1 >> 8) & 0xff) | 0x01);
3676e8a65abSBenjamin Herrenschmidt }
3686e8a65abSBenjamin Herrenschmidt 
3696e8a65abSBenjamin Herrenschmidt static void ppc_hash32_set_c(PowerPCCPU *cpu, hwaddr pte_offset, uint64_t pte1)
3706e8a65abSBenjamin Herrenschmidt {
3716e8a65abSBenjamin Herrenschmidt     target_ulong base = ppc_hash32_hpt_base(cpu);
3726e8a65abSBenjamin Herrenschmidt     hwaddr offset = pte_offset + 7;
3736e8a65abSBenjamin Herrenschmidt 
3746e8a65abSBenjamin Herrenschmidt     /* The HW performs a non-atomic byte update */
3756e8a65abSBenjamin Herrenschmidt     stb_phys(CPU(cpu)->as, base + offset, (pte1 & 0xff) | 0x80);
3766e8a65abSBenjamin Herrenschmidt }
3776e8a65abSBenjamin Herrenschmidt 
378fcf5ef2aSThomas Huth static hwaddr ppc_hash32_htab_lookup(PowerPCCPU *cpu,
379fcf5ef2aSThomas Huth                                      target_ulong sr, target_ulong eaddr,
380fcf5ef2aSThomas Huth                                      ppc_hash_pte32_t *pte)
381fcf5ef2aSThomas Huth {
382fcf5ef2aSThomas Huth     hwaddr pteg_off, pte_offset;
383fcf5ef2aSThomas Huth     hwaddr hash;
384fcf5ef2aSThomas Huth     uint32_t vsid, pgidx, ptem;
385fcf5ef2aSThomas Huth 
386fcf5ef2aSThomas Huth     vsid = sr & SR32_VSID;
387fcf5ef2aSThomas Huth     pgidx = (eaddr & ~SEGMENT_MASK_256M) >> TARGET_PAGE_BITS;
388fcf5ef2aSThomas Huth     hash = vsid ^ pgidx;
389fcf5ef2aSThomas Huth     ptem = (vsid << 7) | (pgidx >> 10);
390fcf5ef2aSThomas Huth 
391fcf5ef2aSThomas Huth     /* Page address translation */
392fcf5ef2aSThomas Huth     qemu_log_mask(CPU_LOG_MMU, "htab_base " TARGET_FMT_plx
393fcf5ef2aSThomas Huth             " htab_mask " TARGET_FMT_plx
394fcf5ef2aSThomas Huth             " hash " TARGET_FMT_plx "\n",
39536778660SDavid Gibson             ppc_hash32_hpt_base(cpu), ppc_hash32_hpt_mask(cpu), hash);
396fcf5ef2aSThomas Huth 
397fcf5ef2aSThomas Huth     /* Primary PTEG lookup */
398fcf5ef2aSThomas Huth     qemu_log_mask(CPU_LOG_MMU, "0 htab=" TARGET_FMT_plx "/" TARGET_FMT_plx
399fcf5ef2aSThomas Huth             " vsid=%" PRIx32 " ptem=%" PRIx32
400fcf5ef2aSThomas Huth             " hash=" TARGET_FMT_plx "\n",
40136778660SDavid Gibson             ppc_hash32_hpt_base(cpu), ppc_hash32_hpt_mask(cpu),
40236778660SDavid Gibson             vsid, ptem, hash);
403fcf5ef2aSThomas Huth     pteg_off = get_pteg_offset32(cpu, hash);
404fcf5ef2aSThomas Huth     pte_offset = ppc_hash32_pteg_search(cpu, pteg_off, 0, ptem, pte);
405fcf5ef2aSThomas Huth     if (pte_offset == -1) {
406fcf5ef2aSThomas Huth         /* Secondary PTEG lookup */
407fcf5ef2aSThomas Huth         qemu_log_mask(CPU_LOG_MMU, "1 htab=" TARGET_FMT_plx "/" TARGET_FMT_plx
408fcf5ef2aSThomas Huth                 " vsid=%" PRIx32 " api=%" PRIx32
40936778660SDavid Gibson                 " hash=" TARGET_FMT_plx "\n", ppc_hash32_hpt_base(cpu),
41036778660SDavid Gibson                 ppc_hash32_hpt_mask(cpu), vsid, ptem, ~hash);
411fcf5ef2aSThomas Huth         pteg_off = get_pteg_offset32(cpu, ~hash);
412fcf5ef2aSThomas Huth         pte_offset = ppc_hash32_pteg_search(cpu, pteg_off, 1, ptem, pte);
413fcf5ef2aSThomas Huth     }
414fcf5ef2aSThomas Huth 
415fcf5ef2aSThomas Huth     return pte_offset;
416fcf5ef2aSThomas Huth }
417fcf5ef2aSThomas Huth 
418fcf5ef2aSThomas Huth static hwaddr ppc_hash32_pte_raddr(target_ulong sr, ppc_hash_pte32_t pte,
419fcf5ef2aSThomas Huth                                    target_ulong eaddr)
420fcf5ef2aSThomas Huth {
421fcf5ef2aSThomas Huth     hwaddr rpn = pte.pte1 & HPTE32_R_RPN;
422fcf5ef2aSThomas Huth     hwaddr mask = ~TARGET_PAGE_MASK;
423fcf5ef2aSThomas Huth 
424fcf5ef2aSThomas Huth     return (rpn & ~mask) | (eaddr & mask);
425fcf5ef2aSThomas Huth }
426fcf5ef2aSThomas Huth 
427*51806b54SRichard Henderson bool ppc_hash32_xlate(PowerPCCPU *cpu, vaddr eaddr, MMUAccessType access_type,
4286c3c873cSRichard Henderson                       hwaddr *raddrp, int *psizep, int *protp,
4296c3c873cSRichard Henderson                       bool guest_visible)
430fcf5ef2aSThomas Huth {
431fcf5ef2aSThomas Huth     CPUState *cs = CPU(cpu);
432fcf5ef2aSThomas Huth     CPUPPCState *env = &cpu->env;
433fcf5ef2aSThomas Huth     target_ulong sr;
434fcf5ef2aSThomas Huth     hwaddr pte_offset;
435fcf5ef2aSThomas Huth     ppc_hash_pte32_t pte;
436fcf5ef2aSThomas Huth     int prot;
437182357dbSRichard Henderson     int need_prot;
438fcf5ef2aSThomas Huth     hwaddr raddr;
439fcf5ef2aSThomas Huth 
4406c3c873cSRichard Henderson     /* There are no hash32 large pages. */
4416c3c873cSRichard Henderson     *psizep = TARGET_PAGE_BITS;
442fcf5ef2aSThomas Huth 
443fcf5ef2aSThomas Huth     /* 1. Handle real mode accesses */
44431fa64ecSRichard Henderson     if (access_type == MMU_INST_FETCH ? !msr_ir : !msr_dr) {
445fcf5ef2aSThomas Huth         /* Translation is off */
4466c3c873cSRichard Henderson         *raddrp = eaddr;
4476c3c873cSRichard Henderson         *protp = PAGE_READ | PAGE_WRITE | PAGE_EXEC;
4486c3c873cSRichard Henderson         return true;
449fcf5ef2aSThomas Huth     }
450fcf5ef2aSThomas Huth 
4516c3c873cSRichard Henderson     need_prot = prot_for_access_type(access_type);
4526c3c873cSRichard Henderson 
453fcf5ef2aSThomas Huth     /* 2. Check Block Address Translation entries (BATs) */
454fcf5ef2aSThomas Huth     if (env->nb_BATs != 0) {
4556c3c873cSRichard Henderson         raddr = ppc_hash32_bat_lookup(cpu, eaddr, access_type, protp);
456fcf5ef2aSThomas Huth         if (raddr != -1) {
4576c3c873cSRichard Henderson             if (need_prot & ~*protp) {
4586c3c873cSRichard Henderson                 if (guest_visible) {
45931fa64ecSRichard Henderson                     if (access_type == MMU_INST_FETCH) {
460fcf5ef2aSThomas Huth                         cs->exception_index = POWERPC_EXCP_ISI;
461fcf5ef2aSThomas Huth                         env->error_code = 0x08000000;
462fcf5ef2aSThomas Huth                     } else {
463fcf5ef2aSThomas Huth                         cs->exception_index = POWERPC_EXCP_DSI;
464fcf5ef2aSThomas Huth                         env->error_code = 0;
465fcf5ef2aSThomas Huth                         env->spr[SPR_DAR] = eaddr;
46631fa64ecSRichard Henderson                         if (access_type == MMU_DATA_STORE) {
467fcf5ef2aSThomas Huth                             env->spr[SPR_DSISR] = 0x0a000000;
468fcf5ef2aSThomas Huth                         } else {
469fcf5ef2aSThomas Huth                             env->spr[SPR_DSISR] = 0x08000000;
470fcf5ef2aSThomas Huth                         }
471fcf5ef2aSThomas Huth                     }
472fcf5ef2aSThomas Huth                 }
4736c3c873cSRichard Henderson                 return false;
4746c3c873cSRichard Henderson             }
4756c3c873cSRichard Henderson             *raddrp = raddr;
4766c3c873cSRichard Henderson             return true;
477fcf5ef2aSThomas Huth         }
478fcf5ef2aSThomas Huth     }
479fcf5ef2aSThomas Huth 
480fcf5ef2aSThomas Huth     /* 3. Look up the Segment Register */
481fcf5ef2aSThomas Huth     sr = env->sr[eaddr >> 28];
482fcf5ef2aSThomas Huth 
483fcf5ef2aSThomas Huth     /* 4. Handle direct store segments */
484fcf5ef2aSThomas Huth     if (sr & SR32_T) {
4856c3c873cSRichard Henderson         return ppc_hash32_direct_store(cpu, sr, eaddr, access_type,
4866c3c873cSRichard Henderson                                        raddrp, protp, guest_visible);
487fcf5ef2aSThomas Huth     }
488fcf5ef2aSThomas Huth 
489fcf5ef2aSThomas Huth     /* 5. Check for segment level no-execute violation */
49031fa64ecSRichard Henderson     if (access_type == MMU_INST_FETCH && (sr & SR32_NX)) {
4916c3c873cSRichard Henderson         if (guest_visible) {
492fcf5ef2aSThomas Huth             cs->exception_index = POWERPC_EXCP_ISI;
493fcf5ef2aSThomas Huth             env->error_code = 0x10000000;
4946c3c873cSRichard Henderson         }
4956c3c873cSRichard Henderson         return false;
496fcf5ef2aSThomas Huth     }
497fcf5ef2aSThomas Huth 
498fcf5ef2aSThomas Huth     /* 6. Locate the PTE in the hash table */
499fcf5ef2aSThomas Huth     pte_offset = ppc_hash32_htab_lookup(cpu, sr, eaddr, &pte);
500fcf5ef2aSThomas Huth     if (pte_offset == -1) {
5016c3c873cSRichard Henderson         if (guest_visible) {
50231fa64ecSRichard Henderson             if (access_type == MMU_INST_FETCH) {
503fcf5ef2aSThomas Huth                 cs->exception_index = POWERPC_EXCP_ISI;
504fcf5ef2aSThomas Huth                 env->error_code = 0x40000000;
505fcf5ef2aSThomas Huth             } else {
506fcf5ef2aSThomas Huth                 cs->exception_index = POWERPC_EXCP_DSI;
507fcf5ef2aSThomas Huth                 env->error_code = 0;
508fcf5ef2aSThomas Huth                 env->spr[SPR_DAR] = eaddr;
50931fa64ecSRichard Henderson                 if (access_type == MMU_DATA_STORE) {
510fcf5ef2aSThomas Huth                     env->spr[SPR_DSISR] = 0x42000000;
511fcf5ef2aSThomas Huth                 } else {
512fcf5ef2aSThomas Huth                     env->spr[SPR_DSISR] = 0x40000000;
513fcf5ef2aSThomas Huth                 }
514fcf5ef2aSThomas Huth             }
5156c3c873cSRichard Henderson         }
5166c3c873cSRichard Henderson         return false;
517fcf5ef2aSThomas Huth     }
518fcf5ef2aSThomas Huth     qemu_log_mask(CPU_LOG_MMU,
519fcf5ef2aSThomas Huth                 "found PTE at offset %08" HWADDR_PRIx "\n", pte_offset);
520fcf5ef2aSThomas Huth 
521fcf5ef2aSThomas Huth     /* 7. Check access permissions */
522fcf5ef2aSThomas Huth 
523fcf5ef2aSThomas Huth     prot = ppc_hash32_pte_prot(cpu, sr, pte);
524fcf5ef2aSThomas Huth 
525182357dbSRichard Henderson     if (need_prot & ~prot) {
526fcf5ef2aSThomas Huth         /* Access right violation */
527fcf5ef2aSThomas Huth         qemu_log_mask(CPU_LOG_MMU, "PTE access rejected\n");
5286c3c873cSRichard Henderson         if (guest_visible) {
52931fa64ecSRichard Henderson             if (access_type == MMU_INST_FETCH) {
530fcf5ef2aSThomas Huth                 cs->exception_index = POWERPC_EXCP_ISI;
531fcf5ef2aSThomas Huth                 env->error_code = 0x08000000;
532fcf5ef2aSThomas Huth             } else {
533fcf5ef2aSThomas Huth                 cs->exception_index = POWERPC_EXCP_DSI;
534fcf5ef2aSThomas Huth                 env->error_code = 0;
535fcf5ef2aSThomas Huth                 env->spr[SPR_DAR] = eaddr;
53631fa64ecSRichard Henderson                 if (access_type == MMU_DATA_STORE) {
537fcf5ef2aSThomas Huth                     env->spr[SPR_DSISR] = 0x0a000000;
538fcf5ef2aSThomas Huth                 } else {
539fcf5ef2aSThomas Huth                     env->spr[SPR_DSISR] = 0x08000000;
540fcf5ef2aSThomas Huth                 }
541fcf5ef2aSThomas Huth             }
5426c3c873cSRichard Henderson         }
5436c3c873cSRichard Henderson         return false;
544fcf5ef2aSThomas Huth     }
545fcf5ef2aSThomas Huth 
546fcf5ef2aSThomas Huth     qemu_log_mask(CPU_LOG_MMU, "PTE access granted !\n");
547fcf5ef2aSThomas Huth 
548fcf5ef2aSThomas Huth     /* 8. Update PTE referenced and changed bits if necessary */
549fcf5ef2aSThomas Huth 
5506e8a65abSBenjamin Herrenschmidt     if (!(pte.pte1 & HPTE32_R_R)) {
5516e8a65abSBenjamin Herrenschmidt         ppc_hash32_set_r(cpu, pte_offset, pte.pte1);
5526e8a65abSBenjamin Herrenschmidt     }
5536e8a65abSBenjamin Herrenschmidt     if (!(pte.pte1 & HPTE32_R_C)) {
55431fa64ecSRichard Henderson         if (access_type == MMU_DATA_STORE) {
5556e8a65abSBenjamin Herrenschmidt             ppc_hash32_set_c(cpu, pte_offset, pte.pte1);
556fcf5ef2aSThomas Huth         } else {
557596e3ca8SDavid Gibson             /*
558596e3ca8SDavid Gibson              * Treat the page as read-only for now, so that a later write
559596e3ca8SDavid Gibson              * will pass through this function again to set the C bit
560596e3ca8SDavid Gibson              */
561fcf5ef2aSThomas Huth             prot &= ~PAGE_WRITE;
562fcf5ef2aSThomas Huth         }
563fcf5ef2aSThomas Huth      }
564fcf5ef2aSThomas Huth 
565fcf5ef2aSThomas Huth     /* 9. Determine the real address from the PTE */
566fcf5ef2aSThomas Huth 
5676c3c873cSRichard Henderson     *raddrp = ppc_hash32_pte_raddr(sr, pte, eaddr);
5686c3c873cSRichard Henderson     *protp = prot;
5696c3c873cSRichard Henderson     return true;
5706c3c873cSRichard Henderson }
571