1 /* 2 * MicroBlaze helper routines. 3 * 4 * Copyright (c) 2009 Edgar E. Iglesias <edgar.iglesias@gmail.com> 5 * Copyright (c) 2009-2012 PetaLogix Qld Pty Ltd. 6 * 7 * This library is free software; you can redistribute it and/or 8 * modify it under the terms of the GNU Lesser General Public 9 * License as published by the Free Software Foundation; either 10 * version 2 of the License, or (at your option) any later version. 11 * 12 * This library is distributed in the hope that it will be useful, 13 * but WITHOUT ANY WARRANTY; without even the implied warranty of 14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 15 * Lesser General Public License for more details. 16 * 17 * You should have received a copy of the GNU Lesser General Public 18 * License along with this library; if not, see <http://www.gnu.org/licenses/>. 19 */ 20 21 #include "qemu/osdep.h" 22 #include "cpu.h" 23 #include "exec/exec-all.h" 24 #include "qemu/host-utils.h" 25 #include "exec/log.h" 26 27 #define D(x) 28 29 #if defined(CONFIG_USER_ONLY) 30 31 void mb_cpu_do_interrupt(CPUState *cs) 32 { 33 MicroBlazeCPU *cpu = MICROBLAZE_CPU(cs); 34 CPUMBState *env = &cpu->env; 35 36 cs->exception_index = -1; 37 env->res_addr = RES_ADDR_NONE; 38 env->regs[14] = env->sregs[SR_PC]; 39 } 40 41 bool mb_cpu_tlb_fill(CPUState *cs, vaddr address, int size, 42 MMUAccessType access_type, int mmu_idx, 43 bool probe, uintptr_t retaddr) 44 { 45 cs->exception_index = 0xaa; 46 cpu_loop_exit_restore(cs, retaddr); 47 } 48 49 #else /* !CONFIG_USER_ONLY */ 50 51 bool mb_cpu_tlb_fill(CPUState *cs, vaddr address, int size, 52 MMUAccessType access_type, int mmu_idx, 53 bool probe, uintptr_t retaddr) 54 { 55 MicroBlazeCPU *cpu = MICROBLAZE_CPU(cs); 56 CPUMBState *env = &cpu->env; 57 struct microblaze_mmu_lookup lu; 58 unsigned int hit; 59 int prot; 60 61 if (mmu_idx == MMU_NOMMU_IDX) { 62 /* MMU disabled or not available. */ 63 address &= TARGET_PAGE_MASK; 64 prot = PAGE_BITS; 65 tlb_set_page(cs, address, address, prot, mmu_idx, TARGET_PAGE_SIZE); 66 return true; 67 } 68 69 hit = mmu_translate(&env->mmu, &lu, address, access_type, mmu_idx); 70 if (likely(hit)) { 71 uint32_t vaddr = address & TARGET_PAGE_MASK; 72 uint32_t paddr = lu.paddr + vaddr - lu.vaddr; 73 74 qemu_log_mask(CPU_LOG_MMU, "MMU map mmu=%d v=%x p=%x prot=%x\n", 75 mmu_idx, vaddr, paddr, lu.prot); 76 tlb_set_page(cs, vaddr, paddr, lu.prot, mmu_idx, TARGET_PAGE_SIZE); 77 return true; 78 } 79 80 /* TLB miss. */ 81 if (probe) { 82 return false; 83 } 84 85 qemu_log_mask(CPU_LOG_MMU, "mmu=%d miss v=%" VADDR_PRIx "\n", 86 mmu_idx, address); 87 88 env->sregs[SR_EAR] = address; 89 switch (lu.err) { 90 case ERR_PROT: 91 env->sregs[SR_ESR] = access_type == MMU_INST_FETCH ? 17 : 16; 92 env->sregs[SR_ESR] |= (access_type == MMU_DATA_STORE) << 10; 93 break; 94 case ERR_MISS: 95 env->sregs[SR_ESR] = access_type == MMU_INST_FETCH ? 19 : 18; 96 env->sregs[SR_ESR] |= (access_type == MMU_DATA_STORE) << 10; 97 break; 98 default: 99 abort(); 100 } 101 102 if (cs->exception_index == EXCP_MMU) { 103 cpu_abort(cs, "recursive faults\n"); 104 } 105 106 /* TLB miss. */ 107 cs->exception_index = EXCP_MMU; 108 cpu_loop_exit_restore(cs, retaddr); 109 } 110 111 void mb_cpu_do_interrupt(CPUState *cs) 112 { 113 MicroBlazeCPU *cpu = MICROBLAZE_CPU(cs); 114 CPUMBState *env = &cpu->env; 115 uint32_t t; 116 117 /* IMM flag cannot propagate across a branch and into the dslot. */ 118 assert(!((env->iflags & D_FLAG) && (env->iflags & IMM_FLAG))); 119 assert(!(env->iflags & (DRTI_FLAG | DRTE_FLAG | DRTB_FLAG))); 120 /* assert(env->sregs[SR_MSR] & (MSR_EE)); Only for HW exceptions. */ 121 env->res_addr = RES_ADDR_NONE; 122 switch (cs->exception_index) { 123 case EXCP_HW_EXCP: 124 if (!(env->pvr.regs[0] & PVR0_USE_EXC_MASK)) { 125 qemu_log_mask(LOG_GUEST_ERROR, "Exception raised on system without exceptions!\n"); 126 return; 127 } 128 129 env->regs[17] = env->sregs[SR_PC] + 4; 130 env->sregs[SR_ESR] &= ~(1 << 12); 131 132 /* Exception breaks branch + dslot sequence? */ 133 if (env->iflags & D_FLAG) { 134 env->sregs[SR_ESR] |= 1 << 12 ; 135 env->sregs[SR_BTR] = env->btarget; 136 } 137 138 /* Disable the MMU. */ 139 t = (env->sregs[SR_MSR] & (MSR_VM | MSR_UM)) << 1; 140 env->sregs[SR_MSR] &= ~(MSR_VMS | MSR_UMS | MSR_VM | MSR_UM); 141 env->sregs[SR_MSR] |= t; 142 /* Exception in progress. */ 143 env->sregs[SR_MSR] |= MSR_EIP; 144 145 qemu_log_mask(CPU_LOG_INT, 146 "hw exception at pc=%" PRIx64 " ear=%" PRIx64 " " 147 "esr=%" PRIx64 " iflags=%x\n", 148 env->sregs[SR_PC], env->sregs[SR_EAR], 149 env->sregs[SR_ESR], env->iflags); 150 log_cpu_state_mask(CPU_LOG_INT, cs, 0); 151 env->iflags &= ~(IMM_FLAG | D_FLAG); 152 env->sregs[SR_PC] = cpu->cfg.base_vectors + 0x20; 153 break; 154 155 case EXCP_MMU: 156 env->regs[17] = env->sregs[SR_PC]; 157 158 env->sregs[SR_ESR] &= ~(1 << 12); 159 /* Exception breaks branch + dslot sequence? */ 160 if (env->iflags & D_FLAG) { 161 D(qemu_log("D_FLAG set at exception bimm=%d\n", env->bimm)); 162 env->sregs[SR_ESR] |= 1 << 12 ; 163 env->sregs[SR_BTR] = env->btarget; 164 165 /* Reexecute the branch. */ 166 env->regs[17] -= 4; 167 /* was the branch immprefixed?. */ 168 if (env->bimm) { 169 qemu_log_mask(CPU_LOG_INT, 170 "bimm exception at pc=%" PRIx64 " " 171 "iflags=%x\n", 172 env->sregs[SR_PC], env->iflags); 173 env->regs[17] -= 4; 174 log_cpu_state_mask(CPU_LOG_INT, cs, 0); 175 } 176 } else if (env->iflags & IMM_FLAG) { 177 D(qemu_log("IMM_FLAG set at exception\n")); 178 env->regs[17] -= 4; 179 } 180 181 /* Disable the MMU. */ 182 t = (env->sregs[SR_MSR] & (MSR_VM | MSR_UM)) << 1; 183 env->sregs[SR_MSR] &= ~(MSR_VMS | MSR_UMS | MSR_VM | MSR_UM); 184 env->sregs[SR_MSR] |= t; 185 /* Exception in progress. */ 186 env->sregs[SR_MSR] |= MSR_EIP; 187 188 qemu_log_mask(CPU_LOG_INT, 189 "exception at pc=%" PRIx64 " ear=%" PRIx64 " " 190 "iflags=%x\n", 191 env->sregs[SR_PC], env->sregs[SR_EAR], env->iflags); 192 log_cpu_state_mask(CPU_LOG_INT, cs, 0); 193 env->iflags &= ~(IMM_FLAG | D_FLAG); 194 env->sregs[SR_PC] = cpu->cfg.base_vectors + 0x20; 195 break; 196 197 case EXCP_IRQ: 198 assert(!(env->sregs[SR_MSR] & (MSR_EIP | MSR_BIP))); 199 assert(env->sregs[SR_MSR] & MSR_IE); 200 assert(!(env->iflags & D_FLAG)); 201 202 t = (env->sregs[SR_MSR] & (MSR_VM | MSR_UM)) << 1; 203 204 #if 0 205 #include "disas/disas.h" 206 207 /* Useful instrumentation when debugging interrupt issues in either 208 the models or in sw. */ 209 { 210 const char *sym; 211 212 sym = lookup_symbol(env->sregs[SR_PC]); 213 if (sym 214 && (!strcmp("netif_rx", sym) 215 || !strcmp("process_backlog", sym))) { 216 217 qemu_log( 218 "interrupt at pc=%x msr=%x %x iflags=%x sym=%s\n", 219 env->sregs[SR_PC], env->sregs[SR_MSR], t, env->iflags, 220 sym); 221 222 log_cpu_state(cs, 0); 223 } 224 } 225 #endif 226 qemu_log_mask(CPU_LOG_INT, 227 "interrupt at pc=%" PRIx64 " msr=%" PRIx64 " %x " 228 "iflags=%x\n", 229 env->sregs[SR_PC], env->sregs[SR_MSR], t, env->iflags); 230 231 env->sregs[SR_MSR] &= ~(MSR_VMS | MSR_UMS | MSR_VM \ 232 | MSR_UM | MSR_IE); 233 env->sregs[SR_MSR] |= t; 234 235 env->regs[14] = env->sregs[SR_PC]; 236 env->sregs[SR_PC] = cpu->cfg.base_vectors + 0x10; 237 //log_cpu_state_mask(CPU_LOG_INT, cs, 0); 238 break; 239 240 case EXCP_BREAK: 241 case EXCP_HW_BREAK: 242 assert(!(env->iflags & IMM_FLAG)); 243 assert(!(env->iflags & D_FLAG)); 244 t = (env->sregs[SR_MSR] & (MSR_VM | MSR_UM)) << 1; 245 qemu_log_mask(CPU_LOG_INT, 246 "break at pc=%" PRIx64 " msr=%" PRIx64 " %x " 247 "iflags=%x\n", 248 env->sregs[SR_PC], env->sregs[SR_MSR], t, env->iflags); 249 log_cpu_state_mask(CPU_LOG_INT, cs, 0); 250 env->sregs[SR_MSR] &= ~(MSR_VMS | MSR_UMS | MSR_VM | MSR_UM); 251 env->sregs[SR_MSR] |= t; 252 env->sregs[SR_MSR] |= MSR_BIP; 253 if (cs->exception_index == EXCP_HW_BREAK) { 254 env->regs[16] = env->sregs[SR_PC]; 255 env->sregs[SR_MSR] |= MSR_BIP; 256 env->sregs[SR_PC] = cpu->cfg.base_vectors + 0x18; 257 } else 258 env->sregs[SR_PC] = env->btarget; 259 break; 260 default: 261 cpu_abort(cs, "unhandled exception type=%d\n", 262 cs->exception_index); 263 break; 264 } 265 } 266 267 hwaddr mb_cpu_get_phys_page_debug(CPUState *cs, vaddr addr) 268 { 269 MicroBlazeCPU *cpu = MICROBLAZE_CPU(cs); 270 CPUMBState *env = &cpu->env; 271 target_ulong vaddr, paddr = 0; 272 struct microblaze_mmu_lookup lu; 273 int mmu_idx = cpu_mmu_index(env, false); 274 unsigned int hit; 275 276 if (mmu_idx != MMU_NOMMU_IDX) { 277 hit = mmu_translate(&env->mmu, &lu, addr, 0, 0); 278 if (hit) { 279 vaddr = addr & TARGET_PAGE_MASK; 280 paddr = lu.paddr + vaddr - lu.vaddr; 281 } else 282 paddr = 0; /* ???. */ 283 } else 284 paddr = addr & TARGET_PAGE_MASK; 285 286 return paddr; 287 } 288 #endif 289 290 bool mb_cpu_exec_interrupt(CPUState *cs, int interrupt_request) 291 { 292 MicroBlazeCPU *cpu = MICROBLAZE_CPU(cs); 293 CPUMBState *env = &cpu->env; 294 295 if ((interrupt_request & CPU_INTERRUPT_HARD) 296 && (env->sregs[SR_MSR] & MSR_IE) 297 && !(env->sregs[SR_MSR] & (MSR_EIP | MSR_BIP)) 298 && !(env->iflags & (D_FLAG | IMM_FLAG))) { 299 cs->exception_index = EXCP_IRQ; 300 mb_cpu_do_interrupt(cs); 301 return true; 302 } 303 return false; 304 } 305