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