More generic 64 bit multiplication support, by Aurelien Jarno.
[qemu] / target-mips / op_helper.c
1 /*
2  *  MIPS emulation helpers for qemu.
3  * 
4  *  Copyright (c) 2004-2005 Jocelyn Mayer
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, write to the Free Software
18  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
19  */
20 #include <stdlib.h>
21 #include "exec.h"
22
23 #define GETPC() (__builtin_return_address(0))
24
25 /*****************************************************************************/
26 /* Exceptions processing helpers */
27 void cpu_loop_exit(void)
28 {
29     longjmp(env->jmp_env, 1);
30 }
31
32 void do_raise_exception_err (uint32_t exception, int error_code)
33 {
34 #if 1
35     if (logfile && exception < 0x100)
36         fprintf(logfile, "%s: %d %d\n", __func__, exception, error_code);
37 #endif
38     env->exception_index = exception;
39     env->error_code = error_code;
40     T0 = 0;
41     cpu_loop_exit();
42 }
43
44 void do_raise_exception (uint32_t exception)
45 {
46     do_raise_exception_err(exception, 0);
47 }
48
49 void do_restore_state (void *pc_ptr)
50 {
51   TranslationBlock *tb;
52   unsigned long pc = (unsigned long) pc_ptr;
53
54   tb = tb_find_pc (pc);
55   cpu_restore_state (tb, env, pc, NULL);
56 }
57
58 void do_raise_exception_direct_err (uint32_t exception, int error_code)
59 {
60     do_restore_state (GETPC ());
61     do_raise_exception_err (exception, error_code);
62 }
63
64 void do_raise_exception_direct (uint32_t exception)
65 {
66     do_raise_exception_direct_err (exception, 0);
67 }
68
69 #define MEMSUFFIX _raw
70 #include "op_helper_mem.c"
71 #undef MEMSUFFIX
72 #if !defined(CONFIG_USER_ONLY)
73 #define MEMSUFFIX _user
74 #include "op_helper_mem.c"
75 #undef MEMSUFFIX
76 #define MEMSUFFIX _kernel
77 #include "op_helper_mem.c"
78 #undef MEMSUFFIX
79 #endif
80
81 #ifdef TARGET_MIPS64
82 #if TARGET_LONG_BITS > HOST_LONG_BITS
83 /* Those might call libgcc functions.  */
84 void do_dsll (void)
85 {
86     T0 = T0 << T1;
87 }
88
89 void do_dsll32 (void)
90 {
91     T0 = T0 << (T1 + 32);
92 }
93
94 void do_dsra (void)
95 {
96     T0 = (int64_t)T0 >> T1;
97 }
98
99 void do_dsra32 (void)
100 {
101     T0 = (int64_t)T0 >> (T1 + 32);
102 }
103
104 void do_dsrl (void)
105 {
106     T0 = T0 >> T1;
107 }
108
109 void do_dsrl32 (void)
110 {
111     T0 = T0 >> (T1 + 32);
112 }
113
114 void do_drotr (void)
115 {
116     target_ulong tmp;
117
118     if (T1) {
119        tmp = T0 << (0x40 - T1);
120        T0 = (T0 >> T1) | tmp;
121     }
122 }
123
124 void do_drotr32 (void)
125 {
126     target_ulong tmp;
127
128     if (T1) {
129        tmp = T0 << (0x40 - (32 + T1));
130        T0 = (T0 >> (32 + T1)) | tmp;
131     }
132 }
133
134 void do_dsllv (void)
135 {
136     T0 = T1 << (T0 & 0x3F);
137 }
138
139 void do_dsrav (void)
140 {
141     T0 = (int64_t)T1 >> (T0 & 0x3F);
142 }
143
144 void do_dsrlv (void)
145 {
146     T0 = T1 >> (T0 & 0x3F);
147 }
148
149 void do_drotrv (void)
150 {
151     target_ulong tmp;
152
153     T0 &= 0x3F;
154     if (T0) {
155        tmp = T1 << (0x40 - T0);
156        T0 = (T1 >> T0) | tmp;
157     } else
158        T0 = T1;
159 }
160 #endif /* TARGET_LONG_BITS > HOST_LONG_BITS */
161 #endif /* TARGET_MIPS64 */
162
163 /* 64 bits arithmetic for 32 bits hosts */
164 #if TARGET_LONG_BITS > HOST_LONG_BITS
165 static inline uint64_t get_HILO (void)
166 {
167     return (env->HI << 32) | (uint32_t)env->LO;
168 }
169
170 static inline void set_HILO (uint64_t HILO)
171 {
172     env->LO = (int32_t)HILO;
173     env->HI = (int32_t)(HILO >> 32);
174 }
175
176 void do_mult (void)
177 {
178     set_HILO((int64_t)(int32_t)T0 * (int64_t)(int32_t)T1);
179 }
180
181 void do_multu (void)
182 {
183     set_HILO((uint64_t)(uint32_t)T0 * (uint64_t)(uint32_t)T1);
184 }
185
186 void do_madd (void)
187 {
188     int64_t tmp;
189
190     tmp = ((int64_t)(int32_t)T0 * (int64_t)(int32_t)T1);
191     set_HILO((int64_t)get_HILO() + tmp);
192 }
193
194 void do_maddu (void)
195 {
196     uint64_t tmp;
197
198     tmp = ((uint64_t)(uint32_t)T0 * (uint64_t)(uint32_t)T1);
199     set_HILO(get_HILO() + tmp);
200 }
201
202 void do_msub (void)
203 {
204     int64_t tmp;
205
206     tmp = ((int64_t)(int32_t)T0 * (int64_t)(int32_t)T1);
207     set_HILO((int64_t)get_HILO() - tmp);
208 }
209
210 void do_msubu (void)
211 {
212     uint64_t tmp;
213
214     tmp = ((uint64_t)(uint32_t)T0 * (uint64_t)(uint32_t)T1);
215     set_HILO(get_HILO() - tmp);
216 }
217 #endif
218
219 #if HOST_LONG_BITS < 64
220 void do_div (void)
221 {
222     /* 64bit datatypes because we may see overflow/underflow. */
223     if (T1 != 0) {
224         env->LO = (int32_t)((int64_t)(int32_t)T0 / (int32_t)T1);
225         env->HI = (int32_t)((int64_t)(int32_t)T0 % (int32_t)T1);
226     }
227 }
228 #endif
229
230 #ifdef TARGET_MIPS64
231 void do_ddiv (void)
232 {
233     if (T1 != 0) {
234         lldiv_t res = lldiv((int64_t)T0, (int64_t)T1);
235         env->LO = res.quot;
236         env->HI = res.rem;
237     }
238 }
239
240 void do_ddivu (void)
241 {
242     if (T1 != 0) {
243         /* XXX: lldivu? */
244         lldiv_t res = lldiv(T0, T1);
245         env->LO = (uint64_t)res.quot;
246         env->HI = (uint64_t)res.rem;
247     }
248 }
249 #endif
250
251 #if defined(CONFIG_USER_ONLY) 
252 void do_mfc0_random (void)
253 {
254     cpu_abort(env, "mfc0 random\n");
255 }
256
257 void do_mfc0_count (void)
258 {
259     cpu_abort(env, "mfc0 count\n");
260 }
261
262 void cpu_mips_store_count(CPUState *env, uint32_t value)
263 {
264     cpu_abort(env, "mtc0 count\n");
265 }
266
267 void cpu_mips_store_compare(CPUState *env, uint32_t value)
268 {
269     cpu_abort(env, "mtc0 compare\n");
270 }
271
272 void cpu_mips_update_irq(CPUState *env)
273 {
274     cpu_abort(env, "mtc0 status / mtc0 cause\n");
275 }
276
277 void do_mtc0_status_debug(uint32_t old, uint32_t val)
278 {
279     cpu_abort(env, "mtc0 status debug\n");
280 }
281
282 void do_mtc0_status_irqraise_debug (void)
283 {
284     cpu_abort(env, "mtc0 status irqraise debug\n");
285 }
286
287 void cpu_mips_tlb_flush (CPUState *env, int flush_global)
288 {
289     cpu_abort(env, "mips_tlb_flush\n");
290 }
291
292 #else
293
294 /* CP0 helpers */
295 void do_mfc0_random (void)
296 {
297     T0 = (int32_t)cpu_mips_get_random(env);
298 }
299
300 void do_mfc0_count (void)
301 {
302     T0 = (int32_t)cpu_mips_get_count(env);
303 }
304
305 void do_mtc0_status_debug(uint32_t old, uint32_t val)
306 {
307     fprintf(logfile, "Status %08x (%08x) => %08x (%08x) Cause %08x",
308             old, old & env->CP0_Cause & CP0Ca_IP_mask,
309             val, val & env->CP0_Cause & CP0Ca_IP_mask,
310             env->CP0_Cause);
311     (env->hflags & MIPS_HFLAG_UM) ? fputs(", UM\n", logfile)
312                                   : fputs("\n", logfile);
313 }
314
315 void do_mtc0_status_irqraise_debug(void)
316 {
317     fprintf(logfile, "Raise pending IRQs\n");
318 }
319
320 void fpu_handle_exception(void)
321 {
322 #ifdef CONFIG_SOFTFLOAT
323     int flags = get_float_exception_flags(&env->fp_status);
324     unsigned int cpuflags = 0, enable, cause = 0;
325
326     enable = GET_FP_ENABLE(env->fcr31);
327
328     /* determine current flags */   
329     if (flags & float_flag_invalid) {
330         cpuflags |= FP_INVALID;
331         cause |= FP_INVALID & enable;
332     }
333     if (flags & float_flag_divbyzero) {
334         cpuflags |= FP_DIV0;    
335         cause |= FP_DIV0 & enable;
336     }
337     if (flags & float_flag_overflow) {
338         cpuflags |= FP_OVERFLOW;    
339         cause |= FP_OVERFLOW & enable;
340     }
341     if (flags & float_flag_underflow) {
342         cpuflags |= FP_UNDERFLOW;   
343         cause |= FP_UNDERFLOW & enable;
344     }
345     if (flags & float_flag_inexact) {
346         cpuflags |= FP_INEXACT; 
347         cause |= FP_INEXACT & enable;
348     }
349     SET_FP_FLAGS(env->fcr31, cpuflags);
350     SET_FP_CAUSE(env->fcr31, cause);
351 #else
352     SET_FP_FLAGS(env->fcr31, 0);
353     SET_FP_CAUSE(env->fcr31, 0);
354 #endif
355 }
356
357 /* TLB management */
358 void cpu_mips_tlb_flush (CPUState *env, int flush_global)
359 {
360     /* Flush qemu's TLB and discard all shadowed entries.  */
361     tlb_flush (env, flush_global);
362     env->tlb_in_use = env->nb_tlb;
363 }
364
365 static void r4k_mips_tlb_flush_extra (CPUState *env, int first)
366 {
367     /* Discard entries from env->tlb[first] onwards.  */
368     while (env->tlb_in_use > first) {
369         r4k_invalidate_tlb(env, --env->tlb_in_use, 0);
370     }
371 }
372
373 static void r4k_fill_tlb (int idx)
374 {
375     r4k_tlb_t *tlb;
376
377     /* XXX: detect conflicting TLBs and raise a MCHECK exception when needed */
378     tlb = &env->mmu.r4k.tlb[idx];
379     tlb->VPN = env->CP0_EntryHi & (TARGET_PAGE_MASK << 1);
380 #ifdef TARGET_MIPS64
381     tlb->VPN &= 0xC00000FFFFFFFFFFULL;
382 #endif
383     tlb->ASID = env->CP0_EntryHi & 0xFF;
384     tlb->PageMask = env->CP0_PageMask;
385     tlb->G = env->CP0_EntryLo0 & env->CP0_EntryLo1 & 1;
386     tlb->V0 = (env->CP0_EntryLo0 & 2) != 0;
387     tlb->D0 = (env->CP0_EntryLo0 & 4) != 0;
388     tlb->C0 = (env->CP0_EntryLo0 >> 3) & 0x7;
389     tlb->PFN[0] = (env->CP0_EntryLo0 >> 6) << 12;
390     tlb->V1 = (env->CP0_EntryLo1 & 2) != 0;
391     tlb->D1 = (env->CP0_EntryLo1 & 4) != 0;
392     tlb->C1 = (env->CP0_EntryLo1 >> 3) & 0x7;
393     tlb->PFN[1] = (env->CP0_EntryLo1 >> 6) << 12;
394 }
395
396 void r4k_do_tlbwi (void)
397 {
398     /* Discard cached TLB entries.  We could avoid doing this if the
399        tlbwi is just upgrading access permissions on the current entry;
400        that might be a further win.  */
401     r4k_mips_tlb_flush_extra (env, env->nb_tlb);
402
403     r4k_invalidate_tlb(env, env->CP0_Index % env->nb_tlb, 0);
404     r4k_fill_tlb(env->CP0_Index % env->nb_tlb);
405 }
406
407 void r4k_do_tlbwr (void)
408 {
409     int r = cpu_mips_get_random(env);
410
411     r4k_invalidate_tlb(env, r, 1);
412     r4k_fill_tlb(r);
413 }
414
415 void r4k_do_tlbp (void)
416 {
417     r4k_tlb_t *tlb;
418     target_ulong mask;
419     target_ulong tag;
420     target_ulong VPN;
421     uint8_t ASID;
422     int i;
423
424     ASID = env->CP0_EntryHi & 0xFF;
425     for (i = 0; i < env->nb_tlb; i++) {
426         tlb = &env->mmu.r4k.tlb[i];
427         /* 1k pages are not supported. */
428         mask = tlb->PageMask | ~(TARGET_PAGE_MASK << 1);
429         tag = env->CP0_EntryHi & ~mask;
430         VPN = tlb->VPN & ~mask;
431         /* Check ASID, virtual page number & size */
432         if ((tlb->G == 1 || tlb->ASID == ASID) && VPN == tag) {
433             /* TLB match */
434             env->CP0_Index = i;
435             break;
436         }
437     }
438     if (i == env->nb_tlb) {
439         /* No match.  Discard any shadow entries, if any of them match.  */
440         for (i = env->nb_tlb; i < env->tlb_in_use; i++) {
441             tlb = &env->mmu.r4k.tlb[i];
442             /* 1k pages are not supported. */
443             mask = tlb->PageMask | ~(TARGET_PAGE_MASK << 1);
444             tag = env->CP0_EntryHi & ~mask;
445             VPN = tlb->VPN & ~mask;
446             /* Check ASID, virtual page number & size */
447             if ((tlb->G == 1 || tlb->ASID == ASID) && VPN == tag) {
448                 r4k_mips_tlb_flush_extra (env, i);
449                 break;
450             }
451         }
452
453         env->CP0_Index |= 0x80000000;
454     }
455 }
456
457 void r4k_do_tlbr (void)
458 {
459     r4k_tlb_t *tlb;
460     uint8_t ASID;
461
462     ASID = env->CP0_EntryHi & 0xFF;
463     tlb = &env->mmu.r4k.tlb[env->CP0_Index % env->nb_tlb];
464
465     /* If this will change the current ASID, flush qemu's TLB.  */
466     if (ASID != tlb->ASID)
467         cpu_mips_tlb_flush (env, 1);
468
469     r4k_mips_tlb_flush_extra(env, env->nb_tlb);
470
471     env->CP0_EntryHi = tlb->VPN | tlb->ASID;
472     env->CP0_PageMask = tlb->PageMask;
473     env->CP0_EntryLo0 = tlb->G | (tlb->V0 << 1) | (tlb->D0 << 2) |
474                         (tlb->C0 << 3) | (tlb->PFN[0] >> 6);
475     env->CP0_EntryLo1 = tlb->G | (tlb->V1 << 1) | (tlb->D1 << 2) |
476                         (tlb->C1 << 3) | (tlb->PFN[1] >> 6);
477 }
478
479 #endif /* !CONFIG_USER_ONLY */
480
481 void dump_ldst (const unsigned char *func)
482 {
483     if (loglevel)
484         fprintf(logfile, "%s => " TARGET_FMT_lx " " TARGET_FMT_lx "\n", __func__, T0, T1);
485 }
486
487 void dump_sc (void)
488 {
489     if (loglevel) {
490         fprintf(logfile, "%s " TARGET_FMT_lx " at " TARGET_FMT_lx " (" TARGET_FMT_lx ")\n", __func__,
491                 T1, T0, env->CP0_LLAddr);
492     }
493 }
494
495 void debug_pre_eret (void)
496 {
497     fprintf(logfile, "ERET: PC " TARGET_FMT_lx " EPC " TARGET_FMT_lx,
498             env->PC, env->CP0_EPC);
499     if (env->CP0_Status & (1 << CP0St_ERL))
500         fprintf(logfile, " ErrorEPC " TARGET_FMT_lx, env->CP0_ErrorEPC);
501     if (env->hflags & MIPS_HFLAG_DM)
502         fprintf(logfile, " DEPC " TARGET_FMT_lx, env->CP0_DEPC);
503     fputs("\n", logfile);
504 }
505
506 void debug_post_eret (void)
507 {
508     fprintf(logfile, "  =>  PC " TARGET_FMT_lx " EPC " TARGET_FMT_lx,
509             env->PC, env->CP0_EPC);
510     if (env->CP0_Status & (1 << CP0St_ERL))
511         fprintf(logfile, " ErrorEPC " TARGET_FMT_lx, env->CP0_ErrorEPC);
512     if (env->hflags & MIPS_HFLAG_DM)
513         fprintf(logfile, " DEPC " TARGET_FMT_lx, env->CP0_DEPC);
514     if (env->hflags & MIPS_HFLAG_UM)
515         fputs(", UM\n", logfile);
516     else
517         fputs("\n", logfile);
518 }
519
520 void do_pmon (int function)
521 {
522     function /= 2;
523     switch (function) {
524     case 2: /* TODO: char inbyte(int waitflag); */
525         if (env->gpr[4] == 0)
526             env->gpr[2] = -1;
527         /* Fall through */
528     case 11: /* TODO: char inbyte (void); */
529         env->gpr[2] = -1;
530         break;
531     case 3:
532     case 12:
533         printf("%c", (char)(env->gpr[4] & 0xFF));
534         break;
535     case 17:
536         break;
537     case 158:
538         {
539             unsigned char *fmt = (void *)(unsigned long)env->gpr[4];
540             printf("%s", fmt);
541         }
542         break;
543     }
544 }
545
546 #if !defined(CONFIG_USER_ONLY) 
547
548 static void do_unaligned_access (target_ulong addr, int is_write, int is_user, void *retaddr);
549
550 #define MMUSUFFIX _mmu
551 #define ALIGNED_ONLY
552
553 #define SHIFT 0
554 #include "softmmu_template.h"
555
556 #define SHIFT 1
557 #include "softmmu_template.h"
558
559 #define SHIFT 2
560 #include "softmmu_template.h"
561
562 #define SHIFT 3
563 #include "softmmu_template.h"
564
565 static void do_unaligned_access (target_ulong addr, int is_write, int is_user, void *retaddr)
566 {
567     env->CP0_BadVAddr = addr;
568     do_restore_state (retaddr);
569     do_raise_exception ((is_write == 1) ? EXCP_AdES : EXCP_AdEL);
570 }
571
572 void tlb_fill (target_ulong addr, int is_write, int is_user, void *retaddr)
573 {
574     TranslationBlock *tb;
575     CPUState *saved_env;
576     unsigned long pc;
577     int ret;
578
579     /* XXX: hack to restore env in all cases, even if not called from
580        generated code */
581     saved_env = env;
582     env = cpu_single_env;
583     ret = cpu_mips_handle_mmu_fault(env, addr, is_write, is_user, 1);
584     if (ret) {
585         if (retaddr) {
586             /* now we have a real cpu fault */
587             pc = (unsigned long)retaddr;
588             tb = tb_find_pc(pc);
589             if (tb) {
590                 /* the PC is inside the translated code. It means that we have
591                    a virtual CPU fault */
592                 cpu_restore_state(tb, env, pc, NULL);
593             }
594         }
595         do_raise_exception_err(env->exception_index, env->error_code);
596     }
597     env = saved_env;
598 }
599
600 #endif