~vcs-imports/qemu/git

« back to all changes in this revision

Viewing changes to target-sh4/helper.c

  • Committer: ths
  • Date: 2007-05-05 19:24:38 UTC
  • Revision ID: git-v1:608e8ce280cee231b3de0e7922060efd23447a54
Linker scripts for MIPS hosts.


git-svn-id: svn://svn.savannah.nongnu.org/qemu/trunk@2772 c046a42c-6fe2-441c-8c8c-71466251a162

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
1
/*
2
2
 *  SH4 emulation
3
 
 *
 
3
 * 
4
4
 *  Copyright (c) 2005 Samuel Tardieu
5
5
 *
6
6
 * This library is free software; you can redistribute it and/or
27
27
 
28
28
#include "cpu.h"
29
29
#include "exec-all.h"
30
 
#include "hw/sh_intc.h"
31
30
 
32
31
#if defined(CONFIG_USER_ONLY)
33
32
 
37
36
}
38
37
 
39
38
int cpu_sh4_handle_mmu_fault(CPUState * env, target_ulong address, int rw,
40
 
                             int mmu_idx, int is_softmmu)
 
39
                             int is_user, int is_softmmu)
41
40
{
42
41
    env->tea = address;
43
 
    env->exception_index = 0;
44
42
    switch (rw) {
45
43
    case 0:
46
 
        env->tea = address;
47
44
        env->exception_index = 0x0a0;
48
45
        break;
49
46
    case 1:
50
 
        env->tea = address;
51
47
        env->exception_index = 0x0c0;
52
48
        break;
 
49
    case 2:
 
50
        env->exception_index = 0x0a0;
 
51
        break;
53
52
    }
54
53
    return 1;
55
54
}
75
74
 
76
75
void do_interrupt(CPUState * env)
77
76
{
78
 
    int do_irq = env->interrupt_request & CPU_INTERRUPT_HARD;
79
 
    int do_exp, irq_vector = env->exception_index;
80
 
 
81
 
    /* prioritize exceptions over interrupts */
82
 
 
83
 
    do_exp = env->exception_index != -1;
84
 
    do_irq = do_irq && (env->exception_index == -1);
85
 
 
86
 
    if (env->sr & SR_BL) {
87
 
        if (do_exp && env->exception_index != 0x1e0) {
88
 
            env->exception_index = 0x000; /* masked exception -> reset */
89
 
        }
90
 
        if (do_irq && !env->intr_at_halt) {
91
 
            return; /* masked */
92
 
        }
93
 
        env->intr_at_halt = 0;
94
 
    }
95
 
 
96
 
    if (do_irq) {
97
 
        irq_vector = sh_intc_get_pending_vector(env->intc_handle,
98
 
                                                (env->sr >> 4) & 0xf);
99
 
        if (irq_vector == -1) {
100
 
            return; /* masked */
101
 
        }
102
 
    }
103
 
 
104
77
    if (loglevel & CPU_LOG_INT) {
105
78
        const char *expname;
106
79
        switch (env->exception_index) {
144
117
            expname = "trapa";
145
118
            break;
146
119
        default:
147
 
            expname = do_irq ? "interrupt" : "???";
148
 
            break;
 
120
            expname = "???";
 
121
            break;
149
122
        }
150
123
        fprintf(logfile, "exception 0x%03x [%s] raised\n",
151
 
                irq_vector, expname);
 
124
                env->exception_index, expname);
152
125
        cpu_dump_state(env, logfile, fprintf, 0);
153
126
    }
154
127
 
155
128
    env->ssr = env->sr;
156
 
    env->spc = env->pc;
 
129
    env->spc = env->spc;
157
130
    env->sgr = env->gregs[15];
158
131
    env->sr |= SR_BL | SR_MD | SR_RB;
159
132
 
160
 
    if (env->flags & (DELAY_SLOT | DELAY_SLOT_CONDITIONAL)) {
161
 
        /* Branch instruction should be executed again before delay slot. */
162
 
        env->spc -= 2;
163
 
        /* Clear flags for exception/interrupt routine. */
164
 
        env->flags &= ~(DELAY_SLOT | DELAY_SLOT_CONDITIONAL | DELAY_SLOT_TRUE);
165
 
    }
166
 
    if (env->flags & DELAY_SLOT_CLEARME)
167
 
        env->flags = 0;
168
 
 
169
 
    if (do_exp) {
170
 
        env->expevt = env->exception_index;
171
 
        switch (env->exception_index) {
172
 
        case 0x000:
173
 
        case 0x020:
174
 
        case 0x140:
175
 
            env->sr &= ~SR_FD;
176
 
            env->sr |= 0xf << 4; /* IMASK */
177
 
            env->pc = 0xa0000000;
178
 
            break;
179
 
        case 0x040:
180
 
        case 0x060:
181
 
            env->pc = env->vbr + 0x400;
182
 
            break;
183
 
        case 0x160:
184
 
            env->spc += 2; /* special case for TRAPA */
185
 
            /* fall through */
186
 
        default:
187
 
            env->pc = env->vbr + 0x100;
188
 
            break;
189
 
        }
190
 
        return;
191
 
    }
192
 
 
193
 
    if (do_irq) {
194
 
        env->intevt = irq_vector;
195
 
        env->pc = env->vbr + 0x600;
196
 
        return;
 
133
    env->expevt = env->exception_index & 0x7ff;
 
134
    switch (env->exception_index) {
 
135
    case 0x040:
 
136
    case 0x060:
 
137
    case 0x080:
 
138
        env->pc = env->vbr + 0x400;
 
139
        break;
 
140
    case 0x140:
 
141
        env->pc = 0xa0000000;
 
142
        break;
 
143
    default:
 
144
        env->pc = env->vbr + 0x100;
 
145
        break;
197
146
    }
198
147
}
199
148
 
203
152
 
204
153
    switch (itlbnb) {
205
154
    case 0:
206
 
        and_mask = 0x1f;
 
155
        and_mask = 0x7f;
207
156
        break;
208
157
    case 1:
209
158
        and_mask = 0xe7;
218
167
        break;
219
168
    }
220
169
 
221
 
    env->mmucr &= (and_mask << 24) | 0x00ffffff;
 
170
    env->mmucr &= (and_mask << 24);
222
171
    env->mmucr |= (or_mask << 24);
223
172
}
224
173
 
226
175
{
227
176
    if ((env->mmucr & 0xe0000000) == 0xe0000000)
228
177
        return 0;
229
 
    if ((env->mmucr & 0x98000000) == 0x18000000)
 
178
    if ((env->mmucr & 0x98000000) == 0x08000000)
230
179
        return 1;
231
180
    if ((env->mmucr & 0x54000000) == 0x04000000)
232
181
        return 2;
251
200
    for (i = 0; i < nbtlb; i++) {
252
201
        if (!entries[i].v)
253
202
            continue;           /* Invalid entry */
254
 
        if (use_asid && entries[i].asid != asid)
 
203
        if (use_asid && entries[i].asid != asid && !entries[i].sh)
255
204
            continue;           /* Bad ASID */
256
205
#if 0
257
206
        switch (entries[i].sz) {
274
223
        start = (entries[i].vpn << 10) & ~(entries[i].size - 1);
275
224
        end = start + entries[i].size - 1;
276
225
        if (address >= start && address <= end) {       /* Match */
277
 
            if (match != MMU_DTLB_MISS)
 
226
            if (match != -1)
278
227
                return MMU_DTLB_MULTIPLE;       /* Multiple match */
279
228
            match = i;
280
229
        }
282
231
    return match;
283
232
}
284
233
 
285
 
static int same_tlb_entry_exists(const tlb_t * haystack, uint8_t nbtlb,
286
 
                                 const tlb_t * needle)
287
 
{
288
 
    int i;
289
 
    for (i = 0; i < nbtlb; i++)
290
 
        if (!memcmp(&haystack[i], needle, sizeof(tlb_t)))
291
 
            return 1;
292
 
    return 0;
293
 
}
294
 
 
295
 
static void increment_urc(CPUState * env)
296
 
{
297
 
    uint8_t urb, urc;
298
 
 
299
 
    /* Increment URC */
300
 
    urb = ((env->mmucr) >> 18) & 0x3f;
301
 
    urc = ((env->mmucr) >> 10) & 0x3f;
302
 
    urc++;
303
 
    if (urc == urb || urc == UTLB_SIZE - 1)
304
 
        urc = 0;
305
 
    env->mmucr = (env->mmucr & 0xffff03ff) | (urc << 10);
306
 
}
307
 
 
308
234
/* Find itlb entry - update itlb from utlb if necessary and asked for
309
235
   Return entry, MMU_ITLB_MISS, MMU_ITLB_MULTIPLE or MMU_DTLB_MULTIPLE
310
236
   Update the itlb from utlb if update is not 0
320
246
    else if (e == MMU_DTLB_MISS && update) {
321
247
        e = find_tlb_entry(env, address, env->utlb, UTLB_SIZE, use_asid);
322
248
        if (e >= 0) {
323
 
            tlb_t * ientry;
324
249
            n = itlb_replacement(env);
325
 
            ientry = &env->itlb[n];
326
 
            if (ientry->v) {
327
 
                if (!same_tlb_entry_exists(env->utlb, UTLB_SIZE, ientry))
328
 
                    tlb_flush_page(env, ientry->vpn << 10);
329
 
            }
330
 
            *ientry = env->utlb[e];
 
250
            env->itlb[n] = env->utlb[e];
331
251
            e = n;
332
 
        } else if (e == MMU_DTLB_MISS)
333
 
            e = MMU_ITLB_MISS;
334
 
    } else if (e == MMU_DTLB_MISS)
335
 
        e = MMU_ITLB_MISS;
 
252
        }
 
253
    }
336
254
    if (e >= 0)
337
255
        update_itlb_use(env, e);
338
256
    return e;
342
260
   Return entry, MMU_DTLB_MISS, MMU_DTLB_MULTIPLE */
343
261
int find_utlb_entry(CPUState * env, target_ulong address, int use_asid)
344
262
{
345
 
    /* per utlb access */
346
 
    increment_urc(env);
 
263
    uint8_t urb, urc;
 
264
 
 
265
    /* Increment URC */
 
266
    urb = ((env->mmucr) >> 18) & 0x3f;
 
267
    urc = ((env->mmucr) >> 10) & 0x3f;
 
268
    urc++;
 
269
    if (urc == urb || urc == UTLB_SIZE - 1)
 
270
        urc = 0;
 
271
    env->mmucr = (env->mmucr & 0xffff03ff) | (urc << 10);
347
272
 
348
273
    /* Return entry */
349
274
    return find_tlb_entry(env, address, env->utlb, UTLB_SIZE, use_asid);
362
287
    int use_asid, is_code, n;
363
288
    tlb_t *matching = NULL;
364
289
 
365
 
    use_asid = (env->mmucr & MMUCR_SV) == 0 || (env->sr & SR_MD) == 0;
 
290
    use_asid = (env->mmucr & MMUCR_SV) == 0 && (env->sr & SR_MD) == 0;
366
291
    is_code = env->pc == address;       /* Hack */
367
292
 
368
293
    /* Use a hack to find if this is an instruction or data access */
429
354
            return (rw & PAGE_WRITE) ? MMU_DTLB_MISS_WRITE :
430
355
                MMU_DTLB_MISS_READ;
431
356
        }
432
 
        if (address >= 0x80000000 && address < 0xc0000000) {
433
 
            /* Mask upper 3 bits for P1 and P2 areas */
434
 
            *physical = address & 0x1fffffff;
435
 
        } else if (address >= 0xfc000000) {
436
 
            /*
437
 
             * Mask upper 3 bits for control registers in P4 area,
438
 
             * to unify access to control registers via P0-P3 area.
439
 
             * The addresses for cache store queue, TLB address array
440
 
             * are not masked.
441
 
             */
442
 
        *physical = address & 0x1fffffff;
443
 
        } else {
444
 
            /* access to cache store queue, or TLB address array. */
445
 
            *physical = address;
446
 
        }
 
357
        /* Mask upper 3 bits */
 
358
        *physical = address & 0x1FFFFFFF;
447
359
        *prot = PAGE_READ | PAGE_WRITE;
448
360
        return MMU_OK;
449
361
    }
460
372
}
461
373
 
462
374
int cpu_sh4_handle_mmu_fault(CPUState * env, target_ulong address, int rw,
463
 
                             int mmu_idx, int is_softmmu)
 
375
                             int is_user, int is_softmmu)
464
376
{
465
377
    target_ulong physical, page_offset, page_size;
466
378
    int prot, ret, access_type;
467
379
 
468
 
    switch (rw) {
469
 
    case 0:
470
 
        rw = PAGE_READ;
471
 
        break;
472
 
    case 1:
473
 
        rw = PAGE_WRITE;
474
 
        break;
475
 
    case 2: /* READ_ACCESS_TYPE == 2 defined in softmmu_template.h */
476
 
        rw = PAGE_READ;
477
 
        break;
478
 
    default:
479
 
        /* fatal error */
480
 
        assert(0);
481
 
    }
482
 
 
483
380
    /* XXXXX */
484
381
#if 0
485
 
    fprintf(stderr, "%s pc %08x ad %08x rw %d mmu_idx %d smmu %d\n",
486
 
            __func__, env->pc, address, rw, mmu_idx, is_softmmu);
 
382
    fprintf(stderr, "%s pc %08x ad %08x rw %d is_user %d smmu %d\n",
 
383
            __func__, env->pc, address, rw, is_user, is_softmmu);
487
384
#endif
488
385
 
489
386
    access_type = ACCESS_INT;
529
426
    address = (address & TARGET_PAGE_MASK) + page_offset;
530
427
    physical = (physical & TARGET_PAGE_MASK) + page_offset;
531
428
 
532
 
    return tlb_set_page(env, address, physical, prot, mmu_idx, is_softmmu);
 
429
    return tlb_set_page(env, address, physical, prot, is_user, is_softmmu);
533
430
}
534
431
 
535
432
target_phys_addr_t cpu_get_phys_page_debug(CPUState * env, target_ulong addr)
541
438
    return physical;
542
439
}
543
440
 
544
 
void cpu_load_tlb(CPUState * env)
545
 
{
546
 
    int n = cpu_mmucr_urc(env->mmucr);
547
 
    tlb_t * entry = &env->utlb[n];
548
 
 
549
 
    if (entry->v) {
550
 
        /* Overwriting valid entry in utlb. */
551
 
        target_ulong address = entry->vpn << 10;
552
 
        if (!same_tlb_entry_exists(env->itlb, ITLB_SIZE, entry)) {
553
 
            tlb_flush_page(env, address);
554
 
        }
555
 
    }
556
 
 
557
 
    /* per utlb access cannot implemented. */
558
 
    increment_urc(env);
559
 
 
560
 
    /* Take values into cpu status from registers. */
561
 
    entry->asid = (uint8_t)cpu_pteh_asid(env->pteh);
562
 
    entry->vpn  = cpu_pteh_vpn(env->pteh);
563
 
    entry->v    = (uint8_t)cpu_ptel_v(env->ptel);
564
 
    entry->ppn  = cpu_ptel_ppn(env->ptel);
565
 
    entry->sz   = (uint8_t)cpu_ptel_sz(env->ptel);
566
 
    switch (entry->sz) {
567
 
    case 0: /* 00 */
568
 
        entry->size = 1024; /* 1K */
569
 
        break;
570
 
    case 1: /* 01 */
571
 
        entry->size = 1024 * 4; /* 4K */
572
 
        break;
573
 
    case 2: /* 10 */
574
 
        entry->size = 1024 * 64; /* 64K */
575
 
        break;
576
 
    case 3: /* 11 */
577
 
        entry->size = 1024 * 1024; /* 1M */
578
 
        break;
579
 
    default:
580
 
        assert(0);
581
 
        break;
582
 
    }
583
 
    entry->sh   = (uint8_t)cpu_ptel_sh(env->ptel);
584
 
    entry->c    = (uint8_t)cpu_ptel_c(env->ptel);
585
 
    entry->pr   = (uint8_t)cpu_ptel_pr(env->ptel);
586
 
    entry->d    = (uint8_t)cpu_ptel_d(env->ptel);
587
 
    entry->wt   = (uint8_t)cpu_ptel_wt(env->ptel);
588
 
    entry->sa   = (uint8_t)cpu_ptea_sa(env->ptea);
589
 
    entry->tc   = (uint8_t)cpu_ptea_tc(env->ptea);
590
 
}
591
 
 
592
 
void cpu_sh4_write_mmaped_utlb_addr(CPUSH4State *s, target_phys_addr_t addr,
593
 
                                    uint32_t mem_value)
594
 
{
595
 
    int associate = addr & 0x0000080;
596
 
    uint32_t vpn = (mem_value & 0xfffffc00) >> 10;
597
 
    uint8_t d = (uint8_t)((mem_value & 0x00000200) >> 9);
598
 
    uint8_t v = (uint8_t)((mem_value & 0x00000100) >> 8);
599
 
    uint8_t asid = (uint8_t)(mem_value & 0x000000ff);
600
 
 
601
 
    if (associate) {
602
 
        int i;
603
 
        tlb_t * utlb_match_entry = NULL;
604
 
        int needs_tlb_flush = 0;
605
 
 
606
 
        /* search UTLB */
607
 
        for (i = 0; i < UTLB_SIZE; i++) {
608
 
            tlb_t * entry = &s->utlb[i];
609
 
            if (!entry->v)
610
 
                continue;
611
 
 
612
 
            if (entry->vpn == vpn && entry->asid == asid) {
613
 
                if (utlb_match_entry) {
614
 
                    /* Multiple TLB Exception */
615
 
                    s->exception_index = 0x140;
616
 
                    s->tea = addr;
617
 
                    break;
618
 
                }
619
 
                if (entry->v && !v)
620
 
                    needs_tlb_flush = 1;
621
 
                entry->v = v;
622
 
                entry->d = d;
623
 
                utlb_match_entry = entry;
624
 
            }
625
 
            increment_urc(s); /* per utlb access */
626
 
        }
627
 
 
628
 
        /* search ITLB */
629
 
        for (i = 0; i < ITLB_SIZE; i++) {
630
 
            tlb_t * entry = &s->itlb[i];
631
 
            if (entry->vpn == vpn && entry->asid == asid) {
632
 
                if (entry->v && !v)
633
 
                    needs_tlb_flush = 1;
634
 
                if (utlb_match_entry)
635
 
                    *entry = *utlb_match_entry;
636
 
                else
637
 
                    entry->v = v;
638
 
                break;
639
 
            }
640
 
        }
641
 
 
642
 
        if (needs_tlb_flush)
643
 
            tlb_flush_page(s, vpn << 10);
644
 
        
645
 
    } else {
646
 
        int index = (addr & 0x00003f00) >> 8;
647
 
        tlb_t * entry = &s->utlb[index];
648
 
        if (entry->v) {
649
 
            /* Overwriting valid entry in utlb. */
650
 
            target_ulong address = entry->vpn << 10;
651
 
            if (!same_tlb_entry_exists(s->itlb, ITLB_SIZE, entry)) {
652
 
                tlb_flush_page(s, address);
653
 
            }
654
 
        }
655
 
        entry->asid = asid;
656
 
        entry->vpn = vpn;
657
 
        entry->d = d;
658
 
        entry->v = v;
659
 
        increment_urc(s);
660
 
    }
661
 
}
662
 
 
663
441
#endif