2 * MIPS emulation helpers for qemu.
4 * Copyright (c) 2004-2005 Jocelyn Mayer
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.
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.
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
22 #define MIPS_DEBUG_DISAS
24 #define GETPC() (__builtin_return_address(0))
26 /*****************************************************************************/
27 /* Exceptions processing helpers */
28 void cpu_loop_exit(void)
30 longjmp(env->jmp_env, 1);
33 void do_raise_exception_err (uint32_t exception, int error_code)
36 if (logfile && exception < 0x100)
37 fprintf(logfile, "%s: %d %d\n", __func__, exception, error_code);
39 env->exception_index = exception;
40 env->error_code = error_code;
45 void do_raise_exception (uint32_t exception)
47 do_raise_exception_err(exception, 0);
50 void do_restore_state (void *pc_ptr)
53 unsigned long pc = (unsigned long) pc_ptr;
56 cpu_restore_state (tb, env, pc, NULL);
59 void do_raise_exception_direct (uint32_t exception)
61 do_restore_state (GETPC ());
62 do_raise_exception_err (exception, 0);
65 #define MEMSUFFIX _raw
66 #include "op_helper_mem.c"
68 #if !defined(CONFIG_USER_ONLY)
69 #define MEMSUFFIX _user
70 #include "op_helper_mem.c"
72 #define MEMSUFFIX _kernel
73 #include "op_helper_mem.c"
77 /* 64 bits arithmetic for 32 bits hosts */
78 #if (HOST_LONG_BITS == 32)
79 static inline uint64_t get_HILO (void)
81 return ((uint64_t)env->HI << 32) | (uint64_t)env->LO;
84 static inline void set_HILO (uint64_t HILO)
86 env->LO = HILO & 0xFFFFFFFF;
92 set_HILO((int64_t)(int32_t)T0 * (int64_t)(int32_t)T1);
97 set_HILO((uint64_t)T0 * (uint64_t)T1);
104 tmp = ((int64_t)(int32_t)T0 * (int64_t)(int32_t)T1);
105 set_HILO((int64_t)get_HILO() + tmp);
112 tmp = ((uint64_t)T0 * (uint64_t)T1);
113 set_HILO(get_HILO() + tmp);
120 tmp = ((int64_t)(int32_t)T0 * (int64_t)(int32_t)T1);
121 set_HILO((int64_t)get_HILO() - tmp);
128 tmp = ((uint64_t)T0 * (uint64_t)T1);
129 set_HILO(get_HILO() - tmp);
133 #if defined(CONFIG_USER_ONLY)
134 void do_mfc0_random (void)
136 cpu_abort(env, "mfc0 random\n");
139 void do_mfc0_count (void)
141 cpu_abort(env, "mfc0 count\n");
144 void do_mtc0 (int reg, int sel)
146 cpu_abort(env, "mtc0 reg=%d sel=%d\n", reg, sel);
151 cpu_abort(env, "tlbwi\n");
156 cpu_abort(env, "tlbwr\n");
161 cpu_abort(env, "tlbp\n");
166 cpu_abort(env, "tlbr\n");
172 void do_mfc0_random (void)
174 T0 = cpu_mips_get_random(env);
177 void do_mfc0_count (void)
179 T0 = cpu_mips_get_count(env);
182 void do_mtc0 (int reg, int sel)
184 const unsigned char *rn;
185 uint32_t val, old, mask;
187 if (sel != 0 && reg != 16 && reg != 28) {
195 val = (env->CP0_index & 0x80000000) | (T0 & 0x0000000F);
196 old = env->CP0_index;
197 env->CP0_index = val;
201 val = T0 & 0x3FFFFFFF;
202 old = env->CP0_EntryLo0;
203 env->CP0_EntryLo0 = val;
207 val = T0 & 0x3FFFFFFF;
208 old = env->CP0_EntryLo1;
209 env->CP0_EntryLo1 = val;
213 val = (env->CP0_Context & 0xFF800000) | (T0 & 0x007FFFF0);
214 old = env->CP0_Context;
215 env->CP0_Context = val;
219 val = T0 & 0x01FFE000;
220 old = env->CP0_PageMask;
221 env->CP0_PageMask = val;
225 val = T0 & 0x0000000F;
226 old = env->CP0_Wired;
227 env->CP0_Wired = val;
232 old = cpu_mips_get_count(env);
233 cpu_mips_store_count(env, val);
237 val = T0 & 0xFFFFE0FF;
238 old = env->CP0_EntryHi;
239 env->CP0_EntryHi = val;
240 /* If the ASID changes, flush qemu's TLB. */
241 if ((old & 0xFF) != (val & 0xFF))
242 cpu_mips_tlb_flush (env, 1);
247 old = env->CP0_Compare;
248 cpu_mips_store_compare(env, val);
252 val = T0 & 0xFA78FF01;
253 if (T0 & (1 << CP0St_UM))
254 env->hflags |= MIPS_HFLAG_UM;
256 env->hflags &= ~MIPS_HFLAG_UM;
257 if (T0 & (1 << CP0St_ERL))
258 env->hflags |= MIPS_HFLAG_ERL;
260 env->hflags &= ~MIPS_HFLAG_ERL;
261 if (T0 & (1 << CP0St_EXL))
262 env->hflags |= MIPS_HFLAG_EXL;
264 env->hflags &= ~MIPS_HFLAG_EXL;
265 old = env->CP0_Status;
266 env->CP0_Status = val;
267 /* If we unmasked an asserted IRQ, raise it */
269 if (loglevel & CPU_LOG_TB_IN_ASM) {
270 fprintf(logfile, "Status %08x => %08x Cause %08x (%08x %08x %08x)\n",
271 old, val, env->CP0_Cause, old & mask, val & mask,
272 env->CP0_Cause & mask);
274 if ((val & (1 << CP0St_IE)) && !(old & (1 << CP0St_IE)) &&
275 !(env->hflags & MIPS_HFLAG_EXL) &&
276 !(env->hflags & MIPS_HFLAG_ERL) &&
277 !(env->hflags & MIPS_HFLAG_DM) &&
278 (env->CP0_Status & env->CP0_Cause & mask)) {
280 fprintf(logfile, "Raise pending IRQs\n");
281 env->interrupt_request |= CPU_INTERRUPT_HARD;
282 } else if (!(val & (1 << CP0St_IE)) && (old & (1 << CP0St_IE))) {
283 env->interrupt_request &= ~CPU_INTERRUPT_HARD;
288 val = (env->CP0_Cause & 0xB000F87C) | (T0 & 0x000C00300);
289 old = env->CP0_Cause;
290 env->CP0_Cause = val;
294 /* Check if we ever asserted a software IRQ */
295 for (i = 0; i < 2; i++) {
297 if ((val & mask) & !(old & mask))
313 #if defined(MIPS_USES_R4K_TLB)
314 val = (env->CP0_Config0 & 0x8017FF80) | (T0 & 0x7E000001);
316 val = (env->CP0_Config0 & 0xFE17FF80) | (T0 & 0x00000001);
318 old = env->CP0_Config0;
319 env->CP0_Config0 = val;
325 rn = "bad config selector";
331 old = env->CP0_WatchLo;
332 env->CP0_WatchLo = val;
336 val = T0 & 0x40FF0FF8;
337 old = env->CP0_WatchHi;
338 env->CP0_WatchHi = val;
342 val = (env->CP0_Debug & 0x8C03FC1F) | (T0 & 0x13300120);
343 if (T0 & (1 << CP0DB_DM))
344 env->hflags |= MIPS_HFLAG_DM;
346 env->hflags &= ~MIPS_HFLAG_DM;
347 old = env->CP0_Debug;
348 env->CP0_Debug = val;
360 val = T0 & 0xFFFFFCF6;
361 old = env->CP0_TagLo;
362 env->CP0_TagLo = val;
374 old = env->CP0_ErrorEPC;
375 env->CP0_ErrorEPC = val;
380 old = env->CP0_DESAVE;
381 env->CP0_DESAVE = val;
391 #if defined MIPS_DEBUG_DISAS
392 if (loglevel & CPU_LOG_TB_IN_ASM) {
393 fprintf(logfile, "%08x mtc0 %s %08x => %08x (%d %d %08x)\n",
394 env->PC, rn, T0, val, reg, sel, old);
401 #include "softfloat.h"
403 void fpu_handle_exception(void)
405 #ifdef CONFIG_SOFTFLOAT
406 int flags = get_float_exception_flags(&env->fp_status);
407 unsigned int cpuflags = 0, enable, cause = 0;
409 enable = GET_FP_ENABLE(env->fcr31);
411 /* determine current flags */
412 if (flags & float_flag_invalid) {
413 cpuflags |= FP_INVALID;
414 cause |= FP_INVALID & enable;
416 if (flags & float_flag_divbyzero) {
418 cause |= FP_DIV0 & enable;
420 if (flags & float_flag_overflow) {
421 cpuflags |= FP_OVERFLOW;
422 cause |= FP_OVERFLOW & enable;
424 if (flags & float_flag_underflow) {
425 cpuflags |= FP_UNDERFLOW;
426 cause |= FP_UNDERFLOW & enable;
428 if (flags & float_flag_inexact) {
429 cpuflags |= FP_INEXACT;
430 cause |= FP_INEXACT & enable;
432 SET_FP_FLAGS(env->fcr31, cpuflags);
433 SET_FP_CAUSE(env->fcr31, cause);
435 SET_FP_FLAGS(env->fcr31, 0);
436 SET_FP_CAUSE(env->fcr31, 0);
439 #endif /* MIPS_USES_FPU */
442 #if defined(MIPS_USES_R4K_TLB)
443 void cpu_mips_tlb_flush (CPUState *env, int flush_global)
445 /* Flush qemu's TLB and discard all shadowed entries. */
446 tlb_flush (env, flush_global);
447 env->tlb_in_use = MIPS_TLB_NB;
450 static void invalidate_tlb (int idx, int use_extra)
456 ASID = env->CP0_EntryHi & 0xFF;
458 tlb = &env->tlb[idx];
459 /* The qemu TLB is flushed then the ASID changes, so no need to
460 flush these entries again. */
461 if (tlb->G == 0 && tlb->ASID != ASID) {
465 if (use_extra && env->tlb_in_use < MIPS_TLB_MAX) {
466 /* For tlbwr, we can shadow the discarded entry into
467 a new (fake) TLB entry, as long as the guest can not
468 tell that it's there. */
469 env->tlb[env->tlb_in_use] = *tlb;
475 tb_invalidate_page_range(tlb->PFN[0], tlb->end - tlb->VPN);
477 while (addr < tlb->end) {
478 tlb_flush_page (env, addr);
479 addr += TARGET_PAGE_SIZE;
483 tb_invalidate_page_range(tlb->PFN[1], tlb->end2 - tlb->end);
485 while (addr < tlb->end2) {
486 tlb_flush_page (env, addr);
487 addr += TARGET_PAGE_SIZE;
492 static void mips_tlb_flush_extra (CPUState *env, int first)
494 /* Discard entries from env->tlb[first] onwards. */
495 while (env->tlb_in_use > first) {
496 invalidate_tlb(--env->tlb_in_use, 0);
500 static void fill_tlb (int idx)
505 /* XXX: detect conflicting TLBs and raise a MCHECK exception when needed */
506 tlb = &env->tlb[idx];
507 tlb->VPN = env->CP0_EntryHi & 0xFFFFE000;
508 tlb->ASID = env->CP0_EntryHi & 0xFF;
509 size = env->CP0_PageMask >> 13;
510 size = 4 * (size + 1);
511 tlb->end = tlb->VPN + (1 << (8 + size));
512 tlb->end2 = tlb->end + (1 << (8 + size));
513 tlb->G = env->CP0_EntryLo0 & env->CP0_EntryLo1 & 1;
514 tlb->V0 = (env->CP0_EntryLo0 & 2) != 0;
515 tlb->D0 = (env->CP0_EntryLo0 & 4) != 0;
516 tlb->C0 = (env->CP0_EntryLo0 >> 3) & 0x7;
517 tlb->PFN[0] = (env->CP0_EntryLo0 >> 6) << 12;
518 tlb->V1 = (env->CP0_EntryLo1 & 2) != 0;
519 tlb->D1 = (env->CP0_EntryLo1 & 4) != 0;
520 tlb->C1 = (env->CP0_EntryLo1 >> 3) & 0x7;
521 tlb->PFN[1] = (env->CP0_EntryLo1 >> 6) << 12;
526 /* Discard cached TLB entries. We could avoid doing this if the
527 tlbwi is just upgrading access permissions on the current entry;
528 that might be a further win. */
529 mips_tlb_flush_extra (env, MIPS_TLB_NB);
531 /* Wildly undefined effects for CP0_index containing a too high value and
532 MIPS_TLB_NB not being a power of two. But so does real silicon. */
533 invalidate_tlb(env->CP0_index & (MIPS_TLB_NB - 1), 0);
534 fill_tlb(env->CP0_index & (MIPS_TLB_NB - 1));
539 int r = cpu_mips_get_random(env);
541 invalidate_tlb(r, 1);
552 tag = env->CP0_EntryHi & 0xFFFFE000;
553 ASID = env->CP0_EntryHi & 0xFF;
554 for (i = 0; i < MIPS_TLB_NB; i++) {
556 /* Check ASID, virtual page number & size */
557 if ((tlb->G == 1 || tlb->ASID == ASID) && tlb->VPN == tag) {
563 if (i == MIPS_TLB_NB) {
564 /* No match. Discard any shadow entries, if any of them match. */
565 for (i = MIPS_TLB_NB; i < env->tlb_in_use; i++) {
568 /* Check ASID, virtual page number & size */
569 if ((tlb->G == 1 || tlb->ASID == ASID) && tlb->VPN == tag) {
570 mips_tlb_flush_extra (env, i);
575 env->CP0_index |= 0x80000000;
585 ASID = env->CP0_EntryHi & 0xFF;
586 tlb = &env->tlb[env->CP0_index & (MIPS_TLB_NB - 1)];
588 /* If this will change the current ASID, flush qemu's TLB. */
589 if (ASID != tlb->ASID)
590 cpu_mips_tlb_flush (env, 1);
592 mips_tlb_flush_extra(env, MIPS_TLB_NB);
594 env->CP0_EntryHi = tlb->VPN | tlb->ASID;
595 size = (tlb->end - tlb->VPN) >> 12;
596 env->CP0_PageMask = (size - 1) << 13;
597 env->CP0_EntryLo0 = tlb->G | (tlb->V0 << 1) | (tlb->D0 << 2)
598 | (tlb->C0 << 3) | (tlb->PFN[0] >> 6);
599 env->CP0_EntryLo1 = tlb->G | (tlb->V1 << 1) | (tlb->D1 << 2)
600 | (tlb->C1 << 3) | (tlb->PFN[1] >> 6);
604 #endif /* !CONFIG_USER_ONLY */
606 void op_dump_ldst (const unsigned char *func)
609 fprintf(logfile, "%s => %08x %08x\n", __func__, T0, T1);
615 fprintf(logfile, "%s %08x at %08x (%08x)\n", __func__,
616 T1, T0, env->CP0_LLAddr);
620 void debug_eret (void)
623 fprintf(logfile, "ERET: pc %08x EPC %08x ErrorEPC %08x (%d)\n",
624 env->PC, env->CP0_EPC, env->CP0_ErrorEPC,
625 env->hflags & MIPS_HFLAG_ERL ? 1 : 0);
629 void do_pmon (int function)
633 case 2: /* TODO: char inbyte(int waitflag); */
634 if (env->gpr[4] == 0)
637 case 11: /* TODO: char inbyte (void); */
642 printf("%c", env->gpr[4] & 0xFF);
648 unsigned char *fmt = (void *)env->gpr[4];
655 #if !defined(CONFIG_USER_ONLY)
657 static void do_unaligned_access (target_ulong addr, int is_write, int is_user, void *retaddr);
659 #define MMUSUFFIX _mmu
663 #include "softmmu_template.h"
666 #include "softmmu_template.h"
669 #include "softmmu_template.h"
672 #include "softmmu_template.h"
674 static void do_unaligned_access (target_ulong addr, int is_write, int is_user, void *retaddr)
676 env->CP0_BadVAddr = addr;
677 do_restore_state (retaddr);
678 do_raise_exception ((is_write == 1) ? EXCP_AdES : EXCP_AdEL);
681 void tlb_fill (target_ulong addr, int is_write, int is_user, void *retaddr)
683 TranslationBlock *tb;
688 /* XXX: hack to restore env in all cases, even if not called from
691 env = cpu_single_env;
692 ret = cpu_mips_handle_mmu_fault(env, addr, is_write, is_user, 1);
695 /* now we have a real cpu fault */
696 pc = (unsigned long)retaddr;
699 /* the PC is inside the translated code. It means that we have
700 a virtual CPU fault */
701 cpu_restore_state(tb, env, pc, NULL);
704 do_raise_exception_err(env->exception_index, env->error_code);