void mips_cpu_do_interrupt(CPUState *cs) { #if !defined(CONFIG_USER_ONLY) MIPSCPU *cpu = MIPS_CPU(cs); CPUMIPSState *env = &cpu->env; bool update_badinstr = 0; target_ulong offset; int cause = -1; const char *name; if (qemu_loglevel_mask(CPU_LOG_INT) && cs->exception_index != EXCP_EXT_INTERRUPT) { if (cs->exception_index < 0 || cs->exception_index > EXCP_LAST) { name = "unknown"; } else { name = excp_names[cs->exception_index]; } qemu_log("%s enter: PC " TARGET_FMT_lx " EPC " TARGET_FMT_lx " %s exception\n", __func__, env->active_tc.PC, env->CP0_EPC, name); } if (cs->exception_index == EXCP_EXT_INTERRUPT && (env->hflags & MIPS_HFLAG_DM)) { cs->exception_index = EXCP_DINT; } offset = 0x180; switch (cs->exception_index) { case EXCP_DSS: env->CP0_Debug |= 1 << CP0DB_DSS; /* Debug single step cannot be raised inside a delay slot and resume will always occur on the next instruction (but we assume the pc has always been updated during code translation). */ env->CP0_DEPC = env->active_tc.PC | !!(env->hflags & MIPS_HFLAG_M16); goto enter_debug_mode; case EXCP_DINT: env->CP0_Debug |= 1 << CP0DB_DINT; goto set_DEPC; case EXCP_DIB: env->CP0_Debug |= 1 << CP0DB_DIB; goto set_DEPC; case EXCP_DBp: env->CP0_Debug |= 1 << CP0DB_DBp; goto set_DEPC; case EXCP_DDBS: env->CP0_Debug |= 1 << CP0DB_DDBS; goto set_DEPC; case EXCP_DDBL: env->CP0_Debug |= 1 << CP0DB_DDBL; set_DEPC: env->CP0_DEPC = exception_resume_pc(env); env->hflags &= ~MIPS_HFLAG_BMASK; enter_debug_mode: if (env->insn_flags & ISA_MIPS3) { env->hflags |= MIPS_HFLAG_64; if (!(env->insn_flags & ISA_MIPS64R6) || env->CP0_Status & (1 << CP0St_KX)) { env->hflags &= ~MIPS_HFLAG_AWRAP; } } env->hflags |= MIPS_HFLAG_DM | MIPS_HFLAG_CP0; env->hflags &= ~(MIPS_HFLAG_KSU); /* EJTAG probe trap enable is not implemented... */ if (!(env->CP0_Status & (1 << CP0St_EXL))) env->CP0_Cause &= ~(1U << CP0Ca_BD); env->active_tc.PC = (int32_t)0xBFC00480; set_hflags_for_handler(env); break; case EXCP_RESET: cpu_reset(CPU(cpu)); break; case EXCP_SRESET: env->CP0_Status |= (1 << CP0St_SR); memset(env->CP0_WatchLo, 0, sizeof(env->CP0_WatchLo)); goto set_error_EPC; case EXCP_NMI: env->CP0_Status |= (1 << CP0St_NMI); set_error_EPC: env->CP0_ErrorEPC = exception_resume_pc(env); env->hflags &= ~MIPS_HFLAG_BMASK; env->CP0_Status |= (1 << CP0St_ERL) | (1 << CP0St_BEV); if (env->insn_flags & ISA_MIPS3) { env->hflags |= MIPS_HFLAG_64; if (!(env->insn_flags & ISA_MIPS64R6) || env->CP0_Status & (1 << CP0St_KX)) { env->hflags &= ~MIPS_HFLAG_AWRAP; } } env->hflags |= MIPS_HFLAG_CP0; env->hflags &= ~(MIPS_HFLAG_KSU); if (!(env->CP0_Status & (1 << CP0St_EXL))) env->CP0_Cause &= ~(1U << CP0Ca_BD); env->active_tc.PC = (int32_t)0xBFC00000; set_hflags_for_handler(env); break; case EXCP_EXT_INTERRUPT: cause = 0; if (env->CP0_Cause & (1 << CP0Ca_IV)) { uint32_t spacing = (env->CP0_IntCtl >> CP0IntCtl_VS) & 0x1f; if ((env->CP0_Status & (1 << CP0St_BEV)) || spacing == 0) { offset = 0x200; } else { uint32_t vector = 0; uint32_t pending = (env->CP0_Cause & CP0Ca_IP_mask) >> CP0Ca_IP; if (env->CP0_Config3 & (1 << CP0C3_VEIC)) { /* For VEIC mode, the external interrupt controller feeds * the vector through the CP0Cause IP lines. */ vector = pending; } else { /* Vectored Interrupts * Mask with Status.IM7-IM0 to get enabled interrupts. */ pending &= (env->CP0_Status >> CP0St_IM) & 0xff; /* Find the highest-priority interrupt. */ while (pending >>= 1) { vector++; } } offset = 0x200 + (vector * (spacing << 5)); } } goto set_EPC; case EXCP_LTLBL: cause = 1; update_badinstr = !(env->error_code & EXCP_INST_NOTAVAIL); goto set_EPC; case EXCP_TLBL: cause = 2; update_badinstr = !(env->error_code & EXCP_INST_NOTAVAIL); if ((env->error_code & EXCP_TLB_NOMATCH) && !(env->CP0_Status & (1 << CP0St_EXL))) { #if defined(TARGET_MIPS64) int R = env->CP0_BadVAddr >> 62; int UX = (env->CP0_Status & (1 << CP0St_UX)) != 0; int SX = (env->CP0_Status & (1 << CP0St_SX)) != 0; int KX = (env->CP0_Status & (1 << CP0St_KX)) != 0; if (((R == 0 && UX) || (R == 1 && SX) || (R == 3 && KX)) && (!(env->insn_flags & (INSN_LOONGSON2E | INSN_LOONGSON2F)))) offset = 0x080; else #endif offset = 0x000; } goto set_EPC; case EXCP_TLBS: cause = 3; update_badinstr = 1; if ((env->error_code & EXCP_TLB_NOMATCH) && !(env->CP0_Status & (1 << CP0St_EXL))) { #if defined(TARGET_MIPS64) int R = env->CP0_BadVAddr >> 62; int UX = (env->CP0_Status & (1 << CP0St_UX)) != 0; int SX = (env->CP0_Status & (1 << CP0St_SX)) != 0; int KX = (env->CP0_Status & (1 << CP0St_KX)) != 0; if (((R == 0 && UX) || (R == 1 && SX) || (R == 3 && KX)) && (!(env->insn_flags & (INSN_LOONGSON2E | INSN_LOONGSON2F)))) offset = 0x080; else #endif offset = 0x000; } goto set_EPC; case EXCP_AdEL: cause = 4; update_badinstr = !(env->error_code & EXCP_INST_NOTAVAIL); goto set_EPC; case EXCP_AdES: cause = 5; update_badinstr = 1; goto set_EPC; case EXCP_IBE: cause = 6; goto set_EPC; case EXCP_DBE: cause = 7; goto set_EPC; case EXCP_SYSCALL: cause = 8; update_badinstr = 1; goto set_EPC; case EXCP_BREAK: cause = 9; update_badinstr = 1; goto set_EPC; case EXCP_RI: cause = 10; update_badinstr = 1; goto set_EPC; case EXCP_CpU: cause = 11; update_badinstr = 1; env->CP0_Cause = (env->CP0_Cause & ~(0x3 << CP0Ca_CE)) | (env->error_code << CP0Ca_CE); goto set_EPC; case EXCP_OVERFLOW: cause = 12; update_badinstr = 1; goto set_EPC; case EXCP_TRAP: cause = 13; update_badinstr = 1; goto set_EPC; case EXCP_MSAFPE: cause = 14; update_badinstr = 1; goto set_EPC; case EXCP_FPE: cause = 15; update_badinstr = 1; goto set_EPC; case EXCP_C2E: cause = 18; goto set_EPC; case EXCP_TLBRI: cause = 19; update_badinstr = 1; goto set_EPC; case EXCP_TLBXI: cause = 20; goto set_EPC; case EXCP_MSADIS: cause = 21; update_badinstr = 1; goto set_EPC; case EXCP_MDMX: cause = 22; goto set_EPC; case EXCP_DWATCH: cause = 23; /* XXX: TODO: manage deferred watch exceptions */ goto set_EPC; case EXCP_MCHECK: cause = 24; goto set_EPC; case EXCP_THREAD: cause = 25; goto set_EPC; case EXCP_DSPDIS: cause = 26; goto set_EPC; case EXCP_CACHE: cause = 30; if (env->CP0_Status & (1 << CP0St_BEV)) { offset = 0x100; } else { offset = 0x20000100; } set_EPC: if (!(env->CP0_Status & (1 << CP0St_EXL))) { env->CP0_EPC = exception_resume_pc(env); if (update_badinstr) { set_badinstr_registers(env); } if (env->hflags & MIPS_HFLAG_BMASK) { env->CP0_Cause |= (1U << CP0Ca_BD); } else { env->CP0_Cause &= ~(1U << CP0Ca_BD); } env->CP0_Status |= (1 << CP0St_EXL); if (env->insn_flags & ISA_MIPS3) { env->hflags |= MIPS_HFLAG_64; if (!(env->insn_flags & ISA_MIPS64R6) || env->CP0_Status & (1 << CP0St_KX)) { env->hflags &= ~MIPS_HFLAG_AWRAP; } } env->hflags |= MIPS_HFLAG_CP0; env->hflags &= ~(MIPS_HFLAG_KSU); } env->hflags &= ~MIPS_HFLAG_BMASK; if (env->CP0_Status & (1 << CP0St_BEV)) { env->active_tc.PC = (int32_t)0xBFC00200; } else { env->active_tc.PC = (int32_t)(env->CP0_EBase & ~0x3ff); } env->active_tc.PC += offset; set_hflags_for_handler(env); env->CP0_Cause = (env->CP0_Cause & ~(0x1f << CP0Ca_EC)) | (cause << CP0Ca_EC); break; default: abort(); } if (qemu_loglevel_mask(CPU_LOG_INT) && cs->exception_index != EXCP_EXT_INTERRUPT) { qemu_log("%s: PC " TARGET_FMT_lx " EPC " TARGET_FMT_lx " cause %d\n" " S %08x C %08x A " TARGET_FMT_lx " D " TARGET_FMT_lx "\n", __func__, env->active_tc.PC, env->CP0_EPC, cause, env->CP0_Status, env->CP0_Cause, env->CP0_BadVAddr, env->CP0_DEPC); } #endif cs->exception_index = EXCP_NONE; }