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.1 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 "qemu/log.h" 23 #include "cpu.h" 24 #include "exec/helper-proto.h" 25 #include "qemu/host-utils.h" 26 #include "accel/tcg/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 /* Raises ESR_EC_DIVZERO if exceptions are enabled. */ 73 static void raise_divzero(CPUMBState *env, uint32_t esr, uintptr_t unwind_pc) 74 { 75 env->msr |= MSR_DZ; 76 77 if ((env->msr & MSR_EE) && env_archcpu(env)->cfg.div_zero_exception) { 78 CPUState *cs = env_cpu(env); 79 80 env->esr = esr; 81 cs->exception_index = EXCP_HW_EXCP; 82 cpu_loop_exit_restore(cs, unwind_pc); 83 } 84 } 85 86 uint32_t helper_divs(CPUMBState *env, uint32_t ra, uint32_t rb) 87 { 88 if (!ra) { 89 raise_divzero(env, ESR_EC_DIVZERO, GETPC()); 90 return 0; 91 } 92 93 /* 94 * Check for division overflows. 95 * 96 * Spec: https://docs.amd.com/r/en-US/ug984-vivado-microblaze-ref/idiv 97 * UG984, Chapter 5 MicroBlaze Instruction Set Architecture, idiv. 98 * 99 * If the U bit is clear, the value of rA is -1, and the value of rB is 100 * -2147483648 (divide overflow), the DZO bit in MSR will be set and 101 * the value in rD will be -2147483648, unless an exception is generated. 102 */ 103 if ((int32_t)ra == -1 && (int32_t)rb == INT32_MIN) { 104 raise_divzero(env, ESR_EC_DIVZERO | ESR_ESS_DEC_OF, GETPC()); 105 return INT32_MIN; 106 } 107 return (int32_t)rb / (int32_t)ra; 108 } 109 110 uint32_t helper_divu(CPUMBState *env, uint32_t ra, uint32_t rb) 111 { 112 if (!ra) { 113 raise_divzero(env, ESR_EC_DIVZERO, GETPC()); 114 return 0; 115 } 116 return rb / ra; 117 } 118 119 /* raise FPU exception. */ 120 static void raise_fpu_exception(CPUMBState *env, uintptr_t ra) 121 { 122 CPUState *cs = env_cpu(env); 123 124 env->esr = ESR_EC_FPU; 125 cs->exception_index = EXCP_HW_EXCP; 126 cpu_loop_exit_restore(cs, ra); 127 } 128 129 static void update_fpu_flags(CPUMBState *env, int flags, uintptr_t ra) 130 { 131 int raise = 0; 132 133 if (flags & float_flag_invalid) { 134 env->fsr |= FSR_IO; 135 raise = 1; 136 } 137 if (flags & float_flag_divbyzero) { 138 env->fsr |= FSR_DZ; 139 raise = 1; 140 } 141 if (flags & float_flag_overflow) { 142 env->fsr |= FSR_OF; 143 raise = 1; 144 } 145 if (flags & float_flag_underflow) { 146 env->fsr |= FSR_UF; 147 raise = 1; 148 } 149 if (raise 150 && (env_archcpu(env)->cfg.pvr_regs[2] & PVR2_FPU_EXC_MASK) 151 && (env->msr & MSR_EE)) { 152 raise_fpu_exception(env, ra); 153 } 154 } 155 156 uint32_t helper_fadd(CPUMBState *env, uint32_t a, uint32_t b) 157 { 158 CPU_FloatU fd, fa, fb; 159 int flags; 160 161 set_float_exception_flags(0, &env->fp_status); 162 fa.l = a; 163 fb.l = b; 164 fd.f = float32_add(fa.f, fb.f, &env->fp_status); 165 166 flags = get_float_exception_flags(&env->fp_status); 167 update_fpu_flags(env, flags, GETPC()); 168 return fd.l; 169 } 170 171 uint32_t helper_frsub(CPUMBState *env, uint32_t a, uint32_t b) 172 { 173 CPU_FloatU fd, fa, fb; 174 int flags; 175 176 set_float_exception_flags(0, &env->fp_status); 177 fa.l = a; 178 fb.l = b; 179 fd.f = float32_sub(fb.f, fa.f, &env->fp_status); 180 flags = get_float_exception_flags(&env->fp_status); 181 update_fpu_flags(env, flags, GETPC()); 182 return fd.l; 183 } 184 185 uint32_t helper_fmul(CPUMBState *env, uint32_t a, uint32_t b) 186 { 187 CPU_FloatU fd, fa, fb; 188 int flags; 189 190 set_float_exception_flags(0, &env->fp_status); 191 fa.l = a; 192 fb.l = b; 193 fd.f = float32_mul(fa.f, fb.f, &env->fp_status); 194 flags = get_float_exception_flags(&env->fp_status); 195 update_fpu_flags(env, flags, GETPC()); 196 197 return fd.l; 198 } 199 200 uint32_t helper_fdiv(CPUMBState *env, uint32_t a, uint32_t b) 201 { 202 CPU_FloatU fd, fa, fb; 203 int flags; 204 205 set_float_exception_flags(0, &env->fp_status); 206 fa.l = a; 207 fb.l = b; 208 fd.f = float32_div(fb.f, fa.f, &env->fp_status); 209 flags = get_float_exception_flags(&env->fp_status); 210 update_fpu_flags(env, flags, GETPC()); 211 212 return fd.l; 213 } 214 215 uint32_t helper_fcmp_un(CPUMBState *env, uint32_t a, uint32_t b) 216 { 217 CPU_FloatU fa, fb; 218 uint32_t r = 0; 219 220 fa.l = a; 221 fb.l = b; 222 223 if (float32_is_signaling_nan(fa.f, &env->fp_status) || 224 float32_is_signaling_nan(fb.f, &env->fp_status)) { 225 update_fpu_flags(env, float_flag_invalid, GETPC()); 226 r = 1; 227 } 228 229 if (float32_is_quiet_nan(fa.f, &env->fp_status) || 230 float32_is_quiet_nan(fb.f, &env->fp_status)) { 231 r = 1; 232 } 233 234 return r; 235 } 236 237 uint32_t helper_fcmp_lt(CPUMBState *env, uint32_t a, uint32_t b) 238 { 239 CPU_FloatU fa, fb; 240 int r; 241 int flags; 242 243 set_float_exception_flags(0, &env->fp_status); 244 fa.l = a; 245 fb.l = b; 246 r = float32_lt(fb.f, fa.f, &env->fp_status); 247 flags = get_float_exception_flags(&env->fp_status); 248 update_fpu_flags(env, flags & float_flag_invalid, GETPC()); 249 250 return r; 251 } 252 253 uint32_t helper_fcmp_eq(CPUMBState *env, uint32_t a, uint32_t b) 254 { 255 CPU_FloatU fa, fb; 256 int flags; 257 int r; 258 259 set_float_exception_flags(0, &env->fp_status); 260 fa.l = a; 261 fb.l = b; 262 r = float32_eq_quiet(fa.f, fb.f, &env->fp_status); 263 flags = get_float_exception_flags(&env->fp_status); 264 update_fpu_flags(env, flags & float_flag_invalid, GETPC()); 265 266 return r; 267 } 268 269 uint32_t helper_fcmp_le(CPUMBState *env, uint32_t a, uint32_t b) 270 { 271 CPU_FloatU fa, fb; 272 int flags; 273 int r; 274 275 fa.l = a; 276 fb.l = b; 277 set_float_exception_flags(0, &env->fp_status); 278 r = float32_le(fa.f, fb.f, &env->fp_status); 279 flags = get_float_exception_flags(&env->fp_status); 280 update_fpu_flags(env, flags & float_flag_invalid, GETPC()); 281 282 283 return r; 284 } 285 286 uint32_t helper_fcmp_gt(CPUMBState *env, uint32_t a, uint32_t b) 287 { 288 CPU_FloatU fa, fb; 289 int flags, r; 290 291 fa.l = a; 292 fb.l = b; 293 set_float_exception_flags(0, &env->fp_status); 294 r = float32_lt(fa.f, fb.f, &env->fp_status); 295 flags = get_float_exception_flags(&env->fp_status); 296 update_fpu_flags(env, flags & float_flag_invalid, GETPC()); 297 return r; 298 } 299 300 uint32_t helper_fcmp_ne(CPUMBState *env, uint32_t a, uint32_t b) 301 { 302 CPU_FloatU fa, fb; 303 int flags, r; 304 305 fa.l = a; 306 fb.l = b; 307 set_float_exception_flags(0, &env->fp_status); 308 r = !float32_eq_quiet(fa.f, fb.f, &env->fp_status); 309 flags = get_float_exception_flags(&env->fp_status); 310 update_fpu_flags(env, flags & float_flag_invalid, GETPC()); 311 312 return r; 313 } 314 315 uint32_t helper_fcmp_ge(CPUMBState *env, uint32_t a, uint32_t b) 316 { 317 CPU_FloatU fa, fb; 318 int flags, r; 319 320 fa.l = a; 321 fb.l = b; 322 set_float_exception_flags(0, &env->fp_status); 323 r = !float32_lt(fa.f, fb.f, &env->fp_status); 324 flags = get_float_exception_flags(&env->fp_status); 325 update_fpu_flags(env, flags & float_flag_invalid, GETPC()); 326 327 return r; 328 } 329 330 uint32_t helper_flt(CPUMBState *env, uint32_t a) 331 { 332 CPU_FloatU fd, fa; 333 334 fa.l = a; 335 fd.f = int32_to_float32(fa.l, &env->fp_status); 336 return fd.l; 337 } 338 339 uint32_t helper_fint(CPUMBState *env, uint32_t a) 340 { 341 CPU_FloatU fa; 342 uint32_t r; 343 int flags; 344 345 set_float_exception_flags(0, &env->fp_status); 346 fa.l = a; 347 r = float32_to_int32(fa.f, &env->fp_status); 348 flags = get_float_exception_flags(&env->fp_status); 349 update_fpu_flags(env, flags, GETPC()); 350 351 return r; 352 } 353 354 uint32_t helper_fsqrt(CPUMBState *env, uint32_t a) 355 { 356 CPU_FloatU fd, fa; 357 int flags; 358 359 set_float_exception_flags(0, &env->fp_status); 360 fa.l = a; 361 fd.l = float32_sqrt(fa.f, &env->fp_status); 362 flags = get_float_exception_flags(&env->fp_status); 363 update_fpu_flags(env, flags, GETPC()); 364 365 return fd.l; 366 } 367 368 uint32_t helper_pcmpbf(uint32_t a, uint32_t b) 369 { 370 unsigned int i; 371 uint32_t mask = 0xff000000; 372 373 for (i = 0; i < 4; i++) { 374 if ((a & mask) == (b & mask)) 375 return i + 1; 376 mask >>= 8; 377 } 378 return 0; 379 } 380 381 void helper_stackprot(CPUMBState *env, target_ulong addr) 382 { 383 if (addr < env->slr || addr > env->shr) { 384 CPUState *cs = env_cpu(env); 385 386 qemu_log_mask(CPU_LOG_INT, "Stack protector violation at " 387 TARGET_FMT_lx " %x %x\n", 388 addr, env->slr, env->shr); 389 390 env->ear = addr; 391 env->esr = ESR_EC_STACKPROT; 392 cs->exception_index = EXCP_HW_EXCP; 393 cpu_loop_exit_restore(cs, GETPC()); 394 } 395 } 396 397 #if !defined(CONFIG_USER_ONLY) 398 #include "system/memory.h" 399 400 /* Writes/reads to the MMU's special regs end up here. */ 401 uint32_t helper_mmu_read(CPUMBState *env, uint32_t ext, uint32_t rn) 402 { 403 return mmu_read(env, ext, rn); 404 } 405 406 void helper_mmu_write(CPUMBState *env, uint32_t ext, uint32_t rn, uint32_t v) 407 { 408 mmu_write(env, ext, rn, v); 409 } 410 411 static void mb_transaction_failed_internal(CPUState *cs, hwaddr physaddr, 412 uint64_t addr, unsigned size, 413 MMUAccessType access_type, 414 uintptr_t retaddr) 415 { 416 CPUMBState *env = cpu_env(cs); 417 MicroBlazeCPU *cpu = env_archcpu(env); 418 const char *access_name = "INVALID"; 419 bool take = env->msr & MSR_EE; 420 uint32_t esr = ESR_EC_DATA_BUS; 421 422 switch (access_type) { 423 case MMU_INST_FETCH: 424 access_name = "INST_FETCH"; 425 esr = ESR_EC_INSN_BUS; 426 take &= cpu->cfg.iopb_bus_exception; 427 break; 428 case MMU_DATA_LOAD: 429 access_name = "DATA_LOAD"; 430 take &= cpu->cfg.dopb_bus_exception; 431 break; 432 case MMU_DATA_STORE: 433 access_name = "DATA_STORE"; 434 take &= cpu->cfg.dopb_bus_exception; 435 break; 436 } 437 438 qemu_log_mask(CPU_LOG_INT, "Transaction failed: addr 0x%" PRIx64 439 "physaddr 0x" HWADDR_FMT_plx " size %d access-type %s (%s)\n", 440 addr, physaddr, size, access_name, 441 take ? "TAKEN" : "DROPPED"); 442 443 if (take) { 444 env->esr = esr; 445 env->ear = addr; 446 cs->exception_index = EXCP_HW_EXCP; 447 cpu_loop_exit_restore(cs, retaddr); 448 } 449 } 450 451 void mb_cpu_transaction_failed(CPUState *cs, hwaddr physaddr, vaddr addr, 452 unsigned size, MMUAccessType access_type, 453 int mmu_idx, MemTxAttrs attrs, 454 MemTxResult response, uintptr_t retaddr) 455 { 456 mb_transaction_failed_internal(cs, physaddr, addr, size, 457 access_type, retaddr); 458 } 459 460 #define LD_EA(NAME, TYPE, FUNC) \ 461 uint32_t HELPER(NAME)(CPUMBState *env, uint64_t ea) \ 462 { \ 463 CPUState *cs = env_cpu(env); \ 464 MemTxResult txres; \ 465 TYPE ret = FUNC(cs->as, ea, MEMTXATTRS_UNSPECIFIED, &txres); \ 466 if (unlikely(txres != MEMTX_OK)) { \ 467 mb_transaction_failed_internal(cs, ea, ea, sizeof(TYPE), \ 468 MMU_DATA_LOAD, GETPC()); \ 469 } \ 470 return ret; \ 471 } 472 473 LD_EA(lbuea, uint8_t, address_space_ldub) 474 LD_EA(lhuea_be, uint16_t, address_space_lduw_be) 475 LD_EA(lhuea_le, uint16_t, address_space_lduw_le) 476 LD_EA(lwea_be, uint32_t, address_space_ldl_be) 477 LD_EA(lwea_le, uint32_t, address_space_ldl_le) 478 479 #define ST_EA(NAME, TYPE, FUNC) \ 480 void HELPER(NAME)(CPUMBState *env, uint32_t data, uint64_t ea) \ 481 { \ 482 CPUState *cs = env_cpu(env); \ 483 MemTxResult txres; \ 484 FUNC(cs->as, ea, data, MEMTXATTRS_UNSPECIFIED, &txres); \ 485 if (unlikely(txres != MEMTX_OK)) { \ 486 mb_transaction_failed_internal(cs, ea, ea, sizeof(TYPE), \ 487 MMU_DATA_STORE, GETPC()); \ 488 } \ 489 } 490 491 ST_EA(sbea, uint8_t, address_space_stb) 492 ST_EA(shea_be, uint16_t, address_space_stw_be) 493 ST_EA(shea_le, uint16_t, address_space_stw_le) 494 ST_EA(swea_be, uint32_t, address_space_stl_be) 495 ST_EA(swea_le, uint32_t, address_space_stl_le) 496 497 #endif 498