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