xref: /qemu/target/ppc/mmu-hash32.c (revision ba1b5df070bb4cf1632aaefa4e17d42881d49988)
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 
30*ba1b5df0SFabiano Rosas /* #define DEBUG_BATS */
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()) {
202*ba1b5df0SFabiano Rosas         target_ulong *BATu, *BATl;
203*ba1b5df0SFabiano Rosas         target_ulong BEPIl, BEPIu, bl;
204*ba1b5df0SFabiano Rosas 
205145e52f3SDavid Gibson         LOG_BATS("no BAT match for " TARGET_FMT_lx ":\n", ea);
20698132796SDavid Gibson         for (i = 0; i < 4; i++) {
20798132796SDavid Gibson             BATu = &BATut[i];
20898132796SDavid Gibson             BATl = &BATlt[i];
209d5aea6f3SDavid Gibson             BEPIu = *BATu & BATU32_BEPIU;
210d5aea6f3SDavid Gibson             BEPIl = *BATu & BATU32_BEPIL;
21198132796SDavid Gibson             bl = (*BATu & 0x00001FFC) << 15;
21298132796SDavid Gibson             LOG_BATS("%s: %cBAT%d v " TARGET_FMT_lx " BATu " TARGET_FMT_lx
21398132796SDavid Gibson                      " BATl " TARGET_FMT_lx "\n\t" TARGET_FMT_lx " "
21498132796SDavid Gibson                      TARGET_FMT_lx " " TARGET_FMT_lx "\n",
21531fa64ecSRichard Henderson                      __func__, ifetch ? 'I' : 'D', i, ea,
21698132796SDavid Gibson                      *BATu, *BATl, BEPIu, BEPIl, bl);
21798132796SDavid Gibson         }
21898132796SDavid Gibson     }
21998132796SDavid Gibson #endif
220145e52f3SDavid Gibson 
221145e52f3SDavid Gibson     return -1;
22298132796SDavid Gibson }
22398132796SDavid Gibson 
2246c3c873cSRichard Henderson static bool ppc_hash32_direct_store(PowerPCCPU *cpu, target_ulong sr,
22531fa64ecSRichard Henderson                                     target_ulong eaddr,
22631fa64ecSRichard Henderson                                     MMUAccessType access_type,
2276c3c873cSRichard Henderson                                     hwaddr *raddr, int *prot,
2286c3c873cSRichard Henderson                                     bool guest_visible)
229723ed73aSDavid Gibson {
2307ef23068SDavid Gibson     CPUState *cs = CPU(cpu);
2317ef23068SDavid Gibson     CPUPPCState *env = &cpu->env;
232723ed73aSDavid Gibson     int key = !!(msr_pr ? (sr & SR32_KP) : (sr & SR32_KS));
233723ed73aSDavid Gibson 
234339aaf5bSAntony Pavlov     qemu_log_mask(CPU_LOG_MMU, "direct store...\n");
235723ed73aSDavid Gibson 
236723ed73aSDavid Gibson     if ((sr & 0x1FF00000) >> 20 == 0x07f) {
237596e3ca8SDavid Gibson         /*
238596e3ca8SDavid Gibson          * Memory-forced I/O controller interface access
239596e3ca8SDavid Gibson          *
240596e3ca8SDavid Gibson          * If T=1 and BUID=x'07F', the 601 performs a memory access
241723ed73aSDavid Gibson          * to SR[28-31] LA[4-31], bypassing all protection mechanisms.
242723ed73aSDavid Gibson          */
243723ed73aSDavid Gibson         *raddr = ((sr & 0xF) << 28) | (eaddr & 0x0FFFFFFF);
244723ed73aSDavid Gibson         *prot = PAGE_READ | PAGE_WRITE | PAGE_EXEC;
2456c3c873cSRichard Henderson         return true;
246723ed73aSDavid Gibson     }
247723ed73aSDavid Gibson 
24831fa64ecSRichard Henderson     if (access_type == MMU_INST_FETCH) {
249723ed73aSDavid Gibson         /* No code fetch is allowed in direct-store areas */
2506c3c873cSRichard Henderson         if (guest_visible) {
25127103424SAndreas Färber             cs->exception_index = POWERPC_EXCP_ISI;
252caa597bdSDavid Gibson             env->error_code = 0x10000000;
2536c3c873cSRichard Henderson         }
2546c3c873cSRichard Henderson         return false;
255723ed73aSDavid Gibson     }
256723ed73aSDavid Gibson 
2576c3c873cSRichard Henderson     /*
2586c3c873cSRichard Henderson      * From ppc_cpu_get_phys_page_debug, env->access_type is not set.
2596c3c873cSRichard Henderson      * Assume ACCESS_INT for that case.
2606c3c873cSRichard Henderson      */
2616c3c873cSRichard Henderson     switch (guest_visible ? env->access_type : ACCESS_INT) {
262723ed73aSDavid Gibson     case ACCESS_INT:
263723ed73aSDavid Gibson         /* Integer load/store : only access allowed */
264723ed73aSDavid Gibson         break;
265723ed73aSDavid Gibson     case ACCESS_FLOAT:
266723ed73aSDavid Gibson         /* Floating point load/store */
26727103424SAndreas Färber         cs->exception_index = POWERPC_EXCP_ALIGN;
268caa597bdSDavid Gibson         env->error_code = POWERPC_EXCP_ALIGN_FP;
269caa597bdSDavid Gibson         env->spr[SPR_DAR] = eaddr;
2706c3c873cSRichard Henderson         return false;
271723ed73aSDavid Gibson     case ACCESS_RES:
272723ed73aSDavid Gibson         /* lwarx, ldarx or srwcx. */
273caa597bdSDavid Gibson         env->error_code = 0;
274caa597bdSDavid Gibson         env->spr[SPR_DAR] = eaddr;
27531fa64ecSRichard Henderson         if (access_type == MMU_DATA_STORE) {
276caa597bdSDavid Gibson             env->spr[SPR_DSISR] = 0x06000000;
277caa597bdSDavid Gibson         } else {
278caa597bdSDavid Gibson             env->spr[SPR_DSISR] = 0x04000000;
279caa597bdSDavid Gibson         }
2806c3c873cSRichard Henderson         return false;
281723ed73aSDavid Gibson     case ACCESS_CACHE:
282596e3ca8SDavid Gibson         /*
283596e3ca8SDavid Gibson          * dcba, dcbt, dcbtst, dcbf, dcbi, dcbst, dcbz, or icbi
284596e3ca8SDavid Gibson          *
285596e3ca8SDavid Gibson          * Should make the instruction do no-op.  As it already do
286596e3ca8SDavid Gibson          * no-op, it's quite easy :-)
287723ed73aSDavid Gibson          */
288723ed73aSDavid Gibson         *raddr = eaddr;
2896c3c873cSRichard Henderson         return true;
290723ed73aSDavid Gibson     case ACCESS_EXT:
291723ed73aSDavid Gibson         /* eciwx or ecowx */
29227103424SAndreas Färber         cs->exception_index = POWERPC_EXCP_DSI;
293caa597bdSDavid Gibson         env->error_code = 0;
294caa597bdSDavid Gibson         env->spr[SPR_DAR] = eaddr;
29531fa64ecSRichard Henderson         if (access_type == MMU_DATA_STORE) {
296caa597bdSDavid Gibson             env->spr[SPR_DSISR] = 0x06100000;
297caa597bdSDavid Gibson         } else {
298caa597bdSDavid Gibson             env->spr[SPR_DSISR] = 0x04100000;
299caa597bdSDavid Gibson         }
3006c3c873cSRichard Henderson         return false;
301723ed73aSDavid Gibson     default:
3026c3c873cSRichard Henderson         cpu_abort(cs, "ERROR: insn should not need address translation\n");
303723ed73aSDavid Gibson     }
3046c3c873cSRichard Henderson 
3056c3c873cSRichard Henderson     *prot = key ? PAGE_READ | PAGE_WRITE : PAGE_READ;
3066c3c873cSRichard Henderson     if (*prot & prot_for_access_type(access_type)) {
307723ed73aSDavid Gibson         *raddr = eaddr;
3086c3c873cSRichard Henderson         return true;
3096c3c873cSRichard Henderson     }
3106c3c873cSRichard Henderson 
3116c3c873cSRichard Henderson     if (guest_visible) {
31227103424SAndreas Färber         cs->exception_index = POWERPC_EXCP_DSI;
313caa597bdSDavid Gibson         env->error_code = 0;
314caa597bdSDavid Gibson         env->spr[SPR_DAR] = eaddr;
31531fa64ecSRichard Henderson         if (access_type == MMU_DATA_STORE) {
316caa597bdSDavid Gibson             env->spr[SPR_DSISR] = 0x0a000000;
317caa597bdSDavid Gibson         } else {
318caa597bdSDavid Gibson             env->spr[SPR_DSISR] = 0x08000000;
319caa597bdSDavid Gibson         }
320723ed73aSDavid Gibson     }
3216c3c873cSRichard Henderson     return false;
322723ed73aSDavid Gibson }
323723ed73aSDavid Gibson 
3247ef23068SDavid Gibson hwaddr get_pteg_offset32(PowerPCCPU *cpu, hwaddr hash)
32559191721SDavid Gibson {
32636778660SDavid Gibson     target_ulong mask = ppc_hash32_hpt_mask(cpu);
3277ef23068SDavid Gibson 
32836778660SDavid Gibson     return (hash * HASH_PTEG_SIZE_32) & mask;
32959191721SDavid Gibson }
33059191721SDavid Gibson 
3317ef23068SDavid Gibson static hwaddr ppc_hash32_pteg_search(PowerPCCPU *cpu, hwaddr pteg_off,
332aea390e4SDavid Gibson                                      bool secondary, target_ulong ptem,
333aea390e4SDavid Gibson                                      ppc_hash_pte32_t *pte)
334aea390e4SDavid Gibson {
335aea390e4SDavid Gibson     hwaddr pte_offset = pteg_off;
336aea390e4SDavid Gibson     target_ulong pte0, pte1;
337aea390e4SDavid Gibson     int i;
338aea390e4SDavid Gibson 
339aea390e4SDavid Gibson     for (i = 0; i < HPTES_PER_GROUP; i++) {
3407ef23068SDavid Gibson         pte0 = ppc_hash32_load_hpte0(cpu, pte_offset);
3413054b0caSBenjamin Herrenschmidt         /*
3423054b0caSBenjamin Herrenschmidt          * pte0 contains the valid bit and must be read before pte1,
3433054b0caSBenjamin Herrenschmidt          * otherwise we might see an old pte1 with a new valid bit and
3443054b0caSBenjamin Herrenschmidt          * thus an inconsistent hpte value
3453054b0caSBenjamin Herrenschmidt          */
3463054b0caSBenjamin Herrenschmidt         smp_rmb();
3477ef23068SDavid Gibson         pte1 = ppc_hash32_load_hpte1(cpu, pte_offset);
348aea390e4SDavid Gibson 
349aea390e4SDavid Gibson         if ((pte0 & HPTE32_V_VALID)
350aea390e4SDavid Gibson             && (secondary == !!(pte0 & HPTE32_V_SECONDARY))
351aea390e4SDavid Gibson             && HPTE32_V_COMPARE(pte0, ptem)) {
352aea390e4SDavid Gibson             pte->pte0 = pte0;
353aea390e4SDavid Gibson             pte->pte1 = pte1;
354aea390e4SDavid Gibson             return pte_offset;
355aea390e4SDavid Gibson         }
356aea390e4SDavid Gibson 
357aea390e4SDavid Gibson         pte_offset += HASH_PTE_SIZE_32;
358aea390e4SDavid Gibson     }
359aea390e4SDavid Gibson 
360aea390e4SDavid Gibson     return -1;
361aea390e4SDavid Gibson }
362aea390e4SDavid Gibson 
3636e8a65abSBenjamin Herrenschmidt static void ppc_hash32_set_r(PowerPCCPU *cpu, hwaddr pte_offset, uint32_t pte1)
3646e8a65abSBenjamin Herrenschmidt {
3656e8a65abSBenjamin Herrenschmidt     target_ulong base = ppc_hash32_hpt_base(cpu);
3666e8a65abSBenjamin Herrenschmidt     hwaddr offset = pte_offset + 6;
3676e8a65abSBenjamin Herrenschmidt 
3686e8a65abSBenjamin Herrenschmidt     /* The HW performs a non-atomic byte update */
3696e8a65abSBenjamin Herrenschmidt     stb_phys(CPU(cpu)->as, base + offset, ((pte1 >> 8) & 0xff) | 0x01);
3706e8a65abSBenjamin Herrenschmidt }
3716e8a65abSBenjamin Herrenschmidt 
3726e8a65abSBenjamin Herrenschmidt static void ppc_hash32_set_c(PowerPCCPU *cpu, hwaddr pte_offset, uint64_t pte1)
3736e8a65abSBenjamin Herrenschmidt {
3746e8a65abSBenjamin Herrenschmidt     target_ulong base = ppc_hash32_hpt_base(cpu);
3756e8a65abSBenjamin Herrenschmidt     hwaddr offset = pte_offset + 7;
3766e8a65abSBenjamin Herrenschmidt 
3776e8a65abSBenjamin Herrenschmidt     /* The HW performs a non-atomic byte update */
3786e8a65abSBenjamin Herrenschmidt     stb_phys(CPU(cpu)->as, base + offset, (pte1 & 0xff) | 0x80);
3796e8a65abSBenjamin Herrenschmidt }
3806e8a65abSBenjamin Herrenschmidt 
3817ef23068SDavid Gibson static hwaddr ppc_hash32_htab_lookup(PowerPCCPU *cpu,
3827f3bdc2dSDavid Gibson                                      target_ulong sr, target_ulong eaddr,
3837f3bdc2dSDavid Gibson                                      ppc_hash_pte32_t *pte)
384c69b6151SDavid Gibson {
385aea390e4SDavid Gibson     hwaddr pteg_off, pte_offset;
386a1ff751aSDavid Gibson     hwaddr hash;
387a1ff751aSDavid Gibson     uint32_t vsid, pgidx, ptem;
388c69b6151SDavid Gibson 
389a1ff751aSDavid Gibson     vsid = sr & SR32_VSID;
390a1ff751aSDavid Gibson     pgidx = (eaddr & ~SEGMENT_MASK_256M) >> TARGET_PAGE_BITS;
391a1ff751aSDavid Gibson     hash = vsid ^ pgidx;
392a1ff751aSDavid Gibson     ptem = (vsid << 7) | (pgidx >> 10);
393a1ff751aSDavid Gibson 
394a1ff751aSDavid Gibson     /* Page address translation */
395339aaf5bSAntony Pavlov     qemu_log_mask(CPU_LOG_MMU, "htab_base " TARGET_FMT_plx
396339aaf5bSAntony Pavlov             " htab_mask " TARGET_FMT_plx
397a1ff751aSDavid Gibson             " hash " TARGET_FMT_plx "\n",
39836778660SDavid Gibson             ppc_hash32_hpt_base(cpu), ppc_hash32_hpt_mask(cpu), hash);
399a1ff751aSDavid Gibson 
400a1ff751aSDavid Gibson     /* Primary PTEG lookup */
401339aaf5bSAntony Pavlov     qemu_log_mask(CPU_LOG_MMU, "0 htab=" TARGET_FMT_plx "/" TARGET_FMT_plx
402a1ff751aSDavid Gibson             " vsid=%" PRIx32 " ptem=%" PRIx32
403a1ff751aSDavid Gibson             " hash=" TARGET_FMT_plx "\n",
40436778660SDavid Gibson             ppc_hash32_hpt_base(cpu), ppc_hash32_hpt_mask(cpu),
40536778660SDavid Gibson             vsid, ptem, hash);
4067ef23068SDavid Gibson     pteg_off = get_pteg_offset32(cpu, hash);
4077ef23068SDavid Gibson     pte_offset = ppc_hash32_pteg_search(cpu, pteg_off, 0, ptem, pte);
408a1ff751aSDavid Gibson     if (pte_offset == -1) {
409a1ff751aSDavid Gibson         /* Secondary PTEG lookup */
410339aaf5bSAntony Pavlov         qemu_log_mask(CPU_LOG_MMU, "1 htab=" TARGET_FMT_plx "/" TARGET_FMT_plx
411a1ff751aSDavid Gibson                 " vsid=%" PRIx32 " api=%" PRIx32
41236778660SDavid Gibson                 " hash=" TARGET_FMT_plx "\n", ppc_hash32_hpt_base(cpu),
41336778660SDavid Gibson                 ppc_hash32_hpt_mask(cpu), vsid, ptem, ~hash);
4147ef23068SDavid Gibson         pteg_off = get_pteg_offset32(cpu, ~hash);
4157ef23068SDavid Gibson         pte_offset = ppc_hash32_pteg_search(cpu, pteg_off, 1, ptem, pte);
416a1ff751aSDavid Gibson     }
417a1ff751aSDavid Gibson 
4187f3bdc2dSDavid Gibson     return pte_offset;
419c69b6151SDavid Gibson }
4200480884fSDavid Gibson 
4216d11d998SDavid Gibson static hwaddr ppc_hash32_pte_raddr(target_ulong sr, ppc_hash_pte32_t pte,
4226d11d998SDavid Gibson                                    target_ulong eaddr)
4236d11d998SDavid Gibson {
42475d5ec89SDavid Gibson     hwaddr rpn = pte.pte1 & HPTE32_R_RPN;
4256d11d998SDavid Gibson     hwaddr mask = ~TARGET_PAGE_MASK;
4266d11d998SDavid Gibson 
4276d11d998SDavid Gibson     return (rpn & ~mask) | (eaddr & mask);
4286d11d998SDavid Gibson }
4296d11d998SDavid Gibson 
43051806b54SRichard Henderson bool ppc_hash32_xlate(PowerPCCPU *cpu, vaddr eaddr, MMUAccessType access_type,
4316c3c873cSRichard Henderson                       hwaddr *raddrp, int *psizep, int *protp,
4326c3c873cSRichard Henderson                       bool guest_visible)
4330480884fSDavid Gibson {
434d0e39c5dSAndreas Färber     CPUState *cs = CPU(cpu);
435d0e39c5dSAndreas Färber     CPUPPCState *env = &cpu->env;
436a1ff751aSDavid Gibson     target_ulong sr;
4377f3bdc2dSDavid Gibson     hwaddr pte_offset;
4387f3bdc2dSDavid Gibson     ppc_hash_pte32_t pte;
439caa597bdSDavid Gibson     int prot;
440182357dbSRichard Henderson     int need_prot;
441caa597bdSDavid Gibson     hwaddr raddr;
4420480884fSDavid Gibson 
4436c3c873cSRichard Henderson     /* There are no hash32 large pages. */
4446c3c873cSRichard Henderson     *psizep = TARGET_PAGE_BITS;
4456a980110SDavid Gibson 
44665d61643SDavid Gibson     /* 1. Handle real mode accesses */
44731fa64ecSRichard Henderson     if (access_type == MMU_INST_FETCH ? !msr_ir : !msr_dr) {
44865d61643SDavid Gibson         /* Translation is off */
4496c3c873cSRichard Henderson         *raddrp = eaddr;
4506c3c873cSRichard Henderson         *protp = PAGE_READ | PAGE_WRITE | PAGE_EXEC;
4516c3c873cSRichard Henderson         return true;
45265d61643SDavid Gibson     }
45365d61643SDavid Gibson 
4546c3c873cSRichard Henderson     need_prot = prot_for_access_type(access_type);
4556c3c873cSRichard Henderson 
45665d61643SDavid Gibson     /* 2. Check Block Address Translation entries (BATs) */
45765d61643SDavid Gibson     if (env->nb_BATs != 0) {
4586c3c873cSRichard Henderson         raddr = ppc_hash32_bat_lookup(cpu, eaddr, access_type, protp);
459caa597bdSDavid Gibson         if (raddr != -1) {
4606c3c873cSRichard Henderson             if (need_prot & ~*protp) {
4616c3c873cSRichard Henderson                 if (guest_visible) {
46231fa64ecSRichard Henderson                     if (access_type == MMU_INST_FETCH) {
46327103424SAndreas Färber                         cs->exception_index = POWERPC_EXCP_ISI;
464caa597bdSDavid Gibson                         env->error_code = 0x08000000;
465caa597bdSDavid Gibson                     } else {
46627103424SAndreas Färber                         cs->exception_index = POWERPC_EXCP_DSI;
467caa597bdSDavid Gibson                         env->error_code = 0;
468caa597bdSDavid Gibson                         env->spr[SPR_DAR] = eaddr;
46931fa64ecSRichard Henderson                         if (access_type == MMU_DATA_STORE) {
470caa597bdSDavid Gibson                             env->spr[SPR_DSISR] = 0x0a000000;
471caa597bdSDavid Gibson                         } else {
472caa597bdSDavid Gibson                             env->spr[SPR_DSISR] = 0x08000000;
473e01b4445SDavid Gibson                         }
474caa597bdSDavid Gibson                     }
475caa597bdSDavid Gibson                 }
4766c3c873cSRichard Henderson                 return false;
4776c3c873cSRichard Henderson             }
4786c3c873cSRichard Henderson             *raddrp = raddr;
4796c3c873cSRichard Henderson             return true;
48065d61643SDavid Gibson         }
481145e52f3SDavid Gibson     }
48265d61643SDavid Gibson 
4834b9605a5SDavid Gibson     /* 3. Look up the Segment Register */
4840480884fSDavid Gibson     sr = env->sr[eaddr >> 28];
4854b9605a5SDavid Gibson 
486723ed73aSDavid Gibson     /* 4. Handle direct store segments */
487723ed73aSDavid Gibson     if (sr & SR32_T) {
4886c3c873cSRichard Henderson         return ppc_hash32_direct_store(cpu, sr, eaddr, access_type,
4896c3c873cSRichard Henderson                                        raddrp, protp, guest_visible);
490723ed73aSDavid Gibson     }
491723ed73aSDavid Gibson 
492bb218042SDavid Gibson     /* 5. Check for segment level no-execute violation */
49331fa64ecSRichard Henderson     if (access_type == MMU_INST_FETCH && (sr & SR32_NX)) {
4946c3c873cSRichard Henderson         if (guest_visible) {
49527103424SAndreas Färber             cs->exception_index = POWERPC_EXCP_ISI;
496caa597bdSDavid Gibson             env->error_code = 0x10000000;
4976c3c873cSRichard Henderson         }
4986c3c873cSRichard Henderson         return false;
499bb218042SDavid Gibson     }
5007f3bdc2dSDavid Gibson 
5017f3bdc2dSDavid Gibson     /* 6. Locate the PTE in the hash table */
5027ef23068SDavid Gibson     pte_offset = ppc_hash32_htab_lookup(cpu, sr, eaddr, &pte);
5037f3bdc2dSDavid Gibson     if (pte_offset == -1) {
5046c3c873cSRichard Henderson         if (guest_visible) {
50531fa64ecSRichard Henderson             if (access_type == MMU_INST_FETCH) {
50627103424SAndreas Färber                 cs->exception_index = POWERPC_EXCP_ISI;
507caa597bdSDavid Gibson                 env->error_code = 0x40000000;
508caa597bdSDavid Gibson             } else {
50927103424SAndreas Färber                 cs->exception_index = POWERPC_EXCP_DSI;
510caa597bdSDavid Gibson                 env->error_code = 0;
511caa597bdSDavid Gibson                 env->spr[SPR_DAR] = eaddr;
51231fa64ecSRichard Henderson                 if (access_type == MMU_DATA_STORE) {
513caa597bdSDavid Gibson                     env->spr[SPR_DSISR] = 0x42000000;
514caa597bdSDavid Gibson                 } else {
515caa597bdSDavid Gibson                     env->spr[SPR_DSISR] = 0x40000000;
516caa597bdSDavid Gibson                 }
517caa597bdSDavid Gibson             }
5186c3c873cSRichard Henderson         }
5196c3c873cSRichard Henderson         return false;
5207f3bdc2dSDavid Gibson     }
521339aaf5bSAntony Pavlov     qemu_log_mask(CPU_LOG_MMU,
522339aaf5bSAntony Pavlov                 "found PTE at offset %08" HWADDR_PRIx "\n", pte_offset);
5237f3bdc2dSDavid Gibson 
5247f3bdc2dSDavid Gibson     /* 7. Check access permissions */
5256a980110SDavid Gibson 
5267ef23068SDavid Gibson     prot = ppc_hash32_pte_prot(cpu, sr, pte);
5276a980110SDavid Gibson 
528182357dbSRichard Henderson     if (need_prot & ~prot) {
5296a980110SDavid Gibson         /* Access right violation */
530339aaf5bSAntony Pavlov         qemu_log_mask(CPU_LOG_MMU, "PTE access rejected\n");
5316c3c873cSRichard Henderson         if (guest_visible) {
53231fa64ecSRichard Henderson             if (access_type == MMU_INST_FETCH) {
53327103424SAndreas Färber                 cs->exception_index = POWERPC_EXCP_ISI;
534caa597bdSDavid Gibson                 env->error_code = 0x08000000;
535caa597bdSDavid Gibson             } else {
53627103424SAndreas Färber                 cs->exception_index = POWERPC_EXCP_DSI;
537caa597bdSDavid Gibson                 env->error_code = 0;
538caa597bdSDavid Gibson                 env->spr[SPR_DAR] = eaddr;
53931fa64ecSRichard Henderson                 if (access_type == MMU_DATA_STORE) {
540caa597bdSDavid Gibson                     env->spr[SPR_DSISR] = 0x0a000000;
541caa597bdSDavid Gibson                 } else {
542caa597bdSDavid Gibson                     env->spr[SPR_DSISR] = 0x08000000;
543caa597bdSDavid Gibson                 }
544caa597bdSDavid Gibson             }
5456c3c873cSRichard Henderson         }
5466c3c873cSRichard Henderson         return false;
5476a980110SDavid Gibson     }
5486a980110SDavid Gibson 
549339aaf5bSAntony Pavlov     qemu_log_mask(CPU_LOG_MMU, "PTE access granted !\n");
55087dc3fd1SDavid Gibson 
55187dc3fd1SDavid Gibson     /* 8. Update PTE referenced and changed bits if necessary */
55287dc3fd1SDavid Gibson 
5536e8a65abSBenjamin Herrenschmidt     if (!(pte.pte1 & HPTE32_R_R)) {
5546e8a65abSBenjamin Herrenschmidt         ppc_hash32_set_r(cpu, pte_offset, pte.pte1);
5556e8a65abSBenjamin Herrenschmidt     }
5566e8a65abSBenjamin Herrenschmidt     if (!(pte.pte1 & HPTE32_R_C)) {
55731fa64ecSRichard Henderson         if (access_type == MMU_DATA_STORE) {
5586e8a65abSBenjamin Herrenschmidt             ppc_hash32_set_c(cpu, pte_offset, pte.pte1);
559b3440746SDavid Gibson         } else {
560596e3ca8SDavid Gibson             /*
561596e3ca8SDavid Gibson              * Treat the page as read-only for now, so that a later write
562596e3ca8SDavid Gibson              * will pass through this function again to set the C bit
563596e3ca8SDavid Gibson              */
564caa597bdSDavid Gibson             prot &= ~PAGE_WRITE;
565b3440746SDavid Gibson         }
5667f3bdc2dSDavid Gibson      }
5670480884fSDavid Gibson 
5686d11d998SDavid Gibson     /* 9. Determine the real address from the PTE */
5696d11d998SDavid Gibson 
5706c3c873cSRichard Henderson     *raddrp = ppc_hash32_pte_raddr(sr, pte, eaddr);
5716c3c873cSRichard Henderson     *protp = prot;
5726c3c873cSRichard Henderson     return true;
5736c3c873cSRichard Henderson }
574