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
helper_put(uint32_t id,uint32_t ctrl,uint32_t data)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
helper_get(uint32_t id,uint32_t ctrl)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
helper_raise_exception(CPUMBState * env,uint32_t index)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. */
raise_divzero(CPUMBState * env,uint32_t esr,uintptr_t unwind_pc)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
helper_divs(CPUMBState * env,uint32_t ra,uint32_t rb)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
helper_divu(CPUMBState * env,uint32_t ra,uint32_t rb)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. */
raise_fpu_exception(CPUMBState * env,uintptr_t ra)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
update_fpu_flags(CPUMBState * env,int flags,uintptr_t ra)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
helper_fadd(CPUMBState * env,uint32_t a,uint32_t b)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
helper_frsub(CPUMBState * env,uint32_t a,uint32_t b)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
helper_fmul(CPUMBState * env,uint32_t a,uint32_t b)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
helper_fdiv(CPUMBState * env,uint32_t a,uint32_t b)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
helper_fcmp_un(CPUMBState * env,uint32_t a,uint32_t b)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
helper_fcmp_lt(CPUMBState * env,uint32_t a,uint32_t b)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
helper_fcmp_eq(CPUMBState * env,uint32_t a,uint32_t b)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
helper_fcmp_le(CPUMBState * env,uint32_t a,uint32_t b)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
helper_fcmp_gt(CPUMBState * env,uint32_t a,uint32_t b)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
helper_fcmp_ne(CPUMBState * env,uint32_t a,uint32_t b)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
helper_fcmp_ge(CPUMBState * env,uint32_t a,uint32_t b)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
helper_flt(CPUMBState * env,uint32_t a)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
helper_fint(CPUMBState * env,uint32_t a)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
helper_fsqrt(CPUMBState * env,uint32_t a)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
helper_pcmpbf(uint32_t a,uint32_t b)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
helper_stackprot(CPUMBState * env,target_ulong addr)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. */
helper_mmu_read(CPUMBState * env,uint32_t ext,uint32_t rn)401 uint32_t helper_mmu_read(CPUMBState *env, uint32_t ext, uint32_t rn)
402 {
403 return mmu_read(env, ext, rn);
404 }
405
helper_mmu_write(CPUMBState * env,uint32_t ext,uint32_t rn,uint32_t v)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
mb_transaction_failed_internal(CPUState * cs,hwaddr physaddr,uint64_t addr,unsigned size,MMUAccessType access_type,uintptr_t retaddr)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
mb_cpu_transaction_failed(CPUState * cs,hwaddr physaddr,vaddr addr,unsigned size,MMUAccessType access_type,int mmu_idx,MemTxAttrs attrs,MemTxResult response,uintptr_t retaddr)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