xref: /openbmc/qemu/target/microblaze/op_helper.c (revision 500eb6db)
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     if (b == 0) {
136         env->sregs[SR_MSR] |= MSR_DZ;
137 
138         if ((env->sregs[SR_MSR] & MSR_EE)
139             && !(env->pvr.regs[2] & PVR2_DIV_ZERO_EXC_MASK)) {
140             env->sregs[SR_ESR] = ESR_EC_DIVZERO;
141             helper_raise_exception(env, EXCP_HW_EXCP);
142         }
143         return 0;
144     }
145     env->sregs[SR_MSR] &= ~MSR_DZ;
146     return 1;
147 }
148 
149 uint32_t helper_divs(CPUMBState *env, uint32_t a, uint32_t b)
150 {
151     if (!div_prepare(env, a, b)) {
152         return 0;
153     }
154     return (int32_t)a / (int32_t)b;
155 }
156 
157 uint32_t helper_divu(CPUMBState *env, uint32_t a, uint32_t b)
158 {
159     if (!div_prepare(env, a, b)) {
160         return 0;
161     }
162     return a / b;
163 }
164 
165 /* raise FPU exception.  */
166 static void raise_fpu_exception(CPUMBState *env)
167 {
168     env->sregs[SR_ESR] = ESR_EC_FPU;
169     helper_raise_exception(env, EXCP_HW_EXCP);
170 }
171 
172 static void update_fpu_flags(CPUMBState *env, int flags)
173 {
174     int raise = 0;
175 
176     if (flags & float_flag_invalid) {
177         env->sregs[SR_FSR] |= FSR_IO;
178         raise = 1;
179     }
180     if (flags & float_flag_divbyzero) {
181         env->sregs[SR_FSR] |= FSR_DZ;
182         raise = 1;
183     }
184     if (flags & float_flag_overflow) {
185         env->sregs[SR_FSR] |= FSR_OF;
186         raise = 1;
187     }
188     if (flags & float_flag_underflow) {
189         env->sregs[SR_FSR] |= FSR_UF;
190         raise = 1;
191     }
192     if (raise
193         && (env->pvr.regs[2] & PVR2_FPU_EXC_MASK)
194         && (env->sregs[SR_MSR] & MSR_EE)) {
195         raise_fpu_exception(env);
196     }
197 }
198 
199 uint32_t helper_fadd(CPUMBState *env, uint32_t a, uint32_t b)
200 {
201     CPU_FloatU fd, fa, fb;
202     int flags;
203 
204     set_float_exception_flags(0, &env->fp_status);
205     fa.l = a;
206     fb.l = b;
207     fd.f = float32_add(fa.f, fb.f, &env->fp_status);
208 
209     flags = get_float_exception_flags(&env->fp_status);
210     update_fpu_flags(env, flags);
211     return fd.l;
212 }
213 
214 uint32_t helper_frsub(CPUMBState *env, uint32_t a, uint32_t b)
215 {
216     CPU_FloatU fd, fa, fb;
217     int flags;
218 
219     set_float_exception_flags(0, &env->fp_status);
220     fa.l = a;
221     fb.l = b;
222     fd.f = float32_sub(fb.f, fa.f, &env->fp_status);
223     flags = get_float_exception_flags(&env->fp_status);
224     update_fpu_flags(env, flags);
225     return fd.l;
226 }
227 
228 uint32_t helper_fmul(CPUMBState *env, uint32_t a, uint32_t b)
229 {
230     CPU_FloatU fd, fa, fb;
231     int flags;
232 
233     set_float_exception_flags(0, &env->fp_status);
234     fa.l = a;
235     fb.l = b;
236     fd.f = float32_mul(fa.f, fb.f, &env->fp_status);
237     flags = get_float_exception_flags(&env->fp_status);
238     update_fpu_flags(env, flags);
239 
240     return fd.l;
241 }
242 
243 uint32_t helper_fdiv(CPUMBState *env, uint32_t a, uint32_t b)
244 {
245     CPU_FloatU fd, fa, fb;
246     int flags;
247 
248     set_float_exception_flags(0, &env->fp_status);
249     fa.l = a;
250     fb.l = b;
251     fd.f = float32_div(fb.f, fa.f, &env->fp_status);
252     flags = get_float_exception_flags(&env->fp_status);
253     update_fpu_flags(env, flags);
254 
255     return fd.l;
256 }
257 
258 uint32_t helper_fcmp_un(CPUMBState *env, uint32_t a, uint32_t b)
259 {
260     CPU_FloatU fa, fb;
261     uint32_t r = 0;
262 
263     fa.l = a;
264     fb.l = b;
265 
266     if (float32_is_signaling_nan(fa.f, &env->fp_status) ||
267         float32_is_signaling_nan(fb.f, &env->fp_status)) {
268         update_fpu_flags(env, float_flag_invalid);
269         r = 1;
270     }
271 
272     if (float32_is_quiet_nan(fa.f, &env->fp_status) ||
273         float32_is_quiet_nan(fb.f, &env->fp_status)) {
274         r = 1;
275     }
276 
277     return r;
278 }
279 
280 uint32_t helper_fcmp_lt(CPUMBState *env, uint32_t a, uint32_t b)
281 {
282     CPU_FloatU fa, fb;
283     int r;
284     int flags;
285 
286     set_float_exception_flags(0, &env->fp_status);
287     fa.l = a;
288     fb.l = b;
289     r = float32_lt(fb.f, fa.f, &env->fp_status);
290     flags = get_float_exception_flags(&env->fp_status);
291     update_fpu_flags(env, flags & float_flag_invalid);
292 
293     return r;
294 }
295 
296 uint32_t helper_fcmp_eq(CPUMBState *env, uint32_t a, uint32_t b)
297 {
298     CPU_FloatU fa, fb;
299     int flags;
300     int r;
301 
302     set_float_exception_flags(0, &env->fp_status);
303     fa.l = a;
304     fb.l = b;
305     r = float32_eq_quiet(fa.f, fb.f, &env->fp_status);
306     flags = get_float_exception_flags(&env->fp_status);
307     update_fpu_flags(env, flags & float_flag_invalid);
308 
309     return r;
310 }
311 
312 uint32_t helper_fcmp_le(CPUMBState *env, uint32_t a, uint32_t b)
313 {
314     CPU_FloatU fa, fb;
315     int flags;
316     int r;
317 
318     fa.l = a;
319     fb.l = b;
320     set_float_exception_flags(0, &env->fp_status);
321     r = float32_le(fa.f, fb.f, &env->fp_status);
322     flags = get_float_exception_flags(&env->fp_status);
323     update_fpu_flags(env, flags & float_flag_invalid);
324 
325 
326     return r;
327 }
328 
329 uint32_t helper_fcmp_gt(CPUMBState *env, uint32_t a, uint32_t b)
330 {
331     CPU_FloatU fa, fb;
332     int flags, r;
333 
334     fa.l = a;
335     fb.l = b;
336     set_float_exception_flags(0, &env->fp_status);
337     r = float32_lt(fa.f, fb.f, &env->fp_status);
338     flags = get_float_exception_flags(&env->fp_status);
339     update_fpu_flags(env, flags & float_flag_invalid);
340     return r;
341 }
342 
343 uint32_t helper_fcmp_ne(CPUMBState *env, uint32_t a, uint32_t b)
344 {
345     CPU_FloatU fa, fb;
346     int flags, r;
347 
348     fa.l = a;
349     fb.l = b;
350     set_float_exception_flags(0, &env->fp_status);
351     r = !float32_eq_quiet(fa.f, fb.f, &env->fp_status);
352     flags = get_float_exception_flags(&env->fp_status);
353     update_fpu_flags(env, flags & float_flag_invalid);
354 
355     return r;
356 }
357 
358 uint32_t helper_fcmp_ge(CPUMBState *env, uint32_t a, uint32_t b)
359 {
360     CPU_FloatU fa, fb;
361     int flags, r;
362 
363     fa.l = a;
364     fb.l = b;
365     set_float_exception_flags(0, &env->fp_status);
366     r = !float32_lt(fa.f, fb.f, &env->fp_status);
367     flags = get_float_exception_flags(&env->fp_status);
368     update_fpu_flags(env, flags & float_flag_invalid);
369 
370     return r;
371 }
372 
373 uint32_t helper_flt(CPUMBState *env, uint32_t a)
374 {
375     CPU_FloatU fd, fa;
376 
377     fa.l = a;
378     fd.f = int32_to_float32(fa.l, &env->fp_status);
379     return fd.l;
380 }
381 
382 uint32_t helper_fint(CPUMBState *env, uint32_t a)
383 {
384     CPU_FloatU fa;
385     uint32_t r;
386     int flags;
387 
388     set_float_exception_flags(0, &env->fp_status);
389     fa.l = a;
390     r = float32_to_int32(fa.f, &env->fp_status);
391     flags = get_float_exception_flags(&env->fp_status);
392     update_fpu_flags(env, flags);
393 
394     return r;
395 }
396 
397 uint32_t helper_fsqrt(CPUMBState *env, uint32_t a)
398 {
399     CPU_FloatU fd, fa;
400     int flags;
401 
402     set_float_exception_flags(0, &env->fp_status);
403     fa.l = a;
404     fd.l = float32_sqrt(fa.f, &env->fp_status);
405     flags = get_float_exception_flags(&env->fp_status);
406     update_fpu_flags(env, flags);
407 
408     return fd.l;
409 }
410 
411 uint32_t helper_pcmpbf(uint32_t a, uint32_t b)
412 {
413     unsigned int i;
414     uint32_t mask = 0xff000000;
415 
416     for (i = 0; i < 4; i++) {
417         if ((a & mask) == (b & mask))
418             return i + 1;
419         mask >>= 8;
420     }
421     return 0;
422 }
423 
424 void helper_memalign(CPUMBState *env, target_ulong addr,
425                      uint32_t dr, uint32_t wr,
426                      uint32_t mask)
427 {
428     if (addr & mask) {
429             qemu_log_mask(CPU_LOG_INT,
430                           "unaligned access addr=" TARGET_FMT_lx
431                           " mask=%x, wr=%d dr=r%d\n",
432                           addr, mask, wr, dr);
433             env->sregs[SR_EAR] = addr;
434             env->sregs[SR_ESR] = ESR_EC_UNALIGNED_DATA | (wr << 10) \
435                                  | (dr & 31) << 5;
436             if (mask == 3) {
437                 env->sregs[SR_ESR] |= 1 << 11;
438             }
439             if (!(env->sregs[SR_MSR] & MSR_EE)) {
440                 return;
441             }
442             helper_raise_exception(env, EXCP_HW_EXCP);
443     }
444 }
445 
446 void helper_stackprot(CPUMBState *env, target_ulong addr)
447 {
448     if (addr < env->slr || addr > env->shr) {
449         qemu_log_mask(CPU_LOG_INT, "Stack protector violation at "
450                       TARGET_FMT_lx " %x %x\n",
451                       addr, env->slr, env->shr);
452         env->sregs[SR_EAR] = addr;
453         env->sregs[SR_ESR] = ESR_EC_STACKPROT;
454         helper_raise_exception(env, EXCP_HW_EXCP);
455     }
456 }
457 
458 #if !defined(CONFIG_USER_ONLY)
459 /* Writes/reads to the MMU's special regs end up here.  */
460 uint32_t helper_mmu_read(CPUMBState *env, uint32_t ext, uint32_t rn)
461 {
462     return mmu_read(env, ext, rn);
463 }
464 
465 void helper_mmu_write(CPUMBState *env, uint32_t ext, uint32_t rn, uint32_t v)
466 {
467     mmu_write(env, ext, rn, v);
468 }
469 
470 void mb_cpu_transaction_failed(CPUState *cs, hwaddr physaddr, vaddr addr,
471                                unsigned size, MMUAccessType access_type,
472                                int mmu_idx, MemTxAttrs attrs,
473                                MemTxResult response, uintptr_t retaddr)
474 {
475     MicroBlazeCPU *cpu;
476     CPUMBState *env;
477     qemu_log_mask(CPU_LOG_INT, "Transaction failed: vaddr 0x%" VADDR_PRIx
478                   " physaddr 0x" TARGET_FMT_plx " size %d access type %s\n",
479                   addr, physaddr, size,
480                   access_type == MMU_INST_FETCH ? "INST_FETCH" :
481                   (access_type == MMU_DATA_LOAD ? "DATA_LOAD" : "DATA_STORE"));
482     cpu = MICROBLAZE_CPU(cs);
483     env = &cpu->env;
484 
485     cpu_restore_state(cs, retaddr, true);
486     if (!(env->sregs[SR_MSR] & MSR_EE)) {
487         return;
488     }
489 
490     env->sregs[SR_EAR] = addr;
491     if (access_type == MMU_INST_FETCH) {
492         if ((env->pvr.regs[2] & PVR2_IOPB_BUS_EXC_MASK)) {
493             env->sregs[SR_ESR] = ESR_EC_INSN_BUS;
494             helper_raise_exception(env, EXCP_HW_EXCP);
495         }
496     } else {
497         if ((env->pvr.regs[2] & PVR2_DOPB_BUS_EXC_MASK)) {
498             env->sregs[SR_ESR] = ESR_EC_DATA_BUS;
499             helper_raise_exception(env, EXCP_HW_EXCP);
500         }
501     }
502 }
503 #endif
504