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 int mb_cpu_handle_mmu_fault(CPUState *cs, vaddr address, int size, int rw, 42 int mmu_idx) 43 { 44 cs->exception_index = 0xaa; 45 cpu_dump_state(cs, stderr, fprintf, 0); 46 return 1; 47 } 48 49 #else /* !CONFIG_USER_ONLY */ 50 51 int mb_cpu_handle_mmu_fault(CPUState *cs, vaddr address, int size, int rw, 52 int mmu_idx) 53 { 54 MicroBlazeCPU *cpu = MICROBLAZE_CPU(cs); 55 CPUMBState *env = &cpu->env; 56 unsigned int hit; 57 unsigned int mmu_available; 58 int r = 1; 59 int prot; 60 61 mmu_available = 0; 62 if (cpu->cfg.use_mmu) { 63 mmu_available = 1; 64 if ((cpu->cfg.pvr == C_PVR_FULL) && 65 (env->pvr.regs[11] & PVR11_USE_MMU) != PVR11_USE_MMU) { 66 mmu_available = 0; 67 } 68 } 69 70 /* Translate if the MMU is available and enabled. */ 71 if (mmu_available && (env->sregs[SR_MSR] & MSR_VM)) { 72 target_ulong vaddr, paddr; 73 struct microblaze_mmu_lookup lu; 74 75 hit = mmu_translate(&env->mmu, &lu, address, rw, mmu_idx); 76 if (hit) { 77 vaddr = address & TARGET_PAGE_MASK; 78 paddr = lu.paddr + vaddr - lu.vaddr; 79 80 qemu_log_mask(CPU_LOG_MMU, "MMU map mmu=%d v=%x p=%x prot=%x\n", 81 mmu_idx, vaddr, paddr, lu.prot); 82 tlb_set_page(cs, vaddr, paddr, lu.prot, mmu_idx, TARGET_PAGE_SIZE); 83 r = 0; 84 } else { 85 env->sregs[SR_EAR] = address; 86 qemu_log_mask(CPU_LOG_MMU, "mmu=%d miss v=%" VADDR_PRIx "\n", 87 mmu_idx, address); 88 89 switch (lu.err) { 90 case ERR_PROT: 91 env->sregs[SR_ESR] = rw == 2 ? 17 : 16; 92 env->sregs[SR_ESR] |= (rw == 1) << 10; 93 break; 94 case ERR_MISS: 95 env->sregs[SR_ESR] = rw == 2 ? 19 : 18; 96 env->sregs[SR_ESR] |= (rw == 1) << 10; 97 break; 98 default: 99 abort(); 100 break; 101 } 102 103 if (cs->exception_index == EXCP_MMU) { 104 cpu_abort(cs, "recursive faults\n"); 105 } 106 107 /* TLB miss. */ 108 cs->exception_index = EXCP_MMU; 109 } 110 } else { 111 /* MMU disabled or not available. */ 112 address &= TARGET_PAGE_MASK; 113 prot = PAGE_BITS; 114 tlb_set_page(cs, address, address, prot, mmu_idx, TARGET_PAGE_SIZE); 115 r = 0; 116 } 117 return r; 118 } 119 120 void mb_cpu_do_interrupt(CPUState *cs) 121 { 122 MicroBlazeCPU *cpu = MICROBLAZE_CPU(cs); 123 CPUMBState *env = &cpu->env; 124 uint32_t t; 125 126 /* IMM flag cannot propagate across a branch and into the dslot. */ 127 assert(!((env->iflags & D_FLAG) && (env->iflags & IMM_FLAG))); 128 assert(!(env->iflags & (DRTI_FLAG | DRTE_FLAG | DRTB_FLAG))); 129 /* assert(env->sregs[SR_MSR] & (MSR_EE)); Only for HW exceptions. */ 130 env->res_addr = RES_ADDR_NONE; 131 switch (cs->exception_index) { 132 case EXCP_HW_EXCP: 133 if (!(env->pvr.regs[0] & PVR0_USE_EXC_MASK)) { 134 qemu_log_mask(LOG_GUEST_ERROR, "Exception raised on system without exceptions!\n"); 135 return; 136 } 137 138 env->regs[17] = env->sregs[SR_PC] + 4; 139 env->sregs[SR_ESR] &= ~(1 << 12); 140 141 /* Exception breaks branch + dslot sequence? */ 142 if (env->iflags & D_FLAG) { 143 env->sregs[SR_ESR] |= 1 << 12 ; 144 env->sregs[SR_BTR] = env->btarget; 145 } 146 147 /* Disable the MMU. */ 148 t = (env->sregs[SR_MSR] & (MSR_VM | MSR_UM)) << 1; 149 env->sregs[SR_MSR] &= ~(MSR_VMS | MSR_UMS | MSR_VM | MSR_UM); 150 env->sregs[SR_MSR] |= t; 151 /* Exception in progress. */ 152 env->sregs[SR_MSR] |= MSR_EIP; 153 154 qemu_log_mask(CPU_LOG_INT, 155 "hw exception at pc=%x ear=%x esr=%x iflags=%x\n", 156 env->sregs[SR_PC], env->sregs[SR_EAR], 157 env->sregs[SR_ESR], env->iflags); 158 log_cpu_state_mask(CPU_LOG_INT, cs, 0); 159 env->iflags &= ~(IMM_FLAG | D_FLAG); 160 env->sregs[SR_PC] = cpu->cfg.base_vectors + 0x20; 161 break; 162 163 case EXCP_MMU: 164 env->regs[17] = env->sregs[SR_PC]; 165 166 env->sregs[SR_ESR] &= ~(1 << 12); 167 /* Exception breaks branch + dslot sequence? */ 168 if (env->iflags & D_FLAG) { 169 D(qemu_log("D_FLAG set at exception bimm=%d\n", env->bimm)); 170 env->sregs[SR_ESR] |= 1 << 12 ; 171 env->sregs[SR_BTR] = env->btarget; 172 173 /* Reexecute the branch. */ 174 env->regs[17] -= 4; 175 /* was the branch immprefixed?. */ 176 if (env->bimm) { 177 qemu_log_mask(CPU_LOG_INT, 178 "bimm exception at pc=%x iflags=%x\n", 179 env->sregs[SR_PC], env->iflags); 180 env->regs[17] -= 4; 181 log_cpu_state_mask(CPU_LOG_INT, cs, 0); 182 } 183 } else if (env->iflags & IMM_FLAG) { 184 D(qemu_log("IMM_FLAG set at exception\n")); 185 env->regs[17] -= 4; 186 } 187 188 /* Disable the MMU. */ 189 t = (env->sregs[SR_MSR] & (MSR_VM | MSR_UM)) << 1; 190 env->sregs[SR_MSR] &= ~(MSR_VMS | MSR_UMS | MSR_VM | MSR_UM); 191 env->sregs[SR_MSR] |= t; 192 /* Exception in progress. */ 193 env->sregs[SR_MSR] |= MSR_EIP; 194 195 qemu_log_mask(CPU_LOG_INT, 196 "exception at pc=%x ear=%x iflags=%x\n", 197 env->sregs[SR_PC], env->sregs[SR_EAR], env->iflags); 198 log_cpu_state_mask(CPU_LOG_INT, cs, 0); 199 env->iflags &= ~(IMM_FLAG | D_FLAG); 200 env->sregs[SR_PC] = cpu->cfg.base_vectors + 0x20; 201 break; 202 203 case EXCP_IRQ: 204 assert(!(env->sregs[SR_MSR] & (MSR_EIP | MSR_BIP))); 205 assert(env->sregs[SR_MSR] & MSR_IE); 206 assert(!(env->iflags & D_FLAG)); 207 208 t = (env->sregs[SR_MSR] & (MSR_VM | MSR_UM)) << 1; 209 210 #if 0 211 #include "disas/disas.h" 212 213 /* Useful instrumentation when debugging interrupt issues in either 214 the models or in sw. */ 215 { 216 const char *sym; 217 218 sym = lookup_symbol(env->sregs[SR_PC]); 219 if (sym 220 && (!strcmp("netif_rx", sym) 221 || !strcmp("process_backlog", sym))) { 222 223 qemu_log( 224 "interrupt at pc=%x msr=%x %x iflags=%x sym=%s\n", 225 env->sregs[SR_PC], env->sregs[SR_MSR], t, env->iflags, 226 sym); 227 228 log_cpu_state(cs, 0); 229 } 230 } 231 #endif 232 qemu_log_mask(CPU_LOG_INT, 233 "interrupt at pc=%x msr=%x %x iflags=%x\n", 234 env->sregs[SR_PC], env->sregs[SR_MSR], t, env->iflags); 235 236 env->sregs[SR_MSR] &= ~(MSR_VMS | MSR_UMS | MSR_VM \ 237 | MSR_UM | MSR_IE); 238 env->sregs[SR_MSR] |= t; 239 240 env->regs[14] = env->sregs[SR_PC]; 241 env->sregs[SR_PC] = cpu->cfg.base_vectors + 0x10; 242 //log_cpu_state_mask(CPU_LOG_INT, cs, 0); 243 break; 244 245 case EXCP_BREAK: 246 case EXCP_HW_BREAK: 247 assert(!(env->iflags & IMM_FLAG)); 248 assert(!(env->iflags & D_FLAG)); 249 t = (env->sregs[SR_MSR] & (MSR_VM | MSR_UM)) << 1; 250 qemu_log_mask(CPU_LOG_INT, 251 "break at pc=%x msr=%x %x iflags=%x\n", 252 env->sregs[SR_PC], env->sregs[SR_MSR], t, env->iflags); 253 log_cpu_state_mask(CPU_LOG_INT, cs, 0); 254 env->sregs[SR_MSR] &= ~(MSR_VMS | MSR_UMS | MSR_VM | MSR_UM); 255 env->sregs[SR_MSR] |= t; 256 env->sregs[SR_MSR] |= MSR_BIP; 257 if (cs->exception_index == EXCP_HW_BREAK) { 258 env->regs[16] = env->sregs[SR_PC]; 259 env->sregs[SR_MSR] |= MSR_BIP; 260 env->sregs[SR_PC] = cpu->cfg.base_vectors + 0x18; 261 } else 262 env->sregs[SR_PC] = env->btarget; 263 break; 264 default: 265 cpu_abort(cs, "unhandled exception type=%d\n", 266 cs->exception_index); 267 break; 268 } 269 } 270 271 hwaddr mb_cpu_get_phys_page_debug(CPUState *cs, vaddr addr) 272 { 273 MicroBlazeCPU *cpu = MICROBLAZE_CPU(cs); 274 CPUMBState *env = &cpu->env; 275 target_ulong vaddr, paddr = 0; 276 struct microblaze_mmu_lookup lu; 277 unsigned int hit; 278 279 if (env->sregs[SR_MSR] & MSR_VM) { 280 hit = mmu_translate(&env->mmu, &lu, addr, 0, 0); 281 if (hit) { 282 vaddr = addr & TARGET_PAGE_MASK; 283 paddr = lu.paddr + vaddr - lu.vaddr; 284 } else 285 paddr = 0; /* ???. */ 286 } else 287 paddr = addr & TARGET_PAGE_MASK; 288 289 return paddr; 290 } 291 #endif 292 293 bool mb_cpu_exec_interrupt(CPUState *cs, int interrupt_request) 294 { 295 MicroBlazeCPU *cpu = MICROBLAZE_CPU(cs); 296 CPUMBState *env = &cpu->env; 297 298 if ((interrupt_request & CPU_INTERRUPT_HARD) 299 && (env->sregs[SR_MSR] & MSR_IE) 300 && !(env->sregs[SR_MSR] & (MSR_EIP | MSR_BIP)) 301 && !(env->iflags & (D_FLAG | IMM_FLAG))) { 302 cs->exception_index = EXCP_IRQ; 303 mb_cpu_do_interrupt(cs); 304 return true; 305 } 306 return false; 307 } 308