helper.c (11497B)
1 /* 2 * Altera Nios II helper routines. 3 * 4 * Copyright (c) 2012 Chris Wulff <crwulff@gmail.com> 5 * 6 * This library is free software; you can redistribute it and/or 7 * modify it under the terms of the GNU Lesser General Public 8 * License as published by the Free Software Foundation; either 9 * version 2.1 of the License, or (at your option) any later version. 10 * 11 * This library is distributed in the hope that it will be useful, 12 * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 14 * Lesser General Public License for more details. 15 * 16 * You should have received a copy of the GNU Lesser General Public 17 * License along with this library; if not, see 18 * <http://www.gnu.org/licenses/lgpl-2.1.html> 19 */ 20 21 #include "qemu/osdep.h" 22 23 #include "cpu.h" 24 #include "qemu/host-utils.h" 25 #include "exec/exec-all.h" 26 #include "exec/cpu_ldst.h" 27 #include "exec/log.h" 28 #include "exec/helper-proto.h" 29 #include "semihosting/semihost.h" 30 31 32 static void do_exception(Nios2CPU *cpu, uint32_t exception_addr, 33 uint32_t tlbmisc_set, bool is_break) 34 { 35 CPUNios2State *env = &cpu->env; 36 CPUState *cs = CPU(cpu); 37 uint32_t old_status = env->ctrl[CR_STATUS]; 38 uint32_t new_status = old_status; 39 40 /* With shadow regs, exceptions are always taken into CRS 0. */ 41 new_status &= ~R_CR_STATUS_CRS_MASK; 42 env->regs = env->shadow_regs[0]; 43 44 if ((old_status & CR_STATUS_EH) == 0) { 45 int r_ea = R_EA, cr_es = CR_ESTATUS; 46 47 if (is_break) { 48 r_ea = R_BA; 49 cr_es = CR_BSTATUS; 50 } 51 env->ctrl[cr_es] = old_status; 52 env->regs[r_ea] = env->pc; 53 54 if (cpu->mmu_present) { 55 new_status |= CR_STATUS_EH; 56 57 /* 58 * There are 4 bits that are always written. 59 * Explicitly clear them, to be set via the argument. 60 */ 61 env->ctrl[CR_TLBMISC] &= ~(CR_TLBMISC_D | 62 CR_TLBMISC_PERM | 63 CR_TLBMISC_BAD | 64 CR_TLBMISC_DBL); 65 env->ctrl[CR_TLBMISC] |= tlbmisc_set; 66 } 67 68 /* 69 * With shadow regs, and EH == 0, PRS is set from CRS. 70 * At least, so says Table 3-9, and some other text, 71 * though Table 3-38 says otherwise. 72 */ 73 new_status = FIELD_DP32(new_status, CR_STATUS, PRS, 74 FIELD_EX32(old_status, CR_STATUS, CRS)); 75 } 76 77 new_status &= ~(CR_STATUS_PIE | CR_STATUS_U); 78 79 env->ctrl[CR_STATUS] = new_status; 80 if (!is_break) { 81 env->ctrl[CR_EXCEPTION] = FIELD_DP32(0, CR_EXCEPTION, CAUSE, 82 cs->exception_index); 83 } 84 env->pc = exception_addr; 85 } 86 87 static void do_iic_irq(Nios2CPU *cpu) 88 { 89 do_exception(cpu, cpu->exception_addr, 0, false); 90 } 91 92 static void do_eic_irq(Nios2CPU *cpu) 93 { 94 CPUNios2State *env = &cpu->env; 95 uint32_t old_status = env->ctrl[CR_STATUS]; 96 uint32_t new_status = old_status; 97 uint32_t old_rs = FIELD_EX32(old_status, CR_STATUS, CRS); 98 uint32_t new_rs = cpu->rrs; 99 100 new_status = FIELD_DP32(new_status, CR_STATUS, CRS, new_rs); 101 new_status = FIELD_DP32(new_status, CR_STATUS, IL, cpu->ril); 102 new_status = FIELD_DP32(new_status, CR_STATUS, NMI, cpu->rnmi); 103 new_status &= ~(CR_STATUS_RSIE | CR_STATUS_U); 104 new_status |= CR_STATUS_IH; 105 106 if (!(new_status & CR_STATUS_EH)) { 107 new_status = FIELD_DP32(new_status, CR_STATUS, PRS, old_rs); 108 if (new_rs == 0) { 109 env->ctrl[CR_ESTATUS] = old_status; 110 } else { 111 if (new_rs != old_rs) { 112 old_status |= CR_STATUS_SRS; 113 } 114 env->shadow_regs[new_rs][R_SSTATUS] = old_status; 115 } 116 env->shadow_regs[new_rs][R_EA] = env->pc; 117 } 118 119 env->ctrl[CR_STATUS] = new_status; 120 nios2_update_crs(env); 121 122 env->pc = cpu->rha; 123 } 124 125 void nios2_cpu_do_interrupt(CPUState *cs) 126 { 127 Nios2CPU *cpu = NIOS2_CPU(cs); 128 CPUNios2State *env = &cpu->env; 129 uint32_t tlbmisc_set = 0; 130 131 if (qemu_loglevel_mask(CPU_LOG_INT)) { 132 const char *name = NULL; 133 134 switch (cs->exception_index) { 135 case EXCP_IRQ: 136 name = "interrupt"; 137 break; 138 case EXCP_TLB_X: 139 case EXCP_TLB_D: 140 if (env->ctrl[CR_STATUS] & CR_STATUS_EH) { 141 name = "TLB MISS (double)"; 142 } else { 143 name = "TLB MISS (fast)"; 144 } 145 break; 146 case EXCP_PERM_R: 147 case EXCP_PERM_W: 148 case EXCP_PERM_X: 149 name = "TLB PERM"; 150 break; 151 case EXCP_SUPERA_X: 152 case EXCP_SUPERA_D: 153 name = "SUPERVISOR (address)"; 154 break; 155 case EXCP_SUPERI: 156 name = "SUPERVISOR (insn)"; 157 break; 158 case EXCP_ILLEGAL: 159 name = "ILLEGAL insn"; 160 break; 161 case EXCP_UNALIGN: 162 name = "Misaligned (data)"; 163 break; 164 case EXCP_UNALIGND: 165 name = "Misaligned (destination)"; 166 break; 167 case EXCP_DIV: 168 name = "DIV error"; 169 break; 170 case EXCP_TRAP: 171 name = "TRAP insn"; 172 break; 173 case EXCP_BREAK: 174 name = "BREAK insn"; 175 break; 176 case EXCP_SEMIHOST: 177 name = "SEMIHOST insn"; 178 break; 179 } 180 if (name) { 181 qemu_log("%s at pc=0x%08x\n", name, env->pc); 182 } else { 183 qemu_log("Unknown exception %d at pc=0x%08x\n", 184 cs->exception_index, env->pc); 185 } 186 } 187 188 switch (cs->exception_index) { 189 case EXCP_IRQ: 190 /* Note that PC is advanced for interrupts as well. */ 191 env->pc += 4; 192 if (cpu->eic_present) { 193 do_eic_irq(cpu); 194 } else { 195 do_iic_irq(cpu); 196 } 197 break; 198 199 case EXCP_TLB_D: 200 tlbmisc_set = CR_TLBMISC_D; 201 /* fall through */ 202 case EXCP_TLB_X: 203 if (env->ctrl[CR_STATUS] & CR_STATUS_EH) { 204 tlbmisc_set |= CR_TLBMISC_DBL; 205 /* 206 * Normally, we don't write to tlbmisc unless !EH, 207 * so do it manually for the double-tlb miss exception. 208 */ 209 env->ctrl[CR_TLBMISC] &= ~(CR_TLBMISC_D | 210 CR_TLBMISC_PERM | 211 CR_TLBMISC_BAD); 212 env->ctrl[CR_TLBMISC] |= tlbmisc_set; 213 do_exception(cpu, cpu->exception_addr, 0, false); 214 } else { 215 tlbmisc_set |= CR_TLBMISC_WE; 216 do_exception(cpu, cpu->fast_tlb_miss_addr, tlbmisc_set, false); 217 } 218 break; 219 220 case EXCP_PERM_R: 221 case EXCP_PERM_W: 222 tlbmisc_set = CR_TLBMISC_D; 223 /* fall through */ 224 case EXCP_PERM_X: 225 tlbmisc_set |= CR_TLBMISC_PERM; 226 if (!(env->ctrl[CR_STATUS] & CR_STATUS_EH)) { 227 tlbmisc_set |= CR_TLBMISC_WE; 228 } 229 do_exception(cpu, cpu->exception_addr, tlbmisc_set, false); 230 break; 231 232 case EXCP_SUPERA_D: 233 case EXCP_UNALIGN: 234 tlbmisc_set = CR_TLBMISC_D; 235 /* fall through */ 236 case EXCP_SUPERA_X: 237 case EXCP_UNALIGND: 238 tlbmisc_set |= CR_TLBMISC_BAD; 239 do_exception(cpu, cpu->exception_addr, tlbmisc_set, false); 240 break; 241 242 case EXCP_SUPERI: 243 case EXCP_ILLEGAL: 244 case EXCP_DIV: 245 case EXCP_TRAP: 246 do_exception(cpu, cpu->exception_addr, 0, false); 247 break; 248 249 case EXCP_BREAK: 250 do_exception(cpu, cpu->exception_addr, 0, true); 251 break; 252 253 case EXCP_SEMIHOST: 254 do_nios2_semihosting(env); 255 break; 256 257 default: 258 cpu_abort(cs, "unhandled exception type=%d\n", cs->exception_index); 259 } 260 } 261 262 hwaddr nios2_cpu_get_phys_page_debug(CPUState *cs, vaddr addr) 263 { 264 Nios2CPU *cpu = NIOS2_CPU(cs); 265 CPUNios2State *env = &cpu->env; 266 target_ulong vaddr, paddr = 0; 267 Nios2MMULookup lu; 268 unsigned int hit; 269 270 if (cpu->mmu_present && (addr < 0xC0000000)) { 271 hit = mmu_translate(env, &lu, addr, 0, 0); 272 if (hit) { 273 vaddr = addr & TARGET_PAGE_MASK; 274 paddr = lu.paddr + vaddr - lu.vaddr; 275 } else { 276 paddr = -1; 277 qemu_log("cpu_get_phys_page debug MISS: %#" PRIx64 "\n", addr); 278 } 279 } else { 280 paddr = addr & TARGET_PAGE_MASK; 281 } 282 283 return paddr; 284 } 285 286 void nios2_cpu_do_unaligned_access(CPUState *cs, vaddr addr, 287 MMUAccessType access_type, 288 int mmu_idx, uintptr_t retaddr) 289 { 290 Nios2CPU *cpu = NIOS2_CPU(cs); 291 CPUNios2State *env = &cpu->env; 292 293 env->ctrl[CR_BADADDR] = addr; 294 cs->exception_index = EXCP_UNALIGN; 295 nios2_cpu_loop_exit_advance(env, retaddr); 296 } 297 298 bool nios2_cpu_tlb_fill(CPUState *cs, vaddr address, int size, 299 MMUAccessType access_type, int mmu_idx, 300 bool probe, uintptr_t retaddr) 301 { 302 Nios2CPU *cpu = NIOS2_CPU(cs); 303 CPUNios2State *env = &cpu->env; 304 unsigned int excp; 305 target_ulong vaddr, paddr; 306 Nios2MMULookup lu; 307 unsigned int hit; 308 309 if (!cpu->mmu_present) { 310 /* No MMU */ 311 address &= TARGET_PAGE_MASK; 312 tlb_set_page(cs, address, address, PAGE_BITS, 313 mmu_idx, TARGET_PAGE_SIZE); 314 return true; 315 } 316 317 if (MMU_SUPERVISOR_IDX == mmu_idx) { 318 if (address >= 0xC0000000) { 319 /* Kernel physical page - TLB bypassed */ 320 address &= TARGET_PAGE_MASK; 321 tlb_set_page(cs, address, address, PAGE_BITS, 322 mmu_idx, TARGET_PAGE_SIZE); 323 return true; 324 } 325 } else { 326 if (address >= 0x80000000) { 327 /* Illegal access from user mode */ 328 if (probe) { 329 return false; 330 } 331 cs->exception_index = (access_type == MMU_INST_FETCH 332 ? EXCP_SUPERA_X : EXCP_SUPERA_D); 333 env->ctrl[CR_BADADDR] = address; 334 nios2_cpu_loop_exit_advance(env, retaddr); 335 } 336 } 337 338 /* Virtual page. */ 339 hit = mmu_translate(env, &lu, address, access_type, mmu_idx); 340 if (hit) { 341 vaddr = address & TARGET_PAGE_MASK; 342 paddr = lu.paddr + vaddr - lu.vaddr; 343 344 if (((access_type == MMU_DATA_LOAD) && (lu.prot & PAGE_READ)) || 345 ((access_type == MMU_DATA_STORE) && (lu.prot & PAGE_WRITE)) || 346 ((access_type == MMU_INST_FETCH) && (lu.prot & PAGE_EXEC))) { 347 tlb_set_page(cs, vaddr, paddr, lu.prot, 348 mmu_idx, TARGET_PAGE_SIZE); 349 return true; 350 } 351 352 /* Permission violation */ 353 excp = (access_type == MMU_DATA_LOAD ? EXCP_PERM_R : 354 access_type == MMU_DATA_STORE ? EXCP_PERM_W : EXCP_PERM_X); 355 } else { 356 excp = (access_type == MMU_INST_FETCH ? EXCP_TLB_X: EXCP_TLB_D); 357 } 358 359 if (probe) { 360 return false; 361 } 362 363 env->ctrl[CR_TLBMISC] = FIELD_DP32(env->ctrl[CR_TLBMISC], CR_TLBMISC, D, 364 access_type != MMU_INST_FETCH); 365 env->ctrl[CR_PTEADDR] = FIELD_DP32(env->ctrl[CR_PTEADDR], CR_PTEADDR, VPN, 366 address >> TARGET_PAGE_BITS); 367 env->mmu.pteaddr_wr = env->ctrl[CR_PTEADDR]; 368 369 cs->exception_index = excp; 370 env->ctrl[CR_BADADDR] = address; 371 nios2_cpu_loop_exit_advance(env, retaddr); 372 }