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/helper-proto.h" 24 #include "qemu/host-utils.h" 25 #include "exec/exec-all.h" 26 #include "exec/cpu_ldst.h" 27 #include "fpu/softfloat.h" 28 29 #define D(x) 30 31 void helper_put(uint32_t id, uint32_t ctrl, uint32_t data) 32 { 33 int test = ctrl & STREAM_TEST; 34 int atomic = ctrl & STREAM_ATOMIC; 35 int control = ctrl & STREAM_CONTROL; 36 int nonblock = ctrl & STREAM_NONBLOCK; 37 int exception = ctrl & STREAM_EXCEPTION; 38 39 qemu_log_mask(LOG_UNIMP, "Unhandled stream put to stream-id=%d data=%x %s%s%s%s%s\n", 40 id, data, 41 test ? "t" : "", 42 nonblock ? "n" : "", 43 exception ? "e" : "", 44 control ? "c" : "", 45 atomic ? "a" : ""); 46 } 47 48 uint32_t helper_get(uint32_t id, uint32_t ctrl) 49 { 50 int test = ctrl & STREAM_TEST; 51 int atomic = ctrl & STREAM_ATOMIC; 52 int control = ctrl & STREAM_CONTROL; 53 int nonblock = ctrl & STREAM_NONBLOCK; 54 int exception = ctrl & STREAM_EXCEPTION; 55 56 qemu_log_mask(LOG_UNIMP, "Unhandled stream get from stream-id=%d %s%s%s%s%s\n", 57 id, 58 test ? "t" : "", 59 nonblock ? "n" : "", 60 exception ? "e" : "", 61 control ? "c" : "", 62 atomic ? "a" : ""); 63 return 0xdead0000 | id; 64 } 65 66 void helper_raise_exception(CPUMBState *env, uint32_t index) 67 { 68 CPUState *cs = env_cpu(env); 69 70 cs->exception_index = index; 71 cpu_loop_exit(cs); 72 } 73 74 static inline uint32_t compute_carry(uint32_t a, uint32_t b, uint32_t cin) 75 { 76 uint32_t cout = 0; 77 78 if ((b == ~0) && cin) 79 cout = 1; 80 else if ((~0 - a) < (b + cin)) 81 cout = 1; 82 return cout; 83 } 84 85 uint32_t helper_cmp(uint32_t a, uint32_t b) 86 { 87 uint32_t t; 88 89 t = b + ~a + 1; 90 if ((b & 0x80000000) ^ (a & 0x80000000)) 91 t = (t & 0x7fffffff) | (b & 0x80000000); 92 return t; 93 } 94 95 uint32_t helper_cmpu(uint32_t a, uint32_t b) 96 { 97 uint32_t t; 98 99 t = b + ~a + 1; 100 if ((b & 0x80000000) ^ (a & 0x80000000)) 101 t = (t & 0x7fffffff) | (a & 0x80000000); 102 return t; 103 } 104 105 uint32_t helper_carry(uint32_t a, uint32_t b, uint32_t cf) 106 { 107 return compute_carry(a, b, cf); 108 } 109 110 static inline int div_prepare(CPUMBState *env, uint32_t a, uint32_t b) 111 { 112 MicroBlazeCPU *cpu = env_archcpu(env); 113 114 if (b == 0) { 115 env->msr |= MSR_DZ; 116 117 if ((env->msr & MSR_EE) && cpu->cfg.div_zero_exception) { 118 env->esr = ESR_EC_DIVZERO; 119 helper_raise_exception(env, EXCP_HW_EXCP); 120 } 121 return 0; 122 } 123 env->msr &= ~MSR_DZ; 124 return 1; 125 } 126 127 uint32_t helper_divs(CPUMBState *env, uint32_t a, uint32_t b) 128 { 129 if (!div_prepare(env, a, b)) { 130 return 0; 131 } 132 return (int32_t)a / (int32_t)b; 133 } 134 135 uint32_t helper_divu(CPUMBState *env, uint32_t a, uint32_t b) 136 { 137 if (!div_prepare(env, a, b)) { 138 return 0; 139 } 140 return a / b; 141 } 142 143 /* raise FPU exception. */ 144 static void raise_fpu_exception(CPUMBState *env) 145 { 146 env->esr = ESR_EC_FPU; 147 helper_raise_exception(env, EXCP_HW_EXCP); 148 } 149 150 static void update_fpu_flags(CPUMBState *env, int flags) 151 { 152 int raise = 0; 153 154 if (flags & float_flag_invalid) { 155 env->fsr |= FSR_IO; 156 raise = 1; 157 } 158 if (flags & float_flag_divbyzero) { 159 env->fsr |= FSR_DZ; 160 raise = 1; 161 } 162 if (flags & float_flag_overflow) { 163 env->fsr |= FSR_OF; 164 raise = 1; 165 } 166 if (flags & float_flag_underflow) { 167 env->fsr |= FSR_UF; 168 raise = 1; 169 } 170 if (raise 171 && (env->pvr.regs[2] & PVR2_FPU_EXC_MASK) 172 && (env->msr & MSR_EE)) { 173 raise_fpu_exception(env); 174 } 175 } 176 177 uint32_t helper_fadd(CPUMBState *env, uint32_t a, uint32_t b) 178 { 179 CPU_FloatU fd, fa, fb; 180 int flags; 181 182 set_float_exception_flags(0, &env->fp_status); 183 fa.l = a; 184 fb.l = b; 185 fd.f = float32_add(fa.f, fb.f, &env->fp_status); 186 187 flags = get_float_exception_flags(&env->fp_status); 188 update_fpu_flags(env, flags); 189 return fd.l; 190 } 191 192 uint32_t helper_frsub(CPUMBState *env, uint32_t a, uint32_t b) 193 { 194 CPU_FloatU fd, fa, fb; 195 int flags; 196 197 set_float_exception_flags(0, &env->fp_status); 198 fa.l = a; 199 fb.l = b; 200 fd.f = float32_sub(fb.f, fa.f, &env->fp_status); 201 flags = get_float_exception_flags(&env->fp_status); 202 update_fpu_flags(env, flags); 203 return fd.l; 204 } 205 206 uint32_t helper_fmul(CPUMBState *env, uint32_t a, uint32_t b) 207 { 208 CPU_FloatU fd, fa, fb; 209 int flags; 210 211 set_float_exception_flags(0, &env->fp_status); 212 fa.l = a; 213 fb.l = b; 214 fd.f = float32_mul(fa.f, fb.f, &env->fp_status); 215 flags = get_float_exception_flags(&env->fp_status); 216 update_fpu_flags(env, flags); 217 218 return fd.l; 219 } 220 221 uint32_t helper_fdiv(CPUMBState *env, uint32_t a, uint32_t b) 222 { 223 CPU_FloatU fd, fa, fb; 224 int flags; 225 226 set_float_exception_flags(0, &env->fp_status); 227 fa.l = a; 228 fb.l = b; 229 fd.f = float32_div(fb.f, fa.f, &env->fp_status); 230 flags = get_float_exception_flags(&env->fp_status); 231 update_fpu_flags(env, flags); 232 233 return fd.l; 234 } 235 236 uint32_t helper_fcmp_un(CPUMBState *env, uint32_t a, uint32_t b) 237 { 238 CPU_FloatU fa, fb; 239 uint32_t r = 0; 240 241 fa.l = a; 242 fb.l = b; 243 244 if (float32_is_signaling_nan(fa.f, &env->fp_status) || 245 float32_is_signaling_nan(fb.f, &env->fp_status)) { 246 update_fpu_flags(env, float_flag_invalid); 247 r = 1; 248 } 249 250 if (float32_is_quiet_nan(fa.f, &env->fp_status) || 251 float32_is_quiet_nan(fb.f, &env->fp_status)) { 252 r = 1; 253 } 254 255 return r; 256 } 257 258 uint32_t helper_fcmp_lt(CPUMBState *env, uint32_t a, uint32_t b) 259 { 260 CPU_FloatU fa, fb; 261 int r; 262 int flags; 263 264 set_float_exception_flags(0, &env->fp_status); 265 fa.l = a; 266 fb.l = b; 267 r = float32_lt(fb.f, fa.f, &env->fp_status); 268 flags = get_float_exception_flags(&env->fp_status); 269 update_fpu_flags(env, flags & float_flag_invalid); 270 271 return r; 272 } 273 274 uint32_t helper_fcmp_eq(CPUMBState *env, uint32_t a, uint32_t b) 275 { 276 CPU_FloatU fa, fb; 277 int flags; 278 int r; 279 280 set_float_exception_flags(0, &env->fp_status); 281 fa.l = a; 282 fb.l = b; 283 r = float32_eq_quiet(fa.f, fb.f, &env->fp_status); 284 flags = get_float_exception_flags(&env->fp_status); 285 update_fpu_flags(env, flags & float_flag_invalid); 286 287 return r; 288 } 289 290 uint32_t helper_fcmp_le(CPUMBState *env, uint32_t a, uint32_t b) 291 { 292 CPU_FloatU fa, fb; 293 int flags; 294 int r; 295 296 fa.l = a; 297 fb.l = b; 298 set_float_exception_flags(0, &env->fp_status); 299 r = float32_le(fa.f, fb.f, &env->fp_status); 300 flags = get_float_exception_flags(&env->fp_status); 301 update_fpu_flags(env, flags & float_flag_invalid); 302 303 304 return r; 305 } 306 307 uint32_t helper_fcmp_gt(CPUMBState *env, uint32_t a, uint32_t b) 308 { 309 CPU_FloatU fa, fb; 310 int flags, r; 311 312 fa.l = a; 313 fb.l = b; 314 set_float_exception_flags(0, &env->fp_status); 315 r = float32_lt(fa.f, fb.f, &env->fp_status); 316 flags = get_float_exception_flags(&env->fp_status); 317 update_fpu_flags(env, flags & float_flag_invalid); 318 return r; 319 } 320 321 uint32_t helper_fcmp_ne(CPUMBState *env, uint32_t a, uint32_t b) 322 { 323 CPU_FloatU fa, fb; 324 int flags, r; 325 326 fa.l = a; 327 fb.l = b; 328 set_float_exception_flags(0, &env->fp_status); 329 r = !float32_eq_quiet(fa.f, fb.f, &env->fp_status); 330 flags = get_float_exception_flags(&env->fp_status); 331 update_fpu_flags(env, flags & float_flag_invalid); 332 333 return r; 334 } 335 336 uint32_t helper_fcmp_ge(CPUMBState *env, uint32_t a, uint32_t b) 337 { 338 CPU_FloatU fa, fb; 339 int flags, r; 340 341 fa.l = a; 342 fb.l = b; 343 set_float_exception_flags(0, &env->fp_status); 344 r = !float32_lt(fa.f, fb.f, &env->fp_status); 345 flags = get_float_exception_flags(&env->fp_status); 346 update_fpu_flags(env, flags & float_flag_invalid); 347 348 return r; 349 } 350 351 uint32_t helper_flt(CPUMBState *env, uint32_t a) 352 { 353 CPU_FloatU fd, fa; 354 355 fa.l = a; 356 fd.f = int32_to_float32(fa.l, &env->fp_status); 357 return fd.l; 358 } 359 360 uint32_t helper_fint(CPUMBState *env, uint32_t a) 361 { 362 CPU_FloatU fa; 363 uint32_t r; 364 int flags; 365 366 set_float_exception_flags(0, &env->fp_status); 367 fa.l = a; 368 r = float32_to_int32(fa.f, &env->fp_status); 369 flags = get_float_exception_flags(&env->fp_status); 370 update_fpu_flags(env, flags); 371 372 return r; 373 } 374 375 uint32_t helper_fsqrt(CPUMBState *env, uint32_t a) 376 { 377 CPU_FloatU fd, fa; 378 int flags; 379 380 set_float_exception_flags(0, &env->fp_status); 381 fa.l = a; 382 fd.l = float32_sqrt(fa.f, &env->fp_status); 383 flags = get_float_exception_flags(&env->fp_status); 384 update_fpu_flags(env, flags); 385 386 return fd.l; 387 } 388 389 uint32_t helper_pcmpbf(uint32_t a, uint32_t b) 390 { 391 unsigned int i; 392 uint32_t mask = 0xff000000; 393 394 for (i = 0; i < 4; i++) { 395 if ((a & mask) == (b & mask)) 396 return i + 1; 397 mask >>= 8; 398 } 399 return 0; 400 } 401 402 void helper_memalign(CPUMBState *env, target_ulong addr, 403 uint32_t dr, uint32_t wr, 404 uint32_t mask) 405 { 406 if (addr & mask) { 407 qemu_log_mask(CPU_LOG_INT, 408 "unaligned access addr=" TARGET_FMT_lx 409 " mask=%x, wr=%d dr=r%d\n", 410 addr, mask, wr, dr); 411 env->ear = addr; 412 env->esr = ESR_EC_UNALIGNED_DATA | (wr << 10) | (dr & 31) << 5; 413 if (mask == 3) { 414 env->esr |= 1 << 11; 415 } 416 if (!(env->msr & MSR_EE)) { 417 return; 418 } 419 helper_raise_exception(env, EXCP_HW_EXCP); 420 } 421 } 422 423 void helper_stackprot(CPUMBState *env, target_ulong addr) 424 { 425 if (addr < env->slr || addr > env->shr) { 426 qemu_log_mask(CPU_LOG_INT, "Stack protector violation at " 427 TARGET_FMT_lx " %x %x\n", 428 addr, env->slr, env->shr); 429 env->ear = addr; 430 env->esr = ESR_EC_STACKPROT; 431 helper_raise_exception(env, EXCP_HW_EXCP); 432 } 433 } 434 435 #if !defined(CONFIG_USER_ONLY) 436 /* Writes/reads to the MMU's special regs end up here. */ 437 uint32_t helper_mmu_read(CPUMBState *env, uint32_t ext, uint32_t rn) 438 { 439 return mmu_read(env, ext, rn); 440 } 441 442 void helper_mmu_write(CPUMBState *env, uint32_t ext, uint32_t rn, uint32_t v) 443 { 444 mmu_write(env, ext, rn, v); 445 } 446 447 void mb_cpu_transaction_failed(CPUState *cs, hwaddr physaddr, vaddr addr, 448 unsigned size, MMUAccessType access_type, 449 int mmu_idx, MemTxAttrs attrs, 450 MemTxResult response, uintptr_t retaddr) 451 { 452 MicroBlazeCPU *cpu; 453 CPUMBState *env; 454 qemu_log_mask(CPU_LOG_INT, "Transaction failed: vaddr 0x%" VADDR_PRIx 455 " physaddr 0x" TARGET_FMT_plx " size %d access type %s\n", 456 addr, physaddr, size, 457 access_type == MMU_INST_FETCH ? "INST_FETCH" : 458 (access_type == MMU_DATA_LOAD ? "DATA_LOAD" : "DATA_STORE")); 459 cpu = MICROBLAZE_CPU(cs); 460 env = &cpu->env; 461 462 cpu_restore_state(cs, retaddr, true); 463 if (!(env->msr & MSR_EE)) { 464 return; 465 } 466 467 env->ear = addr; 468 if (access_type == MMU_INST_FETCH) { 469 if ((env->pvr.regs[2] & PVR2_IOPB_BUS_EXC_MASK)) { 470 env->esr = ESR_EC_INSN_BUS; 471 helper_raise_exception(env, EXCP_HW_EXCP); 472 } 473 } else { 474 if ((env->pvr.regs[2] & PVR2_DOPB_BUS_EXC_MASK)) { 475 env->esr = ESR_EC_DATA_BUS; 476 helper_raise_exception(env, EXCP_HW_EXCP); 477 } 478 } 479 } 480 #endif 481