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