~vcs-imports/qemu/git

1458 by bellard
MIPS target (Jocelyn Mayer)
1
/*
2
 *  MIPS emulation helpers for qemu.
3
 * 
4
 *  Copyright (c) 2004-2005 Jocelyn Mayer
5
 *
6
 * This library is free software; you can redistribute it and/or
7
 * modify it under the terms of the GNU Lesser General Public
8
 * License as published by the Free Software Foundation; either
9
 * version 2 of the License, or (at your option) any later version.
10
 *
11
 * This library is distributed in the hope that it will be useful,
12
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14
 * Lesser General Public License for more details.
15
 *
16
 * You should have received a copy of the GNU Lesser General Public
17
 * License along with this library; if not, write to the Free Software
18
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
19
 */
20
#include <math.h>
21
#include "exec.h"
22
23
#define MIPS_DEBUG_DISAS
24
25
/*****************************************************************************/
26
/* Exceptions processing helpers */
27
void cpu_loop_exit(void)
28
{
29
    longjmp(env->jmp_env, 1);
30
}
31
32
void do_raise_exception_err (uint32_t exception, int error_code)
33
{
34
#if 1
35
    if (logfile && exception < 0x100)
36
        fprintf(logfile, "%s: %d %d\n", __func__, exception, error_code);
37
#endif
38
    env->exception_index = exception;
39
    env->error_code = error_code;
40
    T0 = 0;
41
    cpu_loop_exit();
42
}
43
44
void do_raise_exception (uint32_t exception)
45
{
46
    do_raise_exception_err(exception, 0);
47
}
48
49
#define MEMSUFFIX _raw
50
#include "op_helper_mem.c"
51
#undef MEMSUFFIX
52
#if !defined(CONFIG_USER_ONLY)
53
#define MEMSUFFIX _user
54
#include "op_helper_mem.c"
55
#undef MEMSUFFIX
56
#define MEMSUFFIX _kernel
57
#include "op_helper_mem.c"
58
#undef MEMSUFFIX
59
#endif
60
61
/* 64 bits arithmetic for 32 bits hosts */
62
#if (HOST_LONG_BITS == 32)
63
static inline uint64_t get_HILO (void)
64
{
65
    return ((uint64_t)env->HI << 32) | (uint64_t)env->LO;
66
}
67
68
static inline void set_HILO (uint64_t HILO)
69
{
70
    env->LO = HILO & 0xFFFFFFFF;
71
    env->HI = HILO >> 32;
72
}
73
74
void do_mult (void)
75
{
76
    set_HILO((int64_t)T0 * (int64_t)T1);
77
}
78
79
void do_multu (void)
80
{
81
    set_HILO((uint64_t)T0 * (uint64_t)T1);
82
}
83
84
void do_madd (void)
85
{
86
    int64_t tmp;
87
88
    tmp = ((int64_t)T0 * (int64_t)T1);
89
    set_HILO((int64_t)get_HILO() + tmp);
90
}
91
92
void do_maddu (void)
93
{
94
    uint64_t tmp;
95
96
    tmp = ((uint64_t)T0 * (uint64_t)T1);
97
    set_HILO(get_HILO() + tmp);
98
}
99
100
void do_msub (void)
101
{
102
    int64_t tmp;
103
104
    tmp = ((int64_t)T0 * (int64_t)T1);
105
    set_HILO((int64_t)get_HILO() - tmp);
106
}
107
108
void do_msubu (void)
109
{
110
    uint64_t tmp;
111
112
    tmp = ((uint64_t)T0 * (uint64_t)T1);
113
    set_HILO(get_HILO() - tmp);
114
}
115
#endif
116
117
/* CP0 helpers */
118
void do_mfc0 (int reg, int sel)
119
{
120
    const unsigned char *rn;
121
122
    if (sel != 0 && reg != 16 && reg != 28) {
123
        rn = "invalid";
124
        goto print;
125
    }
126
    switch (reg) {
127
    case 0:
128
        T0 = env->CP0_index;
129
        rn = "Index";
130
        break;
131
    case 1:
132
        T0 = cpu_mips_get_random(env);
133
        rn = "Random";
134
        break;
135
    case 2:
136
        T0 = env->CP0_EntryLo0;
137
        rn = "EntryLo0";
138
        break;
139
    case 3:
140
        T0 = env->CP0_EntryLo1;
141
        rn = "EntryLo1";
142
        break;
143
    case 4:
144
        T0 = env->CP0_Context;
145
        rn = "Context";
146
        break;
147
    case 5:
148
        T0 = env->CP0_PageMask;
149
        rn = "PageMask";
150
        break;
151
    case 6:
152
        T0 = env->CP0_Wired;
153
        rn = "Wired";
154
        break;
155
    case 8:
156
        T0 = env->CP0_BadVAddr;
157
        rn = "BadVaddr";
158
        break;
159
    case 9:
160
        T0 = cpu_mips_get_count(env);
161
        rn = "Count";
162
        break;
163
    case 10:
164
        T0 = env->CP0_EntryHi;
165
        rn = "EntryHi";
166
        break;
167
    case 11:
168
        T0 = env->CP0_Compare;
169
        rn = "Compare";
170
        break;
171
    case 12:
172
        T0 = env->CP0_Status;
173
        if (env->hflags & MIPS_HFLAG_UM)
1467 by bellard
fixed C0 status codes (Ralf Baechle)
174
            T0 |= (1 << CP0St_UM);
1458 by bellard
MIPS target (Jocelyn Mayer)
175
        if (env->hflags & MIPS_HFLAG_ERL)
1467 by bellard
fixed C0 status codes (Ralf Baechle)
176
            T0 |= (1 << CP0St_ERL);
1458 by bellard
MIPS target (Jocelyn Mayer)
177
        if (env->hflags & MIPS_HFLAG_EXL)
1467 by bellard
fixed C0 status codes (Ralf Baechle)
178
            T0 |= (1 << CP0St_EXL);
1458 by bellard
MIPS target (Jocelyn Mayer)
179
        rn = "Status";
180
        break;
181
    case 13:
182
        T0 = env->CP0_Cause;
183
        rn = "Cause";
184
        break;
185
    case 14:
186
        T0 = env->CP0_EPC;
187
        rn = "EPC";
188
        break;
189
    case 15:
190
        T0 = env->CP0_PRid;
191
        rn = "PRid";
192
        break;
193
    case 16:
194
        switch (sel) {
195
        case 0:
196
            T0 = env->CP0_Config0;
197
            rn = "Config";
198
            break;
199
        case 1:
200
            T0 = env->CP0_Config1;
201
            rn = "Config1";
202
            break;
203
        default:
204
            rn = "Unknown config register";
205
            break;
206
        }
207
        break;
208
    case 17:
209
        T0 = env->CP0_LLAddr >> 4;
210
        rn = "LLAddr";
211
        break;
212
    case 18:
213
        T0 = env->CP0_WatchLo;
214
        rn = "WatchLo";
215
        break;
216
    case 19:
217
        T0 = env->CP0_WatchHi;
218
        rn = "WatchHi";
219
        break;
220
    case 23:
221
        T0 = env->CP0_Debug;
222
        if (env->hflags & MIPS_HFLAG_DM)
223
            T0 |= 1 << CP0DB_DM;
224
        rn = "Debug";
225
        break;
226
    case 24:
227
        T0 = env->CP0_DEPC;
228
        rn = "DEPC";
229
        break;
230
    case 28:
231
        switch (sel) {
232
        case 0:
233
            T0 = env->CP0_TagLo;
234
            rn = "TagLo";
235
            break;
236
        case 1:
237
            T0 = env->CP0_DataLo;
238
            rn = "DataLo";
239
            break;
240
        default:
241
            rn = "unknown sel";
242
            break;
243
        }
244
        break;
245
    case 30:
246
        T0 = env->CP0_ErrorEPC;
247
        rn = "ErrorEPC";
248
        break;
249
    case 31:
250
        T0 = env->CP0_DESAVE;
251
        rn = "DESAVE";
252
        break;
253
    default:
254
        rn = "unknown";
255
        break;
256
    }
257
 print:
258
#if defined MIPS_DEBUG_DISAS
259
    if (loglevel & CPU_LOG_TB_IN_ASM) {
260
        fprintf(logfile, "%08x mfc0 %s => %08x (%d %d)\n",
261
                env->PC, rn, T0, reg, sel);
262
    }
263
#endif
264
    return;
265
}
266
267
void do_mtc0 (int reg, int sel)
268
{
269
    const unsigned char *rn;
270
    uint32_t val, old, mask;
271
272
    if (sel != 0 && reg != 16 && reg != 28) {
273
        val = -1;
274
        old = -1;
275
        rn = "invalid";
276
        goto print;
277
    }
278
    switch (reg) {
279
    case 0:
280
        val = (env->CP0_index & 0x80000000) | (T0 & 0x0000000F);
281
        old = env->CP0_index;
282
        env->CP0_index = val;
283
        rn = "Index";
284
        break;
285
    case 2:
286
        val = T0 & 0x03FFFFFFF;
287
        old = env->CP0_EntryLo0;
288
        env->CP0_EntryLo0 = val;
289
        rn = "EntryLo0";
290
        break;
291
    case 3:
292
        val = T0 & 0x03FFFFFFF;
293
        old = env->CP0_EntryLo1;
294
        env->CP0_EntryLo1 = val;
295
        rn = "EntryLo1";
296
        break;
297
    case 4:
298
        val = (env->CP0_Context & 0xFF000000) | (T0 & 0x00FFFFF0);
299
        old = env->CP0_Context;
300
        env->CP0_Context = val;
301
        rn = "Context";
302
        break;
303
    case 5:
304
        val = T0 & 0x01FFE000;
305
        old = env->CP0_PageMask;
306
        env->CP0_PageMask = val;
307
        rn = "PageMask";
308
        break;
309
    case 6:
310
        val = T0 & 0x0000000F;
311
        old = env->CP0_Wired;
312
        env->CP0_Wired = val;
313
        rn = "Wired";
314
        break;
315
    case 9:
316
        val = T0;
317
        old = cpu_mips_get_count(env);
318
        cpu_mips_store_count(env, val);
319
        rn = "Count";
320
        break;
321
    case 10:
322
        val = T0 & 0xFFFFF0FF;
323
        old = env->CP0_EntryHi;
324
        env->CP0_EntryHi = val;
325
        rn = "EntryHi";
326
        break;
327
    case 11:
328
        val = T0;
329
        old = env->CP0_Compare;
330
        cpu_mips_store_compare(env, val);
331
        rn = "Compare";
332
        break;
333
    case 12:
334
        val = T0 & 0xFA78FF01;
335
        if (T0 & (1 << CP0St_UM))
336
            env->hflags |= MIPS_HFLAG_UM;
337
        else
338
            env->hflags &= ~MIPS_HFLAG_UM;
339
        if (T0 & (1 << CP0St_ERL))
340
            env->hflags |= MIPS_HFLAG_ERL;
341
        else
342
            env->hflags &= ~MIPS_HFLAG_ERL;
343
        if (T0 & (1 << CP0St_EXL))
344
            env->hflags |= MIPS_HFLAG_EXL;
345
        else
346
            env->hflags &= ~MIPS_HFLAG_EXL;
347
        old = env->CP0_Status;
348
        env->CP0_Status = val;
349
        /* If we unmasked an asserted IRQ, raise it */
1464 by bellard
soft irq are just irqs (Ralf Baechle)
350
        mask = 0x0000FF00;
1458 by bellard
MIPS target (Jocelyn Mayer)
351
        if (loglevel & CPU_LOG_TB_IN_ASM) {
352
            fprintf(logfile, "Status %08x => %08x Cause %08x (%08x %08x %08x)\n",
353
                    old, val, env->CP0_Cause, old & mask, val & mask,
354
                    env->CP0_Cause & mask);
355
        }
356
#if 1
357
        if ((val & (1 << CP0St_IE)) && !(old & (1 << CP0St_IE)) &&
358
            !(env->hflags & MIPS_HFLAG_EXL) &&
359
            !(env->hflags & MIPS_HFLAG_ERL) &&
360
            !(env->hflags & MIPS_HFLAG_DM) && 
1472 by bellard
use mask in C0_status (Ralf Baechle)
361
            (env->CP0_Status & env->CP0_Cause & mask)) {
1458 by bellard
MIPS target (Jocelyn Mayer)
362
            if (logfile)
363
                fprintf(logfile, "Raise pending IRQs\n");
364
            env->interrupt_request |= CPU_INTERRUPT_HARD;
365
            do_raise_exception(EXCP_EXT_INTERRUPT);
366
        } else if (!(val & 0x00000001) && (old & 0x00000001)) {
367
            env->interrupt_request &= ~CPU_INTERRUPT_HARD;
368
        }
369
#endif
370
        rn = "Status";
371
        break;
372
    case 13:
373
        val = (env->CP0_Cause & 0xB000F87C) | (T0 & 0x000C00300);
374
        old = env->CP0_Cause;
375
        env->CP0_Cause = val;
376
#if 0
1499 by bellard
correct split between helper.c and op_helper.c - cosmetics
377
        {
378
            int i;
379
            /* Check if we ever asserted a software IRQ */
380
            for (i = 0; i < 2; i++) {
381
                mask = 0x100 << i;
382
                if ((val & mask) & !(old & mask))
383
                    mips_set_irq(i);
384
            }
1458 by bellard
MIPS target (Jocelyn Mayer)
385
        }
386
#endif
387
        rn = "Cause";
388
        break;
389
    case 14:
390
        val = T0;
391
        old = env->CP0_EPC;
392
        env->CP0_EPC = val;
393
        rn = "EPC";
394
        break;
395
    case 16:
396
        switch (sel) {
397
        case 0:
398
#if defined(MIPS_USES_R4K_TLB)
399
            val = (env->CP0_Config0 & 0x8017FF80) | (T0 & 0x7E000001);
400
#else
401
            val = (env->CP0_Config0 & 0xFE17FF80) | (T0 & 0x00000001);
402
#endif
403
            old = env->CP0_Config0;
404
            env->CP0_Config0 = val;
405
            rn = "Config0";
406
            break;
407
        default:
408
            val = -1;
409
            old = -1;
410
            rn = "bad config selector";
411
            break;
412
        }
413
        break;
414
    case 18:
415
        val = T0;
416
        old = env->CP0_WatchLo;
417
        env->CP0_WatchLo = val;
418
        rn = "WatchLo";
419
        break;
420
    case 19:
421
        val = T0 & 0x40FF0FF8;
422
        old = env->CP0_WatchHi;
423
        env->CP0_WatchHi = val;
424
        rn = "WatchHi";
425
        break;
426
    case 23:
427
        val = (env->CP0_Debug & 0x8C03FC1F) | (T0 & 0x13300120);
428
        if (T0 & (1 << CP0DB_DM))
429
            env->hflags |= MIPS_HFLAG_DM;
430
        else
431
            env->hflags &= ~MIPS_HFLAG_DM;
432
        old = env->CP0_Debug;
433
        env->CP0_Debug = val;
434
        rn = "Debug";
435
        break;
436
    case 24:
437
        val = T0;
438
        old = env->CP0_DEPC;
439
        env->CP0_DEPC = val;
440
        rn = "DEPC";
441
        break;
442
    case 28:
443
        switch (sel) {
444
        case 0:
445
            val = T0 & 0xFFFFFCF6;
446
            old = env->CP0_TagLo;
447
            env->CP0_TagLo = val;
448
            rn = "TagLo";
449
            break;
450
        default:
451
            val = -1;
452
            old = -1;
453
            rn = "invalid sel";
454
            break;
455
        }
456
        break;
457
    case 30:
458
        val = T0;
459
        old = env->CP0_ErrorEPC;
460
        env->CP0_ErrorEPC = val;
461
        rn = "EPC";
462
        break;
463
    case 31:
464
        val = T0;
465
        old = env->CP0_DESAVE;
466
        env->CP0_DESAVE = val;
467
        rn = "DESAVE";
468
        break;
469
    default:
470
        val = -1;
471
        old = -1;
472
        rn = "unknown";
473
        break;
474
    }
475
 print:
476
#if defined MIPS_DEBUG_DISAS
477
    if (loglevel & CPU_LOG_TB_IN_ASM) {
478
        fprintf(logfile, "%08x mtc0 %s %08x => %08x (%d %d %08x)\n",
479
                env->PC, rn, T0, val, reg, sel, old);
480
    }
481
#endif
482
    return;
483
}
484
485
/* TLB management */
486
#if defined(MIPS_USES_R4K_TLB)
487
static void invalidate_tb (int idx)
488
{
489
    tlb_t *tlb;
490
    target_ulong addr, end;
491
492
    tlb = &env->tlb[idx];
493
    if (tlb->V[0]) {
494
        addr = tlb->PFN[0];
495
        end = addr + (tlb->end - tlb->VPN);
496
        tb_invalidate_page_range(addr, end);
497
    }
498
    if (tlb->V[1]) {
499
        addr = tlb->PFN[1];
500
        end = addr + (tlb->end - tlb->VPN);
501
        tb_invalidate_page_range(addr, end);
502
    }
503
}
504
505
static void fill_tb (int idx)
506
{
507
    tlb_t *tlb;
508
    int size;
509
510
    /* XXX: detect conflicting TLBs and raise a MCHECK exception when needed */
511
    tlb = &env->tlb[idx];
512
    tlb->VPN = env->CP0_EntryHi & 0xFFFFE000;
513
    tlb->ASID = env->CP0_EntryHi & 0x000000FF;
514
    size = env->CP0_PageMask >> 13;
515
    size = 4 * (size + 1);
516
    tlb->end = tlb->VPN + (1 << (8 + size));
517
    tlb->G = env->CP0_EntryLo0 & env->CP0_EntryLo1 & 1;
518
    tlb->V[0] = env->CP0_EntryLo0 & 2;
519
    tlb->D[0] = env->CP0_EntryLo0 & 4;
520
    tlb->C[0] = (env->CP0_EntryLo0 >> 3) & 0x7;
521
    tlb->PFN[0] = (env->CP0_EntryLo0 >> 6) << 12;
522
    tlb->V[1] = env->CP0_EntryLo1 & 2;
523
    tlb->D[1] = env->CP0_EntryLo1 & 4;
524
    tlb->C[1] = (env->CP0_EntryLo1 >> 3) & 0x7;
525
    tlb->PFN[1] = (env->CP0_EntryLo1 >> 6) << 12;
526
}
527
528
void do_tlbwi (void)
529
{
1473 by bellard
use MIPS_TLB_NB constant (Ralf Baechle)
530
    /* Wildly undefined effects for CP0_index containing a too high value and
531
       MIPS_TLB_NB not being a power of two.  But so does real silicon.  */
532
    invalidate_tb(env->CP0_index & (MIPS_TLB_NB - 1));
533
    fill_tb(env->CP0_index & (MIPS_TLB_NB - 1));
1458 by bellard
MIPS target (Jocelyn Mayer)
534
}
535
536
void do_tlbwr (void)
537
{
538
    int r = cpu_mips_get_random(env);
539
540
    invalidate_tb(r);
541
    fill_tb(r);
542
}
543
544
void do_tlbp (void)
545
{
546
    tlb_t *tlb;
547
    target_ulong tag;
548
    uint8_t ASID;
549
    int i;
550
551
    tag = (env->CP0_EntryHi & 0xFFFFE000);
552
    ASID = env->CP0_EntryHi & 0x000000FF;
1473 by bellard
use MIPS_TLB_NB constant (Ralf Baechle)
553
        for (i = 0; i < MIPS_TLB_NB; i++) {
1458 by bellard
MIPS target (Jocelyn Mayer)
554
        tlb = &env->tlb[i];
555
        /* Check ASID, virtual page number & size */
556
        if ((tlb->G == 1 || tlb->ASID == ASID) && tlb->VPN == tag) {
557
            /* TLB match */
558
            env->CP0_index = i;
559
            break;
560
        }
561
    }
1473 by bellard
use MIPS_TLB_NB constant (Ralf Baechle)
562
    if (i == MIPS_TLB_NB) {
1458 by bellard
MIPS target (Jocelyn Mayer)
563
        env->CP0_index |= 0x80000000;
564
    }
565
}
566
567
void do_tlbr (void)
568
{
569
    tlb_t *tlb;
570
    int size;
571
1473 by bellard
use MIPS_TLB_NB constant (Ralf Baechle)
572
    tlb = &env->tlb[env->CP0_index & (MIPS_TLB_NB - 1)];
1458 by bellard
MIPS target (Jocelyn Mayer)
573
    env->CP0_EntryHi = tlb->VPN | tlb->ASID;
574
    size = (tlb->end - tlb->VPN) >> 12;
575
    env->CP0_PageMask = (size - 1) << 13;
576
    env->CP0_EntryLo0 = tlb->V[0] | tlb->D[0] | (tlb->C[0] << 3) |
577
        (tlb->PFN[0] >> 6);
578
    env->CP0_EntryLo1 = tlb->V[1] | tlb->D[1] | (tlb->C[1] << 3) |
579
        (tlb->PFN[1] >> 6);
580
}
581
#endif
582
583
void op_dump_ldst (const unsigned char *func)
584
{
585
    if (loglevel)
586
        fprintf(logfile, "%s => %08x %08x\n", __func__, T0, T1);
587
}
588
589
void dump_sc (void)
590
{
591
    if (loglevel) {
592
        fprintf(logfile, "%s %08x at %08x (%08x)\n", __func__,
593
                T1, T0, env->CP0_LLAddr);
594
    }
595
}
596
597
void debug_eret (void)
598
{
599
    if (loglevel) {
600
        fprintf(logfile, "ERET: pc %08x EPC %08x ErrorEPC %08x (%d)\n",
601
                env->PC, env->CP0_EPC, env->CP0_ErrorEPC,
602
                env->hflags & MIPS_HFLAG_ERL ? 1 : 0);
603
    }
604
}
605
606
void do_pmon (int function)
607
{
608
    function /= 2;
609
    switch (function) {
610
    case 2: /* TODO: char inbyte(int waitflag); */
611
        if (env->gpr[4] == 0)
612
            env->gpr[2] = -1;
613
        /* Fall through */
614
    case 11: /* TODO: char inbyte (void); */
615
        env->gpr[2] = -1;
616
        break;
617
    case 3:
618
    case 12:
619
        printf("%c", env->gpr[4] & 0xFF);
620
        break;
621
    case 17:
622
        break;
623
    case 158:
624
        {
625
            unsigned char *fmt = (void *)env->gpr[4];
626
            printf("%s", fmt);
627
        }
628
        break;
629
    }
630
}
1499 by bellard
correct split between helper.c and op_helper.c - cosmetics
631
632
#if !defined(CONFIG_USER_ONLY) 
633
634
#define MMUSUFFIX _mmu
635
#define GETPC() (__builtin_return_address(0))
636
637
#define SHIFT 0
638
#include "softmmu_template.h"
639
640
#define SHIFT 1
641
#include "softmmu_template.h"
642
643
#define SHIFT 2
644
#include "softmmu_template.h"
645
646
#define SHIFT 3
647
#include "softmmu_template.h"
648
649
void tlb_fill (target_ulong addr, int is_write, int is_user, void *retaddr)
650
{
651
    TranslationBlock *tb;
652
    CPUState *saved_env;
653
    unsigned long pc;
654
    int ret;
655
656
    /* XXX: hack to restore env in all cases, even if not called from
657
       generated code */
658
    saved_env = env;
659
    env = cpu_single_env;
660
    ret = cpu_mips_handle_mmu_fault(env, addr, is_write, is_user, 1);
661
    if (ret) {
662
        if (retaddr) {
663
            /* now we have a real cpu fault */
664
            pc = (unsigned long)retaddr;
665
            tb = tb_find_pc(pc);
666
            if (tb) {
667
                /* the PC is inside the translated code. It means that we have
668
                   a virtual CPU fault */
669
                cpu_restore_state(tb, env, pc, NULL);
670
            }
671
        }
672
        do_raise_exception_err(env->exception_index, env->error_code);
673
    }
674
    env = saved_env;
675
}
676
677
#endif