xref: /openbmc/qemu/target/m68k/op_helper.c (revision 4fad446b)
1 /*
2  *  M68K helper routines
3  *
4  *  Copyright (c) 2007 CodeSourcery
5  *
6  * This library is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU Lesser General Public
8  * License as published by the Free Software Foundation; either
9  * version 2 of the License, or (at your option) any later version.
10  *
11  * This library is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14  * Lesser General Public License for more details.
15  *
16  * You should have received a copy of the GNU Lesser General Public
17  * License along with this library; if not, see <http://www.gnu.org/licenses/>.
18  */
19 #include "qemu/osdep.h"
20 #include "cpu.h"
21 #include "exec/helper-proto.h"
22 #include "exec/exec-all.h"
23 #include "exec/cpu_ldst.h"
24 #include "exec/semihost.h"
25 
26 #if defined(CONFIG_USER_ONLY)
27 
28 void m68k_cpu_do_interrupt(CPUState *cs)
29 {
30     cs->exception_index = -1;
31 }
32 
33 static inline void do_interrupt_m68k_hardirq(CPUM68KState *env)
34 {
35 }
36 
37 #else
38 
39 /* Try to fill the TLB and return an exception if error. If retaddr is
40    NULL, it means that the function was called in C code (i.e. not
41    from generated code or from helper.c) */
42 void tlb_fill(CPUState *cs, target_ulong addr, MMUAccessType access_type,
43               int mmu_idx, uintptr_t retaddr)
44 {
45     int ret;
46 
47     ret = m68k_cpu_handle_mmu_fault(cs, addr, access_type, mmu_idx);
48     if (unlikely(ret)) {
49         /* now we have a real cpu fault */
50         cpu_loop_exit_restore(cs, retaddr);
51     }
52 }
53 
54 static void do_rte(CPUM68KState *env)
55 {
56     uint32_t sp;
57     uint32_t fmt;
58 
59     sp = env->aregs[7];
60     fmt = cpu_ldl_kernel(env, sp);
61     env->pc = cpu_ldl_kernel(env, sp + 4);
62     sp |= (fmt >> 28) & 3;
63     env->aregs[7] = sp + 8;
64 
65     helper_set_sr(env, fmt);
66 }
67 
68 static void do_interrupt_all(CPUM68KState *env, int is_hw)
69 {
70     CPUState *cs = CPU(m68k_env_get_cpu(env));
71     uint32_t sp;
72     uint32_t fmt;
73     uint32_t retaddr;
74     uint32_t vector;
75 
76     fmt = 0;
77     retaddr = env->pc;
78 
79     if (!is_hw) {
80         switch (cs->exception_index) {
81         case EXCP_RTE:
82             /* Return from an exception.  */
83             do_rte(env);
84             return;
85         case EXCP_HALT_INSN:
86             if (semihosting_enabled()
87                     && (env->sr & SR_S) != 0
88                     && (env->pc & 3) == 0
89                     && cpu_lduw_code(env, env->pc - 4) == 0x4e71
90                     && cpu_ldl_code(env, env->pc) == 0x4e7bf000) {
91                 env->pc += 4;
92                 do_m68k_semihosting(env, env->dregs[0]);
93                 return;
94             }
95             cs->halted = 1;
96             cs->exception_index = EXCP_HLT;
97             cpu_loop_exit(cs);
98             return;
99         }
100         if (cs->exception_index >= EXCP_TRAP0
101             && cs->exception_index <= EXCP_TRAP15) {
102             /* Move the PC after the trap instruction.  */
103             retaddr += 2;
104         }
105     }
106 
107     vector = cs->exception_index << 2;
108 
109     fmt |= 0x40000000;
110     fmt |= vector << 16;
111     fmt |= env->sr;
112     fmt |= cpu_m68k_get_ccr(env);
113 
114     env->sr |= SR_S;
115     if (is_hw) {
116         env->sr = (env->sr & ~SR_I) | (env->pending_level << SR_I_SHIFT);
117         env->sr &= ~SR_M;
118     }
119     m68k_switch_sp(env);
120     sp = env->aregs[7];
121     fmt |= (sp & 3) << 28;
122 
123     /* ??? This could cause MMU faults.  */
124     sp &= ~3;
125     sp -= 4;
126     cpu_stl_kernel(env, sp, retaddr);
127     sp -= 4;
128     cpu_stl_kernel(env, sp, fmt);
129     env->aregs[7] = sp;
130     /* Jump to vector.  */
131     env->pc = cpu_ldl_kernel(env, env->vbr + vector);
132 }
133 
134 void m68k_cpu_do_interrupt(CPUState *cs)
135 {
136     M68kCPU *cpu = M68K_CPU(cs);
137     CPUM68KState *env = &cpu->env;
138 
139     do_interrupt_all(env, 0);
140 }
141 
142 static inline void do_interrupt_m68k_hardirq(CPUM68KState *env)
143 {
144     do_interrupt_all(env, 1);
145 }
146 #endif
147 
148 bool m68k_cpu_exec_interrupt(CPUState *cs, int interrupt_request)
149 {
150     M68kCPU *cpu = M68K_CPU(cs);
151     CPUM68KState *env = &cpu->env;
152 
153     if (interrupt_request & CPU_INTERRUPT_HARD
154         && ((env->sr & SR_I) >> SR_I_SHIFT) < env->pending_level) {
155         /* Real hardware gets the interrupt vector via an IACK cycle
156            at this point.  Current emulated hardware doesn't rely on
157            this, so we provide/save the vector when the interrupt is
158            first signalled.  */
159         cs->exception_index = env->pending_vector;
160         do_interrupt_m68k_hardirq(env);
161         return true;
162     }
163     return false;
164 }
165 
166 static void raise_exception_ra(CPUM68KState *env, int tt, uintptr_t raddr)
167 {
168     CPUState *cs = CPU(m68k_env_get_cpu(env));
169 
170     cs->exception_index = tt;
171     cpu_loop_exit_restore(cs, raddr);
172 }
173 
174 static void raise_exception(CPUM68KState *env, int tt)
175 {
176     raise_exception_ra(env, tt, 0);
177 }
178 
179 void HELPER(raise_exception)(CPUM68KState *env, uint32_t tt)
180 {
181     raise_exception(env, tt);
182 }
183 
184 void HELPER(divuw)(CPUM68KState *env, int destr, uint32_t den)
185 {
186     uint32_t num = env->dregs[destr];
187     uint32_t quot, rem;
188 
189     if (den == 0) {
190         raise_exception_ra(env, EXCP_DIV0, GETPC());
191     }
192     quot = num / den;
193     rem = num % den;
194 
195     env->cc_c = 0; /* always cleared, even if overflow */
196     if (quot > 0xffff) {
197         env->cc_v = -1;
198         /* real 68040 keeps N and unset Z on overflow,
199          * whereas documentation says "undefined"
200          */
201         env->cc_z = 1;
202         return;
203     }
204     env->dregs[destr] = deposit32(quot, 16, 16, rem);
205     env->cc_z = (int16_t)quot;
206     env->cc_n = (int16_t)quot;
207     env->cc_v = 0;
208 }
209 
210 void HELPER(divsw)(CPUM68KState *env, int destr, int32_t den)
211 {
212     int32_t num = env->dregs[destr];
213     uint32_t quot, rem;
214 
215     if (den == 0) {
216         raise_exception_ra(env, EXCP_DIV0, GETPC());
217     }
218     quot = num / den;
219     rem = num % den;
220 
221     env->cc_c = 0; /* always cleared, even if overflow */
222     if (quot != (int16_t)quot) {
223         env->cc_v = -1;
224         /* nothing else is modified */
225         /* real 68040 keeps N and unset Z on overflow,
226          * whereas documentation says "undefined"
227          */
228         env->cc_z = 1;
229         return;
230     }
231     env->dregs[destr] = deposit32(quot, 16, 16, rem);
232     env->cc_z = (int16_t)quot;
233     env->cc_n = (int16_t)quot;
234     env->cc_v = 0;
235 }
236 
237 void HELPER(divul)(CPUM68KState *env, int numr, int regr, uint32_t den)
238 {
239     uint32_t num = env->dregs[numr];
240     uint32_t quot, rem;
241 
242     if (den == 0) {
243         raise_exception_ra(env, EXCP_DIV0, GETPC());
244     }
245     quot = num / den;
246     rem = num % den;
247 
248     env->cc_c = 0;
249     env->cc_z = quot;
250     env->cc_n = quot;
251     env->cc_v = 0;
252 
253     if (m68k_feature(env, M68K_FEATURE_CF_ISA_A)) {
254         if (numr == regr) {
255             env->dregs[numr] = quot;
256         } else {
257             env->dregs[regr] = rem;
258         }
259     } else {
260         env->dregs[regr] = rem;
261         env->dregs[numr] = quot;
262     }
263 }
264 
265 void HELPER(divsl)(CPUM68KState *env, int numr, int regr, int32_t den)
266 {
267     int32_t num = env->dregs[numr];
268     int32_t quot, rem;
269 
270     if (den == 0) {
271         raise_exception_ra(env, EXCP_DIV0, GETPC());
272     }
273     quot = num / den;
274     rem = num % den;
275 
276     env->cc_c = 0;
277     env->cc_z = quot;
278     env->cc_n = quot;
279     env->cc_v = 0;
280 
281     if (m68k_feature(env, M68K_FEATURE_CF_ISA_A)) {
282         if (numr == regr) {
283             env->dregs[numr] = quot;
284         } else {
285             env->dregs[regr] = rem;
286         }
287     } else {
288         env->dregs[regr] = rem;
289         env->dregs[numr] = quot;
290     }
291 }
292 
293 void HELPER(divull)(CPUM68KState *env, int numr, int regr, uint32_t den)
294 {
295     uint64_t num = deposit64(env->dregs[numr], 32, 32, env->dregs[regr]);
296     uint64_t quot;
297     uint32_t rem;
298 
299     if (den == 0) {
300         raise_exception_ra(env, EXCP_DIV0, GETPC());
301     }
302     quot = num / den;
303     rem = num % den;
304 
305     env->cc_c = 0; /* always cleared, even if overflow */
306     if (quot > 0xffffffffULL) {
307         env->cc_v = -1;
308         /* real 68040 keeps N and unset Z on overflow,
309          * whereas documentation says "undefined"
310          */
311         env->cc_z = 1;
312         return;
313     }
314     env->cc_z = quot;
315     env->cc_n = quot;
316     env->cc_v = 0;
317 
318     /*
319      * If Dq and Dr are the same, the quotient is returned.
320      * therefore we set Dq last.
321      */
322 
323     env->dregs[regr] = rem;
324     env->dregs[numr] = quot;
325 }
326 
327 void HELPER(divsll)(CPUM68KState *env, int numr, int regr, int32_t den)
328 {
329     int64_t num = deposit64(env->dregs[numr], 32, 32, env->dregs[regr]);
330     int64_t quot;
331     int32_t rem;
332 
333     if (den == 0) {
334         raise_exception_ra(env, EXCP_DIV0, GETPC());
335     }
336     quot = num / den;
337     rem = num % den;
338 
339     env->cc_c = 0; /* always cleared, even if overflow */
340     if (quot != (int32_t)quot) {
341         env->cc_v = -1;
342         /* real 68040 keeps N and unset Z on overflow,
343          * whereas documentation says "undefined"
344          */
345         env->cc_z = 1;
346         return;
347     }
348     env->cc_z = quot;
349     env->cc_n = quot;
350     env->cc_v = 0;
351 
352     /*
353      * If Dq and Dr are the same, the quotient is returned.
354      * therefore we set Dq last.
355      */
356 
357     env->dregs[regr] = rem;
358     env->dregs[numr] = quot;
359 }
360 
361 /* We're executing in a serial context -- no need to be atomic.  */
362 void HELPER(cas2w)(CPUM68KState *env, uint32_t regs, uint32_t a1, uint32_t a2)
363 {
364     uint32_t Dc1 = extract32(regs, 9, 3);
365     uint32_t Dc2 = extract32(regs, 6, 3);
366     uint32_t Du1 = extract32(regs, 3, 3);
367     uint32_t Du2 = extract32(regs, 0, 3);
368     int16_t c1 = env->dregs[Dc1];
369     int16_t c2 = env->dregs[Dc2];
370     int16_t u1 = env->dregs[Du1];
371     int16_t u2 = env->dregs[Du2];
372     int16_t l1, l2;
373     uintptr_t ra = GETPC();
374 
375     l1 = cpu_lduw_data_ra(env, a1, ra);
376     l2 = cpu_lduw_data_ra(env, a2, ra);
377     if (l1 == c1 && l2 == c2) {
378         cpu_stw_data_ra(env, a1, u1, ra);
379         cpu_stw_data_ra(env, a2, u2, ra);
380     }
381 
382     if (c1 != l1) {
383         env->cc_n = l1;
384         env->cc_v = c1;
385     } else {
386         env->cc_n = l2;
387         env->cc_v = c2;
388     }
389     env->cc_op = CC_OP_CMPW;
390     env->dregs[Dc1] = deposit32(env->dregs[Dc1], 0, 16, l1);
391     env->dregs[Dc2] = deposit32(env->dregs[Dc2], 0, 16, l2);
392 }
393 
394 static void do_cas2l(CPUM68KState *env, uint32_t regs, uint32_t a1, uint32_t a2,
395                      bool parallel)
396 {
397     uint32_t Dc1 = extract32(regs, 9, 3);
398     uint32_t Dc2 = extract32(regs, 6, 3);
399     uint32_t Du1 = extract32(regs, 3, 3);
400     uint32_t Du2 = extract32(regs, 0, 3);
401     uint32_t c1 = env->dregs[Dc1];
402     uint32_t c2 = env->dregs[Dc2];
403     uint32_t u1 = env->dregs[Du1];
404     uint32_t u2 = env->dregs[Du2];
405     uint32_t l1, l2;
406     uintptr_t ra = GETPC();
407 #if defined(CONFIG_ATOMIC64) && !defined(CONFIG_USER_ONLY)
408     int mmu_idx = cpu_mmu_index(env, 0);
409     TCGMemOpIdx oi;
410 #endif
411 
412     if (parallel) {
413         /* We're executing in a parallel context -- must be atomic.  */
414 #ifdef CONFIG_ATOMIC64
415         uint64_t c, u, l;
416         if ((a1 & 7) == 0 && a2 == a1 + 4) {
417             c = deposit64(c2, 32, 32, c1);
418             u = deposit64(u2, 32, 32, u1);
419 #ifdef CONFIG_USER_ONLY
420             l = helper_atomic_cmpxchgq_be(env, a1, c, u);
421 #else
422             oi = make_memop_idx(MO_BEQ, mmu_idx);
423             l = helper_atomic_cmpxchgq_be_mmu(env, a1, c, u, oi, ra);
424 #endif
425             l1 = l >> 32;
426             l2 = l;
427         } else if ((a2 & 7) == 0 && a1 == a2 + 4) {
428             c = deposit64(c1, 32, 32, c2);
429             u = deposit64(u1, 32, 32, u2);
430 #ifdef CONFIG_USER_ONLY
431             l = helper_atomic_cmpxchgq_be(env, a2, c, u);
432 #else
433             oi = make_memop_idx(MO_BEQ, mmu_idx);
434             l = helper_atomic_cmpxchgq_be_mmu(env, a2, c, u, oi, ra);
435 #endif
436             l2 = l >> 32;
437             l1 = l;
438         } else
439 #endif
440         {
441             /* Tell the main loop we need to serialize this insn.  */
442             cpu_loop_exit_atomic(ENV_GET_CPU(env), ra);
443         }
444     } else {
445         /* We're executing in a serial context -- no need to be atomic.  */
446         l1 = cpu_ldl_data_ra(env, a1, ra);
447         l2 = cpu_ldl_data_ra(env, a2, ra);
448         if (l1 == c1 && l2 == c2) {
449             cpu_stl_data_ra(env, a1, u1, ra);
450             cpu_stl_data_ra(env, a2, u2, ra);
451         }
452     }
453 
454     if (c1 != l1) {
455         env->cc_n = l1;
456         env->cc_v = c1;
457     } else {
458         env->cc_n = l2;
459         env->cc_v = c2;
460     }
461     env->cc_op = CC_OP_CMPL;
462     env->dregs[Dc1] = l1;
463     env->dregs[Dc2] = l2;
464 }
465 
466 void HELPER(cas2l)(CPUM68KState *env, uint32_t regs, uint32_t a1, uint32_t a2)
467 {
468     do_cas2l(env, regs, a1, a2, false);
469 }
470 
471 void HELPER(cas2l_parallel)(CPUM68KState *env, uint32_t regs, uint32_t a1,
472                             uint32_t a2)
473 {
474     do_cas2l(env, regs, a1, a2, true);
475 }
476 
477 struct bf_data {
478     uint32_t addr;
479     uint32_t bofs;
480     uint32_t blen;
481     uint32_t len;
482 };
483 
484 static struct bf_data bf_prep(uint32_t addr, int32_t ofs, uint32_t len)
485 {
486     int bofs, blen;
487 
488     /* Bound length; map 0 to 32.  */
489     len = ((len - 1) & 31) + 1;
490 
491     /* Note that ofs is signed.  */
492     addr += ofs / 8;
493     bofs = ofs % 8;
494     if (bofs < 0) {
495         bofs += 8;
496         addr -= 1;
497     }
498 
499     /* Compute the number of bytes required (minus one) to
500        satisfy the bitfield.  */
501     blen = (bofs + len - 1) / 8;
502 
503     /* Canonicalize the bit offset for data loaded into a 64-bit big-endian
504        word.  For the cases where BLEN is not a power of 2, adjust ADDR so
505        that we can use the next power of two sized load without crossing a
506        page boundary, unless the field itself crosses the boundary.  */
507     switch (blen) {
508     case 0:
509         bofs += 56;
510         break;
511     case 1:
512         bofs += 48;
513         break;
514     case 2:
515         if (addr & 1) {
516             bofs += 8;
517             addr -= 1;
518         }
519         /* fallthru */
520     case 3:
521         bofs += 32;
522         break;
523     case 4:
524         if (addr & 3) {
525             bofs += 8 * (addr & 3);
526             addr &= -4;
527         }
528         break;
529     default:
530         g_assert_not_reached();
531     }
532 
533     return (struct bf_data){
534         .addr = addr,
535         .bofs = bofs,
536         .blen = blen,
537         .len = len,
538     };
539 }
540 
541 static uint64_t bf_load(CPUM68KState *env, uint32_t addr, int blen,
542                         uintptr_t ra)
543 {
544     switch (blen) {
545     case 0:
546         return cpu_ldub_data_ra(env, addr, ra);
547     case 1:
548         return cpu_lduw_data_ra(env, addr, ra);
549     case 2:
550     case 3:
551         return cpu_ldl_data_ra(env, addr, ra);
552     case 4:
553         return cpu_ldq_data_ra(env, addr, ra);
554     default:
555         g_assert_not_reached();
556     }
557 }
558 
559 static void bf_store(CPUM68KState *env, uint32_t addr, int blen,
560                      uint64_t data, uintptr_t ra)
561 {
562     switch (blen) {
563     case 0:
564         cpu_stb_data_ra(env, addr, data, ra);
565         break;
566     case 1:
567         cpu_stw_data_ra(env, addr, data, ra);
568         break;
569     case 2:
570     case 3:
571         cpu_stl_data_ra(env, addr, data, ra);
572         break;
573     case 4:
574         cpu_stq_data_ra(env, addr, data, ra);
575         break;
576     default:
577         g_assert_not_reached();
578     }
579 }
580 
581 uint32_t HELPER(bfexts_mem)(CPUM68KState *env, uint32_t addr,
582                             int32_t ofs, uint32_t len)
583 {
584     uintptr_t ra = GETPC();
585     struct bf_data d = bf_prep(addr, ofs, len);
586     uint64_t data = bf_load(env, d.addr, d.blen, ra);
587 
588     return (int64_t)(data << d.bofs) >> (64 - d.len);
589 }
590 
591 uint64_t HELPER(bfextu_mem)(CPUM68KState *env, uint32_t addr,
592                             int32_t ofs, uint32_t len)
593 {
594     uintptr_t ra = GETPC();
595     struct bf_data d = bf_prep(addr, ofs, len);
596     uint64_t data = bf_load(env, d.addr, d.blen, ra);
597 
598     /* Put CC_N at the top of the high word; put the zero-extended value
599        at the bottom of the low word.  */
600     data <<= d.bofs;
601     data >>= 64 - d.len;
602     data |= data << (64 - d.len);
603 
604     return data;
605 }
606 
607 uint32_t HELPER(bfins_mem)(CPUM68KState *env, uint32_t addr, uint32_t val,
608                            int32_t ofs, uint32_t len)
609 {
610     uintptr_t ra = GETPC();
611     struct bf_data d = bf_prep(addr, ofs, len);
612     uint64_t data = bf_load(env, d.addr, d.blen, ra);
613     uint64_t mask = -1ull << (64 - d.len) >> d.bofs;
614 
615     data = (data & ~mask) | (((uint64_t)val << (64 - d.len)) >> d.bofs);
616 
617     bf_store(env, d.addr, d.blen, data, ra);
618 
619     /* The field at the top of the word is also CC_N for CC_OP_LOGIC.  */
620     return val << (32 - d.len);
621 }
622 
623 uint32_t HELPER(bfchg_mem)(CPUM68KState *env, uint32_t addr,
624                            int32_t ofs, uint32_t len)
625 {
626     uintptr_t ra = GETPC();
627     struct bf_data d = bf_prep(addr, ofs, len);
628     uint64_t data = bf_load(env, d.addr, d.blen, ra);
629     uint64_t mask = -1ull << (64 - d.len) >> d.bofs;
630 
631     bf_store(env, d.addr, d.blen, data ^ mask, ra);
632 
633     return ((data & mask) << d.bofs) >> 32;
634 }
635 
636 uint32_t HELPER(bfclr_mem)(CPUM68KState *env, uint32_t addr,
637                            int32_t ofs, uint32_t len)
638 {
639     uintptr_t ra = GETPC();
640     struct bf_data d = bf_prep(addr, ofs, len);
641     uint64_t data = bf_load(env, d.addr, d.blen, ra);
642     uint64_t mask = -1ull << (64 - d.len) >> d.bofs;
643 
644     bf_store(env, d.addr, d.blen, data & ~mask, ra);
645 
646     return ((data & mask) << d.bofs) >> 32;
647 }
648 
649 uint32_t HELPER(bfset_mem)(CPUM68KState *env, uint32_t addr,
650                            int32_t ofs, uint32_t len)
651 {
652     uintptr_t ra = GETPC();
653     struct bf_data d = bf_prep(addr, ofs, len);
654     uint64_t data = bf_load(env, d.addr, d.blen, ra);
655     uint64_t mask = -1ull << (64 - d.len) >> d.bofs;
656 
657     bf_store(env, d.addr, d.blen, data | mask, ra);
658 
659     return ((data & mask) << d.bofs) >> 32;
660 }
661 
662 uint32_t HELPER(bfffo_reg)(uint32_t n, uint32_t ofs, uint32_t len)
663 {
664     return (n ? clz32(n) : len) + ofs;
665 }
666 
667 uint64_t HELPER(bfffo_mem)(CPUM68KState *env, uint32_t addr,
668                            int32_t ofs, uint32_t len)
669 {
670     uintptr_t ra = GETPC();
671     struct bf_data d = bf_prep(addr, ofs, len);
672     uint64_t data = bf_load(env, d.addr, d.blen, ra);
673     uint64_t mask = -1ull << (64 - d.len) >> d.bofs;
674     uint64_t n = (data & mask) << d.bofs;
675     uint32_t ffo = helper_bfffo_reg(n >> 32, ofs, d.len);
676 
677     /* Return FFO in the low word and N in the high word.
678        Note that because of MASK and the shift, the low word
679        is already zero.  */
680     return n | ffo;
681 }
682