Statistics
| Branch: | Revision:

root / target-mips / helper.c @ ef5b2344

History | View | Annotate | Download (22.1 kB)

1 6af0bf9c bellard
/*
2 6af0bf9c bellard
 *  MIPS emulation helpers for qemu.
3 5fafdf24 ths
 *
4 6af0bf9c bellard
 *  Copyright (c) 2004-2005 Jocelyn Mayer
5 6af0bf9c bellard
 *
6 6af0bf9c bellard
 * This library is free software; you can redistribute it and/or
7 6af0bf9c bellard
 * modify it under the terms of the GNU Lesser General Public
8 6af0bf9c bellard
 * License as published by the Free Software Foundation; either
9 6af0bf9c bellard
 * version 2 of the License, or (at your option) any later version.
10 6af0bf9c bellard
 *
11 6af0bf9c bellard
 * This library is distributed in the hope that it will be useful,
12 6af0bf9c bellard
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 6af0bf9c bellard
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14 6af0bf9c bellard
 * Lesser General Public License for more details.
15 6af0bf9c bellard
 *
16 6af0bf9c bellard
 * You should have received a copy of the GNU Lesser General Public
17 8167ee88 Blue Swirl
 * License along with this library; if not, see <http://www.gnu.org/licenses/>.
18 6af0bf9c bellard
 */
19 e37e863f bellard
#include <stdarg.h>
20 e37e863f bellard
#include <stdlib.h>
21 e37e863f bellard
#include <stdio.h>
22 e37e863f bellard
#include <string.h>
23 e37e863f bellard
#include <inttypes.h>
24 e37e863f bellard
#include <signal.h>
25 e37e863f bellard
26 e37e863f bellard
#include "cpu.h"
27 6af0bf9c bellard
28 43057ab1 bellard
enum {
29 43057ab1 bellard
    TLBRET_DIRTY = -4,
30 43057ab1 bellard
    TLBRET_INVALID = -3,
31 43057ab1 bellard
    TLBRET_NOMATCH = -2,
32 43057ab1 bellard
    TLBRET_BADADDR = -1,
33 43057ab1 bellard
    TLBRET_MATCH = 0
34 43057ab1 bellard
};
35 43057ab1 bellard
36 3c7b48b7 Paul Brook
#if !defined(CONFIG_USER_ONLY)
37 3c7b48b7 Paul Brook
38 29929e34 ths
/* no MMU emulation */
39 7db13fae Andreas Färber
int no_mmu_map_address (CPUMIPSState *env, target_phys_addr_t *physical, int *prot,
40 6af0bf9c bellard
                        target_ulong address, int rw, int access_type)
41 6af0bf9c bellard
{
42 29929e34 ths
    *physical = address;
43 29929e34 ths
    *prot = PAGE_READ | PAGE_WRITE;
44 29929e34 ths
    return TLBRET_MATCH;
45 29929e34 ths
}
46 29929e34 ths
47 29929e34 ths
/* fixed mapping MMU emulation */
48 7db13fae Andreas Färber
int fixed_mmu_map_address (CPUMIPSState *env, target_phys_addr_t *physical, int *prot,
49 29929e34 ths
                           target_ulong address, int rw, int access_type)
50 29929e34 ths
{
51 29929e34 ths
    if (address <= (int32_t)0x7FFFFFFFUL) {
52 29929e34 ths
        if (!(env->CP0_Status & (1 << CP0St_ERL)))
53 29929e34 ths
            *physical = address + 0x40000000UL;
54 29929e34 ths
        else
55 29929e34 ths
            *physical = address;
56 29929e34 ths
    } else if (address <= (int32_t)0xBFFFFFFFUL)
57 29929e34 ths
        *physical = address & 0x1FFFFFFF;
58 29929e34 ths
    else
59 29929e34 ths
        *physical = address;
60 29929e34 ths
61 29929e34 ths
    *prot = PAGE_READ | PAGE_WRITE;
62 29929e34 ths
    return TLBRET_MATCH;
63 29929e34 ths
}
64 29929e34 ths
65 29929e34 ths
/* MIPS32/MIPS64 R4000-style MMU emulation */
66 7db13fae Andreas Färber
int r4k_map_address (CPUMIPSState *env, target_phys_addr_t *physical, int *prot,
67 29929e34 ths
                     target_ulong address, int rw, int access_type)
68 29929e34 ths
{
69 925fd0f2 ths
    uint8_t ASID = env->CP0_EntryHi & 0xFF;
70 3b1c8be4 ths
    int i;
71 6af0bf9c bellard
72 ead9360e ths
    for (i = 0; i < env->tlb->tlb_in_use; i++) {
73 c227f099 Anthony Liguori
        r4k_tlb_t *tlb = &env->tlb->mmu.r4k.tlb[i];
74 3b1c8be4 ths
        /* 1k pages are not supported. */
75 f2e9ebef ths
        target_ulong mask = tlb->PageMask | ~(TARGET_PAGE_MASK << 1);
76 3b1c8be4 ths
        target_ulong tag = address & ~mask;
77 f2e9ebef ths
        target_ulong VPN = tlb->VPN & ~mask;
78 d26bc211 ths
#if defined(TARGET_MIPS64)
79 e034e2c3 ths
        tag &= env->SEGMask;
80 100ce988 ths
#endif
81 3b1c8be4 ths
82 6af0bf9c bellard
        /* Check ASID, virtual page number & size */
83 f2e9ebef ths
        if ((tlb->G == 1 || tlb->ASID == ASID) && VPN == tag) {
84 6af0bf9c bellard
            /* TLB match */
85 f2e9ebef ths
            int n = !!(address & mask & ~(mask >> 1));
86 6af0bf9c bellard
            /* Check access rights */
87 f2e9ebef ths
            if (!(n ? tlb->V1 : tlb->V0))
88 43057ab1 bellard
                return TLBRET_INVALID;
89 f2e9ebef ths
            if (rw == 0 || (n ? tlb->D1 : tlb->D0)) {
90 3b1c8be4 ths
                *physical = tlb->PFN[n] | (address & (mask >> 1));
91 9fb63ac2 bellard
                *prot = PAGE_READ;
92 98c1b82b pbrook
                if (n ? tlb->D1 : tlb->D0)
93 9fb63ac2 bellard
                    *prot |= PAGE_WRITE;
94 43057ab1 bellard
                return TLBRET_MATCH;
95 6af0bf9c bellard
            }
96 43057ab1 bellard
            return TLBRET_DIRTY;
97 6af0bf9c bellard
        }
98 6af0bf9c bellard
    }
99 43057ab1 bellard
    return TLBRET_NOMATCH;
100 6af0bf9c bellard
}
101 6af0bf9c bellard
102 7db13fae Andreas Färber
static int get_physical_address (CPUMIPSState *env, target_phys_addr_t *physical,
103 43057ab1 bellard
                                int *prot, target_ulong address,
104 43057ab1 bellard
                                int rw, int access_type)
105 6af0bf9c bellard
{
106 b4ab4b4e ths
    /* User mode can only access useg/xuseg */
107 43057ab1 bellard
    int user_mode = (env->hflags & MIPS_HFLAG_MODE) == MIPS_HFLAG_UM;
108 671880e6 ths
    int supervisor_mode = (env->hflags & MIPS_HFLAG_MODE) == MIPS_HFLAG_SM;
109 671880e6 ths
    int kernel_mode = !user_mode && !supervisor_mode;
110 d26bc211 ths
#if defined(TARGET_MIPS64)
111 b4ab4b4e ths
    int UX = (env->CP0_Status & (1 << CP0St_UX)) != 0;
112 b4ab4b4e ths
    int SX = (env->CP0_Status & (1 << CP0St_SX)) != 0;
113 b4ab4b4e ths
    int KX = (env->CP0_Status & (1 << CP0St_KX)) != 0;
114 b4ab4b4e ths
#endif
115 43057ab1 bellard
    int ret = TLBRET_MATCH;
116 43057ab1 bellard
117 6af0bf9c bellard
#if 0
118 93fcfe39 aliguori
    qemu_log("user mode %d h %08x\n", user_mode, env->hflags);
119 6af0bf9c bellard
#endif
120 b4ab4b4e ths
121 b4ab4b4e ths
    if (address <= (int32_t)0x7FFFFFFFUL) {
122 b4ab4b4e ths
        /* useg */
123 996ba2cc ths
        if (env->CP0_Status & (1 << CP0St_ERL)) {
124 29929e34 ths
            *physical = address & 0xFFFFFFFF;
125 6af0bf9c bellard
            *prot = PAGE_READ | PAGE_WRITE;
126 996ba2cc ths
        } else {
127 ead9360e ths
            ret = env->tlb->map_address(env, physical, prot, address, rw, access_type);
128 6af0bf9c bellard
        }
129 d26bc211 ths
#if defined(TARGET_MIPS64)
130 89fc88da ths
    } else if (address < 0x4000000000000000ULL) {
131 b4ab4b4e ths
        /* xuseg */
132 6958549d aurel32
        if (UX && address <= (0x3FFFFFFFFFFFFFFFULL & env->SEGMask)) {
133 ead9360e ths
            ret = env->tlb->map_address(env, physical, prot, address, rw, access_type);
134 6958549d aurel32
        } else {
135 6958549d aurel32
            ret = TLBRET_BADADDR;
136 b4ab4b4e ths
        }
137 89fc88da ths
    } else if (address < 0x8000000000000000ULL) {
138 b4ab4b4e ths
        /* xsseg */
139 6958549d aurel32
        if ((supervisor_mode || kernel_mode) &&
140 6958549d aurel32
            SX && address <= (0x7FFFFFFFFFFFFFFFULL & env->SEGMask)) {
141 ead9360e ths
            ret = env->tlb->map_address(env, physical, prot, address, rw, access_type);
142 6958549d aurel32
        } else {
143 6958549d aurel32
            ret = TLBRET_BADADDR;
144 b4ab4b4e ths
        }
145 89fc88da ths
    } else if (address < 0xC000000000000000ULL) {
146 b4ab4b4e ths
        /* xkphys */
147 671880e6 ths
        if (kernel_mode && KX &&
148 6d35524c ths
            (address & 0x07FFFFFFFFFFFFFFULL) <= env->PAMask) {
149 6d35524c ths
            *physical = address & env->PAMask;
150 b4ab4b4e ths
            *prot = PAGE_READ | PAGE_WRITE;
151 6958549d aurel32
        } else {
152 6958549d aurel32
            ret = TLBRET_BADADDR;
153 6958549d aurel32
        }
154 89fc88da ths
    } else if (address < 0xFFFFFFFF80000000ULL) {
155 b4ab4b4e ths
        /* xkseg */
156 6958549d aurel32
        if (kernel_mode && KX &&
157 6958549d aurel32
            address <= (0xFFFFFFFF7FFFFFFFULL & env->SEGMask)) {
158 ead9360e ths
            ret = env->tlb->map_address(env, physical, prot, address, rw, access_type);
159 6958549d aurel32
        } else {
160 6958549d aurel32
            ret = TLBRET_BADADDR;
161 6958549d aurel32
        }
162 b4ab4b4e ths
#endif
163 5dc4b744 ths
    } else if (address < (int32_t)0xA0000000UL) {
164 6af0bf9c bellard
        /* kseg0 */
165 671880e6 ths
        if (kernel_mode) {
166 671880e6 ths
            *physical = address - (int32_t)0x80000000UL;
167 671880e6 ths
            *prot = PAGE_READ | PAGE_WRITE;
168 671880e6 ths
        } else {
169 671880e6 ths
            ret = TLBRET_BADADDR;
170 671880e6 ths
        }
171 5dc4b744 ths
    } else if (address < (int32_t)0xC0000000UL) {
172 6af0bf9c bellard
        /* kseg1 */
173 671880e6 ths
        if (kernel_mode) {
174 671880e6 ths
            *physical = address - (int32_t)0xA0000000UL;
175 671880e6 ths
            *prot = PAGE_READ | PAGE_WRITE;
176 671880e6 ths
        } else {
177 671880e6 ths
            ret = TLBRET_BADADDR;
178 671880e6 ths
        }
179 5dc4b744 ths
    } else if (address < (int32_t)0xE0000000UL) {
180 89fc88da ths
        /* sseg (kseg2) */
181 671880e6 ths
        if (supervisor_mode || kernel_mode) {
182 671880e6 ths
            ret = env->tlb->map_address(env, physical, prot, address, rw, access_type);
183 671880e6 ths
        } else {
184 671880e6 ths
            ret = TLBRET_BADADDR;
185 671880e6 ths
        }
186 6af0bf9c bellard
    } else {
187 6af0bf9c bellard
        /* kseg3 */
188 6af0bf9c bellard
        /* XXX: debug segment is not emulated */
189 671880e6 ths
        if (kernel_mode) {
190 671880e6 ths
            ret = env->tlb->map_address(env, physical, prot, address, rw, access_type);
191 671880e6 ths
        } else {
192 671880e6 ths
            ret = TLBRET_BADADDR;
193 671880e6 ths
        }
194 6af0bf9c bellard
    }
195 6af0bf9c bellard
#if 0
196 93fcfe39 aliguori
    qemu_log(TARGET_FMT_lx " %d %d => " TARGET_FMT_lx " %d (%d)\n",
197 93fcfe39 aliguori
            address, rw, access_type, *physical, *prot, ret);
198 6af0bf9c bellard
#endif
199 6af0bf9c bellard
200 6af0bf9c bellard
    return ret;
201 6af0bf9c bellard
}
202 932e71cd aurel32
#endif
203 6af0bf9c bellard
204 7db13fae Andreas Färber
static void raise_mmu_exception(CPUMIPSState *env, target_ulong address,
205 1147e189 Aurelien Jarno
                                int rw, int tlb_error)
206 1147e189 Aurelien Jarno
{
207 1147e189 Aurelien Jarno
    int exception = 0, error_code = 0;
208 1147e189 Aurelien Jarno
209 1147e189 Aurelien Jarno
    switch (tlb_error) {
210 1147e189 Aurelien Jarno
    default:
211 1147e189 Aurelien Jarno
    case TLBRET_BADADDR:
212 1147e189 Aurelien Jarno
        /* Reference to kernel address from user mode or supervisor mode */
213 1147e189 Aurelien Jarno
        /* Reference to supervisor address from user mode */
214 1147e189 Aurelien Jarno
        if (rw)
215 1147e189 Aurelien Jarno
            exception = EXCP_AdES;
216 1147e189 Aurelien Jarno
        else
217 1147e189 Aurelien Jarno
            exception = EXCP_AdEL;
218 1147e189 Aurelien Jarno
        break;
219 1147e189 Aurelien Jarno
    case TLBRET_NOMATCH:
220 1147e189 Aurelien Jarno
        /* No TLB match for a mapped address */
221 1147e189 Aurelien Jarno
        if (rw)
222 1147e189 Aurelien Jarno
            exception = EXCP_TLBS;
223 1147e189 Aurelien Jarno
        else
224 1147e189 Aurelien Jarno
            exception = EXCP_TLBL;
225 1147e189 Aurelien Jarno
        error_code = 1;
226 1147e189 Aurelien Jarno
        break;
227 1147e189 Aurelien Jarno
    case TLBRET_INVALID:
228 1147e189 Aurelien Jarno
        /* TLB match with no valid bit */
229 1147e189 Aurelien Jarno
        if (rw)
230 1147e189 Aurelien Jarno
            exception = EXCP_TLBS;
231 1147e189 Aurelien Jarno
        else
232 1147e189 Aurelien Jarno
            exception = EXCP_TLBL;
233 1147e189 Aurelien Jarno
        break;
234 1147e189 Aurelien Jarno
    case TLBRET_DIRTY:
235 1147e189 Aurelien Jarno
        /* TLB match but 'D' bit is cleared */
236 1147e189 Aurelien Jarno
        exception = EXCP_LTLBL;
237 1147e189 Aurelien Jarno
        break;
238 1147e189 Aurelien Jarno
239 1147e189 Aurelien Jarno
    }
240 1147e189 Aurelien Jarno
    /* Raise exception */
241 1147e189 Aurelien Jarno
    env->CP0_BadVAddr = address;
242 1147e189 Aurelien Jarno
    env->CP0_Context = (env->CP0_Context & ~0x007fffff) |
243 1147e189 Aurelien Jarno
                       ((address >> 9) & 0x007ffff0);
244 1147e189 Aurelien Jarno
    env->CP0_EntryHi =
245 1147e189 Aurelien Jarno
        (env->CP0_EntryHi & 0xFF) | (address & (TARGET_PAGE_MASK << 1));
246 1147e189 Aurelien Jarno
#if defined(TARGET_MIPS64)
247 1147e189 Aurelien Jarno
    env->CP0_EntryHi &= env->SEGMask;
248 1147e189 Aurelien Jarno
    env->CP0_XContext = (env->CP0_XContext & ((~0ULL) << (env->SEGBITS - 7))) |
249 1147e189 Aurelien Jarno
                        ((address & 0xC00000000000ULL) >> (55 - env->SEGBITS)) |
250 1147e189 Aurelien Jarno
                        ((address & ((1ULL << env->SEGBITS) - 1) & 0xFFFFFFFFFFFFE000ULL) >> 9);
251 1147e189 Aurelien Jarno
#endif
252 1147e189 Aurelien Jarno
    env->exception_index = exception;
253 1147e189 Aurelien Jarno
    env->error_code = error_code;
254 1147e189 Aurelien Jarno
}
255 1147e189 Aurelien Jarno
256 4fcc562b Paul Brook
#if !defined(CONFIG_USER_ONLY)
257 7db13fae Andreas Färber
target_phys_addr_t cpu_get_phys_page_debug(CPUMIPSState *env, target_ulong addr)
258 6af0bf9c bellard
{
259 60c9af07 Aurelien Jarno
    target_phys_addr_t phys_addr;
260 932e71cd aurel32
    int prot;
261 6af0bf9c bellard
262 932e71cd aurel32
    if (get_physical_address(env, &phys_addr, &prot, addr, 0, ACCESS_INT) != 0)
263 932e71cd aurel32
        return -1;
264 932e71cd aurel32
    return phys_addr;
265 6af0bf9c bellard
}
266 4fcc562b Paul Brook
#endif
267 6af0bf9c bellard
268 7db13fae Andreas Färber
int cpu_mips_handle_mmu_fault (CPUMIPSState *env, target_ulong address, int rw,
269 97b348e7 Blue Swirl
                               int mmu_idx)
270 6af0bf9c bellard
{
271 932e71cd aurel32
#if !defined(CONFIG_USER_ONLY)
272 60c9af07 Aurelien Jarno
    target_phys_addr_t physical;
273 6af0bf9c bellard
    int prot;
274 6af0bf9c bellard
    int access_type;
275 99e43d36 Aurelien Jarno
#endif
276 6af0bf9c bellard
    int ret = 0;
277 6af0bf9c bellard
278 4ad40f36 bellard
#if 0
279 93fcfe39 aliguori
    log_cpu_state(env, 0);
280 4ad40f36 bellard
#endif
281 97b348e7 Blue Swirl
    qemu_log("%s pc " TARGET_FMT_lx " ad " TARGET_FMT_lx " rw %d mmu_idx %d\n",
282 97b348e7 Blue Swirl
              __func__, env->active_tc.PC, address, rw, mmu_idx);
283 4ad40f36 bellard
284 4ad40f36 bellard
    rw &= 1;
285 4ad40f36 bellard
286 6af0bf9c bellard
    /* data access */
287 99e43d36 Aurelien Jarno
#if !defined(CONFIG_USER_ONLY)
288 6af0bf9c bellard
    /* XXX: put correct access by using cpu_restore_state()
289 6af0bf9c bellard
       correctly */
290 6af0bf9c bellard
    access_type = ACCESS_INT;
291 6af0bf9c bellard
    ret = get_physical_address(env, &physical, &prot,
292 6af0bf9c bellard
                               address, rw, access_type);
293 60c9af07 Aurelien Jarno
    qemu_log("%s address=" TARGET_FMT_lx " ret %d physical " TARGET_FMT_plx " prot %d\n",
294 93fcfe39 aliguori
              __func__, address, ret, physical, prot);
295 43057ab1 bellard
    if (ret == TLBRET_MATCH) {
296 99e43d36 Aurelien Jarno
        tlb_set_page(env, address & TARGET_PAGE_MASK,
297 99e43d36 Aurelien Jarno
                     physical & TARGET_PAGE_MASK, prot | PAGE_EXEC,
298 99e43d36 Aurelien Jarno
                     mmu_idx, TARGET_PAGE_SIZE);
299 99e43d36 Aurelien Jarno
        ret = 0;
300 932e71cd aurel32
    } else if (ret < 0)
301 932e71cd aurel32
#endif
302 932e71cd aurel32
    {
303 1147e189 Aurelien Jarno
        raise_mmu_exception(env, address, rw, ret);
304 6af0bf9c bellard
        ret = 1;
305 6af0bf9c bellard
    }
306 6af0bf9c bellard
307 6af0bf9c bellard
    return ret;
308 6af0bf9c bellard
}
309 6af0bf9c bellard
310 25b91e32 Aurelien Jarno
#if !defined(CONFIG_USER_ONLY)
311 7db13fae Andreas Färber
target_phys_addr_t cpu_mips_translate_address(CPUMIPSState *env, target_ulong address, int rw)
312 25b91e32 Aurelien Jarno
{
313 25b91e32 Aurelien Jarno
    target_phys_addr_t physical;
314 25b91e32 Aurelien Jarno
    int prot;
315 25b91e32 Aurelien Jarno
    int access_type;
316 25b91e32 Aurelien Jarno
    int ret = 0;
317 25b91e32 Aurelien Jarno
318 25b91e32 Aurelien Jarno
    rw &= 1;
319 25b91e32 Aurelien Jarno
320 25b91e32 Aurelien Jarno
    /* data access */
321 25b91e32 Aurelien Jarno
    access_type = ACCESS_INT;
322 25b91e32 Aurelien Jarno
    ret = get_physical_address(env, &physical, &prot,
323 25b91e32 Aurelien Jarno
                               address, rw, access_type);
324 25b91e32 Aurelien Jarno
    if (ret != TLBRET_MATCH) {
325 25b91e32 Aurelien Jarno
        raise_mmu_exception(env, address, rw, ret);
326 c36bbb28 Aurelien Jarno
        return -1LL;
327 c36bbb28 Aurelien Jarno
    } else {
328 c36bbb28 Aurelien Jarno
        return physical;
329 25b91e32 Aurelien Jarno
    }
330 25b91e32 Aurelien Jarno
}
331 25b91e32 Aurelien Jarno
#endif
332 25b91e32 Aurelien Jarno
333 9a5d878f ths
static const char * const excp_names[EXCP_LAST + 1] = {
334 9a5d878f ths
    [EXCP_RESET] = "reset",
335 9a5d878f ths
    [EXCP_SRESET] = "soft reset",
336 9a5d878f ths
    [EXCP_DSS] = "debug single step",
337 9a5d878f ths
    [EXCP_DINT] = "debug interrupt",
338 9a5d878f ths
    [EXCP_NMI] = "non-maskable interrupt",
339 9a5d878f ths
    [EXCP_MCHECK] = "machine check",
340 9a5d878f ths
    [EXCP_EXT_INTERRUPT] = "interrupt",
341 9a5d878f ths
    [EXCP_DFWATCH] = "deferred watchpoint",
342 9a5d878f ths
    [EXCP_DIB] = "debug instruction breakpoint",
343 9a5d878f ths
    [EXCP_IWATCH] = "instruction fetch watchpoint",
344 9a5d878f ths
    [EXCP_AdEL] = "address error load",
345 9a5d878f ths
    [EXCP_AdES] = "address error store",
346 9a5d878f ths
    [EXCP_TLBF] = "TLB refill",
347 9a5d878f ths
    [EXCP_IBE] = "instruction bus error",
348 9a5d878f ths
    [EXCP_DBp] = "debug breakpoint",
349 9a5d878f ths
    [EXCP_SYSCALL] = "syscall",
350 9a5d878f ths
    [EXCP_BREAK] = "break",
351 9a5d878f ths
    [EXCP_CpU] = "coprocessor unusable",
352 9a5d878f ths
    [EXCP_RI] = "reserved instruction",
353 9a5d878f ths
    [EXCP_OVERFLOW] = "arithmetic overflow",
354 9a5d878f ths
    [EXCP_TRAP] = "trap",
355 9a5d878f ths
    [EXCP_FPE] = "floating point",
356 9a5d878f ths
    [EXCP_DDBS] = "debug data break store",
357 9a5d878f ths
    [EXCP_DWATCH] = "data watchpoint",
358 9a5d878f ths
    [EXCP_LTLBL] = "TLB modify",
359 9a5d878f ths
    [EXCP_TLBL] = "TLB load",
360 9a5d878f ths
    [EXCP_TLBS] = "TLB store",
361 9a5d878f ths
    [EXCP_DBE] = "data bus error",
362 9a5d878f ths
    [EXCP_DDBL] = "debug data break load",
363 9a5d878f ths
    [EXCP_THREAD] = "thread",
364 9a5d878f ths
    [EXCP_MDMX] = "MDMX",
365 9a5d878f ths
    [EXCP_C2E] = "precise coprocessor 2",
366 9a5d878f ths
    [EXCP_CACHE] = "cache error",
367 14e51cc7 ths
};
368 14e51cc7 ths
369 32188a03 Nathan Froyd
#if !defined(CONFIG_USER_ONLY)
370 7db13fae Andreas Färber
static target_ulong exception_resume_pc (CPUMIPSState *env)
371 32188a03 Nathan Froyd
{
372 32188a03 Nathan Froyd
    target_ulong bad_pc;
373 32188a03 Nathan Froyd
    target_ulong isa_mode;
374 32188a03 Nathan Froyd
375 32188a03 Nathan Froyd
    isa_mode = !!(env->hflags & MIPS_HFLAG_M16);
376 32188a03 Nathan Froyd
    bad_pc = env->active_tc.PC | isa_mode;
377 32188a03 Nathan Froyd
    if (env->hflags & MIPS_HFLAG_BMASK) {
378 32188a03 Nathan Froyd
        /* If the exception was raised from a delay slot, come back to
379 32188a03 Nathan Froyd
           the jump.  */
380 32188a03 Nathan Froyd
        bad_pc -= (env->hflags & MIPS_HFLAG_B16 ? 2 : 4);
381 32188a03 Nathan Froyd
    }
382 32188a03 Nathan Froyd
383 32188a03 Nathan Froyd
    return bad_pc;
384 32188a03 Nathan Froyd
}
385 bbfa8f72 Nathan Froyd
386 7db13fae Andreas Färber
static void set_hflags_for_handler (CPUMIPSState *env)
387 bbfa8f72 Nathan Froyd
{
388 bbfa8f72 Nathan Froyd
    /* Exception handlers are entered in 32-bit mode.  */
389 bbfa8f72 Nathan Froyd
    env->hflags &= ~(MIPS_HFLAG_M16);
390 bbfa8f72 Nathan Froyd
    /* ...except that microMIPS lets you choose.  */
391 bbfa8f72 Nathan Froyd
    if (env->insn_flags & ASE_MICROMIPS) {
392 bbfa8f72 Nathan Froyd
        env->hflags |= (!!(env->CP0_Config3
393 bbfa8f72 Nathan Froyd
                           & (1 << CP0C3_ISA_ON_EXC))
394 bbfa8f72 Nathan Froyd
                        << MIPS_HFLAG_M16_SHIFT);
395 bbfa8f72 Nathan Froyd
    }
396 bbfa8f72 Nathan Froyd
}
397 32188a03 Nathan Froyd
#endif
398 32188a03 Nathan Froyd
399 7db13fae Andreas Färber
void do_interrupt (CPUMIPSState *env)
400 6af0bf9c bellard
{
401 932e71cd aurel32
#if !defined(CONFIG_USER_ONLY)
402 fca1be7c Andreas Färber
    MIPSCPU *cpu = mips_env_get_cpu(env);
403 932e71cd aurel32
    target_ulong offset;
404 932e71cd aurel32
    int cause = -1;
405 932e71cd aurel32
    const char *name;
406 100ce988 ths
407 93fcfe39 aliguori
    if (qemu_log_enabled() && env->exception_index != EXCP_EXT_INTERRUPT) {
408 932e71cd aurel32
        if (env->exception_index < 0 || env->exception_index > EXCP_LAST)
409 932e71cd aurel32
            name = "unknown";
410 932e71cd aurel32
        else
411 932e71cd aurel32
            name = excp_names[env->exception_index];
412 b67bfe8d ths
413 93fcfe39 aliguori
        qemu_log("%s enter: PC " TARGET_FMT_lx " EPC " TARGET_FMT_lx " %s exception\n",
414 93fcfe39 aliguori
                 __func__, env->active_tc.PC, env->CP0_EPC, name);
415 932e71cd aurel32
    }
416 932e71cd aurel32
    if (env->exception_index == EXCP_EXT_INTERRUPT &&
417 932e71cd aurel32
        (env->hflags & MIPS_HFLAG_DM))
418 932e71cd aurel32
        env->exception_index = EXCP_DINT;
419 932e71cd aurel32
    offset = 0x180;
420 932e71cd aurel32
    switch (env->exception_index) {
421 932e71cd aurel32
    case EXCP_DSS:
422 932e71cd aurel32
        env->CP0_Debug |= 1 << CP0DB_DSS;
423 932e71cd aurel32
        /* Debug single step cannot be raised inside a delay slot and
424 932e71cd aurel32
           resume will always occur on the next instruction
425 932e71cd aurel32
           (but we assume the pc has always been updated during
426 932e71cd aurel32
           code translation). */
427 32188a03 Nathan Froyd
        env->CP0_DEPC = env->active_tc.PC | !!(env->hflags & MIPS_HFLAG_M16);
428 932e71cd aurel32
        goto enter_debug_mode;
429 932e71cd aurel32
    case EXCP_DINT:
430 932e71cd aurel32
        env->CP0_Debug |= 1 << CP0DB_DINT;
431 932e71cd aurel32
        goto set_DEPC;
432 932e71cd aurel32
    case EXCP_DIB:
433 932e71cd aurel32
        env->CP0_Debug |= 1 << CP0DB_DIB;
434 932e71cd aurel32
        goto set_DEPC;
435 932e71cd aurel32
    case EXCP_DBp:
436 932e71cd aurel32
        env->CP0_Debug |= 1 << CP0DB_DBp;
437 932e71cd aurel32
        goto set_DEPC;
438 932e71cd aurel32
    case EXCP_DDBS:
439 932e71cd aurel32
        env->CP0_Debug |= 1 << CP0DB_DDBS;
440 932e71cd aurel32
        goto set_DEPC;
441 932e71cd aurel32
    case EXCP_DDBL:
442 932e71cd aurel32
        env->CP0_Debug |= 1 << CP0DB_DDBL;
443 932e71cd aurel32
    set_DEPC:
444 32188a03 Nathan Froyd
        env->CP0_DEPC = exception_resume_pc(env);
445 32188a03 Nathan Froyd
        env->hflags &= ~MIPS_HFLAG_BMASK;
446 0eaef5aa ths
 enter_debug_mode:
447 932e71cd aurel32
        env->hflags |= MIPS_HFLAG_DM | MIPS_HFLAG_64 | MIPS_HFLAG_CP0;
448 932e71cd aurel32
        env->hflags &= ~(MIPS_HFLAG_KSU);
449 932e71cd aurel32
        /* EJTAG probe trap enable is not implemented... */
450 932e71cd aurel32
        if (!(env->CP0_Status & (1 << CP0St_EXL)))
451 932e71cd aurel32
            env->CP0_Cause &= ~(1 << CP0Ca_BD);
452 932e71cd aurel32
        env->active_tc.PC = (int32_t)0xBFC00480;
453 bbfa8f72 Nathan Froyd
        set_hflags_for_handler(env);
454 932e71cd aurel32
        break;
455 932e71cd aurel32
    case EXCP_RESET:
456 fca1be7c Andreas Färber
        cpu_reset(CPU(cpu));
457 932e71cd aurel32
        break;
458 932e71cd aurel32
    case EXCP_SRESET:
459 932e71cd aurel32
        env->CP0_Status |= (1 << CP0St_SR);
460 932e71cd aurel32
        memset(env->CP0_WatchLo, 0, sizeof(*env->CP0_WatchLo));
461 932e71cd aurel32
        goto set_error_EPC;
462 932e71cd aurel32
    case EXCP_NMI:
463 932e71cd aurel32
        env->CP0_Status |= (1 << CP0St_NMI);
464 0eaef5aa ths
 set_error_EPC:
465 32188a03 Nathan Froyd
        env->CP0_ErrorEPC = exception_resume_pc(env);
466 32188a03 Nathan Froyd
        env->hflags &= ~MIPS_HFLAG_BMASK;
467 932e71cd aurel32
        env->CP0_Status |= (1 << CP0St_ERL) | (1 << CP0St_BEV);
468 932e71cd aurel32
        env->hflags |= MIPS_HFLAG_64 | MIPS_HFLAG_CP0;
469 932e71cd aurel32
        env->hflags &= ~(MIPS_HFLAG_KSU);
470 932e71cd aurel32
        if (!(env->CP0_Status & (1 << CP0St_EXL)))
471 932e71cd aurel32
            env->CP0_Cause &= ~(1 << CP0Ca_BD);
472 932e71cd aurel32
        env->active_tc.PC = (int32_t)0xBFC00000;
473 bbfa8f72 Nathan Froyd
        set_hflags_for_handler(env);
474 932e71cd aurel32
        break;
475 932e71cd aurel32
    case EXCP_EXT_INTERRUPT:
476 932e71cd aurel32
        cause = 0;
477 932e71cd aurel32
        if (env->CP0_Cause & (1 << CP0Ca_IV))
478 932e71cd aurel32
            offset = 0x200;
479 138afb02 Edgar E. Iglesias
480 138afb02 Edgar E. Iglesias
        if (env->CP0_Config3 & ((1 << CP0C3_VInt) | (1 << CP0C3_VEIC))) {
481 138afb02 Edgar E. Iglesias
            /* Vectored Interrupts.  */
482 138afb02 Edgar E. Iglesias
            unsigned int spacing;
483 138afb02 Edgar E. Iglesias
            unsigned int vector;
484 138afb02 Edgar E. Iglesias
            unsigned int pending = (env->CP0_Cause & CP0Ca_IP_mask) >> 8;
485 138afb02 Edgar E. Iglesias
486 e4280973 Edgar E. Iglesias
            pending &= env->CP0_Status >> 8;
487 138afb02 Edgar E. Iglesias
            /* Compute the Vector Spacing.  */
488 138afb02 Edgar E. Iglesias
            spacing = (env->CP0_IntCtl >> CP0IntCtl_VS) & ((1 << 6) - 1);
489 138afb02 Edgar E. Iglesias
            spacing <<= 5;
490 138afb02 Edgar E. Iglesias
491 138afb02 Edgar E. Iglesias
            if (env->CP0_Config3 & (1 << CP0C3_VInt)) {
492 138afb02 Edgar E. Iglesias
                /* For VInt mode, the MIPS computes the vector internally.  */
493 e4280973 Edgar E. Iglesias
                for (vector = 7; vector > 0; vector--) {
494 e4280973 Edgar E. Iglesias
                    if (pending & (1 << vector)) {
495 138afb02 Edgar E. Iglesias
                        /* Found it.  */
496 138afb02 Edgar E. Iglesias
                        break;
497 138afb02 Edgar E. Iglesias
                    }
498 138afb02 Edgar E. Iglesias
                }
499 138afb02 Edgar E. Iglesias
            } else {
500 138afb02 Edgar E. Iglesias
                /* For VEIC mode, the external interrupt controller feeds the
501 e7d81004 Stefan Weil
                   vector through the CP0Cause IP lines.  */
502 138afb02 Edgar E. Iglesias
                vector = pending;
503 138afb02 Edgar E. Iglesias
            }
504 138afb02 Edgar E. Iglesias
            offset = 0x200 + vector * spacing;
505 138afb02 Edgar E. Iglesias
        }
506 932e71cd aurel32
        goto set_EPC;
507 932e71cd aurel32
    case EXCP_LTLBL:
508 932e71cd aurel32
        cause = 1;
509 932e71cd aurel32
        goto set_EPC;
510 932e71cd aurel32
    case EXCP_TLBL:
511 932e71cd aurel32
        cause = 2;
512 932e71cd aurel32
        if (env->error_code == 1 && !(env->CP0_Status & (1 << CP0St_EXL))) {
513 0eaef5aa ths
#if defined(TARGET_MIPS64)
514 932e71cd aurel32
            int R = env->CP0_BadVAddr >> 62;
515 932e71cd aurel32
            int UX = (env->CP0_Status & (1 << CP0St_UX)) != 0;
516 932e71cd aurel32
            int SX = (env->CP0_Status & (1 << CP0St_SX)) != 0;
517 932e71cd aurel32
            int KX = (env->CP0_Status & (1 << CP0St_KX)) != 0;
518 0eaef5aa ths
519 3fc00a7b Aurelien Jarno
            if (((R == 0 && UX) || (R == 1 && SX) || (R == 3 && KX)) &&
520 3fc00a7b Aurelien Jarno
                (!(env->insn_flags & (INSN_LOONGSON2E | INSN_LOONGSON2F))))
521 932e71cd aurel32
                offset = 0x080;
522 932e71cd aurel32
            else
523 0eaef5aa ths
#endif
524 932e71cd aurel32
                offset = 0x000;
525 932e71cd aurel32
        }
526 932e71cd aurel32
        goto set_EPC;
527 932e71cd aurel32
    case EXCP_TLBS:
528 932e71cd aurel32
        cause = 3;
529 932e71cd aurel32
        if (env->error_code == 1 && !(env->CP0_Status & (1 << CP0St_EXL))) {
530 0eaef5aa ths
#if defined(TARGET_MIPS64)
531 932e71cd aurel32
            int R = env->CP0_BadVAddr >> 62;
532 932e71cd aurel32
            int UX = (env->CP0_Status & (1 << CP0St_UX)) != 0;
533 932e71cd aurel32
            int SX = (env->CP0_Status & (1 << CP0St_SX)) != 0;
534 932e71cd aurel32
            int KX = (env->CP0_Status & (1 << CP0St_KX)) != 0;
535 0eaef5aa ths
536 3fc00a7b Aurelien Jarno
            if (((R == 0 && UX) || (R == 1 && SX) || (R == 3 && KX)) &&
537 3fc00a7b Aurelien Jarno
                (!(env->insn_flags & (INSN_LOONGSON2E | INSN_LOONGSON2F))))
538 932e71cd aurel32
                offset = 0x080;
539 932e71cd aurel32
            else
540 0eaef5aa ths
#endif
541 932e71cd aurel32
                offset = 0x000;
542 932e71cd aurel32
        }
543 932e71cd aurel32
        goto set_EPC;
544 932e71cd aurel32
    case EXCP_AdEL:
545 932e71cd aurel32
        cause = 4;
546 932e71cd aurel32
        goto set_EPC;
547 932e71cd aurel32
    case EXCP_AdES:
548 932e71cd aurel32
        cause = 5;
549 932e71cd aurel32
        goto set_EPC;
550 932e71cd aurel32
    case EXCP_IBE:
551 932e71cd aurel32
        cause = 6;
552 932e71cd aurel32
        goto set_EPC;
553 932e71cd aurel32
    case EXCP_DBE:
554 932e71cd aurel32
        cause = 7;
555 932e71cd aurel32
        goto set_EPC;
556 932e71cd aurel32
    case EXCP_SYSCALL:
557 932e71cd aurel32
        cause = 8;
558 932e71cd aurel32
        goto set_EPC;
559 932e71cd aurel32
    case EXCP_BREAK:
560 932e71cd aurel32
        cause = 9;
561 932e71cd aurel32
        goto set_EPC;
562 932e71cd aurel32
    case EXCP_RI:
563 932e71cd aurel32
        cause = 10;
564 932e71cd aurel32
        goto set_EPC;
565 932e71cd aurel32
    case EXCP_CpU:
566 932e71cd aurel32
        cause = 11;
567 932e71cd aurel32
        env->CP0_Cause = (env->CP0_Cause & ~(0x3 << CP0Ca_CE)) |
568 932e71cd aurel32
                         (env->error_code << CP0Ca_CE);
569 932e71cd aurel32
        goto set_EPC;
570 932e71cd aurel32
    case EXCP_OVERFLOW:
571 932e71cd aurel32
        cause = 12;
572 932e71cd aurel32
        goto set_EPC;
573 932e71cd aurel32
    case EXCP_TRAP:
574 932e71cd aurel32
        cause = 13;
575 932e71cd aurel32
        goto set_EPC;
576 932e71cd aurel32
    case EXCP_FPE:
577 932e71cd aurel32
        cause = 15;
578 932e71cd aurel32
        goto set_EPC;
579 932e71cd aurel32
    case EXCP_C2E:
580 932e71cd aurel32
        cause = 18;
581 932e71cd aurel32
        goto set_EPC;
582 932e71cd aurel32
    case EXCP_MDMX:
583 932e71cd aurel32
        cause = 22;
584 932e71cd aurel32
        goto set_EPC;
585 932e71cd aurel32
    case EXCP_DWATCH:
586 932e71cd aurel32
        cause = 23;
587 932e71cd aurel32
        /* XXX: TODO: manage defered watch exceptions */
588 932e71cd aurel32
        goto set_EPC;
589 932e71cd aurel32
    case EXCP_MCHECK:
590 932e71cd aurel32
        cause = 24;
591 932e71cd aurel32
        goto set_EPC;
592 932e71cd aurel32
    case EXCP_THREAD:
593 932e71cd aurel32
        cause = 25;
594 932e71cd aurel32
        goto set_EPC;
595 932e71cd aurel32
    case EXCP_CACHE:
596 932e71cd aurel32
        cause = 30;
597 932e71cd aurel32
        if (env->CP0_Status & (1 << CP0St_BEV)) {
598 932e71cd aurel32
            offset = 0x100;
599 932e71cd aurel32
        } else {
600 932e71cd aurel32
            offset = 0x20000100;
601 932e71cd aurel32
        }
602 0eaef5aa ths
 set_EPC:
603 932e71cd aurel32
        if (!(env->CP0_Status & (1 << CP0St_EXL))) {
604 32188a03 Nathan Froyd
            env->CP0_EPC = exception_resume_pc(env);
605 932e71cd aurel32
            if (env->hflags & MIPS_HFLAG_BMASK) {
606 932e71cd aurel32
                env->CP0_Cause |= (1 << CP0Ca_BD);
607 0eaef5aa ths
            } else {
608 932e71cd aurel32
                env->CP0_Cause &= ~(1 << CP0Ca_BD);
609 0eaef5aa ths
            }
610 932e71cd aurel32
            env->CP0_Status |= (1 << CP0St_EXL);
611 932e71cd aurel32
            env->hflags |= MIPS_HFLAG_64 | MIPS_HFLAG_CP0;
612 932e71cd aurel32
            env->hflags &= ~(MIPS_HFLAG_KSU);
613 6af0bf9c bellard
        }
614 932e71cd aurel32
        env->hflags &= ~MIPS_HFLAG_BMASK;
615 932e71cd aurel32
        if (env->CP0_Status & (1 << CP0St_BEV)) {
616 932e71cd aurel32
            env->active_tc.PC = (int32_t)0xBFC00200;
617 932e71cd aurel32
        } else {
618 932e71cd aurel32
            env->active_tc.PC = (int32_t)(env->CP0_EBase & ~0x3ff);
619 6af0bf9c bellard
        }
620 932e71cd aurel32
        env->active_tc.PC += offset;
621 bbfa8f72 Nathan Froyd
        set_hflags_for_handler(env);
622 932e71cd aurel32
        env->CP0_Cause = (env->CP0_Cause & ~(0x1f << CP0Ca_EC)) | (cause << CP0Ca_EC);
623 932e71cd aurel32
        break;
624 932e71cd aurel32
    default:
625 93fcfe39 aliguori
        qemu_log("Invalid MIPS exception %d. Exiting\n", env->exception_index);
626 932e71cd aurel32
        printf("Invalid MIPS exception %d. Exiting\n", env->exception_index);
627 932e71cd aurel32
        exit(1);
628 932e71cd aurel32
    }
629 93fcfe39 aliguori
    if (qemu_log_enabled() && env->exception_index != EXCP_EXT_INTERRUPT) {
630 93fcfe39 aliguori
        qemu_log("%s: PC " TARGET_FMT_lx " EPC " TARGET_FMT_lx " cause %d\n"
631 932e71cd aurel32
                "    S %08x C %08x A " TARGET_FMT_lx " D " TARGET_FMT_lx "\n",
632 932e71cd aurel32
                __func__, env->active_tc.PC, env->CP0_EPC, cause,
633 932e71cd aurel32
                env->CP0_Status, env->CP0_Cause, env->CP0_BadVAddr,
634 932e71cd aurel32
                env->CP0_DEPC);
635 6af0bf9c bellard
    }
636 932e71cd aurel32
#endif
637 6af0bf9c bellard
    env->exception_index = EXCP_NONE;
638 6af0bf9c bellard
}
639 2ee4aed8 bellard
640 3c7b48b7 Paul Brook
#if !defined(CONFIG_USER_ONLY)
641 7db13fae Andreas Färber
void r4k_invalidate_tlb (CPUMIPSState *env, int idx, int use_extra)
642 2ee4aed8 bellard
{
643 c227f099 Anthony Liguori
    r4k_tlb_t *tlb;
644 3b1c8be4 ths
    target_ulong addr;
645 3b1c8be4 ths
    target_ulong end;
646 3b1c8be4 ths
    uint8_t ASID = env->CP0_EntryHi & 0xFF;
647 3b1c8be4 ths
    target_ulong mask;
648 2ee4aed8 bellard
649 ead9360e ths
    tlb = &env->tlb->mmu.r4k.tlb[idx];
650 f2e9ebef ths
    /* The qemu TLB is flushed when the ASID changes, so no need to
651 2ee4aed8 bellard
       flush these entries again.  */
652 2ee4aed8 bellard
    if (tlb->G == 0 && tlb->ASID != ASID) {
653 2ee4aed8 bellard
        return;
654 2ee4aed8 bellard
    }
655 2ee4aed8 bellard
656 ead9360e ths
    if (use_extra && env->tlb->tlb_in_use < MIPS_TLB_MAX) {
657 2ee4aed8 bellard
        /* For tlbwr, we can shadow the discarded entry into
658 6958549d aurel32
           a new (fake) TLB entry, as long as the guest can not
659 6958549d aurel32
           tell that it's there.  */
660 ead9360e ths
        env->tlb->mmu.r4k.tlb[env->tlb->tlb_in_use] = *tlb;
661 ead9360e ths
        env->tlb->tlb_in_use++;
662 2ee4aed8 bellard
        return;
663 2ee4aed8 bellard
    }
664 2ee4aed8 bellard
665 3b1c8be4 ths
    /* 1k pages are not supported. */
666 f2e9ebef ths
    mask = tlb->PageMask | ~(TARGET_PAGE_MASK << 1);
667 3b1c8be4 ths
    if (tlb->V0) {
668 f2e9ebef ths
        addr = tlb->VPN & ~mask;
669 d26bc211 ths
#if defined(TARGET_MIPS64)
670 e034e2c3 ths
        if (addr >= (0xFFFFFFFF80000000ULL & env->SEGMask)) {
671 100ce988 ths
            addr |= 0x3FFFFF0000000000ULL;
672 100ce988 ths
        }
673 100ce988 ths
#endif
674 3b1c8be4 ths
        end = addr | (mask >> 1);
675 3b1c8be4 ths
        while (addr < end) {
676 3b1c8be4 ths
            tlb_flush_page (env, addr);
677 3b1c8be4 ths
            addr += TARGET_PAGE_SIZE;
678 3b1c8be4 ths
        }
679 3b1c8be4 ths
    }
680 3b1c8be4 ths
    if (tlb->V1) {
681 f2e9ebef ths
        addr = (tlb->VPN & ~mask) | ((mask >> 1) + 1);
682 d26bc211 ths
#if defined(TARGET_MIPS64)
683 e034e2c3 ths
        if (addr >= (0xFFFFFFFF80000000ULL & env->SEGMask)) {
684 100ce988 ths
            addr |= 0x3FFFFF0000000000ULL;
685 100ce988 ths
        }
686 100ce988 ths
#endif
687 3b1c8be4 ths
        end = addr | mask;
688 53715e48 ths
        while (addr - 1 < end) {
689 3b1c8be4 ths
            tlb_flush_page (env, addr);
690 3b1c8be4 ths
            addr += TARGET_PAGE_SIZE;
691 3b1c8be4 ths
        }
692 3b1c8be4 ths
    }
693 2ee4aed8 bellard
}
694 3c7b48b7 Paul Brook
#endif