X-Git-Url: https://git.proxmox.com/?a=blobdiff_plain;f=target-mips%2Fhelper.c;h=33e0e8863772f0eec7c8a4e108b2bedc8430c895;hb=30c367ed446b6ea53245589a5cf373578ac075d7;hp=7f659ae6bc684ad97ebf3ea69ec92d86cd15cece;hpb=c227f0995e1722a1abccc28cadf0664266bd8043;p=qemu.git diff --git a/target-mips/helper.c b/target-mips/helper.c index 7f659ae6b..33e0e8863 100644 --- a/target-mips/helper.c +++ b/target-mips/helper.c @@ -24,7 +24,6 @@ #include #include "cpu.h" -#include "exec-all.h" enum { TLBRET_DIRTY = -4, @@ -34,8 +33,10 @@ enum { TLBRET_MATCH = 0 }; +#if !defined(CONFIG_USER_ONLY) + /* no MMU emulation */ -int no_mmu_map_address (CPUState *env, target_ulong *physical, int *prot, +int no_mmu_map_address (CPUMIPSState *env, hwaddr *physical, int *prot, target_ulong address, int rw, int access_type) { *physical = address; @@ -44,7 +45,7 @@ int no_mmu_map_address (CPUState *env, target_ulong *physical, int *prot, } /* fixed mapping MMU emulation */ -int fixed_mmu_map_address (CPUState *env, target_ulong *physical, int *prot, +int fixed_mmu_map_address (CPUMIPSState *env, hwaddr *physical, int *prot, target_ulong address, int rw, int access_type) { if (address <= (int32_t)0x7FFFFFFFUL) { @@ -62,7 +63,7 @@ int fixed_mmu_map_address (CPUState *env, target_ulong *physical, int *prot, } /* MIPS32/MIPS64 R4000-style MMU emulation */ -int r4k_map_address (CPUState *env, target_ulong *physical, int *prot, +int r4k_map_address (CPUMIPSState *env, hwaddr *physical, int *prot, target_ulong address, int rw, int access_type) { uint8_t ASID = env->CP0_EntryHi & 0xFF; @@ -98,8 +99,7 @@ int r4k_map_address (CPUState *env, target_ulong *physical, int *prot, return TLBRET_NOMATCH; } -#if !defined(CONFIG_USER_ONLY) -static int get_physical_address (CPUState *env, target_ulong *physical, +static int get_physical_address (CPUMIPSState *env, hwaddr *physical, int *prot, target_ulong address, int rw, int access_type) { @@ -193,7 +193,7 @@ static int get_physical_address (CPUState *env, target_ulong *physical, } } #if 0 - qemu_log(TARGET_FMT_lx " %d %d => " TARGET_FMT_lx " %d (%d)\n", + qemu_log(TARGET_FMT_lx " %d %d => %" HWADDR_PRIx " %d (%d)\n", address, rw, access_type, *physical, *prot, ret); #endif @@ -201,108 +201,138 @@ static int get_physical_address (CPUState *env, target_ulong *physical, } #endif -target_phys_addr_t cpu_get_phys_page_debug(CPUState *env, target_ulong addr) +static void raise_mmu_exception(CPUMIPSState *env, target_ulong address, + int rw, int tlb_error) +{ + int exception = 0, error_code = 0; + + switch (tlb_error) { + default: + case TLBRET_BADADDR: + /* Reference to kernel address from user mode or supervisor mode */ + /* Reference to supervisor address from user mode */ + if (rw) + exception = EXCP_AdES; + else + exception = EXCP_AdEL; + break; + case TLBRET_NOMATCH: + /* No TLB match for a mapped address */ + if (rw) + exception = EXCP_TLBS; + else + exception = EXCP_TLBL; + error_code = 1; + break; + case TLBRET_INVALID: + /* TLB match with no valid bit */ + if (rw) + exception = EXCP_TLBS; + else + exception = EXCP_TLBL; + break; + case TLBRET_DIRTY: + /* TLB match but 'D' bit is cleared */ + exception = EXCP_LTLBL; + break; + + } + /* Raise exception */ + env->CP0_BadVAddr = address; + env->CP0_Context = (env->CP0_Context & ~0x007fffff) | + ((address >> 9) & 0x007ffff0); + env->CP0_EntryHi = + (env->CP0_EntryHi & 0xFF) | (address & (TARGET_PAGE_MASK << 1)); +#if defined(TARGET_MIPS64) + env->CP0_EntryHi &= env->SEGMask; + env->CP0_XContext = (env->CP0_XContext & ((~0ULL) << (env->SEGBITS - 7))) | + ((address & 0xC00000000000ULL) >> (55 - env->SEGBITS)) | + ((address & ((1ULL << env->SEGBITS) - 1) & 0xFFFFFFFFFFFFE000ULL) >> 9); +#endif + env->exception_index = exception; + env->error_code = error_code; +} + +#if !defined(CONFIG_USER_ONLY) +hwaddr mips_cpu_get_phys_page_debug(CPUState *cs, vaddr addr) { -#if defined(CONFIG_USER_ONLY) - return addr; -#else - target_ulong phys_addr; + MIPSCPU *cpu = MIPS_CPU(cs); + hwaddr phys_addr; int prot; - if (get_physical_address(env, &phys_addr, &prot, addr, 0, ACCESS_INT) != 0) + if (get_physical_address(&cpu->env, &phys_addr, &prot, addr, 0, + ACCESS_INT) != 0) { return -1; + } return phys_addr; -#endif } +#endif -int cpu_mips_handle_mmu_fault (CPUState *env, target_ulong address, int rw, - int mmu_idx, int is_softmmu) +int cpu_mips_handle_mmu_fault (CPUMIPSState *env, target_ulong address, int rw, + int mmu_idx) { #if !defined(CONFIG_USER_ONLY) - target_ulong physical; + hwaddr physical; int prot; -#endif - int exception = 0, error_code = 0; int access_type; +#endif int ret = 0; #if 0 - log_cpu_state(env, 0); + log_cpu_state(CPU(mips_env_get_cpu(env)), 0); #endif - qemu_log("%s pc " TARGET_FMT_lx " ad " TARGET_FMT_lx " rw %d mmu_idx %d smmu %d\n", - __func__, env->active_tc.PC, address, rw, mmu_idx, is_softmmu); + qemu_log("%s pc " TARGET_FMT_lx " ad " TARGET_FMT_lx " rw %d mmu_idx %d\n", + __func__, env->active_tc.PC, address, rw, mmu_idx); rw &= 1; /* data access */ +#if !defined(CONFIG_USER_ONLY) /* XXX: put correct access by using cpu_restore_state() correctly */ access_type = ACCESS_INT; -#if defined(CONFIG_USER_ONLY) - ret = TLBRET_NOMATCH; -#else ret = get_physical_address(env, &physical, &prot, address, rw, access_type); - qemu_log("%s address=" TARGET_FMT_lx " ret %d physical " TARGET_FMT_lx " prot %d\n", + qemu_log("%s address=" TARGET_FMT_lx " ret %d physical " TARGET_FMT_plx " prot %d\n", __func__, address, ret, physical, prot); if (ret == TLBRET_MATCH) { - ret = tlb_set_page(env, address & TARGET_PAGE_MASK, - physical & TARGET_PAGE_MASK, prot, - mmu_idx, is_softmmu); + tlb_set_page(env, address & TARGET_PAGE_MASK, + physical & TARGET_PAGE_MASK, prot | PAGE_EXEC, + mmu_idx, TARGET_PAGE_SIZE); + ret = 0; } else if (ret < 0) #endif { - switch (ret) { - default: - case TLBRET_BADADDR: - /* Reference to kernel address from user mode or supervisor mode */ - /* Reference to supervisor address from user mode */ - if (rw) - exception = EXCP_AdES; - else - exception = EXCP_AdEL; - break; - case TLBRET_NOMATCH: - /* No TLB match for a mapped address */ - if (rw) - exception = EXCP_TLBS; - else - exception = EXCP_TLBL; - error_code = 1; - break; - case TLBRET_INVALID: - /* TLB match with no valid bit */ - if (rw) - exception = EXCP_TLBS; - else - exception = EXCP_TLBL; - break; - case TLBRET_DIRTY: - /* TLB match but 'D' bit is cleared */ - exception = EXCP_LTLBL; - break; - - } - /* Raise exception */ - env->CP0_BadVAddr = address; - env->CP0_Context = (env->CP0_Context & ~0x007fffff) | - ((address >> 9) & 0x007ffff0); - env->CP0_EntryHi = - (env->CP0_EntryHi & 0xFF) | (address & (TARGET_PAGE_MASK << 1)); -#if defined(TARGET_MIPS64) - env->CP0_EntryHi &= env->SEGMask; - env->CP0_XContext = (env->CP0_XContext & ((~0ULL) << (env->SEGBITS - 7))) | - ((address & 0xC00000000000ULL) >> (55 - env->SEGBITS)) | - ((address & ((1ULL << env->SEGBITS) - 1) & 0xFFFFFFFFFFFFE000ULL) >> 9); -#endif - env->exception_index = exception; - env->error_code = error_code; + raise_mmu_exception(env, address, rw, ret); ret = 1; } return ret; } +#if !defined(CONFIG_USER_ONLY) +hwaddr cpu_mips_translate_address(CPUMIPSState *env, target_ulong address, int rw) +{ + hwaddr physical; + int prot; + int access_type; + int ret = 0; + + rw &= 1; + + /* data access */ + access_type = ACCESS_INT; + ret = get_physical_address(env, &physical, &prot, + address, rw, access_type); + if (ret != TLBRET_MATCH) { + raise_mmu_exception(env, address, rw, ret); + return -1LL; + } else { + return physical; + } +} +#endif + static const char * const excp_names[EXCP_LAST + 1] = { [EXCP_RESET] = "reset", [EXCP_SRESET] = "soft reset", @@ -339,8 +369,40 @@ static const char * const excp_names[EXCP_LAST + 1] = { [EXCP_CACHE] = "cache error", }; -void do_interrupt (CPUState *env) +target_ulong exception_resume_pc (CPUMIPSState *env) { + target_ulong bad_pc; + target_ulong isa_mode; + + isa_mode = !!(env->hflags & MIPS_HFLAG_M16); + bad_pc = env->active_tc.PC | isa_mode; + if (env->hflags & MIPS_HFLAG_BMASK) { + /* If the exception was raised from a delay slot, come back to + the jump. */ + bad_pc -= (env->hflags & MIPS_HFLAG_B16 ? 2 : 4); + } + + return bad_pc; +} + +#if !defined(CONFIG_USER_ONLY) +static void set_hflags_for_handler (CPUMIPSState *env) +{ + /* Exception handlers are entered in 32-bit mode. */ + env->hflags &= ~(MIPS_HFLAG_M16); + /* ...except that microMIPS lets you choose. */ + if (env->insn_flags & ASE_MICROMIPS) { + env->hflags |= (!!(env->CP0_Config3 + & (1 << CP0C3_ISA_ON_EXC)) + << MIPS_HFLAG_M16_SHIFT); + } +} +#endif + +void mips_cpu_do_interrupt(CPUState *cs) +{ + MIPSCPU *cpu = MIPS_CPU(cs); + CPUMIPSState *env = &cpu->env; #if !defined(CONFIG_USER_ONLY) target_ulong offset; int cause = -1; @@ -366,7 +428,7 @@ void do_interrupt (CPUState *env) 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->CP0_DEPC = env->active_tc.PC | !!(env->hflags & MIPS_HFLAG_M16); goto enter_debug_mode; case EXCP_DINT: env->CP0_Debug |= 1 << CP0DB_DINT; @@ -383,14 +445,8 @@ void do_interrupt (CPUState *env) case EXCP_DDBL: env->CP0_Debug |= 1 << CP0DB_DDBL; set_DEPC: - if (env->hflags & MIPS_HFLAG_BMASK) { - /* If the exception was raised from a delay slot, - come back to the jump. */ - env->CP0_DEPC = env->active_tc.PC - 4; - env->hflags &= ~MIPS_HFLAG_BMASK; - } else { - env->CP0_DEPC = env->active_tc.PC; - } + env->CP0_DEPC = exception_resume_pc(env); + env->hflags &= ~MIPS_HFLAG_BMASK; enter_debug_mode: env->hflags |= MIPS_HFLAG_DM | MIPS_HFLAG_64 | MIPS_HFLAG_CP0; env->hflags &= ~(MIPS_HFLAG_KSU); @@ -398,9 +454,10 @@ void do_interrupt (CPUState *env) if (!(env->CP0_Status & (1 << CP0St_EXL))) env->CP0_Cause &= ~(1 << CP0Ca_BD); env->active_tc.PC = (int32_t)0xBFC00480; + set_hflags_for_handler(env); break; case EXCP_RESET: - cpu_reset(env); + cpu_reset(CPU(cpu)); break; case EXCP_SRESET: env->CP0_Status |= (1 << CP0St_SR); @@ -409,25 +466,47 @@ void do_interrupt (CPUState *env) case EXCP_NMI: env->CP0_Status |= (1 << CP0St_NMI); set_error_EPC: - if (env->hflags & MIPS_HFLAG_BMASK) { - /* If the exception was raised from a delay slot, - come back to the jump. */ - env->CP0_ErrorEPC = env->active_tc.PC - 4; - env->hflags &= ~MIPS_HFLAG_BMASK; - } else { - env->CP0_ErrorEPC = env->active_tc.PC; - } + env->CP0_ErrorEPC = exception_resume_pc(env); + env->hflags &= ~MIPS_HFLAG_BMASK; env->CP0_Status |= (1 << CP0St_ERL) | (1 << CP0St_BEV); env->hflags |= MIPS_HFLAG_64 | MIPS_HFLAG_CP0; env->hflags &= ~(MIPS_HFLAG_KSU); if (!(env->CP0_Status & (1 << CP0St_EXL))) env->CP0_Cause &= ~(1 << 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)) offset = 0x200; + + if (env->CP0_Config3 & ((1 << CP0C3_VInt) | (1 << CP0C3_VEIC))) { + /* Vectored Interrupts. */ + unsigned int spacing; + unsigned int vector; + unsigned int pending = (env->CP0_Cause & CP0Ca_IP_mask) >> 8; + + pending &= env->CP0_Status >> 8; + /* Compute the Vector Spacing. */ + spacing = (env->CP0_IntCtl >> CP0IntCtl_VS) & ((1 << 6) - 1); + spacing <<= 5; + + if (env->CP0_Config3 & (1 << CP0C3_VInt)) { + /* For VInt mode, the MIPS computes the vector internally. */ + for (vector = 7; vector > 0; vector--) { + if (pending & (1 << vector)) { + /* Found it. */ + break; + } + } + } else { + /* For VEIC mode, the external interrupt controller feeds the + vector through the CP0Cause IP lines. */ + vector = pending; + } + offset = 0x200 + vector * spacing; + } goto set_EPC; case EXCP_LTLBL: cause = 1; @@ -441,7 +520,8 @@ void do_interrupt (CPUState *env) 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)) + if (((R == 0 && UX) || (R == 1 && SX) || (R == 3 && KX)) && + (!(env->insn_flags & (INSN_LOONGSON2E | INSN_LOONGSON2F)))) offset = 0x080; else #endif @@ -457,7 +537,8 @@ void do_interrupt (CPUState *env) 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)) + if (((R == 0 && UX) || (R == 1 && SX) || (R == 3 && KX)) && + (!(env->insn_flags & (INSN_LOONGSON2E | INSN_LOONGSON2F)))) offset = 0x080; else #endif @@ -515,6 +596,9 @@ void do_interrupt (CPUState *env) 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)) { @@ -524,13 +608,10 @@ void do_interrupt (CPUState *env) } set_EPC: if (!(env->CP0_Status & (1 << CP0St_EXL))) { + env->CP0_EPC = exception_resume_pc(env); if (env->hflags & MIPS_HFLAG_BMASK) { - /* If the exception was raised from a delay slot, - come back to the jump. */ - env->CP0_EPC = env->active_tc.PC - 4; env->CP0_Cause |= (1 << CP0Ca_BD); } else { - env->CP0_EPC = env->active_tc.PC; env->CP0_Cause &= ~(1 << CP0Ca_BD); } env->CP0_Status |= (1 << CP0St_EXL); @@ -544,6 +625,7 @@ void do_interrupt (CPUState *env) 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: @@ -562,7 +644,8 @@ void do_interrupt (CPUState *env) env->exception_index = EXCP_NONE; } -void r4k_invalidate_tlb (CPUState *env, int idx, int use_extra) +#if !defined(CONFIG_USER_ONLY) +void r4k_invalidate_tlb (CPUMIPSState *env, int idx, int use_extra) { r4k_tlb_t *tlb; target_ulong addr; @@ -615,3 +698,4 @@ void r4k_invalidate_tlb (CPUState *env, int idx, int use_extra) } } } +#endif