xref: /openbmc/qemu/target/microblaze/op_helper.c (revision 65a117da)
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 #define D(x)
30 
31 void helper_put(uint32_t id, uint32_t ctrl, uint32_t data)
32 {
33     int test = ctrl & STREAM_TEST;
34     int atomic = ctrl & STREAM_ATOMIC;
35     int control = ctrl & STREAM_CONTROL;
36     int nonblock = ctrl & STREAM_NONBLOCK;
37     int exception = ctrl & STREAM_EXCEPTION;
38 
39     qemu_log_mask(LOG_UNIMP, "Unhandled stream put to stream-id=%d data=%x %s%s%s%s%s\n",
40              id, data,
41              test ? "t" : "",
42              nonblock ? "n" : "",
43              exception ? "e" : "",
44              control ? "c" : "",
45              atomic ? "a" : "");
46 }
47 
48 uint32_t helper_get(uint32_t id, uint32_t ctrl)
49 {
50     int test = ctrl & STREAM_TEST;
51     int atomic = ctrl & STREAM_ATOMIC;
52     int control = ctrl & STREAM_CONTROL;
53     int nonblock = ctrl & STREAM_NONBLOCK;
54     int exception = ctrl & STREAM_EXCEPTION;
55 
56     qemu_log_mask(LOG_UNIMP, "Unhandled stream get from stream-id=%d %s%s%s%s%s\n",
57              id,
58              test ? "t" : "",
59              nonblock ? "n" : "",
60              exception ? "e" : "",
61              control ? "c" : "",
62              atomic ? "a" : "");
63     return 0xdead0000 | id;
64 }
65 
66 void helper_raise_exception(CPUMBState *env, uint32_t index)
67 {
68     CPUState *cs = env_cpu(env);
69 
70     cs->exception_index = index;
71     cpu_loop_exit(cs);
72 }
73 
74 void helper_debug(CPUMBState *env)
75 {
76     int i;
77 
78     qemu_log("PC=%" PRIx64 "\n", env->sregs[SR_PC]);
79     qemu_log("rmsr=%" PRIx64 " resr=%" PRIx64 " rear=%" PRIx64 " "
80              "debug[%x] imm=%x iflags=%x\n",
81              env->sregs[SR_MSR], env->sregs[SR_ESR], env->sregs[SR_EAR],
82              env->debug, env->imm, env->iflags);
83     qemu_log("btaken=%d btarget=%" PRIx64 " mode=%s(saved=%s) eip=%d ie=%d\n",
84              env->btaken, env->btarget,
85              (env->sregs[SR_MSR] & MSR_UM) ? "user" : "kernel",
86              (env->sregs[SR_MSR] & MSR_UMS) ? "user" : "kernel",
87              (bool)(env->sregs[SR_MSR] & MSR_EIP),
88              (bool)(env->sregs[SR_MSR] & MSR_IE));
89     for (i = 0; i < 32; i++) {
90         qemu_log("r%2.2d=%8.8x ", i, env->regs[i]);
91         if ((i + 1) % 4 == 0)
92             qemu_log("\n");
93     }
94     qemu_log("\n\n");
95 }
96 
97 static inline uint32_t compute_carry(uint32_t a, uint32_t b, uint32_t cin)
98 {
99     uint32_t cout = 0;
100 
101     if ((b == ~0) && cin)
102         cout = 1;
103     else if ((~0 - a) < (b + cin))
104         cout = 1;
105     return cout;
106 }
107 
108 uint32_t helper_cmp(uint32_t a, uint32_t b)
109 {
110     uint32_t t;
111 
112     t = b + ~a + 1;
113     if ((b & 0x80000000) ^ (a & 0x80000000))
114         t = (t & 0x7fffffff) | (b & 0x80000000);
115     return t;
116 }
117 
118 uint32_t helper_cmpu(uint32_t a, uint32_t b)
119 {
120     uint32_t t;
121 
122     t = b + ~a + 1;
123     if ((b & 0x80000000) ^ (a & 0x80000000))
124         t = (t & 0x7fffffff) | (a & 0x80000000);
125     return t;
126 }
127 
128 uint32_t helper_carry(uint32_t a, uint32_t b, uint32_t cf)
129 {
130     return compute_carry(a, b, cf);
131 }
132 
133 static inline int div_prepare(CPUMBState *env, uint32_t a, uint32_t b)
134 {
135     MicroBlazeCPU *cpu = env_archcpu(env);
136 
137     if (b == 0) {
138         env->sregs[SR_MSR] |= MSR_DZ;
139 
140         if ((env->sregs[SR_MSR] & MSR_EE) && cpu->cfg.div_zero_exception) {
141             env->sregs[SR_ESR] = ESR_EC_DIVZERO;
142             helper_raise_exception(env, EXCP_HW_EXCP);
143         }
144         return 0;
145     }
146     env->sregs[SR_MSR] &= ~MSR_DZ;
147     return 1;
148 }
149 
150 uint32_t helper_divs(CPUMBState *env, uint32_t a, uint32_t b)
151 {
152     if (!div_prepare(env, a, b)) {
153         return 0;
154     }
155     return (int32_t)a / (int32_t)b;
156 }
157 
158 uint32_t helper_divu(CPUMBState *env, uint32_t a, uint32_t b)
159 {
160     if (!div_prepare(env, a, b)) {
161         return 0;
162     }
163     return a / b;
164 }
165 
166 /* raise FPU exception.  */
167 static void raise_fpu_exception(CPUMBState *env)
168 {
169     env->sregs[SR_ESR] = ESR_EC_FPU;
170     helper_raise_exception(env, EXCP_HW_EXCP);
171 }
172 
173 static void update_fpu_flags(CPUMBState *env, int flags)
174 {
175     int raise = 0;
176 
177     if (flags & float_flag_invalid) {
178         env->sregs[SR_FSR] |= FSR_IO;
179         raise = 1;
180     }
181     if (flags & float_flag_divbyzero) {
182         env->sregs[SR_FSR] |= FSR_DZ;
183         raise = 1;
184     }
185     if (flags & float_flag_overflow) {
186         env->sregs[SR_FSR] |= FSR_OF;
187         raise = 1;
188     }
189     if (flags & float_flag_underflow) {
190         env->sregs[SR_FSR] |= FSR_UF;
191         raise = 1;
192     }
193     if (raise
194         && (env->pvr.regs[2] & PVR2_FPU_EXC_MASK)
195         && (env->sregs[SR_MSR] & MSR_EE)) {
196         raise_fpu_exception(env);
197     }
198 }
199 
200 uint32_t helper_fadd(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_add(fa.f, fb.f, &env->fp_status);
209 
210     flags = get_float_exception_flags(&env->fp_status);
211     update_fpu_flags(env, flags);
212     return fd.l;
213 }
214 
215 uint32_t helper_frsub(CPUMBState *env, uint32_t a, uint32_t b)
216 {
217     CPU_FloatU fd, fa, fb;
218     int flags;
219 
220     set_float_exception_flags(0, &env->fp_status);
221     fa.l = a;
222     fb.l = b;
223     fd.f = float32_sub(fb.f, fa.f, &env->fp_status);
224     flags = get_float_exception_flags(&env->fp_status);
225     update_fpu_flags(env, flags);
226     return fd.l;
227 }
228 
229 uint32_t helper_fmul(CPUMBState *env, uint32_t a, uint32_t b)
230 {
231     CPU_FloatU fd, fa, fb;
232     int flags;
233 
234     set_float_exception_flags(0, &env->fp_status);
235     fa.l = a;
236     fb.l = b;
237     fd.f = float32_mul(fa.f, fb.f, &env->fp_status);
238     flags = get_float_exception_flags(&env->fp_status);
239     update_fpu_flags(env, flags);
240 
241     return fd.l;
242 }
243 
244 uint32_t helper_fdiv(CPUMBState *env, uint32_t a, uint32_t b)
245 {
246     CPU_FloatU fd, fa, fb;
247     int flags;
248 
249     set_float_exception_flags(0, &env->fp_status);
250     fa.l = a;
251     fb.l = b;
252     fd.f = float32_div(fb.f, fa.f, &env->fp_status);
253     flags = get_float_exception_flags(&env->fp_status);
254     update_fpu_flags(env, flags);
255 
256     return fd.l;
257 }
258 
259 uint32_t helper_fcmp_un(CPUMBState *env, uint32_t a, uint32_t b)
260 {
261     CPU_FloatU fa, fb;
262     uint32_t r = 0;
263 
264     fa.l = a;
265     fb.l = b;
266 
267     if (float32_is_signaling_nan(fa.f, &env->fp_status) ||
268         float32_is_signaling_nan(fb.f, &env->fp_status)) {
269         update_fpu_flags(env, float_flag_invalid);
270         r = 1;
271     }
272 
273     if (float32_is_quiet_nan(fa.f, &env->fp_status) ||
274         float32_is_quiet_nan(fb.f, &env->fp_status)) {
275         r = 1;
276     }
277 
278     return r;
279 }
280 
281 uint32_t helper_fcmp_lt(CPUMBState *env, uint32_t a, uint32_t b)
282 {
283     CPU_FloatU fa, fb;
284     int r;
285     int flags;
286 
287     set_float_exception_flags(0, &env->fp_status);
288     fa.l = a;
289     fb.l = b;
290     r = float32_lt(fb.f, fa.f, &env->fp_status);
291     flags = get_float_exception_flags(&env->fp_status);
292     update_fpu_flags(env, flags & float_flag_invalid);
293 
294     return r;
295 }
296 
297 uint32_t helper_fcmp_eq(CPUMBState *env, uint32_t a, uint32_t b)
298 {
299     CPU_FloatU fa, fb;
300     int flags;
301     int r;
302 
303     set_float_exception_flags(0, &env->fp_status);
304     fa.l = a;
305     fb.l = b;
306     r = float32_eq_quiet(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_fcmp_le(CPUMBState *env, uint32_t a, uint32_t b)
314 {
315     CPU_FloatU fa, fb;
316     int flags;
317     int r;
318 
319     fa.l = a;
320     fb.l = b;
321     set_float_exception_flags(0, &env->fp_status);
322     r = float32_le(fa.f, fb.f, &env->fp_status);
323     flags = get_float_exception_flags(&env->fp_status);
324     update_fpu_flags(env, flags & float_flag_invalid);
325 
326 
327     return r;
328 }
329 
330 uint32_t helper_fcmp_gt(CPUMBState *env, uint32_t a, uint32_t b)
331 {
332     CPU_FloatU fa, fb;
333     int flags, r;
334 
335     fa.l = a;
336     fb.l = b;
337     set_float_exception_flags(0, &env->fp_status);
338     r = float32_lt(fa.f, fb.f, &env->fp_status);
339     flags = get_float_exception_flags(&env->fp_status);
340     update_fpu_flags(env, flags & float_flag_invalid);
341     return r;
342 }
343 
344 uint32_t helper_fcmp_ne(CPUMBState *env, uint32_t a, uint32_t b)
345 {
346     CPU_FloatU fa, fb;
347     int flags, r;
348 
349     fa.l = a;
350     fb.l = b;
351     set_float_exception_flags(0, &env->fp_status);
352     r = !float32_eq_quiet(fa.f, fb.f, &env->fp_status);
353     flags = get_float_exception_flags(&env->fp_status);
354     update_fpu_flags(env, flags & float_flag_invalid);
355 
356     return r;
357 }
358 
359 uint32_t helper_fcmp_ge(CPUMBState *env, uint32_t a, uint32_t b)
360 {
361     CPU_FloatU fa, fb;
362     int flags, r;
363 
364     fa.l = a;
365     fb.l = b;
366     set_float_exception_flags(0, &env->fp_status);
367     r = !float32_lt(fa.f, fb.f, &env->fp_status);
368     flags = get_float_exception_flags(&env->fp_status);
369     update_fpu_flags(env, flags & float_flag_invalid);
370 
371     return r;
372 }
373 
374 uint32_t helper_flt(CPUMBState *env, uint32_t a)
375 {
376     CPU_FloatU fd, fa;
377 
378     fa.l = a;
379     fd.f = int32_to_float32(fa.l, &env->fp_status);
380     return fd.l;
381 }
382 
383 uint32_t helper_fint(CPUMBState *env, uint32_t a)
384 {
385     CPU_FloatU fa;
386     uint32_t r;
387     int flags;
388 
389     set_float_exception_flags(0, &env->fp_status);
390     fa.l = a;
391     r = float32_to_int32(fa.f, &env->fp_status);
392     flags = get_float_exception_flags(&env->fp_status);
393     update_fpu_flags(env, flags);
394 
395     return r;
396 }
397 
398 uint32_t helper_fsqrt(CPUMBState *env, uint32_t a)
399 {
400     CPU_FloatU fd, fa;
401     int flags;
402 
403     set_float_exception_flags(0, &env->fp_status);
404     fa.l = a;
405     fd.l = float32_sqrt(fa.f, &env->fp_status);
406     flags = get_float_exception_flags(&env->fp_status);
407     update_fpu_flags(env, flags);
408 
409     return fd.l;
410 }
411 
412 uint32_t helper_pcmpbf(uint32_t a, uint32_t b)
413 {
414     unsigned int i;
415     uint32_t mask = 0xff000000;
416 
417     for (i = 0; i < 4; i++) {
418         if ((a & mask) == (b & mask))
419             return i + 1;
420         mask >>= 8;
421     }
422     return 0;
423 }
424 
425 void helper_memalign(CPUMBState *env, target_ulong addr,
426                      uint32_t dr, uint32_t wr,
427                      uint32_t mask)
428 {
429     if (addr & mask) {
430             qemu_log_mask(CPU_LOG_INT,
431                           "unaligned access addr=" TARGET_FMT_lx
432                           " mask=%x, wr=%d dr=r%d\n",
433                           addr, mask, wr, dr);
434             env->sregs[SR_EAR] = addr;
435             env->sregs[SR_ESR] = ESR_EC_UNALIGNED_DATA | (wr << 10) \
436                                  | (dr & 31) << 5;
437             if (mask == 3) {
438                 env->sregs[SR_ESR] |= 1 << 11;
439             }
440             if (!(env->sregs[SR_MSR] & MSR_EE)) {
441                 return;
442             }
443             helper_raise_exception(env, EXCP_HW_EXCP);
444     }
445 }
446 
447 void helper_stackprot(CPUMBState *env, target_ulong addr)
448 {
449     if (addr < env->slr || addr > env->shr) {
450         qemu_log_mask(CPU_LOG_INT, "Stack protector violation at "
451                       TARGET_FMT_lx " %x %x\n",
452                       addr, env->slr, env->shr);
453         env->sregs[SR_EAR] = addr;
454         env->sregs[SR_ESR] = ESR_EC_STACKPROT;
455         helper_raise_exception(env, EXCP_HW_EXCP);
456     }
457 }
458 
459 #if !defined(CONFIG_USER_ONLY)
460 /* Writes/reads to the MMU's special regs end up here.  */
461 uint32_t helper_mmu_read(CPUMBState *env, uint32_t ext, uint32_t rn)
462 {
463     return mmu_read(env, ext, rn);
464 }
465 
466 void helper_mmu_write(CPUMBState *env, uint32_t ext, uint32_t rn, uint32_t v)
467 {
468     mmu_write(env, ext, rn, v);
469 }
470 
471 void mb_cpu_transaction_failed(CPUState *cs, hwaddr physaddr, vaddr addr,
472                                unsigned size, MMUAccessType access_type,
473                                int mmu_idx, MemTxAttrs attrs,
474                                MemTxResult response, uintptr_t retaddr)
475 {
476     MicroBlazeCPU *cpu;
477     CPUMBState *env;
478     qemu_log_mask(CPU_LOG_INT, "Transaction failed: vaddr 0x%" VADDR_PRIx
479                   " physaddr 0x" TARGET_FMT_plx " size %d access type %s\n",
480                   addr, physaddr, size,
481                   access_type == MMU_INST_FETCH ? "INST_FETCH" :
482                   (access_type == MMU_DATA_LOAD ? "DATA_LOAD" : "DATA_STORE"));
483     cpu = MICROBLAZE_CPU(cs);
484     env = &cpu->env;
485 
486     cpu_restore_state(cs, retaddr, true);
487     if (!(env->sregs[SR_MSR] & MSR_EE)) {
488         return;
489     }
490 
491     env->sregs[SR_EAR] = addr;
492     if (access_type == MMU_INST_FETCH) {
493         if ((env->pvr.regs[2] & PVR2_IOPB_BUS_EXC_MASK)) {
494             env->sregs[SR_ESR] = ESR_EC_INSN_BUS;
495             helper_raise_exception(env, EXCP_HW_EXCP);
496         }
497     } else {
498         if ((env->pvr.regs[2] & PVR2_DOPB_BUS_EXC_MASK)) {
499             env->sregs[SR_ESR] = ESR_EC_DATA_BUS;
500             helper_raise_exception(env, EXCP_HW_EXCP);
501         }
502     }
503 }
504 #endif
505