diff options
Diffstat (limited to 'target-mips/op_helper.c')
-rw-r--r-- | target-mips/op_helper.c | 176 |
1 files changed, 135 insertions, 41 deletions
diff --git a/target-mips/op_helper.c b/target-mips/op_helper.c index 61a39df..783a1a9 100644 --- a/target-mips/op_helper.c +++ b/target-mips/op_helper.c @@ -1498,20 +1498,69 @@ target_ulong helper_yield(target_ulong arg1) } #ifndef CONFIG_USER_ONLY -/* TLB management */ -void cpu_mips_tlb_flush (CPUState *env, int flush_global) +static void inline r4k_invalidate_tlb_shadow (CPUState *env, int idx) { - /* Flush qemu's TLB and discard all shadowed entries. */ - tlb_flush (env, flush_global); - env->tlb->tlb_in_use = env->tlb->nb_tlb; + r4k_tlb_t *tlb; + uint8_t ASID = env->CP0_EntryHi & 0xFF; + + tlb = &env->tlb->mmu.r4k.tlb[idx]; + /* The qemu TLB is flushed when the ASID changes, so no need to + flush these entries again. */ + if (tlb->G == 0 && tlb->ASID != ASID) { + return; + } } -static void r4k_mips_tlb_flush_extra (CPUState *env, int first) +static void inline r4k_invalidate_tlb (CPUState *env, int idx) { - /* Discard entries from env->tlb[first] onwards. */ - while (env->tlb->tlb_in_use > first) { - r4k_invalidate_tlb(env, --env->tlb->tlb_in_use, 0); + r4k_tlb_t *tlb; + target_ulong addr; + target_ulong end; + uint8_t ASID = env->CP0_EntryHi & 0xFF; + target_ulong mask; + + tlb = &env->tlb->mmu.r4k.tlb[idx]; + /* The qemu TLB is flushed when the ASID changes, so no need to + flush these entries again. */ + if (tlb->G == 0 && tlb->ASID != ASID) { + return; + } + + /* 1k pages are not supported. */ + mask = tlb->PageMask | ~(TARGET_PAGE_MASK << 1); + if (tlb->V0) { + addr = tlb->VPN & ~mask; +#if defined(TARGET_MIPS64) + if (addr >= (0xFFFFFFFF80000000ULL & env->SEGMask)) { + addr |= 0x3FFFFF0000000000ULL; + } +#endif + end = addr | (mask >> 1); + while (addr < end) { + tlb_flush_page (env, addr); + addr += TARGET_PAGE_SIZE; + } } + if (tlb->V1) { + addr = (tlb->VPN & ~mask) | ((mask >> 1) + 1); +#if defined(TARGET_MIPS64) + if (addr >= (0xFFFFFFFF80000000ULL & env->SEGMask)) { + addr |= 0x3FFFFF0000000000ULL; + } +#endif + end = addr | mask; + while (addr - 1 < end) { + tlb_flush_page (env, addr); + addr += TARGET_PAGE_SIZE; + } + } +} + +/* TLB management */ +void cpu_mips_tlb_flush (CPUState *env, int flush_global) +{ + /* Flush qemu's TLB and discard all shadowed entries. */ + tlb_flush (env, flush_global); } static void r4k_fill_tlb (int idx) @@ -1537,26 +1586,65 @@ static void r4k_fill_tlb (int idx) tlb->PFN[1] = (env->CP0_EntryLo1 >> 6) << 12; } +void r4k_helper_ptw_tlbrefill(CPUState *target_env) +{ + CPUState *saved_env; + + /* Save current 'env' value */ + saved_env = env; + env = target_env; + + /* Do TLB load on behalf of Page Table Walk */ + int r = cpu_mips_get_random(env); + r4k_invalidate_tlb_shadow(env, r); + r4k_fill_tlb(r); + + /* Restore 'env' value */ + env = saved_env; +} + void r4k_helper_tlbwi (void) { - int idx; + r4k_tlb_t *tlb; + target_ulong tag; + target_ulong VPN; + target_ulong mask; - idx = (env->CP0_Index & ~0x80000000) % env->tlb->nb_tlb; + /* If tlbwi is trying to upgrading access permissions on current entry, + * we do not need to flush tlb hash table. + */ + tlb = &env->tlb->mmu.r4k.tlb[env->CP0_Index % env->tlb->nb_tlb]; + mask = tlb->PageMask | ~(TARGET_PAGE_MASK << 1); + tag = env->CP0_EntryHi & ~mask; + VPN = tlb->VPN & ~mask; + if (VPN == tag) + { + if (tlb->ASID == (env->CP0_EntryHi & 0xFF)) + { + tlb->V0 = (env->CP0_EntryLo0 & 2) != 0; + tlb->D0 = (env->CP0_EntryLo0 & 4) != 0; + tlb->C0 = (env->CP0_EntryLo0 >> 3) & 0x7; + tlb->PFN[0] = (env->CP0_EntryLo0 >> 6) << 12; + tlb->V1 = (env->CP0_EntryLo1 & 2) != 0; + tlb->D1 = (env->CP0_EntryLo1 & 4) != 0; + tlb->C1 = (env->CP0_EntryLo1 >> 3) & 0x7; + tlb->PFN[1] = (env->CP0_EntryLo1 >> 6) << 12; + return; + } + } - /* Discard cached TLB entries. We could avoid doing this if the - tlbwi is just upgrading access permissions on the current entry; - that might be a further win. */ - r4k_mips_tlb_flush_extra (env, env->tlb->nb_tlb); + /*flush all the tlb cache */ + cpu_mips_tlb_flush (env, 1); - r4k_invalidate_tlb(env, idx, 0); - r4k_fill_tlb(idx); + r4k_invalidate_tlb(env, env->CP0_Index % env->tlb->nb_tlb); + r4k_fill_tlb(env->CP0_Index % env->tlb->nb_tlb); } void r4k_helper_tlbwr (void) { int r = cpu_mips_get_random(env); - r4k_invalidate_tlb(env, r, 1); + r4k_invalidate_tlb_shadow(env, r); r4k_fill_tlb(r); } @@ -1568,6 +1656,8 @@ void r4k_helper_tlbp (void) target_ulong VPN; uint8_t ASID; int i; + target_ulong addr; + target_ulong end; ASID = env->CP0_EntryHi & 0xFF; for (i = 0; i < env->tlb->nb_tlb; i++) { @@ -1577,27 +1667,16 @@ void r4k_helper_tlbp (void) tag = env->CP0_EntryHi & ~mask; VPN = tlb->VPN & ~mask; /* Check ASID, virtual page number & size */ - if ((tlb->G == 1 || tlb->ASID == ASID) && VPN == tag) { + if (unlikely((tlb->G == 1 || tlb->ASID == ASID) && VPN == tag)) { /* TLB match */ env->CP0_Index = i; break; } } if (i == env->tlb->nb_tlb) { - /* No match. Discard any shadow entries, if any of them match. */ - for (i = env->tlb->nb_tlb; i < env->tlb->tlb_in_use; i++) { - tlb = &env->tlb->mmu.r4k.tlb[i]; - /* 1k pages are not supported. */ - mask = tlb->PageMask | ~(TARGET_PAGE_MASK << 1); - tag = env->CP0_EntryHi & ~mask; - VPN = tlb->VPN & ~mask; - /* Check ASID, virtual page number & size */ - if ((tlb->G == 1 || tlb->ASID == ASID) && VPN == tag) { - r4k_mips_tlb_flush_extra (env, i); - break; - } - } - + /* No match. Discard any shadow entries, if any of them match. */ + int index = ((env->CP0_EntryHi>>5)&0x1ff00) | ASID; + index |= (env->CP0_EntryHi>>13)&0x20000; env->CP0_Index |= 0x80000000; } } @@ -1606,17 +1685,16 @@ void r4k_helper_tlbr (void) { r4k_tlb_t *tlb; uint8_t ASID; - int idx; ASID = env->CP0_EntryHi & 0xFF; - idx = (env->CP0_Index & ~0x80000000) % env->tlb->nb_tlb; - tlb = &env->tlb->mmu.r4k.tlb[idx]; + tlb = &env->tlb->mmu.r4k.tlb[env->CP0_Index % env->tlb->nb_tlb]; /* If this will change the current ASID, flush qemu's TLB. */ if (ASID != tlb->ASID) cpu_mips_tlb_flush (env, 1); - r4k_mips_tlb_flush_extra(env, env->tlb->nb_tlb); + /*flush all the tlb cache */ + cpu_mips_tlb_flush (env, 1); env->CP0_EntryHi = tlb->VPN | tlb->ASID; env->CP0_PageMask = tlb->PageMask; @@ -1870,7 +1948,7 @@ static unsigned long v2p_mmu(target_ulong addr, int is_user) { int index; target_ulong tlb_addr; - target_phys_addr_t physaddr; + unsigned long physaddr; void *retaddr; index = (addr >> TARGET_PAGE_BITS) & (CPU_TLB_SIZE - 1); @@ -1892,12 +1970,12 @@ redo: * to the address of simulation host (not the physical * address of simulated OS. */ -target_phys_addr_t v2p(target_ulong ptr, int is_user) +unsigned long v2p(target_ulong ptr, int is_user) { CPUState *saved_env; int index; target_ulong addr; - target_phys_addr_t physaddr; + unsigned long physaddr; saved_env = env; env = cpu_single_env; @@ -1907,7 +1985,7 @@ target_phys_addr_t v2p(target_ulong ptr, int is_user) (addr & TARGET_PAGE_MASK), 0)) { physaddr = v2p_mmu(addr, is_user); } else { - physaddr = (target_phys_addr_t)addr + env->tlb_table[is_user][index].addend; + physaddr = addr + env->tlb_table[is_user][index].addend; } env = saved_env; return physaddr; @@ -2230,6 +2308,7 @@ uint64_t helper_float_roundl_d(uint64_t fdt0) { uint64_t dt2; + set_float_exception_flags(0, &env->active_fpu.fp_status); set_float_rounding_mode(float_round_nearest_even, &env->active_fpu.fp_status); dt2 = float64_to_int64(fdt0, &env->active_fpu.fp_status); RESTORE_ROUNDING_MODE; @@ -2243,6 +2322,7 @@ uint64_t helper_float_roundl_s(uint32_t fst0) { uint64_t dt2; + set_float_exception_flags(0, &env->active_fpu.fp_status); set_float_rounding_mode(float_round_nearest_even, &env->active_fpu.fp_status); dt2 = float32_to_int64(fst0, &env->active_fpu.fp_status); RESTORE_ROUNDING_MODE; @@ -2256,6 +2336,7 @@ uint32_t helper_float_roundw_d(uint64_t fdt0) { uint32_t wt2; + set_float_exception_flags(0, &env->active_fpu.fp_status); set_float_rounding_mode(float_round_nearest_even, &env->active_fpu.fp_status); wt2 = float64_to_int32(fdt0, &env->active_fpu.fp_status); RESTORE_ROUNDING_MODE; @@ -2269,6 +2350,7 @@ uint32_t helper_float_roundw_s(uint32_t fst0) { uint32_t wt2; + set_float_exception_flags(0, &env->active_fpu.fp_status); set_float_rounding_mode(float_round_nearest_even, &env->active_fpu.fp_status); wt2 = float32_to_int32(fst0, &env->active_fpu.fp_status); RESTORE_ROUNDING_MODE; @@ -2282,6 +2364,7 @@ uint64_t helper_float_truncl_d(uint64_t fdt0) { uint64_t dt2; + set_float_exception_flags(0, &env->active_fpu.fp_status); dt2 = float64_to_int64_round_to_zero(fdt0, &env->active_fpu.fp_status); update_fcr31(); if (GET_FP_CAUSE(env->active_fpu.fcr31) & (FP_OVERFLOW | FP_INVALID)) @@ -2293,6 +2376,7 @@ uint64_t helper_float_truncl_s(uint32_t fst0) { uint64_t dt2; + set_float_exception_flags(0, &env->active_fpu.fp_status); dt2 = float32_to_int64_round_to_zero(fst0, &env->active_fpu.fp_status); update_fcr31(); if (GET_FP_CAUSE(env->active_fpu.fcr31) & (FP_OVERFLOW | FP_INVALID)) @@ -2304,6 +2388,7 @@ uint32_t helper_float_truncw_d(uint64_t fdt0) { uint32_t wt2; + set_float_exception_flags(0, &env->active_fpu.fp_status); wt2 = float64_to_int32_round_to_zero(fdt0, &env->active_fpu.fp_status); update_fcr31(); if (GET_FP_CAUSE(env->active_fpu.fcr31) & (FP_OVERFLOW | FP_INVALID)) @@ -2315,6 +2400,7 @@ uint32_t helper_float_truncw_s(uint32_t fst0) { uint32_t wt2; + set_float_exception_flags(0, &env->active_fpu.fp_status); wt2 = float32_to_int32_round_to_zero(fst0, &env->active_fpu.fp_status); update_fcr31(); if (GET_FP_CAUSE(env->active_fpu.fcr31) & (FP_OVERFLOW | FP_INVALID)) @@ -2326,6 +2412,7 @@ uint64_t helper_float_ceill_d(uint64_t fdt0) { uint64_t dt2; + set_float_exception_flags(0, &env->active_fpu.fp_status); set_float_rounding_mode(float_round_up, &env->active_fpu.fp_status); dt2 = float64_to_int64(fdt0, &env->active_fpu.fp_status); RESTORE_ROUNDING_MODE; @@ -2339,6 +2426,7 @@ uint64_t helper_float_ceill_s(uint32_t fst0) { uint64_t dt2; + set_float_exception_flags(0, &env->active_fpu.fp_status); set_float_rounding_mode(float_round_up, &env->active_fpu.fp_status); dt2 = float32_to_int64(fst0, &env->active_fpu.fp_status); RESTORE_ROUNDING_MODE; @@ -2352,6 +2440,7 @@ uint32_t helper_float_ceilw_d(uint64_t fdt0) { uint32_t wt2; + set_float_exception_flags(0, &env->active_fpu.fp_status); set_float_rounding_mode(float_round_up, &env->active_fpu.fp_status); wt2 = float64_to_int32(fdt0, &env->active_fpu.fp_status); RESTORE_ROUNDING_MODE; @@ -2365,6 +2454,7 @@ uint32_t helper_float_ceilw_s(uint32_t fst0) { uint32_t wt2; + set_float_exception_flags(0, &env->active_fpu.fp_status); set_float_rounding_mode(float_round_up, &env->active_fpu.fp_status); wt2 = float32_to_int32(fst0, &env->active_fpu.fp_status); RESTORE_ROUNDING_MODE; @@ -2378,6 +2468,7 @@ uint64_t helper_float_floorl_d(uint64_t fdt0) { uint64_t dt2; + set_float_exception_flags(0, &env->active_fpu.fp_status); set_float_rounding_mode(float_round_down, &env->active_fpu.fp_status); dt2 = float64_to_int64(fdt0, &env->active_fpu.fp_status); RESTORE_ROUNDING_MODE; @@ -2391,6 +2482,7 @@ uint64_t helper_float_floorl_s(uint32_t fst0) { uint64_t dt2; + set_float_exception_flags(0, &env->active_fpu.fp_status); set_float_rounding_mode(float_round_down, &env->active_fpu.fp_status); dt2 = float32_to_int64(fst0, &env->active_fpu.fp_status); RESTORE_ROUNDING_MODE; @@ -2404,6 +2496,7 @@ uint32_t helper_float_floorw_d(uint64_t fdt0) { uint32_t wt2; + set_float_exception_flags(0, &env->active_fpu.fp_status); set_float_rounding_mode(float_round_down, &env->active_fpu.fp_status); wt2 = float64_to_int32(fdt0, &env->active_fpu.fp_status); RESTORE_ROUNDING_MODE; @@ -2417,6 +2510,7 @@ uint32_t helper_float_floorw_s(uint32_t fst0) { uint32_t wt2; + set_float_exception_flags(0, &env->active_fpu.fp_status); set_float_rounding_mode(float_round_down, &env->active_fpu.fp_status); wt2 = float32_to_int32(fst0, &env->active_fpu.fp_status); RESTORE_ROUNDING_MODE; |