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