1 /* 2 * MIPS emulation helpers for qemu. 3 * 4 * Copyright (c) 2004-2005 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 21 #include "qemu/osdep.h" 22 #include "cpu.h" 23 #include "internal.h" 24 #include "exec/helper-proto.h" 25 #include "exec/exec-all.h" 26 #include "exec/memop.h" 27 #include "fpu_helper.h" 28 29 /*****************************************************************************/ 30 /* Exceptions processing helpers */ 31 32 void helper_raise_exception_err(CPUMIPSState *env, uint32_t exception, 33 int error_code) 34 { 35 do_raise_exception_err(env, exception, error_code, 0); 36 } 37 38 void helper_raise_exception(CPUMIPSState *env, uint32_t exception) 39 { 40 do_raise_exception(env, exception, GETPC()); 41 } 42 43 void helper_raise_exception_debug(CPUMIPSState *env) 44 { 45 do_raise_exception(env, EXCP_DEBUG, 0); 46 } 47 48 static void raise_exception(CPUMIPSState *env, uint32_t exception) 49 { 50 do_raise_exception(env, exception, 0); 51 } 52 53 /* 64 bits arithmetic for 32 bits hosts */ 54 static inline uint64_t get_HILO(CPUMIPSState *env) 55 { 56 return ((uint64_t)(env->active_tc.HI[0]) << 32) | 57 (uint32_t)env->active_tc.LO[0]; 58 } 59 60 static inline target_ulong set_HIT0_LO(CPUMIPSState *env, uint64_t HILO) 61 { 62 env->active_tc.LO[0] = (int32_t)(HILO & 0xFFFFFFFF); 63 return env->active_tc.HI[0] = (int32_t)(HILO >> 32); 64 } 65 66 static inline target_ulong set_HI_LOT0(CPUMIPSState *env, uint64_t HILO) 67 { 68 target_ulong tmp = env->active_tc.LO[0] = (int32_t)(HILO & 0xFFFFFFFF); 69 env->active_tc.HI[0] = (int32_t)(HILO >> 32); 70 return tmp; 71 } 72 73 /* Multiplication variants of the vr54xx. */ 74 target_ulong helper_muls(CPUMIPSState *env, target_ulong arg1, 75 target_ulong arg2) 76 { 77 return set_HI_LOT0(env, 0 - ((int64_t)(int32_t)arg1 * 78 (int64_t)(int32_t)arg2)); 79 } 80 81 target_ulong helper_mulsu(CPUMIPSState *env, target_ulong arg1, 82 target_ulong arg2) 83 { 84 return set_HI_LOT0(env, 0 - (uint64_t)(uint32_t)arg1 * 85 (uint64_t)(uint32_t)arg2); 86 } 87 88 target_ulong helper_macc(CPUMIPSState *env, target_ulong arg1, 89 target_ulong arg2) 90 { 91 return set_HI_LOT0(env, (int64_t)get_HILO(env) + (int64_t)(int32_t)arg1 * 92 (int64_t)(int32_t)arg2); 93 } 94 95 target_ulong helper_macchi(CPUMIPSState *env, target_ulong arg1, 96 target_ulong arg2) 97 { 98 return set_HIT0_LO(env, (int64_t)get_HILO(env) + (int64_t)(int32_t)arg1 * 99 (int64_t)(int32_t)arg2); 100 } 101 102 target_ulong helper_maccu(CPUMIPSState *env, target_ulong arg1, 103 target_ulong arg2) 104 { 105 return set_HI_LOT0(env, (uint64_t)get_HILO(env) + 106 (uint64_t)(uint32_t)arg1 * (uint64_t)(uint32_t)arg2); 107 } 108 109 target_ulong helper_macchiu(CPUMIPSState *env, target_ulong arg1, 110 target_ulong arg2) 111 { 112 return set_HIT0_LO(env, (uint64_t)get_HILO(env) + 113 (uint64_t)(uint32_t)arg1 * (uint64_t)(uint32_t)arg2); 114 } 115 116 target_ulong helper_msac(CPUMIPSState *env, target_ulong arg1, 117 target_ulong arg2) 118 { 119 return set_HI_LOT0(env, (int64_t)get_HILO(env) - (int64_t)(int32_t)arg1 * 120 (int64_t)(int32_t)arg2); 121 } 122 123 target_ulong helper_msachi(CPUMIPSState *env, target_ulong arg1, 124 target_ulong arg2) 125 { 126 return set_HIT0_LO(env, (int64_t)get_HILO(env) - (int64_t)(int32_t)arg1 * 127 (int64_t)(int32_t)arg2); 128 } 129 130 target_ulong helper_msacu(CPUMIPSState *env, target_ulong arg1, 131 target_ulong arg2) 132 { 133 return set_HI_LOT0(env, (uint64_t)get_HILO(env) - 134 (uint64_t)(uint32_t)arg1 * (uint64_t)(uint32_t)arg2); 135 } 136 137 target_ulong helper_msachiu(CPUMIPSState *env, target_ulong arg1, 138 target_ulong arg2) 139 { 140 return set_HIT0_LO(env, (uint64_t)get_HILO(env) - 141 (uint64_t)(uint32_t)arg1 * (uint64_t)(uint32_t)arg2); 142 } 143 144 target_ulong helper_mulhi(CPUMIPSState *env, target_ulong arg1, 145 target_ulong arg2) 146 { 147 return set_HIT0_LO(env, (int64_t)(int32_t)arg1 * (int64_t)(int32_t)arg2); 148 } 149 150 target_ulong helper_mulhiu(CPUMIPSState *env, target_ulong arg1, 151 target_ulong arg2) 152 { 153 return set_HIT0_LO(env, (uint64_t)(uint32_t)arg1 * 154 (uint64_t)(uint32_t)arg2); 155 } 156 157 target_ulong helper_mulshi(CPUMIPSState *env, target_ulong arg1, 158 target_ulong arg2) 159 { 160 return set_HIT0_LO(env, 0 - (int64_t)(int32_t)arg1 * 161 (int64_t)(int32_t)arg2); 162 } 163 164 target_ulong helper_mulshiu(CPUMIPSState *env, target_ulong arg1, 165 target_ulong arg2) 166 { 167 return set_HIT0_LO(env, 0 - (uint64_t)(uint32_t)arg1 * 168 (uint64_t)(uint32_t)arg2); 169 } 170 171 static inline target_ulong bitswap(target_ulong v) 172 { 173 v = ((v >> 1) & (target_ulong)0x5555555555555555ULL) | 174 ((v & (target_ulong)0x5555555555555555ULL) << 1); 175 v = ((v >> 2) & (target_ulong)0x3333333333333333ULL) | 176 ((v & (target_ulong)0x3333333333333333ULL) << 2); 177 v = ((v >> 4) & (target_ulong)0x0F0F0F0F0F0F0F0FULL) | 178 ((v & (target_ulong)0x0F0F0F0F0F0F0F0FULL) << 4); 179 return v; 180 } 181 182 #ifdef TARGET_MIPS64 183 target_ulong helper_dbitswap(target_ulong rt) 184 { 185 return bitswap(rt); 186 } 187 #endif 188 189 target_ulong helper_bitswap(target_ulong rt) 190 { 191 return (int32_t)bitswap(rt); 192 } 193 194 target_ulong helper_rotx(target_ulong rs, uint32_t shift, uint32_t shiftx, 195 uint32_t stripe) 196 { 197 int i; 198 uint64_t tmp0 = ((uint64_t)rs) << 32 | ((uint64_t)rs & 0xffffffff); 199 uint64_t tmp1 = tmp0; 200 for (i = 0; i <= 46; i++) { 201 int s; 202 if (i & 0x8) { 203 s = shift; 204 } else { 205 s = shiftx; 206 } 207 208 if (stripe != 0 && !(i & 0x4)) { 209 s = ~s; 210 } 211 if (s & 0x10) { 212 if (tmp0 & (1LL << (i + 16))) { 213 tmp1 |= 1LL << i; 214 } else { 215 tmp1 &= ~(1LL << i); 216 } 217 } 218 } 219 220 uint64_t tmp2 = tmp1; 221 for (i = 0; i <= 38; i++) { 222 int s; 223 if (i & 0x4) { 224 s = shift; 225 } else { 226 s = shiftx; 227 } 228 229 if (s & 0x8) { 230 if (tmp1 & (1LL << (i + 8))) { 231 tmp2 |= 1LL << i; 232 } else { 233 tmp2 &= ~(1LL << i); 234 } 235 } 236 } 237 238 uint64_t tmp3 = tmp2; 239 for (i = 0; i <= 34; i++) { 240 int s; 241 if (i & 0x2) { 242 s = shift; 243 } else { 244 s = shiftx; 245 } 246 if (s & 0x4) { 247 if (tmp2 & (1LL << (i + 4))) { 248 tmp3 |= 1LL << i; 249 } else { 250 tmp3 &= ~(1LL << i); 251 } 252 } 253 } 254 255 uint64_t tmp4 = tmp3; 256 for (i = 0; i <= 32; i++) { 257 int s; 258 if (i & 0x1) { 259 s = shift; 260 } else { 261 s = shiftx; 262 } 263 if (s & 0x2) { 264 if (tmp3 & (1LL << (i + 2))) { 265 tmp4 |= 1LL << i; 266 } else { 267 tmp4 &= ~(1LL << i); 268 } 269 } 270 } 271 272 uint64_t tmp5 = tmp4; 273 for (i = 0; i <= 31; i++) { 274 int s; 275 s = shift; 276 if (s & 0x1) { 277 if (tmp4 & (1LL << (i + 1))) { 278 tmp5 |= 1LL << i; 279 } else { 280 tmp5 &= ~(1LL << i); 281 } 282 } 283 } 284 285 return (int64_t)(int32_t)(uint32_t)tmp5; 286 } 287 288 void helper_fork(target_ulong arg1, target_ulong arg2) 289 { 290 /* 291 * arg1 = rt, arg2 = rs 292 * TODO: store to TC register 293 */ 294 } 295 296 target_ulong helper_yield(CPUMIPSState *env, target_ulong arg) 297 { 298 target_long arg1 = arg; 299 300 if (arg1 < 0) { 301 /* No scheduling policy implemented. */ 302 if (arg1 != -2) { 303 if (env->CP0_VPEControl & (1 << CP0VPECo_YSI) && 304 env->active_tc.CP0_TCStatus & (1 << CP0TCSt_DT)) { 305 env->CP0_VPEControl &= ~(0x7 << CP0VPECo_EXCPT); 306 env->CP0_VPEControl |= 4 << CP0VPECo_EXCPT; 307 do_raise_exception(env, EXCP_THREAD, GETPC()); 308 } 309 } 310 } else if (arg1 == 0) { 311 if (0) { 312 /* TODO: TC underflow */ 313 env->CP0_VPEControl &= ~(0x7 << CP0VPECo_EXCPT); 314 do_raise_exception(env, EXCP_THREAD, GETPC()); 315 } else { 316 /* TODO: Deallocate TC */ 317 } 318 } else if (arg1 > 0) { 319 /* Yield qualifier inputs not implemented. */ 320 env->CP0_VPEControl &= ~(0x7 << CP0VPECo_EXCPT); 321 env->CP0_VPEControl |= 2 << CP0VPECo_EXCPT; 322 do_raise_exception(env, EXCP_THREAD, GETPC()); 323 } 324 return env->CP0_YQMask; 325 } 326 327 static inline void check_hwrena(CPUMIPSState *env, int reg, uintptr_t pc) 328 { 329 if ((env->hflags & MIPS_HFLAG_CP0) || (env->CP0_HWREna & (1 << reg))) { 330 return; 331 } 332 do_raise_exception(env, EXCP_RI, pc); 333 } 334 335 target_ulong helper_rdhwr_cpunum(CPUMIPSState *env) 336 { 337 check_hwrena(env, 0, GETPC()); 338 return env->CP0_EBase & 0x3ff; 339 } 340 341 target_ulong helper_rdhwr_synci_step(CPUMIPSState *env) 342 { 343 check_hwrena(env, 1, GETPC()); 344 return env->SYNCI_Step; 345 } 346 347 target_ulong helper_rdhwr_cc(CPUMIPSState *env) 348 { 349 check_hwrena(env, 2, GETPC()); 350 #ifdef CONFIG_USER_ONLY 351 return env->CP0_Count; 352 #else 353 return (int32_t)cpu_mips_get_count(env); 354 #endif 355 } 356 357 target_ulong helper_rdhwr_ccres(CPUMIPSState *env) 358 { 359 check_hwrena(env, 3, GETPC()); 360 return env->CCRes; 361 } 362 363 target_ulong helper_rdhwr_performance(CPUMIPSState *env) 364 { 365 check_hwrena(env, 4, GETPC()); 366 return env->CP0_Performance0; 367 } 368 369 target_ulong helper_rdhwr_xnp(CPUMIPSState *env) 370 { 371 check_hwrena(env, 5, GETPC()); 372 return (env->CP0_Config5 >> CP0C5_XNP) & 1; 373 } 374 375 void helper_pmon(CPUMIPSState *env, int function) 376 { 377 function /= 2; 378 switch (function) { 379 case 2: /* TODO: char inbyte(int waitflag); */ 380 if (env->active_tc.gpr[4] == 0) { 381 env->active_tc.gpr[2] = -1; 382 } 383 /* Fall through */ 384 case 11: /* TODO: char inbyte (void); */ 385 env->active_tc.gpr[2] = -1; 386 break; 387 case 3: 388 case 12: 389 printf("%c", (char)(env->active_tc.gpr[4] & 0xFF)); 390 break; 391 case 17: 392 break; 393 case 158: 394 { 395 unsigned char *fmt = (void *)(uintptr_t)env->active_tc.gpr[4]; 396 printf("%s", fmt); 397 } 398 break; 399 } 400 } 401 402 void helper_wait(CPUMIPSState *env) 403 { 404 CPUState *cs = env_cpu(env); 405 406 cs->halted = 1; 407 cpu_reset_interrupt(cs, CPU_INTERRUPT_WAKE); 408 /* 409 * Last instruction in the block, PC was updated before 410 * - no need to recover PC and icount. 411 */ 412 raise_exception(env, EXCP_HLT); 413 } 414 415 #if !defined(CONFIG_USER_ONLY) 416 417 void mips_cpu_do_unaligned_access(CPUState *cs, vaddr addr, 418 MMUAccessType access_type, 419 int mmu_idx, uintptr_t retaddr) 420 { 421 MIPSCPU *cpu = MIPS_CPU(cs); 422 CPUMIPSState *env = &cpu->env; 423 int error_code = 0; 424 int excp; 425 426 if (!(env->hflags & MIPS_HFLAG_DM)) { 427 env->CP0_BadVAddr = addr; 428 } 429 430 if (access_type == MMU_DATA_STORE) { 431 excp = EXCP_AdES; 432 } else { 433 excp = EXCP_AdEL; 434 if (access_type == MMU_INST_FETCH) { 435 error_code |= EXCP_INST_NOTAVAIL; 436 } 437 } 438 439 do_raise_exception_err(env, excp, error_code, retaddr); 440 } 441 442 void mips_cpu_do_transaction_failed(CPUState *cs, hwaddr physaddr, 443 vaddr addr, unsigned size, 444 MMUAccessType access_type, 445 int mmu_idx, MemTxAttrs attrs, 446 MemTxResult response, uintptr_t retaddr) 447 { 448 MIPSCPU *cpu = MIPS_CPU(cs); 449 CPUMIPSState *env = &cpu->env; 450 451 if (access_type == MMU_INST_FETCH) { 452 do_raise_exception(env, EXCP_IBE, retaddr); 453 } else { 454 do_raise_exception(env, EXCP_DBE, retaddr); 455 } 456 } 457 #endif /* !CONFIG_USER_ONLY */ 458