0.7.1-alt1
[qemu] / 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 <math.h>
21 #include "exec.h"
22
23 #define MIPS_DEBUG_DISAS
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 #define MEMSUFFIX _raw
50 #include "op_helper_mem.c"
51 #undef MEMSUFFIX
52 #if !defined(CONFIG_USER_ONLY)
53 #define MEMSUFFIX _user
54 #include "op_helper_mem.c"
55 #undef MEMSUFFIX
56 #define MEMSUFFIX _kernel
57 #include "op_helper_mem.c"
58 #undef MEMSUFFIX
59 #endif
60
61 /* 64 bits arithmetic for 32 bits hosts */
62 #if (HOST_LONG_BITS == 32)
63 static inline uint64_t get_HILO (void)
64 {
65     return ((uint64_t)env->HI << 32) | (uint64_t)env->LO;
66 }
67
68 static inline void set_HILO (uint64_t HILO)
69 {
70     env->LO = HILO & 0xFFFFFFFF;
71     env->HI = HILO >> 32;
72 }
73
74 void do_mult (void)
75 {
76     set_HILO((int64_t)T0 * (int64_t)T1);
77 }
78
79 void do_multu (void)
80 {
81     set_HILO((uint64_t)T0 * (uint64_t)T1);
82 }
83
84 void do_madd (void)
85 {
86     int64_t tmp;
87
88     tmp = ((int64_t)T0 * (int64_t)T1);
89     set_HILO((int64_t)get_HILO() + tmp);
90 }
91
92 void do_maddu (void)
93 {
94     uint64_t tmp;
95
96     tmp = ((uint64_t)T0 * (uint64_t)T1);
97     set_HILO(get_HILO() + tmp);
98 }
99
100 void do_msub (void)
101 {
102     int64_t tmp;
103
104     tmp = ((int64_t)T0 * (int64_t)T1);
105     set_HILO((int64_t)get_HILO() - tmp);
106 }
107
108 void do_msubu (void)
109 {
110     uint64_t tmp;
111
112     tmp = ((uint64_t)T0 * (uint64_t)T1);
113     set_HILO(get_HILO() - tmp);
114 }
115 #endif
116
117 /* CP0 helpers */
118 void do_mfc0 (int reg, int sel)
119 {
120     const unsigned char *rn;
121
122     if (sel != 0 && reg != 16 && reg != 28) {
123         rn = "invalid";
124         goto print;
125     }
126     switch (reg) {
127     case 0:
128         T0 = env->CP0_index;
129         rn = "Index";
130         break;
131     case 1:
132         T0 = cpu_mips_get_random(env);
133         rn = "Random";
134         break;
135     case 2:
136         T0 = env->CP0_EntryLo0;
137         rn = "EntryLo0";
138         break;
139     case 3:
140         T0 = env->CP0_EntryLo1;
141         rn = "EntryLo1";
142         break;
143     case 4:
144         T0 = env->CP0_Context;
145         rn = "Context";
146         break;
147     case 5:
148         T0 = env->CP0_PageMask;
149         rn = "PageMask";
150         break;
151     case 6:
152         T0 = env->CP0_Wired;
153         rn = "Wired";
154         break;
155     case 8:
156         T0 = env->CP0_BadVAddr;
157         rn = "BadVaddr";
158         break;
159     case 9:
160         T0 = cpu_mips_get_count(env);
161         rn = "Count";
162         break;
163     case 10:
164         T0 = env->CP0_EntryHi;
165         rn = "EntryHi";
166         break;
167     case 11:
168         T0 = env->CP0_Compare;
169         rn = "Compare";
170         break;
171     case 12:
172         T0 = env->CP0_Status;
173         if (env->hflags & MIPS_HFLAG_UM)
174             T0 |= (1 << CP0St_UM);
175         if (env->hflags & MIPS_HFLAG_ERL)
176             T0 |= (1 << CP0St_ERL);
177         if (env->hflags & MIPS_HFLAG_EXL)
178             T0 |= (1 << CP0St_EXL);
179         rn = "Status";
180         break;
181     case 13:
182         T0 = env->CP0_Cause;
183         rn = "Cause";
184         break;
185     case 14:
186         T0 = env->CP0_EPC;
187         rn = "EPC";
188         break;
189     case 15:
190         T0 = env->CP0_PRid;
191         rn = "PRid";
192         break;
193     case 16:
194         switch (sel) {
195         case 0:
196             T0 = env->CP0_Config0;
197             rn = "Config";
198             break;
199         case 1:
200             T0 = env->CP0_Config1;
201             rn = "Config1";
202             break;
203         default:
204             rn = "Unknown config register";
205             break;
206         }
207         break;
208     case 17:
209         T0 = env->CP0_LLAddr >> 4;
210         rn = "LLAddr";
211         break;
212     case 18:
213         T0 = env->CP0_WatchLo;
214         rn = "WatchLo";
215         break;
216     case 19:
217         T0 = env->CP0_WatchHi;
218         rn = "WatchHi";
219         break;
220     case 23:
221         T0 = env->CP0_Debug;
222         if (env->hflags & MIPS_HFLAG_DM)
223             T0 |= 1 << CP0DB_DM;
224         rn = "Debug";
225         break;
226     case 24:
227         T0 = env->CP0_DEPC;
228         rn = "DEPC";
229         break;
230     case 28:
231         switch (sel) {
232         case 0:
233             T0 = env->CP0_TagLo;
234             rn = "TagLo";
235             break;
236         case 1:
237             T0 = env->CP0_DataLo;
238             rn = "DataLo";
239             break;
240         default:
241             rn = "unknown sel";
242             break;
243         }
244         break;
245     case 30:
246         T0 = env->CP0_ErrorEPC;
247         rn = "ErrorEPC";
248         break;
249     case 31:
250         T0 = env->CP0_DESAVE;
251         rn = "DESAVE";
252         break;
253     default:
254         rn = "unknown";
255         break;
256     }
257  print:
258 #if defined MIPS_DEBUG_DISAS
259     if (loglevel & CPU_LOG_TB_IN_ASM) {
260         fprintf(logfile, "%08x mfc0 %s => %08x (%d %d)\n",
261                 env->PC, rn, T0, reg, sel);
262     }
263 #endif
264     return;
265 }
266
267 void do_mtc0 (int reg, int sel)
268 {
269     const unsigned char *rn;
270     uint32_t val, old, mask;
271
272     if (sel != 0 && reg != 16 && reg != 28) {
273         val = -1;
274         old = -1;
275         rn = "invalid";
276         goto print;
277     }
278     switch (reg) {
279     case 0:
280         val = (env->CP0_index & 0x80000000) | (T0 & 0x0000000F);
281         old = env->CP0_index;
282         env->CP0_index = val;
283         rn = "Index";
284         break;
285     case 2:
286         val = T0 & 0x03FFFFFFF;
287         old = env->CP0_EntryLo0;
288         env->CP0_EntryLo0 = val;
289         rn = "EntryLo0";
290         break;
291     case 3:
292         val = T0 & 0x03FFFFFFF;
293         old = env->CP0_EntryLo1;
294         env->CP0_EntryLo1 = val;
295         rn = "EntryLo1";
296         break;
297     case 4:
298         val = (env->CP0_Context & 0xFF000000) | (T0 & 0x00FFFFF0);
299         old = env->CP0_Context;
300         env->CP0_Context = val;
301         rn = "Context";
302         break;
303     case 5:
304         val = T0 & 0x01FFE000;
305         old = env->CP0_PageMask;
306         env->CP0_PageMask = val;
307         rn = "PageMask";
308         break;
309     case 6:
310         val = T0 & 0x0000000F;
311         old = env->CP0_Wired;
312         env->CP0_Wired = val;
313         rn = "Wired";
314         break;
315     case 9:
316         val = T0;
317         old = cpu_mips_get_count(env);
318         cpu_mips_store_count(env, val);
319         rn = "Count";
320         break;
321     case 10:
322         val = T0 & 0xFFFFF0FF;
323         old = env->CP0_EntryHi;
324         env->CP0_EntryHi = val;
325         rn = "EntryHi";
326         break;
327     case 11:
328         val = T0;
329         old = env->CP0_Compare;
330         cpu_mips_store_compare(env, val);
331         rn = "Compare";
332         break;
333     case 12:
334         val = T0 & 0xFA78FF01;
335         if (T0 & (1 << CP0St_UM))
336             env->hflags |= MIPS_HFLAG_UM;
337         else
338             env->hflags &= ~MIPS_HFLAG_UM;
339         if (T0 & (1 << CP0St_ERL))
340             env->hflags |= MIPS_HFLAG_ERL;
341         else
342             env->hflags &= ~MIPS_HFLAG_ERL;
343         if (T0 & (1 << CP0St_EXL))
344             env->hflags |= MIPS_HFLAG_EXL;
345         else
346             env->hflags &= ~MIPS_HFLAG_EXL;
347         old = env->CP0_Status;
348         env->CP0_Status = val;
349         /* If we unmasked an asserted IRQ, raise it */
350         mask = 0x0000FF00;
351         if (loglevel & CPU_LOG_TB_IN_ASM) {
352             fprintf(logfile, "Status %08x => %08x Cause %08x (%08x %08x %08x)\n",
353                     old, val, env->CP0_Cause, old & mask, val & mask,
354                     env->CP0_Cause & mask);
355         }
356 #if 1
357         if ((val & (1 << CP0St_IE)) && !(old & (1 << CP0St_IE)) &&
358             !(env->hflags & MIPS_HFLAG_EXL) &&
359             !(env->hflags & MIPS_HFLAG_ERL) &&
360             !(env->hflags & MIPS_HFLAG_DM) && 
361             (env->CP0_Status & env->CP0_Cause & mask)) {
362             if (logfile)
363                 fprintf(logfile, "Raise pending IRQs\n");
364             env->interrupt_request |= CPU_INTERRUPT_HARD;
365             do_raise_exception(EXCP_EXT_INTERRUPT);
366         } else if (!(val & 0x00000001) && (old & 0x00000001)) {
367             env->interrupt_request &= ~CPU_INTERRUPT_HARD;
368         }
369 #endif
370         rn = "Status";
371         break;
372     case 13:
373         val = (env->CP0_Cause & 0xB000F87C) | (T0 & 0x000C00300);
374         old = env->CP0_Cause;
375         env->CP0_Cause = val;
376 #if 0
377         {
378             int i;
379             /* Check if we ever asserted a software IRQ */
380             for (i = 0; i < 2; i++) {
381                 mask = 0x100 << i;
382                 if ((val & mask) & !(old & mask))
383                     mips_set_irq(i);
384             }
385         }
386 #endif
387         rn = "Cause";
388         break;
389     case 14:
390         val = T0;
391         old = env->CP0_EPC;
392         env->CP0_EPC = val;
393         rn = "EPC";
394         break;
395     case 16:
396         switch (sel) {
397         case 0:
398 #if defined(MIPS_USES_R4K_TLB)
399             val = (env->CP0_Config0 & 0x8017FF80) | (T0 & 0x7E000001);
400 #else
401             val = (env->CP0_Config0 & 0xFE17FF80) | (T0 & 0x00000001);
402 #endif
403             old = env->CP0_Config0;
404             env->CP0_Config0 = val;
405             rn = "Config0";
406             break;
407         default:
408             val = -1;
409             old = -1;
410             rn = "bad config selector";
411             break;
412         }
413         break;
414     case 18:
415         val = T0;
416         old = env->CP0_WatchLo;
417         env->CP0_WatchLo = val;
418         rn = "WatchLo";
419         break;
420     case 19:
421         val = T0 & 0x40FF0FF8;
422         old = env->CP0_WatchHi;
423         env->CP0_WatchHi = val;
424         rn = "WatchHi";
425         break;
426     case 23:
427         val = (env->CP0_Debug & 0x8C03FC1F) | (T0 & 0x13300120);
428         if (T0 & (1 << CP0DB_DM))
429             env->hflags |= MIPS_HFLAG_DM;
430         else
431             env->hflags &= ~MIPS_HFLAG_DM;
432         old = env->CP0_Debug;
433         env->CP0_Debug = val;
434         rn = "Debug";
435         break;
436     case 24:
437         val = T0;
438         old = env->CP0_DEPC;
439         env->CP0_DEPC = val;
440         rn = "DEPC";
441         break;
442     case 28:
443         switch (sel) {
444         case 0:
445             val = T0 & 0xFFFFFCF6;
446             old = env->CP0_TagLo;
447             env->CP0_TagLo = val;
448             rn = "TagLo";
449             break;
450         default:
451             val = -1;
452             old = -1;
453             rn = "invalid sel";
454             break;
455         }
456         break;
457     case 30:
458         val = T0;
459         old = env->CP0_ErrorEPC;
460         env->CP0_ErrorEPC = val;
461         rn = "EPC";
462         break;
463     case 31:
464         val = T0;
465         old = env->CP0_DESAVE;
466         env->CP0_DESAVE = val;
467         rn = "DESAVE";
468         break;
469     default:
470         val = -1;
471         old = -1;
472         rn = "unknown";
473         break;
474     }
475  print:
476 #if defined MIPS_DEBUG_DISAS
477     if (loglevel & CPU_LOG_TB_IN_ASM) {
478         fprintf(logfile, "%08x mtc0 %s %08x => %08x (%d %d %08x)\n",
479                 env->PC, rn, T0, val, reg, sel, old);
480     }
481 #endif
482     return;
483 }
484
485 /* TLB management */
486 #if defined(MIPS_USES_R4K_TLB)
487 static void invalidate_tb (int idx)
488 {
489     tlb_t *tlb;
490     target_ulong addr, end;
491
492     tlb = &env->tlb[idx];
493     if (tlb->V[0]) {
494         addr = tlb->PFN[0];
495         end = addr + (tlb->end - tlb->VPN);
496         tb_invalidate_page_range(addr, end);
497     }
498     if (tlb->V[1]) {
499         addr = tlb->PFN[1];
500         end = addr + (tlb->end - tlb->VPN);
501         tb_invalidate_page_range(addr, end);
502     }
503 }
504
505 static void fill_tb (int idx)
506 {
507     tlb_t *tlb;
508     int size;
509
510     /* XXX: detect conflicting TLBs and raise a MCHECK exception when needed */
511     tlb = &env->tlb[idx];
512     tlb->VPN = env->CP0_EntryHi & 0xFFFFE000;
513     tlb->ASID = env->CP0_EntryHi & 0x000000FF;
514     size = env->CP0_PageMask >> 13;
515     size = 4 * (size + 1);
516     tlb->end = tlb->VPN + (1 << (8 + size));
517     tlb->G = env->CP0_EntryLo0 & env->CP0_EntryLo1 & 1;
518     tlb->V[0] = env->CP0_EntryLo0 & 2;
519     tlb->D[0] = env->CP0_EntryLo0 & 4;
520     tlb->C[0] = (env->CP0_EntryLo0 >> 3) & 0x7;
521     tlb->PFN[0] = (env->CP0_EntryLo0 >> 6) << 12;
522     tlb->V[1] = env->CP0_EntryLo1 & 2;
523     tlb->D[1] = env->CP0_EntryLo1 & 4;
524     tlb->C[1] = (env->CP0_EntryLo1 >> 3) & 0x7;
525     tlb->PFN[1] = (env->CP0_EntryLo1 >> 6) << 12;
526 }
527
528 void do_tlbwi (void)
529 {
530     /* Wildly undefined effects for CP0_index containing a too high value and
531        MIPS_TLB_NB not being a power of two.  But so does real silicon.  */
532     invalidate_tb(env->CP0_index & (MIPS_TLB_NB - 1));
533     fill_tb(env->CP0_index & (MIPS_TLB_NB - 1));
534 }
535
536 void do_tlbwr (void)
537 {
538     int r = cpu_mips_get_random(env);
539
540     invalidate_tb(r);
541     fill_tb(r);
542 }
543
544 void do_tlbp (void)
545 {
546     tlb_t *tlb;
547     target_ulong tag;
548     uint8_t ASID;
549     int i;
550
551     tag = (env->CP0_EntryHi & 0xFFFFE000);
552     ASID = env->CP0_EntryHi & 0x000000FF;
553         for (i = 0; i < MIPS_TLB_NB; i++) {
554         tlb = &env->tlb[i];
555         /* Check ASID, virtual page number & size */
556         if ((tlb->G == 1 || tlb->ASID == ASID) && tlb->VPN == tag) {
557             /* TLB match */
558             env->CP0_index = i;
559             break;
560         }
561     }
562     if (i == MIPS_TLB_NB) {
563         env->CP0_index |= 0x80000000;
564     }
565 }
566
567 void do_tlbr (void)
568 {
569     tlb_t *tlb;
570     int size;
571
572     tlb = &env->tlb[env->CP0_index & (MIPS_TLB_NB - 1)];
573     env->CP0_EntryHi = tlb->VPN | tlb->ASID;
574     size = (tlb->end - tlb->VPN) >> 12;
575     env->CP0_PageMask = (size - 1) << 13;
576     env->CP0_EntryLo0 = tlb->V[0] | tlb->D[0] | (tlb->C[0] << 3) |
577         (tlb->PFN[0] >> 6);
578     env->CP0_EntryLo1 = tlb->V[1] | tlb->D[1] | (tlb->C[1] << 3) |
579         (tlb->PFN[1] >> 6);
580 }
581 #endif
582
583 void op_dump_ldst (const unsigned char *func)
584 {
585     if (loglevel)
586         fprintf(logfile, "%s => %08x %08x\n", __func__, T0, T1);
587 }
588
589 void dump_sc (void)
590 {
591     if (loglevel) {
592         fprintf(logfile, "%s %08x at %08x (%08x)\n", __func__,
593                 T1, T0, env->CP0_LLAddr);
594     }
595 }
596
597 void debug_eret (void)
598 {
599     if (loglevel) {
600         fprintf(logfile, "ERET: pc %08x EPC %08x ErrorEPC %08x (%d)\n",
601                 env->PC, env->CP0_EPC, env->CP0_ErrorEPC,
602                 env->hflags & MIPS_HFLAG_ERL ? 1 : 0);
603     }
604 }
605
606 void do_pmon (int function)
607 {
608     function /= 2;
609     switch (function) {
610     case 2: /* TODO: char inbyte(int waitflag); */
611         if (env->gpr[4] == 0)
612             env->gpr[2] = -1;
613         /* Fall through */
614     case 11: /* TODO: char inbyte (void); */
615         env->gpr[2] = -1;
616         break;
617     case 3:
618     case 12:
619         printf("%c", env->gpr[4] & 0xFF);
620         break;
621     case 17:
622         break;
623     case 158:
624         {
625             unsigned char *fmt = (void *)env->gpr[4];
626             printf("%s", fmt);
627         }
628         break;
629     }
630 }
631
632 #if !defined(CONFIG_USER_ONLY) 
633
634 #define MMUSUFFIX _mmu
635 #define GETPC() (__builtin_return_address(0))
636
637 #define SHIFT 0
638 #include "softmmu_template.h"
639
640 #define SHIFT 1
641 #include "softmmu_template.h"
642
643 #define SHIFT 2
644 #include "softmmu_template.h"
645
646 #define SHIFT 3
647 #include "softmmu_template.h"
648
649 void tlb_fill (target_ulong addr, int is_write, int is_user, void *retaddr)
650 {
651     TranslationBlock *tb;
652     CPUState *saved_env;
653     unsigned long pc;
654     int ret;
655
656     /* XXX: hack to restore env in all cases, even if not called from
657        generated code */
658     saved_env = env;
659     env = cpu_single_env;
660     ret = cpu_mips_handle_mmu_fault(env, addr, is_write, is_user, 1);
661     if (ret) {
662         if (retaddr) {
663             /* now we have a real cpu fault */
664             pc = (unsigned long)retaddr;
665             tb = tb_find_pc(pc);
666             if (tb) {
667                 /* the PC is inside the translated code. It means that we have
668                    a virtual CPU fault */
669                 cpu_restore_state(tb, env, pc, NULL);
670             }
671         }
672         do_raise_exception_err(env->exception_index, env->error_code);
673     }
674     env = saved_env;
675 }
676
677 #endif