diff options
author | Philippe Mathieu-Daudé <f4bug@amsat.org> | 2021-04-13 11:46:18 +0200 |
---|---|---|
committer | Philippe Mathieu-Daudé <f4bug@amsat.org> | 2021-05-02 16:49:35 +0200 |
commit | 920b48cc14fe44a466f7387263993e86b4e21bce (patch) | |
tree | 306f17548d0f096a31b66d724942f741d2171b80 /target/mips/tlb_helper.c | |
parent | c284201702386b159277422318e51697647a2047 (diff) | |
download | qemu-920b48cc14fe44a466f7387263993e86b4e21bce.zip qemu-920b48cc14fe44a466f7387263993e86b4e21bce.tar.gz qemu-920b48cc14fe44a466f7387263993e86b4e21bce.tar.bz2 |
target/mips: Move tlb_helper.c to tcg/sysemu/
Move tlb_helper.c to the tcg/sysemu/ subdir, along with
the following 3 declarations to tcg-internal.h:
- cpu_mips_tlb_flush()
- cpu_mips_translate_address()
- r4k_invalidate_tlb()
Simplify tlb_helper.c #ifdef'ry because files in tcg/sysemu/
are only build when sysemu mode is configured.
Reviewed-by: Richard Henderson <richard.henderson@linaro.org>
Signed-off-by: Philippe Mathieu-Daudé <f4bug@amsat.org>
Message-Id: <20210428170410.479308-22-f4bug@amsat.org>
Diffstat (limited to 'target/mips/tlb_helper.c')
-rw-r--r-- | target/mips/tlb_helper.c | 1075 |
1 files changed, 0 insertions, 1075 deletions
diff --git a/target/mips/tlb_helper.c b/target/mips/tlb_helper.c deleted file mode 100644 index bfb08ea..0000000 --- a/target/mips/tlb_helper.c +++ /dev/null @@ -1,1075 +0,0 @@ -/* - * MIPS TLB (Translation lookaside buffer) helpers. - * - * Copyright (c) 2004-2005 Jocelyn Mayer - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, see <http://www.gnu.org/licenses/>. - */ -#include "qemu/osdep.h" - -#include "cpu.h" -#include "internal.h" -#include "exec/exec-all.h" -#include "exec/cpu_ldst.h" -#include "exec/log.h" -#include "hw/mips/cpudevs.h" - -#if !defined(CONFIG_USER_ONLY) - -/* no MMU emulation */ -int no_mmu_map_address(CPUMIPSState *env, hwaddr *physical, int *prot, - target_ulong address, MMUAccessType access_type) -{ - *physical = address; - *prot = PAGE_READ | PAGE_WRITE | PAGE_EXEC; - return TLBRET_MATCH; -} - -/* fixed mapping MMU emulation */ -int fixed_mmu_map_address(CPUMIPSState *env, hwaddr *physical, int *prot, - target_ulong address, MMUAccessType access_type) -{ - if (address <= (int32_t)0x7FFFFFFFUL) { - if (!(env->CP0_Status & (1 << CP0St_ERL))) { - *physical = address + 0x40000000UL; - } else { - *physical = address; - } - } else if (address <= (int32_t)0xBFFFFFFFUL) { - *physical = address & 0x1FFFFFFF; - } else { - *physical = address; - } - - *prot = PAGE_READ | PAGE_WRITE | PAGE_EXEC; - return TLBRET_MATCH; -} - -/* MIPS32/MIPS64 R4000-style MMU emulation */ -int r4k_map_address(CPUMIPSState *env, hwaddr *physical, int *prot, - target_ulong address, MMUAccessType access_type) -{ - uint16_t ASID = env->CP0_EntryHi & env->CP0_EntryHi_ASID_mask; - uint32_t MMID = env->CP0_MemoryMapID; - bool mi = !!((env->CP0_Config5 >> CP0C5_MI) & 1); - uint32_t tlb_mmid; - int i; - - MMID = mi ? MMID : (uint32_t) ASID; - - for (i = 0; i < env->tlb->tlb_in_use; i++) { - r4k_tlb_t *tlb = &env->tlb->mmu.r4k.tlb[i]; - /* 1k pages are not supported. */ - target_ulong mask = tlb->PageMask | ~(TARGET_PAGE_MASK << 1); - target_ulong tag = address & ~mask; - target_ulong VPN = tlb->VPN & ~mask; -#if defined(TARGET_MIPS64) - tag &= env->SEGMask; -#endif - - /* Check ASID/MMID, virtual page number & size */ - tlb_mmid = mi ? tlb->MMID : (uint32_t) tlb->ASID; - if ((tlb->G == 1 || tlb_mmid == MMID) && VPN == tag && !tlb->EHINV) { - /* TLB match */ - int n = !!(address & mask & ~(mask >> 1)); - /* Check access rights */ - if (!(n ? tlb->V1 : tlb->V0)) { - return TLBRET_INVALID; - } - if (access_type == MMU_INST_FETCH && (n ? tlb->XI1 : tlb->XI0)) { - return TLBRET_XI; - } - if (access_type == MMU_DATA_LOAD && (n ? tlb->RI1 : tlb->RI0)) { - return TLBRET_RI; - } - if (access_type != MMU_DATA_STORE || (n ? tlb->D1 : tlb->D0)) { - *physical = tlb->PFN[n] | (address & (mask >> 1)); - *prot = PAGE_READ; - if (n ? tlb->D1 : tlb->D0) { - *prot |= PAGE_WRITE; - } - if (!(n ? tlb->XI1 : tlb->XI0)) { - *prot |= PAGE_EXEC; - } - return TLBRET_MATCH; - } - return TLBRET_DIRTY; - } - } - return TLBRET_NOMATCH; -} - -static void no_mmu_init(CPUMIPSState *env, const mips_def_t *def) -{ - env->tlb->nb_tlb = 1; - env->tlb->map_address = &no_mmu_map_address; -} - -static void fixed_mmu_init(CPUMIPSState *env, const mips_def_t *def) -{ - env->tlb->nb_tlb = 1; - env->tlb->map_address = &fixed_mmu_map_address; -} - -static void r4k_mmu_init(CPUMIPSState *env, const mips_def_t *def) -{ - env->tlb->nb_tlb = 1 + ((def->CP0_Config1 >> CP0C1_MMU) & 63); - env->tlb->map_address = &r4k_map_address; - env->tlb->helper_tlbwi = r4k_helper_tlbwi; - env->tlb->helper_tlbwr = r4k_helper_tlbwr; - env->tlb->helper_tlbp = r4k_helper_tlbp; - env->tlb->helper_tlbr = r4k_helper_tlbr; - env->tlb->helper_tlbinv = r4k_helper_tlbinv; - env->tlb->helper_tlbinvf = r4k_helper_tlbinvf; -} - -void mmu_init(CPUMIPSState *env, const mips_def_t *def) -{ - env->tlb = g_malloc0(sizeof(CPUMIPSTLBContext)); - - switch (def->mmu_type) { - case MMU_TYPE_NONE: - no_mmu_init(env, def); - break; - case MMU_TYPE_R4000: - r4k_mmu_init(env, def); - break; - case MMU_TYPE_FMT: - fixed_mmu_init(env, def); - break; - case MMU_TYPE_R3000: - case MMU_TYPE_R6000: - case MMU_TYPE_R8000: - default: - cpu_abort(env_cpu(env), "MMU type not supported\n"); - } -} - -void cpu_mips_tlb_flush(CPUMIPSState *env) -{ - /* Flush qemu's TLB and discard all shadowed entries. */ - tlb_flush(env_cpu(env)); - env->tlb->tlb_in_use = env->tlb->nb_tlb; -} - -static void raise_mmu_exception(CPUMIPSState *env, target_ulong address, - MMUAccessType access_type, int tlb_error) -{ - CPUState *cs = env_cpu(env); - int exception = 0, error_code = 0; - - if (access_type == MMU_INST_FETCH) { - error_code |= EXCP_INST_NOTAVAIL; - } - - 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 (access_type == MMU_DATA_STORE) { - exception = EXCP_AdES; - } else { - exception = EXCP_AdEL; - } - break; - case TLBRET_NOMATCH: - /* No TLB match for a mapped address */ - if (access_type == MMU_DATA_STORE) { - exception = EXCP_TLBS; - } else { - exception = EXCP_TLBL; - } - error_code |= EXCP_TLB_NOMATCH; - break; - case TLBRET_INVALID: - /* TLB match with no valid bit */ - if (access_type == MMU_DATA_STORE) { - exception = EXCP_TLBS; - } else { - exception = EXCP_TLBL; - } - break; - case TLBRET_DIRTY: - /* TLB match but 'D' bit is cleared */ - exception = EXCP_LTLBL; - break; - case TLBRET_XI: - /* Execute-Inhibit Exception */ - if (env->CP0_PageGrain & (1 << CP0PG_IEC)) { - exception = EXCP_TLBXI; - } else { - exception = EXCP_TLBL; - } - break; - case TLBRET_RI: - /* Read-Inhibit Exception */ - if (env->CP0_PageGrain & (1 << CP0PG_IEC)) { - exception = EXCP_TLBRI; - } else { - exception = EXCP_TLBL; - } - break; - } - /* Raise exception */ - if (!(env->hflags & MIPS_HFLAG_DM)) { - env->CP0_BadVAddr = address; - } - env->CP0_Context = (env->CP0_Context & ~0x007fffff) | - ((address >> 9) & 0x007ffff0); - env->CP0_EntryHi = (env->CP0_EntryHi & env->CP0_EntryHi_ASID_mask) | - (env->CP0_EntryHi & (1 << CP0EnHi_EHINV)) | - (address & (TARGET_PAGE_MASK << 1)); -#if defined(TARGET_MIPS64) - env->CP0_EntryHi &= env->SEGMask; - env->CP0_XContext = - (env->CP0_XContext & ((~0ULL) << (env->SEGBITS - 7))) | /* PTEBase */ - (extract64(address, 62, 2) << (env->SEGBITS - 9)) | /* R */ - (extract64(address, 13, env->SEGBITS - 13) << 4); /* BadVPN2 */ -#endif - cs->exception_index = exception; - env->error_code = error_code; -} - -#if !defined(TARGET_MIPS64) - -/* - * Perform hardware page table walk - * - * Memory accesses are performed using the KERNEL privilege level. - * Synchronous exceptions detected on memory accesses cause a silent exit - * from page table walking, resulting in a TLB or XTLB Refill exception. - * - * Implementations are not required to support page table walk memory - * accesses from mapped memory regions. When an unsupported access is - * attempted, a silent exit is taken, resulting in a TLB or XTLB Refill - * exception. - * - * Note that if an exception is caused by AddressTranslation or LoadMemory - * functions, the exception is not taken, a silent exit is taken, - * resulting in a TLB or XTLB Refill exception. - */ - -static bool get_pte(CPUMIPSState *env, uint64_t vaddr, int entry_size, - uint64_t *pte) -{ - if ((vaddr & ((entry_size >> 3) - 1)) != 0) { - return false; - } - if (entry_size == 64) { - *pte = cpu_ldq_code(env, vaddr); - } else { - *pte = cpu_ldl_code(env, vaddr); - } - return true; -} - -static uint64_t get_tlb_entry_layout(CPUMIPSState *env, uint64_t entry, - int entry_size, int ptei) -{ - uint64_t result = entry; - uint64_t rixi; - if (ptei > entry_size) { - ptei -= 32; - } - result >>= (ptei - 2); - rixi = result & 3; - result >>= 2; - result |= rixi << CP0EnLo_XI; - return result; -} - -static int walk_directory(CPUMIPSState *env, uint64_t *vaddr, - int directory_index, bool *huge_page, bool *hgpg_directory_hit, - uint64_t *pw_entrylo0, uint64_t *pw_entrylo1) -{ - int dph = (env->CP0_PWCtl >> CP0PC_DPH) & 0x1; - int psn = (env->CP0_PWCtl >> CP0PC_PSN) & 0x3F; - int hugepg = (env->CP0_PWCtl >> CP0PC_HUGEPG) & 0x1; - int pf_ptew = (env->CP0_PWField >> CP0PF_PTEW) & 0x3F; - int ptew = (env->CP0_PWSize >> CP0PS_PTEW) & 0x3F; - int native_shift = (((env->CP0_PWSize >> CP0PS_PS) & 1) == 0) ? 2 : 3; - int directory_shift = (ptew > 1) ? -1 : - (hugepg && (ptew == 1)) ? native_shift + 1 : native_shift; - int leaf_shift = (ptew > 1) ? -1 : - (ptew == 1) ? native_shift + 1 : native_shift; - uint32_t direntry_size = 1 << (directory_shift + 3); - uint32_t leafentry_size = 1 << (leaf_shift + 3); - uint64_t entry; - uint64_t paddr; - int prot; - uint64_t lsb = 0; - uint64_t w = 0; - - if (get_physical_address(env, &paddr, &prot, *vaddr, MMU_DATA_LOAD, - cpu_mmu_index(env, false)) != - TLBRET_MATCH) { - /* wrong base address */ - return 0; - } - if (!get_pte(env, *vaddr, direntry_size, &entry)) { - return 0; - } - - if ((entry & (1 << psn)) && hugepg) { - *huge_page = true; - *hgpg_directory_hit = true; - entry = get_tlb_entry_layout(env, entry, leafentry_size, pf_ptew); - w = directory_index - 1; - if (directory_index & 0x1) { - /* Generate adjacent page from same PTE for odd TLB page */ - lsb = (1 << w) >> 6; - *pw_entrylo0 = entry & ~lsb; /* even page */ - *pw_entrylo1 = entry | lsb; /* odd page */ - } else if (dph) { - int oddpagebit = 1 << leaf_shift; - uint64_t vaddr2 = *vaddr ^ oddpagebit; - if (*vaddr & oddpagebit) { - *pw_entrylo1 = entry; - } else { - *pw_entrylo0 = entry; - } - if (get_physical_address(env, &paddr, &prot, vaddr2, MMU_DATA_LOAD, - cpu_mmu_index(env, false)) != - TLBRET_MATCH) { - return 0; - } - if (!get_pte(env, vaddr2, leafentry_size, &entry)) { - return 0; - } - entry = get_tlb_entry_layout(env, entry, leafentry_size, pf_ptew); - if (*vaddr & oddpagebit) { - *pw_entrylo0 = entry; - } else { - *pw_entrylo1 = entry; - } - } else { - return 0; - } - return 1; - } else { - *vaddr = entry; - return 2; - } -} - -static bool page_table_walk_refill(CPUMIPSState *env, vaddr address, - int mmu_idx) -{ - int gdw = (env->CP0_PWSize >> CP0PS_GDW) & 0x3F; - int udw = (env->CP0_PWSize >> CP0PS_UDW) & 0x3F; - int mdw = (env->CP0_PWSize >> CP0PS_MDW) & 0x3F; - int ptw = (env->CP0_PWSize >> CP0PS_PTW) & 0x3F; - int ptew = (env->CP0_PWSize >> CP0PS_PTEW) & 0x3F; - - /* Initial values */ - bool huge_page = false; - bool hgpg_bdhit = false; - bool hgpg_gdhit = false; - bool hgpg_udhit = false; - bool hgpg_mdhit = false; - - int32_t pw_pagemask = 0; - target_ulong pw_entryhi = 0; - uint64_t pw_entrylo0 = 0; - uint64_t pw_entrylo1 = 0; - - /* Native pointer size */ - /*For the 32-bit architectures, this bit is fixed to 0.*/ - int native_shift = (((env->CP0_PWSize >> CP0PS_PS) & 1) == 0) ? 2 : 3; - - /* Indices from PWField */ - int pf_gdw = (env->CP0_PWField >> CP0PF_GDW) & 0x3F; - int pf_udw = (env->CP0_PWField >> CP0PF_UDW) & 0x3F; - int pf_mdw = (env->CP0_PWField >> CP0PF_MDW) & 0x3F; - int pf_ptw = (env->CP0_PWField >> CP0PF_PTW) & 0x3F; - int pf_ptew = (env->CP0_PWField >> CP0PF_PTEW) & 0x3F; - - /* Indices computed from faulting address */ - int gindex = (address >> pf_gdw) & ((1 << gdw) - 1); - int uindex = (address >> pf_udw) & ((1 << udw) - 1); - int mindex = (address >> pf_mdw) & ((1 << mdw) - 1); - int ptindex = (address >> pf_ptw) & ((1 << ptw) - 1); - - /* Other HTW configs */ - int hugepg = (env->CP0_PWCtl >> CP0PC_HUGEPG) & 0x1; - - /* HTW Shift values (depend on entry size) */ - int directory_shift = (ptew > 1) ? -1 : - (hugepg && (ptew == 1)) ? native_shift + 1 : native_shift; - int leaf_shift = (ptew > 1) ? -1 : - (ptew == 1) ? native_shift + 1 : native_shift; - - /* Offsets into tables */ - int goffset = gindex << directory_shift; - int uoffset = uindex << directory_shift; - int moffset = mindex << directory_shift; - int ptoffset0 = (ptindex >> 1) << (leaf_shift + 1); - int ptoffset1 = ptoffset0 | (1 << (leaf_shift)); - - uint32_t leafentry_size = 1 << (leaf_shift + 3); - - /* Starting address - Page Table Base */ - uint64_t vaddr = env->CP0_PWBase; - - uint64_t dir_entry; - uint64_t paddr; - int prot; - int m; - - if (!(env->CP0_Config3 & (1 << CP0C3_PW))) { - /* walker is unimplemented */ - return false; - } - if (!(env->CP0_PWCtl & (1 << CP0PC_PWEN))) { - /* walker is disabled */ - return false; - } - if (!(gdw > 0 || udw > 0 || mdw > 0)) { - /* no structure to walk */ - return false; - } - if ((directory_shift == -1) || (leaf_shift == -1)) { - return false; - } - - /* Global Directory */ - if (gdw > 0) { - vaddr |= goffset; - switch (walk_directory(env, &vaddr, pf_gdw, &huge_page, &hgpg_gdhit, - &pw_entrylo0, &pw_entrylo1)) - { - case 0: - return false; - case 1: - goto refill; - case 2: - default: - break; - } - } - - /* Upper directory */ - if (udw > 0) { - vaddr |= uoffset; - switch (walk_directory(env, &vaddr, pf_udw, &huge_page, &hgpg_udhit, - &pw_entrylo0, &pw_entrylo1)) - { - case 0: - return false; - case 1: - goto refill; - case 2: - default: - break; - } - } - - /* Middle directory */ - if (mdw > 0) { - vaddr |= moffset; - switch (walk_directory(env, &vaddr, pf_mdw, &huge_page, &hgpg_mdhit, - &pw_entrylo0, &pw_entrylo1)) - { - case 0: - return false; - case 1: - goto refill; - case 2: - default: - break; - } - } - - /* Leaf Level Page Table - First half of PTE pair */ - vaddr |= ptoffset0; - if (get_physical_address(env, &paddr, &prot, vaddr, MMU_DATA_LOAD, - cpu_mmu_index(env, false)) != - TLBRET_MATCH) { - return false; - } - if (!get_pte(env, vaddr, leafentry_size, &dir_entry)) { - return false; - } - dir_entry = get_tlb_entry_layout(env, dir_entry, leafentry_size, pf_ptew); - pw_entrylo0 = dir_entry; - - /* Leaf Level Page Table - Second half of PTE pair */ - vaddr |= ptoffset1; - if (get_physical_address(env, &paddr, &prot, vaddr, MMU_DATA_LOAD, - cpu_mmu_index(env, false)) != - TLBRET_MATCH) { - return false; - } - if (!get_pte(env, vaddr, leafentry_size, &dir_entry)) { - return false; - } - dir_entry = get_tlb_entry_layout(env, dir_entry, leafentry_size, pf_ptew); - pw_entrylo1 = dir_entry; - -refill: - - m = (1 << pf_ptw) - 1; - - if (huge_page) { - switch (hgpg_bdhit << 3 | hgpg_gdhit << 2 | hgpg_udhit << 1 | - hgpg_mdhit) - { - case 4: - m = (1 << pf_gdw) - 1; - if (pf_gdw & 1) { - m >>= 1; - } - break; - case 2: - m = (1 << pf_udw) - 1; - if (pf_udw & 1) { - m >>= 1; - } - break; - case 1: - m = (1 << pf_mdw) - 1; - if (pf_mdw & 1) { - m >>= 1; - } - break; - } - } - pw_pagemask = m >> TARGET_PAGE_BITS_MIN; - update_pagemask(env, pw_pagemask << CP0PM_MASK, &pw_pagemask); - pw_entryhi = (address & ~0x1fff) | (env->CP0_EntryHi & 0xFF); - { - target_ulong tmp_entryhi = env->CP0_EntryHi; - int32_t tmp_pagemask = env->CP0_PageMask; - uint64_t tmp_entrylo0 = env->CP0_EntryLo0; - uint64_t tmp_entrylo1 = env->CP0_EntryLo1; - - env->CP0_EntryHi = pw_entryhi; - env->CP0_PageMask = pw_pagemask; - env->CP0_EntryLo0 = pw_entrylo0; - env->CP0_EntryLo1 = pw_entrylo1; - - /* - * The hardware page walker inserts a page into the TLB in a manner - * identical to a TLBWR instruction as executed by the software refill - * handler. - */ - r4k_helper_tlbwr(env); - - env->CP0_EntryHi = tmp_entryhi; - env->CP0_PageMask = tmp_pagemask; - env->CP0_EntryLo0 = tmp_entrylo0; - env->CP0_EntryLo1 = tmp_entrylo1; - } - return true; -} -#endif - -bool mips_cpu_tlb_fill(CPUState *cs, vaddr address, int size, - MMUAccessType access_type, int mmu_idx, - bool probe, uintptr_t retaddr) -{ - MIPSCPU *cpu = MIPS_CPU(cs); - CPUMIPSState *env = &cpu->env; - hwaddr physical; - int prot; - int ret = TLBRET_BADADDR; - - /* data access */ - /* XXX: put correct access by using cpu_restore_state() correctly */ - ret = get_physical_address(env, &physical, &prot, address, - access_type, mmu_idx); - switch (ret) { - case TLBRET_MATCH: - qemu_log_mask(CPU_LOG_MMU, - "%s address=%" VADDR_PRIx " physical " TARGET_FMT_plx - " prot %d\n", __func__, address, physical, prot); - break; - default: - qemu_log_mask(CPU_LOG_MMU, - "%s address=%" VADDR_PRIx " ret %d\n", __func__, address, - ret); - break; - } - if (ret == TLBRET_MATCH) { - tlb_set_page(cs, address & TARGET_PAGE_MASK, - physical & TARGET_PAGE_MASK, prot, - mmu_idx, TARGET_PAGE_SIZE); - return true; - } -#if !defined(TARGET_MIPS64) - if ((ret == TLBRET_NOMATCH) && (env->tlb->nb_tlb > 1)) { - /* - * Memory reads during hardware page table walking are performed - * as if they were kernel-mode load instructions. - */ - int mode = (env->hflags & MIPS_HFLAG_KSU); - bool ret_walker; - env->hflags &= ~MIPS_HFLAG_KSU; - ret_walker = page_table_walk_refill(env, address, mmu_idx); - env->hflags |= mode; - if (ret_walker) { - ret = get_physical_address(env, &physical, &prot, address, - access_type, mmu_idx); - if (ret == TLBRET_MATCH) { - tlb_set_page(cs, address & TARGET_PAGE_MASK, - physical & TARGET_PAGE_MASK, prot, - mmu_idx, TARGET_PAGE_SIZE); - return true; - } - } - } -#endif - if (probe) { - return false; - } - - raise_mmu_exception(env, address, access_type, ret); - do_raise_exception_err(env, cs->exception_index, env->error_code, retaddr); -} - -hwaddr cpu_mips_translate_address(CPUMIPSState *env, target_ulong address, - MMUAccessType access_type, uintptr_t retaddr) -{ - hwaddr physical; - int prot; - int ret = 0; - CPUState *cs = env_cpu(env); - - /* data access */ - ret = get_physical_address(env, &physical, &prot, address, access_type, - cpu_mmu_index(env, false)); - if (ret == TLBRET_MATCH) { - return physical; - } - - raise_mmu_exception(env, address, access_type, ret); - cpu_loop_exit_restore(cs, retaddr); -} - -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); - } -} - -static inline void set_badinstr_registers(CPUMIPSState *env) -{ - if (env->insn_flags & ISA_NANOMIPS32) { - if (env->CP0_Config3 & (1 << CP0C3_BI)) { - uint32_t instr = (cpu_lduw_code(env, env->active_tc.PC)) << 16; - if ((instr & 0x10000000) == 0) { - instr |= cpu_lduw_code(env, env->active_tc.PC + 2); - } - env->CP0_BadInstr = instr; - - if ((instr & 0xFC000000) == 0x60000000) { - instr = cpu_lduw_code(env, env->active_tc.PC + 4) << 16; - env->CP0_BadInstrX = instr; - } - } - return; - } - - if (env->hflags & MIPS_HFLAG_M16) { - /* TODO: add BadInstr support for microMIPS */ - return; - } - if (env->CP0_Config3 & (1 << CP0C3_BI)) { - env->CP0_BadInstr = cpu_ldl_code(env, env->active_tc.PC); - } - if ((env->CP0_Config3 & (1 << CP0C3_BP)) && - (env->hflags & MIPS_HFLAG_BMASK)) { - env->CP0_BadInstrP = cpu_ldl_code(env, env->active_tc.PC - 4); - } -} - -void mips_cpu_do_interrupt(CPUState *cs) -{ - MIPSCPU *cpu = MIPS_CPU(cs); - CPUMIPSState *env = &cpu->env; - bool update_badinstr = 0; - target_ulong offset; - int cause = -1; - - if (qemu_loglevel_mask(CPU_LOG_INT) - && cs->exception_index != EXCP_EXT_INTERRUPT) { - qemu_log("%s enter: PC " TARGET_FMT_lx " EPC " TARGET_FMT_lx - " %s exception\n", - __func__, env->active_tc.PC, env->CP0_EPC, - mips_exception_name(cs->exception_index)); - } - 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; - /* Setup DExcCode - SDBBP instruction */ - env->CP0_Debug = (env->CP0_Debug & ~(0x1fULL << CP0DB_DEC)) | - (9 << CP0DB_DEC); - 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_MIPS_R6) || - 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 = env->exception_base + 0x480; - 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_MIPS_R6) || - 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 = env->exception_base; - 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 KX = (env->CP0_Status & (1 << CP0St_KX)) != 0; - - if ((R != 0 || UX) && (R != 3 || KX) && - (!(env->insn_flags & (INSN_LOONGSON2E | INSN_LOONGSON2F)))) { - offset = 0x080; - } else { -#endif - offset = 0x000; -#if defined(TARGET_MIPS64) - } -#endif - } - 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 KX = (env->CP0_Status & (1 << CP0St_KX)) != 0; - - if ((R != 0 || UX) && (R != 3 || KX) && - (!(env->insn_flags & (INSN_LOONGSON2E | INSN_LOONGSON2F)))) { - offset = 0x080; - } else { -#endif - offset = 0x000; -#if defined(TARGET_MIPS64) - } -#endif - } - 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; - offset = 0x100; - 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_MIPS_R6) || - 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 = env->exception_base + 0x200; - } else if (cause == 30 && !(env->CP0_Config3 & (1 << CP0C3_SC) && - env->CP0_Config5 & (1 << CP0C5_CV))) { - /* Force KSeg1 for cache errors */ - env->active_tc.PC = KSEG1_BASE | (env->CP0_EBase & 0x1FFFF000); - } else { - env->active_tc.PC = env->CP0_EBase & ~0xfff; - } - - 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); - } - cs->exception_index = EXCP_NONE; -} - -void r4k_invalidate_tlb(CPUMIPSState *env, int idx, int use_extra) -{ - CPUState *cs = env_cpu(env); - r4k_tlb_t *tlb; - target_ulong addr; - target_ulong end; - uint16_t ASID = env->CP0_EntryHi & env->CP0_EntryHi_ASID_mask; - uint32_t MMID = env->CP0_MemoryMapID; - bool mi = !!((env->CP0_Config5 >> CP0C5_MI) & 1); - uint32_t tlb_mmid; - target_ulong mask; - - MMID = mi ? MMID : (uint32_t) ASID; - - tlb = &env->tlb->mmu.r4k.tlb[idx]; - /* - * The qemu TLB is flushed when the ASID/MMID changes, so no need to - * flush these entries again. - */ - tlb_mmid = mi ? tlb->MMID : (uint32_t) tlb->ASID; - if (tlb->G == 0 && tlb_mmid != MMID) { - return; - } - - if (use_extra && env->tlb->tlb_in_use < MIPS_TLB_MAX) { - /* - * For tlbwr, we can shadow the discarded entry into - * a new (fake) TLB entry, as long as the guest can not - * tell that it's there. - */ - env->tlb->mmu.r4k.tlb[env->tlb->tlb_in_use] = *tlb; - env->tlb->tlb_in_use++; - 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(cs, 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(cs, addr); - addr += TARGET_PAGE_SIZE; - } - } -} -#endif /* !CONFIG_USER_ONLY */ |