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

#include "cpu.h"
#include "exec-all.h"
30
#include "qemu-common.h"
31

bellard's avatar
bellard committed
32
//#define DEBUG_MMU
blueswir1's avatar
blueswir1 committed
33
//#define DEBUG_FEATURES
34

blueswir1's avatar
blueswir1 committed
35
static int cpu_sparc_find_by_name(sparc_def_t *cpu_def, const char *cpu_model);
36

37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52
/* Sparc MMU emulation */

/* thread support */

spinlock_t global_cpu_lock = SPIN_LOCK_UNLOCKED;

void cpu_lock(void)
{
    spin_lock(&global_cpu_lock);
}

void cpu_unlock(void)
{
    spin_unlock(&global_cpu_lock);
}

53
#if defined(CONFIG_USER_ONLY)
bellard's avatar
bellard committed
54

blueswir1's avatar
blueswir1 committed
55
int cpu_sparc_handle_mmu_fault(CPUState *env1, target_ulong address, int rw,
56
                               int mmu_idx, int is_softmmu)
bellard's avatar
bellard committed
57
{
bellard's avatar
bellard committed
58
    if (rw & 2)
blueswir1's avatar
blueswir1 committed
59
        env1->exception_index = TT_TFAULT;
bellard's avatar
bellard committed
60
    else
blueswir1's avatar
blueswir1 committed
61
        env1->exception_index = TT_DFAULT;
bellard's avatar
bellard committed
62 63 64 65
    return 1;
}

#else
66

bellard's avatar
bellard committed
67
#ifndef TARGET_SPARC64
bellard's avatar
bellard committed
68 69 70
/*
 * Sparc V8 Reference MMU (SRMMU)
 */
71
static const int access_table[8][8] = {
72 73 74 75 76 77 78 79
    { 0, 0, 0, 0, 8, 0, 12, 12 },
    { 0, 0, 0, 0, 8, 0, 0, 0 },
    { 8, 8, 0, 0, 0, 8, 12, 12 },
    { 8, 8, 0, 0, 0, 8, 0, 0 },
    { 8, 0, 8, 0, 8, 8, 12, 12 },
    { 8, 0, 8, 0, 8, 0, 8, 0 },
    { 8, 8, 8, 0, 8, 8, 12, 12 },
    { 8, 8, 8, 0, 8, 8, 8, 0 }
80 81
};

bellard's avatar
bellard committed
82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102
static const int perm_table[2][8] = {
    {
        PAGE_READ,
        PAGE_READ | PAGE_WRITE,
        PAGE_READ | PAGE_EXEC,
        PAGE_READ | PAGE_WRITE | PAGE_EXEC,
        PAGE_EXEC,
        PAGE_READ | PAGE_WRITE,
        PAGE_READ | PAGE_EXEC,
        PAGE_READ | PAGE_WRITE | PAGE_EXEC
    },
    {
        PAGE_READ,
        PAGE_READ | PAGE_WRITE,
        PAGE_READ | PAGE_EXEC,
        PAGE_READ | PAGE_WRITE | PAGE_EXEC,
        PAGE_EXEC,
        PAGE_READ,
        0,
        0,
    }
103 104
};

105 106 107
static int get_physical_address(CPUState *env, target_phys_addr_t *physical,
                                int *prot, int *access_index,
                                target_ulong address, int rw, int mmu_idx)
108
{
bellard's avatar
bellard committed
109 110
    int access_perms = 0;
    target_phys_addr_t pde_ptr;
111 112
    uint32_t pde;
    target_ulong virt_addr;
113
    int error_code = 0, is_dirty, is_user;
bellard's avatar
bellard committed
114
    unsigned long page_offset;
115

116
    is_user = mmu_idx == MMU_USER_IDX;
117
    virt_addr = address & TARGET_PAGE_MASK;
blueswir1's avatar
blueswir1 committed
118

119
    if ((env->mmuregs[0] & MMU_E) == 0) { /* MMU disabled */
blueswir1's avatar
blueswir1 committed
120
        // Boot mode: instruction fetches are taken from PROM
121
        if (rw == 2 && (env->mmuregs[0] & env->def->mmu_bm)) {
blueswir1's avatar
blueswir1 committed
122
            *physical = env->prom_addr | (address & 0x7ffffULL);
blueswir1's avatar
blueswir1 committed
123 124 125
            *prot = PAGE_READ | PAGE_EXEC;
            return 0;
        }
blueswir1's avatar
blueswir1 committed
126
        *physical = address;
bellard's avatar
bellard committed
127
        *prot = PAGE_READ | PAGE_WRITE | PAGE_EXEC;
bellard's avatar
bellard committed
128
        return 0;
129 130
    }

bellard's avatar
bellard committed
131
    *access_index = ((rw & 1) << 2) | (rw & 2) | (is_user? 0 : 1);
132
    *physical = 0xffffffffffff0000ULL;
bellard's avatar
bellard committed
133

134 135
    /* SPARC reference MMU table walk: Context table->L1->L2->PTE */
    /* Context base + context number */
136
    pde_ptr = (env->mmuregs[1] << 4) + (env->mmuregs[2] << 2);
bellard's avatar
bellard committed
137
    pde = ldl_phys(pde_ptr);
138 139 140

    /* Ctx pde */
    switch (pde & PTE_ENTRYTYPE_MASK) {
bellard's avatar
bellard committed
141
    default:
142
    case 0: /* Invalid */
blueswir1's avatar
blueswir1 committed
143
        return 1 << 2;
bellard's avatar
bellard committed
144
    case 2: /* L0 PTE, maybe should not happen? */
145
    case 3: /* Reserved */
bellard's avatar
bellard committed
146
        return 4 << 2;
bellard's avatar
bellard committed
147
    case 1: /* L0 PDE */
blueswir1's avatar
blueswir1 committed
148
        pde_ptr = ((address >> 22) & ~3) + ((pde & ~3) << 4);
bellard's avatar
bellard committed
149
        pde = ldl_phys(pde_ptr);
150

blueswir1's avatar
blueswir1 committed
151 152 153 154 155 156 157 158
        switch (pde & PTE_ENTRYTYPE_MASK) {
        default:
        case 0: /* Invalid */
            return (1 << 8) | (1 << 2);
        case 3: /* Reserved */
            return (1 << 8) | (4 << 2);
        case 1: /* L1 PDE */
            pde_ptr = ((address & 0xfc0000) >> 16) + ((pde & ~3) << 4);
bellard's avatar
bellard committed
159
            pde = ldl_phys(pde_ptr);
160

blueswir1's avatar
blueswir1 committed
161 162 163 164 165 166 167 168
            switch (pde & PTE_ENTRYTYPE_MASK) {
            default:
            case 0: /* Invalid */
                return (2 << 8) | (1 << 2);
            case 3: /* Reserved */
                return (2 << 8) | (4 << 2);
            case 1: /* L2 PDE */
                pde_ptr = ((address & 0x3f000) >> 10) + ((pde & ~3) << 4);
bellard's avatar
bellard committed
169
                pde = ldl_phys(pde_ptr);
170

blueswir1's avatar
blueswir1 committed
171 172 173 174 175 176 177 178 179
                switch (pde & PTE_ENTRYTYPE_MASK) {
                default:
                case 0: /* Invalid */
                    return (3 << 8) | (1 << 2);
                case 1: /* PDE, should not happen */
                case 3: /* Reserved */
                    return (3 << 8) | (4 << 2);
                case 2: /* L3 PTE */
                    virt_addr = address & TARGET_PAGE_MASK;
blueswir1's avatar
blueswir1 committed
180 181
                    page_offset = (address & TARGET_PAGE_MASK) &
                        (TARGET_PAGE_SIZE - 1);
blueswir1's avatar
blueswir1 committed
182 183 184 185 186 187 188 189 190 191 192
                }
                break;
            case 2: /* L2 PTE */
                virt_addr = address & ~0x3ffff;
                page_offset = address & 0x3ffff;
            }
            break;
        case 2: /* L1 PTE */
            virt_addr = address & ~0xffffff;
            page_offset = address & 0xffffff;
        }
193 194 195
    }

    /* update page modified and dirty bits */
bellard's avatar
bellard committed
196
    is_dirty = (rw & 1) && !(pde & PG_MODIFIED_MASK);
197
    if (!(pde & PG_ACCESSED_MASK) || is_dirty) {
blueswir1's avatar
blueswir1 committed
198 199 200
        pde |= PG_ACCESSED_MASK;
        if (is_dirty)
            pde |= PG_MODIFIED_MASK;
bellard's avatar
bellard committed
201
        stl_phys_notdirty(pde_ptr, pde);
202 203 204
    }
    /* check access */
    access_perms = (pde & PTE_ACCESS_MASK) >> PTE_ACCESS_SHIFT;
bellard's avatar
bellard committed
205
    error_code = access_table[*access_index][access_perms];
bellard's avatar
bellard committed
206
    if (error_code && !((env->mmuregs[0] & MMU_NF) && is_user))
blueswir1's avatar
blueswir1 committed
207
        return error_code;
208 209

    /* the page can be put in the TLB */
bellard's avatar
bellard committed
210 211
    *prot = perm_table[is_user][access_perms];
    if (!(pde & PG_MODIFIED_MASK)) {
212 213
        /* only set write access if already dirty... otherwise wait
           for dirty access */
bellard's avatar
bellard committed
214
        *prot &= ~PAGE_WRITE;
215 216 217 218
    }

    /* Even if large ptes, we map only one 4KB page in the cache to
       avoid filling it too fast */
219
    *physical = ((target_phys_addr_t)(pde & PTE_ADDR_MASK) << 4) + page_offset;
bellard's avatar
bellard committed
220
    return error_code;
bellard's avatar
bellard committed
221 222 223
}

/* Perform address translation */
224
int cpu_sparc_handle_mmu_fault (CPUState *env, target_ulong address, int rw,
225
                              int mmu_idx, int is_softmmu)
bellard's avatar
bellard committed
226
{
227
    target_phys_addr_t paddr;
228
    target_ulong vaddr;
bellard's avatar
bellard committed
229
    int error_code = 0, prot, ret = 0, access_index;
230

blueswir1's avatar
blueswir1 committed
231 232
    error_code = get_physical_address(env, &paddr, &prot, &access_index,
                                      address, rw, mmu_idx);
bellard's avatar
bellard committed
233
    if (error_code == 0) {
blueswir1's avatar
blueswir1 committed
234 235
        vaddr = address & TARGET_PAGE_MASK;
        paddr &= TARGET_PAGE_MASK;
bellard's avatar
bellard committed
236
#ifdef DEBUG_MMU
blueswir1's avatar
blueswir1 committed
237
        printf("Translate at " TARGET_FMT_lx " -> " TARGET_FMT_plx ", vaddr "
238
               TARGET_FMT_lx "\n", address, paddr, vaddr);
bellard's avatar
bellard committed
239
#endif
240
        ret = tlb_set_page_exec(env, vaddr, paddr, prot, mmu_idx, is_softmmu);
blueswir1's avatar
blueswir1 committed
241
        return ret;
bellard's avatar
bellard committed
242
    }
243 244

    if (env->mmuregs[3]) /* Fault status register */
blueswir1's avatar
blueswir1 committed
245
        env->mmuregs[3] = 1; /* overflow (not read before another fault) */
bellard's avatar
bellard committed
246
    env->mmuregs[3] |= (access_index << 5) | error_code | 2;
247 248
    env->mmuregs[4] = address; /* Fault address register */

bellard's avatar
bellard committed
249
    if ((env->mmuregs[0] & MMU_NF) || env->psret == 0)  {
bellard's avatar
bellard committed
250 251 252 253
        // No fault mode: if a mapping is available, just override
        // permissions. If no mapping is available, redirect accesses to
        // neverland. Fake/overridden mappings will be flushed when
        // switching to normal mode.
blueswir1's avatar
blueswir1 committed
254
        vaddr = address & TARGET_PAGE_MASK;
bellard's avatar
bellard committed
255
        prot = PAGE_READ | PAGE_WRITE | PAGE_EXEC;
256
        ret = tlb_set_page_exec(env, vaddr, paddr, prot, mmu_idx, is_softmmu);
blueswir1's avatar
blueswir1 committed
257
        return ret;
bellard's avatar
bellard committed
258 259 260 261 262 263
    } else {
        if (rw & 2)
            env->exception_index = TT_TFAULT;
        else
            env->exception_index = TT_DFAULT;
        return 1;
bellard's avatar
bellard committed
264
    }
265
}
266 267 268 269 270 271 272

target_ulong mmu_probe(CPUState *env, target_ulong address, int mmulev)
{
    target_phys_addr_t pde_ptr;
    uint32_t pde;

    /* Context base + context number */
273 274
    pde_ptr = (target_phys_addr_t)(env->mmuregs[1] << 4) +
        (env->mmuregs[2] << 2);
275 276 277 278 279 280 281
    pde = ldl_phys(pde_ptr);

    switch (pde & PTE_ENTRYTYPE_MASK) {
    default:
    case 0: /* Invalid */
    case 2: /* PTE, maybe should not happen? */
    case 3: /* Reserved */
blueswir1's avatar
blueswir1 committed
282
        return 0;
283
    case 1: /* L1 PDE */
blueswir1's avatar
blueswir1 committed
284 285 286
        if (mmulev == 3)
            return pde;
        pde_ptr = ((address >> 22) & ~3) + ((pde & ~3) << 4);
287 288
        pde = ldl_phys(pde_ptr);

blueswir1's avatar
blueswir1 committed
289 290 291 292 293 294 295 296 297 298 299
        switch (pde & PTE_ENTRYTYPE_MASK) {
        default:
        case 0: /* Invalid */
        case 3: /* Reserved */
            return 0;
        case 2: /* L1 PTE */
            return pde;
        case 1: /* L2 PDE */
            if (mmulev == 2)
                return pde;
            pde_ptr = ((address & 0xfc0000) >> 16) + ((pde & ~3) << 4);
300 301
            pde = ldl_phys(pde_ptr);

blueswir1's avatar
blueswir1 committed
302 303 304 305 306 307 308 309 310 311 312
            switch (pde & PTE_ENTRYTYPE_MASK) {
            default:
            case 0: /* Invalid */
            case 3: /* Reserved */
                return 0;
            case 2: /* L2 PTE */
                return pde;
            case 1: /* L3 PDE */
                if (mmulev == 1)
                    return pde;
                pde_ptr = ((address & 0x3f000) >> 10) + ((pde & ~3) << 4);
313 314
                pde = ldl_phys(pde_ptr);

blueswir1's avatar
blueswir1 committed
315 316 317 318 319 320 321 322 323 324 325
                switch (pde & PTE_ENTRYTYPE_MASK) {
                default:
                case 0: /* Invalid */
                case 1: /* PDE, should not happen */
                case 3: /* Reserved */
                    return 0;
                case 2: /* L3 PTE */
                    return pde;
                }
            }
        }
326 327 328 329 330 331 332
    }
    return 0;
}

#ifdef DEBUG_MMU
void dump_mmu(CPUState *env)
{
333 334 335
    target_ulong va, va1, va2;
    unsigned int n, m, o;
    target_phys_addr_t pde_ptr, pa;
336 337 338 339 340
    uint32_t pde;

    printf("MMU dump:\n");
    pde_ptr = (env->mmuregs[1] << 4) + (env->mmuregs[2] << 2);
    pde = ldl_phys(pde_ptr);
341 342
    printf("Root ptr: " TARGET_FMT_plx ", ctx: %d\n",
           (target_phys_addr_t)env->mmuregs[1] << 4, env->mmuregs[2]);
343
    for (n = 0, va = 0; n < 256; n++, va += 16 * 1024 * 1024) {
blueswir1's avatar
blueswir1 committed
344 345 346 347
        pde = mmu_probe(env, va, 2);
        if (pde) {
            pa = cpu_get_phys_page_debug(env, va);
            printf("VA: " TARGET_FMT_lx ", PA: " TARGET_FMT_plx
348
                   " PDE: " TARGET_FMT_lx "\n", va, pa, pde);
blueswir1's avatar
blueswir1 committed
349 350 351 352 353
            for (m = 0, va1 = va; m < 64; m++, va1 += 256 * 1024) {
                pde = mmu_probe(env, va1, 1);
                if (pde) {
                    pa = cpu_get_phys_page_debug(env, va1);
                    printf(" VA: " TARGET_FMT_lx ", PA: " TARGET_FMT_plx
354
                           " PDE: " TARGET_FMT_lx "\n", va1, pa, pde);
blueswir1's avatar
blueswir1 committed
355 356 357 358 359
                    for (o = 0, va2 = va1; o < 64; o++, va2 += 4 * 1024) {
                        pde = mmu_probe(env, va2, 0);
                        if (pde) {
                            pa = cpu_get_phys_page_debug(env, va2);
                            printf("  VA: " TARGET_FMT_lx ", PA: "
360 361
                                   TARGET_FMT_plx " PTE: " TARGET_FMT_lx "\n",
                                   va2, pa, pde);
blueswir1's avatar
blueswir1 committed
362 363 364 365 366
                        }
                    }
                }
            }
        }
367 368 369 370 371 372
    }
    printf("MMU dump ends\n");
}
#endif /* DEBUG_MMU */

#else /* !TARGET_SPARC64 */
bellard's avatar
bellard committed
373 374 375
/*
 * UltraSparc IIi I/DMMUs
 */
blueswir1's avatar
blueswir1 committed
376 377
static int get_physical_address_data(CPUState *env,
                                     target_phys_addr_t *physical, int *prot,
blueswir1's avatar
blueswir1 committed
378
                                     target_ulong address, int rw, int is_user)
bellard's avatar
bellard committed
379 380 381 382 383
{
    target_ulong mask;
    unsigned int i;

    if ((env->lsu & DMMU_E) == 0) { /* DMMU disabled */
blueswir1's avatar
blueswir1 committed
384 385
        *physical = address;
        *prot = PAGE_READ | PAGE_WRITE;
bellard's avatar
bellard committed
386 387 388 389
        return 0;
    }

    for (i = 0; i < 64; i++) {
blueswir1's avatar
blueswir1 committed
390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412
        switch ((env->dtlb_tte[i] >> 61) & 3) {
        default:
        case 0x0: // 8k
            mask = 0xffffffffffffe000ULL;
            break;
        case 0x1: // 64k
            mask = 0xffffffffffff0000ULL;
            break;
        case 0x2: // 512k
            mask = 0xfffffffffff80000ULL;
            break;
        case 0x3: // 4M
            mask = 0xffffffffffc00000ULL;
            break;
        }
        // ctx match, vaddr match?
        if (env->dmmuregs[1] == (env->dtlb_tag[i] & 0x1fff) &&
            (address & mask) == (env->dtlb_tag[i] & ~0x1fffULL)) {
            // valid, access ok?
            if ((env->dtlb_tte[i] & 0x8000000000000000ULL) == 0 ||
                ((env->dtlb_tte[i] & 0x4) && is_user) ||
                (!(env->dtlb_tte[i] & 0x2) && (rw == 1))) {
                if (env->dmmuregs[3]) /* Fault status register */
blueswir1's avatar
blueswir1 committed
413 414
                    env->dmmuregs[3] = 2; /* overflow (not read before
                                             another fault) */
blueswir1's avatar
blueswir1 committed
415 416 417
                env->dmmuregs[3] |= (is_user << 3) | ((rw == 1) << 2) | 1;
                env->dmmuregs[4] = address; /* Fault address register */
                env->exception_index = TT_DFAULT;
bellard's avatar
bellard committed
418
#ifdef DEBUG_MMU
blueswir1's avatar
blueswir1 committed
419
                printf("DFAULT at 0x%" PRIx64 "\n", address);
bellard's avatar
bellard committed
420
#endif
blueswir1's avatar
blueswir1 committed
421 422
                return 1;
            }
blueswir1's avatar
blueswir1 committed
423 424
            *physical = (env->dtlb_tte[i] & mask & 0x1fffffff000ULL) +
                (address & ~mask & 0x1fffffff000ULL);
blueswir1's avatar
blueswir1 committed
425 426 427 428 429
            *prot = PAGE_READ;
            if (env->dtlb_tte[i] & 0x2)
                *prot |= PAGE_WRITE;
            return 0;
        }
bellard's avatar
bellard committed
430
    }
bellard's avatar
bellard committed
431
#ifdef DEBUG_MMU
bellard's avatar
bellard committed
432
    printf("DMISS at 0x%" PRIx64 "\n", address);
bellard's avatar
bellard committed
433
#endif
blueswir1's avatar
blueswir1 committed
434
    env->dmmuregs[6] = (address & ~0x1fffULL) | (env->dmmuregs[1] & 0x1fff);
bellard's avatar
bellard committed
435
    env->exception_index = TT_DMISS;
bellard's avatar
bellard committed
436 437 438
    return 1;
}

blueswir1's avatar
blueswir1 committed
439 440
static int get_physical_address_code(CPUState *env,
                                     target_phys_addr_t *physical, int *prot,
blueswir1's avatar
blueswir1 committed
441
                                     target_ulong address, int is_user)
bellard's avatar
bellard committed
442 443 444 445 446
{
    target_ulong mask;
    unsigned int i;

    if ((env->lsu & IMMU_E) == 0) { /* IMMU disabled */
blueswir1's avatar
blueswir1 committed
447 448
        *physical = address;
        *prot = PAGE_EXEC;
bellard's avatar
bellard committed
449 450
        return 0;
    }
bellard's avatar
bellard committed
451

bellard's avatar
bellard committed
452
    for (i = 0; i < 64; i++) {
blueswir1's avatar
blueswir1 committed
453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474
        switch ((env->itlb_tte[i] >> 61) & 3) {
        default:
        case 0x0: // 8k
            mask = 0xffffffffffffe000ULL;
            break;
        case 0x1: // 64k
            mask = 0xffffffffffff0000ULL;
            break;
        case 0x2: // 512k
            mask = 0xfffffffffff80000ULL;
            break;
        case 0x3: // 4M
            mask = 0xffffffffffc00000ULL;
                break;
        }
        // ctx match, vaddr match?
        if (env->dmmuregs[1] == (env->itlb_tag[i] & 0x1fff) &&
            (address & mask) == (env->itlb_tag[i] & ~0x1fffULL)) {
            // valid, access ok?
            if ((env->itlb_tte[i] & 0x8000000000000000ULL) == 0 ||
                ((env->itlb_tte[i] & 0x4) && is_user)) {
                if (env->immuregs[3]) /* Fault status register */
blueswir1's avatar
blueswir1 committed
475 476
                    env->immuregs[3] = 2; /* overflow (not read before
                                             another fault) */
blueswir1's avatar
blueswir1 committed
477 478
                env->immuregs[3] |= (is_user << 3) | 1;
                env->exception_index = TT_TFAULT;
bellard's avatar
bellard committed
479
#ifdef DEBUG_MMU
blueswir1's avatar
blueswir1 committed
480
                printf("TFAULT at 0x%" PRIx64 "\n", address);
bellard's avatar
bellard committed
481
#endif
blueswir1's avatar
blueswir1 committed
482 483
                return 1;
            }
blueswir1's avatar
blueswir1 committed
484 485
            *physical = (env->itlb_tte[i] & mask & 0x1fffffff000ULL) +
                (address & ~mask & 0x1fffffff000ULL);
blueswir1's avatar
blueswir1 committed
486 487 488
            *prot = PAGE_EXEC;
            return 0;
        }
bellard's avatar
bellard committed
489
    }
bellard's avatar
bellard committed
490
#ifdef DEBUG_MMU
bellard's avatar
bellard committed
491
    printf("TMISS at 0x%" PRIx64 "\n", address);
bellard's avatar
bellard committed
492
#endif
blueswir1's avatar
blueswir1 committed
493
    env->immuregs[6] = (address & ~0x1fffULL) | (env->dmmuregs[1] & 0x1fff);
bellard's avatar
bellard committed
494
    env->exception_index = TT_TMISS;
bellard's avatar
bellard committed
495 496 497
    return 1;
}

498 499 500
static int get_physical_address(CPUState *env, target_phys_addr_t *physical,
                                int *prot, int *access_index,
                                target_ulong address, int rw, int mmu_idx)
bellard's avatar
bellard committed
501
{
502 503
    int is_user = mmu_idx == MMU_USER_IDX;

bellard's avatar
bellard committed
504
    if (rw == 2)
blueswir1's avatar
blueswir1 committed
505 506
        return get_physical_address_code(env, physical, prot, address,
                                         is_user);
bellard's avatar
bellard committed
507
    else
blueswir1's avatar
blueswir1 committed
508 509
        return get_physical_address_data(env, physical, prot, address, rw,
                                         is_user);
bellard's avatar
bellard committed
510 511 512 513
}

/* Perform address translation */
int cpu_sparc_handle_mmu_fault (CPUState *env, target_ulong address, int rw,
514
                              int mmu_idx, int is_softmmu)
bellard's avatar
bellard committed
515
{
bellard's avatar
bellard committed
516
    target_ulong virt_addr, vaddr;
bellard's avatar
bellard committed
517 518 519
    target_phys_addr_t paddr;
    int error_code = 0, prot, ret = 0, access_index;

blueswir1's avatar
blueswir1 committed
520 521
    error_code = get_physical_address(env, &paddr, &prot, &access_index,
                                      address, rw, mmu_idx);
bellard's avatar
bellard committed
522
    if (error_code == 0) {
blueswir1's avatar
blueswir1 committed
523
        virt_addr = address & TARGET_PAGE_MASK;
blueswir1's avatar
blueswir1 committed
524 525
        vaddr = virt_addr + ((address & TARGET_PAGE_MASK) &
                             (TARGET_PAGE_SIZE - 1));
bellard's avatar
bellard committed
526
#ifdef DEBUG_MMU
blueswir1's avatar
blueswir1 committed
527 528
        printf("Translate at 0x%" PRIx64 " -> 0x%" PRIx64 ", vaddr 0x%" PRIx64
               "\n", address, paddr, vaddr);
bellard's avatar
bellard committed
529
#endif
530
        ret = tlb_set_page_exec(env, vaddr, paddr, prot, mmu_idx, is_softmmu);
blueswir1's avatar
blueswir1 committed
531
        return ret;
bellard's avatar
bellard committed
532 533 534 535 536
    }
    // XXX
    return 1;
}

bellard's avatar
bellard committed
537 538 539 540 541 542
#ifdef DEBUG_MMU
void dump_mmu(CPUState *env)
{
    unsigned int i;
    const char *mask;

blueswir1's avatar
blueswir1 committed
543 544
    printf("MMU contexts: Primary: %" PRId64 ", Secondary: %" PRId64 "\n",
           env->dmmuregs[1], env->dmmuregs[2]);
bellard's avatar
bellard committed
545
    if ((env->lsu & DMMU_E) == 0) {
blueswir1's avatar
blueswir1 committed
546
        printf("DMMU disabled\n");
bellard's avatar
bellard committed
547
    } else {
blueswir1's avatar
blueswir1 committed
548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565
        printf("DMMU dump:\n");
        for (i = 0; i < 64; i++) {
            switch ((env->dtlb_tte[i] >> 61) & 3) {
            default:
            case 0x0:
                mask = "  8k";
                break;
            case 0x1:
                mask = " 64k";
                break;
            case 0x2:
                mask = "512k";
                break;
            case 0x3:
                mask = "  4M";
                break;
            }
            if ((env->dtlb_tte[i] & 0x8000000000000000ULL) != 0) {
blueswir1's avatar
blueswir1 committed
566 567
                printf("VA: " TARGET_FMT_lx ", PA: " TARGET_FMT_lx
                       ", %s, %s, %s, %s, ctx %" PRId64 "\n",
blueswir1's avatar
blueswir1 committed
568 569 570 571 572 573 574 575 576
                       env->dtlb_tag[i] & ~0x1fffULL,
                       env->dtlb_tte[i] & 0x1ffffffe000ULL,
                       mask,
                       env->dtlb_tte[i] & 0x4? "priv": "user",
                       env->dtlb_tte[i] & 0x2? "RW": "RO",
                       env->dtlb_tte[i] & 0x40? "locked": "unlocked",
                       env->dtlb_tag[i] & 0x1fffULL);
            }
        }
bellard's avatar
bellard committed
577 578
    }
    if ((env->lsu & IMMU_E) == 0) {
blueswir1's avatar
blueswir1 committed
579
        printf("IMMU disabled\n");
bellard's avatar
bellard committed
580
    } else {
blueswir1's avatar
blueswir1 committed
581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598
        printf("IMMU dump:\n");
        for (i = 0; i < 64; i++) {
            switch ((env->itlb_tte[i] >> 61) & 3) {
            default:
            case 0x0:
                mask = "  8k";
                break;
            case 0x1:
                mask = " 64k";
                break;
            case 0x2:
                mask = "512k";
                break;
            case 0x3:
                mask = "  4M";
                break;
            }
            if ((env->itlb_tte[i] & 0x8000000000000000ULL) != 0) {
blueswir1's avatar
blueswir1 committed
599 600
                printf("VA: " TARGET_FMT_lx ", PA: " TARGET_FMT_lx
                       ", %s, %s, %s, ctx %" PRId64 "\n",
blueswir1's avatar
blueswir1 committed
601 602 603 604 605 606 607 608
                       env->itlb_tag[i] & ~0x1fffULL,
                       env->itlb_tte[i] & 0x1ffffffe000ULL,
                       mask,
                       env->itlb_tte[i] & 0x4? "priv": "user",
                       env->itlb_tte[i] & 0x40? "locked": "unlocked",
                       env->itlb_tag[i] & 0x1fffULL);
            }
        }
bellard's avatar
bellard committed
609 610
    }
}
611 612 613 614 615
#endif /* DEBUG_MMU */

#endif /* TARGET_SPARC64 */
#endif /* !CONFIG_USER_ONLY */

616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648

#if defined(CONFIG_USER_ONLY)
target_phys_addr_t cpu_get_phys_page_debug(CPUState *env, target_ulong addr)
{
    return addr;
}

#else
target_phys_addr_t cpu_get_phys_page_debug(CPUState *env, target_ulong addr)
{
    target_phys_addr_t phys_addr;
    int prot, access_index;

    if (get_physical_address(env, &phys_addr, &prot, &access_index, addr, 2,
                             MMU_KERNEL_IDX) != 0)
        if (get_physical_address(env, &phys_addr, &prot, &access_index, addr,
                                 0, MMU_KERNEL_IDX) != 0)
            return -1;
    if (cpu_get_physical_page_desc(phys_addr) == IO_MEM_UNASSIGNED)
        return -1;
    return phys_addr;
}
#endif

void cpu_reset(CPUSPARCState *env)
{
    tlb_flush(env, 1);
    env->cwp = 0;
    env->wim = 1;
    env->regwptr = env->regbase + (env->cwp * 16);
#if defined(CONFIG_USER_ONLY)
    env->user_mode_only = 1;
#ifdef TARGET_SPARC64
649 650
    env->cleanwin = env->nwindows - 2;
    env->cansave = env->nwindows - 2;
651 652 653 654 655 656 657 658 659 660
    env->pstate = PS_RMO | PS_PEF | PS_IE;
    env->asi = 0x82; // Primary no-fault
#endif
#else
    env->psret = 0;
    env->psrs = 1;
    env->psrps = 1;
#ifdef TARGET_SPARC64
    env->pstate = PS_PRIV;
    env->hpstate = HS_PRIV;
blueswir1's avatar
blueswir1 committed
661
    env->pc = 0x1fff0000020ULL; // XXX should be different for system_reset
662
    env->tsptr = &env->ts[env->tl & MAXTL_MASK];
663 664 665
#else
    env->pc = 0;
    env->mmuregs[0] &= ~(MMU_E | MMU_NF);
666
    env->mmuregs[0] |= env->def->mmu_bm;
667 668 669 670 671
#endif
    env->npc = env->pc + 4;
#endif
}

blueswir1's avatar
blueswir1 committed
672
static int cpu_sparc_register(CPUSPARCState *env, const char *cpu_model)
673
{
blueswir1's avatar
blueswir1 committed
674
    sparc_def_t def1, *def = &def1;
675

blueswir1's avatar
blueswir1 committed
676 677
    if (cpu_sparc_find_by_name(def, cpu_model) < 0)
        return -1;
678

679 680 681 682 683 684
    env->def = qemu_mallocz(sizeof(*def));
    memcpy(env->def, def, sizeof(*def));
#if defined(CONFIG_USER_ONLY)
    if ((env->def->features & CPU_FEATURE_FLOAT))
        env->def->features |= CPU_FEATURE_FLOAT128;
#endif
685 686 687
    env->cpu_model_str = cpu_model;
    env->version = def->iu_version;
    env->fsr = def->fpu_version;
688
    env->nwindows = def->nwindows;
689 690 691
#if !defined(TARGET_SPARC64)
    env->mmuregs[0] |= def->mmu_version;
    cpu_sparc_set_id(env, 0);
692
#else
693
    env->mmu_version = def->mmu_version;
694 695
    env->maxtl = def->maxtl;
    env->version |= def->maxtl << 8;
696
    env->version |= def->nwindows - 1;
697
#endif
blueswir1's avatar
blueswir1 committed
698 699 700 701 702
    return 0;
}

static void cpu_sparc_close(CPUSPARCState *env)
{
703
    free(env->def);
blueswir1's avatar
blueswir1 committed
704 705 706 707 708 709 710 711 712 713 714
    free(env);
}

CPUSPARCState *cpu_sparc_init(const char *cpu_model)
{
    CPUSPARCState *env;

    env = qemu_mallocz(sizeof(CPUSPARCState));
    if (!env)
        return NULL;
    cpu_exec_init(env);
715 716 717

    gen_intermediate_code_init(env);

blueswir1's avatar
blueswir1 committed
718 719 720 721
    if (cpu_sparc_register(env, cpu_model) < 0) {
        cpu_sparc_close(env);
        return NULL;
    }
722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737
    cpu_reset(env);

    return env;
}

void cpu_sparc_set_id(CPUSPARCState *env, unsigned int cpu)
{
#if !defined(TARGET_SPARC64)
    env->mxccregs[7] = ((cpu + 8) & 0xf) << 24;
#endif
}

static const sparc_def_t sparc_defs[] = {
#ifdef TARGET_SPARC64
    {
        .name = "Fujitsu Sparc64",
738
        .iu_version = ((0x04ULL << 48) | (0x02ULL << 32) | (0ULL << 24)),
739
        .fpu_version = 0x00000000,
740
        .mmu_version = mmu_us_12,
741
        .nwindows = 4,
742
        .maxtl = 4,
blueswir1's avatar
blueswir1 committed
743
        .features = CPU_DEFAULT_FEATURES,
744 745 746
    },
    {
        .name = "Fujitsu Sparc64 III",
747
        .iu_version = ((0x04ULL << 48) | (0x03ULL << 32) | (0ULL << 24)),
748
        .fpu_version = 0x00000000,
749
        .mmu_version = mmu_us_12,
750
        .nwindows = 5,
751
        .maxtl = 4,
blueswir1's avatar
blueswir1 committed
752
        .features = CPU_DEFAULT_FEATURES,
753 754 755
    },
    {
        .name = "Fujitsu Sparc64 IV",
756
        .iu_version = ((0x04ULL << 48) | (0x04ULL << 32) | (0ULL << 24)),
757
        .fpu_version = 0x00000000,
758
        .mmu_version = mmu_us_12,
759
        .nwindows = 8,
760
        .maxtl = 5,
blueswir1's avatar
blueswir1 committed
761
        .features = CPU_DEFAULT_FEATURES,
762 763 764
    },
    {
        .name = "Fujitsu Sparc64 V",
765
        .iu_version = ((0x04ULL << 48) | (0x05ULL << 32) | (0x51ULL << 24)),
766
        .fpu_version = 0x00000000,
767
        .mmu_version = mmu_us_12,
768
        .nwindows = 8,
769
        .maxtl = 5,
blueswir1's avatar
blueswir1 committed
770
        .features = CPU_DEFAULT_FEATURES,
771 772 773
    },
    {
        .name = "TI UltraSparc I",
774
        .iu_version = ((0x17ULL << 48) | (0x10ULL << 32) | (0x40ULL << 24)),
775
        .fpu_version = 0x00000000,
776
        .mmu_version = mmu_us_12,
777
        .nwindows = 8,
778
        .maxtl = 5,
blueswir1's avatar
blueswir1 committed
779
        .features = CPU_DEFAULT_FEATURES,
780 781 782
    },
    {
        .name = "TI UltraSparc II",
783
        .iu_version = ((0x17ULL << 48) | (0x11ULL << 32) | (0x20ULL << 24)),
784
        .fpu_version = 0x00000000,
785
        .mmu_version = mmu_us_12,
786
        .nwindows = 8,
787
        .maxtl = 5,
blueswir1's avatar
blueswir1 committed
788
        .features = CPU_DEFAULT_FEATURES,
789 790 791
    },
    {
        .name = "TI UltraSparc IIi",
792
        .iu_version = ((0x17ULL << 48) | (0x12ULL << 32) | (0x91ULL << 24)),
793
        .fpu_version = 0x00000000,
794
        .mmu_version = mmu_us_12,
795
        .nwindows = 8,
796
        .maxtl = 5,
blueswir1's avatar
blueswir1 committed
797
        .features = CPU_DEFAULT_FEATURES,
798 799 800
    },
    {
        .name = "TI UltraSparc IIe",
801
        .iu_version = ((0x17ULL << 48) | (0x13ULL << 32) | (0x14ULL << 24)),
802
        .fpu_version = 0x00000000,
803
        .mmu_version = mmu_us_12,
804
        .nwindows = 8,
805
        .maxtl = 5,
blueswir1's avatar
blueswir1 committed
806
        .features = CPU_DEFAULT_FEATURES,
807 808 809
    },
    {
        .name = "Sun UltraSparc III",
810
        .iu_version = ((0x3eULL << 48) | (0x14ULL << 32) | (0x34ULL << 24)),
811
        .fpu_version = 0x00000000,
812
        .mmu_version = mmu_us_12,
813
        .nwindows = 8,
814
        .maxtl = 5,
blueswir1's avatar
blueswir1 committed
815
        .features = CPU_DEFAULT_FEATURES,
816 817 818
    },
    {
        .name = "Sun UltraSparc III Cu",
819
        .iu_version = ((0x3eULL << 48) | (0x15ULL << 32) | (0x41ULL << 24)),
820
        .fpu_version = 0x00000000,
821
        .mmu_version = mmu_us_3,
822
        .nwindows = 8,
823
        .maxtl = 5,
blueswir1's avatar
blueswir1 committed
824
        .features = CPU_DEFAULT_FEATURES,
825 826 827
    },
    {
        .name = "Sun UltraSparc IIIi",
828
        .iu_version = ((0x3eULL << 48) | (0x16ULL << 32) | (0x34ULL << 24)),
829
        .fpu_version = 0x00000000,
830
        .mmu_version = mmu_us_12,
831
        .nwindows = 8,
832
        .maxtl = 5,
blueswir1's avatar
blueswir1 committed
833
        .features = CPU_DEFAULT_FEATURES,
834 835 836
    },
    {
        .name = "Sun UltraSparc IV",
837
        .iu_version = ((0x3eULL << 48) | (0x18ULL << 32) | (0x31ULL << 24)),
838
        .fpu_version = 0x00000000,
839
        .mmu_version = mmu_us_4,
840
        .nwindows = 8,
841
        .maxtl = 5,
blueswir1's avatar
blueswir1 committed
842
        .features = CPU_DEFAULT_FEATURES,
843 844 845
    },
    {
        .name = "Sun UltraSparc IV+",
846
        .iu_version = ((0x3eULL << 48) | (0x19ULL << 32) | (0x22ULL << 24)),
847
        .fpu_version = 0x00000000,
848
        .mmu_version = mmu_us_12,
849
        .nwindows = 8,
850
        .maxtl = 5,
851
        .features = CPU_DEFAULT_FEATURES | CPU_FEATURE_CMT,
852 853 854
    },
    {
        .name = "Sun UltraSparc IIIi+",
855
        .iu_version = ((0x3eULL << 48) | (0x22ULL << 32) | (0ULL << 24)),
856
        .fpu_version = 0x00000000,
857
        .mmu_version = mmu_us_3,
858
        .nwindows = 8,
859
        .maxtl = 5,
blueswir1's avatar
blueswir1 committed
860
        .features = CPU_DEFAULT_FEATURES,
861
    },
862 863 864
    {
        .name = "Sun UltraSparc T1",
        // defined in sparc_ifu_fdp.v and ctu.h
865
        .iu_version = ((0x3eULL << 48) | (0x23ULL << 32) | (0x02ULL << 24)),
866 867 868
        .fpu_version = 0x00000000,
        .mmu_version = mmu_sun4v,
        .nwindows = 8,
869
        .maxtl = 6,
870 871 872 873 874 875
        .features = CPU_DEFAULT_FEATURES | CPU_FEATURE_HYPV | CPU_FEATURE_CMT
        | CPU_FEATURE_GL,
    },
    {
        .name = "Sun UltraSparc T2",
        // defined in tlu_asi_ctl.v and n2_revid_cust.v
876
        .iu_version = ((0x3eULL << 48) | (0x24ULL << 32) | (0x02ULL << 24)),
877 878 879
        .fpu_version = 0x00000000,
        .mmu_version = mmu_sun4v,
        .nwindows = 8,
880
        .maxtl = 6,
881 882 883
        .features = CPU_DEFAULT_FEATURES | CPU_FEATURE_HYPV | CPU_FEATURE_CMT
        | CPU_FEATURE_GL,
    },
884 885
    {
        .name = "NEC UltraSparc I",
886
        .iu_version = ((0x22ULL << 48) | (0x10ULL << 32) | (0x40ULL << 24)),
887
        .fpu_version = 0x00000000,
888
        .mmu_version = mmu_us_12,
889
        .nwindows = 8,
890
        .maxtl = 5,
blueswir1's avatar
blueswir1 committed
891
        .features = CPU_DEFAULT_FEATURES,
892 893 894 895 896 897 898 899 900 901 902 903
    },
#else
    {
        .name = "Fujitsu MB86900",
        .iu_version = 0x00 << 24, /* Impl 0, ver 0 */
        .fpu_version = 4 << 17, /* FPU version 4 (Meiko) */
        .mmu_version = 0x00 << 24, /* Impl 0, ver 0 */
        .mmu_bm = 0x00004000,
        .mmu_ctpr_mask = 0x007ffff0,
        .mmu_cxr_mask = 0x0000003f,
        .mmu_sfsr_mask = 0xffffffff,
        .mmu_trcr_mask = 0xffffffff,
904
        .nwindows = 7,
blueswir1's avatar
blueswir1 committed
905
        .features = CPU_FEATURE_FLOAT | CPU_FEATURE_FSMULD,
906 907 908 909 910 911 912 913 914 915 916
    },
    {
        .name = "Fujitsu MB86904",
        .iu_version = 0x04 << 24, /* Impl 0, ver 4 */
        .fpu_version = 4 << 17, /* FPU version 4 (Meiko) */
        .mmu_version = 0x04 << 24, /* Impl 0, ver 4 */
        .mmu_bm = 0x00004000,
        .mmu_ctpr_mask = 0x00ffffc0,
        .mmu_cxr_mask = 0x000000ff,
        .mmu_sfsr_mask = 0x00016fff,
        .mmu_trcr_mask = 0x00ffffff,
917
        .nwindows = 8,
blueswir1's avatar
blueswir1 committed
918
        .features = CPU_DEFAULT_FEATURES,
blueswir1's avatar