1 /* 2 * Alpha emulation cpu helpers for qemu. 3 * 4 * Copyright (c) 2007 Jocelyn Mayer 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 <http://www.gnu.org/licenses/>. 18 */ 19 20 #include "qemu/osdep.h" 21 #include "qemu/log.h" 22 #include "cpu.h" 23 #include "exec/exec-all.h" 24 #include "fpu/softfloat-types.h" 25 #include "exec/helper-proto.h" 26 #include "qemu/qemu-print.h" 27 28 29 #define CONVERT_BIT(X, SRC, DST) \ 30 (SRC > DST ? (X) / (SRC / DST) & (DST) : ((X) & SRC) * (DST / SRC)) 31 32 uint64_t cpu_alpha_load_fpcr(CPUAlphaState *env) 33 { 34 return (uint64_t)env->fpcr << 32; 35 } 36 37 void cpu_alpha_store_fpcr(CPUAlphaState *env, uint64_t val) 38 { 39 static const uint8_t rm_map[] = { 40 [FPCR_DYN_NORMAL >> FPCR_DYN_SHIFT] = float_round_nearest_even, 41 [FPCR_DYN_CHOPPED >> FPCR_DYN_SHIFT] = float_round_to_zero, 42 [FPCR_DYN_MINUS >> FPCR_DYN_SHIFT] = float_round_down, 43 [FPCR_DYN_PLUS >> FPCR_DYN_SHIFT] = float_round_up, 44 }; 45 46 uint32_t fpcr = val >> 32; 47 uint32_t t = 0; 48 49 /* Record the raw value before adjusting for linux-user. */ 50 env->fpcr = fpcr; 51 52 #ifdef CONFIG_USER_ONLY 53 /* 54 * Override some of these bits with the contents of ENV->SWCR. 55 * In system mode, some of these would trap to the kernel, at 56 * which point the kernel's handler would emulate and apply 57 * the software exception mask. 58 */ 59 uint32_t soft_fpcr = alpha_ieee_swcr_to_fpcr(env->swcr) >> 32; 60 fpcr |= soft_fpcr & (FPCR_STATUS_MASK | FPCR_DNZ); 61 62 /* 63 * The IOV exception is disabled by the kernel with SWCR_TRAP_ENABLE_INV, 64 * which got mapped by alpha_ieee_swcr_to_fpcr to FPCR_INVD. 65 * Add FPCR_IOV to fpcr_exc_enable so that it is handled identically. 66 */ 67 t |= CONVERT_BIT(soft_fpcr, FPCR_INVD, FPCR_IOV); 68 #endif 69 70 t |= CONVERT_BIT(fpcr, FPCR_INED, FPCR_INE); 71 t |= CONVERT_BIT(fpcr, FPCR_UNFD, FPCR_UNF); 72 t |= CONVERT_BIT(fpcr, FPCR_OVFD, FPCR_OVF); 73 t |= CONVERT_BIT(fpcr, FPCR_DZED, FPCR_DZE); 74 t |= CONVERT_BIT(fpcr, FPCR_INVD, FPCR_INV); 75 76 env->fpcr_exc_enable = ~t & FPCR_STATUS_MASK; 77 78 env->fpcr_dyn_round = rm_map[(fpcr & FPCR_DYN_MASK) >> FPCR_DYN_SHIFT]; 79 env->fp_status.flush_inputs_to_zero = (fpcr & FPCR_DNZ) != 0; 80 81 t = (fpcr & FPCR_UNFD) && (fpcr & FPCR_UNDZ); 82 #ifdef CONFIG_USER_ONLY 83 t |= (env->swcr & SWCR_MAP_UMZ) != 0; 84 #endif 85 env->fpcr_flush_to_zero = t; 86 } 87 88 uint64_t helper_load_fpcr(CPUAlphaState *env) 89 { 90 return cpu_alpha_load_fpcr(env); 91 } 92 93 void helper_store_fpcr(CPUAlphaState *env, uint64_t val) 94 { 95 cpu_alpha_store_fpcr(env, val); 96 } 97 98 static uint64_t *cpu_alpha_addr_gr(CPUAlphaState *env, unsigned reg) 99 { 100 #ifndef CONFIG_USER_ONLY 101 if (env->flags & ENV_FLAG_PAL_MODE) { 102 if (reg >= 8 && reg <= 14) { 103 return &env->shadow[reg - 8]; 104 } else if (reg == 25) { 105 return &env->shadow[7]; 106 } 107 } 108 #endif 109 return &env->ir[reg]; 110 } 111 112 uint64_t cpu_alpha_load_gr(CPUAlphaState *env, unsigned reg) 113 { 114 return *cpu_alpha_addr_gr(env, reg); 115 } 116 117 void cpu_alpha_store_gr(CPUAlphaState *env, unsigned reg, uint64_t val) 118 { 119 *cpu_alpha_addr_gr(env, reg) = val; 120 } 121 122 #if defined(CONFIG_USER_ONLY) 123 void alpha_cpu_record_sigsegv(CPUState *cs, vaddr address, 124 MMUAccessType access_type, 125 bool maperr, uintptr_t retaddr) 126 { 127 AlphaCPU *cpu = ALPHA_CPU(cs); 128 target_ulong mmcsr, cause; 129 130 /* Assuming !maperr, infer the missing protection. */ 131 switch (access_type) { 132 case MMU_DATA_LOAD: 133 mmcsr = MM_K_FOR; 134 cause = 0; 135 break; 136 case MMU_DATA_STORE: 137 mmcsr = MM_K_FOW; 138 cause = 1; 139 break; 140 case MMU_INST_FETCH: 141 mmcsr = MM_K_FOE; 142 cause = -1; 143 break; 144 default: 145 g_assert_not_reached(); 146 } 147 if (maperr) { 148 if (address < BIT_ULL(TARGET_VIRT_ADDR_SPACE_BITS - 1)) { 149 /* Userspace address, therefore page not mapped. */ 150 mmcsr = MM_K_TNV; 151 } else { 152 /* Kernel or invalid address. */ 153 mmcsr = MM_K_ACV; 154 } 155 } 156 157 /* Record the arguments that PALcode would give to the kernel. */ 158 cpu->env.trap_arg0 = address; 159 cpu->env.trap_arg1 = mmcsr; 160 cpu->env.trap_arg2 = cause; 161 } 162 #else 163 /* Returns the OSF/1 entMM failure indication, or -1 on success. */ 164 static int get_physical_address(CPUAlphaState *env, target_ulong addr, 165 int prot_need, int mmu_idx, 166 target_ulong *pphys, int *pprot) 167 { 168 CPUState *cs = env_cpu(env); 169 target_long saddr = addr; 170 target_ulong phys = 0; 171 target_ulong L1pte, L2pte, L3pte; 172 target_ulong pt, index; 173 int prot = 0; 174 int ret = MM_K_ACV; 175 176 /* Handle physical accesses. */ 177 if (mmu_idx == MMU_PHYS_IDX) { 178 phys = addr; 179 prot = PAGE_READ | PAGE_WRITE | PAGE_EXEC; 180 ret = -1; 181 goto exit; 182 } 183 184 /* Ensure that the virtual address is properly sign-extended from 185 the last implemented virtual address bit. */ 186 if (saddr >> TARGET_VIRT_ADDR_SPACE_BITS != saddr >> 63) { 187 goto exit; 188 } 189 190 /* Translate the superpage. */ 191 /* ??? When we do more than emulate Unix PALcode, we'll need to 192 determine which KSEG is actually active. */ 193 if (saddr < 0 && ((saddr >> 41) & 3) == 2) { 194 /* User-space cannot access KSEG addresses. */ 195 if (mmu_idx != MMU_KERNEL_IDX) { 196 goto exit; 197 } 198 199 /* For the benefit of the Typhoon chipset, move bit 40 to bit 43. 200 We would not do this if the 48-bit KSEG is enabled. */ 201 phys = saddr & ((1ull << 40) - 1); 202 phys |= (saddr & (1ull << 40)) << 3; 203 204 prot = PAGE_READ | PAGE_WRITE | PAGE_EXEC; 205 ret = -1; 206 goto exit; 207 } 208 209 /* Interpret the page table exactly like PALcode does. */ 210 211 pt = env->ptbr; 212 213 /* TODO: rather than using ldq_phys() to read the page table we should 214 * use address_space_ldq() so that we can handle the case when 215 * the page table read gives a bus fault, rather than ignoring it. 216 * For the existing code the zero data that ldq_phys will return for 217 * an access to invalid memory will result in our treating the page 218 * table as invalid, which may even be the right behaviour. 219 */ 220 221 /* L1 page table read. */ 222 index = (addr >> (TARGET_PAGE_BITS + 20)) & 0x3ff; 223 L1pte = ldq_phys(cs->as, pt + index*8); 224 225 if (unlikely((L1pte & PTE_VALID) == 0)) { 226 ret = MM_K_TNV; 227 goto exit; 228 } 229 if (unlikely((L1pte & PTE_KRE) == 0)) { 230 goto exit; 231 } 232 pt = L1pte >> 32 << TARGET_PAGE_BITS; 233 234 /* L2 page table read. */ 235 index = (addr >> (TARGET_PAGE_BITS + 10)) & 0x3ff; 236 L2pte = ldq_phys(cs->as, pt + index*8); 237 238 if (unlikely((L2pte & PTE_VALID) == 0)) { 239 ret = MM_K_TNV; 240 goto exit; 241 } 242 if (unlikely((L2pte & PTE_KRE) == 0)) { 243 goto exit; 244 } 245 pt = L2pte >> 32 << TARGET_PAGE_BITS; 246 247 /* L3 page table read. */ 248 index = (addr >> TARGET_PAGE_BITS) & 0x3ff; 249 L3pte = ldq_phys(cs->as, pt + index*8); 250 251 phys = L3pte >> 32 << TARGET_PAGE_BITS; 252 if (unlikely((L3pte & PTE_VALID) == 0)) { 253 ret = MM_K_TNV; 254 goto exit; 255 } 256 257 #if PAGE_READ != 1 || PAGE_WRITE != 2 || PAGE_EXEC != 4 258 # error page bits out of date 259 #endif 260 261 /* Check access violations. */ 262 if (L3pte & (PTE_KRE << mmu_idx)) { 263 prot |= PAGE_READ | PAGE_EXEC; 264 } 265 if (L3pte & (PTE_KWE << mmu_idx)) { 266 prot |= PAGE_WRITE; 267 } 268 if (unlikely((prot & prot_need) == 0 && prot_need)) { 269 goto exit; 270 } 271 272 /* Check fault-on-operation violations. */ 273 prot &= ~(L3pte >> 1); 274 ret = -1; 275 if (unlikely((prot & prot_need) == 0)) { 276 ret = (prot_need & PAGE_EXEC ? MM_K_FOE : 277 prot_need & PAGE_WRITE ? MM_K_FOW : 278 prot_need & PAGE_READ ? MM_K_FOR : -1); 279 } 280 281 exit: 282 *pphys = phys; 283 *pprot = prot; 284 return ret; 285 } 286 287 hwaddr alpha_cpu_get_phys_page_debug(CPUState *cs, vaddr addr) 288 { 289 target_ulong phys; 290 int prot, fail; 291 292 fail = get_physical_address(cpu_env(cs), addr, 0, 0, &phys, &prot); 293 return (fail >= 0 ? -1 : phys); 294 } 295 296 bool alpha_cpu_tlb_fill(CPUState *cs, vaddr addr, int size, 297 MMUAccessType access_type, int mmu_idx, 298 bool probe, uintptr_t retaddr) 299 { 300 CPUAlphaState *env = cpu_env(cs); 301 target_ulong phys; 302 int prot, fail; 303 304 fail = get_physical_address(env, addr, 1 << access_type, 305 mmu_idx, &phys, &prot); 306 if (unlikely(fail >= 0)) { 307 if (probe) { 308 return false; 309 } 310 cs->exception_index = EXCP_MMFAULT; 311 env->trap_arg0 = addr; 312 env->trap_arg1 = fail; 313 env->trap_arg2 = (access_type == MMU_DATA_LOAD ? 0ull : 314 access_type == MMU_DATA_STORE ? 1ull : 315 /* access_type == MMU_INST_FETCH */ -1ull); 316 cpu_loop_exit_restore(cs, retaddr); 317 } 318 319 tlb_set_page(cs, addr & TARGET_PAGE_MASK, phys & TARGET_PAGE_MASK, 320 prot, mmu_idx, TARGET_PAGE_SIZE); 321 return true; 322 } 323 324 void alpha_cpu_do_interrupt(CPUState *cs) 325 { 326 CPUAlphaState *env = cpu_env(cs); 327 int i = cs->exception_index; 328 329 if (qemu_loglevel_mask(CPU_LOG_INT)) { 330 static int count; 331 const char *name = "<unknown>"; 332 333 switch (i) { 334 case EXCP_RESET: 335 name = "reset"; 336 break; 337 case EXCP_MCHK: 338 name = "mchk"; 339 break; 340 case EXCP_SMP_INTERRUPT: 341 name = "smp_interrupt"; 342 break; 343 case EXCP_CLK_INTERRUPT: 344 name = "clk_interrupt"; 345 break; 346 case EXCP_DEV_INTERRUPT: 347 name = "dev_interrupt"; 348 break; 349 case EXCP_MMFAULT: 350 name = "mmfault"; 351 break; 352 case EXCP_UNALIGN: 353 name = "unalign"; 354 break; 355 case EXCP_OPCDEC: 356 name = "opcdec"; 357 break; 358 case EXCP_ARITH: 359 name = "arith"; 360 break; 361 case EXCP_FEN: 362 name = "fen"; 363 break; 364 case EXCP_CALL_PAL: 365 name = "call_pal"; 366 break; 367 } 368 qemu_log("INT %6d: %s(%#x) cpu=%d pc=%016" 369 PRIx64 " sp=%016" PRIx64 "\n", 370 ++count, name, env->error_code, cs->cpu_index, 371 env->pc, env->ir[IR_SP]); 372 } 373 374 cs->exception_index = -1; 375 376 switch (i) { 377 case EXCP_RESET: 378 i = 0x0000; 379 break; 380 case EXCP_MCHK: 381 i = 0x0080; 382 break; 383 case EXCP_SMP_INTERRUPT: 384 i = 0x0100; 385 break; 386 case EXCP_CLK_INTERRUPT: 387 i = 0x0180; 388 break; 389 case EXCP_DEV_INTERRUPT: 390 i = 0x0200; 391 break; 392 case EXCP_MMFAULT: 393 i = 0x0280; 394 break; 395 case EXCP_UNALIGN: 396 i = 0x0300; 397 break; 398 case EXCP_OPCDEC: 399 i = 0x0380; 400 break; 401 case EXCP_ARITH: 402 i = 0x0400; 403 break; 404 case EXCP_FEN: 405 i = 0x0480; 406 break; 407 case EXCP_CALL_PAL: 408 i = env->error_code; 409 /* There are 64 entry points for both privileged and unprivileged, 410 with bit 0x80 indicating unprivileged. Each entry point gets 411 64 bytes to do its job. */ 412 if (i & 0x80) { 413 i = 0x2000 + (i - 0x80) * 64; 414 } else { 415 i = 0x1000 + i * 64; 416 } 417 break; 418 default: 419 cpu_abort(cs, "Unhandled CPU exception"); 420 } 421 422 /* Remember where the exception happened. Emulate real hardware in 423 that the low bit of the PC indicates PALmode. */ 424 env->exc_addr = env->pc | (env->flags & ENV_FLAG_PAL_MODE); 425 426 /* Continue execution at the PALcode entry point. */ 427 env->pc = env->palbr + i; 428 429 /* Switch to PALmode. */ 430 env->flags |= ENV_FLAG_PAL_MODE; 431 } 432 433 bool alpha_cpu_exec_interrupt(CPUState *cs, int interrupt_request) 434 { 435 CPUAlphaState *env = cpu_env(cs); 436 int idx = -1; 437 438 /* We never take interrupts while in PALmode. */ 439 if (env->flags & ENV_FLAG_PAL_MODE) { 440 return false; 441 } 442 443 /* Fall through the switch, collecting the highest priority 444 interrupt that isn't masked by the processor status IPL. */ 445 /* ??? This hard-codes the OSF/1 interrupt levels. */ 446 switch ((env->flags >> ENV_FLAG_PS_SHIFT) & PS_INT_MASK) { 447 case 0 ... 3: 448 if (interrupt_request & CPU_INTERRUPT_HARD) { 449 idx = EXCP_DEV_INTERRUPT; 450 } 451 /* FALLTHRU */ 452 case 4: 453 if (interrupt_request & CPU_INTERRUPT_TIMER) { 454 idx = EXCP_CLK_INTERRUPT; 455 } 456 /* FALLTHRU */ 457 case 5: 458 if (interrupt_request & CPU_INTERRUPT_SMP) { 459 idx = EXCP_SMP_INTERRUPT; 460 } 461 /* FALLTHRU */ 462 case 6: 463 if (interrupt_request & CPU_INTERRUPT_MCHK) { 464 idx = EXCP_MCHK; 465 } 466 } 467 if (idx >= 0) { 468 cs->exception_index = idx; 469 env->error_code = 0; 470 alpha_cpu_do_interrupt(cs); 471 return true; 472 } 473 return false; 474 } 475 476 #endif /* !CONFIG_USER_ONLY */ 477 478 void alpha_cpu_dump_state(CPUState *cs, FILE *f, int flags) 479 { 480 static const char linux_reg_names[31][4] = { 481 "v0", "t0", "t1", "t2", "t3", "t4", "t5", "t6", 482 "t7", "s0", "s1", "s2", "s3", "s4", "s5", "fp", 483 "a0", "a1", "a2", "a3", "a4", "a5", "t8", "t9", 484 "t10", "t11", "ra", "t12", "at", "gp", "sp" 485 }; 486 CPUAlphaState *env = cpu_env(cs); 487 int i; 488 489 qemu_fprintf(f, "PC " TARGET_FMT_lx " PS %02x\n", 490 env->pc, extract32(env->flags, ENV_FLAG_PS_SHIFT, 8)); 491 for (i = 0; i < 31; i++) { 492 qemu_fprintf(f, "%-8s" TARGET_FMT_lx "%c", 493 linux_reg_names[i], cpu_alpha_load_gr(env, i), 494 (i % 3) == 2 ? '\n' : ' '); 495 } 496 497 qemu_fprintf(f, "lock_a " TARGET_FMT_lx " lock_v " TARGET_FMT_lx "\n", 498 env->lock_addr, env->lock_value); 499 500 if (flags & CPU_DUMP_FPU) { 501 for (i = 0; i < 31; i++) { 502 qemu_fprintf(f, "f%-7d%016" PRIx64 "%c", i, env->fir[i], 503 (i % 3) == 2 ? '\n' : ' '); 504 } 505 qemu_fprintf(f, "fpcr %016" PRIx64 "\n", cpu_alpha_load_fpcr(env)); 506 } 507 qemu_fprintf(f, "\n"); 508 } 509 510 /* This should only be called from translate, via gen_excp. 511 We expect that ENV->PC has already been updated. */ 512 G_NORETURN void helper_excp(CPUAlphaState *env, int excp, int error) 513 { 514 CPUState *cs = env_cpu(env); 515 516 cs->exception_index = excp; 517 env->error_code = error; 518 cpu_loop_exit(cs); 519 } 520 521 /* This may be called from any of the helpers to set up EXCEPTION_INDEX. */ 522 G_NORETURN void dynamic_excp(CPUAlphaState *env, uintptr_t retaddr, 523 int excp, int error) 524 { 525 CPUState *cs = env_cpu(env); 526 527 cs->exception_index = excp; 528 env->error_code = error; 529 if (retaddr) { 530 cpu_restore_state(cs, retaddr); 531 /* Floating-point exceptions (our only users) point to the next PC. */ 532 env->pc += 4; 533 } 534 cpu_loop_exit(cs); 535 } 536 537 G_NORETURN void arith_excp(CPUAlphaState *env, uintptr_t retaddr, 538 int exc, uint64_t mask) 539 { 540 env->trap_arg0 = exc; 541 env->trap_arg1 = mask; 542 dynamic_excp(env, retaddr, EXCP_ARITH, 0); 543 } 544