mmu-hash32: Remove odd pointer usage from BAT code
[qemu/ar7.git] / target-ppc / mmu-hash32.c
blobb7f6e8f2be6d019ca83657f56ba46368f18dc3b5
1 /*
2 * PowerPC MMU, TLB and BAT emulation helpers for QEMU.
4 * Copyright (c) 2003-2007 Jocelyn Mayer
5 * Copyright (c) 2013 David Gibson, IBM Corporation
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.
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.
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/>.
21 #include "cpu.h"
22 #include "helper.h"
23 #include "sysemu/kvm.h"
24 #include "kvm_ppc.h"
25 #include "mmu-hash32.h"
27 //#define DEBUG_MMU
28 //#define DEBUG_BAT
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
38 #ifdef DEBUG_BATS
39 # define LOG_BATS(...) qemu_log(__VA_ARGS__)
40 #else
41 # define LOG_BATS(...) do { } while (0)
42 #endif
44 struct mmu_ctx_hash32 {
45 hwaddr raddr; /* Real address */
46 int prot; /* Protection bits */
47 int key; /* Access key */
48 int nx; /* Non-execute area */
51 static int ppc_hash32_pp_check(int key, int pp, int nx)
53 int access;
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;
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;
82 if (nx == 0) {
83 access |= PAGE_EXEC;
86 return access;
89 static int ppc_hash32_check_prot(int prot, int rwx)
91 int ret;
93 if (rwx == 2) {
94 if (prot & PAGE_EXEC) {
95 ret = 0;
96 } else {
97 ret = -2;
99 } else if (rwx) {
100 if (prot & PAGE_WRITE) {
101 ret = 0;
102 } else {
103 ret = -2;
105 } else {
106 if (prot & PAGE_READ) {
107 ret = 0;
108 } else {
109 ret = -2;
113 return ret;
116 /* Perform BAT hit & translation */
117 static void hash32_bat_size_prot(CPUPPCState *env, target_ulong *blp,
118 int *validp, int *protp,
119 target_ulong batu, target_ulong batl)
121 target_ulong bl;
122 int pp, valid, prot;
124 bl = (batu & BATU32_BL) << 15;
125 valid = 0;
126 prot = 0;
127 if (((msr_pr == 0) && (batu & BATU32_VS)) ||
128 ((msr_pr != 0) && (batu & BATU32_VP))) {
129 valid = 1;
130 pp = batl & BATL32_PP;
131 if (pp != 0) {
132 prot = PAGE_READ | PAGE_EXEC;
133 if (pp == 0x2) {
134 prot |= PAGE_WRITE;
138 *blp = bl;
139 *validp = valid;
140 *protp = prot;
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)
147 target_ulong bl;
148 int key, pp, valid, prot;
150 bl = (batl & BATL32_601_BL) << 17;
151 LOG_BATS("b %02x ==> bl " TARGET_FMT_lx " msk " TARGET_FMT_lx "\n",
152 (uint8_t)(batl & BATL32_601_BL), bl, ~bl);
153 prot = 0;
154 valid = !!(batl & BATL32_601_V);
155 if (valid) {
156 pp = batu & BATU32_601_PP;
157 if (msr_pr == 0) {
158 key = !!(batu & BATU32_601_KS);
159 } else {
160 key = !!(batu & BATU32_601_KP);
162 prot = ppc_hash32_pp_check(key, pp, 0);
164 *blp = bl;
165 *validp = valid;
166 *protp = prot;
169 static int ppc_hash32_get_bat(CPUPPCState *env, struct mmu_ctx_hash32 *ctx,
170 target_ulong virtual, int rwx)
172 target_ulong *BATlt, *BATut;
173 target_ulong BEPIl, BEPIu, bl;
174 int i, valid, prot;
175 int ret = -1;
177 LOG_BATS("%s: %cBAT v " TARGET_FMT_lx "\n", __func__,
178 rwx == 2 ? 'I' : 'D', virtual);
179 if (rwx == 2) {
180 BATlt = env->IBAT[1];
181 BATut = env->IBAT[0];
182 } else {
183 BATlt = env->DBAT[1];
184 BATut = env->DBAT[0];
186 for (i = 0; i < env->nb_BATs; i++) {
187 target_ulong batu = BATut[i];
188 target_ulong batl = BATlt[i];
190 BEPIu = batu & BATU32_BEPIU;
191 BEPIl = batu & BATU32_BEPIL;
192 if (unlikely(env->mmu_model == POWERPC_MMU_601)) {
193 hash32_bat_601_size_prot(env, &bl, &valid, &prot, batu, batl);
194 } else {
195 hash32_bat_size_prot(env, &bl, &valid, &prot, batu, batl);
197 LOG_BATS("%s: %cBAT%d v " TARGET_FMT_lx " BATu " TARGET_FMT_lx
198 " BATl " TARGET_FMT_lx "\n", __func__,
199 type == ACCESS_CODE ? 'I' : 'D', i, virtual, batu, batl);
200 if ((virtual & BATU32_BEPIU) == BEPIu &&
201 ((virtual & BATU32_BEPIL) & ~bl) == BEPIl) {
202 /* BAT matches */
203 if (valid != 0) {
204 /* Get physical address */
205 ctx->raddr = (batl & BATL32_BRPNU) |
206 ((virtual & BATU32_BEPIL & bl) | (batl & BATL32_BRPNL)) |
207 (virtual & 0x0001F000);
208 /* Compute access rights */
209 ctx->prot = prot;
210 ret = ppc_hash32_check_prot(ctx->prot, rwx);
211 if (ret == 0) {
212 LOG_BATS("BAT %d match: r " TARGET_FMT_plx " prot=%c%c\n",
213 i, ctx->raddr, ctx->prot & PAGE_READ ? 'R' : '-',
214 ctx->prot & PAGE_WRITE ? 'W' : '-');
216 break;
220 if (ret < 0) {
221 #if defined(DEBUG_BATS)
222 if (qemu_log_enabled()) {
223 LOG_BATS("no BAT match for " TARGET_FMT_lx ":\n", virtual);
224 for (i = 0; i < 4; i++) {
225 BATu = &BATut[i];
226 BATl = &BATlt[i];
227 BEPIu = *BATu & BATU32_BEPIU;
228 BEPIl = *BATu & BATU32_BEPIL;
229 bl = (*BATu & 0x00001FFC) << 15;
230 LOG_BATS("%s: %cBAT%d v " TARGET_FMT_lx " BATu " TARGET_FMT_lx
231 " BATl " TARGET_FMT_lx "\n\t" TARGET_FMT_lx " "
232 TARGET_FMT_lx " " TARGET_FMT_lx "\n",
233 __func__, type == ACCESS_CODE ? 'I' : 'D', i, virtual,
234 *BATu, *BATl, BEPIu, BEPIl, bl);
237 #endif
239 /* No hit */
240 return ret;
243 static int ppc_hash32_direct_store(CPUPPCState *env, target_ulong sr,
244 target_ulong eaddr, int rwx,
245 hwaddr *raddr, int *prot)
247 int key = !!(msr_pr ? (sr & SR32_KP) : (sr & SR32_KS));
249 LOG_MMU("direct store...\n");
251 if ((sr & 0x1FF00000) >> 20 == 0x07f) {
252 /* Memory-forced I/O controller interface access */
253 /* If T=1 and BUID=x'07F', the 601 performs a memory access
254 * to SR[28-31] LA[4-31], bypassing all protection mechanisms.
256 *raddr = ((sr & 0xF) << 28) | (eaddr & 0x0FFFFFFF);
257 *prot = PAGE_READ | PAGE_WRITE | PAGE_EXEC;
258 return 0;
261 if (rwx == 2) {
262 /* No code fetch is allowed in direct-store areas */
263 return -4;
266 switch (env->access_type) {
267 case ACCESS_INT:
268 /* Integer load/store : only access allowed */
269 break;
270 case ACCESS_FLOAT:
271 /* Floating point load/store */
272 return -4;
273 case ACCESS_RES:
274 /* lwarx, ldarx or srwcx. */
275 return -4;
276 case ACCESS_CACHE:
277 /* dcba, dcbt, dcbtst, dcbf, dcbi, dcbst, dcbz, or icbi */
278 /* Should make the instruction do no-op.
279 * As it already do no-op, it's quite easy :-)
281 *raddr = eaddr;
282 return 0;
283 case ACCESS_EXT:
284 /* eciwx or ecowx */
285 return -4;
286 default:
287 qemu_log("ERROR: instruction should not need "
288 "address translation\n");
289 return -4;
291 if ((rwx == 1 || key != 1) && (rwx == 0 || key != 0)) {
292 *raddr = eaddr;
293 return 2;
294 } else {
295 return -2;
299 static int ppc_hash32_pte_update_flags(struct mmu_ctx_hash32 *ctx, uint32_t *pte1p,
300 int ret, int rwx)
302 int store = 0;
304 /* Update page flags */
305 if (!(*pte1p & HPTE32_R_R)) {
306 /* Update accessed flag */
307 *pte1p |= HPTE32_R_R;
308 store = 1;
310 if (!(*pte1p & HPTE32_R_C)) {
311 if (rwx == 1 && ret == 0) {
312 /* Update changed flag */
313 *pte1p |= HPTE32_R_C;
314 store = 1;
315 } else {
316 /* Force page fault for first write access */
317 ctx->prot &= ~PAGE_WRITE;
321 return store;
324 hwaddr get_pteg_offset32(CPUPPCState *env, hwaddr hash)
326 return (hash * HASH_PTEG_SIZE_32) & env->htab_mask;
329 static hwaddr ppc_hash32_pteg_search(CPUPPCState *env, hwaddr pteg_off,
330 bool secondary, target_ulong ptem,
331 ppc_hash_pte32_t *pte)
333 hwaddr pte_offset = pteg_off;
334 target_ulong pte0, pte1;
335 int i;
337 for (i = 0; i < HPTES_PER_GROUP; i++) {
338 pte0 = ppc_hash32_load_hpte0(env, pte_offset);
339 pte1 = ppc_hash32_load_hpte1(env, pte_offset);
341 if ((pte0 & HPTE32_V_VALID)
342 && (secondary == !!(pte0 & HPTE32_V_SECONDARY))
343 && HPTE32_V_COMPARE(pte0, ptem)) {
344 pte->pte0 = pte0;
345 pte->pte1 = pte1;
346 return pte_offset;
349 pte_offset += HASH_PTE_SIZE_32;
352 return -1;
355 static hwaddr ppc_hash32_htab_lookup(CPUPPCState *env,
356 target_ulong sr, target_ulong eaddr,
357 ppc_hash_pte32_t *pte)
359 hwaddr pteg_off, pte_offset;
360 hwaddr hash;
361 uint32_t vsid, pgidx, ptem;
363 vsid = sr & SR32_VSID;
364 pgidx = (eaddr & ~SEGMENT_MASK_256M) >> TARGET_PAGE_BITS;
365 hash = vsid ^ pgidx;
366 ptem = (vsid << 7) | (pgidx >> 10);
368 /* Page address translation */
369 LOG_MMU("htab_base " TARGET_FMT_plx " htab_mask " TARGET_FMT_plx
370 " hash " TARGET_FMT_plx "\n",
371 env->htab_base, env->htab_mask, hash);
373 /* Primary PTEG lookup */
374 LOG_MMU("0 htab=" TARGET_FMT_plx "/" TARGET_FMT_plx
375 " vsid=%" PRIx32 " ptem=%" PRIx32
376 " hash=" TARGET_FMT_plx "\n",
377 env->htab_base, env->htab_mask, vsid, ptem, hash);
378 pteg_off = get_pteg_offset32(env, hash);
379 pte_offset = ppc_hash32_pteg_search(env, pteg_off, 0, ptem, pte);
380 if (pte_offset == -1) {
381 /* Secondary PTEG lookup */
382 LOG_MMU("1 htab=" TARGET_FMT_plx "/" TARGET_FMT_plx
383 " vsid=%" PRIx32 " api=%" PRIx32
384 " hash=" TARGET_FMT_plx "\n", env->htab_base,
385 env->htab_mask, vsid, ptem, ~hash);
386 pteg_off = get_pteg_offset32(env, ~hash);
387 pte_offset = ppc_hash32_pteg_search(env, pteg_off, 1, ptem, pte);
390 return pte_offset;
393 static int ppc_hash32_translate(CPUPPCState *env, struct mmu_ctx_hash32 *ctx,
394 target_ulong eaddr, int rwx)
396 int ret;
397 target_ulong sr;
398 hwaddr pte_offset;
399 ppc_hash_pte32_t pte;
401 assert((rwx == 0) || (rwx == 1) || (rwx == 2));
403 /* 1. Handle real mode accesses */
404 if (((rwx == 2) && (msr_ir == 0)) || ((rwx != 2) && (msr_dr == 0))) {
405 /* Translation is off */
406 ctx->raddr = eaddr;
407 ctx->prot = PAGE_READ | PAGE_EXEC | PAGE_WRITE;
408 return 0;
411 /* 2. Check Block Address Translation entries (BATs) */
412 if (env->nb_BATs != 0) {
413 ret = ppc_hash32_get_bat(env, ctx, eaddr, rwx);
414 if (ret == 0) {
415 return 0;
419 /* 3. Look up the Segment Register */
420 sr = env->sr[eaddr >> 28];
422 /* 4. Handle direct store segments */
423 if (sr & SR32_T) {
424 return ppc_hash32_direct_store(env, sr, eaddr, rwx,
425 &ctx->raddr, &ctx->prot);
428 /* 5. Check for segment level no-execute violation */
429 ctx->nx = !!(sr & SR32_NX);
430 if ((rwx == 2) && ctx->nx) {
431 return -3;
434 /* 6. Locate the PTE in the hash table */
435 pte_offset = ppc_hash32_htab_lookup(env, sr, eaddr, &pte);
436 if (pte_offset == -1) {
437 return -1;
439 LOG_MMU("found PTE at offset %08" HWADDR_PRIx "\n", pte_offset);
441 /* 7. Check access permissions */
442 ctx->key = (((sr & SR32_KP) && (msr_pr != 0)) ||
443 ((sr & SR32_KS) && (msr_pr == 0))) ? 1 : 0;
445 int access, pp;
447 pp = pte.pte1 & HPTE32_R_PP;
448 /* Compute access rights */
449 access = ppc_hash32_pp_check(ctx->key, pp, ctx->nx);
450 /* Keep the matching PTE informations */
451 ctx->raddr = pte.pte1;
452 ctx->prot = access;
453 ret = ppc_hash32_check_prot(ctx->prot, rwx);
454 if (ret == 0) {
455 /* Access granted */
456 LOG_MMU("PTE access granted !\n");
457 } else {
458 /* Access right violation */
459 LOG_MMU("PTE access rejected\n");
462 /* Update page flags */
463 if (ppc_hash32_pte_update_flags(ctx, &pte.pte1, ret, rwx) == 1) {
464 ppc_hash32_store_hpte1(env, pte_offset, pte.pte1);
467 return ret;
470 hwaddr ppc_hash32_get_phys_page_debug(CPUPPCState *env, target_ulong addr)
472 struct mmu_ctx_hash32 ctx;
474 /* FIXME: Will not behave sanely for direct store segments, but
475 * they're almost never used */
476 if (unlikely(ppc_hash32_translate(env, &ctx, addr, 0)
477 != 0)) {
478 return -1;
481 return ctx.raddr & TARGET_PAGE_MASK;
484 int ppc_hash32_handle_mmu_fault(CPUPPCState *env, target_ulong address, int rwx,
485 int mmu_idx)
487 struct mmu_ctx_hash32 ctx;
488 int ret = 0;
490 ret = ppc_hash32_translate(env, &ctx, address, rwx);
491 if (ret == 0) {
492 tlb_set_page(env, address & TARGET_PAGE_MASK,
493 ctx.raddr & TARGET_PAGE_MASK, ctx.prot,
494 mmu_idx, TARGET_PAGE_SIZE);
495 ret = 0;
496 } else if (ret < 0) {
497 LOG_MMU_STATE(env);
498 if (rwx == 2) {
499 switch (ret) {
500 case -1:
501 /* No matches in page tables or TLB */
502 env->exception_index = POWERPC_EXCP_ISI;
503 env->error_code = 0x40000000;
504 break;
505 case -2:
506 /* Access rights violation */
507 env->exception_index = POWERPC_EXCP_ISI;
508 env->error_code = 0x08000000;
509 break;
510 case -3:
511 /* No execute protection violation */
512 env->exception_index = POWERPC_EXCP_ISI;
513 env->error_code = 0x10000000;
514 break;
515 case -4:
516 /* Direct store exception */
517 /* No code fetch is allowed in direct-store areas */
518 env->exception_index = POWERPC_EXCP_ISI;
519 env->error_code = 0x10000000;
520 break;
522 } else {
523 switch (ret) {
524 case -1:
525 /* No matches in page tables or TLB */
526 env->exception_index = POWERPC_EXCP_DSI;
527 env->error_code = 0;
528 env->spr[SPR_DAR] = address;
529 if (rwx == 1) {
530 env->spr[SPR_DSISR] = 0x42000000;
531 } else {
532 env->spr[SPR_DSISR] = 0x40000000;
534 break;
535 case -2:
536 /* Access rights violation */
537 env->exception_index = POWERPC_EXCP_DSI;
538 env->error_code = 0;
539 env->spr[SPR_DAR] = address;
540 if (rwx == 1) {
541 env->spr[SPR_DSISR] = 0x0A000000;
542 } else {
543 env->spr[SPR_DSISR] = 0x08000000;
545 break;
546 case -4:
547 /* Direct store exception */
548 switch (env->access_type) {
549 case ACCESS_FLOAT:
550 /* Floating point load/store */
551 env->exception_index = POWERPC_EXCP_ALIGN;
552 env->error_code = POWERPC_EXCP_ALIGN_FP;
553 env->spr[SPR_DAR] = address;
554 break;
555 case ACCESS_RES:
556 /* lwarx, ldarx or stwcx. */
557 env->exception_index = POWERPC_EXCP_DSI;
558 env->error_code = 0;
559 env->spr[SPR_DAR] = address;
560 if (rwx == 1) {
561 env->spr[SPR_DSISR] = 0x06000000;
562 } else {
563 env->spr[SPR_DSISR] = 0x04000000;
565 break;
566 case ACCESS_EXT:
567 /* eciwx or ecowx */
568 env->exception_index = POWERPC_EXCP_DSI;
569 env->error_code = 0;
570 env->spr[SPR_DAR] = address;
571 if (rwx == 1) {
572 env->spr[SPR_DSISR] = 0x06100000;
573 } else {
574 env->spr[SPR_DSISR] = 0x04100000;
576 break;
577 default:
578 printf("DSI: invalid exception (%d)\n", ret);
579 env->exception_index = POWERPC_EXCP_PROGRAM;
580 env->error_code =
581 POWERPC_EXCP_INVAL | POWERPC_EXCP_INVAL_INVAL;
582 env->spr[SPR_DAR] = address;
583 break;
585 break;
588 #if 0
589 printf("%s: set exception to %d %02x\n", __func__,
590 env->exception, env->error_code);
591 #endif
592 ret = 1;
595 return ret;