Statistics
| Branch: | Revision:

root / target-mips / helper.c @ e37e863f

History | View | Annotate | Download (12.4 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
#include <stdarg.h>
21
#include <stdlib.h>
22
#include <stdio.h>
23
#include <string.h>
24
#include <inttypes.h>
25
#include <signal.h>
26
#include <assert.h>
27

    
28
#include "cpu.h"
29
#include "exec-all.h"
30

    
31
/* MIPS32 4K MMU emulation */
32
#ifdef MIPS_USES_R4K_TLB
33
static int map_address (CPUState *env, target_ulong *physical, int *prot,
34
                        target_ulong address, int rw, int access_type)
35
{
36
    tlb_t *tlb;
37
    target_ulong tag;
38
    uint8_t ASID;
39
    int i, n;
40
    int ret;
41

    
42
    ret = -2;
43
    tag = (address & 0xFFFFE000);
44
    ASID = env->CP0_EntryHi & 0x000000FF;
45
    for (i = 0; i < MIPS_TLB_NB; i++) {
46
        tlb = &env->tlb[i];
47
        /* Check ASID, virtual page number & size */
48
        if ((tlb->G == 1 || tlb->ASID == ASID) &&
49
            tlb->VPN == tag && address < tlb->end) {
50
            /* TLB match */
51
            n = (address >> 12) & 1;
52
            /* Check access rights */
53
            if ((tlb->V[n] & 2) && (rw == 0 || (tlb->D[n] & 4))) {
54
                *physical = tlb->PFN[n] | (address & 0xFFF);
55
                *prot = PAGE_READ;
56
                if (tlb->D[n])
57
                    *prot |= PAGE_WRITE;
58
                return 0;
59
            } else if (!(tlb->V[n] & 2)) {
60
                return -3;
61
            } else {
62
                return -4;
63
            }
64
        }
65
    }
66

    
67
    return ret;
68
}
69
#endif
70

    
71
int get_physical_address (CPUState *env, target_ulong *physical, int *prot,
72
                          target_ulong address, int rw, int access_type)
73
{
74
    int user_mode;
75
    int ret;
76

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

    
136
    return ret;
137
}
138

    
139
#if defined(CONFIG_USER_ONLY) 
140
target_ulong cpu_get_phys_page_debug(CPUState *env, target_ulong addr)
141
{
142
    return addr;
143
}
144
#else
145
target_ulong cpu_get_phys_page_debug(CPUState *env, target_ulong addr)
146
{
147
    target_ulong phys_addr;
148
    int prot;
149

    
150
    if (get_physical_address(env, &phys_addr, &prot, addr, 0, ACCESS_INT) != 0)
151
        return -1;
152
    return phys_addr;
153
}
154

    
155
void cpu_mips_init_mmu (CPUState *env)
156
{
157
}
158
#endif /* !defined(CONFIG_USER_ONLY) */
159

    
160
int cpu_mips_handle_mmu_fault (CPUState *env, target_ulong address, int rw,
161
                               int is_user, int is_softmmu)
162
{
163
    target_ulong physical;
164
    int prot;
165
    int exception = 0, error_code = 0;
166
    int access_type;
167
    int ret = 0;
168

    
169
    if (logfile) {
170
        cpu_dump_state(env, logfile, fprintf, 0);
171
        fprintf(logfile, "%s pc %08x ad %08x rw %d is_user %d smmu %d\n",
172
                __func__, env->PC, address, rw, is_user, is_softmmu);
173
    }
174
    /* data access */
175
    /* XXX: put correct access by using cpu_restore_state()
176
       correctly */
177
    access_type = ACCESS_INT;
178
    if (env->user_mode_only) {
179
        /* user mode only emulation */
180
        ret = -2;
181
        goto do_fault;
182
    }
183
    ret = get_physical_address(env, &physical, &prot,
184
                               address, rw, access_type);
185
    if (logfile) {
186
        fprintf(logfile, "%s address=%08x ret %d physical %08x prot %d\n",
187
                __func__, address, ret, physical, prot);
188
    }
189
    if (ret == 0) {
190
        ret = tlb_set_page(env, address & ~0xFFF, physical & ~0xFFF, prot,
191
                           is_user, is_softmmu);
192
    } else if (ret < 0) {
193
    do_fault:
194
        switch (ret) {
195
        default:
196
        case -1:
197
            /* Reference to kernel address from user mode or supervisor mode */
198
            /* Reference to supervisor address from user mode */
199
            if (rw)
200
                exception = EXCP_AdES;
201
            else
202
                exception = EXCP_AdEL;
203
            break;
204
        case -2:
205
            /* No TLB match for a mapped address */
206
            if (rw)
207
                exception = EXCP_TLBS;
208
            else
209
                exception = EXCP_TLBL;
210
            error_code = 1;
211
            break;
212
        case -3:
213
            /* TLB match with no valid bit */
214
            if (rw)
215
                exception = EXCP_TLBS;
216
            else
217
                exception = EXCP_TLBL;
218
            error_code = 0;
219
            break;
220
        case -4:
221
            /* TLB match but 'D' bit is cleared */
222
            exception = EXCP_LTLBL;
223
            break;
224
                
225
        }
226
        /* Raise exception */
227
        env->CP0_BadVAddr = address;
228
        env->CP0_Context = (env->CP0_Context & 0xff800000) |
229
                           ((address >> 8) &   0x007ffff0);
230
        env->CP0_EntryHi =
231
            (env->CP0_EntryHi & 0x000000FF) | (address & 0xFFFFF000);
232
        env->exception_index = exception;
233
        env->error_code = error_code;
234
        ret = 1;
235
    }
236

    
237
    return ret;
238
}
239

    
240
void do_interrupt (CPUState *env)
241
{
242
    target_ulong pc, offset;
243
    int cause = -1;
244

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