xref: /qemu/target/ppc/mmu-hash32.c (revision 51806b545834e0902dd2d17d1f66c7a2d83422f3)
19d7c3f4aSDavid Gibson /*
29d7c3f4aSDavid Gibson  *  PowerPC MMU, TLB and BAT emulation helpers for QEMU.
39d7c3f4aSDavid Gibson  *
49d7c3f4aSDavid Gibson  *  Copyright (c) 2003-2007 Jocelyn Mayer
59d7c3f4aSDavid Gibson  *  Copyright (c) 2013 David Gibson, IBM Corporation
69d7c3f4aSDavid Gibson  *
79d7c3f4aSDavid Gibson  * This library is free software; you can redistribute it and/or
89d7c3f4aSDavid Gibson  * modify it under the terms of the GNU Lesser General Public
99d7c3f4aSDavid Gibson  * License as published by the Free Software Foundation; either
106bd039cdSChetan Pant  * version 2.1 of the License, or (at your option) any later version.
119d7c3f4aSDavid Gibson  *
129d7c3f4aSDavid Gibson  * This library is distributed in the hope that it will be useful,
139d7c3f4aSDavid Gibson  * but WITHOUT ANY WARRANTY; without even the implied warranty of
149d7c3f4aSDavid Gibson  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
159d7c3f4aSDavid Gibson  * Lesser General Public License for more details.
169d7c3f4aSDavid Gibson  *
179d7c3f4aSDavid Gibson  * You should have received a copy of the GNU Lesser General Public
189d7c3f4aSDavid Gibson  * License along with this library; if not, see <http://www.gnu.org/licenses/>.
199d7c3f4aSDavid Gibson  */
209d7c3f4aSDavid Gibson 
210d75590dSPeter Maydell #include "qemu/osdep.h"
229d7c3f4aSDavid Gibson #include "cpu.h"
2363c91552SPaolo Bonzini #include "exec/exec-all.h"
249d7c3f4aSDavid Gibson #include "sysemu/kvm.h"
259d7c3f4aSDavid Gibson #include "kvm_ppc.h"
26182357dbSRichard Henderson #include "internal.h"
279d7c3f4aSDavid Gibson #include "mmu-hash32.h"
28508127e2SPaolo Bonzini #include "exec/log.h"
299d7c3f4aSDavid Gibson 
30596e3ca8SDavid Gibson /* #define DEBUG_BAT */
319d7c3f4aSDavid Gibson 
3298132796SDavid Gibson #ifdef DEBUG_BATS
3348880da6SPaolo Bonzini #  define LOG_BATS(...) qemu_log_mask(CPU_LOG_MMU, __VA_ARGS__)
3498132796SDavid Gibson #else
3598132796SDavid Gibson #  define LOG_BATS(...) do { } while (0)
3698132796SDavid Gibson #endif
3798132796SDavid Gibson 
385dc68eb0SDavid Gibson struct mmu_ctx_hash32 {
395dc68eb0SDavid Gibson     hwaddr raddr;      /* Real address              */
405dc68eb0SDavid Gibson     int prot;                      /* Protection bits           */
415dc68eb0SDavid Gibson     int key;                       /* Access key                */
425dc68eb0SDavid Gibson };
435dc68eb0SDavid Gibson 
44e01b4445SDavid Gibson static int ppc_hash32_pp_prot(int key, int pp, int nx)
45496272a7SDavid Gibson {
46e01b4445SDavid Gibson     int prot;
47496272a7SDavid Gibson 
48496272a7SDavid Gibson     if (key == 0) {
49496272a7SDavid Gibson         switch (pp) {
50496272a7SDavid Gibson         case 0x0:
51496272a7SDavid Gibson         case 0x1:
52496272a7SDavid Gibson         case 0x2:
53e01b4445SDavid Gibson             prot = PAGE_READ | PAGE_WRITE;
54496272a7SDavid Gibson             break;
55e01b4445SDavid Gibson 
56e01b4445SDavid Gibson         case 0x3:
57e01b4445SDavid Gibson             prot = PAGE_READ;
58e01b4445SDavid Gibson             break;
59e01b4445SDavid Gibson 
60e01b4445SDavid Gibson         default:
61e01b4445SDavid Gibson             abort();
62496272a7SDavid Gibson         }
63496272a7SDavid Gibson     } else {
64496272a7SDavid Gibson         switch (pp) {
65496272a7SDavid Gibson         case 0x0:
66e01b4445SDavid Gibson             prot = 0;
67496272a7SDavid Gibson             break;
68e01b4445SDavid Gibson 
69496272a7SDavid Gibson         case 0x1:
70496272a7SDavid Gibson         case 0x3:
71e01b4445SDavid Gibson             prot = PAGE_READ;
72496272a7SDavid Gibson             break;
73e01b4445SDavid Gibson 
74496272a7SDavid Gibson         case 0x2:
75e01b4445SDavid Gibson             prot = PAGE_READ | PAGE_WRITE;
76496272a7SDavid Gibson             break;
77e01b4445SDavid Gibson 
78e01b4445SDavid Gibson         default:
79e01b4445SDavid Gibson             abort();
80496272a7SDavid Gibson         }
81496272a7SDavid Gibson     }
82496272a7SDavid Gibson     if (nx == 0) {
83e01b4445SDavid Gibson         prot |= PAGE_EXEC;
84496272a7SDavid Gibson     }
85496272a7SDavid Gibson 
86e01b4445SDavid Gibson     return prot;
87496272a7SDavid Gibson }
88496272a7SDavid Gibson 
897ef23068SDavid Gibson static int ppc_hash32_pte_prot(PowerPCCPU *cpu,
90e01b4445SDavid Gibson                                target_ulong sr, ppc_hash_pte32_t pte)
91496272a7SDavid Gibson {
927ef23068SDavid Gibson     CPUPPCState *env = &cpu->env;
93e01b4445SDavid Gibson     unsigned pp, key;
94496272a7SDavid Gibson 
95e01b4445SDavid Gibson     key = !!(msr_pr ? (sr & SR32_KP) : (sr & SR32_KS));
96e01b4445SDavid Gibson     pp = pte.pte1 & HPTE32_R_PP;
97496272a7SDavid Gibson 
98e01b4445SDavid Gibson     return ppc_hash32_pp_prot(key, pp, !!(sr & SR32_NX));
99496272a7SDavid Gibson }
100496272a7SDavid Gibson 
1017ef23068SDavid Gibson static target_ulong hash32_bat_size(PowerPCCPU *cpu,
1029986ed1eSDavid Gibson                                     target_ulong batu, target_ulong batl)
10398132796SDavid Gibson {
1047ef23068SDavid Gibson     CPUPPCState *env = &cpu->env;
1057ef23068SDavid Gibson 
1066fc76aa9SDavid Gibson     if ((msr_pr && !(batu & BATU32_VP))
1076fc76aa9SDavid Gibson         || (!msr_pr && !(batu & BATU32_VS))) {
1086fc76aa9SDavid Gibson         return 0;
109e1d49515SDavid Gibson     }
1106fc76aa9SDavid Gibson 
1116fc76aa9SDavid Gibson     return BATU32_BEPI & ~((batu & BATU32_BL) << 15);
112e1d49515SDavid Gibson }
113e1d49515SDavid Gibson 
1147ef23068SDavid Gibson static int hash32_bat_prot(PowerPCCPU *cpu,
115e1d49515SDavid Gibson                            target_ulong batu, target_ulong batl)
116e1d49515SDavid Gibson {
117e1d49515SDavid Gibson     int pp, prot;
118e1d49515SDavid Gibson 
119e1d49515SDavid Gibson     prot = 0;
1209986ed1eSDavid Gibson     pp = batl & BATL32_PP;
12198132796SDavid Gibson     if (pp != 0) {
12298132796SDavid Gibson         prot = PAGE_READ | PAGE_EXEC;
12398132796SDavid Gibson         if (pp == 0x2) {
12498132796SDavid Gibson             prot |= PAGE_WRITE;
12598132796SDavid Gibson         }
12698132796SDavid Gibson     }
127e1d49515SDavid Gibson     return prot;
12898132796SDavid Gibson }
12998132796SDavid Gibson 
1307ef23068SDavid Gibson static target_ulong hash32_bat_601_size(PowerPCCPU *cpu,
1319986ed1eSDavid Gibson                                 target_ulong batu, target_ulong batl)
13298132796SDavid Gibson {
1336fc76aa9SDavid Gibson     if (!(batl & BATL32_601_V)) {
1346fc76aa9SDavid Gibson         return 0;
1356fc76aa9SDavid Gibson     }
13698132796SDavid Gibson 
1376fc76aa9SDavid Gibson     return BATU32_BEPI & ~((batl & BATL32_601_BL) << 17);
138e1d49515SDavid Gibson }
139e1d49515SDavid Gibson 
1407ef23068SDavid Gibson static int hash32_bat_601_prot(PowerPCCPU *cpu,
141e1d49515SDavid Gibson                                target_ulong batu, target_ulong batl)
142e1d49515SDavid Gibson {
1437ef23068SDavid Gibson     CPUPPCState *env = &cpu->env;
144e1d49515SDavid Gibson     int key, pp;
145e1d49515SDavid Gibson 
1469986ed1eSDavid Gibson     pp = batu & BATU32_601_PP;
14798132796SDavid Gibson     if (msr_pr == 0) {
1489986ed1eSDavid Gibson         key = !!(batu & BATU32_601_KS);
14998132796SDavid Gibson     } else {
1509986ed1eSDavid Gibson         key = !!(batu & BATU32_601_KP);
15198132796SDavid Gibson     }
152e01b4445SDavid Gibson     return ppc_hash32_pp_prot(key, pp, 0);
15398132796SDavid Gibson }
15498132796SDavid Gibson 
15531fa64ecSRichard Henderson static hwaddr ppc_hash32_bat_lookup(PowerPCCPU *cpu, target_ulong ea,
15631fa64ecSRichard Henderson                                     MMUAccessType access_type, int *prot)
15798132796SDavid Gibson {
1587ef23068SDavid Gibson     CPUPPCState *env = &cpu->env;
1599986ed1eSDavid Gibson     target_ulong *BATlt, *BATut;
16031fa64ecSRichard Henderson     bool ifetch = access_type == MMU_INST_FETCH;
161145e52f3SDavid Gibson     int i;
16298132796SDavid Gibson 
16398132796SDavid Gibson     LOG_BATS("%s: %cBAT v " TARGET_FMT_lx "\n", __func__,
16431fa64ecSRichard Henderson              ifetch ? 'I' : 'D', ea);
16531fa64ecSRichard Henderson     if (ifetch) {
16698132796SDavid Gibson         BATlt = env->IBAT[1];
16798132796SDavid Gibson         BATut = env->IBAT[0];
16891cda45bSDavid Gibson     } else {
16998132796SDavid Gibson         BATlt = env->DBAT[1];
17098132796SDavid Gibson         BATut = env->DBAT[0];
17198132796SDavid Gibson     }
17298132796SDavid Gibson     for (i = 0; i < env->nb_BATs; i++) {
1739986ed1eSDavid Gibson         target_ulong batu = BATut[i];
1749986ed1eSDavid Gibson         target_ulong batl = BATlt[i];
1756fc76aa9SDavid Gibson         target_ulong mask;
1769986ed1eSDavid Gibson 
17798132796SDavid Gibson         if (unlikely(env->mmu_model == POWERPC_MMU_601)) {
1787ef23068SDavid Gibson             mask = hash32_bat_601_size(cpu, batu, batl);
17998132796SDavid Gibson         } else {
1807ef23068SDavid Gibson             mask = hash32_bat_size(cpu, batu, batl);
18198132796SDavid Gibson         }
18298132796SDavid Gibson         LOG_BATS("%s: %cBAT%d v " TARGET_FMT_lx " BATu " TARGET_FMT_lx
18398132796SDavid Gibson                  " BATl " TARGET_FMT_lx "\n", __func__,
18431fa64ecSRichard Henderson                  ifetch ? 'I' : 'D', i, ea, batu, batl);
1856fc76aa9SDavid Gibson 
186145e52f3SDavid Gibson         if (mask && ((ea & mask) == (batu & BATU32_BEPI))) {
187145e52f3SDavid Gibson             hwaddr raddr = (batl & mask) | (ea & ~mask);
188145e52f3SDavid Gibson 
189145e52f3SDavid Gibson             if (unlikely(env->mmu_model == POWERPC_MMU_601)) {
1907ef23068SDavid Gibson                 *prot = hash32_bat_601_prot(cpu, batu, batl);
191145e52f3SDavid Gibson             } else {
1927ef23068SDavid Gibson                 *prot = hash32_bat_prot(cpu, batu, batl);
19398132796SDavid Gibson             }
194145e52f3SDavid Gibson 
195145e52f3SDavid Gibson             return raddr & TARGET_PAGE_MASK;
19698132796SDavid Gibson         }
19798132796SDavid Gibson     }
198145e52f3SDavid Gibson 
199145e52f3SDavid Gibson     /* No hit */
20098132796SDavid Gibson #if defined(DEBUG_BATS)
20198132796SDavid Gibson     if (qemu_log_enabled()) {
202145e52f3SDavid Gibson         LOG_BATS("no BAT match for " TARGET_FMT_lx ":\n", ea);
20398132796SDavid Gibson         for (i = 0; i < 4; i++) {
20498132796SDavid Gibson             BATu = &BATut[i];
20598132796SDavid Gibson             BATl = &BATlt[i];
206d5aea6f3SDavid Gibson             BEPIu = *BATu & BATU32_BEPIU;
207d5aea6f3SDavid Gibson             BEPIl = *BATu & BATU32_BEPIL;
20898132796SDavid Gibson             bl = (*BATu & 0x00001FFC) << 15;
20998132796SDavid Gibson             LOG_BATS("%s: %cBAT%d v " TARGET_FMT_lx " BATu " TARGET_FMT_lx
21098132796SDavid Gibson                      " BATl " TARGET_FMT_lx "\n\t" TARGET_FMT_lx " "
21198132796SDavid Gibson                      TARGET_FMT_lx " " TARGET_FMT_lx "\n",
21231fa64ecSRichard Henderson                      __func__, ifetch ? 'I' : 'D', i, ea,
21398132796SDavid Gibson                      *BATu, *BATl, BEPIu, BEPIl, bl);
21498132796SDavid Gibson         }
21598132796SDavid Gibson     }
21698132796SDavid Gibson #endif
217145e52f3SDavid Gibson 
218145e52f3SDavid Gibson     return -1;
21998132796SDavid Gibson }
22098132796SDavid Gibson 
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)
226723ed73aSDavid Gibson {
2277ef23068SDavid Gibson     CPUState *cs = CPU(cpu);
2287ef23068SDavid Gibson     CPUPPCState *env = &cpu->env;
229723ed73aSDavid Gibson     int key = !!(msr_pr ? (sr & SR32_KP) : (sr & SR32_KS));
230723ed73aSDavid Gibson 
231339aaf5bSAntony Pavlov     qemu_log_mask(CPU_LOG_MMU, "direct store...\n");
232723ed73aSDavid Gibson 
233723ed73aSDavid Gibson     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
238723ed73aSDavid Gibson          * to SR[28-31] LA[4-31], bypassing all protection mechanisms.
239723ed73aSDavid Gibson          */
240723ed73aSDavid Gibson         *raddr = ((sr & 0xF) << 28) | (eaddr & 0x0FFFFFFF);
241723ed73aSDavid Gibson         *prot = PAGE_READ | PAGE_WRITE | PAGE_EXEC;
2426c3c873cSRichard Henderson         return true;
243723ed73aSDavid Gibson     }
244723ed73aSDavid Gibson 
24531fa64ecSRichard Henderson     if (access_type == MMU_INST_FETCH) {
246723ed73aSDavid Gibson         /* No code fetch is allowed in direct-store areas */
2476c3c873cSRichard Henderson         if (guest_visible) {
24827103424SAndreas Färber             cs->exception_index = POWERPC_EXCP_ISI;
249caa597bdSDavid Gibson             env->error_code = 0x10000000;
2506c3c873cSRichard Henderson         }
2516c3c873cSRichard Henderson         return false;
252723ed73aSDavid Gibson     }
253723ed73aSDavid Gibson 
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) {
259723ed73aSDavid Gibson     case ACCESS_INT:
260723ed73aSDavid Gibson         /* Integer load/store : only access allowed */
261723ed73aSDavid Gibson         break;
262723ed73aSDavid Gibson     case ACCESS_FLOAT:
263723ed73aSDavid Gibson         /* Floating point load/store */
26427103424SAndreas Färber         cs->exception_index = POWERPC_EXCP_ALIGN;
265caa597bdSDavid Gibson         env->error_code = POWERPC_EXCP_ALIGN_FP;
266caa597bdSDavid Gibson         env->spr[SPR_DAR] = eaddr;
2676c3c873cSRichard Henderson         return false;
268723ed73aSDavid Gibson     case ACCESS_RES:
269723ed73aSDavid Gibson         /* lwarx, ldarx or srwcx. */
270caa597bdSDavid Gibson         env->error_code = 0;
271caa597bdSDavid Gibson         env->spr[SPR_DAR] = eaddr;
27231fa64ecSRichard Henderson         if (access_type == MMU_DATA_STORE) {
273caa597bdSDavid Gibson             env->spr[SPR_DSISR] = 0x06000000;
274caa597bdSDavid Gibson         } else {
275caa597bdSDavid Gibson             env->spr[SPR_DSISR] = 0x04000000;
276caa597bdSDavid Gibson         }
2776c3c873cSRichard Henderson         return false;
278723ed73aSDavid Gibson     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 :-)
284723ed73aSDavid Gibson          */
285723ed73aSDavid Gibson         *raddr = eaddr;
2866c3c873cSRichard Henderson         return true;
287723ed73aSDavid Gibson     case ACCESS_EXT:
288723ed73aSDavid Gibson         /* eciwx or ecowx */
28927103424SAndreas Färber         cs->exception_index = POWERPC_EXCP_DSI;
290caa597bdSDavid Gibson         env->error_code = 0;
291caa597bdSDavid Gibson         env->spr[SPR_DAR] = eaddr;
29231fa64ecSRichard Henderson         if (access_type == MMU_DATA_STORE) {
293caa597bdSDavid Gibson             env->spr[SPR_DSISR] = 0x06100000;
294caa597bdSDavid Gibson         } else {
295caa597bdSDavid Gibson             env->spr[SPR_DSISR] = 0x04100000;
296caa597bdSDavid Gibson         }
2976c3c873cSRichard Henderson         return false;
298723ed73aSDavid Gibson     default:
2996c3c873cSRichard Henderson         cpu_abort(cs, "ERROR: insn should not need address translation\n");
300723ed73aSDavid Gibson     }
3016c3c873cSRichard Henderson 
3026c3c873cSRichard Henderson     *prot = key ? PAGE_READ | PAGE_WRITE : PAGE_READ;
3036c3c873cSRichard Henderson     if (*prot & prot_for_access_type(access_type)) {
304723ed73aSDavid Gibson         *raddr = eaddr;
3056c3c873cSRichard Henderson         return true;
3066c3c873cSRichard Henderson     }
3076c3c873cSRichard Henderson 
3086c3c873cSRichard Henderson     if (guest_visible) {
30927103424SAndreas Färber         cs->exception_index = POWERPC_EXCP_DSI;
310caa597bdSDavid Gibson         env->error_code = 0;
311caa597bdSDavid Gibson         env->spr[SPR_DAR] = eaddr;
31231fa64ecSRichard Henderson         if (access_type == MMU_DATA_STORE) {
313caa597bdSDavid Gibson             env->spr[SPR_DSISR] = 0x0a000000;
314caa597bdSDavid Gibson         } else {
315caa597bdSDavid Gibson             env->spr[SPR_DSISR] = 0x08000000;
316caa597bdSDavid Gibson         }
317723ed73aSDavid Gibson     }
3186c3c873cSRichard Henderson     return false;
319723ed73aSDavid Gibson }
320723ed73aSDavid Gibson 
3217ef23068SDavid Gibson hwaddr get_pteg_offset32(PowerPCCPU *cpu, hwaddr hash)
32259191721SDavid Gibson {
32336778660SDavid Gibson     target_ulong mask = ppc_hash32_hpt_mask(cpu);
3247ef23068SDavid Gibson 
32536778660SDavid Gibson     return (hash * HASH_PTEG_SIZE_32) & mask;
32659191721SDavid Gibson }
32759191721SDavid Gibson 
3287ef23068SDavid Gibson static hwaddr ppc_hash32_pteg_search(PowerPCCPU *cpu, hwaddr pteg_off,
329aea390e4SDavid Gibson                                      bool secondary, target_ulong ptem,
330aea390e4SDavid Gibson                                      ppc_hash_pte32_t *pte)
331aea390e4SDavid Gibson {
332aea390e4SDavid Gibson     hwaddr pte_offset = pteg_off;
333aea390e4SDavid Gibson     target_ulong pte0, pte1;
334aea390e4SDavid Gibson     int i;
335aea390e4SDavid Gibson 
336aea390e4SDavid Gibson     for (i = 0; i < HPTES_PER_GROUP; i++) {
3377ef23068SDavid Gibson         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();
3447ef23068SDavid Gibson         pte1 = ppc_hash32_load_hpte1(cpu, pte_offset);
345aea390e4SDavid Gibson 
346aea390e4SDavid Gibson         if ((pte0 & HPTE32_V_VALID)
347aea390e4SDavid Gibson             && (secondary == !!(pte0 & HPTE32_V_SECONDARY))
348aea390e4SDavid Gibson             && HPTE32_V_COMPARE(pte0, ptem)) {
349aea390e4SDavid Gibson             pte->pte0 = pte0;
350aea390e4SDavid Gibson             pte->pte1 = pte1;
351aea390e4SDavid Gibson             return pte_offset;
352aea390e4SDavid Gibson         }
353aea390e4SDavid Gibson 
354aea390e4SDavid Gibson         pte_offset += HASH_PTE_SIZE_32;
355aea390e4SDavid Gibson     }
356aea390e4SDavid Gibson 
357aea390e4SDavid Gibson     return -1;
358aea390e4SDavid Gibson }
359aea390e4SDavid Gibson 
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 
3787ef23068SDavid Gibson static hwaddr ppc_hash32_htab_lookup(PowerPCCPU *cpu,
3797f3bdc2dSDavid Gibson                                      target_ulong sr, target_ulong eaddr,
3807f3bdc2dSDavid Gibson                                      ppc_hash_pte32_t *pte)
381c69b6151SDavid Gibson {
382aea390e4SDavid Gibson     hwaddr pteg_off, pte_offset;
383a1ff751aSDavid Gibson     hwaddr hash;
384a1ff751aSDavid Gibson     uint32_t vsid, pgidx, ptem;
385c69b6151SDavid Gibson 
386a1ff751aSDavid Gibson     vsid = sr & SR32_VSID;
387a1ff751aSDavid Gibson     pgidx = (eaddr & ~SEGMENT_MASK_256M) >> TARGET_PAGE_BITS;
388a1ff751aSDavid Gibson     hash = vsid ^ pgidx;
389a1ff751aSDavid Gibson     ptem = (vsid << 7) | (pgidx >> 10);
390a1ff751aSDavid Gibson 
391a1ff751aSDavid Gibson     /* Page address translation */
392339aaf5bSAntony Pavlov     qemu_log_mask(CPU_LOG_MMU, "htab_base " TARGET_FMT_plx
393339aaf5bSAntony Pavlov             " htab_mask " TARGET_FMT_plx
394a1ff751aSDavid Gibson             " hash " TARGET_FMT_plx "\n",
39536778660SDavid Gibson             ppc_hash32_hpt_base(cpu), ppc_hash32_hpt_mask(cpu), hash);
396a1ff751aSDavid Gibson 
397a1ff751aSDavid Gibson     /* Primary PTEG lookup */
398339aaf5bSAntony Pavlov     qemu_log_mask(CPU_LOG_MMU, "0 htab=" TARGET_FMT_plx "/" TARGET_FMT_plx
399a1ff751aSDavid Gibson             " vsid=%" PRIx32 " ptem=%" PRIx32
400a1ff751aSDavid Gibson             " hash=" TARGET_FMT_plx "\n",
40136778660SDavid Gibson             ppc_hash32_hpt_base(cpu), ppc_hash32_hpt_mask(cpu),
40236778660SDavid Gibson             vsid, ptem, hash);
4037ef23068SDavid Gibson     pteg_off = get_pteg_offset32(cpu, hash);
4047ef23068SDavid Gibson     pte_offset = ppc_hash32_pteg_search(cpu, pteg_off, 0, ptem, pte);
405a1ff751aSDavid Gibson     if (pte_offset == -1) {
406a1ff751aSDavid Gibson         /* Secondary PTEG lookup */
407339aaf5bSAntony Pavlov         qemu_log_mask(CPU_LOG_MMU, "1 htab=" TARGET_FMT_plx "/" TARGET_FMT_plx
408a1ff751aSDavid Gibson                 " 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);
4117ef23068SDavid Gibson         pteg_off = get_pteg_offset32(cpu, ~hash);
4127ef23068SDavid Gibson         pte_offset = ppc_hash32_pteg_search(cpu, pteg_off, 1, ptem, pte);
413a1ff751aSDavid Gibson     }
414a1ff751aSDavid Gibson 
4157f3bdc2dSDavid Gibson     return pte_offset;
416c69b6151SDavid Gibson }
4170480884fSDavid Gibson 
4186d11d998SDavid Gibson static hwaddr ppc_hash32_pte_raddr(target_ulong sr, ppc_hash_pte32_t pte,
4196d11d998SDavid Gibson                                    target_ulong eaddr)
4206d11d998SDavid Gibson {
42175d5ec89SDavid Gibson     hwaddr rpn = pte.pte1 & HPTE32_R_RPN;
4226d11d998SDavid Gibson     hwaddr mask = ~TARGET_PAGE_MASK;
4236d11d998SDavid Gibson 
4246d11d998SDavid Gibson     return (rpn & ~mask) | (eaddr & mask);
4256d11d998SDavid Gibson }
4266d11d998SDavid Gibson 
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)
4300480884fSDavid Gibson {
431d0e39c5dSAndreas Färber     CPUState *cs = CPU(cpu);
432d0e39c5dSAndreas Färber     CPUPPCState *env = &cpu->env;
433a1ff751aSDavid Gibson     target_ulong sr;
4347f3bdc2dSDavid Gibson     hwaddr pte_offset;
4357f3bdc2dSDavid Gibson     ppc_hash_pte32_t pte;
436caa597bdSDavid Gibson     int prot;
437182357dbSRichard Henderson     int need_prot;
438caa597bdSDavid Gibson     hwaddr raddr;
4390480884fSDavid Gibson 
4406c3c873cSRichard Henderson     /* There are no hash32 large pages. */
4416c3c873cSRichard Henderson     *psizep = TARGET_PAGE_BITS;
4426a980110SDavid Gibson 
44365d61643SDavid Gibson     /* 1. Handle real mode accesses */
44431fa64ecSRichard Henderson     if (access_type == MMU_INST_FETCH ? !msr_ir : !msr_dr) {
44565d61643SDavid Gibson         /* Translation is off */
4466c3c873cSRichard Henderson         *raddrp = eaddr;
4476c3c873cSRichard Henderson         *protp = PAGE_READ | PAGE_WRITE | PAGE_EXEC;
4486c3c873cSRichard Henderson         return true;
44965d61643SDavid Gibson     }
45065d61643SDavid Gibson 
4516c3c873cSRichard Henderson     need_prot = prot_for_access_type(access_type);
4526c3c873cSRichard Henderson 
45365d61643SDavid Gibson     /* 2. Check Block Address Translation entries (BATs) */
45465d61643SDavid Gibson     if (env->nb_BATs != 0) {
4556c3c873cSRichard Henderson         raddr = ppc_hash32_bat_lookup(cpu, eaddr, access_type, protp);
456caa597bdSDavid Gibson         if (raddr != -1) {
4576c3c873cSRichard Henderson             if (need_prot & ~*protp) {
4586c3c873cSRichard Henderson                 if (guest_visible) {
45931fa64ecSRichard Henderson                     if (access_type == MMU_INST_FETCH) {
46027103424SAndreas Färber                         cs->exception_index = POWERPC_EXCP_ISI;
461caa597bdSDavid Gibson                         env->error_code = 0x08000000;
462caa597bdSDavid Gibson                     } else {
46327103424SAndreas Färber                         cs->exception_index = POWERPC_EXCP_DSI;
464caa597bdSDavid Gibson                         env->error_code = 0;
465caa597bdSDavid Gibson                         env->spr[SPR_DAR] = eaddr;
46631fa64ecSRichard Henderson                         if (access_type == MMU_DATA_STORE) {
467caa597bdSDavid Gibson                             env->spr[SPR_DSISR] = 0x0a000000;
468caa597bdSDavid Gibson                         } else {
469caa597bdSDavid Gibson                             env->spr[SPR_DSISR] = 0x08000000;
470e01b4445SDavid Gibson                         }
471caa597bdSDavid Gibson                     }
472caa597bdSDavid Gibson                 }
4736c3c873cSRichard Henderson                 return false;
4746c3c873cSRichard Henderson             }
4756c3c873cSRichard Henderson             *raddrp = raddr;
4766c3c873cSRichard Henderson             return true;
47765d61643SDavid Gibson         }
478145e52f3SDavid Gibson     }
47965d61643SDavid Gibson 
4804b9605a5SDavid Gibson     /* 3. Look up the Segment Register */
4810480884fSDavid Gibson     sr = env->sr[eaddr >> 28];
4824b9605a5SDavid Gibson 
483723ed73aSDavid Gibson     /* 4. Handle direct store segments */
484723ed73aSDavid Gibson     if (sr & SR32_T) {
4856c3c873cSRichard Henderson         return ppc_hash32_direct_store(cpu, sr, eaddr, access_type,
4866c3c873cSRichard Henderson                                        raddrp, protp, guest_visible);
487723ed73aSDavid Gibson     }
488723ed73aSDavid Gibson 
489bb218042SDavid Gibson     /* 5. Check for segment level no-execute violation */
49031fa64ecSRichard Henderson     if (access_type == MMU_INST_FETCH && (sr & SR32_NX)) {
4916c3c873cSRichard Henderson         if (guest_visible) {
49227103424SAndreas Färber             cs->exception_index = POWERPC_EXCP_ISI;
493caa597bdSDavid Gibson             env->error_code = 0x10000000;
4946c3c873cSRichard Henderson         }
4956c3c873cSRichard Henderson         return false;
496bb218042SDavid Gibson     }
4977f3bdc2dSDavid Gibson 
4987f3bdc2dSDavid Gibson     /* 6. Locate the PTE in the hash table */
4997ef23068SDavid Gibson     pte_offset = ppc_hash32_htab_lookup(cpu, sr, eaddr, &pte);
5007f3bdc2dSDavid Gibson     if (pte_offset == -1) {
5016c3c873cSRichard Henderson         if (guest_visible) {
50231fa64ecSRichard Henderson             if (access_type == MMU_INST_FETCH) {
50327103424SAndreas Färber                 cs->exception_index = POWERPC_EXCP_ISI;
504caa597bdSDavid Gibson                 env->error_code = 0x40000000;
505caa597bdSDavid Gibson             } else {
50627103424SAndreas Färber                 cs->exception_index = POWERPC_EXCP_DSI;
507caa597bdSDavid Gibson                 env->error_code = 0;
508caa597bdSDavid Gibson                 env->spr[SPR_DAR] = eaddr;
50931fa64ecSRichard Henderson                 if (access_type == MMU_DATA_STORE) {
510caa597bdSDavid Gibson                     env->spr[SPR_DSISR] = 0x42000000;
511caa597bdSDavid Gibson                 } else {
512caa597bdSDavid Gibson                     env->spr[SPR_DSISR] = 0x40000000;
513caa597bdSDavid Gibson                 }
514caa597bdSDavid Gibson             }
5156c3c873cSRichard Henderson         }
5166c3c873cSRichard Henderson         return false;
5177f3bdc2dSDavid Gibson     }
518339aaf5bSAntony Pavlov     qemu_log_mask(CPU_LOG_MMU,
519339aaf5bSAntony Pavlov                 "found PTE at offset %08" HWADDR_PRIx "\n", pte_offset);
5207f3bdc2dSDavid Gibson 
5217f3bdc2dSDavid Gibson     /* 7. Check access permissions */
5226a980110SDavid Gibson 
5237ef23068SDavid Gibson     prot = ppc_hash32_pte_prot(cpu, sr, pte);
5246a980110SDavid Gibson 
525182357dbSRichard Henderson     if (need_prot & ~prot) {
5266a980110SDavid Gibson         /* Access right violation */
527339aaf5bSAntony Pavlov         qemu_log_mask(CPU_LOG_MMU, "PTE access rejected\n");
5286c3c873cSRichard Henderson         if (guest_visible) {
52931fa64ecSRichard Henderson             if (access_type == MMU_INST_FETCH) {
53027103424SAndreas Färber                 cs->exception_index = POWERPC_EXCP_ISI;
531caa597bdSDavid Gibson                 env->error_code = 0x08000000;
532caa597bdSDavid Gibson             } else {
53327103424SAndreas Färber                 cs->exception_index = POWERPC_EXCP_DSI;
534caa597bdSDavid Gibson                 env->error_code = 0;
535caa597bdSDavid Gibson                 env->spr[SPR_DAR] = eaddr;
53631fa64ecSRichard Henderson                 if (access_type == MMU_DATA_STORE) {
537caa597bdSDavid Gibson                     env->spr[SPR_DSISR] = 0x0a000000;
538caa597bdSDavid Gibson                 } else {
539caa597bdSDavid Gibson                     env->spr[SPR_DSISR] = 0x08000000;
540caa597bdSDavid Gibson                 }
541caa597bdSDavid Gibson             }
5426c3c873cSRichard Henderson         }
5436c3c873cSRichard Henderson         return false;
5446a980110SDavid Gibson     }
5456a980110SDavid Gibson 
546339aaf5bSAntony Pavlov     qemu_log_mask(CPU_LOG_MMU, "PTE access granted !\n");
54787dc3fd1SDavid Gibson 
54887dc3fd1SDavid Gibson     /* 8. Update PTE referenced and changed bits if necessary */
54987dc3fd1SDavid Gibson 
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);
556b3440746SDavid Gibson         } 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              */
561caa597bdSDavid Gibson             prot &= ~PAGE_WRITE;
562b3440746SDavid Gibson         }
5637f3bdc2dSDavid Gibson      }
5640480884fSDavid Gibson 
5656d11d998SDavid Gibson     /* 9. Determine the real address from the PTE */
5666d11d998SDavid Gibson 
5676c3c873cSRichard Henderson     *raddrp = ppc_hash32_pte_raddr(sr, pte, eaddr);
5686c3c873cSRichard Henderson     *protp = prot;
5696c3c873cSRichard Henderson     return true;
5706c3c873cSRichard Henderson }
571