op_helper.c 13.8 KB
Newer Older
B
bellard 已提交
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
/*
 *  MIPS emulation helpers for qemu.
 * 
 *  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
 * 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
#include <stdlib.h>
B
bellard 已提交
21 22
#include "exec.h"

B
bellard 已提交
23 24
#define GETPC() (__builtin_return_address(0))

B
bellard 已提交
25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48
/*****************************************************************************/
/* Exceptions processing helpers */
void cpu_loop_exit(void)
{
    longjmp(env->jmp_env, 1);
}

void do_raise_exception_err (uint32_t exception, int error_code)
{
#if 1
    if (logfile && exception < 0x100)
        fprintf(logfile, "%s: %d %d\n", __func__, exception, error_code);
#endif
    env->exception_index = exception;
    env->error_code = error_code;
    T0 = 0;
    cpu_loop_exit();
}

void do_raise_exception (uint32_t exception)
{
    do_raise_exception_err(exception, 0);
}

B
bellard 已提交
49 50 51 52 53 54 55 56 57
void do_restore_state (void *pc_ptr)
{
  TranslationBlock *tb;
  unsigned long pc = (unsigned long) pc_ptr;

  tb = tb_find_pc (pc);
  cpu_restore_state (tb, env, pc, NULL);
}

58
void do_raise_exception_direct_err (uint32_t exception, int error_code)
B
bellard 已提交
59 60
{
    do_restore_state (GETPC ());
61 62 63 64 65 66
    do_raise_exception_err (exception, error_code);
}

void do_raise_exception_direct (uint32_t exception)
{
    do_raise_exception_direct_err (exception, 0);
B
bellard 已提交
67 68
}

B
bellard 已提交
69 70 71 72 73 74 75 76 77 78 79 80
#define MEMSUFFIX _raw
#include "op_helper_mem.c"
#undef MEMSUFFIX
#if !defined(CONFIG_USER_ONLY)
#define MEMSUFFIX _user
#include "op_helper_mem.c"
#undef MEMSUFFIX
#define MEMSUFFIX _kernel
#include "op_helper_mem.c"
#undef MEMSUFFIX
#endif

T
ths 已提交
81
#ifdef TARGET_MIPS64
82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120
#if TARGET_LONG_BITS > HOST_LONG_BITS
/* Those might call libgcc functions.  */
void do_dsll (void)
{
    T0 = T0 << T1;
}

void do_dsll32 (void)
{
    T0 = T0 << (T1 + 32);
}

void do_dsra (void)
{
    T0 = (int64_t)T0 >> T1;
}

void do_dsra32 (void)
{
    T0 = (int64_t)T0 >> (T1 + 32);
}

void do_dsrl (void)
{
    T0 = T0 >> T1;
}

void do_dsrl32 (void)
{
    T0 = T0 >> (T1 + 32);
}

void do_drotr (void)
{
    target_ulong tmp;

    if (T1) {
       tmp = T0 << (0x40 - T1);
       T0 = (T0 >> T1) | tmp;
121
    }
122 123 124 125 126 127 128 129 130
}

void do_drotr32 (void)
{
    target_ulong tmp;

    if (T1) {
       tmp = T0 << (0x40 - (32 + T1));
       T0 = (T0 >> (32 + T1)) | tmp;
131
    }
132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160
}

void do_dsllv (void)
{
    T0 = T1 << (T0 & 0x3F);
}

void do_dsrav (void)
{
    T0 = (int64_t)T1 >> (T0 & 0x3F);
}

void do_dsrlv (void)
{
    T0 = T1 >> (T0 & 0x3F);
}

void do_drotrv (void)
{
    target_ulong tmp;

    T0 &= 0x3F;
    if (T0) {
       tmp = T1 << (0x40 - T0);
       T0 = (T1 >> T0) | tmp;
    } else
       T0 = T1;
}
#endif /* TARGET_LONG_BITS > HOST_LONG_BITS */
T
ths 已提交
161
#endif /* TARGET_MIPS64 */
162

B
bellard 已提交
163
/* 64 bits arithmetic for 32 bits hosts */
164
#if TARGET_LONG_BITS > HOST_LONG_BITS
B
bellard 已提交
165 166
static inline uint64_t get_HILO (void)
{
T
ths 已提交
167
    return (env->HI << 32) | (uint32_t)env->LO;
B
bellard 已提交
168 169 170 171
}

static inline void set_HILO (uint64_t HILO)
{
T
ths 已提交
172
    env->LO = (int32_t)HILO;
T
ths 已提交
173
    env->HI = (int32_t)(HILO >> 32);
B
bellard 已提交
174 175 176 177
}

void do_mult (void)
{
B
bellard 已提交
178
    set_HILO((int64_t)(int32_t)T0 * (int64_t)(int32_t)T1);
B
bellard 已提交
179 180 181 182
}

void do_multu (void)
{
183
    set_HILO((uint64_t)(uint32_t)T0 * (uint64_t)(uint32_t)T1);
B
bellard 已提交
184 185 186 187 188 189
}

void do_madd (void)
{
    int64_t tmp;

B
bellard 已提交
190
    tmp = ((int64_t)(int32_t)T0 * (int64_t)(int32_t)T1);
B
bellard 已提交
191 192 193 194 195 196 197
    set_HILO((int64_t)get_HILO() + tmp);
}

void do_maddu (void)
{
    uint64_t tmp;

198
    tmp = ((uint64_t)(uint32_t)T0 * (uint64_t)(uint32_t)T1);
B
bellard 已提交
199 200 201 202 203 204 205
    set_HILO(get_HILO() + tmp);
}

void do_msub (void)
{
    int64_t tmp;

B
bellard 已提交
206
    tmp = ((int64_t)(int32_t)T0 * (int64_t)(int32_t)T1);
B
bellard 已提交
207 208 209 210 211 212 213
    set_HILO((int64_t)get_HILO() - tmp);
}

void do_msubu (void)
{
    uint64_t tmp;

214
    tmp = ((uint64_t)(uint32_t)T0 * (uint64_t)(uint32_t)T1);
B
bellard 已提交
215 216 217 218
    set_HILO(get_HILO() - tmp);
}
#endif

T
ths 已提交
219
#ifdef TARGET_MIPS64
220 221
void do_dmult (void)
{
222
    env->LO = (int64_t)T0 * (int64_t)T1;
223
    /* XXX */
224
    env->HI = (env->LO | (1ULL << 63)) ? ~0ULL : 0ULL;
225 226 227 228
}

void do_dmultu (void)
{
229
    env->LO = T0 * T1;
230
    /* XXX */
231
    env->HI = 0;
232 233 234 235 236
}

void do_ddiv (void)
{
    if (T1 != 0) {
237 238 239
        lldiv_t res = lldiv((int64_t)T0, (int64_t)T1);
        env->LO = res.quot;
        env->HI = res.rem;
240 241 242 243 244 245
    }
}

void do_ddivu (void)
{
    if (T1 != 0) {
246 247 248 249
        /* XXX: lldivu? */
        lldiv_t res = lldiv(T0, T1);
        env->LO = (uint64_t)res.quot;
        env->HI = (uint64_t)res.rem;
250 251 252 253
    }
}
#endif

B
bellard 已提交
254
#if defined(CONFIG_USER_ONLY) 
255
void do_mfc0_random (void)
B
bellard 已提交
256
{
257
    cpu_abort(env, "mfc0 random\n");
B
bellard 已提交
258
}
259 260 261 262 263 264

void do_mfc0_count (void)
{
    cpu_abort(env, "mfc0 count\n");
}

265
void cpu_mips_store_count(CPUState *env, uint32_t value)
B
bellard 已提交
266
{
267 268 269 270 271 272 273 274
    cpu_abort(env, "mtc0 count\n");
}

void cpu_mips_store_compare(CPUState *env, uint32_t value)
{
    cpu_abort(env, "mtc0 compare\n");
}

275 276 277 278 279
void cpu_mips_update_irq(CPUState *env)
{
    cpu_abort(env, "mtc0 status / mtc0 cause\n");
}

280 281
void do_mtc0_status_debug(uint32_t old, uint32_t val)
{
282
    cpu_abort(env, "mtc0 status debug\n");
283 284
}

285
void do_mtc0_status_irqraise_debug (void)
286
{
287
    cpu_abort(env, "mtc0 status irqraise debug\n");
B
bellard 已提交
288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308
}

void do_tlbwi (void)
{
    cpu_abort(env, "tlbwi\n");
}

void do_tlbwr (void)
{
    cpu_abort(env, "tlbwr\n");
}

void do_tlbp (void)
{
    cpu_abort(env, "tlbp\n");
}

void do_tlbr (void)
{
    cpu_abort(env, "tlbr\n");
}
309

310 311 312 313 314
void cpu_mips_tlb_flush (CPUState *env, int flush_global)
{
    cpu_abort(env, "mips_tlb_flush\n");
}

B
bellard 已提交
315 316
#else

B
bellard 已提交
317
/* CP0 helpers */
318
void do_mfc0_random (void)
B
bellard 已提交
319
{
T
ths 已提交
320
    T0 = (int32_t)cpu_mips_get_random(env);
321
}
B
bellard 已提交
322

323 324
void do_mfc0_count (void)
{
T
ths 已提交
325
    T0 = (int32_t)cpu_mips_get_count(env);
B
bellard 已提交
326 327
}

328
void do_mtc0_status_debug(uint32_t old, uint32_t val)
B
bellard 已提交
329
{
330 331 332 333 334 335
    fprintf(logfile, "Status %08x (%08x) => %08x (%08x) Cause %08x",
            old, old & env->CP0_Cause & CP0Ca_IP_mask,
            val, val & env->CP0_Cause & CP0Ca_IP_mask,
            env->CP0_Cause);
    (env->hflags & MIPS_HFLAG_UM) ? fputs(", UM\n", logfile)
                                  : fputs("\n", logfile);
336 337 338 339 340
}

void do_mtc0_status_irqraise_debug(void)
{
    fprintf(logfile, "Raise pending IRQs\n");
B
bellard 已提交
341 342
}

B
bellard 已提交
343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379
void fpu_handle_exception(void)
{
#ifdef CONFIG_SOFTFLOAT
    int flags = get_float_exception_flags(&env->fp_status);
    unsigned int cpuflags = 0, enable, cause = 0;

    enable = GET_FP_ENABLE(env->fcr31);

    /* determine current flags */   
    if (flags & float_flag_invalid) {
        cpuflags |= FP_INVALID;
        cause |= FP_INVALID & enable;
    }
    if (flags & float_flag_divbyzero) {
        cpuflags |= FP_DIV0;    
        cause |= FP_DIV0 & enable;
    }
    if (flags & float_flag_overflow) {
        cpuflags |= FP_OVERFLOW;    
        cause |= FP_OVERFLOW & enable;
    }
    if (flags & float_flag_underflow) {
        cpuflags |= FP_UNDERFLOW;   
        cause |= FP_UNDERFLOW & enable;
    }
    if (flags & float_flag_inexact) {
        cpuflags |= FP_INEXACT; 
        cause |= FP_INEXACT & enable;
    }
    SET_FP_FLAGS(env->fcr31, cpuflags);
    SET_FP_CAUSE(env->fcr31, cause);
#else
    SET_FP_FLAGS(env->fcr31, 0);
    SET_FP_CAUSE(env->fcr31, 0);
#endif
}

B
bellard 已提交
380 381
/* TLB management */
#if defined(MIPS_USES_R4K_TLB)
382 383 384 385 386 387 388 389 390 391 392
void cpu_mips_tlb_flush (CPUState *env, int flush_global)
{
    /* Flush qemu's TLB and discard all shadowed entries.  */
    tlb_flush (env, flush_global);
    env->tlb_in_use = MIPS_TLB_NB;
}

static void mips_tlb_flush_extra (CPUState *env, int first)
{
    /* Discard entries from env->tlb[first] onwards.  */
    while (env->tlb_in_use > first) {
393
        invalidate_tlb(env, --env->tlb_in_use, 0);
394 395 396
    }
}

397
static void fill_tlb (int idx)
B
bellard 已提交
398 399 400 401 402
{
    tlb_t *tlb;

    /* XXX: detect conflicting TLBs and raise a MCHECK exception when needed */
    tlb = &env->tlb[idx];
403
    tlb->VPN = env->CP0_EntryHi & ~(target_ulong)0x1FFF;
404
    tlb->ASID = env->CP0_EntryHi & 0xFF;
T
ths 已提交
405
    tlb->PageMask = env->CP0_PageMask;
B
bellard 已提交
406
    tlb->G = env->CP0_EntryLo0 & env->CP0_EntryLo1 & 1;
407 408 409
    tlb->V0 = (env->CP0_EntryLo0 & 2) != 0;
    tlb->D0 = (env->CP0_EntryLo0 & 4) != 0;
    tlb->C0 = (env->CP0_EntryLo0 >> 3) & 0x7;
B
bellard 已提交
410
    tlb->PFN[0] = (env->CP0_EntryLo0 >> 6) << 12;
411 412 413
    tlb->V1 = (env->CP0_EntryLo1 & 2) != 0;
    tlb->D1 = (env->CP0_EntryLo1 & 4) != 0;
    tlb->C1 = (env->CP0_EntryLo1 >> 3) & 0x7;
B
bellard 已提交
414 415 416 417 418
    tlb->PFN[1] = (env->CP0_EntryLo1 >> 6) << 12;
}

void do_tlbwi (void)
{
419 420 421 422 423
    /* Discard cached TLB entries.  We could avoid doing this if the
       tlbwi is just upgrading access permissions on the current entry;
       that might be a further win.  */
    mips_tlb_flush_extra (env, MIPS_TLB_NB);

T
ths 已提交
424
    /* Wildly undefined effects for CP0_Index containing a too high value and
425
       MIPS_TLB_NB not being a power of two.  But so does real silicon.  */
T
ths 已提交
426 427
    invalidate_tlb(env, env->CP0_Index & (MIPS_TLB_NB - 1), 0);
    fill_tlb(env->CP0_Index & (MIPS_TLB_NB - 1));
B
bellard 已提交
428 429 430 431 432 433
}

void do_tlbwr (void)
{
    int r = cpu_mips_get_random(env);

434
    invalidate_tlb(env, r, 1);
435
    fill_tlb(r);
B
bellard 已提交
436 437 438 439 440 441 442 443 444
}

void do_tlbp (void)
{
    tlb_t *tlb;
    target_ulong tag;
    uint8_t ASID;
    int i;

T
ths 已提交
445
    tag = env->CP0_EntryHi & (int32_t)0xFFFFE000;
B
bellard 已提交
446 447
    ASID = env->CP0_EntryHi & 0xFF;
    for (i = 0; i < MIPS_TLB_NB; i++) {
B
bellard 已提交
448 449 450 451
        tlb = &env->tlb[i];
        /* Check ASID, virtual page number & size */
        if ((tlb->G == 1 || tlb->ASID == ASID) && tlb->VPN == tag) {
            /* TLB match */
T
ths 已提交
452
            env->CP0_Index = i;
B
bellard 已提交
453 454 455
            break;
        }
    }
456
    if (i == MIPS_TLB_NB) {
457 458 459 460 461 462 463 464 465 466 467
        /* No match.  Discard any shadow entries, if any of them match.  */
        for (i = MIPS_TLB_NB; i < env->tlb_in_use; i++) {
	    tlb = &env->tlb[i];

	    /* Check ASID, virtual page number & size */
	    if ((tlb->G == 1 || tlb->ASID == ASID) && tlb->VPN == tag) {
                mips_tlb_flush_extra (env, i);
	        break;
	    }
	}

T
ths 已提交
468
        env->CP0_Index |= 0x80000000;
B
bellard 已提交
469 470 471 472 473 474
    }
}

void do_tlbr (void)
{
    tlb_t *tlb;
475
    uint8_t ASID;
B
bellard 已提交
476

477
    ASID = env->CP0_EntryHi & 0xFF;
T
ths 已提交
478
    tlb = &env->tlb[env->CP0_Index & (MIPS_TLB_NB - 1)];
B
bellard 已提交
479 480

    /* If this will change the current ASID, flush qemu's TLB.  */
481 482 483 484
    if (ASID != tlb->ASID)
        cpu_mips_tlb_flush (env, 1);

    mips_tlb_flush_extra(env, MIPS_TLB_NB);
B
bellard 已提交
485

B
bellard 已提交
486
    env->CP0_EntryHi = tlb->VPN | tlb->ASID;
T
ths 已提交
487
    env->CP0_PageMask = tlb->PageMask;
T
ths 已提交
488 489 490 491
    env->CP0_EntryLo0 = tlb->G | (tlb->V0 << 1) | (tlb->D0 << 2) |
                        (tlb->C0 << 3) | (tlb->PFN[0] >> 6);
    env->CP0_EntryLo1 = tlb->G | (tlb->V1 << 1) | (tlb->D1 << 2) |
                        (tlb->C1 << 3) | (tlb->PFN[1] >> 6);
B
bellard 已提交
492 493 494
}
#endif

B
bellard 已提交
495 496
#endif /* !CONFIG_USER_ONLY */

497
void dump_ldst (const unsigned char *func)
B
bellard 已提交
498 499
{
    if (loglevel)
T
ths 已提交
500
        fprintf(logfile, "%s => " TARGET_FMT_lx " " TARGET_FMT_lx "\n", __func__, T0, T1);
B
bellard 已提交
501 502 503 504 505
}

void dump_sc (void)
{
    if (loglevel) {
T
ths 已提交
506
        fprintf(logfile, "%s " TARGET_FMT_lx " at " TARGET_FMT_lx " (" TARGET_FMT_lx ")\n", __func__,
B
bellard 已提交
507 508 509 510
                T1, T0, env->CP0_LLAddr);
    }
}

511
void debug_pre_eret (void)
B
bellard 已提交
512
{
513 514 515 516 517 518 519 520 521 522 523
    fprintf(logfile, "ERET: PC " TARGET_FMT_lx " EPC " TARGET_FMT_lx,
            env->PC, env->CP0_EPC);
    if (env->CP0_Status & (1 << CP0St_ERL))
        fprintf(logfile, " ErrorEPC " TARGET_FMT_lx, env->CP0_ErrorEPC);
    if (env->hflags & MIPS_HFLAG_DM)
        fprintf(logfile, " DEPC " TARGET_FMT_lx, env->CP0_DEPC);
    fputs("\n", logfile);
}

void debug_post_eret (void)
{
T
ths 已提交
524
    fprintf(logfile, "  =>  PC " TARGET_FMT_lx " EPC " TARGET_FMT_lx,
525 526 527 528 529 530 531 532
            env->PC, env->CP0_EPC);
    if (env->CP0_Status & (1 << CP0St_ERL))
        fprintf(logfile, " ErrorEPC " TARGET_FMT_lx, env->CP0_ErrorEPC);
    if (env->hflags & MIPS_HFLAG_DM)
        fprintf(logfile, " DEPC " TARGET_FMT_lx, env->CP0_DEPC);
    if (env->hflags & MIPS_HFLAG_UM)
        fputs(", UM\n", logfile);
    else
T
ths 已提交
533
        fputs("\n", logfile);
B
bellard 已提交
534 535 536 537 538 539 540 541 542 543 544 545 546 547 548
}

void do_pmon (int function)
{
    function /= 2;
    switch (function) {
    case 2: /* TODO: char inbyte(int waitflag); */
        if (env->gpr[4] == 0)
            env->gpr[2] = -1;
        /* Fall through */
    case 11: /* TODO: char inbyte (void); */
        env->gpr[2] = -1;
        break;
    case 3:
    case 12:
549
        printf("%c", (char)(env->gpr[4] & 0xFF));
B
bellard 已提交
550 551 552 553 554
        break;
    case 17:
        break;
    case 158:
        {
555
            unsigned char *fmt = (void *)(unsigned long)env->gpr[4];
B
bellard 已提交
556 557 558 559 560
            printf("%s", fmt);
        }
        break;
    }
}
561 562 563

#if !defined(CONFIG_USER_ONLY) 

B
bellard 已提交
564 565
static void do_unaligned_access (target_ulong addr, int is_write, int is_user, void *retaddr);

566
#define MMUSUFFIX _mmu
B
bellard 已提交
567
#define ALIGNED_ONLY
568 569 570 571 572 573 574 575 576 577 578 579 580

#define SHIFT 0
#include "softmmu_template.h"

#define SHIFT 1
#include "softmmu_template.h"

#define SHIFT 2
#include "softmmu_template.h"

#define SHIFT 3
#include "softmmu_template.h"

B
bellard 已提交
581 582 583 584 585 586 587
static void do_unaligned_access (target_ulong addr, int is_write, int is_user, void *retaddr)
{
    env->CP0_BadVAddr = addr;
    do_restore_state (retaddr);
    do_raise_exception ((is_write == 1) ? EXCP_AdES : EXCP_AdEL);
}

588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616
void tlb_fill (target_ulong addr, int is_write, int is_user, void *retaddr)
{
    TranslationBlock *tb;
    CPUState *saved_env;
    unsigned long pc;
    int ret;

    /* XXX: hack to restore env in all cases, even if not called from
       generated code */
    saved_env = env;
    env = cpu_single_env;
    ret = cpu_mips_handle_mmu_fault(env, addr, is_write, is_user, 1);
    if (ret) {
        if (retaddr) {
            /* now we have a real cpu fault */
            pc = (unsigned long)retaddr;
            tb = tb_find_pc(pc);
            if (tb) {
                /* the PC is inside the translated code. It means that we have
                   a virtual CPU fault */
                cpu_restore_state(tb, env, pc, NULL);
            }
        }
        do_raise_exception_err(env->exception_index, env->error_code);
    }
    env = saved_env;
}

#endif