xref: /openbmc/qemu/target/cris/op_helper.c (revision 0274bd7b)
1 /*
2  *  CRIS helper routines
3  *
4  *  Copyright (c) 2007 AXIS Communications
5  *  Written by Edgar E. Iglesias
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 "cpu.h"
23 #include "mmu.h"
24 #include "exec/helper-proto.h"
25 #include "qemu/host-utils.h"
26 #include "exec/exec-all.h"
27 
28 //#define CRIS_OP_HELPER_DEBUG
29 
30 
31 #ifdef CRIS_OP_HELPER_DEBUG
32 #define D(x) x
33 #define D_LOG(...) qemu_log(__VA_ARGS__)
34 #else
35 #define D(x)
36 #define D_LOG(...) do { } while (0)
37 #endif
38 
39 void helper_raise_exception(CPUCRISState *env, uint32_t index)
40 {
41     CPUState *cs = env_cpu(env);
42 
43     cs->exception_index = index;
44     cpu_loop_exit(cs);
45 }
46 
47 void helper_tlb_flush_pid(CPUCRISState *env, uint32_t pid)
48 {
49 #if !defined(CONFIG_USER_ONLY)
50     pid &= 0xff;
51     if (pid != (env->pregs[PR_PID] & 0xff)) {
52         cris_mmu_flush_pid(env, env->pregs[PR_PID]);
53     }
54 #endif
55 }
56 
57 void helper_spc_write(CPUCRISState *env, uint32_t new_spc)
58 {
59 #if !defined(CONFIG_USER_ONLY)
60     CPUState *cs = env_cpu(env);
61 
62     tlb_flush_page(cs, env->pregs[PR_SPC]);
63     tlb_flush_page(cs, new_spc);
64 #endif
65 }
66 
67 /* Used by the tlb decoder.  */
68 #define EXTRACT_FIELD(src, start, end)                  \
69     (((src) >> start) & ((1 << (end - start + 1)) - 1))
70 
71 void helper_movl_sreg_reg(CPUCRISState *env, uint32_t sreg, uint32_t reg)
72 {
73     uint32_t srs;
74     srs = env->pregs[PR_SRS];
75     srs &= 3;
76     env->sregs[srs][sreg] = env->regs[reg];
77 
78 #if !defined(CONFIG_USER_ONLY)
79     if (srs == 1 || srs == 2) {
80         if (sreg == 6) {
81             /* Writes to tlb-hi write to mm_cause as a side effect.  */
82             env->sregs[SFR_RW_MM_TLB_HI] = env->regs[reg];
83             env->sregs[SFR_R_MM_CAUSE] = env->regs[reg];
84         } else if (sreg == 5) {
85             uint32_t set;
86             uint32_t idx;
87             uint32_t lo, hi;
88             uint32_t vaddr;
89             int tlb_v;
90 
91             idx = set = env->sregs[SFR_RW_MM_TLB_SEL];
92             set >>= 4;
93             set &= 3;
94 
95             idx &= 15;
96             /* We've just made a write to tlb_lo.  */
97             lo = env->sregs[SFR_RW_MM_TLB_LO];
98             /* Writes are done via r_mm_cause.  */
99             hi = env->sregs[SFR_R_MM_CAUSE];
100 
101             vaddr = EXTRACT_FIELD(env->tlbsets[srs - 1][set][idx].hi, 13, 31);
102             vaddr <<= TARGET_PAGE_BITS;
103             tlb_v = EXTRACT_FIELD(env->tlbsets[srs - 1][set][idx].lo, 3, 3);
104             env->tlbsets[srs - 1][set][idx].lo = lo;
105             env->tlbsets[srs - 1][set][idx].hi = hi;
106 
107             D_LOG("tlb flush vaddr=%x v=%d pc=%x\n",
108                   vaddr, tlb_v, env->pc);
109             if (tlb_v) {
110                 tlb_flush_page(env_cpu(env), vaddr);
111             }
112         }
113     }
114 #endif
115 }
116 
117 void helper_movl_reg_sreg(CPUCRISState *env, uint32_t reg, uint32_t sreg)
118 {
119     uint32_t srs;
120     env->pregs[PR_SRS] &= 3;
121     srs = env->pregs[PR_SRS];
122 
123 #if !defined(CONFIG_USER_ONLY)
124     if (srs == 1 || srs == 2) {
125         uint32_t set;
126         uint32_t idx;
127         uint32_t lo, hi;
128 
129         idx = set = env->sregs[SFR_RW_MM_TLB_SEL];
130         set >>= 4;
131         set &= 3;
132         idx &= 15;
133 
134         /* Update the mirror regs.  */
135         hi = env->tlbsets[srs - 1][set][idx].hi;
136         lo = env->tlbsets[srs - 1][set][idx].lo;
137         env->sregs[SFR_RW_MM_TLB_HI] = hi;
138         env->sregs[SFR_RW_MM_TLB_LO] = lo;
139     }
140 #endif
141     env->regs[reg] = env->sregs[srs][sreg];
142 }
143 
144 static void cris_ccs_rshift(CPUCRISState *env)
145 {
146     uint32_t ccs;
147 
148     /* Apply the ccs shift.  */
149     ccs = env->pregs[PR_CCS];
150     ccs = (ccs & 0xc0000000) | ((ccs & 0x0fffffff) >> 10);
151     if (ccs & U_FLAG) {
152         /* Enter user mode.  */
153         env->ksp = env->regs[R_SP];
154         env->regs[R_SP] = env->pregs[PR_USP];
155     }
156 
157     env->pregs[PR_CCS] = ccs;
158 }
159 
160 void helper_rfe(CPUCRISState *env)
161 {
162     int rflag = env->pregs[PR_CCS] & R_FLAG;
163 
164     D_LOG("rfe: erp=%x pid=%x ccs=%x btarget=%x\n",
165           env->pregs[PR_ERP], env->pregs[PR_PID],
166           env->pregs[PR_CCS],
167           env->btarget);
168 
169     cris_ccs_rshift(env);
170 
171     /* RFE sets the P_FLAG only if the R_FLAG is not set.  */
172     if (!rflag) {
173         env->pregs[PR_CCS] |= P_FLAG;
174     }
175 }
176 
177 void helper_rfn(CPUCRISState *env)
178 {
179     int rflag = env->pregs[PR_CCS] & R_FLAG;
180 
181     D_LOG("rfn: erp=%x pid=%x ccs=%x btarget=%x\n",
182           env->pregs[PR_ERP], env->pregs[PR_PID],
183           env->pregs[PR_CCS],
184           env->btarget);
185 
186     cris_ccs_rshift(env);
187 
188     /* Set the P_FLAG only if the R_FLAG is not set.  */
189     if (!rflag) {
190         env->pregs[PR_CCS] |= P_FLAG;
191     }
192 
193     /* Always set the M flag.  */
194     env->pregs[PR_CCS] |= M_FLAG_V32;
195 }
196 
197 uint32_t helper_btst(CPUCRISState *env, uint32_t t0, uint32_t t1, uint32_t ccs)
198 {
199     /* FIXME: clean this up.  */
200 
201     /*
202      * des ref:
203      *  The N flag is set according to the selected bit in the dest reg.
204      *  The Z flag is set if the selected bit and all bits to the right are
205      *  zero.
206      *  The X flag is cleared.
207      *  Other flags are left untouched.
208      *  The destination reg is not affected.
209      */
210     unsigned int fz, sbit, bset, mask, masked_t0;
211 
212     sbit = t1 & 31;
213     bset = !!(t0 & (1 << sbit));
214     mask = sbit == 31 ? -1 : (1 << (sbit + 1)) - 1;
215     masked_t0 = t0 & mask;
216     fz = !(masked_t0 | bset);
217 
218     /* Clear the X, N and Z flags.  */
219     ccs = ccs & ~(X_FLAG | N_FLAG | Z_FLAG);
220     if (env->pregs[PR_VR] < 32) {
221         ccs &= ~(V_FLAG | C_FLAG);
222     }
223     /* Set the N and Z flags accordingly.  */
224     ccs |= (bset << 3) | (fz << 2);
225     return ccs;
226 }
227 
228 static inline uint32_t evaluate_flags_writeback(CPUCRISState *env,
229                                                 uint32_t flags, uint32_t ccs)
230 {
231     unsigned int x, z, mask;
232 
233     /* Extended arithmetic, leave the z flag alone.  */
234     x = env->cc_x;
235     mask = env->cc_mask | X_FLAG;
236     if (x) {
237         z = flags & Z_FLAG;
238         mask = mask & ~z;
239     }
240     flags &= mask;
241 
242     /* all insn clear the x-flag except setf or clrf.  */
243     ccs &= ~mask;
244     ccs |= flags;
245     return ccs;
246 }
247 
248 uint32_t helper_evaluate_flags_muls(CPUCRISState *env,
249                                     uint32_t ccs, uint32_t res, uint32_t mof)
250 {
251     uint32_t flags = 0;
252     int64_t tmp;
253     int dneg;
254 
255     dneg = ((int32_t)res) < 0;
256 
257     tmp = mof;
258     tmp <<= 32;
259     tmp |= res;
260     if (tmp == 0) {
261         flags |= Z_FLAG;
262     } else if (tmp < 0) {
263         flags |= N_FLAG;
264     }
265     if ((dneg && mof != -1) || (!dneg && mof != 0)) {
266         flags |= V_FLAG;
267     }
268     return evaluate_flags_writeback(env, flags, ccs);
269 }
270 
271 uint32_t helper_evaluate_flags_mulu(CPUCRISState *env,
272                                     uint32_t ccs, uint32_t res, uint32_t mof)
273 {
274     uint32_t flags = 0;
275     uint64_t tmp;
276 
277     tmp = mof;
278     tmp <<= 32;
279     tmp |= res;
280     if (tmp == 0) {
281         flags |= Z_FLAG;
282     } else if (tmp >> 63) {
283         flags |= N_FLAG;
284     }
285     if (mof) {
286         flags |= V_FLAG;
287     }
288 
289     return evaluate_flags_writeback(env, flags, ccs);
290 }
291 
292 uint32_t helper_evaluate_flags_mcp(CPUCRISState *env, uint32_t ccs,
293 				   uint32_t src, uint32_t dst, uint32_t res)
294 {
295     uint32_t flags = 0;
296 
297     src = src & 0x80000000;
298     dst = dst & 0x80000000;
299 
300     if ((res & 0x80000000L) != 0L) {
301         flags |= N_FLAG;
302         if (!src && !dst) {
303             flags |= V_FLAG;
304         } else if (src & dst) {
305             flags |= R_FLAG;
306         }
307     } else {
308         if (res == 0L) {
309             flags |= Z_FLAG;
310         }
311         if (src & dst) {
312             flags |= V_FLAG;
313         }
314         if (dst | src) {
315             flags |= R_FLAG;
316         }
317     }
318 
319     return evaluate_flags_writeback(env, flags, ccs);
320 }
321 
322 uint32_t helper_evaluate_flags_alu_4(CPUCRISState *env, uint32_t ccs,
323 				     uint32_t src, uint32_t dst, uint32_t res)
324 {
325     uint32_t flags = 0;
326 
327     src = src & 0x80000000;
328     dst = dst & 0x80000000;
329 
330     if ((res & 0x80000000L) != 0L) {
331         flags |= N_FLAG;
332         if (!src && !dst) {
333             flags |= V_FLAG;
334         } else if (src & dst) {
335             flags |= C_FLAG;
336         }
337     } else {
338         if (res == 0L) {
339             flags |= Z_FLAG;
340         }
341         if (src & dst) {
342             flags |= V_FLAG;
343         }
344         if (dst | src) {
345             flags |= C_FLAG;
346         }
347     }
348 
349     return evaluate_flags_writeback(env, flags, ccs);
350 }
351 
352 uint32_t helper_evaluate_flags_sub_4(CPUCRISState *env, uint32_t ccs,
353 				     uint32_t src, uint32_t dst, uint32_t res)
354 {
355     uint32_t flags = 0;
356 
357     src = (~src) & 0x80000000;
358     dst = dst & 0x80000000;
359 
360     if ((res & 0x80000000L) != 0L) {
361         flags |= N_FLAG;
362         if (!src && !dst) {
363             flags |= V_FLAG;
364         } else if (src & dst) {
365             flags |= C_FLAG;
366         }
367     } else {
368         if (res == 0L) {
369             flags |= Z_FLAG;
370         }
371         if (src & dst) {
372             flags |= V_FLAG;
373         }
374         if (dst | src) {
375             flags |= C_FLAG;
376         }
377     }
378 
379     flags ^= C_FLAG;
380     return evaluate_flags_writeback(env, flags, ccs);
381 }
382 
383 uint32_t helper_evaluate_flags_move_4(CPUCRISState *env,
384                                       uint32_t ccs, uint32_t res)
385 {
386     uint32_t flags = 0;
387 
388     if ((int32_t)res < 0) {
389         flags |= N_FLAG;
390     } else if (res == 0L) {
391         flags |= Z_FLAG;
392     }
393 
394     return evaluate_flags_writeback(env, flags, ccs);
395 }
396 
397 uint32_t helper_evaluate_flags_move_2(CPUCRISState *env,
398                                       uint32_t ccs, uint32_t res)
399 {
400     uint32_t flags = 0;
401 
402     if ((int16_t)res < 0L) {
403         flags |= N_FLAG;
404     } else if (res == 0) {
405         flags |= Z_FLAG;
406     }
407 
408     return evaluate_flags_writeback(env, flags, ccs);
409 }
410 
411 /*
412  * TODO: This is expensive. We could split things up and only evaluate part of
413  * CCR on a need to know basis. For now, we simply re-evaluate everything.
414  */
415 void helper_evaluate_flags(CPUCRISState *env)
416 {
417     uint32_t src, dst, res;
418     uint32_t flags = 0;
419 
420     src = env->cc_src;
421     dst = env->cc_dest;
422     res = env->cc_result;
423 
424     if (env->cc_op == CC_OP_SUB || env->cc_op == CC_OP_CMP) {
425         src = ~src;
426     }
427 
428     /*
429      * Now, evaluate the flags. This stuff is based on
430      * Per Zander's CRISv10 simulator.
431      */
432     switch (env->cc_size) {
433     case 1:
434         if ((res & 0x80L) != 0L) {
435             flags |= N_FLAG;
436             if (((src & 0x80L) == 0L) && ((dst & 0x80L) == 0L)) {
437                 flags |= V_FLAG;
438             } else if (((src & 0x80L) != 0L) && ((dst & 0x80L) != 0L)) {
439                 flags |= C_FLAG;
440             }
441         } else {
442             if ((res & 0xFFL) == 0L) {
443                 flags |= Z_FLAG;
444             }
445             if (((src & 0x80L) != 0L) && ((dst & 0x80L) != 0L)) {
446                 flags |= V_FLAG;
447             }
448             if ((dst & 0x80L) != 0L || (src & 0x80L) != 0L) {
449                 flags |= C_FLAG;
450             }
451         }
452         break;
453     case 2:
454         if ((res & 0x8000L) != 0L) {
455             flags |= N_FLAG;
456             if (((src & 0x8000L) == 0L) && ((dst & 0x8000L) == 0L)) {
457                 flags |= V_FLAG;
458             } else if (((src & 0x8000L) != 0L) && ((dst & 0x8000L) != 0L)) {
459                 flags |= C_FLAG;
460             }
461         } else {
462             if ((res & 0xFFFFL) == 0L) {
463                 flags |= Z_FLAG;
464             }
465             if (((src & 0x8000L) != 0L) && ((dst & 0x8000L) != 0L)) {
466                 flags |= V_FLAG;
467             }
468             if ((dst & 0x8000L) != 0L || (src & 0x8000L) != 0L) {
469                 flags |= C_FLAG;
470             }
471         }
472         break;
473     case 4:
474         if ((res & 0x80000000L) != 0L) {
475             flags |= N_FLAG;
476             if (((src & 0x80000000L) == 0L) && ((dst & 0x80000000L) == 0L)) {
477                 flags |= V_FLAG;
478             } else if (((src & 0x80000000L) != 0L) &&
479                        ((dst & 0x80000000L) != 0L)) {
480                 flags |= C_FLAG;
481             }
482         } else {
483             if (res == 0L) {
484                 flags |= Z_FLAG;
485             }
486             if (((src & 0x80000000L) != 0L) && ((dst & 0x80000000L) != 0L)) {
487                 flags |= V_FLAG;
488             }
489             if ((dst & 0x80000000L) != 0L || (src & 0x80000000L) != 0L) {
490                 flags |= C_FLAG;
491             }
492         }
493         break;
494     default:
495         break;
496     }
497 
498     if (env->cc_op == CC_OP_SUB || env->cc_op == CC_OP_CMP) {
499         flags ^= C_FLAG;
500     }
501 
502     env->pregs[PR_CCS] = evaluate_flags_writeback(env, flags,
503                                                   env->pregs[PR_CCS]);
504 }
505 
506 void helper_top_evaluate_flags(CPUCRISState *env)
507 {
508     switch (env->cc_op) {
509     case CC_OP_MCP:
510         env->pregs[PR_CCS]
511             = helper_evaluate_flags_mcp(env, env->pregs[PR_CCS],
512                                         env->cc_src, env->cc_dest,
513                                         env->cc_result);
514         break;
515     case CC_OP_MULS:
516         env->pregs[PR_CCS]
517             = helper_evaluate_flags_muls(env, env->pregs[PR_CCS],
518                                          env->cc_result, env->pregs[PR_MOF]);
519         break;
520     case CC_OP_MULU:
521         env->pregs[PR_CCS]
522             = helper_evaluate_flags_mulu(env, env->pregs[PR_CCS],
523                                          env->cc_result, env->pregs[PR_MOF]);
524         break;
525     case CC_OP_MOVE:
526     case CC_OP_AND:
527     case CC_OP_OR:
528     case CC_OP_XOR:
529     case CC_OP_ASR:
530     case CC_OP_LSR:
531     case CC_OP_LSL:
532         switch (env->cc_size) {
533         case 4:
534             env->pregs[PR_CCS] =
535                 helper_evaluate_flags_move_4(env,
536                                              env->pregs[PR_CCS],
537                                              env->cc_result);
538             break;
539         case 2:
540             env->pregs[PR_CCS] =
541                 helper_evaluate_flags_move_2(env,
542                                              env->pregs[PR_CCS],
543                                              env->cc_result);
544             break;
545         default:
546             helper_evaluate_flags(env);
547             break;
548         }
549         break;
550     case CC_OP_FLAGS:
551         /* live.  */
552         break;
553     case CC_OP_SUB:
554     case CC_OP_CMP:
555         if (env->cc_size == 4) {
556             env->pregs[PR_CCS] =
557                 helper_evaluate_flags_sub_4(env,
558                                             env->pregs[PR_CCS],
559                                             env->cc_src, env->cc_dest,
560                                             env->cc_result);
561         } else {
562             helper_evaluate_flags(env);
563         }
564         break;
565     default:
566         switch (env->cc_size) {
567         case 4:
568             env->pregs[PR_CCS] =
569                 helper_evaluate_flags_alu_4(env,
570                                             env->pregs[PR_CCS],
571                                             env->cc_src, env->cc_dest,
572                                             env->cc_result);
573             break;
574         default:
575             helper_evaluate_flags(env);
576             break;
577         }
578         break;
579     }
580 }
581