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