helper.c 19.9 KB
Newer Older
B
bellard 已提交
1 2
/*
 *  MIPS emulation helpers for qemu.
3
 *
B
bellard 已提交
4 5 6 7 8 9 10 11 12 13 14 15 16
 *  Copyright (c) 2004-2005 Jocelyn Mayer
 *
 * 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
17
 * License along with this library; if not, see <http://www.gnu.org/licenses/>.
B
bellard 已提交
18
 */
19 20 21 22 23 24 25 26 27
#include <stdarg.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <inttypes.h>
#include <signal.h>

#include "cpu.h"
#include "exec-all.h"
B
bellard 已提交
28

29 30 31 32 33 34 35 36
enum {
    TLBRET_DIRTY = -4,
    TLBRET_INVALID = -3,
    TLBRET_NOMATCH = -2,
    TLBRET_BADADDR = -1,
    TLBRET_MATCH = 0
};

37 38
/* no MMU emulation */
int no_mmu_map_address (CPUState *env, target_ulong *physical, int *prot,
B
bellard 已提交
39
                        target_ulong address, int rw, int access_type)
40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66
{
    *physical = address;
    *prot = PAGE_READ | PAGE_WRITE;
    return TLBRET_MATCH;
}

/* fixed mapping MMU emulation */
int fixed_mmu_map_address (CPUState *env, target_ulong *physical, int *prot,
                           target_ulong address, int rw, int access_type)
{
    if (address <= (int32_t)0x7FFFFFFFUL) {
        if (!(env->CP0_Status & (1 << CP0St_ERL)))
            *physical = address + 0x40000000UL;
        else
            *physical = address;
    } else if (address <= (int32_t)0xBFFFFFFFUL)
        *physical = address & 0x1FFFFFFF;
    else
        *physical = address;

    *prot = PAGE_READ | PAGE_WRITE;
    return TLBRET_MATCH;
}

/* MIPS32/MIPS64 R4000-style MMU emulation */
int r4k_map_address (CPUState *env, target_ulong *physical, int *prot,
                     target_ulong address, int rw, int access_type)
B
bellard 已提交
67
{
68
    uint8_t ASID = env->CP0_EntryHi & 0xFF;
T
ths 已提交
69
    int i;
B
bellard 已提交
70

71
    for (i = 0; i < env->tlb->tlb_in_use; i++) {
A
Anthony Liguori 已提交
72
        r4k_tlb_t *tlb = &env->tlb->mmu.r4k.tlb[i];
T
ths 已提交
73
        /* 1k pages are not supported. */
T
ths 已提交
74
        target_ulong mask = tlb->PageMask | ~(TARGET_PAGE_MASK << 1);
T
ths 已提交
75
        target_ulong tag = address & ~mask;
T
ths 已提交
76
        target_ulong VPN = tlb->VPN & ~mask;
77
#if defined(TARGET_MIPS64)
T
ths 已提交
78
        tag &= env->SEGMask;
79
#endif
T
ths 已提交
80

B
bellard 已提交
81
        /* Check ASID, virtual page number & size */
T
ths 已提交
82
        if ((tlb->G == 1 || tlb->ASID == ASID) && VPN == tag) {
B
bellard 已提交
83
            /* TLB match */
T
ths 已提交
84
            int n = !!(address & mask & ~(mask >> 1));
B
bellard 已提交
85
            /* Check access rights */
T
ths 已提交
86
            if (!(n ? tlb->V1 : tlb->V0))
87
                return TLBRET_INVALID;
T
ths 已提交
88
            if (rw == 0 || (n ? tlb->D1 : tlb->D0)) {
T
ths 已提交
89
                *physical = tlb->PFN[n] | (address & (mask >> 1));
B
bellard 已提交
90
                *prot = PAGE_READ;
91
                if (n ? tlb->D1 : tlb->D0)
B
bellard 已提交
92
                    *prot |= PAGE_WRITE;
93
                return TLBRET_MATCH;
B
bellard 已提交
94
            }
95
            return TLBRET_DIRTY;
B
bellard 已提交
96 97
        }
    }
98
    return TLBRET_NOMATCH;
B
bellard 已提交
99 100
}

101
#if !defined(CONFIG_USER_ONLY)
102 103 104
static int get_physical_address (CPUState *env, target_ulong *physical,
                                int *prot, target_ulong address,
                                int rw, int access_type)
B
bellard 已提交
105
{
106
    /* User mode can only access useg/xuseg */
107
    int user_mode = (env->hflags & MIPS_HFLAG_MODE) == MIPS_HFLAG_UM;
108 109
    int supervisor_mode = (env->hflags & MIPS_HFLAG_MODE) == MIPS_HFLAG_SM;
    int kernel_mode = !user_mode && !supervisor_mode;
110
#if defined(TARGET_MIPS64)
111 112 113 114
    int UX = (env->CP0_Status & (1 << CP0St_UX)) != 0;
    int SX = (env->CP0_Status & (1 << CP0St_SX)) != 0;
    int KX = (env->CP0_Status & (1 << CP0St_KX)) != 0;
#endif
115 116
    int ret = TLBRET_MATCH;

B
bellard 已提交
117
#if 0
118
    qemu_log("user mode %d h %08x\n", user_mode, env->hflags);
B
bellard 已提交
119
#endif
120 121 122

    if (address <= (int32_t)0x7FFFFFFFUL) {
        /* useg */
123
        if (env->CP0_Status & (1 << CP0St_ERL)) {
124
            *physical = address & 0xFFFFFFFF;
B
bellard 已提交
125
            *prot = PAGE_READ | PAGE_WRITE;
126
        } else {
127
            ret = env->tlb->map_address(env, physical, prot, address, rw, access_type);
B
bellard 已提交
128
        }
129
#if defined(TARGET_MIPS64)
T
ths 已提交
130
    } else if (address < 0x4000000000000000ULL) {
131
        /* xuseg */
A
aurel32 已提交
132
        if (UX && address <= (0x3FFFFFFFFFFFFFFFULL & env->SEGMask)) {
133
            ret = env->tlb->map_address(env, physical, prot, address, rw, access_type);
A
aurel32 已提交
134 135
        } else {
            ret = TLBRET_BADADDR;
136
        }
T
ths 已提交
137
    } else if (address < 0x8000000000000000ULL) {
138
        /* xsseg */
A
aurel32 已提交
139 140
        if ((supervisor_mode || kernel_mode) &&
            SX && address <= (0x7FFFFFFFFFFFFFFFULL & env->SEGMask)) {
141
            ret = env->tlb->map_address(env, physical, prot, address, rw, access_type);
A
aurel32 已提交
142 143
        } else {
            ret = TLBRET_BADADDR;
144
        }
T
ths 已提交
145
    } else if (address < 0xC000000000000000ULL) {
146
        /* xkphys */
147
        if (kernel_mode && KX &&
148 149
            (address & 0x07FFFFFFFFFFFFFFULL) <= env->PAMask) {
            *physical = address & env->PAMask;
150
            *prot = PAGE_READ | PAGE_WRITE;
A
aurel32 已提交
151 152 153
        } else {
            ret = TLBRET_BADADDR;
        }
T
ths 已提交
154
    } else if (address < 0xFFFFFFFF80000000ULL) {
155
        /* xkseg */
A
aurel32 已提交
156 157
        if (kernel_mode && KX &&
            address <= (0xFFFFFFFF7FFFFFFFULL & env->SEGMask)) {
158
            ret = env->tlb->map_address(env, physical, prot, address, rw, access_type);
A
aurel32 已提交
159 160 161
        } else {
            ret = TLBRET_BADADDR;
        }
162
#endif
T
ths 已提交
163
    } else if (address < (int32_t)0xA0000000UL) {
B
bellard 已提交
164
        /* kseg0 */
165 166 167 168 169 170
        if (kernel_mode) {
            *physical = address - (int32_t)0x80000000UL;
            *prot = PAGE_READ | PAGE_WRITE;
        } else {
            ret = TLBRET_BADADDR;
        }
T
ths 已提交
171
    } else if (address < (int32_t)0xC0000000UL) {
B
bellard 已提交
172
        /* kseg1 */
173 174 175 176 177 178
        if (kernel_mode) {
            *physical = address - (int32_t)0xA0000000UL;
            *prot = PAGE_READ | PAGE_WRITE;
        } else {
            ret = TLBRET_BADADDR;
        }
T
ths 已提交
179
    } else if (address < (int32_t)0xE0000000UL) {
T
ths 已提交
180
        /* sseg (kseg2) */
181 182 183 184 185
        if (supervisor_mode || kernel_mode) {
            ret = env->tlb->map_address(env, physical, prot, address, rw, access_type);
        } else {
            ret = TLBRET_BADADDR;
        }
B
bellard 已提交
186 187 188
    } else {
        /* kseg3 */
        /* XXX: debug segment is not emulated */
189 190 191 192 193
        if (kernel_mode) {
            ret = env->tlb->map_address(env, physical, prot, address, rw, access_type);
        } else {
            ret = TLBRET_BADADDR;
        }
B
bellard 已提交
194 195
    }
#if 0
196 197
    qemu_log(TARGET_FMT_lx " %d %d => " TARGET_FMT_lx " %d (%d)\n",
            address, rw, access_type, *physical, *prot, ret);
B
bellard 已提交
198 199 200 201
#endif

    return ret;
}
202
#endif
B
bellard 已提交
203

A
Anthony Liguori 已提交
204
target_phys_addr_t cpu_get_phys_page_debug(CPUState *env, target_ulong addr)
B
bellard 已提交
205
{
206 207 208 209 210
#if defined(CONFIG_USER_ONLY)
    return addr;
#else
    target_ulong phys_addr;
    int prot;
B
bellard 已提交
211

212 213 214 215
    if (get_physical_address(env, &phys_addr, &prot, addr, 0, ACCESS_INT) != 0)
        return -1;
    return phys_addr;
#endif
B
bellard 已提交
216 217 218
}

int cpu_mips_handle_mmu_fault (CPUState *env, target_ulong address, int rw,
219
                               int mmu_idx, int is_softmmu)
B
bellard 已提交
220
{
221
#if !defined(CONFIG_USER_ONLY)
B
bellard 已提交
222 223
    target_ulong physical;
    int prot;
224
#endif
B
bellard 已提交
225 226 227 228
    int exception = 0, error_code = 0;
    int access_type;
    int ret = 0;

B
bellard 已提交
229
#if 0
230
    log_cpu_state(env, 0);
B
bellard 已提交
231
#endif
232 233
    qemu_log("%s pc " TARGET_FMT_lx " ad " TARGET_FMT_lx " rw %d mmu_idx %d smmu %d\n",
              __func__, env->active_tc.PC, address, rw, mmu_idx, is_softmmu);
B
bellard 已提交
234 235 236

    rw &= 1;

B
bellard 已提交
237 238 239 240
    /* data access */
    /* XXX: put correct access by using cpu_restore_state()
       correctly */
    access_type = ACCESS_INT;
241 242 243
#if defined(CONFIG_USER_ONLY)
    ret = TLBRET_NOMATCH;
#else
B
bellard 已提交
244 245
    ret = get_physical_address(env, &physical, &prot,
                               address, rw, access_type);
246 247
    qemu_log("%s address=" TARGET_FMT_lx " ret %d physical " TARGET_FMT_lx " prot %d\n",
              __func__, address, ret, physical, prot);
248 249 250
    if (ret == TLBRET_MATCH) {
       ret = tlb_set_page(env, address & TARGET_PAGE_MASK,
                          physical & TARGET_PAGE_MASK, prot,
251
                          mmu_idx, is_softmmu);
252 253 254
    } else if (ret < 0)
#endif
    {
B
bellard 已提交
255 256
        switch (ret) {
        default:
257
        case TLBRET_BADADDR:
B
bellard 已提交
258 259 260 261 262 263 264
            /* Reference to kernel address from user mode or supervisor mode */
            /* Reference to supervisor address from user mode */
            if (rw)
                exception = EXCP_AdES;
            else
                exception = EXCP_AdEL;
            break;
265
        case TLBRET_NOMATCH:
B
bellard 已提交
266 267 268 269 270 271 272
            /* No TLB match for a mapped address */
            if (rw)
                exception = EXCP_TLBS;
            else
                exception = EXCP_TLBL;
            error_code = 1;
            break;
273
        case TLBRET_INVALID:
B
bellard 已提交
274 275 276 277 278 279
            /* TLB match with no valid bit */
            if (rw)
                exception = EXCP_TLBS;
            else
                exception = EXCP_TLBL;
            break;
280
        case TLBRET_DIRTY:
B
bellard 已提交
281 282 283
            /* TLB match but 'D' bit is cleared */
            exception = EXCP_LTLBL;
            break;
284

B
bellard 已提交
285 286 287
        }
        /* Raise exception */
        env->CP0_BadVAddr = address;
288
        env->CP0_Context = (env->CP0_Context & ~0x007fffff) |
A
aurel32 已提交
289
                           ((address >> 9) &   0x007ffff0);
B
bellard 已提交
290
        env->CP0_EntryHi =
291
            (env->CP0_EntryHi & 0xFF) | (address & (TARGET_PAGE_MASK << 1));
292
#if defined(TARGET_MIPS64)
T
ths 已提交
293 294
        env->CP0_EntryHi &= env->SEGMask;
        env->CP0_XContext = (env->CP0_XContext & ((~0ULL) << (env->SEGBITS - 7))) |
T
ths 已提交
295
                            ((address & 0xC00000000000ULL) >> (55 - env->SEGBITS)) |
T
ths 已提交
296
                            ((address & ((1ULL << env->SEGBITS) - 1) & 0xFFFFFFFFFFFFE000ULL) >> 9);
297
#endif
B
bellard 已提交
298 299 300 301 302 303 304 305
        env->exception_index = exception;
        env->error_code = error_code;
        ret = 1;
    }

    return ret;
}

T
ths 已提交
306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339
static const char * const excp_names[EXCP_LAST + 1] = {
    [EXCP_RESET] = "reset",
    [EXCP_SRESET] = "soft reset",
    [EXCP_DSS] = "debug single step",
    [EXCP_DINT] = "debug interrupt",
    [EXCP_NMI] = "non-maskable interrupt",
    [EXCP_MCHECK] = "machine check",
    [EXCP_EXT_INTERRUPT] = "interrupt",
    [EXCP_DFWATCH] = "deferred watchpoint",
    [EXCP_DIB] = "debug instruction breakpoint",
    [EXCP_IWATCH] = "instruction fetch watchpoint",
    [EXCP_AdEL] = "address error load",
    [EXCP_AdES] = "address error store",
    [EXCP_TLBF] = "TLB refill",
    [EXCP_IBE] = "instruction bus error",
    [EXCP_DBp] = "debug breakpoint",
    [EXCP_SYSCALL] = "syscall",
    [EXCP_BREAK] = "break",
    [EXCP_CpU] = "coprocessor unusable",
    [EXCP_RI] = "reserved instruction",
    [EXCP_OVERFLOW] = "arithmetic overflow",
    [EXCP_TRAP] = "trap",
    [EXCP_FPE] = "floating point",
    [EXCP_DDBS] = "debug data break store",
    [EXCP_DWATCH] = "data watchpoint",
    [EXCP_LTLBL] = "TLB modify",
    [EXCP_TLBL] = "TLB load",
    [EXCP_TLBS] = "TLB store",
    [EXCP_DBE] = "data bus error",
    [EXCP_DDBL] = "debug data break load",
    [EXCP_THREAD] = "thread",
    [EXCP_MDMX] = "MDMX",
    [EXCP_C2E] = "precise coprocessor 2",
    [EXCP_CACHE] = "cache error",
340 341
};

B
bellard 已提交
342 343
void do_interrupt (CPUState *env)
{
344 345 346 347
#if !defined(CONFIG_USER_ONLY)
    target_ulong offset;
    int cause = -1;
    const char *name;
348

349
    if (qemu_log_enabled() && env->exception_index != EXCP_EXT_INTERRUPT) {
350 351 352 353
        if (env->exception_index < 0 || env->exception_index > EXCP_LAST)
            name = "unknown";
        else
            name = excp_names[env->exception_index];
T
ths 已提交
354

355 356
        qemu_log("%s enter: PC " TARGET_FMT_lx " EPC " TARGET_FMT_lx " %s exception\n",
                 __func__, env->active_tc.PC, env->CP0_EPC, name);
357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391
    }
    if (env->exception_index == EXCP_EXT_INTERRUPT &&
        (env->hflags & MIPS_HFLAG_DM))
        env->exception_index = EXCP_DINT;
    offset = 0x180;
    switch (env->exception_index) {
    case EXCP_DSS:
        env->CP0_Debug |= 1 << CP0DB_DSS;
        /* Debug single step cannot be raised inside a delay slot and
           resume will always occur on the next instruction
           (but we assume the pc has always been updated during
           code translation). */
        env->CP0_DEPC = env->active_tc.PC;
        goto enter_debug_mode;
    case EXCP_DINT:
        env->CP0_Debug |= 1 << CP0DB_DINT;
        goto set_DEPC;
    case EXCP_DIB:
        env->CP0_Debug |= 1 << CP0DB_DIB;
        goto set_DEPC;
    case EXCP_DBp:
        env->CP0_Debug |= 1 << CP0DB_DBp;
        goto set_DEPC;
    case EXCP_DDBS:
        env->CP0_Debug |= 1 << CP0DB_DDBS;
        goto set_DEPC;
    case EXCP_DDBL:
        env->CP0_Debug |= 1 << CP0DB_DDBL;
    set_DEPC:
        if (env->hflags & MIPS_HFLAG_BMASK) {
            /* If the exception was raised from a delay slot,
               come back to the jump.  */
            env->CP0_DEPC = env->active_tc.PC - 4;
            env->hflags &= ~MIPS_HFLAG_BMASK;
        } else {
T
ths 已提交
392
            env->CP0_DEPC = env->active_tc.PC;
393
        }
T
ths 已提交
394
 enter_debug_mode:
395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410
        env->hflags |= MIPS_HFLAG_DM | MIPS_HFLAG_64 | MIPS_HFLAG_CP0;
        env->hflags &= ~(MIPS_HFLAG_KSU);
        /* EJTAG probe trap enable is not implemented... */
        if (!(env->CP0_Status & (1 << CP0St_EXL)))
            env->CP0_Cause &= ~(1 << CP0Ca_BD);
        env->active_tc.PC = (int32_t)0xBFC00480;
        break;
    case EXCP_RESET:
        cpu_reset(env);
        break;
    case EXCP_SRESET:
        env->CP0_Status |= (1 << CP0St_SR);
        memset(env->CP0_WatchLo, 0, sizeof(*env->CP0_WatchLo));
        goto set_error_EPC;
    case EXCP_NMI:
        env->CP0_Status |= (1 << CP0St_NMI);
T
ths 已提交
411
 set_error_EPC:
412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437
        if (env->hflags & MIPS_HFLAG_BMASK) {
            /* If the exception was raised from a delay slot,
               come back to the jump.  */
            env->CP0_ErrorEPC = env->active_tc.PC - 4;
            env->hflags &= ~MIPS_HFLAG_BMASK;
        } else {
            env->CP0_ErrorEPC = env->active_tc.PC;
        }
        env->CP0_Status |= (1 << CP0St_ERL) | (1 << CP0St_BEV);
        env->hflags |= MIPS_HFLAG_64 | MIPS_HFLAG_CP0;
        env->hflags &= ~(MIPS_HFLAG_KSU);
        if (!(env->CP0_Status & (1 << CP0St_EXL)))
            env->CP0_Cause &= ~(1 << CP0Ca_BD);
        env->active_tc.PC = (int32_t)0xBFC00000;
        break;
    case EXCP_EXT_INTERRUPT:
        cause = 0;
        if (env->CP0_Cause & (1 << CP0Ca_IV))
            offset = 0x200;
        goto set_EPC;
    case EXCP_LTLBL:
        cause = 1;
        goto set_EPC;
    case EXCP_TLBL:
        cause = 2;
        if (env->error_code == 1 && !(env->CP0_Status & (1 << CP0St_EXL))) {
T
ths 已提交
438
#if defined(TARGET_MIPS64)
439 440 441 442
            int R = env->CP0_BadVAddr >> 62;
            int UX = (env->CP0_Status & (1 << CP0St_UX)) != 0;
            int SX = (env->CP0_Status & (1 << CP0St_SX)) != 0;
            int KX = (env->CP0_Status & (1 << CP0St_KX)) != 0;
T
ths 已提交
443

444 445 446
            if ((R == 0 && UX) || (R == 1 && SX) || (R == 3 && KX))
                offset = 0x080;
            else
T
ths 已提交
447
#endif
448 449 450 451 452 453
                offset = 0x000;
        }
        goto set_EPC;
    case EXCP_TLBS:
        cause = 3;
        if (env->error_code == 1 && !(env->CP0_Status & (1 << CP0St_EXL))) {
T
ths 已提交
454
#if defined(TARGET_MIPS64)
455 456 457 458
            int R = env->CP0_BadVAddr >> 62;
            int UX = (env->CP0_Status & (1 << CP0St_UX)) != 0;
            int SX = (env->CP0_Status & (1 << CP0St_SX)) != 0;
            int KX = (env->CP0_Status & (1 << CP0St_KX)) != 0;
T
ths 已提交
459

460 461 462
            if ((R == 0 && UX) || (R == 1 && SX) || (R == 3 && KX))
                offset = 0x080;
            else
T
ths 已提交
463
#endif
464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524
                offset = 0x000;
        }
        goto set_EPC;
    case EXCP_AdEL:
        cause = 4;
        goto set_EPC;
    case EXCP_AdES:
        cause = 5;
        goto set_EPC;
    case EXCP_IBE:
        cause = 6;
        goto set_EPC;
    case EXCP_DBE:
        cause = 7;
        goto set_EPC;
    case EXCP_SYSCALL:
        cause = 8;
        goto set_EPC;
    case EXCP_BREAK:
        cause = 9;
        goto set_EPC;
    case EXCP_RI:
        cause = 10;
        goto set_EPC;
    case EXCP_CpU:
        cause = 11;
        env->CP0_Cause = (env->CP0_Cause & ~(0x3 << CP0Ca_CE)) |
                         (env->error_code << CP0Ca_CE);
        goto set_EPC;
    case EXCP_OVERFLOW:
        cause = 12;
        goto set_EPC;
    case EXCP_TRAP:
        cause = 13;
        goto set_EPC;
    case EXCP_FPE:
        cause = 15;
        goto set_EPC;
    case EXCP_C2E:
        cause = 18;
        goto set_EPC;
    case EXCP_MDMX:
        cause = 22;
        goto set_EPC;
    case EXCP_DWATCH:
        cause = 23;
        /* XXX: TODO: manage defered watch exceptions */
        goto set_EPC;
    case EXCP_MCHECK:
        cause = 24;
        goto set_EPC;
    case EXCP_THREAD:
        cause = 25;
        goto set_EPC;
    case EXCP_CACHE:
        cause = 30;
        if (env->CP0_Status & (1 << CP0St_BEV)) {
            offset = 0x100;
        } else {
            offset = 0x20000100;
        }
T
ths 已提交
525
 set_EPC:
526 527 528 529 530 531
        if (!(env->CP0_Status & (1 << CP0St_EXL))) {
            if (env->hflags & MIPS_HFLAG_BMASK) {
                /* If the exception was raised from a delay slot,
                   come back to the jump.  */
                env->CP0_EPC = env->active_tc.PC - 4;
                env->CP0_Cause |= (1 << CP0Ca_BD);
T
ths 已提交
532
            } else {
533 534
                env->CP0_EPC = env->active_tc.PC;
                env->CP0_Cause &= ~(1 << CP0Ca_BD);
T
ths 已提交
535
            }
536 537 538
            env->CP0_Status |= (1 << CP0St_EXL);
            env->hflags |= MIPS_HFLAG_64 | MIPS_HFLAG_CP0;
            env->hflags &= ~(MIPS_HFLAG_KSU);
B
bellard 已提交
539
        }
540 541 542 543 544
        env->hflags &= ~MIPS_HFLAG_BMASK;
        if (env->CP0_Status & (1 << CP0St_BEV)) {
            env->active_tc.PC = (int32_t)0xBFC00200;
        } else {
            env->active_tc.PC = (int32_t)(env->CP0_EBase & ~0x3ff);
B
bellard 已提交
545
        }
546 547 548 549
        env->active_tc.PC += offset;
        env->CP0_Cause = (env->CP0_Cause & ~(0x1f << CP0Ca_EC)) | (cause << CP0Ca_EC);
        break;
    default:
550
        qemu_log("Invalid MIPS exception %d. Exiting\n", env->exception_index);
551 552 553
        printf("Invalid MIPS exception %d. Exiting\n", env->exception_index);
        exit(1);
    }
554 555
    if (qemu_log_enabled() && env->exception_index != EXCP_EXT_INTERRUPT) {
        qemu_log("%s: PC " TARGET_FMT_lx " EPC " TARGET_FMT_lx " cause %d\n"
556 557 558 559
                "    S %08x C %08x A " TARGET_FMT_lx " D " TARGET_FMT_lx "\n",
                __func__, env->active_tc.PC, env->CP0_EPC, cause,
                env->CP0_Status, env->CP0_Cause, env->CP0_BadVAddr,
                env->CP0_DEPC);
B
bellard 已提交
560
    }
561
#endif
B
bellard 已提交
562 563
    env->exception_index = EXCP_NONE;
}
564

565
void r4k_invalidate_tlb (CPUState *env, int idx, int use_extra)
566
{
A
Anthony Liguori 已提交
567
    r4k_tlb_t *tlb;
T
ths 已提交
568 569 570 571
    target_ulong addr;
    target_ulong end;
    uint8_t ASID = env->CP0_EntryHi & 0xFF;
    target_ulong mask;
572

573
    tlb = &env->tlb->mmu.r4k.tlb[idx];
T
ths 已提交
574
    /* The qemu TLB is flushed when the ASID changes, so no need to
575 576 577 578 579
       flush these entries again.  */
    if (tlb->G == 0 && tlb->ASID != ASID) {
        return;
    }

580
    if (use_extra && env->tlb->tlb_in_use < MIPS_TLB_MAX) {
581
        /* For tlbwr, we can shadow the discarded entry into
A
aurel32 已提交
582 583
           a new (fake) TLB entry, as long as the guest can not
           tell that it's there.  */
584 585
        env->tlb->mmu.r4k.tlb[env->tlb->tlb_in_use] = *tlb;
        env->tlb->tlb_in_use++;
586 587 588
        return;
    }

T
ths 已提交
589
    /* 1k pages are not supported. */
T
ths 已提交
590
    mask = tlb->PageMask | ~(TARGET_PAGE_MASK << 1);
T
ths 已提交
591
    if (tlb->V0) {
T
ths 已提交
592
        addr = tlb->VPN & ~mask;
593
#if defined(TARGET_MIPS64)
T
ths 已提交
594
        if (addr >= (0xFFFFFFFF80000000ULL & env->SEGMask)) {
595 596 597
            addr |= 0x3FFFFF0000000000ULL;
        }
#endif
T
ths 已提交
598 599 600 601 602 603 604
        end = addr | (mask >> 1);
        while (addr < end) {
            tlb_flush_page (env, addr);
            addr += TARGET_PAGE_SIZE;
        }
    }
    if (tlb->V1) {
T
ths 已提交
605
        addr = (tlb->VPN & ~mask) | ((mask >> 1) + 1);
606
#if defined(TARGET_MIPS64)
T
ths 已提交
607
        if (addr >= (0xFFFFFFFF80000000ULL & env->SEGMask)) {
608 609 610
            addr |= 0x3FFFFF0000000000ULL;
        }
#endif
T
ths 已提交
611
        end = addr | mask;
612
        while (addr - 1 < end) {
T
ths 已提交
613 614 615 616
            tlb_flush_page (env, addr);
            addr += TARGET_PAGE_SIZE;
        }
    }
617
}