helper.c (10172B)
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#if defined(CONFIG_USER_ONLY) 32 33void nios2_cpu_do_interrupt(CPUState *cs) 34{ 35 Nios2CPU *cpu = NIOS2_CPU(cs); 36 CPUNios2State *env = &cpu->env; 37 cs->exception_index = -1; 38 env->regs[R_EA] = env->regs[R_PC] + 4; 39} 40 41bool nios2_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 51void nios2_cpu_do_interrupt(CPUState *cs) 52{ 53 Nios2CPU *cpu = NIOS2_CPU(cs); 54 CPUNios2State *env = &cpu->env; 55 56 switch (cs->exception_index) { 57 case EXCP_IRQ: 58 assert(env->regs[CR_STATUS] & CR_STATUS_PIE); 59 60 qemu_log_mask(CPU_LOG_INT, "interrupt at pc=%x\n", env->regs[R_PC]); 61 62 env->regs[CR_ESTATUS] = env->regs[CR_STATUS]; 63 env->regs[CR_STATUS] |= CR_STATUS_IH; 64 env->regs[CR_STATUS] &= ~(CR_STATUS_PIE | CR_STATUS_U); 65 66 env->regs[CR_EXCEPTION] &= ~(0x1F << 2); 67 env->regs[CR_EXCEPTION] |= (cs->exception_index & 0x1F) << 2; 68 69 env->regs[R_EA] = env->regs[R_PC] + 4; 70 env->regs[R_PC] = cpu->exception_addr; 71 break; 72 73 case EXCP_TLBD: 74 if ((env->regs[CR_STATUS] & CR_STATUS_EH) == 0) { 75 qemu_log_mask(CPU_LOG_INT, "TLB MISS (fast) at pc=%x\n", 76 env->regs[R_PC]); 77 78 /* Fast TLB miss */ 79 /* Variation from the spec. Table 3-35 of the cpu reference shows 80 * estatus not being changed for TLB miss but this appears to 81 * be incorrect. */ 82 env->regs[CR_ESTATUS] = env->regs[CR_STATUS]; 83 env->regs[CR_STATUS] |= CR_STATUS_EH; 84 env->regs[CR_STATUS] &= ~(CR_STATUS_PIE | CR_STATUS_U); 85 86 env->regs[CR_EXCEPTION] &= ~(0x1F << 2); 87 env->regs[CR_EXCEPTION] |= (cs->exception_index & 0x1F) << 2; 88 89 env->regs[CR_TLBMISC] &= ~CR_TLBMISC_DBL; 90 env->regs[CR_TLBMISC] |= CR_TLBMISC_WR; 91 92 env->regs[R_EA] = env->regs[R_PC] + 4; 93 env->regs[R_PC] = cpu->fast_tlb_miss_addr; 94 } else { 95 qemu_log_mask(CPU_LOG_INT, "TLB MISS (double) at pc=%x\n", 96 env->regs[R_PC]); 97 98 /* Double TLB miss */ 99 env->regs[CR_STATUS] |= CR_STATUS_EH; 100 env->regs[CR_STATUS] &= ~(CR_STATUS_PIE | CR_STATUS_U); 101 102 env->regs[CR_EXCEPTION] &= ~(0x1F << 2); 103 env->regs[CR_EXCEPTION] |= (cs->exception_index & 0x1F) << 2; 104 105 env->regs[CR_TLBMISC] |= CR_TLBMISC_DBL; 106 107 env->regs[R_PC] = cpu->exception_addr; 108 } 109 break; 110 111 case EXCP_TLBR: 112 case EXCP_TLBW: 113 case EXCP_TLBX: 114 qemu_log_mask(CPU_LOG_INT, "TLB PERM at pc=%x\n", env->regs[R_PC]); 115 116 env->regs[CR_ESTATUS] = env->regs[CR_STATUS]; 117 env->regs[CR_STATUS] |= CR_STATUS_EH; 118 env->regs[CR_STATUS] &= ~(CR_STATUS_PIE | CR_STATUS_U); 119 120 env->regs[CR_EXCEPTION] &= ~(0x1F << 2); 121 env->regs[CR_EXCEPTION] |= (cs->exception_index & 0x1F) << 2; 122 123 if ((env->regs[CR_STATUS] & CR_STATUS_EH) == 0) { 124 env->regs[CR_TLBMISC] |= CR_TLBMISC_WR; 125 } 126 127 env->regs[R_EA] = env->regs[R_PC] + 4; 128 env->regs[R_PC] = cpu->exception_addr; 129 break; 130 131 case EXCP_SUPERA: 132 case EXCP_SUPERI: 133 case EXCP_SUPERD: 134 qemu_log_mask(CPU_LOG_INT, "SUPERVISOR exception at pc=%x\n", 135 env->regs[R_PC]); 136 137 if ((env->regs[CR_STATUS] & CR_STATUS_EH) == 0) { 138 env->regs[CR_ESTATUS] = env->regs[CR_STATUS]; 139 env->regs[R_EA] = env->regs[R_PC] + 4; 140 } 141 142 env->regs[CR_STATUS] |= CR_STATUS_EH; 143 env->regs[CR_STATUS] &= ~(CR_STATUS_PIE | CR_STATUS_U); 144 145 env->regs[CR_EXCEPTION] &= ~(0x1F << 2); 146 env->regs[CR_EXCEPTION] |= (cs->exception_index & 0x1F) << 2; 147 148 env->regs[R_PC] = cpu->exception_addr; 149 break; 150 151 case EXCP_ILLEGAL: 152 case EXCP_TRAP: 153 qemu_log_mask(CPU_LOG_INT, "TRAP exception at pc=%x\n", 154 env->regs[R_PC]); 155 156 if ((env->regs[CR_STATUS] & CR_STATUS_EH) == 0) { 157 env->regs[CR_ESTATUS] = env->regs[CR_STATUS]; 158 env->regs[R_EA] = env->regs[R_PC] + 4; 159 } 160 161 env->regs[CR_STATUS] |= CR_STATUS_EH; 162 env->regs[CR_STATUS] &= ~(CR_STATUS_PIE | CR_STATUS_U); 163 164 env->regs[CR_EXCEPTION] &= ~(0x1F << 2); 165 env->regs[CR_EXCEPTION] |= (cs->exception_index & 0x1F) << 2; 166 167 env->regs[R_PC] = cpu->exception_addr; 168 break; 169 170 case EXCP_BREAK: 171 qemu_log_mask(CPU_LOG_INT, "BREAK exception at pc=%x\n", 172 env->regs[R_PC]); 173 /* The semihosting instruction is "break 1". */ 174 if (semihosting_enabled() && 175 cpu_ldl_code(env, env->regs[R_PC]) == 0x003da07a) { 176 qemu_log_mask(CPU_LOG_INT, "Entering semihosting\n"); 177 env->regs[R_PC] += 4; 178 do_nios2_semihosting(env); 179 break; 180 } 181 182 if ((env->regs[CR_STATUS] & CR_STATUS_EH) == 0) { 183 env->regs[CR_BSTATUS] = env->regs[CR_STATUS]; 184 env->regs[R_BA] = env->regs[R_PC] + 4; 185 } 186 187 env->regs[CR_STATUS] |= CR_STATUS_EH; 188 env->regs[CR_STATUS] &= ~(CR_STATUS_PIE | CR_STATUS_U); 189 190 env->regs[CR_EXCEPTION] &= ~(0x1F << 2); 191 env->regs[CR_EXCEPTION] |= (cs->exception_index & 0x1F) << 2; 192 193 env->regs[R_PC] = cpu->exception_addr; 194 break; 195 196 default: 197 cpu_abort(cs, "unhandled exception type=%d\n", 198 cs->exception_index); 199 break; 200 } 201} 202 203hwaddr nios2_cpu_get_phys_page_debug(CPUState *cs, vaddr addr) 204{ 205 Nios2CPU *cpu = NIOS2_CPU(cs); 206 CPUNios2State *env = &cpu->env; 207 target_ulong vaddr, paddr = 0; 208 Nios2MMULookup lu; 209 unsigned int hit; 210 211 if (cpu->mmu_present && (addr < 0xC0000000)) { 212 hit = mmu_translate(env, &lu, addr, 0, 0); 213 if (hit) { 214 vaddr = addr & TARGET_PAGE_MASK; 215 paddr = lu.paddr + vaddr - lu.vaddr; 216 } else { 217 paddr = -1; 218 qemu_log("cpu_get_phys_page debug MISS: %#" PRIx64 "\n", addr); 219 } 220 } else { 221 paddr = addr & TARGET_PAGE_MASK; 222 } 223 224 return paddr; 225} 226 227void nios2_cpu_do_unaligned_access(CPUState *cs, vaddr addr, 228 MMUAccessType access_type, 229 int mmu_idx, uintptr_t retaddr) 230{ 231 Nios2CPU *cpu = NIOS2_CPU(cs); 232 CPUNios2State *env = &cpu->env; 233 234 env->regs[CR_BADADDR] = addr; 235 env->regs[CR_EXCEPTION] = EXCP_UNALIGN << 2; 236 helper_raise_exception(env, EXCP_UNALIGN); 237} 238 239bool nios2_cpu_tlb_fill(CPUState *cs, vaddr address, int size, 240 MMUAccessType access_type, int mmu_idx, 241 bool probe, uintptr_t retaddr) 242{ 243 Nios2CPU *cpu = NIOS2_CPU(cs); 244 CPUNios2State *env = &cpu->env; 245 unsigned int excp = EXCP_TLBD; 246 target_ulong vaddr, paddr; 247 Nios2MMULookup lu; 248 unsigned int hit; 249 250 if (!cpu->mmu_present) { 251 /* No MMU */ 252 address &= TARGET_PAGE_MASK; 253 tlb_set_page(cs, address, address, PAGE_BITS, 254 mmu_idx, TARGET_PAGE_SIZE); 255 return true; 256 } 257 258 if (MMU_SUPERVISOR_IDX == mmu_idx) { 259 if (address >= 0xC0000000) { 260 /* Kernel physical page - TLB bypassed */ 261 address &= TARGET_PAGE_MASK; 262 tlb_set_page(cs, address, address, PAGE_BITS, 263 mmu_idx, TARGET_PAGE_SIZE); 264 return true; 265 } 266 } else { 267 if (address >= 0x80000000) { 268 /* Illegal access from user mode */ 269 if (probe) { 270 return false; 271 } 272 cs->exception_index = EXCP_SUPERA; 273 env->regs[CR_BADADDR] = address; 274 cpu_loop_exit_restore(cs, retaddr); 275 } 276 } 277 278 /* Virtual page. */ 279 hit = mmu_translate(env, &lu, address, access_type, mmu_idx); 280 if (hit) { 281 vaddr = address & TARGET_PAGE_MASK; 282 paddr = lu.paddr + vaddr - lu.vaddr; 283 284 if (((access_type == MMU_DATA_LOAD) && (lu.prot & PAGE_READ)) || 285 ((access_type == MMU_DATA_STORE) && (lu.prot & PAGE_WRITE)) || 286 ((access_type == MMU_INST_FETCH) && (lu.prot & PAGE_EXEC))) { 287 tlb_set_page(cs, vaddr, paddr, lu.prot, 288 mmu_idx, TARGET_PAGE_SIZE); 289 return true; 290 } 291 292 /* Permission violation */ 293 excp = (access_type == MMU_DATA_LOAD ? EXCP_TLBR : 294 access_type == MMU_DATA_STORE ? EXCP_TLBW : EXCP_TLBX); 295 } 296 297 if (probe) { 298 return false; 299 } 300 301 if (access_type == MMU_INST_FETCH) { 302 env->regs[CR_TLBMISC] &= ~CR_TLBMISC_D; 303 } else { 304 env->regs[CR_TLBMISC] |= CR_TLBMISC_D; 305 } 306 env->regs[CR_PTEADDR] &= CR_PTEADDR_PTBASE_MASK; 307 env->regs[CR_PTEADDR] |= (address >> 10) & CR_PTEADDR_VPN_MASK; 308 env->mmu.pteaddr_wr = env->regs[CR_PTEADDR]; 309 310 cs->exception_index = excp; 311 env->regs[CR_BADADDR] = address; 312 cpu_loop_exit_restore(cs, retaddr); 313} 314#endif /* !CONFIG_USER_ONLY */