Statistics
| Branch: | Revision:

root / target-ppc / mmu-hash32.c @ 496272a7

History | View | Annotate | Download (17.6 kB)

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 int ppc_hash32_pp_check(int key, int pp, int nx)
41
{
42
    int access;
43

    
44
    /* Compute access rights */
45
    access = 0;
46
    if (key == 0) {
47
        switch (pp) {
48
        case 0x0:
49
        case 0x1:
50
        case 0x2:
51
            access |= PAGE_WRITE;
52
            /* No break here */
53
        case 0x3:
54
            access |= PAGE_READ;
55
            break;
56
        }
57
    } else {
58
        switch (pp) {
59
        case 0x0:
60
            access = 0;
61
            break;
62
        case 0x1:
63
        case 0x3:
64
            access = PAGE_READ;
65
            break;
66
        case 0x2:
67
            access = PAGE_READ | PAGE_WRITE;
68
            break;
69
        }
70
    }
71
    if (nx == 0) {
72
        access |= PAGE_EXEC;
73
    }
74

    
75
    return access;
76
}
77

    
78
static int ppc_hash32_check_prot(int prot, int rw, int access_type)
79
{
80
    int ret;
81

    
82
    if (access_type == ACCESS_CODE) {
83
        if (prot & PAGE_EXEC) {
84
            ret = 0;
85
        } else {
86
            ret = -2;
87
        }
88
    } else if (rw) {
89
        if (prot & PAGE_WRITE) {
90
            ret = 0;
91
        } else {
92
            ret = -2;
93
        }
94
    } else {
95
        if (prot & PAGE_READ) {
96
            ret = 0;
97
        } else {
98
            ret = -2;
99
        }
100
    }
101

    
102
    return ret;
103
}
104

    
105
static inline int pte_is_valid_hash32(target_ulong pte0)
106
{
107
    return pte0 & 0x80000000 ? 1 : 0;
108
}
109

    
110
static int pte_check_hash32(mmu_ctx_t *ctx, target_ulong pte0,
111
                            target_ulong pte1, int h, int rw, int type)
112
{
113
    target_ulong ptem, mmask;
114
    int access, ret, pteh, ptev, pp;
115

    
116
    ret = -1;
117
    /* Check validity and table match */
118
    ptev = pte_is_valid_hash32(pte0);
119
    pteh = (pte0 >> 6) & 1;
120
    if (ptev && h == pteh) {
121
        /* Check vsid & api */
122
        ptem = pte0 & PTE_PTEM_MASK;
123
        mmask = PTE_CHECK_MASK;
124
        pp = pte1 & 0x00000003;
125
        if (ptem == ctx->ptem) {
126
            if (ctx->raddr != (hwaddr)-1ULL) {
127
                /* all matches should have equal RPN, WIMG & PP */
128
                if ((ctx->raddr & mmask) != (pte1 & mmask)) {
129
                    qemu_log("Bad RPN/WIMG/PP\n");
130
                    return -3;
131
                }
132
            }
133
            /* Compute access rights */
134
            access = ppc_hash32_pp_check(ctx->key, pp, ctx->nx);
135
            /* Keep the matching PTE informations */
136
            ctx->raddr = pte1;
137
            ctx->prot = access;
138
            ret = ppc_hash32_check_prot(ctx->prot, rw, type);
139
            if (ret == 0) {
140
                /* Access granted */
141
                LOG_MMU("PTE access granted !\n");
142
            } else {
143
                /* Access right violation */
144
                LOG_MMU("PTE access rejected\n");
145
            }
146
        }
147
    }
148

    
149
    return ret;
150
}
151

    
152
static int ppc_hash32_pte_update_flags(mmu_ctx_t *ctx, target_ulong *pte1p,
153
                                       int ret, int rw)
154
{
155
    int store = 0;
156

    
157
    /* Update page flags */
158
    if (!(*pte1p & 0x00000100)) {
159
        /* Update accessed flag */
160
        *pte1p |= 0x00000100;
161
        store = 1;
162
    }
163
    if (!(*pte1p & 0x00000080)) {
164
        if (rw == 1 && ret == 0) {
165
            /* Update changed flag */
166
            *pte1p |= 0x00000080;
167
            store = 1;
168
        } else {
169
            /* Force page fault for first write access */
170
            ctx->prot &= ~PAGE_WRITE;
171
        }
172
    }
173

    
174
    return store;
175
}
176

    
177
/* PTE table lookup */
178
static int find_pte32(CPUPPCState *env, mmu_ctx_t *ctx, int h,
179
                      int rw, int type, int target_page_bits)
180
{
181
    hwaddr pteg_off;
182
    target_ulong pte0, pte1;
183
    int i, good = -1;
184
    int ret, r;
185

    
186
    ret = -1; /* No entry found */
187
    pteg_off = get_pteg_offset(env, ctx->hash[h], HASH_PTE_SIZE_32);
188
    for (i = 0; i < 8; i++) {
189
        if (env->external_htab) {
190
            pte0 = ldl_p(env->external_htab + pteg_off + (i * 8));
191
            pte1 = ldl_p(env->external_htab + pteg_off + (i * 8) + 4);
192
        } else {
193
            pte0 = ldl_phys(env->htab_base + pteg_off + (i * 8));
194
            pte1 = ldl_phys(env->htab_base + pteg_off + (i * 8) + 4);
195
        }
196
        r = pte_check_hash32(ctx, pte0, pte1, h, rw, type);
197
        LOG_MMU("Load pte from %08" HWADDR_PRIx " => " TARGET_FMT_lx " "
198
                TARGET_FMT_lx " %d %d %d " TARGET_FMT_lx "\n",
199
                pteg_off + (i * 8), pte0, pte1, (int)(pte0 >> 31), h,
200
                (int)((pte0 >> 6) & 1), ctx->ptem);
201
        switch (r) {
202
        case -3:
203
            /* PTE inconsistency */
204
            return -1;
205
        case -2:
206
            /* Access violation */
207
            ret = -2;
208
            good = i;
209
            break;
210
        case -1:
211
        default:
212
            /* No PTE match */
213
            break;
214
        case 0:
215
            /* access granted */
216
            /* XXX: we should go on looping to check all PTEs consistency
217
             *      but if we can speed-up the whole thing as the
218
             *      result would be undefined if PTEs are not consistent.
219
             */
220
            ret = 0;
221
            good = i;
222
            goto done;
223
        }
224
    }
225
    if (good != -1) {
226
    done:
227
        LOG_MMU("found PTE at addr %08" HWADDR_PRIx " prot=%01x ret=%d\n",
228
                ctx->raddr, ctx->prot, ret);
229
        /* Update page flags */
230
        pte1 = ctx->raddr;
231
        if (ppc_hash32_pte_update_flags(ctx, &pte1, ret, rw) == 1) {
232
            if (env->external_htab) {
233
                stl_p(env->external_htab + pteg_off + (good * 8) + 4,
234
                      pte1);
235
            } else {
236
                stl_phys_notdirty(env->htab_base + pteg_off +
237
                                  (good * 8) + 4, pte1);
238
            }
239
        }
240
    }
241

    
242
    /* We have a TLB that saves 4K pages, so let's
243
     * split a huge page to 4k chunks */
244
    if (target_page_bits != TARGET_PAGE_BITS) {
245
        ctx->raddr |= (ctx->eaddr & ((1 << target_page_bits) - 1))
246
                      & TARGET_PAGE_MASK;
247
    }
248
    return ret;
249
}
250

    
251
static int get_segment32(CPUPPCState *env, mmu_ctx_t *ctx,
252
                         target_ulong eaddr, int rw, int type)
253
{
254
    hwaddr hash;
255
    target_ulong vsid;
256
    int ds, pr, target_page_bits;
257
    int ret, ret2;
258
    target_ulong sr, pgidx;
259

    
260
    pr = msr_pr;
261
    ctx->eaddr = eaddr;
262

    
263
    sr = env->sr[eaddr >> 28];
264
    ctx->key = (((sr & 0x20000000) && (pr != 0)) ||
265
                ((sr & 0x40000000) && (pr == 0))) ? 1 : 0;
266
    ds = sr & 0x80000000 ? 1 : 0;
267
    ctx->nx = sr & 0x10000000 ? 1 : 0;
268
    vsid = sr & 0x00FFFFFF;
269
    target_page_bits = TARGET_PAGE_BITS;
270
    LOG_MMU("Check segment v=" TARGET_FMT_lx " %d " TARGET_FMT_lx " nip="
271
            TARGET_FMT_lx " lr=" TARGET_FMT_lx
272
            " ir=%d dr=%d pr=%d %d t=%d\n",
273
            eaddr, (int)(eaddr >> 28), sr, env->nip, env->lr, (int)msr_ir,
274
            (int)msr_dr, pr != 0 ? 1 : 0, rw, type);
275
    pgidx = (eaddr & ~SEGMENT_MASK_256M) >> target_page_bits;
276
    hash = vsid ^ pgidx;
277
    ctx->ptem = (vsid << 7) | (pgidx >> 10);
278

    
279
    LOG_MMU("pte segment: key=%d ds %d nx %d vsid " TARGET_FMT_lx "\n",
280
            ctx->key, ds, ctx->nx, vsid);
281
    ret = -1;
282
    if (!ds) {
283
        /* Check if instruction fetch is allowed, if needed */
284
        if (type != ACCESS_CODE || ctx->nx == 0) {
285
            /* Page address translation */
286
            LOG_MMU("htab_base " TARGET_FMT_plx " htab_mask " TARGET_FMT_plx
287
                    " hash " TARGET_FMT_plx "\n",
288
                    env->htab_base, env->htab_mask, hash);
289
            ctx->hash[0] = hash;
290
            ctx->hash[1] = ~hash;
291

    
292
            /* Initialize real address with an invalid value */
293
            ctx->raddr = (hwaddr)-1ULL;
294
            LOG_MMU("0 htab=" TARGET_FMT_plx "/" TARGET_FMT_plx
295
                    " vsid=" TARGET_FMT_lx " ptem=" TARGET_FMT_lx
296
                    " hash=" TARGET_FMT_plx "\n",
297
                    env->htab_base, env->htab_mask, vsid, ctx->ptem,
298
                    ctx->hash[0]);
299
            /* Primary table lookup */
300
            ret = find_pte32(env, ctx, 0, rw, type, target_page_bits);
301
            if (ret < 0) {
302
                /* Secondary table lookup */
303
                LOG_MMU("1 htab=" TARGET_FMT_plx "/" TARGET_FMT_plx
304
                        " vsid=" TARGET_FMT_lx " api=" TARGET_FMT_lx
305
                        " hash=" TARGET_FMT_plx "\n", env->htab_base,
306
                        env->htab_mask, vsid, ctx->ptem, ctx->hash[1]);
307
                ret2 = find_pte32(env, ctx, 1, rw, type,
308
                                  target_page_bits);
309
                if (ret2 != -1) {
310
                    ret = ret2;
311
                }
312
            }
313
#if defined(DUMP_PAGE_TABLES)
314
            if (qemu_log_enabled()) {
315
                hwaddr curaddr;
316
                uint32_t a0, a1, a2, a3;
317

    
318
                qemu_log("Page table: " TARGET_FMT_plx " len " TARGET_FMT_plx
319
                         "\n", sdr, mask + 0x80);
320
                for (curaddr = sdr; curaddr < (sdr + mask + 0x80);
321
                     curaddr += 16) {
322
                    a0 = ldl_phys(curaddr);
323
                    a1 = ldl_phys(curaddr + 4);
324
                    a2 = ldl_phys(curaddr + 8);
325
                    a3 = ldl_phys(curaddr + 12);
326
                    if (a0 != 0 || a1 != 0 || a2 != 0 || a3 != 0) {
327
                        qemu_log(TARGET_FMT_plx ": %08x %08x %08x %08x\n",
328
                                 curaddr, a0, a1, a2, a3);
329
                    }
330
                }
331
            }
332
#endif
333
        } else {
334
            LOG_MMU("No access allowed\n");
335
            ret = -3;
336
        }
337
    } else {
338
        target_ulong sr;
339

    
340
        LOG_MMU("direct store...\n");
341
        /* Direct-store segment : absolutely *BUGGY* for now */
342

    
343
        /* Direct-store implies a 32-bit MMU.
344
         * Check the Segment Register's bus unit ID (BUID).
345
         */
346
        sr = env->sr[eaddr >> 28];
347
        if ((sr & 0x1FF00000) >> 20 == 0x07f) {
348
            /* Memory-forced I/O controller interface access */
349
            /* If T=1 and BUID=x'07F', the 601 performs a memory access
350
             * to SR[28-31] LA[4-31], bypassing all protection mechanisms.
351
             */
352
            ctx->raddr = ((sr & 0xF) << 28) | (eaddr & 0x0FFFFFFF);
353
            ctx->prot = PAGE_READ | PAGE_WRITE | PAGE_EXEC;
354
            return 0;
355
        }
356

    
357
        switch (type) {
358
        case ACCESS_INT:
359
            /* Integer load/store : only access allowed */
360
            break;
361
        case ACCESS_CODE:
362
            /* No code fetch is allowed in direct-store areas */
363
            return -4;
364
        case ACCESS_FLOAT:
365
            /* Floating point load/store */
366
            return -4;
367
        case ACCESS_RES:
368
            /* lwarx, ldarx or srwcx. */
369
            return -4;
370
        case ACCESS_CACHE:
371
            /* dcba, dcbt, dcbtst, dcbf, dcbi, dcbst, dcbz, or icbi */
372
            /* Should make the instruction do no-op.
373
             * As it already do no-op, it's quite easy :-)
374
             */
375
            ctx->raddr = eaddr;
376
            return 0;
377
        case ACCESS_EXT:
378
            /* eciwx or ecowx */
379
            return -4;
380
        default:
381
            qemu_log("ERROR: instruction should not need "
382
                        "address translation\n");
383
            return -4;
384
        }
385
        if ((rw == 1 || ctx->key != 1) && (rw == 0 || ctx->key != 0)) {
386
            ctx->raddr = eaddr;
387
            ret = 2;
388
        } else {
389
            ret = -2;
390
        }
391
    }
392

    
393
    return ret;
394
}
395

    
396

    
397
static int ppc_hash32_get_physical_address(CPUPPCState *env, mmu_ctx_t *ctx,
398
                                           target_ulong eaddr, int rw,
399
                                           int access_type)
400
{
401
    bool real_mode = (access_type == ACCESS_CODE && msr_ir == 0)
402
        || (access_type != ACCESS_CODE && msr_dr == 0);
403

    
404
    if (real_mode) {
405
        ctx->raddr = eaddr;
406
        ctx->prot = PAGE_READ | PAGE_EXEC | PAGE_WRITE;
407
        return 0;
408
    } else {
409
        int ret = -1;
410

    
411
        /* Try to find a BAT */
412
        if (env->nb_BATs != 0) {
413
            ret = get_bat(env, ctx, eaddr, rw, access_type);
414
        }
415
        if (ret < 0) {
416
            /* We didn't match any BAT entry or don't have BATs */
417
            ret = get_segment32(env, ctx, eaddr, rw, access_type);
418
        }
419
        return ret;
420
    }
421
}
422

    
423
hwaddr ppc_hash32_get_phys_page_debug(CPUPPCState *env, target_ulong addr)
424
{
425
    mmu_ctx_t ctx;
426

    
427
    if (unlikely(ppc_hash32_get_physical_address(env, &ctx, addr, 0, ACCESS_INT)
428
                 != 0)) {
429
        return -1;
430
    }
431

    
432
    return ctx.raddr & TARGET_PAGE_MASK;
433
}
434

    
435
int ppc_hash32_handle_mmu_fault(CPUPPCState *env, target_ulong address, int rw,
436
                                int mmu_idx)
437
{
438
    mmu_ctx_t ctx;
439
    int access_type;
440
    int ret = 0;
441

    
442
    if (rw == 2) {
443
        /* code access */
444
        rw = 0;
445
        access_type = ACCESS_CODE;
446
    } else {
447
        /* data access */
448
        access_type = env->access_type;
449
    }
450
    ret = ppc_hash32_get_physical_address(env, &ctx, address, rw, access_type);
451
    if (ret == 0) {
452
        tlb_set_page(env, address & TARGET_PAGE_MASK,
453
                     ctx.raddr & TARGET_PAGE_MASK, ctx.prot,
454
                     mmu_idx, TARGET_PAGE_SIZE);
455
        ret = 0;
456
    } else if (ret < 0) {
457
        LOG_MMU_STATE(env);
458
        if (access_type == ACCESS_CODE) {
459
            switch (ret) {
460
            case -1:
461
                /* No matches in page tables or TLB */
462
                env->exception_index = POWERPC_EXCP_ISI;
463
                env->error_code = 0x40000000;
464
                break;
465
            case -2:
466
                /* Access rights violation */
467
                env->exception_index = POWERPC_EXCP_ISI;
468
                env->error_code = 0x08000000;
469
                break;
470
            case -3:
471
                /* No execute protection violation */
472
                env->exception_index = POWERPC_EXCP_ISI;
473
                env->error_code = 0x10000000;
474
                break;
475
            case -4:
476
                /* Direct store exception */
477
                /* No code fetch is allowed in direct-store areas */
478
                env->exception_index = POWERPC_EXCP_ISI;
479
                env->error_code = 0x10000000;
480
                break;
481
            }
482
        } else {
483
            switch (ret) {
484
            case -1:
485
                /* No matches in page tables or TLB */
486
                env->exception_index = POWERPC_EXCP_DSI;
487
                env->error_code = 0;
488
                env->spr[SPR_DAR] = address;
489
                if (rw == 1) {
490
                    env->spr[SPR_DSISR] = 0x42000000;
491
                } else {
492
                    env->spr[SPR_DSISR] = 0x40000000;
493
                }
494
                break;
495
            case -2:
496
                /* Access rights violation */
497
                env->exception_index = POWERPC_EXCP_DSI;
498
                env->error_code = 0;
499
                env->spr[SPR_DAR] = address;
500
                if (rw == 1) {
501
                    env->spr[SPR_DSISR] = 0x0A000000;
502
                } else {
503
                    env->spr[SPR_DSISR] = 0x08000000;
504
                }
505
                break;
506
            case -4:
507
                /* Direct store exception */
508
                switch (access_type) {
509
                case ACCESS_FLOAT:
510
                    /* Floating point load/store */
511
                    env->exception_index = POWERPC_EXCP_ALIGN;
512
                    env->error_code = POWERPC_EXCP_ALIGN_FP;
513
                    env->spr[SPR_DAR] = address;
514
                    break;
515
                case ACCESS_RES:
516
                    /* lwarx, ldarx or stwcx. */
517
                    env->exception_index = POWERPC_EXCP_DSI;
518
                    env->error_code = 0;
519
                    env->spr[SPR_DAR] = address;
520
                    if (rw == 1) {
521
                        env->spr[SPR_DSISR] = 0x06000000;
522
                    } else {
523
                        env->spr[SPR_DSISR] = 0x04000000;
524
                    }
525
                    break;
526
                case ACCESS_EXT:
527
                    /* eciwx or ecowx */
528
                    env->exception_index = POWERPC_EXCP_DSI;
529
                    env->error_code = 0;
530
                    env->spr[SPR_DAR] = address;
531
                    if (rw == 1) {
532
                        env->spr[SPR_DSISR] = 0x06100000;
533
                    } else {
534
                        env->spr[SPR_DSISR] = 0x04100000;
535
                    }
536
                    break;
537
                default:
538
                    printf("DSI: invalid exception (%d)\n", ret);
539
                    env->exception_index = POWERPC_EXCP_PROGRAM;
540
                    env->error_code =
541
                        POWERPC_EXCP_INVAL | POWERPC_EXCP_INVAL_INVAL;
542
                    env->spr[SPR_DAR] = address;
543
                    break;
544
                }
545
                break;
546
            }
547
        }
548
#if 0
549
        printf("%s: set exception to %d %02x\n", __func__,
550
               env->exception, env->error_code);
551
#endif
552
        ret = 1;
553
    }
554

    
555
    return ret;
556
}