xref: /qemu/target/ppc/mmu-hash32.c (revision 75d5ec89c03cb2f1a2bd0d9912e624ceb6fd1999)
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
109d7c3f4aSDavid Gibson  * version 2 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 
219d7c3f4aSDavid Gibson #include "cpu.h"
229d7c3f4aSDavid Gibson #include "helper.h"
239d7c3f4aSDavid Gibson #include "sysemu/kvm.h"
249d7c3f4aSDavid Gibson #include "kvm_ppc.h"
259d7c3f4aSDavid Gibson #include "mmu-hash32.h"
269d7c3f4aSDavid Gibson 
279d7c3f4aSDavid Gibson //#define DEBUG_MMU
2898132796SDavid Gibson //#define DEBUG_BAT
299d7c3f4aSDavid Gibson 
309d7c3f4aSDavid Gibson #ifdef DEBUG_MMU
319d7c3f4aSDavid Gibson #  define LOG_MMU(...) qemu_log(__VA_ARGS__)
329d7c3f4aSDavid Gibson #  define LOG_MMU_STATE(env) log_cpu_state((env), 0)
339d7c3f4aSDavid Gibson #else
349d7c3f4aSDavid Gibson #  define LOG_MMU(...) do { } while (0)
359d7c3f4aSDavid Gibson #  define LOG_MMU_STATE(...) do { } while (0)
369d7c3f4aSDavid Gibson #endif
379d7c3f4aSDavid Gibson 
3898132796SDavid Gibson #ifdef DEBUG_BATS
3998132796SDavid Gibson #  define LOG_BATS(...) qemu_log(__VA_ARGS__)
4098132796SDavid Gibson #else
4198132796SDavid Gibson #  define LOG_BATS(...) do { } while (0)
4298132796SDavid Gibson #endif
4398132796SDavid Gibson 
445dc68eb0SDavid Gibson struct mmu_ctx_hash32 {
455dc68eb0SDavid Gibson     hwaddr raddr;      /* Real address              */
465dc68eb0SDavid Gibson     int prot;                      /* Protection bits           */
475dc68eb0SDavid Gibson     int key;                       /* Access key                */
485dc68eb0SDavid Gibson };
495dc68eb0SDavid Gibson 
50e01b4445SDavid Gibson static int ppc_hash32_pp_prot(int key, int pp, int nx)
51496272a7SDavid Gibson {
52e01b4445SDavid Gibson     int prot;
53496272a7SDavid Gibson 
54496272a7SDavid Gibson     if (key == 0) {
55496272a7SDavid Gibson         switch (pp) {
56496272a7SDavid Gibson         case 0x0:
57496272a7SDavid Gibson         case 0x1:
58496272a7SDavid Gibson         case 0x2:
59e01b4445SDavid Gibson             prot = PAGE_READ | PAGE_WRITE;
60496272a7SDavid Gibson             break;
61e01b4445SDavid Gibson 
62e01b4445SDavid Gibson         case 0x3:
63e01b4445SDavid Gibson             prot = PAGE_READ;
64e01b4445SDavid Gibson             break;
65e01b4445SDavid Gibson 
66e01b4445SDavid Gibson         default:
67e01b4445SDavid Gibson             abort();
68496272a7SDavid Gibson         }
69496272a7SDavid Gibson     } else {
70496272a7SDavid Gibson         switch (pp) {
71496272a7SDavid Gibson         case 0x0:
72e01b4445SDavid Gibson             prot = 0;
73496272a7SDavid Gibson             break;
74e01b4445SDavid Gibson 
75496272a7SDavid Gibson         case 0x1:
76496272a7SDavid Gibson         case 0x3:
77e01b4445SDavid Gibson             prot = PAGE_READ;
78496272a7SDavid Gibson             break;
79e01b4445SDavid Gibson 
80496272a7SDavid Gibson         case 0x2:
81e01b4445SDavid Gibson             prot = PAGE_READ | PAGE_WRITE;
82496272a7SDavid Gibson             break;
83e01b4445SDavid Gibson 
84e01b4445SDavid Gibson         default:
85e01b4445SDavid Gibson             abort();
86496272a7SDavid Gibson         }
87496272a7SDavid Gibson     }
88496272a7SDavid Gibson     if (nx == 0) {
89e01b4445SDavid Gibson         prot |= PAGE_EXEC;
90496272a7SDavid Gibson     }
91496272a7SDavid Gibson 
92e01b4445SDavid Gibson     return prot;
93496272a7SDavid Gibson }
94496272a7SDavid Gibson 
95e01b4445SDavid Gibson static int ppc_hash32_pte_prot(CPUPPCState *env,
96e01b4445SDavid Gibson                                target_ulong sr, ppc_hash_pte32_t pte)
97496272a7SDavid Gibson {
98e01b4445SDavid Gibson     unsigned pp, key;
99496272a7SDavid Gibson 
100e01b4445SDavid Gibson     key = !!(msr_pr ? (sr & SR32_KP) : (sr & SR32_KS));
101e01b4445SDavid Gibson     pp = pte.pte1 & HPTE32_R_PP;
102496272a7SDavid Gibson 
103e01b4445SDavid Gibson     return ppc_hash32_pp_prot(key, pp, !!(sr & SR32_NX));
104496272a7SDavid Gibson }
105496272a7SDavid Gibson 
1066fc76aa9SDavid Gibson static target_ulong hash32_bat_size(CPUPPCState *env,
1079986ed1eSDavid Gibson                                     target_ulong batu, target_ulong batl)
10898132796SDavid Gibson {
1096fc76aa9SDavid Gibson     if ((msr_pr && !(batu & BATU32_VP))
1106fc76aa9SDavid Gibson         || (!msr_pr && !(batu & BATU32_VS))) {
1116fc76aa9SDavid Gibson         return 0;
112e1d49515SDavid Gibson     }
1136fc76aa9SDavid Gibson 
1146fc76aa9SDavid Gibson     return BATU32_BEPI & ~((batu & BATU32_BL) << 15);
115e1d49515SDavid Gibson }
116e1d49515SDavid Gibson 
117e1d49515SDavid Gibson static int hash32_bat_prot(CPUPPCState *env,
118e1d49515SDavid Gibson                            target_ulong batu, target_ulong batl)
119e1d49515SDavid Gibson {
120e1d49515SDavid Gibson     int pp, prot;
121e1d49515SDavid Gibson 
122e1d49515SDavid Gibson     prot = 0;
1239986ed1eSDavid Gibson     pp = batl & BATL32_PP;
12498132796SDavid Gibson     if (pp != 0) {
12598132796SDavid Gibson         prot = PAGE_READ | PAGE_EXEC;
12698132796SDavid Gibson         if (pp == 0x2) {
12798132796SDavid Gibson             prot |= PAGE_WRITE;
12898132796SDavid Gibson         }
12998132796SDavid Gibson     }
130e1d49515SDavid Gibson     return prot;
13198132796SDavid Gibson }
13298132796SDavid Gibson 
1336fc76aa9SDavid Gibson static target_ulong hash32_bat_601_size(CPUPPCState *env,
1349986ed1eSDavid Gibson                                 target_ulong batu, target_ulong batl)
13598132796SDavid Gibson {
1366fc76aa9SDavid Gibson     if (!(batl & BATL32_601_V)) {
1376fc76aa9SDavid Gibson         return 0;
1386fc76aa9SDavid Gibson     }
13998132796SDavid Gibson 
1406fc76aa9SDavid Gibson     return BATU32_BEPI & ~((batl & BATL32_601_BL) << 17);
141e1d49515SDavid Gibson }
142e1d49515SDavid Gibson 
143e1d49515SDavid Gibson static int hash32_bat_601_prot(CPUPPCState *env,
144e1d49515SDavid Gibson                                target_ulong batu, target_ulong batl)
145e1d49515SDavid Gibson {
146e1d49515SDavid Gibson     int key, pp;
147e1d49515SDavid Gibson 
1489986ed1eSDavid Gibson     pp = batu & BATU32_601_PP;
14998132796SDavid Gibson     if (msr_pr == 0) {
1509986ed1eSDavid Gibson         key = !!(batu & BATU32_601_KS);
15198132796SDavid Gibson     } else {
1529986ed1eSDavid Gibson         key = !!(batu & BATU32_601_KP);
15398132796SDavid Gibson     }
154e01b4445SDavid Gibson     return ppc_hash32_pp_prot(key, pp, 0);
15598132796SDavid Gibson }
15698132796SDavid Gibson 
157145e52f3SDavid Gibson static hwaddr ppc_hash32_bat_lookup(CPUPPCState *env, target_ulong ea, int rwx,
158145e52f3SDavid Gibson                                     int *prot)
15998132796SDavid Gibson {
1609986ed1eSDavid Gibson     target_ulong *BATlt, *BATut;
161145e52f3SDavid Gibson     int i;
16298132796SDavid Gibson 
16398132796SDavid Gibson     LOG_BATS("%s: %cBAT v " TARGET_FMT_lx "\n", __func__,
164145e52f3SDavid Gibson              rwx == 2 ? 'I' : 'D', ea);
16591cda45bSDavid Gibson     if (rwx == 2) {
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)) {
1786fc76aa9SDavid Gibson             mask = hash32_bat_601_size(env, batu, batl);
17998132796SDavid Gibson         } else {
1806fc76aa9SDavid Gibson             mask = hash32_bat_size(env, 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__,
184145e52f3SDavid Gibson                  type == ACCESS_CODE ? '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)) {
190145e52f3SDavid Gibson                 *prot = hash32_bat_601_prot(env, batu, batl);
191145e52f3SDavid Gibson             } else {
192145e52f3SDavid Gibson                 *prot = hash32_bat_prot(env, 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",
212145e52f3SDavid Gibson                      __func__, type == ACCESS_CODE ? '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 
221723ed73aSDavid Gibson static int ppc_hash32_direct_store(CPUPPCState *env, target_ulong sr,
222723ed73aSDavid Gibson                                    target_ulong eaddr, int rwx,
223723ed73aSDavid Gibson                                    hwaddr *raddr, int *prot)
224723ed73aSDavid Gibson {
225723ed73aSDavid Gibson     int key = !!(msr_pr ? (sr & SR32_KP) : (sr & SR32_KS));
226723ed73aSDavid Gibson 
227723ed73aSDavid Gibson     LOG_MMU("direct store...\n");
228723ed73aSDavid Gibson 
229723ed73aSDavid Gibson     if ((sr & 0x1FF00000) >> 20 == 0x07f) {
230723ed73aSDavid Gibson         /* Memory-forced I/O controller interface access */
231723ed73aSDavid Gibson         /* If T=1 and BUID=x'07F', the 601 performs a memory access
232723ed73aSDavid Gibson          * to SR[28-31] LA[4-31], bypassing all protection mechanisms.
233723ed73aSDavid Gibson          */
234723ed73aSDavid Gibson         *raddr = ((sr & 0xF) << 28) | (eaddr & 0x0FFFFFFF);
235723ed73aSDavid Gibson         *prot = PAGE_READ | PAGE_WRITE | PAGE_EXEC;
236723ed73aSDavid Gibson         return 0;
237723ed73aSDavid Gibson     }
238723ed73aSDavid Gibson 
239723ed73aSDavid Gibson     if (rwx == 2) {
240723ed73aSDavid Gibson         /* No code fetch is allowed in direct-store areas */
241723ed73aSDavid Gibson         return -4;
242723ed73aSDavid Gibson     }
243723ed73aSDavid Gibson 
244723ed73aSDavid Gibson     switch (env->access_type) {
245723ed73aSDavid Gibson     case ACCESS_INT:
246723ed73aSDavid Gibson         /* Integer load/store : only access allowed */
247723ed73aSDavid Gibson         break;
248723ed73aSDavid Gibson     case ACCESS_FLOAT:
249723ed73aSDavid Gibson         /* Floating point load/store */
250723ed73aSDavid Gibson         return -4;
251723ed73aSDavid Gibson     case ACCESS_RES:
252723ed73aSDavid Gibson         /* lwarx, ldarx or srwcx. */
253723ed73aSDavid Gibson         return -4;
254723ed73aSDavid Gibson     case ACCESS_CACHE:
255723ed73aSDavid Gibson         /* dcba, dcbt, dcbtst, dcbf, dcbi, dcbst, dcbz, or icbi */
256723ed73aSDavid Gibson         /* Should make the instruction do no-op.
257723ed73aSDavid Gibson          * As it already do no-op, it's quite easy :-)
258723ed73aSDavid Gibson          */
259723ed73aSDavid Gibson         *raddr = eaddr;
260723ed73aSDavid Gibson         return 0;
261723ed73aSDavid Gibson     case ACCESS_EXT:
262723ed73aSDavid Gibson         /* eciwx or ecowx */
263723ed73aSDavid Gibson         return -4;
264723ed73aSDavid Gibson     default:
265723ed73aSDavid Gibson         qemu_log("ERROR: instruction should not need "
266723ed73aSDavid Gibson                  "address translation\n");
267723ed73aSDavid Gibson         return -4;
268723ed73aSDavid Gibson     }
269723ed73aSDavid Gibson     if ((rwx == 1 || key != 1) && (rwx == 0 || key != 0)) {
270723ed73aSDavid Gibson         *raddr = eaddr;
271723ed73aSDavid Gibson         return 2;
272723ed73aSDavid Gibson     } else {
273723ed73aSDavid Gibson         return -2;
274723ed73aSDavid Gibson     }
275723ed73aSDavid Gibson }
276723ed73aSDavid Gibson 
27759191721SDavid Gibson hwaddr get_pteg_offset32(CPUPPCState *env, hwaddr hash)
27859191721SDavid Gibson {
279d5aea6f3SDavid Gibson     return (hash * HASH_PTEG_SIZE_32) & env->htab_mask;
28059191721SDavid Gibson }
28159191721SDavid Gibson 
282aea390e4SDavid Gibson static hwaddr ppc_hash32_pteg_search(CPUPPCState *env, hwaddr pteg_off,
283aea390e4SDavid Gibson                                      bool secondary, target_ulong ptem,
284aea390e4SDavid Gibson                                      ppc_hash_pte32_t *pte)
285aea390e4SDavid Gibson {
286aea390e4SDavid Gibson     hwaddr pte_offset = pteg_off;
287aea390e4SDavid Gibson     target_ulong pte0, pte1;
288aea390e4SDavid Gibson     int i;
289aea390e4SDavid Gibson 
290aea390e4SDavid Gibson     for (i = 0; i < HPTES_PER_GROUP; i++) {
291aea390e4SDavid Gibson         pte0 = ppc_hash32_load_hpte0(env, pte_offset);
292aea390e4SDavid Gibson         pte1 = ppc_hash32_load_hpte1(env, pte_offset);
293aea390e4SDavid Gibson 
294aea390e4SDavid Gibson         if ((pte0 & HPTE32_V_VALID)
295aea390e4SDavid Gibson             && (secondary == !!(pte0 & HPTE32_V_SECONDARY))
296aea390e4SDavid Gibson             && HPTE32_V_COMPARE(pte0, ptem)) {
297aea390e4SDavid Gibson             pte->pte0 = pte0;
298aea390e4SDavid Gibson             pte->pte1 = pte1;
299aea390e4SDavid Gibson             return pte_offset;
300aea390e4SDavid Gibson         }
301aea390e4SDavid Gibson 
302aea390e4SDavid Gibson         pte_offset += HASH_PTE_SIZE_32;
303aea390e4SDavid Gibson     }
304aea390e4SDavid Gibson 
305aea390e4SDavid Gibson     return -1;
306aea390e4SDavid Gibson }
307aea390e4SDavid Gibson 
3087f3bdc2dSDavid Gibson static hwaddr ppc_hash32_htab_lookup(CPUPPCState *env,
3097f3bdc2dSDavid Gibson                                      target_ulong sr, target_ulong eaddr,
3107f3bdc2dSDavid Gibson                                      ppc_hash_pte32_t *pte)
311c69b6151SDavid Gibson {
312aea390e4SDavid Gibson     hwaddr pteg_off, pte_offset;
313a1ff751aSDavid Gibson     hwaddr hash;
314a1ff751aSDavid Gibson     uint32_t vsid, pgidx, ptem;
315c69b6151SDavid Gibson 
316a1ff751aSDavid Gibson     vsid = sr & SR32_VSID;
317a1ff751aSDavid Gibson     pgidx = (eaddr & ~SEGMENT_MASK_256M) >> TARGET_PAGE_BITS;
318a1ff751aSDavid Gibson     hash = vsid ^ pgidx;
319a1ff751aSDavid Gibson     ptem = (vsid << 7) | (pgidx >> 10);
320a1ff751aSDavid Gibson 
321a1ff751aSDavid Gibson     /* Page address translation */
322a1ff751aSDavid Gibson     LOG_MMU("htab_base " TARGET_FMT_plx " htab_mask " TARGET_FMT_plx
323a1ff751aSDavid Gibson             " hash " TARGET_FMT_plx "\n",
324a1ff751aSDavid Gibson             env->htab_base, env->htab_mask, hash);
325a1ff751aSDavid Gibson 
326a1ff751aSDavid Gibson     /* Primary PTEG lookup */
327a1ff751aSDavid Gibson     LOG_MMU("0 htab=" TARGET_FMT_plx "/" TARGET_FMT_plx
328a1ff751aSDavid Gibson             " vsid=%" PRIx32 " ptem=%" PRIx32
329a1ff751aSDavid Gibson             " hash=" TARGET_FMT_plx "\n",
330a1ff751aSDavid Gibson             env->htab_base, env->htab_mask, vsid, ptem, hash);
331a1ff751aSDavid Gibson     pteg_off = get_pteg_offset32(env, hash);
3327f3bdc2dSDavid Gibson     pte_offset = ppc_hash32_pteg_search(env, pteg_off, 0, ptem, pte);
333a1ff751aSDavid Gibson     if (pte_offset == -1) {
334a1ff751aSDavid Gibson         /* Secondary PTEG lookup */
335a1ff751aSDavid Gibson         LOG_MMU("1 htab=" TARGET_FMT_plx "/" TARGET_FMT_plx
336a1ff751aSDavid Gibson                 " vsid=%" PRIx32 " api=%" PRIx32
337a1ff751aSDavid Gibson                 " hash=" TARGET_FMT_plx "\n", env->htab_base,
338a1ff751aSDavid Gibson                 env->htab_mask, vsid, ptem, ~hash);
339a1ff751aSDavid Gibson         pteg_off = get_pteg_offset32(env, ~hash);
3407f3bdc2dSDavid Gibson         pte_offset = ppc_hash32_pteg_search(env, pteg_off, 1, ptem, pte);
341a1ff751aSDavid Gibson     }
342a1ff751aSDavid Gibson 
3437f3bdc2dSDavid Gibson     return pte_offset;
344c69b6151SDavid Gibson }
3450480884fSDavid Gibson 
3466d11d998SDavid Gibson static hwaddr ppc_hash32_pte_raddr(target_ulong sr, ppc_hash_pte32_t pte,
3476d11d998SDavid Gibson                                    target_ulong eaddr)
3486d11d998SDavid Gibson {
34975d5ec89SDavid Gibson     hwaddr rpn = pte.pte1 & HPTE32_R_RPN;
3506d11d998SDavid Gibson     hwaddr mask = ~TARGET_PAGE_MASK;
3516d11d998SDavid Gibson 
3526d11d998SDavid Gibson     return (rpn & ~mask) | (eaddr & mask);
3536d11d998SDavid Gibson }
3546d11d998SDavid Gibson 
35565d61643SDavid Gibson static int ppc_hash32_translate(CPUPPCState *env, struct mmu_ctx_hash32 *ctx,
35691cda45bSDavid Gibson                                 target_ulong eaddr, int rwx)
3570480884fSDavid Gibson {
358a1ff751aSDavid Gibson     target_ulong sr;
3597f3bdc2dSDavid Gibson     hwaddr pte_offset;
3607f3bdc2dSDavid Gibson     ppc_hash_pte32_t pte;
361b3440746SDavid Gibson     uint32_t new_pte1;
362e01b4445SDavid Gibson     const int need_prot[] = {PAGE_READ, PAGE_WRITE, PAGE_EXEC};
3630480884fSDavid Gibson 
3646a980110SDavid Gibson     assert((rwx == 0) || (rwx == 1) || (rwx == 2));
3656a980110SDavid Gibson 
36665d61643SDavid Gibson     /* 1. Handle real mode accesses */
36765d61643SDavid Gibson     if (((rwx == 2) && (msr_ir == 0)) || ((rwx != 2) && (msr_dr == 0))) {
36865d61643SDavid Gibson         /* Translation is off */
36965d61643SDavid Gibson         ctx->raddr = eaddr;
37065d61643SDavid Gibson         ctx->prot = PAGE_READ | PAGE_EXEC | PAGE_WRITE;
37165d61643SDavid Gibson         return 0;
37265d61643SDavid Gibson     }
37365d61643SDavid Gibson 
37465d61643SDavid Gibson     /* 2. Check Block Address Translation entries (BATs) */
37565d61643SDavid Gibson     if (env->nb_BATs != 0) {
376145e52f3SDavid Gibson         ctx->raddr = ppc_hash32_bat_lookup(env, eaddr, rwx, &ctx->prot);
377145e52f3SDavid Gibson         if (ctx->raddr != -1) {
378e01b4445SDavid Gibson             if (need_prot[rwx] & ~ctx->prot) {
379e01b4445SDavid Gibson                 return -2;
380e01b4445SDavid Gibson             }
381e01b4445SDavid Gibson             return 0;
38265d61643SDavid Gibson         }
383145e52f3SDavid Gibson     }
38465d61643SDavid Gibson 
3854b9605a5SDavid Gibson     /* 3. Look up the Segment Register */
3860480884fSDavid Gibson     sr = env->sr[eaddr >> 28];
3874b9605a5SDavid Gibson 
388723ed73aSDavid Gibson     /* 4. Handle direct store segments */
389723ed73aSDavid Gibson     if (sr & SR32_T) {
390723ed73aSDavid Gibson         return ppc_hash32_direct_store(env, sr, eaddr, rwx,
391723ed73aSDavid Gibson                                        &ctx->raddr, &ctx->prot);
392723ed73aSDavid Gibson     }
393723ed73aSDavid Gibson 
394bb218042SDavid Gibson     /* 5. Check for segment level no-execute violation */
395e01b4445SDavid Gibson     if ((rwx == 2) && (sr & SR32_NX)) {
396bb218042SDavid Gibson         return -3;
397bb218042SDavid Gibson     }
3987f3bdc2dSDavid Gibson 
3997f3bdc2dSDavid Gibson     /* 6. Locate the PTE in the hash table */
4007f3bdc2dSDavid Gibson     pte_offset = ppc_hash32_htab_lookup(env, sr, eaddr, &pte);
4017f3bdc2dSDavid Gibson     if (pte_offset == -1) {
4027f3bdc2dSDavid Gibson         return -1;
4037f3bdc2dSDavid Gibson     }
4047f3bdc2dSDavid Gibson     LOG_MMU("found PTE at offset %08" HWADDR_PRIx "\n", pte_offset);
4057f3bdc2dSDavid Gibson 
4067f3bdc2dSDavid Gibson     /* 7. Check access permissions */
4076a980110SDavid Gibson 
408e01b4445SDavid Gibson     ctx->prot = ppc_hash32_pte_prot(env, sr, pte);
4096a980110SDavid Gibson 
410e01b4445SDavid Gibson     if (need_prot[rwx] & ~ctx->prot) {
4116a980110SDavid Gibson         /* Access right violation */
4126a980110SDavid Gibson         LOG_MMU("PTE access rejected\n");
413e01b4445SDavid Gibson         return -2;
4146a980110SDavid Gibson     }
4156a980110SDavid Gibson 
41687dc3fd1SDavid Gibson     LOG_MMU("PTE access granted !\n");
41787dc3fd1SDavid Gibson 
41887dc3fd1SDavid Gibson     /* 8. Update PTE referenced and changed bits if necessary */
41987dc3fd1SDavid Gibson 
420b3440746SDavid Gibson     new_pte1 = pte.pte1 | HPTE32_R_R; /* set referenced bit */
421b3440746SDavid Gibson     if (rwx == 1) {
422b3440746SDavid Gibson         new_pte1 |= HPTE32_R_C; /* set changed (dirty) bit */
423b3440746SDavid Gibson     } else {
424b3440746SDavid Gibson         /* Treat the page as read-only for now, so that a later write
425b3440746SDavid Gibson          * will pass through this function again to set the C bit */
426b3440746SDavid Gibson         ctx->prot &= ~PAGE_WRITE;
427b3440746SDavid Gibson     }
428b3440746SDavid Gibson 
429b3440746SDavid Gibson     if (new_pte1 != pte.pte1) {
430b3440746SDavid Gibson         ppc_hash32_store_hpte1(env, pte_offset, new_pte1);
4317f3bdc2dSDavid Gibson     }
4320480884fSDavid Gibson 
4336d11d998SDavid Gibson     /* 9. Determine the real address from the PTE */
4346d11d998SDavid Gibson 
4356d11d998SDavid Gibson     ctx->raddr = ppc_hash32_pte_raddr(sr, pte, eaddr);
436e01b4445SDavid Gibson 
437e01b4445SDavid Gibson     return 0;
4380480884fSDavid Gibson }
439629bd516SDavid Gibson 
440f2ad6be8SDavid Gibson hwaddr ppc_hash32_get_phys_page_debug(CPUPPCState *env, target_ulong addr)
441f2ad6be8SDavid Gibson {
4425dc68eb0SDavid Gibson     struct mmu_ctx_hash32 ctx;
443f2ad6be8SDavid Gibson 
44491cda45bSDavid Gibson     /* FIXME: Will not behave sanely for direct store segments, but
44591cda45bSDavid Gibson      * they're almost never used */
44665d61643SDavid Gibson     if (unlikely(ppc_hash32_translate(env, &ctx, addr, 0)
447f2ad6be8SDavid Gibson                  != 0)) {
448f2ad6be8SDavid Gibson         return -1;
449f2ad6be8SDavid Gibson     }
450f2ad6be8SDavid Gibson 
451f2ad6be8SDavid Gibson     return ctx.raddr & TARGET_PAGE_MASK;
452f2ad6be8SDavid Gibson }
453f2ad6be8SDavid Gibson 
45491cda45bSDavid Gibson int ppc_hash32_handle_mmu_fault(CPUPPCState *env, target_ulong address, int rwx,
45525de24abSDavid Gibson                                 int mmu_idx)
45625de24abSDavid Gibson {
4575dc68eb0SDavid Gibson     struct mmu_ctx_hash32 ctx;
45825de24abSDavid Gibson     int ret = 0;
45925de24abSDavid Gibson 
46065d61643SDavid Gibson     ret = ppc_hash32_translate(env, &ctx, address, rwx);
46125de24abSDavid Gibson     if (ret == 0) {
46225de24abSDavid Gibson         tlb_set_page(env, address & TARGET_PAGE_MASK,
46325de24abSDavid Gibson                      ctx.raddr & TARGET_PAGE_MASK, ctx.prot,
46425de24abSDavid Gibson                      mmu_idx, TARGET_PAGE_SIZE);
46525de24abSDavid Gibson         ret = 0;
46625de24abSDavid Gibson     } else if (ret < 0) {
46725de24abSDavid Gibson         LOG_MMU_STATE(env);
46891cda45bSDavid Gibson         if (rwx == 2) {
46925de24abSDavid Gibson             switch (ret) {
47025de24abSDavid Gibson             case -1:
47125de24abSDavid Gibson                 /* No matches in page tables or TLB */
47225de24abSDavid Gibson                 env->exception_index = POWERPC_EXCP_ISI;
47325de24abSDavid Gibson                 env->error_code = 0x40000000;
47425de24abSDavid Gibson                 break;
47525de24abSDavid Gibson             case -2:
47625de24abSDavid Gibson                 /* Access rights violation */
47725de24abSDavid Gibson                 env->exception_index = POWERPC_EXCP_ISI;
47825de24abSDavid Gibson                 env->error_code = 0x08000000;
47925de24abSDavid Gibson                 break;
48025de24abSDavid Gibson             case -3:
48125de24abSDavid Gibson                 /* No execute protection violation */
48225de24abSDavid Gibson                 env->exception_index = POWERPC_EXCP_ISI;
48325de24abSDavid Gibson                 env->error_code = 0x10000000;
48425de24abSDavid Gibson                 break;
48525de24abSDavid Gibson             case -4:
48625de24abSDavid Gibson                 /* Direct store exception */
48725de24abSDavid Gibson                 /* No code fetch is allowed in direct-store areas */
48825de24abSDavid Gibson                 env->exception_index = POWERPC_EXCP_ISI;
48925de24abSDavid Gibson                 env->error_code = 0x10000000;
49025de24abSDavid Gibson                 break;
49125de24abSDavid Gibson             }
49225de24abSDavid Gibson         } else {
49325de24abSDavid Gibson             switch (ret) {
49425de24abSDavid Gibson             case -1:
49525de24abSDavid Gibson                 /* No matches in page tables or TLB */
49625de24abSDavid Gibson                 env->exception_index = POWERPC_EXCP_DSI;
49725de24abSDavid Gibson                 env->error_code = 0;
49825de24abSDavid Gibson                 env->spr[SPR_DAR] = address;
49991cda45bSDavid Gibson                 if (rwx == 1) {
50025de24abSDavid Gibson                     env->spr[SPR_DSISR] = 0x42000000;
50125de24abSDavid Gibson                 } else {
50225de24abSDavid Gibson                     env->spr[SPR_DSISR] = 0x40000000;
50325de24abSDavid Gibson                 }
50425de24abSDavid Gibson                 break;
50525de24abSDavid Gibson             case -2:
50625de24abSDavid Gibson                 /* Access rights violation */
50725de24abSDavid Gibson                 env->exception_index = POWERPC_EXCP_DSI;
50825de24abSDavid Gibson                 env->error_code = 0;
50925de24abSDavid Gibson                 env->spr[SPR_DAR] = address;
51091cda45bSDavid Gibson                 if (rwx == 1) {
51125de24abSDavid Gibson                     env->spr[SPR_DSISR] = 0x0A000000;
51225de24abSDavid Gibson                 } else {
51325de24abSDavid Gibson                     env->spr[SPR_DSISR] = 0x08000000;
51425de24abSDavid Gibson                 }
51525de24abSDavid Gibson                 break;
51625de24abSDavid Gibson             case -4:
51725de24abSDavid Gibson                 /* Direct store exception */
51891cda45bSDavid Gibson                 switch (env->access_type) {
51925de24abSDavid Gibson                 case ACCESS_FLOAT:
52025de24abSDavid Gibson                     /* Floating point load/store */
52125de24abSDavid Gibson                     env->exception_index = POWERPC_EXCP_ALIGN;
52225de24abSDavid Gibson                     env->error_code = POWERPC_EXCP_ALIGN_FP;
52325de24abSDavid Gibson                     env->spr[SPR_DAR] = address;
52425de24abSDavid Gibson                     break;
52525de24abSDavid Gibson                 case ACCESS_RES:
52625de24abSDavid Gibson                     /* lwarx, ldarx or stwcx. */
52725de24abSDavid Gibson                     env->exception_index = POWERPC_EXCP_DSI;
52825de24abSDavid Gibson                     env->error_code = 0;
52925de24abSDavid Gibson                     env->spr[SPR_DAR] = address;
53091cda45bSDavid Gibson                     if (rwx == 1) {
53125de24abSDavid Gibson                         env->spr[SPR_DSISR] = 0x06000000;
53225de24abSDavid Gibson                     } else {
53325de24abSDavid Gibson                         env->spr[SPR_DSISR] = 0x04000000;
53425de24abSDavid Gibson                     }
53525de24abSDavid Gibson                     break;
53625de24abSDavid Gibson                 case ACCESS_EXT:
53725de24abSDavid Gibson                     /* eciwx or ecowx */
53825de24abSDavid Gibson                     env->exception_index = POWERPC_EXCP_DSI;
53925de24abSDavid Gibson                     env->error_code = 0;
54025de24abSDavid Gibson                     env->spr[SPR_DAR] = address;
54191cda45bSDavid Gibson                     if (rwx == 1) {
54225de24abSDavid Gibson                         env->spr[SPR_DSISR] = 0x06100000;
54325de24abSDavid Gibson                     } else {
54425de24abSDavid Gibson                         env->spr[SPR_DSISR] = 0x04100000;
54525de24abSDavid Gibson                     }
54625de24abSDavid Gibson                     break;
54725de24abSDavid Gibson                 default:
54825de24abSDavid Gibson                     printf("DSI: invalid exception (%d)\n", ret);
54925de24abSDavid Gibson                     env->exception_index = POWERPC_EXCP_PROGRAM;
55025de24abSDavid Gibson                     env->error_code =
55125de24abSDavid Gibson                         POWERPC_EXCP_INVAL | POWERPC_EXCP_INVAL_INVAL;
55225de24abSDavid Gibson                     env->spr[SPR_DAR] = address;
55325de24abSDavid Gibson                     break;
55425de24abSDavid Gibson                 }
55525de24abSDavid Gibson                 break;
55625de24abSDavid Gibson             }
55725de24abSDavid Gibson         }
55825de24abSDavid Gibson #if 0
55925de24abSDavid Gibson         printf("%s: set exception to %d %02x\n", __func__,
56025de24abSDavid Gibson                env->exception, env->error_code);
56125de24abSDavid Gibson #endif
56225de24abSDavid Gibson         ret = 1;
56325de24abSDavid Gibson     }
56425de24abSDavid Gibson 
56525de24abSDavid Gibson     return ret;
56625de24abSDavid Gibson }
567