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