Statistics
| Branch: | Revision:

root / target-mips / helper.c @ aa328add

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
enum {
32
    TLBRET_DIRTY = -4,
33
    TLBRET_INVALID = -3,
34
    TLBRET_NOMATCH = -2,
35
    TLBRET_BADADDR = -1,
36
    TLBRET_MATCH = 0
37
};
38

    
39
/* MIPS32 4K MMU emulation */
40
#ifdef MIPS_USES_R4K_TLB
41
static int map_address (CPUState *env, target_ulong *physical, int *prot,
42
                        target_ulong address, int rw, int access_type)
43
{
44
    target_ulong tag = address & (TARGET_PAGE_MASK << 1);
45
    uint8_t ASID = env->CP0_EntryHi & 0xFF;
46
    tlb_t *tlb;
47
    int i, n;
48

    
49
    for (i = 0; i < env->tlb_in_use; i++) {
50
        tlb = &env->tlb[i];
51
        /* Check ASID, virtual page number & size */
52
        if ((tlb->G == 1 || tlb->ASID == ASID) &&
53
            tlb->VPN == tag && address < tlb->end2) {
54
            /* TLB match */
55
            n = (address >> TARGET_PAGE_BITS) & 1;
56
            /* Check access rights */
57
           if (!(n ? tlb->V1 : tlb->V0))
58
                return TLBRET_INVALID;
59
           if (rw == 0 || (n ? tlb->D1 : tlb->D0)) {
60
                *physical = tlb->PFN[n] | (address & ~TARGET_PAGE_MASK);
61
                *prot = PAGE_READ;
62
                if (n ? tlb->D1 : tlb->D0)
63
                    *prot |= PAGE_WRITE;
64
                return TLBRET_MATCH;
65
            }
66
            return TLBRET_DIRTY;
67
        }
68
    }
69
    return TLBRET_NOMATCH;
70
}
71
#endif
72

    
73
static int get_physical_address (CPUState *env, target_ulong *physical,
74
                                int *prot, target_ulong address,
75
                                int rw, int access_type)
76
{
77
    /* User mode can only access useg */
78
    int user_mode = (env->hflags & MIPS_HFLAG_MODE) == MIPS_HFLAG_UM;
79
    int ret = TLBRET_MATCH;
80

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

    
137
    return ret;
138
}
139

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

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

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

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

    
170
    if (logfile) {
171
#if 0
172
        cpu_dump_state(env, logfile, fprintf, 0);
173
#endif
174
        fprintf(logfile, "%s pc %08x ad %08x rw %d is_user %d smmu %d\n",
175
                __func__, env->PC, address, rw, is_user, is_softmmu);
176
    }
177

    
178
    rw &= 1;
179

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

    
243
    return ret;
244
}
245

    
246
void do_interrupt (CPUState *env)
247
{
248
    target_ulong offset;
249
    int cause = -1;
250

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