]> git.proxmox.com Git - qemu.git/blame - target-mips/op_helper.c
dump all mips insn (Thiemo Seufer)
[qemu.git] / target-mips / op_helper.c
CommitLineData
6af0bf9c
FB
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 */
6af0bf9c
FB
20#include "exec.h"
21
22#define MIPS_DEBUG_DISAS
23
4ad40f36
FB
24#define GETPC() (__builtin_return_address(0))
25
6af0bf9c
FB
26/*****************************************************************************/
27/* Exceptions processing helpers */
28void cpu_loop_exit(void)
29{
30 longjmp(env->jmp_env, 1);
31}
32
6af0bf9c
FB
33void do_raise_exception_err (uint32_t exception, int error_code)
34{
35#if 1
36 if (logfile && exception < 0x100)
37 fprintf(logfile, "%s: %d %d\n", __func__, exception, error_code);
38#endif
39 env->exception_index = exception;
40 env->error_code = error_code;
41 T0 = 0;
42 cpu_loop_exit();
43}
44
6af0bf9c
FB
45void do_raise_exception (uint32_t exception)
46{
47 do_raise_exception_err(exception, 0);
48}
49
4ad40f36
FB
50void do_restore_state (void *pc_ptr)
51{
52 TranslationBlock *tb;
53 unsigned long pc = (unsigned long) pc_ptr;
54
55 tb = tb_find_pc (pc);
56 cpu_restore_state (tb, env, pc, NULL);
57}
58
59void do_raise_exception_direct (uint32_t exception)
60{
61 do_restore_state (GETPC ());
62 do_raise_exception_err (exception, 0);
63}
64
6af0bf9c
FB
65#define MEMSUFFIX _raw
66#include "op_helper_mem.c"
67#undef MEMSUFFIX
68#if !defined(CONFIG_USER_ONLY)
69#define MEMSUFFIX _user
70#include "op_helper_mem.c"
71#undef MEMSUFFIX
72#define MEMSUFFIX _kernel
73#include "op_helper_mem.c"
74#undef MEMSUFFIX
75#endif
76
77/* 64 bits arithmetic for 32 bits hosts */
78#if (HOST_LONG_BITS == 32)
79static inline uint64_t get_HILO (void)
80{
81 return ((uint64_t)env->HI << 32) | (uint64_t)env->LO;
82}
83
84static inline void set_HILO (uint64_t HILO)
85{
86 env->LO = HILO & 0xFFFFFFFF;
87 env->HI = HILO >> 32;
88}
89
90void do_mult (void)
91{
4ad40f36 92 set_HILO((int64_t)(int32_t)T0 * (int64_t)(int32_t)T1);
6af0bf9c
FB
93}
94
95void do_multu (void)
96{
97 set_HILO((uint64_t)T0 * (uint64_t)T1);
98}
99
100void do_madd (void)
101{
102 int64_t tmp;
103
4ad40f36 104 tmp = ((int64_t)(int32_t)T0 * (int64_t)(int32_t)T1);
6af0bf9c
FB
105 set_HILO((int64_t)get_HILO() + tmp);
106}
107
108void do_maddu (void)
109{
110 uint64_t tmp;
111
112 tmp = ((uint64_t)T0 * (uint64_t)T1);
113 set_HILO(get_HILO() + tmp);
114}
115
116void do_msub (void)
117{
118 int64_t tmp;
119
4ad40f36 120 tmp = ((int64_t)(int32_t)T0 * (int64_t)(int32_t)T1);
6af0bf9c
FB
121 set_HILO((int64_t)get_HILO() - tmp);
122}
123
124void do_msubu (void)
125{
126 uint64_t tmp;
127
128 tmp = ((uint64_t)T0 * (uint64_t)T1);
129 set_HILO(get_HILO() - tmp);
130}
131#endif
132
048f6b4d
FB
133#if defined(CONFIG_USER_ONLY)
134void do_mfc0 (int reg, int sel)
135{
136 cpu_abort(env, "mfc0 reg=%d sel=%d\n", reg, sel);
137}
138void do_mtc0 (int reg, int sel)
139{
140 cpu_abort(env, "mtc0 reg=%d sel=%d\n", reg, sel);
141}
142
143void do_tlbwi (void)
144{
145 cpu_abort(env, "tlbwi\n");
146}
147
148void do_tlbwr (void)
149{
150 cpu_abort(env, "tlbwr\n");
151}
152
153void do_tlbp (void)
154{
155 cpu_abort(env, "tlbp\n");
156}
157
158void do_tlbr (void)
159{
160 cpu_abort(env, "tlbr\n");
161}
162#else
163
6af0bf9c 164/* CP0 helpers */
6af0bf9c
FB
165void do_mfc0 (int reg, int sel)
166{
167 const unsigned char *rn;
168
169 if (sel != 0 && reg != 16 && reg != 28) {
170 rn = "invalid";
171 goto print;
172 }
173 switch (reg) {
174 case 0:
175 T0 = env->CP0_index;
176 rn = "Index";
177 break;
178 case 1:
179 T0 = cpu_mips_get_random(env);
180 rn = "Random";
181 break;
182 case 2:
183 T0 = env->CP0_EntryLo0;
184 rn = "EntryLo0";
185 break;
186 case 3:
187 T0 = env->CP0_EntryLo1;
188 rn = "EntryLo1";
189 break;
190 case 4:
191 T0 = env->CP0_Context;
192 rn = "Context";
193 break;
194 case 5:
195 T0 = env->CP0_PageMask;
196 rn = "PageMask";
197 break;
198 case 6:
199 T0 = env->CP0_Wired;
200 rn = "Wired";
201 break;
202 case 8:
203 T0 = env->CP0_BadVAddr;
204 rn = "BadVaddr";
205 break;
206 case 9:
207 T0 = cpu_mips_get_count(env);
208 rn = "Count";
209 break;
210 case 10:
211 T0 = env->CP0_EntryHi;
212 rn = "EntryHi";
213 break;
214 case 11:
215 T0 = env->CP0_Compare;
216 rn = "Compare";
217 break;
218 case 12:
219 T0 = env->CP0_Status;
220 if (env->hflags & MIPS_HFLAG_UM)
90b37806 221 T0 |= (1 << CP0St_UM);
6af0bf9c 222 if (env->hflags & MIPS_HFLAG_ERL)
90b37806 223 T0 |= (1 << CP0St_ERL);
6af0bf9c 224 if (env->hflags & MIPS_HFLAG_EXL)
90b37806 225 T0 |= (1 << CP0St_EXL);
6af0bf9c
FB
226 rn = "Status";
227 break;
228 case 13:
229 T0 = env->CP0_Cause;
230 rn = "Cause";
231 break;
232 case 14:
233 T0 = env->CP0_EPC;
234 rn = "EPC";
235 break;
236 case 15:
237 T0 = env->CP0_PRid;
238 rn = "PRid";
239 break;
240 case 16:
241 switch (sel) {
242 case 0:
243 T0 = env->CP0_Config0;
244 rn = "Config";
245 break;
246 case 1:
247 T0 = env->CP0_Config1;
248 rn = "Config1";
249 break;
250 default:
251 rn = "Unknown config register";
252 break;
253 }
254 break;
255 case 17:
256 T0 = env->CP0_LLAddr >> 4;
257 rn = "LLAddr";
258 break;
259 case 18:
260 T0 = env->CP0_WatchLo;
261 rn = "WatchLo";
262 break;
263 case 19:
264 T0 = env->CP0_WatchHi;
265 rn = "WatchHi";
266 break;
267 case 23:
268 T0 = env->CP0_Debug;
269 if (env->hflags & MIPS_HFLAG_DM)
270 T0 |= 1 << CP0DB_DM;
271 rn = "Debug";
272 break;
273 case 24:
274 T0 = env->CP0_DEPC;
275 rn = "DEPC";
276 break;
277 case 28:
278 switch (sel) {
279 case 0:
280 T0 = env->CP0_TagLo;
281 rn = "TagLo";
282 break;
283 case 1:
284 T0 = env->CP0_DataLo;
285 rn = "DataLo";
286 break;
287 default:
288 rn = "unknown sel";
289 break;
290 }
291 break;
292 case 30:
293 T0 = env->CP0_ErrorEPC;
294 rn = "ErrorEPC";
295 break;
296 case 31:
297 T0 = env->CP0_DESAVE;
298 rn = "DESAVE";
299 break;
300 default:
301 rn = "unknown";
302 break;
303 }
304 print:
305#if defined MIPS_DEBUG_DISAS
306 if (loglevel & CPU_LOG_TB_IN_ASM) {
307 fprintf(logfile, "%08x mfc0 %s => %08x (%d %d)\n",
308 env->PC, rn, T0, reg, sel);
309 }
310#endif
311 return;
312}
313
6af0bf9c
FB
314void do_mtc0 (int reg, int sel)
315{
316 const unsigned char *rn;
317 uint32_t val, old, mask;
6af0bf9c
FB
318
319 if (sel != 0 && reg != 16 && reg != 28) {
320 val = -1;
321 old = -1;
322 rn = "invalid";
323 goto print;
324 }
325 switch (reg) {
326 case 0:
327 val = (env->CP0_index & 0x80000000) | (T0 & 0x0000000F);
328 old = env->CP0_index;
329 env->CP0_index = val;
330 rn = "Index";
331 break;
332 case 2:
333 val = T0 & 0x03FFFFFFF;
334 old = env->CP0_EntryLo0;
335 env->CP0_EntryLo0 = val;
336 rn = "EntryLo0";
337 break;
338 case 3:
339 val = T0 & 0x03FFFFFFF;
340 old = env->CP0_EntryLo1;
341 env->CP0_EntryLo1 = val;
342 rn = "EntryLo1";
343 break;
344 case 4:
345 val = (env->CP0_Context & 0xFF000000) | (T0 & 0x00FFFFF0);
346 old = env->CP0_Context;
347 env->CP0_Context = val;
348 rn = "Context";
349 break;
350 case 5:
351 val = T0 & 0x01FFE000;
352 old = env->CP0_PageMask;
353 env->CP0_PageMask = val;
354 rn = "PageMask";
355 break;
356 case 6:
357 val = T0 & 0x0000000F;
358 old = env->CP0_Wired;
359 env->CP0_Wired = val;
360 rn = "Wired";
361 break;
362 case 9:
363 val = T0;
364 old = cpu_mips_get_count(env);
365 cpu_mips_store_count(env, val);
366 rn = "Count";
367 break;
368 case 10:
369 val = T0 & 0xFFFFF0FF;
370 old = env->CP0_EntryHi;
371 env->CP0_EntryHi = val;
4ad40f36
FB
372 /* If the ASID changes, flush qemu's TLB. */
373 if ((old & 0xFF) != (val & 0xFF))
374 tlb_flush (env, 1);
6af0bf9c
FB
375 rn = "EntryHi";
376 break;
377 case 11:
378 val = T0;
379 old = env->CP0_Compare;
380 cpu_mips_store_compare(env, val);
381 rn = "Compare";
382 break;
383 case 12:
384 val = T0 & 0xFA78FF01;
385 if (T0 & (1 << CP0St_UM))
386 env->hflags |= MIPS_HFLAG_UM;
387 else
388 env->hflags &= ~MIPS_HFLAG_UM;
389 if (T0 & (1 << CP0St_ERL))
390 env->hflags |= MIPS_HFLAG_ERL;
391 else
392 env->hflags &= ~MIPS_HFLAG_ERL;
393 if (T0 & (1 << CP0St_EXL))
394 env->hflags |= MIPS_HFLAG_EXL;
395 else
396 env->hflags &= ~MIPS_HFLAG_EXL;
397 old = env->CP0_Status;
398 env->CP0_Status = val;
399 /* If we unmasked an asserted IRQ, raise it */
ae022501 400 mask = 0x0000FF00;
6af0bf9c
FB
401 if (loglevel & CPU_LOG_TB_IN_ASM) {
402 fprintf(logfile, "Status %08x => %08x Cause %08x (%08x %08x %08x)\n",
403 old, val, env->CP0_Cause, old & mask, val & mask,
404 env->CP0_Cause & mask);
405 }
406#if 1
407 if ((val & (1 << CP0St_IE)) && !(old & (1 << CP0St_IE)) &&
408 !(env->hflags & MIPS_HFLAG_EXL) &&
409 !(env->hflags & MIPS_HFLAG_ERL) &&
410 !(env->hflags & MIPS_HFLAG_DM) &&
e1d9a508 411 (env->CP0_Status & env->CP0_Cause & mask)) {
6af0bf9c
FB
412 if (logfile)
413 fprintf(logfile, "Raise pending IRQs\n");
414 env->interrupt_request |= CPU_INTERRUPT_HARD;
415 do_raise_exception(EXCP_EXT_INTERRUPT);
416 } else if (!(val & 0x00000001) && (old & 0x00000001)) {
417 env->interrupt_request &= ~CPU_INTERRUPT_HARD;
418 }
419#endif
420 rn = "Status";
421 break;
422 case 13:
423 val = (env->CP0_Cause & 0xB000F87C) | (T0 & 0x000C00300);
424 old = env->CP0_Cause;
425 env->CP0_Cause = val;
426#if 0
e37e863f
FB
427 {
428 int i;
429 /* Check if we ever asserted a software IRQ */
430 for (i = 0; i < 2; i++) {
431 mask = 0x100 << i;
432 if ((val & mask) & !(old & mask))
433 mips_set_irq(i);
434 }
6af0bf9c
FB
435 }
436#endif
437 rn = "Cause";
438 break;
439 case 14:
440 val = T0;
441 old = env->CP0_EPC;
442 env->CP0_EPC = val;
443 rn = "EPC";
444 break;
445 case 16:
446 switch (sel) {
447 case 0:
448#if defined(MIPS_USES_R4K_TLB)
449 val = (env->CP0_Config0 & 0x8017FF80) | (T0 & 0x7E000001);
450#else
451 val = (env->CP0_Config0 & 0xFE17FF80) | (T0 & 0x00000001);
452#endif
453 old = env->CP0_Config0;
454 env->CP0_Config0 = val;
455 rn = "Config0";
456 break;
457 default:
458 val = -1;
459 old = -1;
460 rn = "bad config selector";
461 break;
462 }
463 break;
464 case 18:
465 val = T0;
466 old = env->CP0_WatchLo;
467 env->CP0_WatchLo = val;
468 rn = "WatchLo";
469 break;
470 case 19:
471 val = T0 & 0x40FF0FF8;
472 old = env->CP0_WatchHi;
473 env->CP0_WatchHi = val;
474 rn = "WatchHi";
475 break;
476 case 23:
477 val = (env->CP0_Debug & 0x8C03FC1F) | (T0 & 0x13300120);
478 if (T0 & (1 << CP0DB_DM))
479 env->hflags |= MIPS_HFLAG_DM;
480 else
481 env->hflags &= ~MIPS_HFLAG_DM;
482 old = env->CP0_Debug;
483 env->CP0_Debug = val;
484 rn = "Debug";
485 break;
486 case 24:
487 val = T0;
488 old = env->CP0_DEPC;
489 env->CP0_DEPC = val;
490 rn = "DEPC";
491 break;
492 case 28:
493 switch (sel) {
494 case 0:
495 val = T0 & 0xFFFFFCF6;
496 old = env->CP0_TagLo;
497 env->CP0_TagLo = val;
498 rn = "TagLo";
499 break;
500 default:
501 val = -1;
502 old = -1;
503 rn = "invalid sel";
504 break;
505 }
506 break;
507 case 30:
508 val = T0;
509 old = env->CP0_ErrorEPC;
510 env->CP0_ErrorEPC = val;
511 rn = "EPC";
512 break;
513 case 31:
514 val = T0;
515 old = env->CP0_DESAVE;
516 env->CP0_DESAVE = val;
517 rn = "DESAVE";
518 break;
519 default:
520 val = -1;
521 old = -1;
522 rn = "unknown";
523 break;
524 }
525 print:
526#if defined MIPS_DEBUG_DISAS
527 if (loglevel & CPU_LOG_TB_IN_ASM) {
528 fprintf(logfile, "%08x mtc0 %s %08x => %08x (%d %d %08x)\n",
529 env->PC, rn, T0, val, reg, sel, old);
530 }
531#endif
532 return;
533}
534
535/* TLB management */
536#if defined(MIPS_USES_R4K_TLB)
98c1b82b 537static void invalidate_tlb (int idx)
6af0bf9c
FB
538{
539 tlb_t *tlb;
98c1b82b 540 target_ulong addr;
6af0bf9c
FB
541
542 tlb = &env->tlb[idx];
98c1b82b
PB
543 if (tlb->V0) {
544 tb_invalidate_page_range(tlb->PFN[0], tlb->end - tlb->VPN);
4ad40f36
FB
545 addr = tlb->VPN;
546 while (addr < tlb->end) {
547 tlb_flush_page (env, addr);
548 addr += TARGET_PAGE_SIZE;
549 }
6af0bf9c 550 }
98c1b82b
PB
551 if (tlb->V1) {
552 tb_invalidate_page_range(tlb->PFN[1], tlb->end2 - tlb->end);
4ad40f36
FB
553 addr = tlb->end;
554 while (addr < tlb->end2) {
555 tlb_flush_page (env, addr);
556 addr += TARGET_PAGE_SIZE;
557 }
6af0bf9c
FB
558 }
559}
560
98c1b82b 561static void fill_tlb (int idx)
6af0bf9c
FB
562{
563 tlb_t *tlb;
564 int size;
565
566 /* XXX: detect conflicting TLBs and raise a MCHECK exception when needed */
567 tlb = &env->tlb[idx];
568 tlb->VPN = env->CP0_EntryHi & 0xFFFFE000;
98c1b82b 569 tlb->ASID = env->CP0_EntryHi & 0xFF;
6af0bf9c
FB
570 size = env->CP0_PageMask >> 13;
571 size = 4 * (size + 1);
572 tlb->end = tlb->VPN + (1 << (8 + size));
4ad40f36 573 tlb->end2 = tlb->end + (1 << (8 + size));
6af0bf9c 574 tlb->G = env->CP0_EntryLo0 & env->CP0_EntryLo1 & 1;
98c1b82b
PB
575 tlb->V0 = (env->CP0_EntryLo0 & 2) != 0;
576 tlb->D0 = (env->CP0_EntryLo0 & 4) != 0;
577 tlb->C0 = (env->CP0_EntryLo0 >> 3) & 0x7;
6af0bf9c 578 tlb->PFN[0] = (env->CP0_EntryLo0 >> 6) << 12;
98c1b82b
PB
579 tlb->V1 = (env->CP0_EntryLo1 & 2) != 0;
580 tlb->D1 = (env->CP0_EntryLo1 & 4) != 0;
581 tlb->C1 = (env->CP0_EntryLo1 >> 3) & 0x7;
6af0bf9c
FB
582 tlb->PFN[1] = (env->CP0_EntryLo1 >> 6) << 12;
583}
584
585void do_tlbwi (void)
586{
7a962d30
FB
587 /* Wildly undefined effects for CP0_index containing a too high value and
588 MIPS_TLB_NB not being a power of two. But so does real silicon. */
98c1b82b
PB
589 invalidate_tlb(env->CP0_index & (MIPS_TLB_NB - 1));
590 fill_tlb(env->CP0_index & (MIPS_TLB_NB - 1));
6af0bf9c
FB
591}
592
593void do_tlbwr (void)
594{
595 int r = cpu_mips_get_random(env);
596
98c1b82b
PB
597 invalidate_tlb(r);
598 fill_tlb(r);
6af0bf9c
FB
599}
600
601void do_tlbp (void)
602{
603 tlb_t *tlb;
604 target_ulong tag;
605 uint8_t ASID;
606 int i;
607
608 tag = (env->CP0_EntryHi & 0xFFFFE000);
609 ASID = env->CP0_EntryHi & 0x000000FF;
7a962d30 610 for (i = 0; i < MIPS_TLB_NB; i++) {
6af0bf9c
FB
611 tlb = &env->tlb[i];
612 /* Check ASID, virtual page number & size */
613 if ((tlb->G == 1 || tlb->ASID == ASID) && tlb->VPN == tag) {
614 /* TLB match */
615 env->CP0_index = i;
616 break;
617 }
618 }
7a962d30 619 if (i == MIPS_TLB_NB) {
6af0bf9c
FB
620 env->CP0_index |= 0x80000000;
621 }
622}
623
624void do_tlbr (void)
625{
626 tlb_t *tlb;
09c56b84 627 uint8_t ASID;
6af0bf9c
FB
628 int size;
629
09c56b84 630 ASID = env->CP0_EntryHi & 0xFF;
7a962d30 631 tlb = &env->tlb[env->CP0_index & (MIPS_TLB_NB - 1)];
4ad40f36
FB
632
633 /* If this will change the current ASID, flush qemu's TLB. */
09c56b84 634 if (ASID != tlb->ASID && tlb->G != 1)
4ad40f36
FB
635 tlb_flush (env, 1);
636
6af0bf9c
FB
637 env->CP0_EntryHi = tlb->VPN | tlb->ASID;
638 size = (tlb->end - tlb->VPN) >> 12;
639 env->CP0_PageMask = (size - 1) << 13;
98c1b82b
PB
640 env->CP0_EntryLo0 = tlb->G | (tlb->V0 << 1) | (tlb->D0 << 2)
641 | (tlb->C0 << 3) | (tlb->PFN[0] >> 6);
642 env->CP0_EntryLo1 = tlb->G | (tlb->V1 << 1) | (tlb->D1 << 2)
643 | (tlb->C1 << 3) | (tlb->PFN[1] >> 6);
6af0bf9c
FB
644}
645#endif
646
048f6b4d
FB
647#endif /* !CONFIG_USER_ONLY */
648
6af0bf9c
FB
649void op_dump_ldst (const unsigned char *func)
650{
651 if (loglevel)
652 fprintf(logfile, "%s => %08x %08x\n", __func__, T0, T1);
653}
654
655void dump_sc (void)
656{
657 if (loglevel) {
658 fprintf(logfile, "%s %08x at %08x (%08x)\n", __func__,
659 T1, T0, env->CP0_LLAddr);
660 }
661}
662
663void debug_eret (void)
664{
665 if (loglevel) {
666 fprintf(logfile, "ERET: pc %08x EPC %08x ErrorEPC %08x (%d)\n",
667 env->PC, env->CP0_EPC, env->CP0_ErrorEPC,
668 env->hflags & MIPS_HFLAG_ERL ? 1 : 0);
669 }
670}
671
6af0bf9c
FB
672void do_pmon (int function)
673{
674 function /= 2;
675 switch (function) {
676 case 2: /* TODO: char inbyte(int waitflag); */
677 if (env->gpr[4] == 0)
678 env->gpr[2] = -1;
679 /* Fall through */
680 case 11: /* TODO: char inbyte (void); */
681 env->gpr[2] = -1;
682 break;
683 case 3:
684 case 12:
685 printf("%c", env->gpr[4] & 0xFF);
686 break;
687 case 17:
688 break;
689 case 158:
690 {
691 unsigned char *fmt = (void *)env->gpr[4];
692 printf("%s", fmt);
693 }
694 break;
695 }
696}
e37e863f
FB
697
698#if !defined(CONFIG_USER_ONLY)
699
4ad40f36
FB
700static void do_unaligned_access (target_ulong addr, int is_write, int is_user, void *retaddr);
701
e37e863f 702#define MMUSUFFIX _mmu
4ad40f36 703#define ALIGNED_ONLY
e37e863f
FB
704
705#define SHIFT 0
706#include "softmmu_template.h"
707
708#define SHIFT 1
709#include "softmmu_template.h"
710
711#define SHIFT 2
712#include "softmmu_template.h"
713
714#define SHIFT 3
715#include "softmmu_template.h"
716
4ad40f36
FB
717static void do_unaligned_access (target_ulong addr, int is_write, int is_user, void *retaddr)
718{
719 env->CP0_BadVAddr = addr;
720 do_restore_state (retaddr);
721 do_raise_exception ((is_write == 1) ? EXCP_AdES : EXCP_AdEL);
722}
723
e37e863f
FB
724void tlb_fill (target_ulong addr, int is_write, int is_user, void *retaddr)
725{
726 TranslationBlock *tb;
727 CPUState *saved_env;
728 unsigned long pc;
729 int ret;
730
731 /* XXX: hack to restore env in all cases, even if not called from
732 generated code */
733 saved_env = env;
734 env = cpu_single_env;
735 ret = cpu_mips_handle_mmu_fault(env, addr, is_write, is_user, 1);
736 if (ret) {
737 if (retaddr) {
738 /* now we have a real cpu fault */
739 pc = (unsigned long)retaddr;
740 tb = tb_find_pc(pc);
741 if (tb) {
742 /* the PC is inside the translated code. It means that we have
743 a virtual CPU fault */
744 cpu_restore_state(tb, env, pc, NULL);
745 }
746 }
747 do_raise_exception_err(env->exception_index, env->error_code);
748 }
749 env = saved_env;
750}
751
752#endif