]>
Commit | Line | Data |
---|---|---|
9d7c3f4a DG |
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 | |
98132796 | 28 | //#define DEBUG_BAT |
9d7c3f4a DG |
29 | |
30 | #ifdef DEBUG_MMU | |
31 | # define LOG_MMU(...) qemu_log(__VA_ARGS__) | |
32 | # define LOG_MMU_STATE(env) log_cpu_state((env), 0) | |
33 | #else | |
34 | # define LOG_MMU(...) do { } while (0) | |
35 | # define LOG_MMU_STATE(...) do { } while (0) | |
36 | #endif | |
37 | ||
98132796 DG |
38 | #ifdef DEBUG_BATS |
39 | # define LOG_BATS(...) qemu_log(__VA_ARGS__) | |
40 | #else | |
41 | # define LOG_BATS(...) do { } while (0) | |
42 | #endif | |
43 | ||
5dc68eb0 DG |
44 | struct mmu_ctx_hash32 { |
45 | hwaddr raddr; /* Real address */ | |
46 | hwaddr eaddr; /* Effective address */ | |
47 | int prot; /* Protection bits */ | |
48 | hwaddr hash[2]; /* Pagetable hash values */ | |
49 | target_ulong ptem; /* Virtual segment ID | API */ | |
50 | int key; /* Access key */ | |
51 | int nx; /* Non-execute area */ | |
52 | }; | |
53 | ||
9d7c3f4a DG |
54 | #define PTE_PTEM_MASK 0x7FFFFFBF |
55 | #define PTE_CHECK_MASK (TARGET_PAGE_MASK | 0x7B) | |
56 | ||
496272a7 DG |
57 | static int ppc_hash32_pp_check(int key, int pp, int nx) |
58 | { | |
59 | int access; | |
60 | ||
61 | /* Compute access rights */ | |
62 | access = 0; | |
63 | if (key == 0) { | |
64 | switch (pp) { | |
65 | case 0x0: | |
66 | case 0x1: | |
67 | case 0x2: | |
68 | access |= PAGE_WRITE; | |
69 | /* No break here */ | |
70 | case 0x3: | |
71 | access |= PAGE_READ; | |
72 | break; | |
73 | } | |
74 | } else { | |
75 | switch (pp) { | |
76 | case 0x0: | |
77 | access = 0; | |
78 | break; | |
79 | case 0x1: | |
80 | case 0x3: | |
81 | access = PAGE_READ; | |
82 | break; | |
83 | case 0x2: | |
84 | access = PAGE_READ | PAGE_WRITE; | |
85 | break; | |
86 | } | |
87 | } | |
88 | if (nx == 0) { | |
89 | access |= PAGE_EXEC; | |
90 | } | |
91 | ||
92 | return access; | |
93 | } | |
94 | ||
95 | static int ppc_hash32_check_prot(int prot, int rw, int access_type) | |
96 | { | |
97 | int ret; | |
98 | ||
99 | if (access_type == ACCESS_CODE) { | |
100 | if (prot & PAGE_EXEC) { | |
101 | ret = 0; | |
102 | } else { | |
103 | ret = -2; | |
104 | } | |
105 | } else if (rw) { | |
106 | if (prot & PAGE_WRITE) { | |
107 | ret = 0; | |
108 | } else { | |
109 | ret = -2; | |
110 | } | |
111 | } else { | |
112 | if (prot & PAGE_READ) { | |
113 | ret = 0; | |
114 | } else { | |
115 | ret = -2; | |
116 | } | |
117 | } | |
118 | ||
119 | return ret; | |
120 | } | |
121 | ||
98132796 DG |
122 | /* Perform BAT hit & translation */ |
123 | static void hash32_bat_size_prot(CPUPPCState *env, target_ulong *blp, | |
124 | int *validp, int *protp, target_ulong *BATu, | |
125 | target_ulong *BATl) | |
126 | { | |
127 | target_ulong bl; | |
128 | int pp, valid, prot; | |
129 | ||
130 | bl = (*BATu & 0x00001FFC) << 15; | |
131 | valid = 0; | |
132 | prot = 0; | |
133 | if (((msr_pr == 0) && (*BATu & 0x00000002)) || | |
134 | ((msr_pr != 0) && (*BATu & 0x00000001))) { | |
135 | valid = 1; | |
136 | pp = *BATl & 0x00000003; | |
137 | if (pp != 0) { | |
138 | prot = PAGE_READ | PAGE_EXEC; | |
139 | if (pp == 0x2) { | |
140 | prot |= PAGE_WRITE; | |
141 | } | |
142 | } | |
143 | } | |
144 | *blp = bl; | |
145 | *validp = valid; | |
146 | *protp = prot; | |
147 | } | |
148 | ||
149 | static void hash32_bat_601_size_prot(CPUPPCState *env, target_ulong *blp, | |
150 | int *validp, int *protp, | |
151 | target_ulong *BATu, target_ulong *BATl) | |
152 | { | |
153 | target_ulong bl; | |
154 | int key, pp, valid, prot; | |
155 | ||
156 | bl = (*BATl & 0x0000003F) << 17; | |
157 | LOG_BATS("b %02x ==> bl " TARGET_FMT_lx " msk " TARGET_FMT_lx "\n", | |
158 | (uint8_t)(*BATl & 0x0000003F), bl, ~bl); | |
159 | prot = 0; | |
160 | valid = (*BATl >> 6) & 1; | |
161 | if (valid) { | |
162 | pp = *BATu & 0x00000003; | |
163 | if (msr_pr == 0) { | |
164 | key = (*BATu >> 3) & 1; | |
165 | } else { | |
166 | key = (*BATu >> 2) & 1; | |
167 | } | |
168 | prot = ppc_hash32_pp_check(key, pp, 0); | |
169 | } | |
170 | *blp = bl; | |
171 | *validp = valid; | |
172 | *protp = prot; | |
173 | } | |
174 | ||
5dc68eb0 | 175 | static int ppc_hash32_get_bat(CPUPPCState *env, struct mmu_ctx_hash32 *ctx, |
98132796 DG |
176 | target_ulong virtual, int rw, int type) |
177 | { | |
178 | target_ulong *BATlt, *BATut, *BATu, *BATl; | |
179 | target_ulong BEPIl, BEPIu, bl; | |
180 | int i, valid, prot; | |
181 | int ret = -1; | |
182 | ||
183 | LOG_BATS("%s: %cBAT v " TARGET_FMT_lx "\n", __func__, | |
184 | type == ACCESS_CODE ? 'I' : 'D', virtual); | |
185 | switch (type) { | |
186 | case ACCESS_CODE: | |
187 | BATlt = env->IBAT[1]; | |
188 | BATut = env->IBAT[0]; | |
189 | break; | |
190 | default: | |
191 | BATlt = env->DBAT[1]; | |
192 | BATut = env->DBAT[0]; | |
193 | break; | |
194 | } | |
195 | for (i = 0; i < env->nb_BATs; i++) { | |
196 | BATu = &BATut[i]; | |
197 | BATl = &BATlt[i]; | |
198 | BEPIu = *BATu & 0xF0000000; | |
199 | BEPIl = *BATu & 0x0FFE0000; | |
200 | if (unlikely(env->mmu_model == POWERPC_MMU_601)) { | |
201 | hash32_bat_601_size_prot(env, &bl, &valid, &prot, BATu, BATl); | |
202 | } else { | |
203 | hash32_bat_size_prot(env, &bl, &valid, &prot, BATu, BATl); | |
204 | } | |
205 | LOG_BATS("%s: %cBAT%d v " TARGET_FMT_lx " BATu " TARGET_FMT_lx | |
206 | " BATl " TARGET_FMT_lx "\n", __func__, | |
207 | type == ACCESS_CODE ? 'I' : 'D', i, virtual, *BATu, *BATl); | |
208 | if ((virtual & 0xF0000000) == BEPIu && | |
209 | ((virtual & 0x0FFE0000) & ~bl) == BEPIl) { | |
210 | /* BAT matches */ | |
211 | if (valid != 0) { | |
212 | /* Get physical address */ | |
213 | ctx->raddr = (*BATl & 0xF0000000) | | |
214 | ((virtual & 0x0FFE0000 & bl) | (*BATl & 0x0FFE0000)) | | |
215 | (virtual & 0x0001F000); | |
216 | /* Compute access rights */ | |
217 | ctx->prot = prot; | |
218 | ret = ppc_hash32_check_prot(ctx->prot, rw, type); | |
219 | if (ret == 0) { | |
220 | LOG_BATS("BAT %d match: r " TARGET_FMT_plx " prot=%c%c\n", | |
221 | i, ctx->raddr, ctx->prot & PAGE_READ ? 'R' : '-', | |
222 | ctx->prot & PAGE_WRITE ? 'W' : '-'); | |
223 | } | |
224 | break; | |
225 | } | |
226 | } | |
227 | } | |
228 | if (ret < 0) { | |
229 | #if defined(DEBUG_BATS) | |
230 | if (qemu_log_enabled()) { | |
231 | LOG_BATS("no BAT match for " TARGET_FMT_lx ":\n", virtual); | |
232 | for (i = 0; i < 4; i++) { | |
233 | BATu = &BATut[i]; | |
234 | BATl = &BATlt[i]; | |
235 | BEPIu = *BATu & 0xF0000000; | |
236 | BEPIl = *BATu & 0x0FFE0000; | |
237 | bl = (*BATu & 0x00001FFC) << 15; | |
238 | LOG_BATS("%s: %cBAT%d v " TARGET_FMT_lx " BATu " TARGET_FMT_lx | |
239 | " BATl " TARGET_FMT_lx "\n\t" TARGET_FMT_lx " " | |
240 | TARGET_FMT_lx " " TARGET_FMT_lx "\n", | |
241 | __func__, type == ACCESS_CODE ? 'I' : 'D', i, virtual, | |
242 | *BATu, *BATl, BEPIu, BEPIl, bl); | |
243 | } | |
244 | } | |
245 | #endif | |
246 | } | |
247 | /* No hit */ | |
248 | return ret; | |
249 | } | |
250 | ||
251 | ||
9d7c3f4a DG |
252 | static inline int pte_is_valid_hash32(target_ulong pte0) |
253 | { | |
254 | return pte0 & 0x80000000 ? 1 : 0; | |
255 | } | |
256 | ||
5dc68eb0 | 257 | static int pte_check_hash32(struct mmu_ctx_hash32 *ctx, target_ulong pte0, |
c69b6151 | 258 | target_ulong pte1, int h, int rw, int type) |
9d7c3f4a DG |
259 | { |
260 | target_ulong ptem, mmask; | |
261 | int access, ret, pteh, ptev, pp; | |
262 | ||
263 | ret = -1; | |
264 | /* Check validity and table match */ | |
265 | ptev = pte_is_valid_hash32(pte0); | |
266 | pteh = (pte0 >> 6) & 1; | |
267 | if (ptev && h == pteh) { | |
268 | /* Check vsid & api */ | |
269 | ptem = pte0 & PTE_PTEM_MASK; | |
270 | mmask = PTE_CHECK_MASK; | |
271 | pp = pte1 & 0x00000003; | |
272 | if (ptem == ctx->ptem) { | |
273 | if (ctx->raddr != (hwaddr)-1ULL) { | |
274 | /* all matches should have equal RPN, WIMG & PP */ | |
275 | if ((ctx->raddr & mmask) != (pte1 & mmask)) { | |
276 | qemu_log("Bad RPN/WIMG/PP\n"); | |
277 | return -3; | |
278 | } | |
279 | } | |
280 | /* Compute access rights */ | |
496272a7 | 281 | access = ppc_hash32_pp_check(ctx->key, pp, ctx->nx); |
9d7c3f4a DG |
282 | /* Keep the matching PTE informations */ |
283 | ctx->raddr = pte1; | |
284 | ctx->prot = access; | |
496272a7 | 285 | ret = ppc_hash32_check_prot(ctx->prot, rw, type); |
9d7c3f4a DG |
286 | if (ret == 0) { |
287 | /* Access granted */ | |
288 | LOG_MMU("PTE access granted !\n"); | |
289 | } else { | |
290 | /* Access right violation */ | |
291 | LOG_MMU("PTE access rejected\n"); | |
292 | } | |
293 | } | |
294 | } | |
295 | ||
296 | return ret; | |
297 | } | |
c69b6151 | 298 | |
5dc68eb0 | 299 | static int ppc_hash32_pte_update_flags(struct mmu_ctx_hash32 *ctx, target_ulong *pte1p, |
496272a7 DG |
300 | int ret, int rw) |
301 | { | |
302 | int store = 0; | |
303 | ||
304 | /* Update page flags */ | |
305 | if (!(*pte1p & 0x00000100)) { | |
306 | /* Update accessed flag */ | |
307 | *pte1p |= 0x00000100; | |
308 | store = 1; | |
309 | } | |
310 | if (!(*pte1p & 0x00000080)) { | |
311 | if (rw == 1 && ret == 0) { | |
312 | /* Update changed flag */ | |
313 | *pte1p |= 0x00000080; | |
314 | store = 1; | |
315 | } else { | |
316 | /* Force page fault for first write access */ | |
317 | ctx->prot &= ~PAGE_WRITE; | |
318 | } | |
319 | } | |
320 | ||
321 | return store; | |
322 | } | |
323 | ||
59191721 DG |
324 | hwaddr get_pteg_offset32(CPUPPCState *env, hwaddr hash) |
325 | { | |
326 | return (hash * HASH_PTE_SIZE_32 * 8) & env->htab_mask; | |
327 | } | |
328 | ||
c69b6151 | 329 | /* PTE table lookup */ |
5dc68eb0 | 330 | static int find_pte32(CPUPPCState *env, struct mmu_ctx_hash32 *ctx, int h, |
0480884f | 331 | int rw, int type, int target_page_bits) |
c69b6151 DG |
332 | { |
333 | hwaddr pteg_off; | |
334 | target_ulong pte0, pte1; | |
335 | int i, good = -1; | |
336 | int ret, r; | |
337 | ||
338 | ret = -1; /* No entry found */ | |
59191721 | 339 | pteg_off = get_pteg_offset32(env, ctx->hash[h]); |
c69b6151 DG |
340 | for (i = 0; i < 8; i++) { |
341 | if (env->external_htab) { | |
342 | pte0 = ldl_p(env->external_htab + pteg_off + (i * 8)); | |
343 | pte1 = ldl_p(env->external_htab + pteg_off + (i * 8) + 4); | |
344 | } else { | |
345 | pte0 = ldl_phys(env->htab_base + pteg_off + (i * 8)); | |
346 | pte1 = ldl_phys(env->htab_base + pteg_off + (i * 8) + 4); | |
347 | } | |
348 | r = pte_check_hash32(ctx, pte0, pte1, h, rw, type); | |
349 | LOG_MMU("Load pte from %08" HWADDR_PRIx " => " TARGET_FMT_lx " " | |
350 | TARGET_FMT_lx " %d %d %d " TARGET_FMT_lx "\n", | |
351 | pteg_off + (i * 8), pte0, pte1, (int)(pte0 >> 31), h, | |
352 | (int)((pte0 >> 6) & 1), ctx->ptem); | |
353 | switch (r) { | |
354 | case -3: | |
355 | /* PTE inconsistency */ | |
356 | return -1; | |
357 | case -2: | |
358 | /* Access violation */ | |
359 | ret = -2; | |
360 | good = i; | |
361 | break; | |
362 | case -1: | |
363 | default: | |
364 | /* No PTE match */ | |
365 | break; | |
366 | case 0: | |
367 | /* access granted */ | |
368 | /* XXX: we should go on looping to check all PTEs consistency | |
369 | * but if we can speed-up the whole thing as the | |
370 | * result would be undefined if PTEs are not consistent. | |
371 | */ | |
372 | ret = 0; | |
373 | good = i; | |
374 | goto done; | |
375 | } | |
376 | } | |
377 | if (good != -1) { | |
378 | done: | |
379 | LOG_MMU("found PTE at addr %08" HWADDR_PRIx " prot=%01x ret=%d\n", | |
380 | ctx->raddr, ctx->prot, ret); | |
381 | /* Update page flags */ | |
382 | pte1 = ctx->raddr; | |
496272a7 | 383 | if (ppc_hash32_pte_update_flags(ctx, &pte1, ret, rw) == 1) { |
c69b6151 DG |
384 | if (env->external_htab) { |
385 | stl_p(env->external_htab + pteg_off + (good * 8) + 4, | |
386 | pte1); | |
387 | } else { | |
388 | stl_phys_notdirty(env->htab_base + pteg_off + | |
389 | (good * 8) + 4, pte1); | |
390 | } | |
391 | } | |
392 | } | |
393 | ||
394 | /* We have a TLB that saves 4K pages, so let's | |
395 | * split a huge page to 4k chunks */ | |
396 | if (target_page_bits != TARGET_PAGE_BITS) { | |
397 | ctx->raddr |= (ctx->eaddr & ((1 << target_page_bits) - 1)) | |
398 | & TARGET_PAGE_MASK; | |
399 | } | |
400 | return ret; | |
401 | } | |
0480884f | 402 | |
5dc68eb0 | 403 | static int get_segment32(CPUPPCState *env, struct mmu_ctx_hash32 *ctx, |
629bd516 | 404 | target_ulong eaddr, int rw, int type) |
0480884f DG |
405 | { |
406 | hwaddr hash; | |
407 | target_ulong vsid; | |
408 | int ds, pr, target_page_bits; | |
409 | int ret, ret2; | |
410 | target_ulong sr, pgidx; | |
411 | ||
412 | pr = msr_pr; | |
413 | ctx->eaddr = eaddr; | |
414 | ||
415 | sr = env->sr[eaddr >> 28]; | |
416 | ctx->key = (((sr & 0x20000000) && (pr != 0)) || | |
417 | ((sr & 0x40000000) && (pr == 0))) ? 1 : 0; | |
418 | ds = sr & 0x80000000 ? 1 : 0; | |
419 | ctx->nx = sr & 0x10000000 ? 1 : 0; | |
420 | vsid = sr & 0x00FFFFFF; | |
421 | target_page_bits = TARGET_PAGE_BITS; | |
422 | LOG_MMU("Check segment v=" TARGET_FMT_lx " %d " TARGET_FMT_lx " nip=" | |
423 | TARGET_FMT_lx " lr=" TARGET_FMT_lx | |
424 | " ir=%d dr=%d pr=%d %d t=%d\n", | |
425 | eaddr, (int)(eaddr >> 28), sr, env->nip, env->lr, (int)msr_ir, | |
426 | (int)msr_dr, pr != 0 ? 1 : 0, rw, type); | |
427 | pgidx = (eaddr & ~SEGMENT_MASK_256M) >> target_page_bits; | |
428 | hash = vsid ^ pgidx; | |
429 | ctx->ptem = (vsid << 7) | (pgidx >> 10); | |
430 | ||
431 | LOG_MMU("pte segment: key=%d ds %d nx %d vsid " TARGET_FMT_lx "\n", | |
432 | ctx->key, ds, ctx->nx, vsid); | |
433 | ret = -1; | |
434 | if (!ds) { | |
435 | /* Check if instruction fetch is allowed, if needed */ | |
436 | if (type != ACCESS_CODE || ctx->nx == 0) { | |
437 | /* Page address translation */ | |
438 | LOG_MMU("htab_base " TARGET_FMT_plx " htab_mask " TARGET_FMT_plx | |
439 | " hash " TARGET_FMT_plx "\n", | |
440 | env->htab_base, env->htab_mask, hash); | |
441 | ctx->hash[0] = hash; | |
442 | ctx->hash[1] = ~hash; | |
443 | ||
444 | /* Initialize real address with an invalid value */ | |
445 | ctx->raddr = (hwaddr)-1ULL; | |
446 | LOG_MMU("0 htab=" TARGET_FMT_plx "/" TARGET_FMT_plx | |
447 | " vsid=" TARGET_FMT_lx " ptem=" TARGET_FMT_lx | |
448 | " hash=" TARGET_FMT_plx "\n", | |
449 | env->htab_base, env->htab_mask, vsid, ctx->ptem, | |
450 | ctx->hash[0]); | |
451 | /* Primary table lookup */ | |
452 | ret = find_pte32(env, ctx, 0, rw, type, target_page_bits); | |
453 | if (ret < 0) { | |
454 | /* Secondary table lookup */ | |
455 | LOG_MMU("1 htab=" TARGET_FMT_plx "/" TARGET_FMT_plx | |
456 | " vsid=" TARGET_FMT_lx " api=" TARGET_FMT_lx | |
457 | " hash=" TARGET_FMT_plx "\n", env->htab_base, | |
458 | env->htab_mask, vsid, ctx->ptem, ctx->hash[1]); | |
459 | ret2 = find_pte32(env, ctx, 1, rw, type, | |
460 | target_page_bits); | |
461 | if (ret2 != -1) { | |
462 | ret = ret2; | |
463 | } | |
464 | } | |
465 | #if defined(DUMP_PAGE_TABLES) | |
466 | if (qemu_log_enabled()) { | |
467 | hwaddr curaddr; | |
468 | uint32_t a0, a1, a2, a3; | |
469 | ||
470 | qemu_log("Page table: " TARGET_FMT_plx " len " TARGET_FMT_plx | |
471 | "\n", sdr, mask + 0x80); | |
472 | for (curaddr = sdr; curaddr < (sdr + mask + 0x80); | |
473 | curaddr += 16) { | |
474 | a0 = ldl_phys(curaddr); | |
475 | a1 = ldl_phys(curaddr + 4); | |
476 | a2 = ldl_phys(curaddr + 8); | |
477 | a3 = ldl_phys(curaddr + 12); | |
478 | if (a0 != 0 || a1 != 0 || a2 != 0 || a3 != 0) { | |
479 | qemu_log(TARGET_FMT_plx ": %08x %08x %08x %08x\n", | |
480 | curaddr, a0, a1, a2, a3); | |
481 | } | |
482 | } | |
483 | } | |
484 | #endif | |
485 | } else { | |
486 | LOG_MMU("No access allowed\n"); | |
487 | ret = -3; | |
488 | } | |
489 | } else { | |
490 | target_ulong sr; | |
491 | ||
492 | LOG_MMU("direct store...\n"); | |
493 | /* Direct-store segment : absolutely *BUGGY* for now */ | |
494 | ||
495 | /* Direct-store implies a 32-bit MMU. | |
496 | * Check the Segment Register's bus unit ID (BUID). | |
497 | */ | |
498 | sr = env->sr[eaddr >> 28]; | |
499 | if ((sr & 0x1FF00000) >> 20 == 0x07f) { | |
500 | /* Memory-forced I/O controller interface access */ | |
501 | /* If T=1 and BUID=x'07F', the 601 performs a memory access | |
502 | * to SR[28-31] LA[4-31], bypassing all protection mechanisms. | |
503 | */ | |
504 | ctx->raddr = ((sr & 0xF) << 28) | (eaddr & 0x0FFFFFFF); | |
505 | ctx->prot = PAGE_READ | PAGE_WRITE | PAGE_EXEC; | |
506 | return 0; | |
507 | } | |
508 | ||
509 | switch (type) { | |
510 | case ACCESS_INT: | |
511 | /* Integer load/store : only access allowed */ | |
512 | break; | |
513 | case ACCESS_CODE: | |
514 | /* No code fetch is allowed in direct-store areas */ | |
515 | return -4; | |
516 | case ACCESS_FLOAT: | |
517 | /* Floating point load/store */ | |
518 | return -4; | |
519 | case ACCESS_RES: | |
520 | /* lwarx, ldarx or srwcx. */ | |
521 | return -4; | |
522 | case ACCESS_CACHE: | |
523 | /* dcba, dcbt, dcbtst, dcbf, dcbi, dcbst, dcbz, or icbi */ | |
524 | /* Should make the instruction do no-op. | |
525 | * As it already do no-op, it's quite easy :-) | |
526 | */ | |
527 | ctx->raddr = eaddr; | |
528 | return 0; | |
529 | case ACCESS_EXT: | |
530 | /* eciwx or ecowx */ | |
531 | return -4; | |
532 | default: | |
533 | qemu_log("ERROR: instruction should not need " | |
534 | "address translation\n"); | |
535 | return -4; | |
536 | } | |
537 | if ((rw == 1 || ctx->key != 1) && (rw == 0 || ctx->key != 0)) { | |
538 | ctx->raddr = eaddr; | |
539 | ret = 2; | |
540 | } else { | |
541 | ret = -2; | |
542 | } | |
543 | } | |
544 | ||
545 | return ret; | |
546 | } | |
629bd516 | 547 | |
5dc68eb0 | 548 | static int ppc_hash32_get_physical_address(CPUPPCState *env, struct mmu_ctx_hash32 *ctx, |
f2ad6be8 DG |
549 | target_ulong eaddr, int rw, |
550 | int access_type) | |
629bd516 DG |
551 | { |
552 | bool real_mode = (access_type == ACCESS_CODE && msr_ir == 0) | |
553 | || (access_type != ACCESS_CODE && msr_dr == 0); | |
554 | ||
555 | if (real_mode) { | |
556 | ctx->raddr = eaddr; | |
557 | ctx->prot = PAGE_READ | PAGE_EXEC | PAGE_WRITE; | |
558 | return 0; | |
559 | } else { | |
560 | int ret = -1; | |
561 | ||
562 | /* Try to find a BAT */ | |
563 | if (env->nb_BATs != 0) { | |
98132796 | 564 | ret = ppc_hash32_get_bat(env, ctx, eaddr, rw, access_type); |
629bd516 DG |
565 | } |
566 | if (ret < 0) { | |
567 | /* We didn't match any BAT entry or don't have BATs */ | |
568 | ret = get_segment32(env, ctx, eaddr, rw, access_type); | |
569 | } | |
570 | return ret; | |
571 | } | |
572 | } | |
25de24ab | 573 | |
f2ad6be8 DG |
574 | hwaddr ppc_hash32_get_phys_page_debug(CPUPPCState *env, target_ulong addr) |
575 | { | |
5dc68eb0 | 576 | struct mmu_ctx_hash32 ctx; |
f2ad6be8 DG |
577 | |
578 | if (unlikely(ppc_hash32_get_physical_address(env, &ctx, addr, 0, ACCESS_INT) | |
579 | != 0)) { | |
580 | return -1; | |
581 | } | |
582 | ||
583 | return ctx.raddr & TARGET_PAGE_MASK; | |
584 | } | |
585 | ||
25de24ab DG |
586 | int ppc_hash32_handle_mmu_fault(CPUPPCState *env, target_ulong address, int rw, |
587 | int mmu_idx) | |
588 | { | |
5dc68eb0 | 589 | struct mmu_ctx_hash32 ctx; |
25de24ab DG |
590 | int access_type; |
591 | int ret = 0; | |
592 | ||
593 | if (rw == 2) { | |
594 | /* code access */ | |
595 | rw = 0; | |
596 | access_type = ACCESS_CODE; | |
597 | } else { | |
598 | /* data access */ | |
599 | access_type = env->access_type; | |
600 | } | |
601 | ret = ppc_hash32_get_physical_address(env, &ctx, address, rw, access_type); | |
602 | if (ret == 0) { | |
603 | tlb_set_page(env, address & TARGET_PAGE_MASK, | |
604 | ctx.raddr & TARGET_PAGE_MASK, ctx.prot, | |
605 | mmu_idx, TARGET_PAGE_SIZE); | |
606 | ret = 0; | |
607 | } else if (ret < 0) { | |
608 | LOG_MMU_STATE(env); | |
609 | if (access_type == ACCESS_CODE) { | |
610 | switch (ret) { | |
611 | case -1: | |
612 | /* No matches in page tables or TLB */ | |
613 | env->exception_index = POWERPC_EXCP_ISI; | |
614 | env->error_code = 0x40000000; | |
615 | break; | |
616 | case -2: | |
617 | /* Access rights violation */ | |
618 | env->exception_index = POWERPC_EXCP_ISI; | |
619 | env->error_code = 0x08000000; | |
620 | break; | |
621 | case -3: | |
622 | /* No execute protection violation */ | |
623 | env->exception_index = POWERPC_EXCP_ISI; | |
624 | env->error_code = 0x10000000; | |
625 | break; | |
626 | case -4: | |
627 | /* Direct store exception */ | |
628 | /* No code fetch is allowed in direct-store areas */ | |
629 | env->exception_index = POWERPC_EXCP_ISI; | |
630 | env->error_code = 0x10000000; | |
631 | break; | |
632 | } | |
633 | } else { | |
634 | switch (ret) { | |
635 | case -1: | |
636 | /* No matches in page tables or TLB */ | |
637 | env->exception_index = POWERPC_EXCP_DSI; | |
638 | env->error_code = 0; | |
639 | env->spr[SPR_DAR] = address; | |
640 | if (rw == 1) { | |
641 | env->spr[SPR_DSISR] = 0x42000000; | |
642 | } else { | |
643 | env->spr[SPR_DSISR] = 0x40000000; | |
644 | } | |
645 | break; | |
646 | case -2: | |
647 | /* Access rights violation */ | |
648 | env->exception_index = POWERPC_EXCP_DSI; | |
649 | env->error_code = 0; | |
650 | env->spr[SPR_DAR] = address; | |
651 | if (rw == 1) { | |
652 | env->spr[SPR_DSISR] = 0x0A000000; | |
653 | } else { | |
654 | env->spr[SPR_DSISR] = 0x08000000; | |
655 | } | |
656 | break; | |
657 | case -4: | |
658 | /* Direct store exception */ | |
659 | switch (access_type) { | |
660 | case ACCESS_FLOAT: | |
661 | /* Floating point load/store */ | |
662 | env->exception_index = POWERPC_EXCP_ALIGN; | |
663 | env->error_code = POWERPC_EXCP_ALIGN_FP; | |
664 | env->spr[SPR_DAR] = address; | |
665 | break; | |
666 | case ACCESS_RES: | |
667 | /* lwarx, ldarx or stwcx. */ | |
668 | env->exception_index = POWERPC_EXCP_DSI; | |
669 | env->error_code = 0; | |
670 | env->spr[SPR_DAR] = address; | |
671 | if (rw == 1) { | |
672 | env->spr[SPR_DSISR] = 0x06000000; | |
673 | } else { | |
674 | env->spr[SPR_DSISR] = 0x04000000; | |
675 | } | |
676 | break; | |
677 | case ACCESS_EXT: | |
678 | /* eciwx or ecowx */ | |
679 | env->exception_index = POWERPC_EXCP_DSI; | |
680 | env->error_code = 0; | |
681 | env->spr[SPR_DAR] = address; | |
682 | if (rw == 1) { | |
683 | env->spr[SPR_DSISR] = 0x06100000; | |
684 | } else { | |
685 | env->spr[SPR_DSISR] = 0x04100000; | |
686 | } | |
687 | break; | |
688 | default: | |
689 | printf("DSI: invalid exception (%d)\n", ret); | |
690 | env->exception_index = POWERPC_EXCP_PROGRAM; | |
691 | env->error_code = | |
692 | POWERPC_EXCP_INVAL | POWERPC_EXCP_INVAL_INVAL; | |
693 | env->spr[SPR_DAR] = address; | |
694 | break; | |
695 | } | |
696 | break; | |
697 | } | |
698 | } | |
699 | #if 0 | |
700 | printf("%s: set exception to %d %02x\n", __func__, | |
701 | env->exception, env->error_code); | |
702 | #endif | |
703 | ret = 1; | |
704 | } | |
705 | ||
706 | return ret; | |
707 | } |