]> Git Repo - qemu.git/blob - target-ppc/mmu-hash32.c
target-ppc: Disentangle get_physical_address() paths
[qemu.git] / target-ppc / mmu-hash32.c
1 /*
2  *  PowerPC MMU, TLB and BAT emulation helpers for QEMU.
3  *
4  *  Copyright (c) 2003-2007 Jocelyn Mayer
5  *  Copyright (c) 2013 David Gibson, IBM Corporation
6  *
7  * This library is free software; you can redistribute it and/or
8  * modify it under the terms of the GNU Lesser General Public
9  * License as published by the Free Software Foundation; either
10  * version 2 of the License, or (at your option) any later version.
11  *
12  * This library is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15  * Lesser General Public License for more details.
16  *
17  * You should have received a copy of the GNU Lesser General Public
18  * License along with this library; if not, see <http://www.gnu.org/licenses/>.
19  */
20
21 #include "cpu.h"
22 #include "helper.h"
23 #include "sysemu/kvm.h"
24 #include "kvm_ppc.h"
25 #include "mmu-hash32.h"
26
27 //#define DEBUG_MMU
28
29 #ifdef DEBUG_MMU
30 #  define LOG_MMU(...) qemu_log(__VA_ARGS__)
31 #  define LOG_MMU_STATE(env) log_cpu_state((env), 0)
32 #else
33 #  define LOG_MMU(...) do { } while (0)
34 #  define LOG_MMU_STATE(...) do { } while (0)
35 #endif
36
37 #define PTE_PTEM_MASK 0x7FFFFFBF
38 #define PTE_CHECK_MASK (TARGET_PAGE_MASK | 0x7B)
39
40 static inline int pte_is_valid_hash32(target_ulong pte0)
41 {
42     return pte0 & 0x80000000 ? 1 : 0;
43 }
44
45 static int pte_check_hash32(mmu_ctx_t *ctx, target_ulong pte0,
46                             target_ulong pte1, int h, int rw, int type)
47 {
48     target_ulong ptem, mmask;
49     int access, ret, pteh, ptev, pp;
50
51     ret = -1;
52     /* Check validity and table match */
53     ptev = pte_is_valid_hash32(pte0);
54     pteh = (pte0 >> 6) & 1;
55     if (ptev && h == pteh) {
56         /* Check vsid & api */
57         ptem = pte0 & PTE_PTEM_MASK;
58         mmask = PTE_CHECK_MASK;
59         pp = pte1 & 0x00000003;
60         if (ptem == ctx->ptem) {
61             if (ctx->raddr != (hwaddr)-1ULL) {
62                 /* all matches should have equal RPN, WIMG & PP */
63                 if ((ctx->raddr & mmask) != (pte1 & mmask)) {
64                     qemu_log("Bad RPN/WIMG/PP\n");
65                     return -3;
66                 }
67             }
68             /* Compute access rights */
69             access = pp_check(ctx->key, pp, ctx->nx);
70             /* Keep the matching PTE informations */
71             ctx->raddr = pte1;
72             ctx->prot = access;
73             ret = check_prot(ctx->prot, rw, type);
74             if (ret == 0) {
75                 /* Access granted */
76                 LOG_MMU("PTE access granted !\n");
77             } else {
78                 /* Access right violation */
79                 LOG_MMU("PTE access rejected\n");
80             }
81         }
82     }
83
84     return ret;
85 }
86
87 /* PTE table lookup */
88 static int find_pte32(CPUPPCState *env, mmu_ctx_t *ctx, int h,
89                       int rw, int type, int target_page_bits)
90 {
91     hwaddr pteg_off;
92     target_ulong pte0, pte1;
93     int i, good = -1;
94     int ret, r;
95
96     ret = -1; /* No entry found */
97     pteg_off = get_pteg_offset(env, ctx->hash[h], HASH_PTE_SIZE_32);
98     for (i = 0; i < 8; i++) {
99         if (env->external_htab) {
100             pte0 = ldl_p(env->external_htab + pteg_off + (i * 8));
101             pte1 = ldl_p(env->external_htab + pteg_off + (i * 8) + 4);
102         } else {
103             pte0 = ldl_phys(env->htab_base + pteg_off + (i * 8));
104             pte1 = ldl_phys(env->htab_base + pteg_off + (i * 8) + 4);
105         }
106         r = pte_check_hash32(ctx, pte0, pte1, h, rw, type);
107         LOG_MMU("Load pte from %08" HWADDR_PRIx " => " TARGET_FMT_lx " "
108                 TARGET_FMT_lx " %d %d %d " TARGET_FMT_lx "\n",
109                 pteg_off + (i * 8), pte0, pte1, (int)(pte0 >> 31), h,
110                 (int)((pte0 >> 6) & 1), ctx->ptem);
111         switch (r) {
112         case -3:
113             /* PTE inconsistency */
114             return -1;
115         case -2:
116             /* Access violation */
117             ret = -2;
118             good = i;
119             break;
120         case -1:
121         default:
122             /* No PTE match */
123             break;
124         case 0:
125             /* access granted */
126             /* XXX: we should go on looping to check all PTEs consistency
127              *      but if we can speed-up the whole thing as the
128              *      result would be undefined if PTEs are not consistent.
129              */
130             ret = 0;
131             good = i;
132             goto done;
133         }
134     }
135     if (good != -1) {
136     done:
137         LOG_MMU("found PTE at addr %08" HWADDR_PRIx " prot=%01x ret=%d\n",
138                 ctx->raddr, ctx->prot, ret);
139         /* Update page flags */
140         pte1 = ctx->raddr;
141         if (pte_update_flags(ctx, &pte1, ret, rw) == 1) {
142             if (env->external_htab) {
143                 stl_p(env->external_htab + pteg_off + (good * 8) + 4,
144                       pte1);
145             } else {
146                 stl_phys_notdirty(env->htab_base + pteg_off +
147                                   (good * 8) + 4, pte1);
148             }
149         }
150     }
151
152     /* We have a TLB that saves 4K pages, so let's
153      * split a huge page to 4k chunks */
154     if (target_page_bits != TARGET_PAGE_BITS) {
155         ctx->raddr |= (ctx->eaddr & ((1 << target_page_bits) - 1))
156                       & TARGET_PAGE_MASK;
157     }
158     return ret;
159 }
160
161 static int get_segment32(CPUPPCState *env, mmu_ctx_t *ctx,
162                          target_ulong eaddr, int rw, int type)
163 {
164     hwaddr hash;
165     target_ulong vsid;
166     int ds, pr, target_page_bits;
167     int ret, ret2;
168     target_ulong sr, pgidx;
169
170     pr = msr_pr;
171     ctx->eaddr = eaddr;
172
173     sr = env->sr[eaddr >> 28];
174     ctx->key = (((sr & 0x20000000) && (pr != 0)) ||
175                 ((sr & 0x40000000) && (pr == 0))) ? 1 : 0;
176     ds = sr & 0x80000000 ? 1 : 0;
177     ctx->nx = sr & 0x10000000 ? 1 : 0;
178     vsid = sr & 0x00FFFFFF;
179     target_page_bits = TARGET_PAGE_BITS;
180     LOG_MMU("Check segment v=" TARGET_FMT_lx " %d " TARGET_FMT_lx " nip="
181             TARGET_FMT_lx " lr=" TARGET_FMT_lx
182             " ir=%d dr=%d pr=%d %d t=%d\n",
183             eaddr, (int)(eaddr >> 28), sr, env->nip, env->lr, (int)msr_ir,
184             (int)msr_dr, pr != 0 ? 1 : 0, rw, type);
185     pgidx = (eaddr & ~SEGMENT_MASK_256M) >> target_page_bits;
186     hash = vsid ^ pgidx;
187     ctx->ptem = (vsid << 7) | (pgidx >> 10);
188
189     LOG_MMU("pte segment: key=%d ds %d nx %d vsid " TARGET_FMT_lx "\n",
190             ctx->key, ds, ctx->nx, vsid);
191     ret = -1;
192     if (!ds) {
193         /* Check if instruction fetch is allowed, if needed */
194         if (type != ACCESS_CODE || ctx->nx == 0) {
195             /* Page address translation */
196             LOG_MMU("htab_base " TARGET_FMT_plx " htab_mask " TARGET_FMT_plx
197                     " hash " TARGET_FMT_plx "\n",
198                     env->htab_base, env->htab_mask, hash);
199             ctx->hash[0] = hash;
200             ctx->hash[1] = ~hash;
201
202             /* Initialize real address with an invalid value */
203             ctx->raddr = (hwaddr)-1ULL;
204             LOG_MMU("0 htab=" TARGET_FMT_plx "/" TARGET_FMT_plx
205                     " vsid=" TARGET_FMT_lx " ptem=" TARGET_FMT_lx
206                     " hash=" TARGET_FMT_plx "\n",
207                     env->htab_base, env->htab_mask, vsid, ctx->ptem,
208                     ctx->hash[0]);
209             /* Primary table lookup */
210             ret = find_pte32(env, ctx, 0, rw, type, target_page_bits);
211             if (ret < 0) {
212                 /* Secondary table lookup */
213                 LOG_MMU("1 htab=" TARGET_FMT_plx "/" TARGET_FMT_plx
214                         " vsid=" TARGET_FMT_lx " api=" TARGET_FMT_lx
215                         " hash=" TARGET_FMT_plx "\n", env->htab_base,
216                         env->htab_mask, vsid, ctx->ptem, ctx->hash[1]);
217                 ret2 = find_pte32(env, ctx, 1, rw, type,
218                                   target_page_bits);
219                 if (ret2 != -1) {
220                     ret = ret2;
221                 }
222             }
223 #if defined(DUMP_PAGE_TABLES)
224             if (qemu_log_enabled()) {
225                 hwaddr curaddr;
226                 uint32_t a0, a1, a2, a3;
227
228                 qemu_log("Page table: " TARGET_FMT_plx " len " TARGET_FMT_plx
229                          "\n", sdr, mask + 0x80);
230                 for (curaddr = sdr; curaddr < (sdr + mask + 0x80);
231                      curaddr += 16) {
232                     a0 = ldl_phys(curaddr);
233                     a1 = ldl_phys(curaddr + 4);
234                     a2 = ldl_phys(curaddr + 8);
235                     a3 = ldl_phys(curaddr + 12);
236                     if (a0 != 0 || a1 != 0 || a2 != 0 || a3 != 0) {
237                         qemu_log(TARGET_FMT_plx ": %08x %08x %08x %08x\n",
238                                  curaddr, a0, a1, a2, a3);
239                     }
240                 }
241             }
242 #endif
243         } else {
244             LOG_MMU("No access allowed\n");
245             ret = -3;
246         }
247     } else {
248         target_ulong sr;
249
250         LOG_MMU("direct store...\n");
251         /* Direct-store segment : absolutely *BUGGY* for now */
252
253         /* Direct-store implies a 32-bit MMU.
254          * Check the Segment Register's bus unit ID (BUID).
255          */
256         sr = env->sr[eaddr >> 28];
257         if ((sr & 0x1FF00000) >> 20 == 0x07f) {
258             /* Memory-forced I/O controller interface access */
259             /* If T=1 and BUID=x'07F', the 601 performs a memory access
260              * to SR[28-31] LA[4-31], bypassing all protection mechanisms.
261              */
262             ctx->raddr = ((sr & 0xF) << 28) | (eaddr & 0x0FFFFFFF);
263             ctx->prot = PAGE_READ | PAGE_WRITE | PAGE_EXEC;
264             return 0;
265         }
266
267         switch (type) {
268         case ACCESS_INT:
269             /* Integer load/store : only access allowed */
270             break;
271         case ACCESS_CODE:
272             /* No code fetch is allowed in direct-store areas */
273             return -4;
274         case ACCESS_FLOAT:
275             /* Floating point load/store */
276             return -4;
277         case ACCESS_RES:
278             /* lwarx, ldarx or srwcx. */
279             return -4;
280         case ACCESS_CACHE:
281             /* dcba, dcbt, dcbtst, dcbf, dcbi, dcbst, dcbz, or icbi */
282             /* Should make the instruction do no-op.
283              * As it already do no-op, it's quite easy :-)
284              */
285             ctx->raddr = eaddr;
286             return 0;
287         case ACCESS_EXT:
288             /* eciwx or ecowx */
289             return -4;
290         default:
291             qemu_log("ERROR: instruction should not need "
292                         "address translation\n");
293             return -4;
294         }
295         if ((rw == 1 || ctx->key != 1) && (rw == 0 || ctx->key != 0)) {
296             ctx->raddr = eaddr;
297             ret = 2;
298         } else {
299             ret = -2;
300         }
301     }
302
303     return ret;
304 }
305
306 int ppc_hash32_get_physical_address(CPUPPCState *env, mmu_ctx_t *ctx,
307                                     target_ulong eaddr, int rw, int access_type)
308 {
309     bool real_mode = (access_type == ACCESS_CODE && msr_ir == 0)
310         || (access_type != ACCESS_CODE && msr_dr == 0);
311
312     if (real_mode) {
313         ctx->raddr = eaddr;
314         ctx->prot = PAGE_READ | PAGE_EXEC | PAGE_WRITE;
315         return 0;
316     } else {
317         int ret = -1;
318
319         /* Try to find a BAT */
320         if (env->nb_BATs != 0) {
321             ret = get_bat(env, ctx, eaddr, rw, access_type);
322         }
323         if (ret < 0) {
324             /* We didn't match any BAT entry or don't have BATs */
325             ret = get_segment32(env, ctx, eaddr, rw, access_type);
326         }
327         return ret;
328     }
329 }
This page took 0.043203 seconds and 4 git commands to generate.