Statistics
| Branch: | Revision:

root / target-sparc / helper.c @ 24741ef3

History | View | Annotate | Download (15.6 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
/* 1 = write OK */
79
static const int rw_table[2][8] = {
80
    { 0, 1, 0, 1, 0, 1, 0, 1 },
81
    { 0, 1, 0, 1, 0, 0, 0, 0 }
82
};
83

    
84
int get_physical_address (CPUState *env, target_phys_addr_t *physical, int *prot,
85
                          int *access_index, target_ulong address, int rw,
86
                          int is_user)
87
{
88
    int access_perms = 0;
89
    target_phys_addr_t pde_ptr;
90
    uint32_t pde;
91
    target_ulong virt_addr;
92
    int error_code = 0, is_dirty;
93
    unsigned long page_offset;
94

    
95
    virt_addr = address & TARGET_PAGE_MASK;
96
    if ((env->mmuregs[0] & MMU_E) == 0) { /* MMU disabled */
97
        *physical = address;
98
        *prot = PAGE_READ | PAGE_WRITE;
99
        return 0;
100
    }
101

    
102
    *access_index = ((rw & 1) << 2) | (rw & 2) | (is_user? 0 : 1);
103
    *physical = 0xfffff000;
104

    
105
    /* SPARC reference MMU table walk: Context table->L1->L2->PTE */
106
    /* Context base + context number */
107
    pde_ptr = (env->mmuregs[1] << 4) + (env->mmuregs[2] << 2);
108
    pde = ldl_phys(pde_ptr);
109

    
110
    /* Ctx pde */
111
    switch (pde & PTE_ENTRYTYPE_MASK) {
112
    default:
113
    case 0: /* Invalid */
114
        return 1 << 2;
115
    case 2: /* L0 PTE, maybe should not happen? */
116
    case 3: /* Reserved */
117
        return 4 << 2;
118
    case 1: /* L0 PDE */
119
        pde_ptr = ((address >> 22) & ~3) + ((pde & ~3) << 4);
120
        pde = ldl_phys(pde_ptr);
121

    
122
        switch (pde & PTE_ENTRYTYPE_MASK) {
123
        default:
124
        case 0: /* Invalid */
125
            return (1 << 8) | (1 << 2);
126
        case 3: /* Reserved */
127
            return (1 << 8) | (4 << 2);
128
        case 1: /* L1 PDE */
129
            pde_ptr = ((address & 0xfc0000) >> 16) + ((pde & ~3) << 4);
130
            pde = ldl_phys(pde_ptr);
131

    
132
            switch (pde & PTE_ENTRYTYPE_MASK) {
133
            default:
134
            case 0: /* Invalid */
135
                return (2 << 8) | (1 << 2);
136
            case 3: /* Reserved */
137
                return (2 << 8) | (4 << 2);
138
            case 1: /* L2 PDE */
139
                pde_ptr = ((address & 0x3f000) >> 10) + ((pde & ~3) << 4);
140
                pde = ldl_phys(pde_ptr);
141

    
142
                switch (pde & PTE_ENTRYTYPE_MASK) {
143
                default:
144
                case 0: /* Invalid */
145
                    return (3 << 8) | (1 << 2);
146
                case 1: /* PDE, should not happen */
147
                case 3: /* Reserved */
148
                    return (3 << 8) | (4 << 2);
149
                case 2: /* L3 PTE */
150
                    virt_addr = address & TARGET_PAGE_MASK;
151
                    page_offset = (address & TARGET_PAGE_MASK) & (TARGET_PAGE_SIZE - 1);
152
                }
153
                break;
154
            case 2: /* L2 PTE */
155
                virt_addr = address & ~0x3ffff;
156
                page_offset = address & 0x3ffff;
157
            }
158
            break;
159
        case 2: /* L1 PTE */
160
            virt_addr = address & ~0xffffff;
161
            page_offset = address & 0xffffff;
162
        }
163
    }
164

    
165
    /* update page modified and dirty bits */
166
    is_dirty = (rw & 1) && !(pde & PG_MODIFIED_MASK);
167
    if (!(pde & PG_ACCESSED_MASK) || is_dirty) {
168
        pde |= PG_ACCESSED_MASK;
169
        if (is_dirty)
170
            pde |= PG_MODIFIED_MASK;
171
        stl_phys_notdirty(pde_ptr, pde);
172
    }
173
    /* check access */
174
    access_perms = (pde & PTE_ACCESS_MASK) >> PTE_ACCESS_SHIFT;
175
    error_code = access_table[*access_index][access_perms];
176
    if (error_code && !(env->mmuregs[0] & MMU_NF))
177
        return error_code;
178

    
179
    /* the page can be put in the TLB */
180
    *prot = PAGE_READ;
181
    if (pde & PG_MODIFIED_MASK) {
182
        /* only set write access if already dirty... otherwise wait
183
           for dirty access */
184
        if (rw_table[is_user][access_perms])
185
                *prot |= PAGE_WRITE;
186
    }
187

    
188
    /* Even if large ptes, we map only one 4KB page in the cache to
189
       avoid filling it too fast */
190
    *physical = ((pde & PTE_ADDR_MASK) << 4) + page_offset;
191
    return error_code;
192
}
193

    
194
/* Perform address translation */
195
int cpu_sparc_handle_mmu_fault (CPUState *env, target_ulong address, int rw,
196
                              int is_user, int is_softmmu)
197
{
198
    target_ulong virt_addr;
199
    target_phys_addr_t paddr;
200
    unsigned long vaddr;
201
    int error_code = 0, prot, ret = 0, access_index;
202

    
203
    error_code = get_physical_address(env, &paddr, &prot, &access_index, address, rw, is_user);
204
    if (error_code == 0) {
205
        virt_addr = address & TARGET_PAGE_MASK;
206
        vaddr = virt_addr + ((address & TARGET_PAGE_MASK) & (TARGET_PAGE_SIZE - 1));
207
        ret = tlb_set_page(env, vaddr, paddr, prot, is_user, is_softmmu);
208
        return ret;
209
    }
210

    
211
    if (env->mmuregs[3]) /* Fault status register */
212
        env->mmuregs[3] = 1; /* overflow (not read before another fault) */
213
    env->mmuregs[3] |= (access_index << 5) | error_code | 2;
214
    env->mmuregs[4] = address; /* Fault address register */
215

    
216
    if ((env->mmuregs[0] & MMU_NF) || env->psret == 0)  {
217
        // No fault mode: if a mapping is available, just override
218
        // permissions. If no mapping is available, redirect accesses to
219
        // neverland. Fake/overridden mappings will be flushed when
220
        // switching to normal mode.
221
        vaddr = address & TARGET_PAGE_MASK;
222
        prot = PAGE_READ | PAGE_WRITE;
223
        ret = tlb_set_page(env, vaddr, paddr, prot, is_user, is_softmmu);
224
        return ret;
225
    } else {
226
        if (rw & 2)
227
            env->exception_index = TT_TFAULT;
228
        else
229
            env->exception_index = TT_DFAULT;
230
        return 1;
231
    }
232
}
233

    
234
target_ulong mmu_probe(CPUState *env, target_ulong address, int mmulev)
235
{
236
    target_phys_addr_t pde_ptr;
237
    uint32_t pde;
238

    
239
    /* Context base + context number */
240
    pde_ptr = (env->mmuregs[1] << 4) + (env->mmuregs[2] << 2);
241
    pde = ldl_phys(pde_ptr);
242

    
243
    switch (pde & PTE_ENTRYTYPE_MASK) {
244
    default:
245
    case 0: /* Invalid */
246
    case 2: /* PTE, maybe should not happen? */
247
    case 3: /* Reserved */
248
        return 0;
249
    case 1: /* L1 PDE */
250
        if (mmulev == 3)
251
            return pde;
252
        pde_ptr = ((address >> 22) & ~3) + ((pde & ~3) << 4);
253
        pde = ldl_phys(pde_ptr);
254

    
255
        switch (pde & PTE_ENTRYTYPE_MASK) {
256
        default:
257
        case 0: /* Invalid */
258
        case 3: /* Reserved */
259
            return 0;
260
        case 2: /* L1 PTE */
261
            return pde;
262
        case 1: /* L2 PDE */
263
            if (mmulev == 2)
264
                return pde;
265
            pde_ptr = ((address & 0xfc0000) >> 16) + ((pde & ~3) << 4);
266
            pde = ldl_phys(pde_ptr);
267

    
268
            switch (pde & PTE_ENTRYTYPE_MASK) {
269
            default:
270
            case 0: /* Invalid */
271
            case 3: /* Reserved */
272
                return 0;
273
            case 2: /* L2 PTE */
274
                return pde;
275
            case 1: /* L3 PDE */
276
                if (mmulev == 1)
277
                    return pde;
278
                pde_ptr = ((address & 0x3f000) >> 10) + ((pde & ~3) << 4);
279
                pde = ldl_phys(pde_ptr);
280

    
281
                switch (pde & PTE_ENTRYTYPE_MASK) {
282
                default:
283
                case 0: /* Invalid */
284
                case 1: /* PDE, should not happen */
285
                case 3: /* Reserved */
286
                    return 0;
287
                case 2: /* L3 PTE */
288
                    return pde;
289
                }
290
            }
291
        }
292
    }
293
    return 0;
294
}
295

    
296
#ifdef DEBUG_MMU
297
void dump_mmu(CPUState *env)
298
{
299
     target_ulong va, va1, va2;
300
     unsigned int n, m, o;
301
     target_phys_addr_t pde_ptr, pa;
302
    uint32_t pde;
303

    
304
    printf("MMU dump:\n");
305
    pde_ptr = (env->mmuregs[1] << 4) + (env->mmuregs[2] << 2);
306
    pde = ldl_phys(pde_ptr);
307
    printf("Root ptr: " TARGET_FMT_lx ", ctx: %d\n", env->mmuregs[1] << 4, env->mmuregs[2]);
308
    for (n = 0, va = 0; n < 256; n++, va += 16 * 1024 * 1024) {
309
        pde_ptr = mmu_probe(env, va, 2);
310
        if (pde_ptr) {
311
            pa = cpu_get_phys_page_debug(env, va);
312
             printf("VA: " TARGET_FMT_lx ", PA: " TARGET_FMT_lx " PDE: " TARGET_FMT_lx "\n", va, pa, pde_ptr);
313
            for (m = 0, va1 = va; m < 64; m++, va1 += 256 * 1024) {
314
                pde_ptr = mmu_probe(env, va1, 1);
315
                if (pde_ptr) {
316
                    pa = cpu_get_phys_page_debug(env, va1);
317
                     printf(" VA: " TARGET_FMT_lx ", PA: " TARGET_FMT_lx " PDE: " TARGET_FMT_lx "\n", va1, pa, pde_ptr);
318
                    for (o = 0, va2 = va1; o < 64; o++, va2 += 4 * 1024) {
319
                        pde_ptr = mmu_probe(env, va2, 0);
320
                        if (pde_ptr) {
321
                            pa = cpu_get_phys_page_debug(env, va2);
322
                             printf("  VA: " TARGET_FMT_lx ", PA: " TARGET_FMT_lx " PTE: " TARGET_FMT_lx "\n", va2, pa, pde_ptr);
323
                        }
324
                    }
325
                }
326
            }
327
        }
328
    }
329
    printf("MMU dump ends\n");
330
}
331
#endif /* DEBUG_MMU */
332

    
333
#else /* !TARGET_SPARC64 */
334
/*
335
 * UltraSparc IIi I/DMMUs
336
 */
337
static int get_physical_address_data(CPUState *env, target_phys_addr_t *physical, int *prot,
338
                          int *access_index, target_ulong address, int rw,
339
                          int is_user)
340
{
341
    target_ulong mask;
342
    unsigned int i;
343

    
344
    if ((env->lsu & DMMU_E) == 0) { /* DMMU disabled */
345
        *physical = address;
346
        *prot = PAGE_READ | PAGE_WRITE;
347
        return 0;
348
    }
349

    
350
    for (i = 0; i < 64; i++) {
351
        switch ((env->dtlb_tte[i] >> 61) & 3) {
352
        default:
353
        case 0x0: // 8k
354
            mask = 0xffffffffffffe000ULL;
355
            break;
356
        case 0x1: // 64k
357
            mask = 0xffffffffffff0000ULL;
358
            break;
359
        case 0x2: // 512k
360
            mask = 0xfffffffffff80000ULL;
361
            break;
362
        case 0x3: // 4M
363
            mask = 0xffffffffffc00000ULL;
364
            break;
365
        }
366
        // ctx match, vaddr match?
367
        if (env->dmmuregs[1] == (env->dtlb_tag[i] & 0x1fff) &&
368
            (address & mask) == (env->dtlb_tag[i] & ~0x1fffULL)) {
369
            // valid, access ok?
370
            if ((env->dtlb_tte[i] & 0x8000000000000000ULL) == 0 ||
371
                ((env->dtlb_tte[i] & 0x4) && is_user) ||
372
                (!(env->dtlb_tte[i] & 0x2) && (rw == 1))) {
373
                if (env->dmmuregs[3]) /* Fault status register */
374
                    env->dmmuregs[3] = 2; /* overflow (not read before another fault) */
375
                env->dmmuregs[3] |= (is_user << 3) | ((rw == 1) << 2) | 1;
376
                env->dmmuregs[4] = address; /* Fault address register */
377
                env->exception_index = TT_DFAULT;
378
#ifdef DEBUG_MMU
379
                printf("DFAULT at 0x%llx\n", address);
380
#endif
381
                return 1;
382
            }
383
            *physical = (env->dtlb_tte[i] & mask & 0x1fffffff000ULL) + (address & ~mask & 0x1fffffff000ULL);
384
            *prot = PAGE_READ;
385
            if (env->dtlb_tte[i] & 0x2)
386
                *prot |= PAGE_WRITE;
387
            return 0;
388
        }
389
    }
390
#ifdef DEBUG_MMU
391
    printf("DMISS at 0x%llx\n", address);
392
#endif
393
    env->exception_index = TT_DMISS;
394
    return 1;
395
}
396

    
397
static int get_physical_address_code(CPUState *env, target_phys_addr_t *physical, int *prot,
398
                          int *access_index, target_ulong address, int rw,
399
                          int is_user)
400
{
401
    target_ulong mask;
402
    unsigned int i;
403

    
404
    if ((env->lsu & IMMU_E) == 0) { /* IMMU disabled */
405
        *physical = address;
406
        *prot = PAGE_READ;
407
        return 0;
408
    }
409

    
410
    for (i = 0; i < 64; i++) {
411
        switch ((env->itlb_tte[i] >> 61) & 3) {
412
        default:
413
        case 0x0: // 8k
414
            mask = 0xffffffffffffe000ULL;
415
            break;
416
        case 0x1: // 64k
417
            mask = 0xffffffffffff0000ULL;
418
            break;
419
        case 0x2: // 512k
420
            mask = 0xfffffffffff80000ULL;
421
            break;
422
        case 0x3: // 4M
423
            mask = 0xffffffffffc00000ULL;
424
                break;
425
        }
426
        // ctx match, vaddr match?
427
        if (env->dmmuregs[1] == (env->itlb_tag[i] & 0x1fff) &&
428
            (address & mask) == (env->itlb_tag[i] & ~0x1fffULL)) {
429
            // valid, access ok?
430
            if ((env->itlb_tte[i] & 0x8000000000000000ULL) == 0 ||
431
                ((env->itlb_tte[i] & 0x4) && is_user)) {
432
                if (env->immuregs[3]) /* Fault status register */
433
                    env->immuregs[3] = 2; /* overflow (not read before another fault) */
434
                env->immuregs[3] |= (is_user << 3) | 1;
435
                env->exception_index = TT_TFAULT;
436
#ifdef DEBUG_MMU
437
                printf("TFAULT at 0x%llx\n", address);
438
#endif
439
                return 1;
440
            }
441
            *physical = (env->itlb_tte[i] & mask & 0x1fffffff000ULL) + (address & ~mask & 0x1fffffff000ULL);
442
            *prot = PAGE_READ;
443
            return 0;
444
        }
445
    }
446
#ifdef DEBUG_MMU
447
    printf("TMISS at 0x%llx\n", address);
448
#endif
449
    env->exception_index = TT_TMISS;
450
    return 1;
451
}
452

    
453
int get_physical_address(CPUState *env, target_phys_addr_t *physical, int *prot,
454
                          int *access_index, target_ulong address, int rw,
455
                          int is_user)
456
{
457
    if (rw == 2)
458
        return get_physical_address_code(env, physical, prot, access_index, address, rw, is_user);
459
    else
460
        return get_physical_address_data(env, physical, prot, access_index, address, rw, is_user);
461
}
462

    
463
/* Perform address translation */
464
int cpu_sparc_handle_mmu_fault (CPUState *env, target_ulong address, int rw,
465
                              int is_user, int is_softmmu)
466
{
467
    target_ulong virt_addr, vaddr;
468
    target_phys_addr_t paddr;
469
    int error_code = 0, prot, ret = 0, access_index;
470

    
471
    error_code = get_physical_address(env, &paddr, &prot, &access_index, address, rw, is_user);
472
    if (error_code == 0) {
473
        virt_addr = address & TARGET_PAGE_MASK;
474
        vaddr = virt_addr + ((address & TARGET_PAGE_MASK) & (TARGET_PAGE_SIZE - 1));
475
#ifdef DEBUG_MMU
476
        printf("Translate at 0x%llx -> 0x%llx, vaddr 0x%llx\n", address, paddr, vaddr);
477
#endif
478
        ret = tlb_set_page(env, vaddr, paddr, prot, is_user, is_softmmu);
479
        return ret;
480
    }
481
    // XXX
482
    return 1;
483
}
484

    
485
#ifdef DEBUG_MMU
486
void dump_mmu(CPUState *env)
487
{
488
    unsigned int i;
489
    const char *mask;
490

    
491
    printf("MMU contexts: Primary: %lld, Secondary: %lld\n", env->dmmuregs[1], env->dmmuregs[2]);
492
    if ((env->lsu & DMMU_E) == 0) {
493
        printf("DMMU disabled\n");
494
    } else {
495
        printf("DMMU dump:\n");
496
        for (i = 0; i < 64; i++) {
497
            switch ((env->dtlb_tte[i] >> 61) & 3) {
498
            default:
499
            case 0x0:
500
                mask = "  8k";
501
                break;
502
            case 0x1:
503
                mask = " 64k";
504
                break;
505
            case 0x2:
506
                mask = "512k";
507
                break;
508
            case 0x3:
509
                mask = "  4M";
510
                break;
511
            }
512
            if ((env->dtlb_tte[i] & 0x8000000000000000ULL) != 0) {
513
                printf("VA: " TARGET_FMT_lx ", PA: " TARGET_FMT_lx ", %s, %s, %s, %s, ctx %lld\n",
514
                       env->dtlb_tag[i] & ~0x1fffULL,
515
                       env->dtlb_tte[i] & 0x1ffffffe000ULL,
516
                       mask,
517
                       env->dtlb_tte[i] & 0x4? "priv": "user",
518
                       env->dtlb_tte[i] & 0x2? "RW": "RO",
519
                       env->dtlb_tte[i] & 0x40? "locked": "unlocked",
520
                       env->dtlb_tag[i] & 0x1fffULL);
521
            }
522
        }
523
    }
524
    if ((env->lsu & IMMU_E) == 0) {
525
        printf("IMMU disabled\n");
526
    } else {
527
        printf("IMMU dump:\n");
528
        for (i = 0; i < 64; i++) {
529
            switch ((env->itlb_tte[i] >> 61) & 3) {
530
            default:
531
            case 0x0:
532
                mask = "  8k";
533
                break;
534
            case 0x1:
535
                mask = " 64k";
536
                break;
537
            case 0x2:
538
                mask = "512k";
539
                break;
540
            case 0x3:
541
                mask = "  4M";
542
                break;
543
            }
544
            if ((env->itlb_tte[i] & 0x8000000000000000ULL) != 0) {
545
                printf("VA: " TARGET_FMT_lx ", PA: " TARGET_FMT_lx ", %s, %s, %s, ctx %lld\n",
546
                       env->itlb_tag[i] & ~0x1fffULL,
547
                       env->itlb_tte[i] & 0x1ffffffe000ULL,
548
                       mask,
549
                       env->itlb_tte[i] & 0x4? "priv": "user",
550
                       env->itlb_tte[i] & 0x40? "locked": "unlocked",
551
                       env->itlb_tag[i] & 0x1fffULL);
552
            }
553
        }
554
    }
555
}
556
#endif /* DEBUG_MMU */
557

    
558
#endif /* TARGET_SPARC64 */
559
#endif /* !CONFIG_USER_ONLY */
560

    
561
void memcpy32(target_ulong *dst, const target_ulong *src)
562
{
563
    dst[0] = src[0];
564
    dst[1] = src[1];
565
    dst[2] = src[2];
566
    dst[3] = src[3];
567
    dst[4] = src[4];
568
    dst[5] = src[5];
569
    dst[6] = src[6];
570
    dst[7] = src[7];
571
}