Statistics
| Branch: | Revision:

root / target-mips / helper.c @ 6af0bf9c

History | View | Annotate | Download (13.2 kB)

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

    
21
#include "exec.h"
22

    
23
/* MIPS32 4K MMU emulation */
24
#if MIPS_USES_4K_TLB
25
static int map_address (CPUState *env, target_ulong *physical, int *prot,
26
                        target_ulong address, int rw, int access_type)
27
{
28
    tlb_t *tlb;
29
    target_ulong tag;
30
    uint8_t ASID;
31
    int i, n;
32
    int ret;
33

    
34
    ret = -2;
35
    tag = (address & 0xFFFFE000);
36
    ASID = env->CP0_EntryHi & 0x000000FF;
37
    for (i = 0; i < 16; i++) {
38
        tlb = &env->tlb[i];
39
        /* Check ASID, virtual page number & size */
40
        if ((tlb->G == 1 || tlb->ASID == ASID) &&
41
            tlb->VPN == tag && address < tlb->end) {
42
            /* TLB match */
43
            n = (address >> 12) & 1;
44
            /* Check access rights */
45
            if ((tlb->V[n] & 2) && (rw == 0 || (tlb->D[n] & 4))) {
46
                *physical = tlb->PFN[n] | (address & 0xFFF);
47
                *prot = PROT_READ;
48
                if (tlb->D[n])
49
                    *prot |= PROT_WRITE;
50
                return 0;
51
            } else if (!(tlb->V[n] & 2)) {
52
                return -3;
53
            } else {
54
                return -4;
55
            }
56
        }
57
    }
58

    
59
    return ret;
60
}
61
#endif
62

    
63
int get_physical_address (CPUState *env, target_ulong *physical, int *prot,
64
                          target_ulong address, int rw, int access_type)
65
{
66
    int user_mode;
67
    int ret;
68

    
69
    /* User mode can only access useg */
70
    user_mode = ((env->hflags & MIPS_HFLAG_MODE) == MIPS_HFLAG_UM) ? 1 : 0;
71
#if 0
72
    if (logfile) {
73
        fprintf(logfile, "user mode %d h %08x\n",
74
                user_mode, env->hflags);
75
    }
76
#endif
77
    if (user_mode && address > 0x7FFFFFFFUL)
78
        return -1;
79
    ret = 0;
80
    if (address < 0x80000000UL) {
81
        if (user_mode || !(env->hflags & MIPS_HFLAG_ERL)) {
82
#if MIPS_USES_4K_TLB
83
            ret = map_address(env, physical, prot, address, rw);
84
#else
85
            *physical = address + 0x40000000UL;
86
            *prot = PAGE_READ | PAGE_WRITE;
87
#endif
88
        } else {
89
            *physical = address;
90
            *prot = PAGE_READ | PAGE_WRITE;
91
        }
92
    } else if (address < 0xA0000000UL) {
93
        /* kseg0 */
94
        /* XXX: check supervisor mode */
95
        *physical = address - 0x80000000UL;
96
        *prot = PAGE_READ | PAGE_WRITE;
97
    } else if (address < 0xC0000000UL) {
98
        /* kseg1 */
99
        /* XXX: check supervisor mode */
100
        *physical = address - 0xA0000000UL;
101
        *prot = PAGE_READ | PAGE_WRITE;
102
    } else if (address < 0xE0000000UL) {
103
        /* kseg2 */
104
#if MIPS_USES_4K_TLB
105
        ret = map_address(env, physical, prot, address, rw);
106
#else
107
        *physical = address;
108
        *prot = PAGE_READ | PAGE_WRITE;
109
#endif
110
    } else {
111
        /* kseg3 */
112
        /* XXX: check supervisor mode */
113
        /* XXX: debug segment is not emulated */
114
#if MIPS_USES_4K_TLB
115
        ret = map_address(env, physical, prot, address, rw);
116
#else
117
        *physical = address;
118
        *prot = PAGE_READ | PAGE_WRITE;
119
#endif
120
    }
121
#if 0
122
    if (logfile) {
123
        fprintf(logfile, "%08x %d %d => %08x %d (%d)\n", address, rw,
124
                access_type, *physical, *prot, ret);
125
    }
126
#endif
127

    
128
    return ret;
129
}
130

    
131
#if defined(CONFIG_USER_ONLY) 
132
target_ulong cpu_get_phys_page_debug(CPUState *env, target_ulong addr)
133
{
134
    return addr;
135
}
136
#else
137
target_ulong cpu_get_phys_page_debug(CPUState *env, target_ulong addr)
138
{
139
    target_ulong phys_addr;
140
    int prot;
141

    
142
    if (get_physical_address(env, &phys_addr, &prot, addr, 0, ACCESS_INT) != 0)
143
        return -1;
144
    return phys_addr;
145
}
146
#endif
147

    
148
#if !defined(CONFIG_USER_ONLY) 
149

    
150
#define MMUSUFFIX _mmu
151
#define GETPC() (__builtin_return_address(0))
152

    
153
#define SHIFT 0
154
#include "softmmu_template.h"
155

    
156
#define SHIFT 1
157
#include "softmmu_template.h"
158

    
159
#define SHIFT 2
160
#include "softmmu_template.h"
161

    
162
#define SHIFT 3
163
#include "softmmu_template.h"
164

    
165
void tlb_fill (target_ulong addr, int is_write, int is_user, void *retaddr)
166
{
167
    TranslationBlock *tb;
168
    CPUState *saved_env;
169
    unsigned long pc;
170
    int ret;
171

    
172
    /* XXX: hack to restore env in all cases, even if not called from
173
       generated code */
174
    saved_env = env;
175
    env = cpu_single_env;
176
    ret = cpu_mips_handle_mmu_fault(env, addr, is_write, is_user, 1);
177
    if (ret) {
178
        if (retaddr) {
179
            /* now we have a real cpu fault */
180
            pc = (unsigned long)retaddr;
181
            tb = tb_find_pc(pc);
182
            if (tb) {
183
                /* the PC is inside the translated code. It means that we have
184
                   a virtual CPU fault */
185
                cpu_restore_state(tb, env, pc, NULL);
186
            }
187
        }
188
        do_raise_exception_err(env->exception_index, env->error_code);
189
    }
190
    env = saved_env;
191
}
192

    
193
void cpu_mips_init_mmu (CPUState *env)
194
{
195
}
196

    
197
#endif /* !defined(CONFIG_USER_ONLY) */
198

    
199
int cpu_mips_handle_mmu_fault (CPUState *env, target_ulong address, int rw,
200
                               int is_user, int is_softmmu)
201
{
202
    target_ulong physical;
203
    int prot;
204
    int exception = 0, error_code = 0;
205
    int access_type;
206
    int ret = 0;
207

    
208
    if (logfile) {
209
        cpu_dump_state(env, logfile, fprintf, 0);
210
        fprintf(logfile, "%s pc %08x ad %08x rw %d is_user %d smmu %d\n",
211
                __func__, env->PC, address, rw, is_user, is_softmmu);
212
    }
213
    /* data access */
214
    /* XXX: put correct access by using cpu_restore_state()
215
       correctly */
216
    access_type = ACCESS_INT;
217
    if (env->user_mode_only) {
218
        /* user mode only emulation */
219
        ret = -2;
220
        goto do_fault;
221
    }
222
    ret = get_physical_address(env, &physical, &prot,
223
                               address, rw, access_type);
224
    if (logfile) {
225
        fprintf(logfile, "%s address=%08x ret %d physical %08x prot %d\n",
226
                __func__, address, ret, physical, prot);
227
    }
228
    if (ret == 0) {
229
        ret = tlb_set_page(env, address & ~0xFFF, physical & ~0xFFF, prot,
230
                           is_user, is_softmmu);
231
    } else if (ret < 0) {
232
    do_fault:
233
        switch (ret) {
234
        default:
235
        case -1:
236
            /* Reference to kernel address from user mode or supervisor mode */
237
            /* Reference to supervisor address from user mode */
238
            if (rw)
239
                exception = EXCP_AdES;
240
            else
241
                exception = EXCP_AdEL;
242
            break;
243
        case -2:
244
            /* No TLB match for a mapped address */
245
            if (rw)
246
                exception = EXCP_TLBS;
247
            else
248
                exception = EXCP_TLBL;
249
            error_code = 1;
250
            break;
251
        case -3:
252
            /* TLB match with no valid bit */
253
            if (rw)
254
                exception = EXCP_TLBS;
255
            else
256
                exception = EXCP_TLBL;
257
            error_code = 0;
258
            break;
259
        case -4:
260
            /* TLB match but 'D' bit is cleared */
261
            exception = EXCP_LTLBL;
262
            break;
263
                
264
        }
265
        if (ret == -2) {
266
            exception = EXCP_AdEL;
267
        }
268
        /* Raise exception */
269
        env->CP0_BadVAddr = address;
270
        env->CP0_Context =
271
            (env->CP0_Context & 0x00000FFF) | (address & 0xFFFFF000);
272
        env->CP0_EntryHi =
273
            (env->CP0_EntryHi & 0x00000FFF) | (address & 0xFFFFF000);
274
        env->exception_index = exception;
275
        env->error_code = error_code;
276
        ret = 1;
277
    }
278

    
279
    return ret;
280
}
281

    
282
void do_interrupt (CPUState *env)
283
{
284
    target_ulong pc, offset;
285
    int cause = -1;
286

    
287
    if (logfile && env->exception_index != EXCP_EXT_INTERRUPT) {
288
        fprintf(logfile, "%s enter: PC %08x EPC %08x cause %d excp %d\n",
289
                __func__, env->PC, env->CP0_EPC, cause, env->exception_index);
290
    }
291
    if (env->exception_index == EXCP_EXT_INTERRUPT &&
292
        (env->hflags & MIPS_HFLAG_DM))
293
        env->exception_index = EXCP_DINT;
294
    offset = 0x180;
295
    switch (env->exception_index) {
296
    case EXCP_DSS:
297
        env->CP0_Debug |= 1 << CP0DB_DSS;
298
        /* Debug single step cannot be raised inside a delay slot and
299
         * resume will always occur on the next instruction
300
         * (but we assume the pc has always been updated during
301
         *  code translation).
302
         */
303
        env->CP0_DEPC = env->PC;
304
        goto enter_debug_mode;
305
    case EXCP_DINT:
306
        env->CP0_Debug |= 1 << CP0DB_DINT;
307
        goto set_DEPC;
308
    case EXCP_DIB:
309
        env->CP0_Debug |= 1 << CP0DB_DIB;
310
        goto set_DEPC;
311
    case EXCP_DBp:
312
        env->CP0_Debug |= 1 << CP0DB_DBp;
313
        goto set_DEPC;
314
    case EXCP_DDBS:
315
        env->CP0_Debug |= 1 << CP0DB_DDBS;
316
        goto set_DEPC;
317
    case EXCP_DDBL:
318
        env->CP0_Debug |= 1 << CP0DB_DDBL;
319
        goto set_DEPC;
320
    set_DEPC:
321
        if (env->hflags & MIPS_HFLAG_DS) {
322
            /* If the exception was raised from a delay slot,
323
             * come back to the jump
324
             */
325
            env->CP0_DEPC = env->PC - 4;
326
        } else {
327
            env->CP0_DEPC = env->PC;
328
        }
329
    enter_debug_mode:
330
        env->hflags |= MIPS_HFLAG_DM;
331
        /* EJTAG probe trap enable is not implemented... */
332
        pc = 0xBFC00480;
333
        break;
334
    case EXCP_RESET:
335
#if defined (MIPS_USES_R4K_TLB)
336
        env->CP0_random = MIPS_TLB_NB - 1;
337
#endif
338
        env->CP0_Wired = 0;
339
        env->CP0_Config0 = MIPS_CONFIG0;
340
#if defined (MIPS_CONFIG1)
341
        env->CP0_Config1 = MIPS_CONFIG1;
342
#endif
343
#if defined (MIPS_CONFIG2)
344
        env->CP0_Config2 = MIPS_CONFIG2;
345
#endif
346
#if defined (MIPS_CONFIG3)
347
        env->CP0_Config3 = MIPS_CONFIG3;
348
#endif
349
        env->CP0_WatchLo = 0;
350
        env->CP0_Status = (1 << CP0St_CU0) | (1 << CP0St_BEV);
351
        goto set_error_EPC;
352
    case EXCP_SRESET:
353
        env->CP0_Status = (1 << CP0St_CU0) | (1 << CP0St_BEV) |
354
            (1 << CP0St_SR);
355
        env->CP0_WatchLo = 0;
356
        goto set_error_EPC;
357
    case EXCP_NMI:
358
        env->CP0_Status = (1 << CP0St_CU0) | (1 << CP0St_BEV) |
359
            (1 << CP0St_NMI);
360
    set_error_EPC:
361
        env->hflags = MIPS_HFLAG_ERL;
362
        if (env->hflags & MIPS_HFLAG_DS) {
363
            /* If the exception was raised from a delay slot,
364
             * come back to the jump
365
             */
366
            env->CP0_ErrorEPC = env->PC - 4;
367
        } else {
368
            env->CP0_ErrorEPC = env->PC;
369
        }
370
        pc = 0xBFC00000;
371
        break;
372
    case EXCP_MCHECK:
373
        cause = 24;
374
        goto set_EPC;
375
    case EXCP_EXT_INTERRUPT:
376
        cause = 0;
377
        if (env->CP0_Cause & (1 << CP0Ca_IV))
378
            offset = 0x200;
379
        goto set_EPC;
380
    case EXCP_DWATCH:
381
        cause = 23;
382
        /* XXX: TODO: manage defered watch exceptions */
383
        goto set_EPC;
384
    case EXCP_AdEL:
385
    case EXCP_AdES:
386
        cause = 4;
387
        goto set_EPC;
388
    case EXCP_TLBL:
389
    case EXCP_TLBF:
390
        cause = 2;
391
        if (env->error_code == 1 && !(env->hflags & MIPS_HFLAG_EXL))
392
            offset = 0x000;
393
        goto set_EPC;
394
    case EXCP_IBE:
395
        cause = 6;
396
        goto set_EPC;
397
    case EXCP_DBE:
398
        cause = 7;
399
        goto set_EPC;
400
    case EXCP_SYSCALL:
401
        cause = 8;
402
        goto set_EPC;
403
    case EXCP_BREAK:
404
        cause = 9;
405
        goto set_EPC;
406
    case EXCP_RI:
407
        cause = 10;
408
        goto set_EPC;
409
    case EXCP_CpU:
410
        cause = 11;
411
        /* XXX: fill in the faulty unit number */
412
        goto set_EPC;
413
    case EXCP_OVERFLOW:
414
        cause = 12;
415
        goto set_EPC;
416
    case EXCP_TRAP:
417
        cause = 13;
418
        goto set_EPC;
419
    case EXCP_LTLBL:
420
        cause = 1;
421
        goto set_EPC;
422
    case EXCP_TLBS:
423
        cause = 3;
424
    set_EPC:
425
        if (env->CP0_Status & (1 << CP0St_BEV)) {
426
            pc = 0xBFC00200;
427
        } else {
428
            pc = 0x80000000;
429
        }
430
        env->hflags |= MIPS_HFLAG_EXL;
431
        pc += offset;
432
        env->CP0_Cause = (env->CP0_Cause & ~0x7C) | (cause << 2);
433
        if (env->hflags & MIPS_HFLAG_DS) {
434
            /* If the exception was raised from a delay slot,
435
             * come back to the jump
436
             */
437
            env->CP0_EPC = env->PC - 4;
438
            env->CP0_Cause |= 0x80000000;
439
        } else {
440
            env->CP0_EPC = env->PC;
441
            env->CP0_Cause &= ~0x80000000;
442
        }
443
        break;
444
    default:
445
        if (logfile) {
446
            fprintf(logfile, "Invalid MIPS exception %d. Exiting\n",
447
                    env->exception_index);
448
        }
449
        printf("Invalid MIPS exception %d. Exiting\n", env->exception_index);
450
        exit(1);
451
    }
452
    env->PC = pc;
453
    if (logfile && env->exception_index != EXCP_EXT_INTERRUPT) {
454
        fprintf(logfile, "%s: PC %08x EPC %08x cause %d excp %d\n"
455
                "    S %08x C %08x A %08x D %08x\n",
456
                __func__, env->PC, env->CP0_EPC, cause, env->exception_index,
457
                env->CP0_Status, env->CP0_Cause, env->CP0_BadVAddr,
458
                env->CP0_DEPC);
459
    }
460
    env->exception_index = EXCP_NONE;
461
}