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