op_helper.c 103 KB
Newer Older
bellard's avatar
bellard committed
1 2
/*
 *  MIPS emulation helpers for qemu.
3
 *
bellard's avatar
bellard committed
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/>.
bellard's avatar
bellard committed
18
 */
19
#include <stdlib.h>
Blue Swirl's avatar
Blue Swirl committed
20
#include "cpu.h"
21
#include "qemu/host-utils.h"
22

pbrook's avatar
pbrook committed
23
#include "helper.h"
24

Blue Swirl's avatar
Blue Swirl committed
25
#if !defined(CONFIG_USER_ONLY)
26
#include "exec/softmmu_exec.h"
Blue Swirl's avatar
Blue Swirl committed
27 28
#endif /* !defined(CONFIG_USER_ONLY) */

29
#ifndef CONFIG_USER_ONLY
30
static inline void cpu_mips_tlb_flush (CPUMIPSState *env, int flush_global);
31 32
#endif

bellard's avatar
bellard committed
33 34 35
/*****************************************************************************/
/* Exceptions processing helpers */

36 37 38 39
static inline void QEMU_NORETURN do_raise_exception_err(CPUMIPSState *env,
                                                        uint32_t exception,
                                                        int error_code,
                                                        uintptr_t pc)
bellard's avatar
bellard committed
40
{
41
    if (exception < EXCP_SC) {
42
        qemu_log("%s: %d %d\n", __func__, exception, error_code);
43
    }
bellard's avatar
bellard committed
44 45
    env->exception_index = exception;
    env->error_code = error_code;
46 47 48

    if (pc) {
        /* now we have a real cpu fault */
49
        cpu_restore_state(env, pc);
50 51
    }

52
    cpu_loop_exit(env);
bellard's avatar
bellard committed
53 54
}

55 56 57
static inline void QEMU_NORETURN do_raise_exception(CPUMIPSState *env,
                                                    uint32_t exception,
                                                    uintptr_t pc)
bellard's avatar
bellard committed
58
{
59
    do_raise_exception_err(env, exception, 0, pc);
bellard's avatar
bellard committed
60 61
}

62 63
void helper_raise_exception_err(CPUMIPSState *env, uint32_t exception,
                                int error_code)
bellard's avatar
bellard committed
64
{
65 66
    do_raise_exception_err(env, exception, error_code, 0);
}
67

68 69 70
void helper_raise_exception(CPUMIPSState *env, uint32_t exception)
{
    do_raise_exception(env, exception, 0);
bellard's avatar
bellard committed
71 72
}

73 74
#if defined(CONFIG_USER_ONLY)
#define HELPER_LD(name, insn, type)                                     \
75 76
static inline type do_##name(CPUMIPSState *env, target_ulong addr,      \
                             int mem_idx)                               \
77 78 79 80 81
{                                                                       \
    return (type) insn##_raw(addr);                                     \
}
#else
#define HELPER_LD(name, insn, type)                                     \
82 83
static inline type do_##name(CPUMIPSState *env, target_ulong addr,      \
                             int mem_idx)                               \
84 85 86
{                                                                       \
    switch (mem_idx)                                                    \
    {                                                                   \
87 88
    case 0: return (type) cpu_##insn##_kernel(env, addr); break;        \
    case 1: return (type) cpu_##insn##_super(env, addr); break;         \
89
    default:                                                            \
90
    case 2: return (type) cpu_##insn##_user(env, addr); break;          \
91 92 93 94 95 96 97 98 99 100 101 102
    }                                                                   \
}
#endif
HELPER_LD(lbu, ldub, uint8_t)
HELPER_LD(lw, ldl, int32_t)
#ifdef TARGET_MIPS64
HELPER_LD(ld, ldq, int64_t)
#endif
#undef HELPER_LD

#if defined(CONFIG_USER_ONLY)
#define HELPER_ST(name, insn, type)                                     \
103 104
static inline void do_##name(CPUMIPSState *env, target_ulong addr,      \
                             type val, int mem_idx)                     \
105 106 107 108 109
{                                                                       \
    insn##_raw(addr, val);                                              \
}
#else
#define HELPER_ST(name, insn, type)                                     \
110 111
static inline void do_##name(CPUMIPSState *env, target_ulong addr,      \
                             type val, int mem_idx)                     \
112 113 114
{                                                                       \
    switch (mem_idx)                                                    \
    {                                                                   \
115 116
    case 0: cpu_##insn##_kernel(env, addr, val); break;                 \
    case 1: cpu_##insn##_super(env, addr, val); break;                  \
117
    default:                                                            \
118
    case 2: cpu_##insn##_user(env, addr, val); break;                   \
119 120 121 122 123 124 125 126 127 128
    }                                                                   \
}
#endif
HELPER_ST(sb, stb, uint8_t)
HELPER_ST(sw, stl, uint32_t)
#ifdef TARGET_MIPS64
HELPER_ST(sd, stq, uint64_t)
#endif
#undef HELPER_ST

129
target_ulong helper_clo (target_ulong arg1)
130
{
131
    return clo32(arg1);
132 133
}

134
target_ulong helper_clz (target_ulong arg1)
135
{
136
    return clz32(arg1);
137 138
}

139
#if defined(TARGET_MIPS64)
140
target_ulong helper_dclo (target_ulong arg1)
141
{
142
    return clo64(arg1);
143 144
}

145
target_ulong helper_dclz (target_ulong arg1)
146
{
147
    return clz64(arg1);
148
}
149
#endif /* TARGET_MIPS64 */
150

bellard's avatar
bellard committed
151
/* 64 bits arithmetic for 32 bits hosts */
152
static inline uint64_t get_HILO(CPUMIPSState *env)
bellard's avatar
bellard committed
153
{
154
    return ((uint64_t)(env->active_tc.HI[0]) << 32) | (uint32_t)env->active_tc.LO[0];
bellard's avatar
bellard committed
155 156
}

157
static inline target_ulong set_HIT0_LO(CPUMIPSState *env, uint64_t HILO)
158
{
159
    target_ulong tmp;
160
    env->active_tc.LO[0] = (int32_t)(HILO & 0xFFFFFFFF);
161 162
    tmp = env->active_tc.HI[0] = (int32_t)(HILO >> 32);
    return tmp;
163 164
}

165
static inline target_ulong set_HI_LOT0(CPUMIPSState *env, uint64_t HILO)
166
{
167
    target_ulong tmp = env->active_tc.LO[0] = (int32_t)(HILO & 0xFFFFFFFF);
168
    env->active_tc.HI[0] = (int32_t)(HILO >> 32);
169
    return tmp;
170 171 172
}

/* Multiplication variants of the vr54xx. */
173 174
target_ulong helper_muls(CPUMIPSState *env, target_ulong arg1,
                         target_ulong arg2)
175
{
176 177
    return set_HI_LOT0(env, 0 - ((int64_t)(int32_t)arg1 *
                                 (int64_t)(int32_t)arg2));
178 179
}

180 181
target_ulong helper_mulsu(CPUMIPSState *env, target_ulong arg1,
                          target_ulong arg2)
182
{
183 184
    return set_HI_LOT0(env, 0 - (uint64_t)(uint32_t)arg1 *
                       (uint64_t)(uint32_t)arg2);
185 186
}

187 188
target_ulong helper_macc(CPUMIPSState *env, target_ulong arg1,
                         target_ulong arg2)
189
{
190 191
    return set_HI_LOT0(env, (int64_t)get_HILO(env) + (int64_t)(int32_t)arg1 *
                       (int64_t)(int32_t)arg2);
192 193
}

194 195
target_ulong helper_macchi(CPUMIPSState *env, target_ulong arg1,
                           target_ulong arg2)
196
{
197 198
    return set_HIT0_LO(env, (int64_t)get_HILO(env) + (int64_t)(int32_t)arg1 *
                       (int64_t)(int32_t)arg2);
199 200
}

201 202
target_ulong helper_maccu(CPUMIPSState *env, target_ulong arg1,
                          target_ulong arg2)
203
{
204 205
    return set_HI_LOT0(env, (uint64_t)get_HILO(env) +
                       (uint64_t)(uint32_t)arg1 * (uint64_t)(uint32_t)arg2);
206 207
}

208 209
target_ulong helper_macchiu(CPUMIPSState *env, target_ulong arg1,
                            target_ulong arg2)
210
{
211 212
    return set_HIT0_LO(env, (uint64_t)get_HILO(env) +
                       (uint64_t)(uint32_t)arg1 * (uint64_t)(uint32_t)arg2);
213 214
}

215 216
target_ulong helper_msac(CPUMIPSState *env, target_ulong arg1,
                         target_ulong arg2)
217
{
218 219
    return set_HI_LOT0(env, (int64_t)get_HILO(env) - (int64_t)(int32_t)arg1 *
                       (int64_t)(int32_t)arg2);
220 221
}

222 223
target_ulong helper_msachi(CPUMIPSState *env, target_ulong arg1,
                           target_ulong arg2)
224
{
225 226
    return set_HIT0_LO(env, (int64_t)get_HILO(env) - (int64_t)(int32_t)arg1 *
                       (int64_t)(int32_t)arg2);
227 228
}

229 230
target_ulong helper_msacu(CPUMIPSState *env, target_ulong arg1,
                          target_ulong arg2)
231
{
232 233
    return set_HI_LOT0(env, (uint64_t)get_HILO(env) -
                       (uint64_t)(uint32_t)arg1 * (uint64_t)(uint32_t)arg2);
234 235
}

236 237
target_ulong helper_msachiu(CPUMIPSState *env, target_ulong arg1,
                            target_ulong arg2)
238
{
239 240
    return set_HIT0_LO(env, (uint64_t)get_HILO(env) -
                       (uint64_t)(uint32_t)arg1 * (uint64_t)(uint32_t)arg2);
241 242
}

243 244
target_ulong helper_mulhi(CPUMIPSState *env, target_ulong arg1,
                          target_ulong arg2)
245
{
246
    return set_HIT0_LO(env, (int64_t)(int32_t)arg1 * (int64_t)(int32_t)arg2);
247 248
}

249 250
target_ulong helper_mulhiu(CPUMIPSState *env, target_ulong arg1,
                           target_ulong arg2)
251
{
252 253
    return set_HIT0_LO(env, (uint64_t)(uint32_t)arg1 *
                       (uint64_t)(uint32_t)arg2);
254 255
}

256 257
target_ulong helper_mulshi(CPUMIPSState *env, target_ulong arg1,
                           target_ulong arg2)
258
{
259 260
    return set_HIT0_LO(env, 0 - (int64_t)(int32_t)arg1 *
                       (int64_t)(int32_t)arg2);
261 262
}

263 264
target_ulong helper_mulshiu(CPUMIPSState *env, target_ulong arg1,
                            target_ulong arg2)
265
{
266 267
    return set_HIT0_LO(env, 0 - (uint64_t)(uint32_t)arg1 *
                       (uint64_t)(uint32_t)arg2);
268
}
bellard's avatar
bellard committed
269

270
#ifndef CONFIG_USER_ONLY
271

272
static inline hwaddr do_translate_address(CPUMIPSState *env,
273 274
                                                      target_ulong address,
                                                      int rw)
275
{
276
    hwaddr lladdr;
277 278 279 280

    lladdr = cpu_mips_translate_address(env, address, rw);

    if (lladdr == -1LL) {
281
        cpu_loop_exit(env);
282 283 284 285 286
    } else {
        return lladdr;
    }
}

287
#define HELPER_LD_ATOMIC(name, insn)                                          \
288
target_ulong helper_##name(CPUMIPSState *env, target_ulong arg, int mem_idx)  \
289
{                                                                             \
290 291
    env->lladdr = do_translate_address(env, arg, 0);                          \
    env->llval = do_##insn(env, arg, mem_idx);                                \
292 293 294 295 296 297 298 299 300
    return env->llval;                                                        \
}
HELPER_LD_ATOMIC(ll, lw)
#ifdef TARGET_MIPS64
HELPER_LD_ATOMIC(lld, ld)
#endif
#undef HELPER_LD_ATOMIC

#define HELPER_ST_ATOMIC(name, ld_insn, st_insn, almask)                      \
301 302
target_ulong helper_##name(CPUMIPSState *env, target_ulong arg1,              \
                           target_ulong arg2, int mem_idx)                    \
303 304 305 306 307
{                                                                             \
    target_long tmp;                                                          \
                                                                              \
    if (arg2 & almask) {                                                      \
        env->CP0_BadVAddr = arg2;                                             \
308
        helper_raise_exception(env, EXCP_AdES);                               \
309
    }                                                                         \
310 311
    if (do_translate_address(env, arg2, 1) == env->lladdr) {                  \
        tmp = do_##ld_insn(env, arg2, mem_idx);                               \
312
        if (tmp == env->llval) {                                              \
313
            do_##st_insn(env, arg2, arg1, mem_idx);                           \
314 315 316 317 318 319 320 321 322 323 324 325
            return 1;                                                         \
        }                                                                     \
    }                                                                         \
    return 0;                                                                 \
}
HELPER_ST_ATOMIC(sc, lw, sw, 0x3)
#ifdef TARGET_MIPS64
HELPER_ST_ATOMIC(scd, ld, sd, 0x7)
#endif
#undef HELPER_ST_ATOMIC
#endif

ths's avatar
ths committed
326 327 328 329 330 331 332 333
#ifdef TARGET_WORDS_BIGENDIAN
#define GET_LMASK(v) ((v) & 3)
#define GET_OFFSET(addr, offset) (addr + (offset))
#else
#define GET_LMASK(v) (((v) & 3) ^ 3)
#define GET_OFFSET(addr, offset) (addr - (offset))
#endif

334 335
void helper_swl(CPUMIPSState *env, target_ulong arg1, target_ulong arg2,
                int mem_idx)
ths's avatar
ths committed
336
{
337
    do_sb(env, arg2, (uint8_t)(arg1 >> 24), mem_idx);
ths's avatar
ths committed
338

339
    if (GET_LMASK(arg2) <= 2)
340
        do_sb(env, GET_OFFSET(arg2, 1), (uint8_t)(arg1 >> 16), mem_idx);
ths's avatar
ths committed
341

342
    if (GET_LMASK(arg2) <= 1)
343
        do_sb(env, GET_OFFSET(arg2, 2), (uint8_t)(arg1 >> 8), mem_idx);
ths's avatar
ths committed
344

345
    if (GET_LMASK(arg2) == 0)
346
        do_sb(env, GET_OFFSET(arg2, 3), (uint8_t)arg1, mem_idx);
ths's avatar
ths committed
347 348
}

349 350
void helper_swr(CPUMIPSState *env, target_ulong arg1, target_ulong arg2,
                int mem_idx)
ths's avatar
ths committed
351
{
352
    do_sb(env, arg2, (uint8_t)arg1, mem_idx);
ths's avatar
ths committed
353

354
    if (GET_LMASK(arg2) >= 1)
355
        do_sb(env, GET_OFFSET(arg2, -1), (uint8_t)(arg1 >> 8), mem_idx);
ths's avatar
ths committed
356

357
    if (GET_LMASK(arg2) >= 2)
358
        do_sb(env, GET_OFFSET(arg2, -2), (uint8_t)(arg1 >> 16), mem_idx);
ths's avatar
ths committed
359

360
    if (GET_LMASK(arg2) == 3)
361
        do_sb(env, GET_OFFSET(arg2, -3), (uint8_t)(arg1 >> 24), mem_idx);
ths's avatar
ths committed
362 363 364 365 366 367 368 369 370 371 372 373
}

#if defined(TARGET_MIPS64)
/* "half" load and stores.  We must do the memory access inline,
   or fault handling won't work.  */

#ifdef TARGET_WORDS_BIGENDIAN
#define GET_LMASK64(v) ((v) & 7)
#else
#define GET_LMASK64(v) (((v) & 7) ^ 7)
#endif

374 375
void helper_sdl(CPUMIPSState *env, target_ulong arg1, target_ulong arg2,
                int mem_idx)
ths's avatar
ths committed
376
{
377
    do_sb(env, arg2, (uint8_t)(arg1 >> 56), mem_idx);
ths's avatar
ths committed
378

379
    if (GET_LMASK64(arg2) <= 6)
380
        do_sb(env, GET_OFFSET(arg2, 1), (uint8_t)(arg1 >> 48), mem_idx);
ths's avatar
ths committed
381

382
    if (GET_LMASK64(arg2) <= 5)
383
        do_sb(env, GET_OFFSET(arg2, 2), (uint8_t)(arg1 >> 40), mem_idx);
ths's avatar
ths committed
384

385
    if (GET_LMASK64(arg2) <= 4)
386
        do_sb(env, GET_OFFSET(arg2, 3), (uint8_t)(arg1 >> 32), mem_idx);
ths's avatar
ths committed
387

388
    if (GET_LMASK64(arg2) <= 3)
389
        do_sb(env, GET_OFFSET(arg2, 4), (uint8_t)(arg1 >> 24), mem_idx);
ths's avatar
ths committed
390

391
    if (GET_LMASK64(arg2) <= 2)
392
        do_sb(env, GET_OFFSET(arg2, 5), (uint8_t)(arg1 >> 16), mem_idx);
ths's avatar
ths committed
393

394
    if (GET_LMASK64(arg2) <= 1)
395
        do_sb(env, GET_OFFSET(arg2, 6), (uint8_t)(arg1 >> 8), mem_idx);
ths's avatar
ths committed
396

397
    if (GET_LMASK64(arg2) <= 0)
398
        do_sb(env, GET_OFFSET(arg2, 7), (uint8_t)arg1, mem_idx);
ths's avatar
ths committed
399 400
}

401 402
void helper_sdr(CPUMIPSState *env, target_ulong arg1, target_ulong arg2,
                int mem_idx)
ths's avatar
ths committed
403
{
404
    do_sb(env, arg2, (uint8_t)arg1, mem_idx);
ths's avatar
ths committed
405

406
    if (GET_LMASK64(arg2) >= 1)
407
        do_sb(env, GET_OFFSET(arg2, -1), (uint8_t)(arg1 >> 8), mem_idx);
ths's avatar
ths committed
408

409
    if (GET_LMASK64(arg2) >= 2)
410
        do_sb(env, GET_OFFSET(arg2, -2), (uint8_t)(arg1 >> 16), mem_idx);
ths's avatar
ths committed
411

412
    if (GET_LMASK64(arg2) >= 3)
413
        do_sb(env, GET_OFFSET(arg2, -3), (uint8_t)(arg1 >> 24), mem_idx);
ths's avatar
ths committed
414

415
    if (GET_LMASK64(arg2) >= 4)
416
        do_sb(env, GET_OFFSET(arg2, -4), (uint8_t)(arg1 >> 32), mem_idx);
ths's avatar
ths committed
417

418
    if (GET_LMASK64(arg2) >= 5)
419
        do_sb(env, GET_OFFSET(arg2, -5), (uint8_t)(arg1 >> 40), mem_idx);
ths's avatar
ths committed
420

421
    if (GET_LMASK64(arg2) >= 6)
422
        do_sb(env, GET_OFFSET(arg2, -6), (uint8_t)(arg1 >> 48), mem_idx);
ths's avatar
ths committed
423

424
    if (GET_LMASK64(arg2) == 7)
425
        do_sb(env, GET_OFFSET(arg2, -7), (uint8_t)(arg1 >> 56), mem_idx);
ths's avatar
ths committed
426 427 428
}
#endif /* TARGET_MIPS64 */

429 430
static const int multiple_regs[] = { 16, 17, 18, 19, 20, 21, 22, 23, 30 };

431 432
void helper_lwm(CPUMIPSState *env, target_ulong addr, target_ulong reglist,
                uint32_t mem_idx)
433 434 435 436 437 438 439 440
{
    target_ulong base_reglist = reglist & 0xf;
    target_ulong do_r31 = reglist & 0x10;

    if (base_reglist > 0 && base_reglist <= ARRAY_SIZE (multiple_regs)) {
        target_ulong i;

        for (i = 0; i < base_reglist; i++) {
441 442
            env->active_tc.gpr[multiple_regs[i]] =
                (target_long)do_lw(env, addr, mem_idx);
443 444 445 446 447
            addr += 4;
        }
    }

    if (do_r31) {
448
        env->active_tc.gpr[31] = (target_long)do_lw(env, addr, mem_idx);
449 450 451
    }
}

452 453
void helper_swm(CPUMIPSState *env, target_ulong addr, target_ulong reglist,
                uint32_t mem_idx)
454 455 456 457 458 459 460 461
{
    target_ulong base_reglist = reglist & 0xf;
    target_ulong do_r31 = reglist & 0x10;

    if (base_reglist > 0 && base_reglist <= ARRAY_SIZE (multiple_regs)) {
        target_ulong i;

        for (i = 0; i < base_reglist; i++) {
462
            do_sw(env, addr, env->active_tc.gpr[multiple_regs[i]], mem_idx);
463 464 465 466 467
            addr += 4;
        }
    }

    if (do_r31) {
468
        do_sw(env, addr, env->active_tc.gpr[31], mem_idx);
469 470 471 472
    }
}

#if defined(TARGET_MIPS64)
473 474
void helper_ldm(CPUMIPSState *env, target_ulong addr, target_ulong reglist,
                uint32_t mem_idx)
475 476 477 478 479 480 481 482
{
    target_ulong base_reglist = reglist & 0xf;
    target_ulong do_r31 = reglist & 0x10;

    if (base_reglist > 0 && base_reglist <= ARRAY_SIZE (multiple_regs)) {
        target_ulong i;

        for (i = 0; i < base_reglist; i++) {
483
            env->active_tc.gpr[multiple_regs[i]] = do_ld(env, addr, mem_idx);
484 485 486 487 488
            addr += 8;
        }
    }

    if (do_r31) {
489
        env->active_tc.gpr[31] = do_ld(env, addr, mem_idx);
490 491 492
    }
}

493 494
void helper_sdm(CPUMIPSState *env, target_ulong addr, target_ulong reglist,
                uint32_t mem_idx)
495 496 497 498 499 500 501 502
{
    target_ulong base_reglist = reglist & 0xf;
    target_ulong do_r31 = reglist & 0x10;

    if (base_reglist > 0 && base_reglist <= ARRAY_SIZE (multiple_regs)) {
        target_ulong i;

        for (i = 0; i < base_reglist; i++) {
503
            do_sd(env, addr, env->active_tc.gpr[multiple_regs[i]], mem_idx);
504 505 506 507 508
            addr += 8;
        }
    }

    if (do_r31) {
509
        do_sd(env, addr, env->active_tc.gpr[31], mem_idx);
510 511 512 513
    }
}
#endif

ths's avatar
ths committed
514
#ifndef CONFIG_USER_ONLY
515
/* SMP helpers.  */
516
static bool mips_vpe_is_wfi(MIPSCPU *c)
517
{
518
    CPUState *cpu = CPU(c);
519 520
    CPUMIPSState *env = &c->env;

521 522
    /* If the VPE is halted but otherwise active, it means it's waiting for
       an interrupt.  */
523
    return cpu->halted && mips_vpe_active(env);
524 525
}

526
static inline void mips_vpe_wake(MIPSCPU *c)
527 528 529 530
{
    /* Dont set ->halted = 0 directly, let it be done via cpu_has_work
       because there might be other conditions that state that c should
       be sleeping.  */
531
    cpu_interrupt(CPU(c), CPU_INTERRUPT_WAKE);
532 533
}

534
static inline void mips_vpe_sleep(MIPSCPU *cpu)
535
{
536
    CPUState *cs = CPU(cpu);
537

538 539
    /* The VPE was shut off, really go to bed.
       Reset any old _WAKE requests.  */
540
    cs->halted = 1;
541
    cpu_reset_interrupt(cs, CPU_INTERRUPT_WAKE);
542 543
}

544
static inline void mips_tc_wake(MIPSCPU *cpu, int tc)
545
{
546 547
    CPUMIPSState *c = &cpu->env;

548
    /* FIXME: TC reschedule.  */
549
    if (mips_vpe_active(c) && !mips_vpe_is_wfi(cpu)) {
550
        mips_vpe_wake(cpu);
551 552 553
    }
}

554
static inline void mips_tc_sleep(MIPSCPU *cpu, int tc)
555
{
556 557
    CPUMIPSState *c = &cpu->env;

558 559
    /* FIXME: TC reschedule.  */
    if (!mips_vpe_active(c)) {
560
        mips_vpe_sleep(cpu);
561 562 563
    }
}

564 565 566 567 568 569 570 571 572
/**
 * mips_cpu_map_tc:
 * @env: CPU from which mapping is performed.
 * @tc: Should point to an int with the value of the global TC index.
 *
 * This function will transform @tc into a local index within the
 * returned #CPUMIPSState.
 */
/* FIXME: This code assumes that all VPEs have the same number of TCs,
573
          which depends on runtime setup. Can probably be fixed by
574
          walking the list of CPUMIPSStates.  */
575
static CPUMIPSState *mips_cpu_map_tc(CPUMIPSState *env, int *tc)
576
{
577
    MIPSCPU *cpu;
578
    CPUState *cs;
579
    CPUState *other_cs;
580
    int vpe_idx;
581 582 583 584 585 586 587 588
    int tc_idx = *tc;

    if (!(env->CP0_VPEConf0 & (1 << CP0VPEC0_MVP))) {
        /* Not allowed to address other CPUs.  */
        *tc = env->current_tc;
        return env;
    }

589 590 591
    cs = CPU(mips_env_get_cpu(env));
    vpe_idx = tc_idx / cs->nr_threads;
    *tc = tc_idx % cs->nr_threads;
592 593 594 595 596 597
    other_cs = qemu_get_cpu(vpe_idx);
    if (other_cs == NULL) {
        return env;
    }
    cpu = MIPS_CPU(other_cs);
    return &cpu->env;
598 599
}

600 601 602 603 604 605 606 607 608
/* The per VPE CP0_Status register shares some fields with the per TC
   CP0_TCStatus registers. These fields are wired to the same registers,
   so changes to either of them should be reflected on both registers.

   Also, EntryHi shares the bottom 8 bit ASID with TCStauts.

   These helper call synchronizes the regs for a given cpu.  */

/* Called for updates to CP0_Status.  */
609
static void sync_c0_status(CPUMIPSState *env, CPUMIPSState *cpu, int tc)
610 611 612 613 614 615 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
{
    int32_t tcstatus, *tcst;
    uint32_t v = cpu->CP0_Status;
    uint32_t cu, mx, asid, ksu;
    uint32_t mask = ((1 << CP0TCSt_TCU3)
                       | (1 << CP0TCSt_TCU2)
                       | (1 << CP0TCSt_TCU1)
                       | (1 << CP0TCSt_TCU0)
                       | (1 << CP0TCSt_TMX)
                       | (3 << CP0TCSt_TKSU)
                       | (0xff << CP0TCSt_TASID));

    cu = (v >> CP0St_CU0) & 0xf;
    mx = (v >> CP0St_MX) & 0x1;
    ksu = (v >> CP0St_KSU) & 0x3;
    asid = env->CP0_EntryHi & 0xff;

    tcstatus = cu << CP0TCSt_TCU0;
    tcstatus |= mx << CP0TCSt_TMX;
    tcstatus |= ksu << CP0TCSt_TKSU;
    tcstatus |= asid;

    if (tc == cpu->current_tc) {
        tcst = &cpu->active_tc.CP0_TCStatus;
    } else {
        tcst = &cpu->tcs[tc].CP0_TCStatus;
    }

    *tcst &= ~mask;
    *tcst |= tcstatus;
    compute_hflags(cpu);
}

/* Called for updates to CP0_TCStatus.  */
644 645
static void sync_c0_tcstatus(CPUMIPSState *cpu, int tc,
                             target_ulong v)
646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675
{
    uint32_t status;
    uint32_t tcu, tmx, tasid, tksu;
    uint32_t mask = ((1 << CP0St_CU3)
                       | (1 << CP0St_CU2)
                       | (1 << CP0St_CU1)
                       | (1 << CP0St_CU0)
                       | (1 << CP0St_MX)
                       | (3 << CP0St_KSU));

    tcu = (v >> CP0TCSt_TCU0) & 0xf;
    tmx = (v >> CP0TCSt_TMX) & 0x1;
    tasid = v & 0xff;
    tksu = (v >> CP0TCSt_TKSU) & 0x3;

    status = tcu << CP0St_CU0;
    status |= tmx << CP0St_MX;
    status |= tksu << CP0St_KSU;

    cpu->CP0_Status &= ~mask;
    cpu->CP0_Status |= status;

    /* Sync the TASID with EntryHi.  */
    cpu->CP0_EntryHi &= ~0xff;
    cpu->CP0_EntryHi = tasid;

    compute_hflags(cpu);
}

/* Called for updates to CP0_EntryHi.  */
676
static void sync_c0_entryhi(CPUMIPSState *cpu, int tc)
677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692
{
    int32_t *tcst;
    uint32_t asid, v = cpu->CP0_EntryHi;

    asid = v & 0xff;

    if (tc == cpu->current_tc) {
        tcst = &cpu->active_tc.CP0_TCStatus;
    } else {
        tcst = &cpu->tcs[tc].CP0_TCStatus;
    }

    *tcst &= ~0xff;
    *tcst |= asid;
}

bellard's avatar
bellard committed
693
/* CP0 helpers */
694
target_ulong helper_mfc0_mvpcontrol(CPUMIPSState *env)
695
{
696
    return env->mvp->CP0_MVPControl;
697 698
}

699
target_ulong helper_mfc0_mvpconf0(CPUMIPSState *env)
700
{
701
    return env->mvp->CP0_MVPConf0;
702 703
}

704
target_ulong helper_mfc0_mvpconf1(CPUMIPSState *env)
705
{
706
    return env->mvp->CP0_MVPConf1;
707 708
}

709
target_ulong helper_mfc0_random(CPUMIPSState *env)
bellard's avatar
bellard committed
710
{
711
    return (int32_t)cpu_mips_get_random(env);
712
}
bellard's avatar
bellard committed
713

714
target_ulong helper_mfc0_tcstatus(CPUMIPSState *env)
715
{
716
    return env->active_tc.CP0_TCStatus;
717 718
}

719
target_ulong helper_mftc0_tcstatus(CPUMIPSState *env)
720 721
{
    int other_tc = env->CP0_VPEControl & (0xff << CP0VPECo_TargTC);
722
    CPUMIPSState *other = mips_cpu_map_tc(env, &other_tc);
723

724 725
    if (other_tc == other->current_tc)
        return other->active_tc.CP0_TCStatus;
726
    else
727
        return other->tcs[other_tc].CP0_TCStatus;
728 729
}

730
target_ulong helper_mfc0_tcbind(CPUMIPSState *env)
731
{
732
    return env->active_tc.CP0_TCBind;
733 734
}

735
target_ulong helper_mftc0_tcbind(CPUMIPSState *env)
736 737
{
    int other_tc = env->CP0_VPEControl & (0xff << CP0VPECo_TargTC);
738
    CPUMIPSState *other = mips_cpu_map_tc(env, &other_tc);
739

740 741
    if (other_tc == other->current_tc)
        return other->active_tc.CP0_TCBind;
742
    else
743
        return other->tcs[other_tc].CP0_TCBind;
744 745
}

746
target_ulong helper_mfc0_tcrestart(CPUMIPSState *env)
747
{
748
    return env->active_tc.PC;
749 750
}

751
target_ulong helper_mftc0_tcrestart(CPUMIPSState *env)
752 753
{
    int other_tc = env->CP0_VPEControl & (0xff << CP0VPECo_TargTC);
754
    CPUMIPSState *other = mips_cpu_map_tc(env, &other_tc);
755

756 757
    if (other_tc == other->current_tc)
        return other->active_tc.PC;
758
    else
759
        return other->tcs[other_tc].PC;
760 761
}

762
target_ulong helper_mfc0_tchalt(CPUMIPSState *env)
763
{
764
    return env->active_tc.CP0_TCHalt;
765 766
}

767
target_ulong helper_mftc0_tchalt(CPUMIPSState *env)
768 769
{
    int other_tc = env->CP0_VPEControl & (0xff << CP0VPECo_TargTC);
770
    CPUMIPSState *other = mips_cpu_map_tc(env, &other_tc);
771

772 773
    if (other_tc == other->current_tc)
        return other->active_tc.CP0_TCHalt;
774
    else
775
        return other->tcs[other_tc].CP0_TCHalt;
776 777
}

778
target_ulong helper_mfc0_tccontext(CPUMIPSState *env)
779
{
780
    return env->active_tc.CP0_TCContext;
781 782
}

783
target_ulong helper_mftc0_tccontext(CPUMIPSState *env)
784 785
{
    int other_tc = env->CP0_VPEControl & (0xff << CP0VPECo_TargTC);
786
    CPUMIPSState *other = mips_cpu_map_tc(env, &other_tc);
787

788 789
    if (other_tc == other->current_tc)
        return other->active_tc.CP0_TCContext;
790
    else
791
        return other->tcs[other_tc].CP0_TCContext;
792 793
}

794
target_ulong helper_mfc0_tcschedule(CPUMIPSState *env)
795
{
796
    return env->active_tc.CP0_TCSchedule;
797 798
}

799
target_ulong helper_mftc0_tcschedule(CPUMIPSState *env)
800 801
{
    int other_tc = env->CP0_VPEControl & (0xff << CP0VPECo_TargTC);
802
    CPUMIPSState *other = mips_cpu_map_tc(env, &other_tc);
803

804 805
    if (other_tc == other->current_tc)
        return other->active_tc.CP0_TCSchedule;
806
    else
807
        return other->tcs[other_tc].CP0_TCSchedule;
808 809
}

810
target_ulong helper_mfc0_tcschefback(CPUMIPSState *env)
811
{
812
    return env->active_tc.CP0_TCScheFBack;
813 814
}

815
target_ulong helper_mftc0_tcschefback(CPUMIPSState *env)
816 817
{
    int other_tc = env->CP0_VPEControl & (0xff << CP0VPECo_TargTC);
818
    CPUMIPSState *other = mips_cpu_map_tc(env, &other_tc);
819

820 821
    if (other_tc == other->current_tc)
        return other->active_tc.CP0_TCScheFBack;
822
    else
823
        return other->tcs[other_tc].CP0_TCScheFBack;
824 825
}

826
target_ulong helper_mfc0_count(CPUMIPSState *env)
827
{
828
    return (int32_t)cpu_mips_get_count(env);
bellard's avatar
bellard committed
829 830
}

831
target_ulong helper_mftc0_entryhi(CPUMIPSState *env)
832 833
{
    int other_tc = env->CP0_VPEControl & (0xff << CP0VPECo_TargTC);
834
    CPUMIPSState *other = mips_cpu_map_tc(env, &other_tc);
835

836
    return other->CP0_EntryHi;
837 838
}

839
target_ulong helper_mftc0_cause(CPUMIPSState *env)
840 841 842
{
    int other_tc = env->CP0_VPEControl & (0xff << CP0VPECo_TargTC);
    int32_t tccause;
843
    CPUMIPSState *other = mips_cpu_map_tc(env, &other_tc);
844 845 846 847 848 849 850 851 852 853

    if (other_tc == other->current_tc) {
        tccause = other->CP0_Cause;
    } else {
        tccause = other->CP0_Cause;
    }

    return tccause;
}

854
target_ulong helper_mftc0_status(CPUMIPSState *env)
855 856
{
    int other_tc = env->CP0_VPEControl & (0xff << CP0VPECo_TargTC);
857
    CPUMIPSState *other = mips_cpu_map_tc(env, &other_tc);
858

859
    return other->CP0_Status;
860 861
}

862
target_ulong helper_mfc0_lladdr(CPUMIPSState *env)
863
{
864
    return (int32_t)(env->lladdr >> env->CP0_LLAddr_shift);
865 866
}

867
target_ulong helper_mfc0_watchlo(CPUMIPSState *env, uint32_t sel)
868
{
869
    return (int32_t)env->CP0_WatchLo[sel];
870 871
}

872
target_ulong helper_mfc0_watchhi(CPUMIPSState *env, uint32_t sel)
873
{
874
    return env->CP0_WatchHi[sel];
875 876
}

877
target_ulong helper_mfc0_debug(CPUMIPSState *env)
878
{
879
    target_ulong t0 = env->CP0_Debug;
880
    if (env->hflags & MIPS_HFLAG_DM)
881 882 883
        t0 |= 1 << CP0DB_DM;

    return t0;
884 885
}

886
target_ulong helper_mftc0_debug(CPUMIPSState *env)
887 888
{
    int other_tc = env->CP0_VPEControl & (0xff << CP0VPECo_TargTC);
889
    int32_t tcstatus;
890
    CPUMIPSState *other = mips_cpu_map_tc(env, &other_tc);
891

892 893
    if (other_tc == other->current_tc)
        tcstatus = other->active_tc.CP0_Debug_tcstatus;
894
    else
895
        tcstatus = other->tcs[other_tc].CP0_Debug_tcstatus;
896 897

    /* XXX: Might be wrong, check with EJTAG spec. */
898
    return (other->CP0_Debug & ~((1 << CP0DB_SSt) | (1 << CP0DB_Halt))) |
899
            (tcstatus & ((1 << CP0DB_SSt) | (1 << CP0DB_Halt)));
900 901 902
}

#if defined(TARGET_MIPS64)
903
target_ulong helper_dmfc0_tcrestart(CPUMIPSState *env)
904
{
905
    return env->active_tc.PC;
906 907
}

908
target_ulong helper_dmfc0_tchalt(CPUMIPSState *env)
909
{
910
    return env->active_tc.CP0_TCHalt;
911 912
}

913
target_ulong helper_dmfc0_tccontext(CPUMIPSState *env)
914
{
915
    return env->active_tc.CP0_TCContext;