14acb54baSEdgar E. Iglesias /* 24acb54baSEdgar E. Iglesias * MicroBlaze helper routines. 34acb54baSEdgar E. Iglesias * 44acb54baSEdgar E. Iglesias * Copyright (c) 2009 Edgar E. Iglesias <edgar.iglesias@gmail.com> 5dadc1064SPeter A. G. Crosthwaite * Copyright (c) 2009-2012 PetaLogix Qld Pty Ltd. 64acb54baSEdgar E. Iglesias * 74acb54baSEdgar E. Iglesias * This library is free software; you can redistribute it and/or 84acb54baSEdgar E. Iglesias * modify it under the terms of the GNU Lesser General Public 94acb54baSEdgar E. Iglesias * License as published by the Free Software Foundation; either 104acb54baSEdgar E. Iglesias * version 2 of the License, or (at your option) any later version. 114acb54baSEdgar E. Iglesias * 124acb54baSEdgar E. Iglesias * This library is distributed in the hope that it will be useful, 134acb54baSEdgar E. Iglesias * but WITHOUT ANY WARRANTY; without even the implied warranty of 144acb54baSEdgar E. Iglesias * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 154acb54baSEdgar E. Iglesias * Lesser General Public License for more details. 164acb54baSEdgar E. Iglesias * 174acb54baSEdgar E. Iglesias * You should have received a copy of the GNU Lesser General Public 188167ee88SBlue Swirl * License along with this library; if not, see <http://www.gnu.org/licenses/>. 194acb54baSEdgar E. Iglesias */ 204acb54baSEdgar E. Iglesias 218fd9deceSPeter Maydell #include "qemu/osdep.h" 224acb54baSEdgar E. Iglesias #include "cpu.h" 2363c91552SPaolo Bonzini #include "exec/exec-all.h" 241de7afc9SPaolo Bonzini #include "qemu/host-utils.h" 25508127e2SPaolo Bonzini #include "exec/log.h" 264acb54baSEdgar E. Iglesias 274acb54baSEdgar E. Iglesias #if defined(CONFIG_USER_ONLY) 284acb54baSEdgar E. Iglesias 2997a8ea5aSAndreas Färber void mb_cpu_do_interrupt(CPUState *cs) 304acb54baSEdgar E. Iglesias { 3197a8ea5aSAndreas Färber MicroBlazeCPU *cpu = MICROBLAZE_CPU(cs); 3297a8ea5aSAndreas Färber CPUMBState *env = &cpu->env; 3397a8ea5aSAndreas Färber 3427103424SAndreas Färber cs->exception_index = -1; 358cc9b43fSPeter A. G. Crosthwaite env->res_addr = RES_ADDR_NONE; 3676e8187dSRichard Henderson env->regs[14] = env->pc; 374acb54baSEdgar E. Iglesias } 384acb54baSEdgar E. Iglesias 39f429d607SRichard Henderson bool mb_cpu_tlb_fill(CPUState *cs, vaddr address, int size, 40f429d607SRichard Henderson MMUAccessType access_type, int mmu_idx, 41f429d607SRichard Henderson bool probe, uintptr_t retaddr) 424acb54baSEdgar E. Iglesias { 4327103424SAndreas Färber cs->exception_index = 0xaa; 44f429d607SRichard Henderson cpu_loop_exit_restore(cs, retaddr); 454acb54baSEdgar E. Iglesias } 464acb54baSEdgar E. Iglesias 474acb54baSEdgar E. Iglesias #else /* !CONFIG_USER_ONLY */ 484acb54baSEdgar E. Iglesias 49f429d607SRichard Henderson bool mb_cpu_tlb_fill(CPUState *cs, vaddr address, int size, 50f429d607SRichard Henderson MMUAccessType access_type, int mmu_idx, 51f429d607SRichard Henderson bool probe, uintptr_t retaddr) 524acb54baSEdgar E. Iglesias { 537510454eSAndreas Färber MicroBlazeCPU *cpu = MICROBLAZE_CPU(cs); 547510454eSAndreas Färber CPUMBState *env = &cpu->env; 55f429d607SRichard Henderson struct microblaze_mmu_lookup lu; 564acb54baSEdgar E. Iglesias unsigned int hit; 574acb54baSEdgar E. Iglesias int prot; 584acb54baSEdgar E. Iglesias 59f429d607SRichard Henderson if (mmu_idx == MMU_NOMMU_IDX) { 60f429d607SRichard Henderson /* MMU disabled or not available. */ 61f429d607SRichard Henderson address &= TARGET_PAGE_MASK; 62f429d607SRichard Henderson prot = PAGE_BITS; 63f429d607SRichard Henderson tlb_set_page(cs, address, address, prot, mmu_idx, TARGET_PAGE_SIZE); 64f429d607SRichard Henderson return true; 65f429d607SRichard Henderson } 664acb54baSEdgar E. Iglesias 67f429d607SRichard Henderson hit = mmu_translate(&env->mmu, &lu, address, access_type, mmu_idx); 68f429d607SRichard Henderson if (likely(hit)) { 69f429d607SRichard Henderson uint32_t vaddr = address & TARGET_PAGE_MASK; 70f429d607SRichard Henderson uint32_t paddr = lu.paddr + vaddr - lu.vaddr; 714acb54baSEdgar E. Iglesias 72339aaf5bSAntony Pavlov qemu_log_mask(CPU_LOG_MMU, "MMU map mmu=%d v=%x p=%x prot=%x\n", 73339aaf5bSAntony Pavlov mmu_idx, vaddr, paddr, lu.prot); 740c591eb0SAndreas Färber tlb_set_page(cs, vaddr, paddr, lu.prot, mmu_idx, TARGET_PAGE_SIZE); 75f429d607SRichard Henderson return true; 76f429d607SRichard Henderson } 77f429d607SRichard Henderson 78f429d607SRichard Henderson /* TLB miss. */ 79f429d607SRichard Henderson if (probe) { 80f429d607SRichard Henderson return false; 81f429d607SRichard Henderson } 82f429d607SRichard Henderson 83339aaf5bSAntony Pavlov qemu_log_mask(CPU_LOG_MMU, "mmu=%d miss v=%" VADDR_PRIx "\n", 84339aaf5bSAntony Pavlov mmu_idx, address); 854acb54baSEdgar E. Iglesias 86b2e80a3cSRichard Henderson env->ear = address; 874acb54baSEdgar E. Iglesias switch (lu.err) { 884acb54baSEdgar E. Iglesias case ERR_PROT: 8978e9caf2SRichard Henderson env->esr = access_type == MMU_INST_FETCH ? 17 : 16; 9078e9caf2SRichard Henderson env->esr |= (access_type == MMU_DATA_STORE) << 10; 914acb54baSEdgar E. Iglesias break; 924acb54baSEdgar E. Iglesias case ERR_MISS: 9378e9caf2SRichard Henderson env->esr = access_type == MMU_INST_FETCH ? 19 : 18; 9478e9caf2SRichard Henderson env->esr |= (access_type == MMU_DATA_STORE) << 10; 954acb54baSEdgar E. Iglesias break; 964acb54baSEdgar E. Iglesias default: 974acb54baSEdgar E. Iglesias abort(); 984acb54baSEdgar E. Iglesias } 994acb54baSEdgar E. Iglesias 10027103424SAndreas Färber if (cs->exception_index == EXCP_MMU) { 101a47dddd7SAndreas Färber cpu_abort(cs, "recursive faults\n"); 1024acb54baSEdgar E. Iglesias } 1034acb54baSEdgar E. Iglesias 1044acb54baSEdgar E. Iglesias /* TLB miss. */ 10527103424SAndreas Färber cs->exception_index = EXCP_MMU; 106f429d607SRichard Henderson cpu_loop_exit_restore(cs, retaddr); 1074acb54baSEdgar E. Iglesias } 108f429d607SRichard Henderson 10997a8ea5aSAndreas Färber void mb_cpu_do_interrupt(CPUState *cs) 1104acb54baSEdgar E. Iglesias { 11197a8ea5aSAndreas Färber MicroBlazeCPU *cpu = MICROBLAZE_CPU(cs); 11297a8ea5aSAndreas Färber CPUMBState *env = &cpu->env; 1131074c0fbSRichard Henderson uint32_t t, msr = mb_cpu_read_msr(env); 1144acb54baSEdgar E. Iglesias 1155225d669SStefan Weil /* IMM flag cannot propagate across a branch and into the dslot. */ 1164acb54baSEdgar E. Iglesias assert(!((env->iflags & D_FLAG) && (env->iflags & IMM_FLAG))); 1174acb54baSEdgar E. Iglesias assert(!(env->iflags & (DRTI_FLAG | DRTE_FLAG | DRTB_FLAG))); 1188cc9b43fSPeter A. G. Crosthwaite env->res_addr = RES_ADDR_NONE; 11927103424SAndreas Färber switch (cs->exception_index) { 120cedb936bSEdgar E. Iglesias case EXCP_HW_EXCP: 121cedb936bSEdgar E. Iglesias if (!(env->pvr.regs[0] & PVR0_USE_EXC_MASK)) { 1221d512a65SPaolo Bonzini qemu_log_mask(LOG_GUEST_ERROR, "Exception raised on system without exceptions!\n"); 123cedb936bSEdgar E. Iglesias return; 124cedb936bSEdgar E. Iglesias } 125cedb936bSEdgar E. Iglesias 12676e8187dSRichard Henderson env->regs[17] = env->pc + 4; 12778e9caf2SRichard Henderson env->esr &= ~(1 << 12); 128cedb936bSEdgar E. Iglesias 129cedb936bSEdgar E. Iglesias /* Exception breaks branch + dslot sequence? */ 130cedb936bSEdgar E. Iglesias if (env->iflags & D_FLAG) { 13178e9caf2SRichard Henderson env->esr |= 1 << 12 ; 1326fbf78f2SRichard Henderson env->btr = env->btarget; 133cedb936bSEdgar E. Iglesias } 134cedb936bSEdgar E. Iglesias 135cedb936bSEdgar E. Iglesias /* Disable the MMU. */ 1361074c0fbSRichard Henderson t = (msr & (MSR_VM | MSR_UM)) << 1; 1371074c0fbSRichard Henderson msr &= ~(MSR_VMS | MSR_UMS | MSR_VM | MSR_UM); 1381074c0fbSRichard Henderson msr |= t; 139cedb936bSEdgar E. Iglesias /* Exception in progress. */ 1401074c0fbSRichard Henderson msr |= MSR_EIP; 1411074c0fbSRichard Henderson mb_cpu_write_msr(env, msr); 142cedb936bSEdgar E. Iglesias 143cedb936bSEdgar E. Iglesias qemu_log_mask(CPU_LOG_INT, 1440f96e96bSRichard Henderson "hw exception at pc=%x ear=%" PRIx64 " " 1456efd5599SRichard Henderson "esr=%x iflags=%x\n", 146b2e80a3cSRichard Henderson env->pc, env->ear, 14778e9caf2SRichard Henderson env->esr, env->iflags); 148a0762859SAndreas Färber log_cpu_state_mask(CPU_LOG_INT, cs, 0); 149cedb936bSEdgar E. Iglesias env->iflags &= ~(IMM_FLAG | D_FLAG); 15076e8187dSRichard Henderson env->pc = cpu->cfg.base_vectors + 0x20; 151cedb936bSEdgar E. Iglesias break; 152cedb936bSEdgar E. Iglesias 1534acb54baSEdgar E. Iglesias case EXCP_MMU: 15476e8187dSRichard Henderson env->regs[17] = env->pc; 1554acb54baSEdgar E. Iglesias 156e3f8d192SRichard Henderson qemu_log_mask(CPU_LOG_INT, 157e3f8d192SRichard Henderson "MMU exception at pc=%x iflags=%x ear=%" PRIx64 "\n", 158e3f8d192SRichard Henderson env->pc, env->iflags, env->ear); 159e3f8d192SRichard Henderson 16078e9caf2SRichard Henderson env->esr &= ~(1 << 12); 1614acb54baSEdgar E. Iglesias /* Exception breaks branch + dslot sequence? */ 1624acb54baSEdgar E. Iglesias if (env->iflags & D_FLAG) { 16378e9caf2SRichard Henderson env->esr |= 1 << 12 ; 1646fbf78f2SRichard Henderson env->btr = env->btarget; 1654acb54baSEdgar E. Iglesias 1664acb54baSEdgar E. Iglesias /* Reexecute the branch. */ 1674acb54baSEdgar E. Iglesias env->regs[17] -= 4; 1684acb54baSEdgar E. Iglesias /* was the branch immprefixed?. */ 1697b34f45fSRichard Henderson if (env->iflags & BIMM_FLAG) { 1704acb54baSEdgar E. Iglesias env->regs[17] -= 4; 171a0762859SAndreas Färber log_cpu_state_mask(CPU_LOG_INT, cs, 0); 1724acb54baSEdgar E. Iglesias } 1734acb54baSEdgar E. Iglesias } else if (env->iflags & IMM_FLAG) { 1744acb54baSEdgar E. Iglesias env->regs[17] -= 4; 1754acb54baSEdgar E. Iglesias } 1764acb54baSEdgar E. Iglesias 1774acb54baSEdgar E. Iglesias /* Disable the MMU. */ 1781074c0fbSRichard Henderson t = (msr & (MSR_VM | MSR_UM)) << 1; 1791074c0fbSRichard Henderson msr &= ~(MSR_VMS | MSR_UMS | MSR_VM | MSR_UM); 1801074c0fbSRichard Henderson msr |= t; 1814acb54baSEdgar E. Iglesias /* Exception in progress. */ 1821074c0fbSRichard Henderson msr |= MSR_EIP; 1831074c0fbSRichard Henderson mb_cpu_write_msr(env, msr); 1844acb54baSEdgar E. Iglesias 1854acb54baSEdgar E. Iglesias qemu_log_mask(CPU_LOG_INT, 1860f96e96bSRichard Henderson "exception at pc=%x ear=%" PRIx64 " iflags=%x\n", 187b2e80a3cSRichard Henderson env->pc, env->ear, env->iflags); 188a0762859SAndreas Färber log_cpu_state_mask(CPU_LOG_INT, cs, 0); 1894acb54baSEdgar E. Iglesias env->iflags &= ~(IMM_FLAG | D_FLAG); 19076e8187dSRichard Henderson env->pc = cpu->cfg.base_vectors + 0x20; 1914acb54baSEdgar E. Iglesias break; 1924acb54baSEdgar E. Iglesias 1934acb54baSEdgar E. Iglesias case EXCP_IRQ: 1941074c0fbSRichard Henderson assert(!(msr & (MSR_EIP | MSR_BIP))); 1951074c0fbSRichard Henderson assert(msr & MSR_IE); 1964acb54baSEdgar E. Iglesias assert(!(env->iflags & D_FLAG)); 1974acb54baSEdgar E. Iglesias 1981074c0fbSRichard Henderson t = (msr & (MSR_VM | MSR_UM)) << 1; 1994acb54baSEdgar E. Iglesias 2004acb54baSEdgar E. Iglesias #if 0 20176cad711SPaolo Bonzini #include "disas/disas.h" 2024acb54baSEdgar E. Iglesias 2034acb54baSEdgar E. Iglesias /* Useful instrumentation when debugging interrupt issues in either 2044acb54baSEdgar E. Iglesias the models or in sw. */ 2054acb54baSEdgar E. Iglesias { 2064acb54baSEdgar E. Iglesias const char *sym; 2074acb54baSEdgar E. Iglesias 20876e8187dSRichard Henderson sym = lookup_symbol(env->pc); 2094acb54baSEdgar E. Iglesias if (sym 2104acb54baSEdgar E. Iglesias && (!strcmp("netif_rx", sym) 2114acb54baSEdgar E. Iglesias || !strcmp("process_backlog", sym))) { 2124acb54baSEdgar E. Iglesias 2131074c0fbSRichard Henderson qemu_log("interrupt at pc=%x msr=%x %x iflags=%x sym=%s\n", 2141074c0fbSRichard Henderson env->pc, msr, t, env->iflags, sym); 2154acb54baSEdgar E. Iglesias 216a0762859SAndreas Färber log_cpu_state(cs, 0); 2174acb54baSEdgar E. Iglesias } 2184acb54baSEdgar E. Iglesias } 2194acb54baSEdgar E. Iglesias #endif 2204acb54baSEdgar E. Iglesias qemu_log_mask(CPU_LOG_INT, 2213e0e16aeSRichard Henderson "interrupt at pc=%x msr=%x %x iflags=%x\n", 2221074c0fbSRichard Henderson env->pc, msr, t, env->iflags); 2234acb54baSEdgar E. Iglesias 2241074c0fbSRichard Henderson msr &= ~(MSR_VMS | MSR_UMS | MSR_VM | MSR_UM | MSR_IE); 2251074c0fbSRichard Henderson msr |= t; 2261074c0fbSRichard Henderson mb_cpu_write_msr(env, msr); 2274acb54baSEdgar E. Iglesias 22876e8187dSRichard Henderson env->regs[14] = env->pc; 22976e8187dSRichard Henderson env->pc = cpu->cfg.base_vectors + 0x10; 230a0762859SAndreas Färber //log_cpu_state_mask(CPU_LOG_INT, cs, 0); 2314acb54baSEdgar E. Iglesias break; 2324acb54baSEdgar E. Iglesias 2334acb54baSEdgar E. Iglesias case EXCP_HW_BREAK: 2344acb54baSEdgar E. Iglesias assert(!(env->iflags & IMM_FLAG)); 2354acb54baSEdgar E. Iglesias assert(!(env->iflags & D_FLAG)); 2361074c0fbSRichard Henderson t = (msr & (MSR_VM | MSR_UM)) << 1; 2374acb54baSEdgar E. Iglesias qemu_log_mask(CPU_LOG_INT, 2383e0e16aeSRichard Henderson "break at pc=%x msr=%x %x iflags=%x\n", 2391074c0fbSRichard Henderson env->pc, msr, t, env->iflags); 240a0762859SAndreas Färber log_cpu_state_mask(CPU_LOG_INT, cs, 0); 2411074c0fbSRichard Henderson msr &= ~(MSR_VMS | MSR_UMS | MSR_VM | MSR_UM); 2421074c0fbSRichard Henderson msr |= t; 2431074c0fbSRichard Henderson msr |= MSR_BIP; 24476e8187dSRichard Henderson env->regs[16] = env->pc; 24576e8187dSRichard Henderson env->pc = cpu->cfg.base_vectors + 0x18; 2461074c0fbSRichard Henderson mb_cpu_write_msr(env, msr); 2474acb54baSEdgar E. Iglesias break; 2484acb54baSEdgar E. Iglesias default: 249a47dddd7SAndreas Färber cpu_abort(cs, "unhandled exception type=%d\n", 25027103424SAndreas Färber cs->exception_index); 2514acb54baSEdgar E. Iglesias break; 2524acb54baSEdgar E. Iglesias } 2534acb54baSEdgar E. Iglesias } 2544acb54baSEdgar E. Iglesias 25500b941e5SAndreas Färber hwaddr mb_cpu_get_phys_page_debug(CPUState *cs, vaddr addr) 2564acb54baSEdgar E. Iglesias { 25700b941e5SAndreas Färber MicroBlazeCPU *cpu = MICROBLAZE_CPU(cs); 25800b941e5SAndreas Färber CPUMBState *env = &cpu->env; 2594acb54baSEdgar E. Iglesias target_ulong vaddr, paddr = 0; 2604acb54baSEdgar E. Iglesias struct microblaze_mmu_lookup lu; 261d10367e0SEdgar E. Iglesias int mmu_idx = cpu_mmu_index(env, false); 2624acb54baSEdgar E. Iglesias unsigned int hit; 2634acb54baSEdgar E. Iglesias 264d10367e0SEdgar E. Iglesias if (mmu_idx != MMU_NOMMU_IDX) { 2654acb54baSEdgar E. Iglesias hit = mmu_translate(&env->mmu, &lu, addr, 0, 0); 2664acb54baSEdgar E. Iglesias if (hit) { 2674acb54baSEdgar E. Iglesias vaddr = addr & TARGET_PAGE_MASK; 2684acb54baSEdgar E. Iglesias paddr = lu.paddr + vaddr - lu.vaddr; 2694acb54baSEdgar E. Iglesias } else 2704acb54baSEdgar E. Iglesias paddr = 0; /* ???. */ 2714acb54baSEdgar E. Iglesias } else 2724acb54baSEdgar E. Iglesias paddr = addr & TARGET_PAGE_MASK; 2734acb54baSEdgar E. Iglesias 2744acb54baSEdgar E. Iglesias return paddr; 2754acb54baSEdgar E. Iglesias } 2764acb54baSEdgar E. Iglesias #endif 27729cd33d3SRichard Henderson 27829cd33d3SRichard Henderson bool mb_cpu_exec_interrupt(CPUState *cs, int interrupt_request) 27929cd33d3SRichard Henderson { 28029cd33d3SRichard Henderson MicroBlazeCPU *cpu = MICROBLAZE_CPU(cs); 28129cd33d3SRichard Henderson CPUMBState *env = &cpu->env; 28229cd33d3SRichard Henderson 28329cd33d3SRichard Henderson if ((interrupt_request & CPU_INTERRUPT_HARD) 2842e5282caSRichard Henderson && (env->msr & MSR_IE) 2852e5282caSRichard Henderson && !(env->msr & (MSR_EIP | MSR_BIP)) 28629cd33d3SRichard Henderson && !(env->iflags & (D_FLAG | IMM_FLAG))) { 28729cd33d3SRichard Henderson cs->exception_index = EXCP_IRQ; 28829cd33d3SRichard Henderson mb_cpu_do_interrupt(cs); 28929cd33d3SRichard Henderson return true; 29029cd33d3SRichard Henderson } 29129cd33d3SRichard Henderson return false; 29229cd33d3SRichard Henderson } 293ab0c8d0fSRichard Henderson 294ab0c8d0fSRichard Henderson void mb_cpu_do_unaligned_access(CPUState *cs, vaddr addr, 295ab0c8d0fSRichard Henderson MMUAccessType access_type, 296ab0c8d0fSRichard Henderson int mmu_idx, uintptr_t retaddr) 297ab0c8d0fSRichard Henderson { 298ab0c8d0fSRichard Henderson MicroBlazeCPU *cpu = MICROBLAZE_CPU(cs); 299ab0c8d0fSRichard Henderson uint32_t esr, iflags; 300ab0c8d0fSRichard Henderson 301ab0c8d0fSRichard Henderson /* Recover the pc and iflags from the corresponding insn_start. */ 302ab0c8d0fSRichard Henderson cpu_restore_state(cs, retaddr, true); 303ab0c8d0fSRichard Henderson iflags = cpu->env.iflags; 304ab0c8d0fSRichard Henderson 305ab0c8d0fSRichard Henderson qemu_log_mask(CPU_LOG_INT, 306*19f27b6cSRichard Henderson "Unaligned access addr=" TARGET_FMT_lx " pc=%x iflags=%x\n", 307*19f27b6cSRichard Henderson (target_ulong)addr, cpu->env.pc, iflags); 308ab0c8d0fSRichard Henderson 309ab0c8d0fSRichard Henderson esr = ESR_EC_UNALIGNED_DATA; 310ab0c8d0fSRichard Henderson if (likely(iflags & ESR_ESS_FLAG)) { 311ab0c8d0fSRichard Henderson esr |= iflags & ESR_ESS_MASK; 312ab0c8d0fSRichard Henderson } else { 313ab0c8d0fSRichard Henderson qemu_log_mask(LOG_UNIMP, "Unaligned access without ESR_ESS_FLAG\n"); 314ab0c8d0fSRichard Henderson } 315ab0c8d0fSRichard Henderson 316ab0c8d0fSRichard Henderson cpu->env.ear = addr; 317ab0c8d0fSRichard Henderson cpu->env.esr = esr; 318ab0c8d0fSRichard Henderson cs->exception_index = EXCP_HW_EXCP; 319ab0c8d0fSRichard Henderson cpu_loop_exit(cs); 320ab0c8d0fSRichard Henderson } 321