master
c 498 lines 14 KB
Raw
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.1 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 "qemu/log.h"
23 #include "cpu.h"
24 #include "exec/helper-proto.h"
25 #include "qemu/host-utils.h"
26 #include "accel/tcg/cpu-ldst-common.h"
27 #include "accel/tcg/cpu-loop.h"
28 #include "fpu/softfloat.h"
29
30 void helper_put(uint32_t id, uint32_t ctrl, uint32_t data)
31 {
32 int test = ctrl & STREAM_TEST;
33 int atomic = ctrl & STREAM_ATOMIC;
34 int control = ctrl & STREAM_CONTROL;
35 int nonblock = ctrl & STREAM_NONBLOCK;
36 int exception = ctrl & STREAM_EXCEPTION;
37
38 qemu_log_mask(LOG_UNIMP, "Unhandled stream put to stream-id=%d data=%x %s%s%s%s%s\n",
39 id, data,
40 test ? "t" : "",
41 nonblock ? "n" : "",
42 exception ? "e" : "",
43 control ? "c" : "",
44 atomic ? "a" : "");
45 }
46
47 uint32_t helper_get(uint32_t id, uint32_t ctrl)
48 {
49 int test = ctrl & STREAM_TEST;
50 int atomic = ctrl & STREAM_ATOMIC;
51 int control = ctrl & STREAM_CONTROL;
52 int nonblock = ctrl & STREAM_NONBLOCK;
53 int exception = ctrl & STREAM_EXCEPTION;
54
55 qemu_log_mask(LOG_UNIMP, "Unhandled stream get from stream-id=%d %s%s%s%s%s\n",
56 id,
57 test ? "t" : "",
58 nonblock ? "n" : "",
59 exception ? "e" : "",
60 control ? "c" : "",
61 atomic ? "a" : "");
62 return 0xdead0000 | id;
63 }
64
65 void helper_raise_exception(CPUMBState *env, uint32_t index)
66 {
67 CPUState *cs = env_cpu(env);
68
69 cs->exception_index = index;
70 cpu_loop_exit(cs);
71 }
72
73 /* Raises ESR_EC_DIVZERO if exceptions are enabled. */
74 static void raise_divzero(CPUMBState *env, uint32_t esr, uintptr_t unwind_pc)
75 {
76 env->msr |= MSR_DZ;
77
78 if ((env->msr & MSR_EE) && env_archcpu(env)->cfg.div_zero_exception) {
79 CPUState *cs = env_cpu(env);
80
81 env->esr = esr;
82 cs->exception_index = EXCP_HW_EXCP;
83 cpu_loop_exit_restore(cs, unwind_pc);
84 }
85 }
86
87 uint32_t helper_divs(CPUMBState *env, uint32_t ra, uint32_t rb)
88 {
89 if (!ra) {
90 raise_divzero(env, ESR_EC_DIVZERO, GETPC());
91 return 0;
92 }
93
94 /*
95 * Check for division overflows.
96 *
97 * Spec: https://docs.amd.com/r/en-US/ug984-vivado-microblaze-ref/idiv
98 * UG984, Chapter 5 MicroBlaze Instruction Set Architecture, idiv.
99 *
100 * If the U bit is clear, the value of rA is -1, and the value of rB is
101 * -2147483648 (divide overflow), the DZO bit in MSR will be set and
102 * the value in rD will be -2147483648, unless an exception is generated.
103 */
104 if ((int32_t)ra == -1 && (int32_t)rb == INT32_MIN) {
105 raise_divzero(env, ESR_EC_DIVZERO | ESR_ESS_DEC_OF, GETPC());
106 return INT32_MIN;
107 }
108 return (int32_t)rb / (int32_t)ra;
109 }
110
111 uint32_t helper_divu(CPUMBState *env, uint32_t ra, uint32_t rb)
112 {
113 if (!ra) {
114 raise_divzero(env, ESR_EC_DIVZERO, GETPC());
115 return 0;
116 }
117 return rb / ra;
118 }
119
120 /* raise FPU exception. */
121 static void raise_fpu_exception(CPUMBState *env, uintptr_t ra)
122 {
123 CPUState *cs = env_cpu(env);
124
125 env->esr = ESR_EC_FPU;
126 cs->exception_index = EXCP_HW_EXCP;
127 cpu_loop_exit_restore(cs, ra);
128 }
129
130 static void update_fpu_flags(CPUMBState *env, int flags, uintptr_t ra)
131 {
132 int raise = 0;
133
134 if (flags & float_flag_invalid) {
135 env->fsr |= FSR_IO;
136 raise = 1;
137 }
138 if (flags & float_flag_divbyzero) {
139 env->fsr |= FSR_DZ;
140 raise = 1;
141 }
142 if (flags & float_flag_overflow) {
143 env->fsr |= FSR_OF;
144 raise = 1;
145 }
146 if (flags & float_flag_underflow) {
147 env->fsr |= FSR_UF;
148 raise = 1;
149 }
150 if (raise
151 && (env_archcpu(env)->cfg.pvr_regs[2] & PVR2_FPU_EXC_MASK)
152 && (env->msr & MSR_EE)) {
153 raise_fpu_exception(env, ra);
154 }
155 }
156
157 uint32_t helper_fadd(CPUMBState *env, uint32_t a, uint32_t b)
158 {
159 CPU_FloatU fd, fa, fb;
160 int flags;
161
162 set_float_exception_flags(0, &env->fp_status);
163 fa.l = a;
164 fb.l = b;
165 fd.f = float32_add(fa.f, fb.f, &env->fp_status);
166
167 flags = get_float_exception_flags(&env->fp_status);
168 update_fpu_flags(env, flags, GETPC());
169 return fd.l;
170 }
171
172 uint32_t helper_frsub(CPUMBState *env, uint32_t a, uint32_t b)
173 {
174 CPU_FloatU fd, fa, fb;
175 int flags;
176
177 set_float_exception_flags(0, &env->fp_status);
178 fa.l = a;
179 fb.l = b;
180 fd.f = float32_sub(fb.f, fa.f, &env->fp_status);
181 flags = get_float_exception_flags(&env->fp_status);
182 update_fpu_flags(env, flags, GETPC());
183 return fd.l;
184 }
185
186 uint32_t helper_fmul(CPUMBState *env, uint32_t a, uint32_t b)
187 {
188 CPU_FloatU fd, fa, fb;
189 int flags;
190
191 set_float_exception_flags(0, &env->fp_status);
192 fa.l = a;
193 fb.l = b;
194 fd.f = float32_mul(fa.f, fb.f, &env->fp_status);
195 flags = get_float_exception_flags(&env->fp_status);
196 update_fpu_flags(env, flags, GETPC());
197
198 return fd.l;
199 }
200
201 uint32_t helper_fdiv(CPUMBState *env, uint32_t a, uint32_t b)
202 {
203 CPU_FloatU fd, fa, fb;
204 int flags;
205
206 set_float_exception_flags(0, &env->fp_status);
207 fa.l = a;
208 fb.l = b;
209 fd.f = float32_div(fb.f, fa.f, &env->fp_status);
210 flags = get_float_exception_flags(&env->fp_status);
211 update_fpu_flags(env, flags, GETPC());
212
213 return fd.l;
214 }
215
216 uint32_t helper_fcmp_un(CPUMBState *env, uint32_t a, uint32_t b)
217 {
218 CPU_FloatU fa, fb;
219 uint32_t r = 0;
220
221 fa.l = a;
222 fb.l = b;
223
224 if (float32_is_signaling_nan(fa.f, &env->fp_status) ||
225 float32_is_signaling_nan(fb.f, &env->fp_status)) {
226 update_fpu_flags(env, float_flag_invalid, GETPC());
227 r = 1;
228 }
229
230 if (float32_is_quiet_nan(fa.f, &env->fp_status) ||
231 float32_is_quiet_nan(fb.f, &env->fp_status)) {
232 r = 1;
233 }
234
235 return r;
236 }
237
238 uint32_t helper_fcmp_lt(CPUMBState *env, uint32_t a, uint32_t b)
239 {
240 CPU_FloatU fa, fb;
241 int r;
242 int flags;
243
244 set_float_exception_flags(0, &env->fp_status);
245 fa.l = a;
246 fb.l = b;
247 r = float32_lt(fb.f, fa.f, &env->fp_status);
248 flags = get_float_exception_flags(&env->fp_status);
249 update_fpu_flags(env, flags & float_flag_invalid, GETPC());
250
251 return r;
252 }
253
254 uint32_t helper_fcmp_eq(CPUMBState *env, uint32_t a, uint32_t b)
255 {
256 CPU_FloatU fa, fb;
257 int flags;
258 int r;
259
260 set_float_exception_flags(0, &env->fp_status);
261 fa.l = a;
262 fb.l = b;
263 r = float32_eq_quiet(fa.f, fb.f, &env->fp_status);
264 flags = get_float_exception_flags(&env->fp_status);
265 update_fpu_flags(env, flags & float_flag_invalid, GETPC());
266
267 return r;
268 }
269
270 uint32_t helper_fcmp_le(CPUMBState *env, uint32_t a, uint32_t b)
271 {
272 CPU_FloatU fa, fb;
273 int flags;
274 int r;
275
276 fa.l = a;
277 fb.l = b;
278 set_float_exception_flags(0, &env->fp_status);
279 r = float32_le(fa.f, fb.f, &env->fp_status);
280 flags = get_float_exception_flags(&env->fp_status);
281 update_fpu_flags(env, flags & float_flag_invalid, GETPC());
282
283
284 return r;
285 }
286
287 uint32_t helper_fcmp_gt(CPUMBState *env, uint32_t a, uint32_t b)
288 {
289 CPU_FloatU fa, fb;
290 int flags, r;
291
292 fa.l = a;
293 fb.l = b;
294 set_float_exception_flags(0, &env->fp_status);
295 r = float32_lt(fa.f, fb.f, &env->fp_status);
296 flags = get_float_exception_flags(&env->fp_status);
297 update_fpu_flags(env, flags & float_flag_invalid, GETPC());
298 return r;
299 }
300
301 uint32_t helper_fcmp_ne(CPUMBState *env, uint32_t a, uint32_t b)
302 {
303 CPU_FloatU fa, fb;
304 int flags, r;
305
306 fa.l = a;
307 fb.l = b;
308 set_float_exception_flags(0, &env->fp_status);
309 r = !float32_eq_quiet(fa.f, fb.f, &env->fp_status);
310 flags = get_float_exception_flags(&env->fp_status);
311 update_fpu_flags(env, flags & float_flag_invalid, GETPC());
312
313 return r;
314 }
315
316 uint32_t helper_fcmp_ge(CPUMBState *env, uint32_t a, uint32_t b)
317 {
318 CPU_FloatU fa, fb;
319 int flags, r;
320
321 fa.l = a;
322 fb.l = b;
323 set_float_exception_flags(0, &env->fp_status);
324 r = !float32_lt(fa.f, fb.f, &env->fp_status);
325 flags = get_float_exception_flags(&env->fp_status);
326 update_fpu_flags(env, flags & float_flag_invalid, GETPC());
327
328 return r;
329 }
330
331 uint32_t helper_flt(CPUMBState *env, uint32_t a)
332 {
333 CPU_FloatU fd, fa;
334
335 fa.l = a;
336 fd.f = int32_to_float32(fa.l, &env->fp_status);
337 return fd.l;
338 }
339
340 uint32_t helper_fint(CPUMBState *env, uint32_t a)
341 {
342 CPU_FloatU fa;
343 uint32_t r;
344 int flags;
345
346 set_float_exception_flags(0, &env->fp_status);
347 fa.l = a;
348 r = float32_to_int32(fa.f, &env->fp_status);
349 flags = get_float_exception_flags(&env->fp_status);
350 update_fpu_flags(env, flags, GETPC());
351
352 return r;
353 }
354
355 uint32_t helper_fsqrt(CPUMBState *env, uint32_t a)
356 {
357 CPU_FloatU fd, fa;
358 int flags;
359
360 set_float_exception_flags(0, &env->fp_status);
361 fa.l = a;
362 fd.l = float32_sqrt(fa.f, &env->fp_status);
363 flags = get_float_exception_flags(&env->fp_status);
364 update_fpu_flags(env, flags, GETPC());
365
366 return fd.l;
367 }
368
369 uint32_t helper_pcmpbf(uint32_t a, uint32_t b)
370 {
371 unsigned int i;
372 uint32_t mask = 0xff000000;
373
374 for (i = 0; i < 4; i++) {
375 if ((a & mask) == (b & mask))
376 return i + 1;
377 mask >>= 8;
378 }
379 return 0;
380 }
381
382 void helper_stackprot(CPUMBState *env, uint32_t addr)
383 {
384 if (addr < env->slr || addr > env->shr) {
385 CPUState *cs = env_cpu(env);
386
387 qemu_log_mask(CPU_LOG_INT, "Stack protector violation at "
388 "0x%x 0x%x 0x%x\n",
389 addr, env->slr, env->shr);
390
391 env->ear = addr;
392 env->esr = ESR_EC_STACKPROT;
393 cs->exception_index = EXCP_HW_EXCP;
394 cpu_loop_exit_restore(cs, GETPC());
395 }
396 }
397
398 #if !defined(CONFIG_USER_ONLY)
399 #include "system/memory.h"
400
401 /* Writes/reads to the MMU's special regs end up here. */
402 uint32_t helper_mmu_read(CPUMBState *env, uint32_t ext, uint32_t rn)
403 {
404 return mmu_read(env, ext, rn);
405 }
406
407 void helper_mmu_write(CPUMBState *env, uint32_t ext, uint32_t rn, uint32_t v)
408 {
409 mmu_write(env, ext, rn, v);
410 }
411
412 static void mb_transaction_failed_internal(CPUState *cs, hwaddr physaddr,
413 uint64_t addr, unsigned size,
414 MMUAccessType access_type,
415 uintptr_t retaddr)
416 {
417 CPUMBState *env = cpu_env(cs);
418 MicroBlazeCPU *cpu = env_archcpu(env);
419 const char *access_name = "INVALID";
420 bool take = env->msr & MSR_EE;
421 uint32_t esr = ESR_EC_DATA_BUS;
422
423 switch (access_type) {
424 case MMU_INST_FETCH:
425 access_name = "INST_FETCH";
426 esr = ESR_EC_INSN_BUS;
427 take &= cpu->cfg.iopb_bus_exception;
428 break;
429 case MMU_DATA_LOAD:
430 access_name = "DATA_LOAD";
431 take &= cpu->cfg.dopb_bus_exception;
432 break;
433 case MMU_DATA_STORE:
434 access_name = "DATA_STORE";
435 take &= cpu->cfg.dopb_bus_exception;
436 break;
437 }
438
439 qemu_log_mask(CPU_LOG_INT, "Transaction failed: addr 0x%" PRIx64
440 "physaddr 0x" HWADDR_FMT_plx " size %d access-type %s (%s)\n",
441 addr, physaddr, size, access_name,
442 take ? "TAKEN" : "DROPPED");
443
444 if (take) {
445 env->esr = esr;
446 env->ear = addr;
447 cs->exception_index = EXCP_HW_EXCP;
448 cpu_loop_exit_restore(cs, retaddr);
449 }
450 }
451
452 void mb_cpu_transaction_failed(CPUState *cs, hwaddr physaddr, vaddr addr,
453 unsigned size, MMUAccessType access_type,
454 int mmu_idx, MemTxAttrs attrs,
455 MemTxResult response, uintptr_t retaddr)
456 {
457 mb_transaction_failed_internal(cs, physaddr, addr, size,
458 access_type, retaddr);
459 }
460
461 #define LD_EA(NAME, TYPE, FUNC) \
462 uint32_t HELPER(NAME)(CPUMBState *env, uint64_t ea) \
463 { \
464 CPUState *cs = env_cpu(env); \
465 MemTxResult txres; \
466 TYPE ret = FUNC(cs->as, ea, MEMTXATTRS_UNSPECIFIED, &txres); \
467 if (unlikely(txres != MEMTX_OK)) { \
468 mb_transaction_failed_internal(cs, ea, ea, sizeof(TYPE), \
469 MMU_DATA_LOAD, GETPC()); \
470 } \
471 return ret; \
472 }
473
474 LD_EA(lbuea, uint8_t, address_space_ldub)
475 LD_EA(lhuea_be, uint16_t, address_space_lduw_be)
476 LD_EA(lhuea_le, uint16_t, address_space_lduw_le)
477 LD_EA(lwea_be, uint32_t, address_space_ldl_be)
478 LD_EA(lwea_le, uint32_t, address_space_ldl_le)
479
480 #define ST_EA(NAME, TYPE, FUNC) \
481 void HELPER(NAME)(CPUMBState *env, uint32_t data, uint64_t ea) \
482 { \
483 CPUState *cs = env_cpu(env); \
484 MemTxResult txres; \
485 FUNC(cs->as, ea, data, MEMTXATTRS_UNSPECIFIED, &txres); \
486 if (unlikely(txres != MEMTX_OK)) { \
487 mb_transaction_failed_internal(cs, ea, ea, sizeof(TYPE), \
488 MMU_DATA_STORE, GETPC()); \
489 } \
490 }
491
492 ST_EA(sbea, uint8_t, address_space_stb)
493 ST_EA(shea_be, uint16_t, address_space_stw_be)
494 ST_EA(shea_le, uint16_t, address_space_stw_le)
495 ST_EA(swea_be, uint32_t, address_space_stl_be)
496 ST_EA(swea_le, uint32_t, address_space_stl_le)
497
498 #endif