xref: /openbmc/qemu/target/microblaze/op_helper.c (revision b6fd8fa68d4308de6d5a77b85320351d60d81876)
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