]>
Commit | Line | Data |
---|---|---|
4acb54ba EI |
1 | /* |
2 | * Microblaze helper routines. | |
3 | * | |
4 | * Copyright (c) 2009 Edgar E. Iglesias <edgar.iglesias@gmail.com>. | |
dadc1064 | 5 | * Copyright (c) 2009-2012 PetaLogix Qld Pty Ltd. |
4acb54ba EI |
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 | |
8167ee88 | 18 | * License along with this library; if not, see <http://www.gnu.org/licenses/>. |
4acb54ba EI |
19 | */ |
20 | ||
8fd9dece | 21 | #include "qemu/osdep.h" |
3e457172 | 22 | #include "cpu.h" |
2ef6175a | 23 | #include "exec/helper-proto.h" |
1de7afc9 | 24 | #include "qemu/host-utils.h" |
63c91552 | 25 | #include "exec/exec-all.h" |
f08b6170 | 26 | #include "exec/cpu_ldst.h" |
24f91e81 | 27 | #include "fpu/softfloat.h" |
4acb54ba | 28 | |
6d76d23e EI |
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 | ||
1d512a65 | 37 | qemu_log_mask(LOG_UNIMP, "Unhandled stream put to stream-id=%d data=%x %s%s%s%s%s\n", |
6d76d23e EI |
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 | ||
1d512a65 | 54 | qemu_log_mask(LOG_UNIMP, "Unhandled stream get from stream-id=%d %s%s%s%s%s\n", |
6d76d23e EI |
55 | id, |
56 | test ? "t" : "", | |
57 | nonblock ? "n" : "", | |
58 | exception ? "e" : "", | |
59 | control ? "c" : "", | |
60 | atomic ? "a" : ""); | |
61 | return 0xdead0000 | id; | |
62 | } | |
63 | ||
64254eba | 64 | void helper_raise_exception(CPUMBState *env, uint32_t index) |
4acb54ba | 65 | { |
f5c7e93a | 66 | CPUState *cs = env_cpu(env); |
27103424 AF |
67 | |
68 | cs->exception_index = index; | |
5638d180 | 69 | cpu_loop_exit(cs); |
4acb54ba EI |
70 | } |
71 | ||
64254eba | 72 | static inline int div_prepare(CPUMBState *env, uint32_t a, uint32_t b) |
4acb54ba | 73 | { |
622cc730 EI |
74 | MicroBlazeCPU *cpu = env_archcpu(env); |
75 | ||
4acb54ba | 76 | if (b == 0) { |
2e5282ca | 77 | env->msr |= MSR_DZ; |
821ebb33 | 78 | |
2e5282ca | 79 | if ((env->msr & MSR_EE) && cpu->cfg.div_zero_exception) { |
78e9caf2 | 80 | env->esr = ESR_EC_DIVZERO; |
64254eba | 81 | helper_raise_exception(env, EXCP_HW_EXCP); |
821ebb33 | 82 | } |
4acb54ba EI |
83 | return 0; |
84 | } | |
2e5282ca | 85 | env->msr &= ~MSR_DZ; |
4acb54ba EI |
86 | return 1; |
87 | } | |
88 | ||
64254eba | 89 | uint32_t helper_divs(CPUMBState *env, uint32_t a, uint32_t b) |
4acb54ba | 90 | { |
64254eba | 91 | if (!div_prepare(env, a, b)) { |
4acb54ba | 92 | return 0; |
64254eba | 93 | } |
4acb54ba EI |
94 | return (int32_t)a / (int32_t)b; |
95 | } | |
96 | ||
64254eba | 97 | uint32_t helper_divu(CPUMBState *env, uint32_t a, uint32_t b) |
4acb54ba | 98 | { |
64254eba | 99 | if (!div_prepare(env, a, b)) { |
4acb54ba | 100 | return 0; |
64254eba | 101 | } |
4acb54ba EI |
102 | return a / b; |
103 | } | |
104 | ||
97694c57 | 105 | /* raise FPU exception. */ |
64254eba | 106 | static void raise_fpu_exception(CPUMBState *env) |
97694c57 | 107 | { |
78e9caf2 | 108 | env->esr = ESR_EC_FPU; |
64254eba | 109 | helper_raise_exception(env, EXCP_HW_EXCP); |
97694c57 EI |
110 | } |
111 | ||
64254eba | 112 | static void update_fpu_flags(CPUMBState *env, int flags) |
97694c57 EI |
113 | { |
114 | int raise = 0; | |
115 | ||
116 | if (flags & float_flag_invalid) { | |
5a8e0136 | 117 | env->fsr |= FSR_IO; |
97694c57 EI |
118 | raise = 1; |
119 | } | |
120 | if (flags & float_flag_divbyzero) { | |
5a8e0136 | 121 | env->fsr |= FSR_DZ; |
97694c57 EI |
122 | raise = 1; |
123 | } | |
124 | if (flags & float_flag_overflow) { | |
5a8e0136 | 125 | env->fsr |= FSR_OF; |
97694c57 EI |
126 | raise = 1; |
127 | } | |
128 | if (flags & float_flag_underflow) { | |
5a8e0136 | 129 | env->fsr |= FSR_UF; |
97694c57 EI |
130 | raise = 1; |
131 | } | |
132 | if (raise | |
133 | && (env->pvr.regs[2] & PVR2_FPU_EXC_MASK) | |
2e5282ca | 134 | && (env->msr & MSR_EE)) { |
64254eba | 135 | raise_fpu_exception(env); |
97694c57 EI |
136 | } |
137 | } | |
138 | ||
64254eba | 139 | uint32_t helper_fadd(CPUMBState *env, uint32_t a, uint32_t b) |
97694c57 EI |
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); | |
64254eba | 150 | update_fpu_flags(env, flags); |
97694c57 EI |
151 | return fd.l; |
152 | } | |
153 | ||
64254eba | 154 | uint32_t helper_frsub(CPUMBState *env, uint32_t a, uint32_t b) |
97694c57 EI |
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); | |
64254eba | 164 | update_fpu_flags(env, flags); |
97694c57 EI |
165 | return fd.l; |
166 | } | |
167 | ||
64254eba | 168 | uint32_t helper_fmul(CPUMBState *env, uint32_t a, uint32_t b) |
97694c57 EI |
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); | |
64254eba | 178 | update_fpu_flags(env, flags); |
97694c57 EI |
179 | |
180 | return fd.l; | |
181 | } | |
182 | ||
64254eba | 183 | uint32_t helper_fdiv(CPUMBState *env, uint32_t a, uint32_t b) |
97694c57 EI |
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); | |
64254eba | 193 | update_fpu_flags(env, flags); |
97694c57 EI |
194 | |
195 | return fd.l; | |
196 | } | |
197 | ||
64254eba | 198 | uint32_t helper_fcmp_un(CPUMBState *env, uint32_t a, uint32_t b) |
97694c57 | 199 | { |
ef9d48da EI |
200 | CPU_FloatU fa, fb; |
201 | uint32_t r = 0; | |
202 | ||
203 | fa.l = a; | |
204 | fb.l = b; | |
205 | ||
af39bc8c AM |
206 | if (float32_is_signaling_nan(fa.f, &env->fp_status) || |
207 | float32_is_signaling_nan(fb.f, &env->fp_status)) { | |
64254eba | 208 | update_fpu_flags(env, float_flag_invalid); |
ef9d48da EI |
209 | r = 1; |
210 | } | |
211 | ||
af39bc8c AM |
212 | if (float32_is_quiet_nan(fa.f, &env->fp_status) || |
213 | float32_is_quiet_nan(fb.f, &env->fp_status)) { | |
ef9d48da EI |
214 | r = 1; |
215 | } | |
216 | ||
217 | return r; | |
97694c57 EI |
218 | } |
219 | ||
64254eba | 220 | uint32_t helper_fcmp_lt(CPUMBState *env, uint32_t a, uint32_t b) |
97694c57 EI |
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); | |
64254eba | 231 | update_fpu_flags(env, flags & float_flag_invalid); |
97694c57 EI |
232 | |
233 | return r; | |
234 | } | |
235 | ||
64254eba | 236 | uint32_t helper_fcmp_eq(CPUMBState *env, uint32_t a, uint32_t b) |
97694c57 EI |
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; | |
211315fb | 245 | r = float32_eq_quiet(fa.f, fb.f, &env->fp_status); |
97694c57 | 246 | flags = get_float_exception_flags(&env->fp_status); |
64254eba | 247 | update_fpu_flags(env, flags & float_flag_invalid); |
97694c57 EI |
248 | |
249 | return r; | |
250 | } | |
251 | ||
64254eba | 252 | uint32_t helper_fcmp_le(CPUMBState *env, uint32_t a, uint32_t b) |
97694c57 EI |
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); | |
64254eba | 263 | update_fpu_flags(env, flags & float_flag_invalid); |
97694c57 EI |
264 | |
265 | ||
266 | return r; | |
267 | } | |
268 | ||
64254eba | 269 | uint32_t helper_fcmp_gt(CPUMBState *env, uint32_t a, uint32_t b) |
97694c57 EI |
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); | |
64254eba | 279 | update_fpu_flags(env, flags & float_flag_invalid); |
97694c57 EI |
280 | return r; |
281 | } | |
282 | ||
64254eba | 283 | uint32_t helper_fcmp_ne(CPUMBState *env, uint32_t a, uint32_t b) |
97694c57 EI |
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); | |
211315fb | 291 | r = !float32_eq_quiet(fa.f, fb.f, &env->fp_status); |
97694c57 | 292 | flags = get_float_exception_flags(&env->fp_status); |
64254eba | 293 | update_fpu_flags(env, flags & float_flag_invalid); |
97694c57 EI |
294 | |
295 | return r; | |
296 | } | |
297 | ||
64254eba | 298 | uint32_t helper_fcmp_ge(CPUMBState *env, uint32_t a, uint32_t b) |
97694c57 EI |
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); | |
64254eba | 308 | update_fpu_flags(env, flags & float_flag_invalid); |
97694c57 EI |
309 | |
310 | return r; | |
311 | } | |
312 | ||
64254eba | 313 | uint32_t helper_flt(CPUMBState *env, uint32_t a) |
97694c57 EI |
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 | ||
64254eba | 322 | uint32_t helper_fint(CPUMBState *env, uint32_t a) |
97694c57 EI |
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); | |
64254eba | 332 | update_fpu_flags(env, flags); |
97694c57 EI |
333 | |
334 | return r; | |
335 | } | |
336 | ||
64254eba | 337 | uint32_t helper_fsqrt(CPUMBState *env, uint32_t a) |
97694c57 EI |
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); | |
64254eba | 346 | update_fpu_flags(env, flags); |
97694c57 EI |
347 | |
348 | return fd.l; | |
349 | } | |
350 | ||
4acb54ba EI |
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 | ||
403322ea EI |
364 | void helper_memalign(CPUMBState *env, target_ulong addr, |
365 | uint32_t dr, uint32_t wr, | |
64254eba | 366 | uint32_t mask) |
968a40f6 | 367 | { |
968a40f6 | 368 | if (addr & mask) { |
97f90cbf | 369 | qemu_log_mask(CPU_LOG_INT, |
403322ea EI |
370 | "unaligned access addr=" TARGET_FMT_lx |
371 | " mask=%x, wr=%d dr=r%d\n", | |
97f90cbf | 372 | addr, mask, wr, dr); |
b2e80a3c | 373 | env->ear = addr; |
78e9caf2 | 374 | env->esr = ESR_EC_UNALIGNED_DATA | (wr << 10) | (dr & 31) << 5; |
3aa80988 | 375 | if (mask == 3) { |
78e9caf2 | 376 | env->esr |= 1 << 11; |
968a40f6 | 377 | } |
2e5282ca | 378 | if (!(env->msr & MSR_EE)) { |
97f90cbf EI |
379 | return; |
380 | } | |
64254eba | 381 | helper_raise_exception(env, EXCP_HW_EXCP); |
968a40f6 EI |
382 | } |
383 | } | |
384 | ||
403322ea | 385 | void helper_stackprot(CPUMBState *env, target_ulong addr) |
5818dee5 EI |
386 | { |
387 | if (addr < env->slr || addr > env->shr) { | |
403322ea EI |
388 | qemu_log_mask(CPU_LOG_INT, "Stack protector violation at " |
389 | TARGET_FMT_lx " %x %x\n", | |
1d512a65 | 390 | addr, env->slr, env->shr); |
b2e80a3c | 391 | env->ear = addr; |
78e9caf2 | 392 | env->esr = ESR_EC_STACKPROT; |
53432dc9 | 393 | helper_raise_exception(env, EXCP_HW_EXCP); |
5818dee5 EI |
394 | } |
395 | } | |
396 | ||
4acb54ba EI |
397 | #if !defined(CONFIG_USER_ONLY) |
398 | /* Writes/reads to the MMU's special regs end up here. */ | |
f0f7e7f7 | 399 | uint32_t helper_mmu_read(CPUMBState *env, uint32_t ext, uint32_t rn) |
4acb54ba | 400 | { |
f0f7e7f7 | 401 | return mmu_read(env, ext, rn); |
4acb54ba EI |
402 | } |
403 | ||
f0f7e7f7 | 404 | void helper_mmu_write(CPUMBState *env, uint32_t ext, uint32_t rn, uint32_t v) |
4acb54ba | 405 | { |
f0f7e7f7 | 406 | mmu_write(env, ext, rn, v); |
4acb54ba | 407 | } |
faed1c2a | 408 | |
bdff8123 PM |
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) | |
faed1c2a | 413 | { |
c658b94f AF |
414 | MicroBlazeCPU *cpu; |
415 | CPUMBState *env; | |
bdff8123 PM |
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")); | |
c658b94f AF |
421 | cpu = MICROBLAZE_CPU(cs); |
422 | env = &cpu->env; | |
bdff8123 PM |
423 | |
424 | cpu_restore_state(cs, retaddr, true); | |
2e5282ca | 425 | if (!(env->msr & MSR_EE)) { |
faed1c2a EI |
426 | return; |
427 | } | |
428 | ||
b2e80a3c | 429 | env->ear = addr; |
bdff8123 | 430 | if (access_type == MMU_INST_FETCH) { |
97f90cbf | 431 | if ((env->pvr.regs[2] & PVR2_IOPB_BUS_EXC_MASK)) { |
78e9caf2 | 432 | env->esr = ESR_EC_INSN_BUS; |
64254eba | 433 | helper_raise_exception(env, EXCP_HW_EXCP); |
faed1c2a EI |
434 | } |
435 | } else { | |
97f90cbf | 436 | if ((env->pvr.regs[2] & PVR2_DOPB_BUS_EXC_MASK)) { |
78e9caf2 | 437 | env->esr = ESR_EC_DATA_BUS; |
64254eba | 438 | helper_raise_exception(env, EXCP_HW_EXCP); |
faed1c2a EI |
439 | } |
440 | } | |
441 | } | |
3c7b48b7 | 442 | #endif |