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