Statistics
| Branch: | Revision:

root / target-sparc / helper.c @ 0f8a249a

History | View | Annotate | Download (18.8 kB)

1
/*
2
 *  sparc helpers
3
 *
4
 *  Copyright (c) 2003-2005 Fabrice Bellard
5
 *
6
 * This library is free software; you can redistribute it and/or
7
 * modify it under the terms of the GNU Lesser General Public
8
 * License as published by the Free Software Foundation; either
9
 * version 2 of the License, or (at your option) any later version.
10
 *
11
 * This library is distributed in the hope that it will be useful,
12
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14
 * Lesser General Public License for more details.
15
 *
16
 * You should have received a copy of the GNU Lesser General Public
17
 * License along with this library; if not, write to the Free Software
18
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
19
 */
20
#include <stdarg.h>
21
#include <stdlib.h>
22
#include <stdio.h>
23
#include <string.h>
24
#include <inttypes.h>
25
#include <signal.h>
26
#include <assert.h>
27

    
28
#include "cpu.h"
29
#include "exec-all.h"
30

    
31
//#define DEBUG_MMU
32

    
33
/* Sparc MMU emulation */
34

    
35
/* thread support */
36

    
37
spinlock_t global_cpu_lock = SPIN_LOCK_UNLOCKED;
38

    
39
void cpu_lock(void)
40
{
41
    spin_lock(&global_cpu_lock);
42
}
43

    
44
void cpu_unlock(void)
45
{
46
    spin_unlock(&global_cpu_lock);
47
}
48

    
49
#if defined(CONFIG_USER_ONLY)
50

    
51
int cpu_sparc_handle_mmu_fault(CPUState *env, target_ulong address, int rw,
52
                               int is_user, int is_softmmu)
53
{
54
    if (rw & 2)
55
        env->exception_index = TT_TFAULT;
56
    else
57
        env->exception_index = TT_DFAULT;
58
    return 1;
59
}
60

    
61
#else
62

    
63
#ifndef TARGET_SPARC64
64
/*
65
 * Sparc V8 Reference MMU (SRMMU)
66
 */
67
static const int access_table[8][8] = {
68
    { 0, 0, 0, 0, 2, 0, 3, 3 },
69
    { 0, 0, 0, 0, 2, 0, 0, 0 },
70
    { 2, 2, 0, 0, 0, 2, 3, 3 },
71
    { 2, 2, 0, 0, 0, 2, 0, 0 },
72
    { 2, 0, 2, 0, 2, 2, 3, 3 },
73
    { 2, 0, 2, 0, 2, 0, 2, 0 },
74
    { 2, 2, 2, 0, 2, 2, 3, 3 },
75
    { 2, 2, 2, 0, 2, 2, 2, 0 }
76
};
77

    
78
static const int perm_table[2][8] = {
79
    {
80
        PAGE_READ,
81
        PAGE_READ | PAGE_WRITE,
82
        PAGE_READ | PAGE_EXEC,
83
        PAGE_READ | PAGE_WRITE | PAGE_EXEC,
84
        PAGE_EXEC,
85
        PAGE_READ | PAGE_WRITE,
86
        PAGE_READ | PAGE_EXEC,
87
        PAGE_READ | PAGE_WRITE | PAGE_EXEC
88
    },
89
    {
90
        PAGE_READ,
91
        PAGE_READ | PAGE_WRITE,
92
        PAGE_READ | PAGE_EXEC,
93
        PAGE_READ | PAGE_WRITE | PAGE_EXEC,
94
        PAGE_EXEC,
95
        PAGE_READ,
96
        0,
97
        0,
98
    }
99
};
100

    
101
int get_physical_address (CPUState *env, target_phys_addr_t *physical, int *prot,
102
                          int *access_index, target_ulong address, int rw,
103
                          int is_user)
104
{
105
    int access_perms = 0;
106
    target_phys_addr_t pde_ptr;
107
    uint32_t pde;
108
    target_ulong virt_addr;
109
    int error_code = 0, is_dirty;
110
    unsigned long page_offset;
111

    
112
    virt_addr = address & TARGET_PAGE_MASK;
113
    if ((env->mmuregs[0] & MMU_E) == 0) { /* MMU disabled */
114
        *physical = address;
115
        *prot = PAGE_READ | PAGE_WRITE | PAGE_EXEC;
116
        return 0;
117
    }
118

    
119
    *access_index = ((rw & 1) << 2) | (rw & 2) | (is_user? 0 : 1);
120
    *physical = 0xffffffffffff0000ULL;
121

    
122
    /* SPARC reference MMU table walk: Context table->L1->L2->PTE */
123
    /* Context base + context number */
124
    pde_ptr = (env->mmuregs[1] << 4) + (env->mmuregs[2] << 2);
125
    pde = ldl_phys(pde_ptr);
126

    
127
    /* Ctx pde */
128
    switch (pde & PTE_ENTRYTYPE_MASK) {
129
    default:
130
    case 0: /* Invalid */
131
        return 1 << 2;
132
    case 2: /* L0 PTE, maybe should not happen? */
133
    case 3: /* Reserved */
134
        return 4 << 2;
135
    case 1: /* L0 PDE */
136
        pde_ptr = ((address >> 22) & ~3) + ((pde & ~3) << 4);
137
        pde = ldl_phys(pde_ptr);
138

    
139
        switch (pde & PTE_ENTRYTYPE_MASK) {
140
        default:
141
        case 0: /* Invalid */
142
            return (1 << 8) | (1 << 2);
143
        case 3: /* Reserved */
144
            return (1 << 8) | (4 << 2);
145
        case 1: /* L1 PDE */
146
            pde_ptr = ((address & 0xfc0000) >> 16) + ((pde & ~3) << 4);
147
            pde = ldl_phys(pde_ptr);
148

    
149
            switch (pde & PTE_ENTRYTYPE_MASK) {
150
            default:
151
            case 0: /* Invalid */
152
                return (2 << 8) | (1 << 2);
153
            case 3: /* Reserved */
154
                return (2 << 8) | (4 << 2);
155
            case 1: /* L2 PDE */
156
                pde_ptr = ((address & 0x3f000) >> 10) + ((pde & ~3) << 4);
157
                pde = ldl_phys(pde_ptr);
158

    
159
                switch (pde & PTE_ENTRYTYPE_MASK) {
160
                default:
161
                case 0: /* Invalid */
162
                    return (3 << 8) | (1 << 2);
163
                case 1: /* PDE, should not happen */
164
                case 3: /* Reserved */
165
                    return (3 << 8) | (4 << 2);
166
                case 2: /* L3 PTE */
167
                    virt_addr = address & TARGET_PAGE_MASK;
168
                    page_offset = (address & TARGET_PAGE_MASK) & (TARGET_PAGE_SIZE - 1);
169
                }
170
                break;
171
            case 2: /* L2 PTE */
172
                virt_addr = address & ~0x3ffff;
173
                page_offset = address & 0x3ffff;
174
            }
175
            break;
176
        case 2: /* L1 PTE */
177
            virt_addr = address & ~0xffffff;
178
            page_offset = address & 0xffffff;
179
        }
180
    }
181

    
182
    /* update page modified and dirty bits */
183
    is_dirty = (rw & 1) && !(pde & PG_MODIFIED_MASK);
184
    if (!(pde & PG_ACCESSED_MASK) || is_dirty) {
185
        pde |= PG_ACCESSED_MASK;
186
        if (is_dirty)
187
            pde |= PG_MODIFIED_MASK;
188
        stl_phys_notdirty(pde_ptr, pde);
189
    }
190
    /* check access */
191
    access_perms = (pde & PTE_ACCESS_MASK) >> PTE_ACCESS_SHIFT;
192
    error_code = access_table[*access_index][access_perms];
193
    if (error_code && !((env->mmuregs[0] & MMU_NF) && is_user))
194
        return error_code;
195

    
196
    /* the page can be put in the TLB */
197
    *prot = perm_table[is_user][access_perms];
198
    if (!(pde & PG_MODIFIED_MASK)) {
199
        /* only set write access if already dirty... otherwise wait
200
           for dirty access */
201
        *prot &= ~PAGE_WRITE;
202
    }
203

    
204
    /* Even if large ptes, we map only one 4KB page in the cache to
205
       avoid filling it too fast */
206
    *physical = ((target_phys_addr_t)(pde & PTE_ADDR_MASK) << 4) + page_offset;
207
    return error_code;
208
}
209

    
210
/* Perform address translation */
211
int cpu_sparc_handle_mmu_fault (CPUState *env, target_ulong address, int rw,
212
                              int is_user, int is_softmmu)
213
{
214
    target_phys_addr_t paddr;
215
    target_ulong vaddr;
216
    int error_code = 0, prot, ret = 0, access_index;
217

    
218
    error_code = get_physical_address(env, &paddr, &prot, &access_index, address, rw, is_user);
219
    if (error_code == 0) {
220
        vaddr = address & TARGET_PAGE_MASK;
221
        paddr &= TARGET_PAGE_MASK;
222
#ifdef DEBUG_MMU
223
        printf("Translate at " TARGET_FMT_lx " -> " TARGET_FMT_plx ", vaddr "
224
               TARGET_FMT_lx "\n", address, paddr, vaddr);
225
#endif
226
        ret = tlb_set_page_exec(env, vaddr, paddr, prot, is_user, is_softmmu);
227
        return ret;
228
    }
229

    
230
    if (env->mmuregs[3]) /* Fault status register */
231
        env->mmuregs[3] = 1; /* overflow (not read before another fault) */
232
    env->mmuregs[3] |= (access_index << 5) | error_code | 2;
233
    env->mmuregs[4] = address; /* Fault address register */
234

    
235
    if ((env->mmuregs[0] & MMU_NF) || env->psret == 0)  {
236
        // No fault mode: if a mapping is available, just override
237
        // permissions. If no mapping is available, redirect accesses to
238
        // neverland. Fake/overridden mappings will be flushed when
239
        // switching to normal mode.
240
        vaddr = address & TARGET_PAGE_MASK;
241
        prot = PAGE_READ | PAGE_WRITE | PAGE_EXEC;
242
        ret = tlb_set_page_exec(env, vaddr, paddr, prot, is_user, is_softmmu);
243
        return ret;
244
    } else {
245
        if (rw & 2)
246
            env->exception_index = TT_TFAULT;
247
        else
248
            env->exception_index = TT_DFAULT;
249
        return 1;
250
    }
251
}
252

    
253
target_ulong mmu_probe(CPUState *env, target_ulong address, int mmulev)
254
{
255
    target_phys_addr_t pde_ptr;
256
    uint32_t pde;
257

    
258
    /* Context base + context number */
259
    pde_ptr = (target_phys_addr_t)(env->mmuregs[1] << 4) +
260
        (env->mmuregs[2] << 2);
261
    pde = ldl_phys(pde_ptr);
262

    
263
    switch (pde & PTE_ENTRYTYPE_MASK) {
264
    default:
265
    case 0: /* Invalid */
266
    case 2: /* PTE, maybe should not happen? */
267
    case 3: /* Reserved */
268
        return 0;
269
    case 1: /* L1 PDE */
270
        if (mmulev == 3)
271
            return pde;
272
        pde_ptr = ((address >> 22) & ~3) + ((pde & ~3) << 4);
273
        pde = ldl_phys(pde_ptr);
274

    
275
        switch (pde & PTE_ENTRYTYPE_MASK) {
276
        default:
277
        case 0: /* Invalid */
278
        case 3: /* Reserved */
279
            return 0;
280
        case 2: /* L1 PTE */
281
            return pde;
282
        case 1: /* L2 PDE */
283
            if (mmulev == 2)
284
                return pde;
285
            pde_ptr = ((address & 0xfc0000) >> 16) + ((pde & ~3) << 4);
286
            pde = ldl_phys(pde_ptr);
287

    
288
            switch (pde & PTE_ENTRYTYPE_MASK) {
289
            default:
290
            case 0: /* Invalid */
291
            case 3: /* Reserved */
292
                return 0;
293
            case 2: /* L2 PTE */
294
                return pde;
295
            case 1: /* L3 PDE */
296
                if (mmulev == 1)
297
                    return pde;
298
                pde_ptr = ((address & 0x3f000) >> 10) + ((pde & ~3) << 4);
299
                pde = ldl_phys(pde_ptr);
300

    
301
                switch (pde & PTE_ENTRYTYPE_MASK) {
302
                default:
303
                case 0: /* Invalid */
304
                case 1: /* PDE, should not happen */
305
                case 3: /* Reserved */
306
                    return 0;
307
                case 2: /* L3 PTE */
308
                    return pde;
309
                }
310
            }
311
        }
312
    }
313
    return 0;
314
}
315

    
316
#ifdef DEBUG_MMU
317
void dump_mmu(CPUState *env)
318
{
319
    target_ulong va, va1, va2;
320
    unsigned int n, m, o;
321
    target_phys_addr_t pde_ptr, pa;
322
    uint32_t pde;
323

    
324
    printf("MMU dump:\n");
325
    pde_ptr = (env->mmuregs[1] << 4) + (env->mmuregs[2] << 2);
326
    pde = ldl_phys(pde_ptr);
327
    printf("Root ptr: " TARGET_FMT_plx ", ctx: %d\n",
328
           (target_phys_addr_t)env->mmuregs[1] << 4, env->mmuregs[2]);
329
    for (n = 0, va = 0; n < 256; n++, va += 16 * 1024 * 1024) {
330
        pde = mmu_probe(env, va, 2);
331
        if (pde) {
332
            pa = cpu_get_phys_page_debug(env, va);
333
            printf("VA: " TARGET_FMT_lx ", PA: " TARGET_FMT_plx
334
                   " PDE: " TARGET_FMT_lx "\n", va, pa, pde);
335
            for (m = 0, va1 = va; m < 64; m++, va1 += 256 * 1024) {
336
                pde = mmu_probe(env, va1, 1);
337
                if (pde) {
338
                    pa = cpu_get_phys_page_debug(env, va1);
339
                    printf(" VA: " TARGET_FMT_lx ", PA: " TARGET_FMT_plx
340
                           " PDE: " TARGET_FMT_lx "\n", va1, pa, pde);
341
                    for (o = 0, va2 = va1; o < 64; o++, va2 += 4 * 1024) {
342
                        pde = mmu_probe(env, va2, 0);
343
                        if (pde) {
344
                            pa = cpu_get_phys_page_debug(env, va2);
345
                            printf("  VA: " TARGET_FMT_lx ", PA: "
346
                                   TARGET_FMT_plx " PTE: " TARGET_FMT_lx "\n",
347
                                   va2, pa, pde);
348
                        }
349
                    }
350
                }
351
            }
352
        }
353
    }
354
    printf("MMU dump ends\n");
355
}
356
#endif /* DEBUG_MMU */
357

    
358
#else /* !TARGET_SPARC64 */
359
/*
360
 * UltraSparc IIi I/DMMUs
361
 */
362
static int get_physical_address_data(CPUState *env, target_phys_addr_t *physical, int *prot,
363
                          int *access_index, target_ulong address, int rw,
364
                          int is_user)
365
{
366
    target_ulong mask;
367
    unsigned int i;
368

    
369
    if ((env->lsu & DMMU_E) == 0) { /* DMMU disabled */
370
        *physical = address;
371
        *prot = PAGE_READ | PAGE_WRITE;
372
        return 0;
373
    }
374

    
375
    for (i = 0; i < 64; i++) {
376
        switch ((env->dtlb_tte[i] >> 61) & 3) {
377
        default:
378
        case 0x0: // 8k
379
            mask = 0xffffffffffffe000ULL;
380
            break;
381
        case 0x1: // 64k
382
            mask = 0xffffffffffff0000ULL;
383
            break;
384
        case 0x2: // 512k
385
            mask = 0xfffffffffff80000ULL;
386
            break;
387
        case 0x3: // 4M
388
            mask = 0xffffffffffc00000ULL;
389
            break;
390
        }
391
        // ctx match, vaddr match?
392
        if (env->dmmuregs[1] == (env->dtlb_tag[i] & 0x1fff) &&
393
            (address & mask) == (env->dtlb_tag[i] & ~0x1fffULL)) {
394
            // valid, access ok?
395
            if ((env->dtlb_tte[i] & 0x8000000000000000ULL) == 0 ||
396
                ((env->dtlb_tte[i] & 0x4) && is_user) ||
397
                (!(env->dtlb_tte[i] & 0x2) && (rw == 1))) {
398
                if (env->dmmuregs[3]) /* Fault status register */
399
                    env->dmmuregs[3] = 2; /* overflow (not read before another fault) */
400
                env->dmmuregs[3] |= (is_user << 3) | ((rw == 1) << 2) | 1;
401
                env->dmmuregs[4] = address; /* Fault address register */
402
                env->exception_index = TT_DFAULT;
403
#ifdef DEBUG_MMU
404
                printf("DFAULT at 0x%" PRIx64 "\n", address);
405
#endif
406
                return 1;
407
            }
408
            *physical = (env->dtlb_tte[i] & mask & 0x1fffffff000ULL) + (address & ~mask & 0x1fffffff000ULL);
409
            *prot = PAGE_READ;
410
            if (env->dtlb_tte[i] & 0x2)
411
                *prot |= PAGE_WRITE;
412
            return 0;
413
        }
414
    }
415
#ifdef DEBUG_MMU
416
    printf("DMISS at 0x%" PRIx64 "\n", address);
417
#endif
418
    env->exception_index = TT_DMISS;
419
    return 1;
420
}
421

    
422
static int get_physical_address_code(CPUState *env, target_phys_addr_t *physical, int *prot,
423
                          int *access_index, target_ulong address, int rw,
424
                          int is_user)
425
{
426
    target_ulong mask;
427
    unsigned int i;
428

    
429
    if ((env->lsu & IMMU_E) == 0) { /* IMMU disabled */
430
        *physical = address;
431
        *prot = PAGE_EXEC;
432
        return 0;
433
    }
434

    
435
    for (i = 0; i < 64; i++) {
436
        switch ((env->itlb_tte[i] >> 61) & 3) {
437
        default:
438
        case 0x0: // 8k
439
            mask = 0xffffffffffffe000ULL;
440
            break;
441
        case 0x1: // 64k
442
            mask = 0xffffffffffff0000ULL;
443
            break;
444
        case 0x2: // 512k
445
            mask = 0xfffffffffff80000ULL;
446
            break;
447
        case 0x3: // 4M
448
            mask = 0xffffffffffc00000ULL;
449
                break;
450
        }
451
        // ctx match, vaddr match?
452
        if (env->dmmuregs[1] == (env->itlb_tag[i] & 0x1fff) &&
453
            (address & mask) == (env->itlb_tag[i] & ~0x1fffULL)) {
454
            // valid, access ok?
455
            if ((env->itlb_tte[i] & 0x8000000000000000ULL) == 0 ||
456
                ((env->itlb_tte[i] & 0x4) && is_user)) {
457
                if (env->immuregs[3]) /* Fault status register */
458
                    env->immuregs[3] = 2; /* overflow (not read before another fault) */
459
                env->immuregs[3] |= (is_user << 3) | 1;
460
                env->exception_index = TT_TFAULT;
461
#ifdef DEBUG_MMU
462
                printf("TFAULT at 0x%" PRIx64 "\n", address);
463
#endif
464
                return 1;
465
            }
466
            *physical = (env->itlb_tte[i] & mask & 0x1fffffff000ULL) + (address & ~mask & 0x1fffffff000ULL);
467
            *prot = PAGE_EXEC;
468
            return 0;
469
        }
470
    }
471
#ifdef DEBUG_MMU
472
    printf("TMISS at 0x%" PRIx64 "\n", address);
473
#endif
474
    env->exception_index = TT_TMISS;
475
    return 1;
476
}
477

    
478
int get_physical_address(CPUState *env, target_phys_addr_t *physical, int *prot,
479
                          int *access_index, target_ulong address, int rw,
480
                          int is_user)
481
{
482
    if (rw == 2)
483
        return get_physical_address_code(env, physical, prot, access_index, address, rw, is_user);
484
    else
485
        return get_physical_address_data(env, physical, prot, access_index, address, rw, is_user);
486
}
487

    
488
/* Perform address translation */
489
int cpu_sparc_handle_mmu_fault (CPUState *env, target_ulong address, int rw,
490
                              int is_user, int is_softmmu)
491
{
492
    target_ulong virt_addr, vaddr;
493
    target_phys_addr_t paddr;
494
    int error_code = 0, prot, ret = 0, access_index;
495

    
496
    error_code = get_physical_address(env, &paddr, &prot, &access_index, address, rw, is_user);
497
    if (error_code == 0) {
498
        virt_addr = address & TARGET_PAGE_MASK;
499
        vaddr = virt_addr + ((address & TARGET_PAGE_MASK) & (TARGET_PAGE_SIZE - 1));
500
#ifdef DEBUG_MMU
501
        printf("Translate at 0x%" PRIx64 " -> 0x%" PRIx64 ", vaddr 0x%" PRIx64 "\n", address, paddr, vaddr);
502
#endif
503
        ret = tlb_set_page_exec(env, vaddr, paddr, prot, is_user, is_softmmu);
504
        return ret;
505
    }
506
    // XXX
507
    return 1;
508
}
509

    
510
#ifdef DEBUG_MMU
511
void dump_mmu(CPUState *env)
512
{
513
    unsigned int i;
514
    const char *mask;
515

    
516
    printf("MMU contexts: Primary: %" PRId64 ", Secondary: %" PRId64 "\n", env->dmmuregs[1], env->dmmuregs[2]);
517
    if ((env->lsu & DMMU_E) == 0) {
518
        printf("DMMU disabled\n");
519
    } else {
520
        printf("DMMU dump:\n");
521
        for (i = 0; i < 64; i++) {
522
            switch ((env->dtlb_tte[i] >> 61) & 3) {
523
            default:
524
            case 0x0:
525
                mask = "  8k";
526
                break;
527
            case 0x1:
528
                mask = " 64k";
529
                break;
530
            case 0x2:
531
                mask = "512k";
532
                break;
533
            case 0x3:
534
                mask = "  4M";
535
                break;
536
            }
537
            if ((env->dtlb_tte[i] & 0x8000000000000000ULL) != 0) {
538
                printf("VA: " TARGET_FMT_lx ", PA: " TARGET_FMT_lx ", %s, %s, %s, %s, ctx %" PRId64 "\n",
539
                       env->dtlb_tag[i] & ~0x1fffULL,
540
                       env->dtlb_tte[i] & 0x1ffffffe000ULL,
541
                       mask,
542
                       env->dtlb_tte[i] & 0x4? "priv": "user",
543
                       env->dtlb_tte[i] & 0x2? "RW": "RO",
544
                       env->dtlb_tte[i] & 0x40? "locked": "unlocked",
545
                       env->dtlb_tag[i] & 0x1fffULL);
546
            }
547
        }
548
    }
549
    if ((env->lsu & IMMU_E) == 0) {
550
        printf("IMMU disabled\n");
551
    } else {
552
        printf("IMMU dump:\n");
553
        for (i = 0; i < 64; i++) {
554
            switch ((env->itlb_tte[i] >> 61) & 3) {
555
            default:
556
            case 0x0:
557
                mask = "  8k";
558
                break;
559
            case 0x1:
560
                mask = " 64k";
561
                break;
562
            case 0x2:
563
                mask = "512k";
564
                break;
565
            case 0x3:
566
                mask = "  4M";
567
                break;
568
            }
569
            if ((env->itlb_tte[i] & 0x8000000000000000ULL) != 0) {
570
                printf("VA: " TARGET_FMT_lx ", PA: " TARGET_FMT_lx ", %s, %s, %s, ctx %" PRId64 "\n",
571
                       env->itlb_tag[i] & ~0x1fffULL,
572
                       env->itlb_tte[i] & 0x1ffffffe000ULL,
573
                       mask,
574
                       env->itlb_tte[i] & 0x4? "priv": "user",
575
                       env->itlb_tte[i] & 0x40? "locked": "unlocked",
576
                       env->itlb_tag[i] & 0x1fffULL);
577
            }
578
        }
579
    }
580
}
581
#endif /* DEBUG_MMU */
582

    
583
#endif /* TARGET_SPARC64 */
584
#endif /* !CONFIG_USER_ONLY */
585

    
586
void memcpy32(target_ulong *dst, const target_ulong *src)
587
{
588
    dst[0] = src[0];
589
    dst[1] = src[1];
590
    dst[2] = src[2];
591
    dst[3] = src[3];
592
    dst[4] = src[4];
593
    dst[5] = src[5];
594
    dst[6] = src[6];
595
    dst[7] = src[7];
596
}