|
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 /* no MMU emulation */ |
|
40 int no_mmu_map_address (CPUState *env, target_ulong *physical, int *prot, |
|
41 target_ulong address, int rw, int access_type) |
|
42 { |
|
43 *physical = address; |
|
44 *prot = PAGE_READ | PAGE_WRITE; |
|
45 return TLBRET_MATCH; |
|
46 } |
|
47 |
|
48 /* fixed mapping MMU emulation */ |
|
49 int fixed_mmu_map_address (CPUState *env, target_ulong *physical, int *prot, |
|
50 target_ulong address, int rw, int access_type) |
|
51 { |
|
52 if (address <= (int32_t)0x7FFFFFFFUL) { |
|
53 if (!(env->CP0_Status & (1 << CP0St_ERL))) |
|
54 *physical = address + 0x40000000UL; |
|
55 else |
|
56 *physical = address; |
|
57 } else if (address <= (int32_t)0xBFFFFFFFUL) |
|
58 *physical = address & 0x1FFFFFFF; |
|
59 else |
|
60 *physical = address; |
|
61 |
|
62 *prot = PAGE_READ | PAGE_WRITE; |
|
63 return TLBRET_MATCH; |
|
64 } |
|
65 |
|
66 /* MIPS32/MIPS64 R4000-style MMU emulation */ |
|
67 int r4k_map_address (CPUState *env, target_ulong *physical, int *prot, |
|
68 target_ulong address, int rw, int access_type) |
|
69 { |
|
70 uint8_t ASID = env->CP0_EntryHi & 0xFF; |
|
71 int i; |
|
72 |
|
73 for (i = 0; i < env->tlb->tlb_in_use; i++) { |
|
74 r4k_tlb_t *tlb = &env->tlb->mmu.r4k.tlb[i]; |
|
75 /* 1k pages are not supported. */ |
|
76 target_ulong mask = tlb->PageMask | ~(TARGET_PAGE_MASK << 1); |
|
77 target_ulong tag = address & ~mask; |
|
78 target_ulong VPN = tlb->VPN & ~mask; |
|
79 #if defined(TARGET_MIPS64) |
|
80 tag &= env->SEGMask; |
|
81 #endif |
|
82 |
|
83 /* Check ASID, virtual page number & size */ |
|
84 if ((tlb->G == 1 || tlb->ASID == ASID) && VPN == tag) { |
|
85 /* TLB match */ |
|
86 int n = !!(address & mask & ~(mask >> 1)); |
|
87 /* Check access rights */ |
|
88 if (!(n ? tlb->V1 : tlb->V0)) |
|
89 return TLBRET_INVALID; |
|
90 if (rw == 0 || (n ? tlb->D1 : tlb->D0)) { |
|
91 *physical = tlb->PFN[n] | (address & (mask >> 1)); |
|
92 *prot = PAGE_READ; |
|
93 if (n ? tlb->D1 : tlb->D0) |
|
94 *prot |= PAGE_WRITE; |
|
95 return TLBRET_MATCH; |
|
96 } |
|
97 return TLBRET_DIRTY; |
|
98 } |
|
99 } |
|
100 return TLBRET_NOMATCH; |
|
101 } |
|
102 |
|
103 static int get_physical_address (CPUState *env, target_ulong *physical, |
|
104 int *prot, target_ulong address, |
|
105 int rw, int access_type) |
|
106 { |
|
107 /* User mode can only access useg/xuseg */ |
|
108 int user_mode = (env->hflags & MIPS_HFLAG_MODE) == MIPS_HFLAG_UM; |
|
109 int supervisor_mode = (env->hflags & MIPS_HFLAG_MODE) == MIPS_HFLAG_SM; |
|
110 int kernel_mode = !user_mode && !supervisor_mode; |
|
111 #if defined(TARGET_MIPS64) |
|
112 int UX = (env->CP0_Status & (1 << CP0St_UX)) != 0; |
|
113 int SX = (env->CP0_Status & (1 << CP0St_SX)) != 0; |
|
114 int KX = (env->CP0_Status & (1 << CP0St_KX)) != 0; |
|
115 #endif |
|
116 int ret = TLBRET_MATCH; |
|
117 |
|
118 #if 0 |
|
119 if (logfile) { |
|
120 fprintf(logfile, "user mode %d h %08x\n", |
|
121 user_mode, env->hflags); |
|
122 } |
|
123 #endif |
|
124 |
|
125 if (address <= (int32_t)0x7FFFFFFFUL) { |
|
126 /* useg */ |
|
127 if (env->CP0_Status & (1 << CP0St_ERL)) { |
|
128 *physical = address & 0xFFFFFFFF; |
|
129 *prot = PAGE_READ | PAGE_WRITE; |
|
130 } else { |
|
131 ret = env->tlb->map_address(env, physical, prot, address, rw, access_type); |
|
132 } |
|
133 #if defined(TARGET_MIPS64) |
|
134 } else if (address < 0x4000000000000000ULL) { |
|
135 /* xuseg */ |
|
136 if (UX && address <= (0x3FFFFFFFFFFFFFFFULL & env->SEGMask)) { |
|
137 ret = env->tlb->map_address(env, physical, prot, address, rw, access_type); |
|
138 } else { |
|
139 ret = TLBRET_BADADDR; |
|
140 } |
|
141 } else if (address < 0x8000000000000000ULL) { |
|
142 /* xsseg */ |
|
143 if ((supervisor_mode || kernel_mode) && |
|
144 SX && address <= (0x7FFFFFFFFFFFFFFFULL & env->SEGMask)) { |
|
145 ret = env->tlb->map_address(env, physical, prot, address, rw, access_type); |
|
146 } else { |
|
147 ret = TLBRET_BADADDR; |
|
148 } |
|
149 } else if (address < 0xC000000000000000ULL) { |
|
150 /* xkphys */ |
|
151 if (kernel_mode && KX && |
|
152 (address & 0x07FFFFFFFFFFFFFFULL) <= env->PAMask) { |
|
153 *physical = address & env->PAMask; |
|
154 *prot = PAGE_READ | PAGE_WRITE; |
|
155 } else { |
|
156 ret = TLBRET_BADADDR; |
|
157 } |
|
158 } else if (address < 0xFFFFFFFF80000000ULL) { |
|
159 /* xkseg */ |
|
160 if (kernel_mode && KX && |
|
161 address <= (0xFFFFFFFF7FFFFFFFULL & env->SEGMask)) { |
|
162 ret = env->tlb->map_address(env, physical, prot, address, rw, access_type); |
|
163 } else { |
|
164 ret = TLBRET_BADADDR; |
|
165 } |
|
166 #endif |
|
167 } else if (address < (int32_t)0xA0000000UL) { |
|
168 /* kseg0 */ |
|
169 if (kernel_mode) { |
|
170 *physical = address - (int32_t)0x80000000UL; |
|
171 *prot = PAGE_READ | PAGE_WRITE; |
|
172 } else { |
|
173 ret = TLBRET_BADADDR; |
|
174 } |
|
175 } else if (address < (int32_t)0xC0000000UL) { |
|
176 /* kseg1 */ |
|
177 if (kernel_mode) { |
|
178 *physical = address - (int32_t)0xA0000000UL; |
|
179 *prot = PAGE_READ | PAGE_WRITE; |
|
180 } else { |
|
181 ret = TLBRET_BADADDR; |
|
182 } |
|
183 } else if (address < (int32_t)0xE0000000UL) { |
|
184 /* sseg (kseg2) */ |
|
185 if (supervisor_mode || kernel_mode) { |
|
186 ret = env->tlb->map_address(env, physical, prot, address, rw, access_type); |
|
187 } else { |
|
188 ret = TLBRET_BADADDR; |
|
189 } |
|
190 } else { |
|
191 /* kseg3 */ |
|
192 /* XXX: debug segment is not emulated */ |
|
193 if (kernel_mode) { |
|
194 ret = env->tlb->map_address(env, physical, prot, address, rw, access_type); |
|
195 } else { |
|
196 ret = TLBRET_BADADDR; |
|
197 } |
|
198 } |
|
199 #if 0 |
|
200 if (logfile) { |
|
201 fprintf(logfile, TARGET_FMT_lx " %d %d => " TARGET_FMT_lx " %d (%d)\n", |
|
202 address, rw, access_type, *physical, *prot, ret); |
|
203 } |
|
204 #endif |
|
205 |
|
206 return ret; |
|
207 } |
|
208 |
|
209 target_phys_addr_t cpu_get_phys_page_debug(CPUState *env, target_ulong addr) |
|
210 { |
|
211 if (env->user_mode_only) |
|
212 return addr; |
|
213 else { |
|
214 target_ulong phys_addr; |
|
215 int prot; |
|
216 |
|
217 if (get_physical_address(env, &phys_addr, &prot, addr, 0, ACCESS_INT) != 0) |
|
218 return -1; |
|
219 return phys_addr; |
|
220 } |
|
221 } |
|
222 |
|
223 int cpu_mips_handle_mmu_fault (CPUState *env, target_ulong address, int rw, |
|
224 int mmu_idx, int is_softmmu) |
|
225 { |
|
226 target_ulong physical; |
|
227 int prot; |
|
228 int exception = 0, error_code = 0; |
|
229 int access_type; |
|
230 int ret = 0; |
|
231 |
|
232 if (logfile) { |
|
233 #if 0 |
|
234 cpu_dump_state(env, logfile, fprintf, 0); |
|
235 #endif |
|
236 fprintf(logfile, "%s pc " TARGET_FMT_lx " ad " TARGET_FMT_lx " rw %d mmu_idx %d smmu %d\n", |
|
237 __func__, env->active_tc.PC, address, rw, mmu_idx, is_softmmu); |
|
238 } |
|
239 |
|
240 rw &= 1; |
|
241 |
|
242 /* data access */ |
|
243 /* XXX: put correct access by using cpu_restore_state() |
|
244 correctly */ |
|
245 access_type = ACCESS_INT; |
|
246 if (env->user_mode_only) { |
|
247 /* user mode only emulation */ |
|
248 ret = TLBRET_NOMATCH; |
|
249 goto do_fault; |
|
250 } |
|
251 ret = get_physical_address(env, &physical, &prot, |
|
252 address, rw, access_type); |
|
253 if (logfile) { |
|
254 fprintf(logfile, "%s address=" TARGET_FMT_lx " ret %d physical " TARGET_FMT_lx " prot %d\n", |
|
255 __func__, address, ret, physical, prot); |
|
256 } |
|
257 if (ret == TLBRET_MATCH) { |
|
258 ret = tlb_set_page(env, address & TARGET_PAGE_MASK, |
|
259 physical & TARGET_PAGE_MASK, prot, |
|
260 mmu_idx, is_softmmu); |
|
261 } else if (ret < 0) { |
|
262 do_fault: |
|
263 switch (ret) { |
|
264 default: |
|
265 case TLBRET_BADADDR: |
|
266 /* Reference to kernel address from user mode or supervisor mode */ |
|
267 /* Reference to supervisor address from user mode */ |
|
268 if (rw) |
|
269 exception = EXCP_AdES; |
|
270 else |
|
271 exception = EXCP_AdEL; |
|
272 break; |
|
273 case TLBRET_NOMATCH: |
|
274 /* No TLB match for a mapped address */ |
|
275 if (rw) |
|
276 exception = EXCP_TLBS; |
|
277 else |
|
278 exception = EXCP_TLBL; |
|
279 error_code = 1; |
|
280 break; |
|
281 case TLBRET_INVALID: |
|
282 /* TLB match with no valid bit */ |
|
283 if (rw) |
|
284 exception = EXCP_TLBS; |
|
285 else |
|
286 exception = EXCP_TLBL; |
|
287 break; |
|
288 case TLBRET_DIRTY: |
|
289 /* TLB match but 'D' bit is cleared */ |
|
290 exception = EXCP_LTLBL; |
|
291 break; |
|
292 |
|
293 } |
|
294 /* Raise exception */ |
|
295 env->CP0_BadVAddr = address; |
|
296 env->CP0_Context = (env->CP0_Context & ~0x007fffff) | |
|
297 ((address >> 9) & 0x007ffff0); |
|
298 env->CP0_EntryHi = |
|
299 (env->CP0_EntryHi & 0xFF) | (address & (TARGET_PAGE_MASK << 1)); |
|
300 #if defined(TARGET_MIPS64) |
|
301 env->CP0_EntryHi &= env->SEGMask; |
|
302 env->CP0_XContext = (env->CP0_XContext & ((~0ULL) << (env->SEGBITS - 7))) | |
|
303 ((address & 0xC00000000000ULL) >> (55 - env->SEGBITS)) | |
|
304 ((address & ((1ULL << env->SEGBITS) - 1) & 0xFFFFFFFFFFFFE000ULL) >> 9); |
|
305 #endif |
|
306 env->exception_index = exception; |
|
307 env->error_code = error_code; |
|
308 ret = 1; |
|
309 } |
|
310 |
|
311 return ret; |
|
312 } |
|
313 |
|
314 static const char * const excp_names[EXCP_LAST + 1] = { |
|
315 [EXCP_RESET] = "reset", |
|
316 [EXCP_SRESET] = "soft reset", |
|
317 [EXCP_DSS] = "debug single step", |
|
318 [EXCP_DINT] = "debug interrupt", |
|
319 [EXCP_NMI] = "non-maskable interrupt", |
|
320 [EXCP_MCHECK] = "machine check", |
|
321 [EXCP_EXT_INTERRUPT] = "interrupt", |
|
322 [EXCP_DFWATCH] = "deferred watchpoint", |
|
323 [EXCP_DIB] = "debug instruction breakpoint", |
|
324 [EXCP_IWATCH] = "instruction fetch watchpoint", |
|
325 [EXCP_AdEL] = "address error load", |
|
326 [EXCP_AdES] = "address error store", |
|
327 [EXCP_TLBF] = "TLB refill", |
|
328 [EXCP_IBE] = "instruction bus error", |
|
329 [EXCP_DBp] = "debug breakpoint", |
|
330 [EXCP_SYSCALL] = "syscall", |
|
331 [EXCP_BREAK] = "break", |
|
332 [EXCP_CpU] = "coprocessor unusable", |
|
333 [EXCP_RI] = "reserved instruction", |
|
334 [EXCP_OVERFLOW] = "arithmetic overflow", |
|
335 [EXCP_TRAP] = "trap", |
|
336 [EXCP_FPE] = "floating point", |
|
337 [EXCP_DDBS] = "debug data break store", |
|
338 [EXCP_DWATCH] = "data watchpoint", |
|
339 [EXCP_LTLBL] = "TLB modify", |
|
340 [EXCP_TLBL] = "TLB load", |
|
341 [EXCP_TLBS] = "TLB store", |
|
342 [EXCP_DBE] = "data bus error", |
|
343 [EXCP_DDBL] = "debug data break load", |
|
344 [EXCP_THREAD] = "thread", |
|
345 [EXCP_MDMX] = "MDMX", |
|
346 [EXCP_C2E] = "precise coprocessor 2", |
|
347 [EXCP_CACHE] = "cache error", |
|
348 }; |
|
349 |
|
350 void do_interrupt (CPUState *env) |
|
351 { |
|
352 if (!env->user_mode_only) { |
|
353 target_ulong offset; |
|
354 int cause = -1; |
|
355 const char *name; |
|
356 |
|
357 if (logfile && env->exception_index != EXCP_EXT_INTERRUPT) { |
|
358 if (env->exception_index < 0 || env->exception_index > EXCP_LAST) |
|
359 name = "unknown"; |
|
360 else |
|
361 name = excp_names[env->exception_index]; |
|
362 |
|
363 fprintf(logfile, "%s enter: PC " TARGET_FMT_lx " EPC " TARGET_FMT_lx " %s exception\n", |
|
364 __func__, env->active_tc.PC, env->CP0_EPC, name); |
|
365 } |
|
366 if (env->exception_index == EXCP_EXT_INTERRUPT && |
|
367 (env->hflags & MIPS_HFLAG_DM)) |
|
368 env->exception_index = EXCP_DINT; |
|
369 offset = 0x180; |
|
370 switch (env->exception_index) { |
|
371 case EXCP_DSS: |
|
372 env->CP0_Debug |= 1 << CP0DB_DSS; |
|
373 /* Debug single step cannot be raised inside a delay slot and |
|
374 resume will always occur on the next instruction |
|
375 (but we assume the pc has always been updated during |
|
376 code translation). */ |
|
377 env->CP0_DEPC = env->active_tc.PC; |
|
378 goto enter_debug_mode; |
|
379 case EXCP_DINT: |
|
380 env->CP0_Debug |= 1 << CP0DB_DINT; |
|
381 goto set_DEPC; |
|
382 case EXCP_DIB: |
|
383 env->CP0_Debug |= 1 << CP0DB_DIB; |
|
384 goto set_DEPC; |
|
385 case EXCP_DBp: |
|
386 env->CP0_Debug |= 1 << CP0DB_DBp; |
|
387 goto set_DEPC; |
|
388 case EXCP_DDBS: |
|
389 env->CP0_Debug |= 1 << CP0DB_DDBS; |
|
390 goto set_DEPC; |
|
391 case EXCP_DDBL: |
|
392 env->CP0_Debug |= 1 << CP0DB_DDBL; |
|
393 set_DEPC: |
|
394 if (env->hflags & MIPS_HFLAG_BMASK) { |
|
395 /* If the exception was raised from a delay slot, |
|
396 come back to the jump. */ |
|
397 env->CP0_DEPC = env->active_tc.PC - 4; |
|
398 env->hflags &= ~MIPS_HFLAG_BMASK; |
|
399 } else { |
|
400 env->CP0_DEPC = env->active_tc.PC; |
|
401 } |
|
402 enter_debug_mode: |
|
403 env->hflags |= MIPS_HFLAG_DM | MIPS_HFLAG_64 | MIPS_HFLAG_CP0; |
|
404 env->hflags &= ~(MIPS_HFLAG_KSU); |
|
405 /* EJTAG probe trap enable is not implemented... */ |
|
406 if (!(env->CP0_Status & (1 << CP0St_EXL))) |
|
407 env->CP0_Cause &= ~(1 << CP0Ca_BD); |
|
408 env->active_tc.PC = (int32_t)0xBFC00480; |
|
409 break; |
|
410 case EXCP_RESET: |
|
411 cpu_reset(env); |
|
412 break; |
|
413 case EXCP_SRESET: |
|
414 env->CP0_Status |= (1 << CP0St_SR); |
|
415 memset(env->CP0_WatchLo, 0, sizeof(*env->CP0_WatchLo)); |
|
416 goto set_error_EPC; |
|
417 case EXCP_NMI: |
|
418 env->CP0_Status |= (1 << CP0St_NMI); |
|
419 set_error_EPC: |
|
420 if (env->hflags & MIPS_HFLAG_BMASK) { |
|
421 /* If the exception was raised from a delay slot, |
|
422 come back to the jump. */ |
|
423 env->CP0_ErrorEPC = env->active_tc.PC - 4; |
|
424 env->hflags &= ~MIPS_HFLAG_BMASK; |
|
425 } else { |
|
426 env->CP0_ErrorEPC = env->active_tc.PC; |
|
427 } |
|
428 env->CP0_Status |= (1 << CP0St_ERL) | (1 << CP0St_BEV); |
|
429 env->hflags |= MIPS_HFLAG_64 | MIPS_HFLAG_CP0; |
|
430 env->hflags &= ~(MIPS_HFLAG_KSU); |
|
431 if (!(env->CP0_Status & (1 << CP0St_EXL))) |
|
432 env->CP0_Cause &= ~(1 << CP0Ca_BD); |
|
433 env->active_tc.PC = (int32_t)0xBFC00000; |
|
434 break; |
|
435 case EXCP_EXT_INTERRUPT: |
|
436 cause = 0; |
|
437 if (env->CP0_Cause & (1 << CP0Ca_IV)) |
|
438 offset = 0x200; |
|
439 goto set_EPC; |
|
440 case EXCP_LTLBL: |
|
441 cause = 1; |
|
442 goto set_EPC; |
|
443 case EXCP_TLBL: |
|
444 cause = 2; |
|
445 if (env->error_code == 1 && !(env->CP0_Status & (1 << CP0St_EXL))) { |
|
446 #if defined(TARGET_MIPS64) |
|
447 int R = env->CP0_BadVAddr >> 62; |
|
448 int UX = (env->CP0_Status & (1 << CP0St_UX)) != 0; |
|
449 int SX = (env->CP0_Status & (1 << CP0St_SX)) != 0; |
|
450 int KX = (env->CP0_Status & (1 << CP0St_KX)) != 0; |
|
451 |
|
452 if ((R == 0 && UX) || (R == 1 && SX) || (R == 3 && KX)) |
|
453 offset = 0x080; |
|
454 else |
|
455 #endif |
|
456 offset = 0x000; |
|
457 } |
|
458 goto set_EPC; |
|
459 case EXCP_TLBS: |
|
460 cause = 3; |
|
461 if (env->error_code == 1 && !(env->CP0_Status & (1 << CP0St_EXL))) { |
|
462 #if defined(TARGET_MIPS64) |
|
463 int R = env->CP0_BadVAddr >> 62; |
|
464 int UX = (env->CP0_Status & (1 << CP0St_UX)) != 0; |
|
465 int SX = (env->CP0_Status & (1 << CP0St_SX)) != 0; |
|
466 int KX = (env->CP0_Status & (1 << CP0St_KX)) != 0; |
|
467 |
|
468 if ((R == 0 && UX) || (R == 1 && SX) || (R == 3 && KX)) |
|
469 offset = 0x080; |
|
470 else |
|
471 #endif |
|
472 offset = 0x000; |
|
473 } |
|
474 goto set_EPC; |
|
475 case EXCP_AdEL: |
|
476 cause = 4; |
|
477 goto set_EPC; |
|
478 case EXCP_AdES: |
|
479 cause = 5; |
|
480 goto set_EPC; |
|
481 case EXCP_IBE: |
|
482 cause = 6; |
|
483 goto set_EPC; |
|
484 case EXCP_DBE: |
|
485 cause = 7; |
|
486 goto set_EPC; |
|
487 case EXCP_SYSCALL: |
|
488 cause = 8; |
|
489 goto set_EPC; |
|
490 case EXCP_BREAK: |
|
491 cause = 9; |
|
492 goto set_EPC; |
|
493 case EXCP_RI: |
|
494 cause = 10; |
|
495 goto set_EPC; |
|
496 case EXCP_CpU: |
|
497 cause = 11; |
|
498 env->CP0_Cause = (env->CP0_Cause & ~(0x3 << CP0Ca_CE)) | |
|
499 (env->error_code << CP0Ca_CE); |
|
500 goto set_EPC; |
|
501 case EXCP_OVERFLOW: |
|
502 cause = 12; |
|
503 goto set_EPC; |
|
504 case EXCP_TRAP: |
|
505 cause = 13; |
|
506 goto set_EPC; |
|
507 case EXCP_FPE: |
|
508 cause = 15; |
|
509 goto set_EPC; |
|
510 case EXCP_C2E: |
|
511 cause = 18; |
|
512 goto set_EPC; |
|
513 case EXCP_MDMX: |
|
514 cause = 22; |
|
515 goto set_EPC; |
|
516 case EXCP_DWATCH: |
|
517 cause = 23; |
|
518 /* XXX: TODO: manage defered watch exceptions */ |
|
519 goto set_EPC; |
|
520 case EXCP_MCHECK: |
|
521 cause = 24; |
|
522 goto set_EPC; |
|
523 case EXCP_THREAD: |
|
524 cause = 25; |
|
525 goto set_EPC; |
|
526 case EXCP_CACHE: |
|
527 cause = 30; |
|
528 if (env->CP0_Status & (1 << CP0St_BEV)) { |
|
529 offset = 0x100; |
|
530 } else { |
|
531 offset = 0x20000100; |
|
532 } |
|
533 set_EPC: |
|
534 if (!(env->CP0_Status & (1 << CP0St_EXL))) { |
|
535 if (env->hflags & MIPS_HFLAG_BMASK) { |
|
536 /* If the exception was raised from a delay slot, |
|
537 come back to the jump. */ |
|
538 env->CP0_EPC = env->active_tc.PC - 4; |
|
539 env->CP0_Cause |= (1 << CP0Ca_BD); |
|
540 } else { |
|
541 env->CP0_EPC = env->active_tc.PC; |
|
542 env->CP0_Cause &= ~(1 << CP0Ca_BD); |
|
543 } |
|
544 env->CP0_Status |= (1 << CP0St_EXL); |
|
545 env->hflags |= MIPS_HFLAG_64 | MIPS_HFLAG_CP0; |
|
546 env->hflags &= ~(MIPS_HFLAG_KSU); |
|
547 } |
|
548 env->hflags &= ~MIPS_HFLAG_BMASK; |
|
549 if (env->CP0_Status & (1 << CP0St_BEV)) { |
|
550 env->active_tc.PC = (int32_t)0xBFC00200; |
|
551 } else { |
|
552 env->active_tc.PC = (int32_t)(env->CP0_EBase & ~0x3ff); |
|
553 } |
|
554 env->active_tc.PC += offset; |
|
555 env->CP0_Cause = (env->CP0_Cause & ~(0x1f << CP0Ca_EC)) | (cause << CP0Ca_EC); |
|
556 break; |
|
557 default: |
|
558 if (logfile) { |
|
559 fprintf(logfile, "Invalid MIPS exception %d. Exiting\n", |
|
560 env->exception_index); |
|
561 } |
|
562 printf("Invalid MIPS exception %d. Exiting\n", env->exception_index); |
|
563 exit(1); |
|
564 } |
|
565 if (logfile && env->exception_index != EXCP_EXT_INTERRUPT) { |
|
566 fprintf(logfile, "%s: PC " TARGET_FMT_lx " EPC " TARGET_FMT_lx " cause %d\n" |
|
567 " S %08x C %08x A " TARGET_FMT_lx " D " TARGET_FMT_lx "\n", |
|
568 __func__, env->active_tc.PC, env->CP0_EPC, cause, |
|
569 env->CP0_Status, env->CP0_Cause, env->CP0_BadVAddr, |
|
570 env->CP0_DEPC); |
|
571 } |
|
572 } |
|
573 env->exception_index = EXCP_NONE; |
|
574 } |
|
575 |
|
576 void r4k_invalidate_tlb (CPUState *env, int idx, int use_extra) |
|
577 { |
|
578 r4k_tlb_t *tlb; |
|
579 target_ulong addr; |
|
580 target_ulong end; |
|
581 uint8_t ASID = env->CP0_EntryHi & 0xFF; |
|
582 target_ulong mask; |
|
583 |
|
584 tlb = &env->tlb->mmu.r4k.tlb[idx]; |
|
585 /* The qemu TLB is flushed when the ASID changes, so no need to |
|
586 flush these entries again. */ |
|
587 if (tlb->G == 0 && tlb->ASID != ASID) { |
|
588 return; |
|
589 } |
|
590 |
|
591 if (use_extra && env->tlb->tlb_in_use < MIPS_TLB_MAX) { |
|
592 /* For tlbwr, we can shadow the discarded entry into |
|
593 a new (fake) TLB entry, as long as the guest can not |
|
594 tell that it's there. */ |
|
595 env->tlb->mmu.r4k.tlb[env->tlb->tlb_in_use] = *tlb; |
|
596 env->tlb->tlb_in_use++; |
|
597 return; |
|
598 } |
|
599 |
|
600 /* 1k pages are not supported. */ |
|
601 mask = tlb->PageMask | ~(TARGET_PAGE_MASK << 1); |
|
602 if (tlb->V0) { |
|
603 addr = tlb->VPN & ~mask; |
|
604 #if defined(TARGET_MIPS64) |
|
605 if (addr >= (0xFFFFFFFF80000000ULL & env->SEGMask)) { |
|
606 addr |= 0x3FFFFF0000000000ULL; |
|
607 } |
|
608 #endif |
|
609 end = addr | (mask >> 1); |
|
610 while (addr < end) { |
|
611 tlb_flush_page (env, addr); |
|
612 addr += TARGET_PAGE_SIZE; |
|
613 } |
|
614 } |
|
615 if (tlb->V1) { |
|
616 addr = (tlb->VPN & ~mask) | ((mask >> 1) + 1); |
|
617 #if defined(TARGET_MIPS64) |
|
618 if (addr >= (0xFFFFFFFF80000000ULL & env->SEGMask)) { |
|
619 addr |= 0x3FFFFF0000000000ULL; |
|
620 } |
|
621 #endif |
|
622 end = addr | mask; |
|
623 while (addr - 1 < end) { |
|
624 tlb_flush_page (env, addr); |
|
625 addr += TARGET_PAGE_SIZE; |
|
626 } |
|
627 } |
|
628 } |