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