Statistics
| Branch: | Revision:

root / target-sparc / helper.c @ ee5bbe38

History | View | Annotate | Download (13 kB)

1
/*
2
 *  sparc helpers
3
 * 
4
 *  Copyright (c) 2003 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_PCALL
32
//#define DEBUG_MMU
33

    
34
/* Sparc MMU emulation */
35

    
36
/* thread support */
37

    
38
spinlock_t global_cpu_lock = SPIN_LOCK_UNLOCKED;
39

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

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

    
50
#if defined(CONFIG_USER_ONLY) 
51

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

    
62
#else
63

    
64
#ifndef TARGET_SPARC64
65
static const int access_table[8][8] = {
66
    { 0, 0, 0, 0, 2, 0, 3, 3 },
67
    { 0, 0, 0, 0, 2, 0, 0, 0 },
68
    { 2, 2, 0, 0, 0, 2, 3, 3 },
69
    { 2, 2, 0, 0, 0, 2, 0, 0 },
70
    { 2, 0, 2, 0, 2, 2, 3, 3 },
71
    { 2, 0, 2, 0, 2, 0, 2, 0 },
72
    { 2, 2, 2, 0, 2, 2, 3, 3 },
73
    { 2, 2, 2, 0, 2, 2, 2, 0 }
74
};
75

    
76
/* 1 = write OK */
77
static const int rw_table[2][8] = {
78
    { 0, 1, 0, 1, 0, 1, 0, 1 },
79
    { 0, 1, 0, 1, 0, 0, 0, 0 }
80
};
81

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

    
239
    if ((env->lsu & DMMU_E) == 0) { /* DMMU disabled */
240
        *physical = address & 0xffffffff;
241
        *prot = PAGE_READ | PAGE_WRITE;
242
        return 0;
243
    }
244

    
245
    for (i = 0; i < 64; i++) {
246
        if ((env->dtlb_tte[i] & 0x8000000000000000ULL) != 0) {
247
            switch (env->dtlb_tte[i] >> 60) {
248
            default:
249
            case 0x4: // 8k
250
                mask = 0xffffffffffffe000ULL;
251
                break;
252
            case 0x5: // 64k
253
                mask = 0xffffffffffff0000ULL;
254
                break;
255
            case 0x6: // 512k
256
                mask = 0xfffffffffff80000ULL;
257
                break;
258
            case 0x7: // 4M
259
                mask = 0xffffffffffc00000ULL;
260
                break;
261
            }
262
            // ctx match, vaddr match?
263
            if (env->dmmuregs[1] == (env->dtlb_tag[i] & 0x1fff) &&
264
                (address & mask) == (env->dtlb_tag[i] & ~0x1fffULL)) {
265
                // access ok?
266
                if (((env->dtlb_tte[i] & 0x4) && !(env->pstate & PS_PRIV)) ||
267
                    (!(env->dtlb_tte[i] & 0x2) && (rw == 1))) {
268
                    env->exception_index = TT_DFAULT;
269
                    return 1;
270
                }
271
                *physical = env->dtlb_tte[i] & 0xffffe000;
272
                *prot = PAGE_READ;
273
                if (env->dtlb_tte[i] & 0x2)
274
                    *prot |= PAGE_WRITE;
275
                return 0;
276
            }
277
        }
278
    }
279
    env->exception_index = TT_DFAULT;
280
    return 1;
281
}
282

    
283
static int get_physical_address_code(CPUState *env, target_phys_addr_t *physical, int *prot,
284
                          int *access_index, target_ulong address, int rw,
285
                          int is_user)
286
{
287
    target_ulong mask;
288
    unsigned int i;
289

    
290
    if ((env->lsu & IMMU_E) == 0) { /* IMMU disabled */
291
        *physical = address & 0xffffffff;
292
        *prot = PAGE_READ;
293
        return 0;
294
    }
295
    for (i = 0; i < 64; i++) {
296
        if ((env->itlb_tte[i] & 0x8000000000000000ULL) != 0) {
297
            switch (env->itlb_tte[i] >> 60) {
298
            default:
299
            case 0x4: // 8k
300
                mask = 0xffffffffffffe000ULL;
301
                break;
302
            case 0x5: // 64k
303
                mask = 0xffffffffffff0000ULL;
304
                break;
305
            case 0x6: // 512k
306
                mask = 0xfffffffffff80000ULL;
307
                break;
308
            case 0x7: // 4M
309
                mask = 0xffffffffffc00000ULL;
310
                break;
311
            }
312
            // ctx match, vaddr match?
313
            if (env->immuregs[1] == (env->itlb_tag[i] & 0x1fff) &&
314
                (address & mask) == (env->itlb_tag[i] & ~0x1fffULL)) {
315
                // access ok?
316
                if ((env->itlb_tte[i] & 0x4) && !(env->pstate & PS_PRIV)) {
317
                    env->exception_index = TT_TFAULT;
318
                    return 1;
319
                }
320
                *physical = env->itlb_tte[i] & 0xffffe000;
321
                *prot = PAGE_READ;
322
                return 0;
323
            }
324
        }
325
    }
326
    env->exception_index = TT_TFAULT;
327
    return 1;
328
}
329

    
330
int get_physical_address(CPUState *env, target_phys_addr_t *physical, int *prot,
331
                          int *access_index, target_ulong address, int rw,
332
                          int is_user)
333
{
334
    if (rw == 2)
335
        return get_physical_address_code(env, physical, prot, access_index, address, rw, is_user);
336
    else
337
        return get_physical_address_data(env, physical, prot, access_index, address, rw, is_user);
338
}
339

    
340
/* Perform address translation */
341
int cpu_sparc_handle_mmu_fault (CPUState *env, target_ulong address, int rw,
342
                              int is_user, int is_softmmu)
343
{
344
    target_ulong virt_addr;
345
    target_phys_addr_t paddr;
346
    unsigned long vaddr;
347
    int error_code = 0, prot, ret = 0, access_index;
348

    
349
    error_code = get_physical_address(env, &paddr, &prot, &access_index, address, rw, is_user);
350
    if (error_code == 0) {
351
        virt_addr = address & TARGET_PAGE_MASK;
352
        vaddr = virt_addr + ((address & TARGET_PAGE_MASK) & (TARGET_PAGE_SIZE - 1));
353
        ret = tlb_set_page(env, vaddr, paddr, prot, is_user, is_softmmu);
354
        return ret;
355
    }
356
    // XXX
357
    return 1;
358
}
359

    
360
#endif
361
#endif
362

    
363
void memcpy32(target_ulong *dst, const target_ulong *src)
364
{
365
    dst[0] = src[0];
366
    dst[1] = src[1];
367
    dst[2] = src[2];
368
    dst[3] = src[3];
369
    dst[4] = src[4];
370
    dst[5] = src[5];
371
    dst[6] = src[6];
372
    dst[7] = src[7];
373
}
374

    
375
#if !defined(TARGET_SPARC64)
376
target_ulong mmu_probe(CPUState *env, target_ulong address, int mmulev)
377
{
378
    target_phys_addr_t pde_ptr;
379
    uint32_t pde;
380

    
381
    /* Context base + context number */
382
    pde_ptr = (env->mmuregs[1] << 4) + (env->mmuregs[2] << 2);
383
    pde = ldl_phys(pde_ptr);
384

    
385
    switch (pde & PTE_ENTRYTYPE_MASK) {
386
    default:
387
    case 0: /* Invalid */
388
    case 2: /* PTE, maybe should not happen? */
389
    case 3: /* Reserved */
390
        return 0;
391
    case 1: /* L1 PDE */
392
        if (mmulev == 3)
393
            return pde;
394
        pde_ptr = ((address >> 22) & ~3) + ((pde & ~3) << 4);
395
        pde = ldl_phys(pde_ptr);
396

    
397
        switch (pde & PTE_ENTRYTYPE_MASK) {
398
        default:
399
        case 0: /* Invalid */
400
        case 3: /* Reserved */
401
            return 0;
402
        case 2: /* L1 PTE */
403
            return pde;
404
        case 1: /* L2 PDE */
405
            if (mmulev == 2)
406
                return pde;
407
            pde_ptr = ((address & 0xfc0000) >> 16) + ((pde & ~3) << 4);
408
            pde = ldl_phys(pde_ptr);
409

    
410
            switch (pde & PTE_ENTRYTYPE_MASK) {
411
            default:
412
            case 0: /* Invalid */
413
            case 3: /* Reserved */
414
                return 0;
415
            case 2: /* L2 PTE */
416
                return pde;
417
            case 1: /* L3 PDE */
418
                if (mmulev == 1)
419
                    return pde;
420
                pde_ptr = ((address & 0x3f000) >> 10) + ((pde & ~3) << 4);
421
                pde = ldl_phys(pde_ptr);
422

    
423
                switch (pde & PTE_ENTRYTYPE_MASK) {
424
                default:
425
                case 0: /* Invalid */
426
                case 1: /* PDE, should not happen */
427
                case 3: /* Reserved */
428
                    return 0;
429
                case 2: /* L3 PTE */
430
                    return pde;
431
                }
432
            }
433
        }
434
    }
435
    return 0;
436
}
437

    
438
#ifdef DEBUG_MMU
439
void dump_mmu(CPUState *env)
440
{
441
     target_ulong va, va1, va2;
442
     unsigned int n, m, o;
443
     target_phys_addr_t pde_ptr, pa;
444
    uint32_t pde;
445

    
446
    printf("MMU dump:\n");
447
    pde_ptr = (env->mmuregs[1] << 4) + (env->mmuregs[2] << 2);
448
    pde = ldl_phys(pde_ptr);
449
    printf("Root ptr: " TARGET_FMT_lx ", ctx: %d\n", env->mmuregs[1] << 4, env->mmuregs[2]);
450
    for (n = 0, va = 0; n < 256; n++, va += 16 * 1024 * 1024) {
451
        pde_ptr = mmu_probe(env, va, 2);
452
        if (pde_ptr) {
453
            pa = cpu_get_phys_page_debug(env, va);
454
             printf("VA: " TARGET_FMT_lx ", PA: " TARGET_FMT_lx " PDE: " TARGET_FMT_lx "\n", va, pa, pde_ptr);
455
            for (m = 0, va1 = va; m < 64; m++, va1 += 256 * 1024) {
456
                pde_ptr = mmu_probe(env, va1, 1);
457
                if (pde_ptr) {
458
                    pa = cpu_get_phys_page_debug(env, va1);
459
                     printf(" VA: " TARGET_FMT_lx ", PA: " TARGET_FMT_lx " PDE: " TARGET_FMT_lx "\n", va1, pa, pde_ptr);
460
                    for (o = 0, va2 = va1; o < 64; o++, va2 += 4 * 1024) {
461
                        pde_ptr = mmu_probe(env, va2, 0);
462
                        if (pde_ptr) {
463
                            pa = cpu_get_phys_page_debug(env, va2);
464
                             printf("  VA: " TARGET_FMT_lx ", PA: " TARGET_FMT_lx " PTE: " TARGET_FMT_lx "\n", va2, pa, pde_ptr);
465
                        }
466
                    }
467
                }
468
            }
469
        }
470
    }
471
    printf("MMU dump ends\n");
472
}
473
#endif
474
#endif