fix wrong bitmasks for CP0_Context and CP0_EntryHi (Thiemo Seufer)
[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 & 0x3FFFFFFF;
334         old = env->CP0_EntryLo0;
335         env->CP0_EntryLo0 = val;
336         rn = "EntryLo0";
337         break;
338     case 3:
339         val = T0 & 0x3FFFFFFF;
340         old = env->CP0_EntryLo1;
341         env->CP0_EntryLo1 = val;
342         rn = "EntryLo1";
343         break;
344     case 4:
345         val = (env->CP0_Context & 0xFF800000) | (T0 & 0x007FFFF0);
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 & 0xFFFFE0FF;
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 ((val & (1 << CP0St_IE)) && !(old & (1 << CP0St_IE)) &&
407             !(env->hflags & MIPS_HFLAG_EXL) &&
408             !(env->hflags & MIPS_HFLAG_ERL) &&
409             !(env->hflags & MIPS_HFLAG_DM) &&
410             (env->CP0_Status & env->CP0_Cause & mask)) {
411             if (logfile)
412                 fprintf(logfile, "Raise pending IRQs\n");
413             env->interrupt_request |= CPU_INTERRUPT_HARD;
414         } else if (!(val & (1 << CP0St_IE)) && (old & (1 << CP0St_IE))) {
415             env->interrupt_request &= ~CPU_INTERRUPT_HARD;
416         }
417         rn = "Status";
418         break;
419     case 13:
420         val = (env->CP0_Cause & 0xB000F87C) | (T0 & 0x000C00300);
421         old = env->CP0_Cause;
422         env->CP0_Cause = val;
423 #if 0
424         {
425             int i;
426             /* Check if we ever asserted a software IRQ */
427             for (i = 0; i < 2; i++) {
428                 mask = 0x100 << i;
429                 if ((val & mask) & !(old & mask))
430                     mips_set_irq(i);
431             }
432         }
433 #endif
434         rn = "Cause";
435         break;
436     case 14:
437         val = T0;
438         old = env->CP0_EPC;
439         env->CP0_EPC = val;
440         rn = "EPC";
441         break;
442     case 16:
443         switch (sel) {
444         case 0:
445 #if defined(MIPS_USES_R4K_TLB)
446             val = (env->CP0_Config0 & 0x8017FF80) | (T0 & 0x7E000001);
447 #else
448             val = (env->CP0_Config0 & 0xFE17FF80) | (T0 & 0x00000001);
449 #endif
450             old = env->CP0_Config0;
451             env->CP0_Config0 = val;
452             rn = "Config0";
453             break;
454         default:
455             val = -1;
456             old = -1;
457             rn = "bad config selector";
458             break;
459         }
460         break;
461     case 18:
462         val = T0;
463         old = env->CP0_WatchLo;
464         env->CP0_WatchLo = val;
465         rn = "WatchLo";
466         break;
467     case 19:
468         val = T0 & 0x40FF0FF8;
469         old = env->CP0_WatchHi;
470         env->CP0_WatchHi = val;
471         rn = "WatchHi";
472         break;
473     case 23:
474         val = (env->CP0_Debug & 0x8C03FC1F) | (T0 & 0x13300120);
475         if (T0 & (1 << CP0DB_DM))
476             env->hflags |= MIPS_HFLAG_DM;
477         else
478             env->hflags &= ~MIPS_HFLAG_DM;
479         old = env->CP0_Debug;
480         env->CP0_Debug = val;
481         rn = "Debug";
482         break;
483     case 24:
484         val = T0;
485         old = env->CP0_DEPC;
486         env->CP0_DEPC = val;
487         rn = "DEPC";
488         break;
489     case 28:
490         switch (sel) {
491         case 0:
492             val = T0 & 0xFFFFFCF6;
493             old = env->CP0_TagLo;
494             env->CP0_TagLo = val;
495             rn = "TagLo";
496             break;
497         default:
498             val = -1;
499             old = -1;
500             rn = "invalid sel";
501             break;
502         }
503         break;
504     case 30:
505         val = T0;
506         old = env->CP0_ErrorEPC;
507         env->CP0_ErrorEPC = val;
508         rn = "EPC";
509         break;
510     case 31:
511         val = T0;
512         old = env->CP0_DESAVE;
513         env->CP0_DESAVE = val;
514         rn = "DESAVE";
515         break;
516     default:
517         val = -1;
518         old = -1;
519         rn = "unknown";
520         break;
521     }
522  print:
523 #if defined MIPS_DEBUG_DISAS
524     if (loglevel & CPU_LOG_TB_IN_ASM) {
525         fprintf(logfile, "%08x mtc0 %s %08x => %08x (%d %d %08x)\n",
526                 env->PC, rn, T0, val, reg, sel, old);
527     }
528 #endif
529     return;
530 }
531
532 /* TLB management */
533 #if defined(MIPS_USES_R4K_TLB)
534 static void invalidate_tlb (int idx)
535 {
536     tlb_t *tlb;
537     target_ulong addr;
538
539     tlb = &env->tlb[idx];
540     if (tlb->V0) {
541         tb_invalidate_page_range(tlb->PFN[0], tlb->end - tlb->VPN);
542         addr = tlb->VPN;
543         while (addr < tlb->end) {
544             tlb_flush_page (env, addr);
545             addr += TARGET_PAGE_SIZE;
546         }
547     }
548     if (tlb->V1) {
549         tb_invalidate_page_range(tlb->PFN[1], tlb->end2 - tlb->end);
550         addr = tlb->end;
551         while (addr < tlb->end2) {
552             tlb_flush_page (env, addr);
553             addr += TARGET_PAGE_SIZE;
554         }
555     }
556 }
557
558 static void fill_tlb (int idx)
559 {
560     tlb_t *tlb;
561     int size;
562
563     /* XXX: detect conflicting TLBs and raise a MCHECK exception when needed */
564     tlb = &env->tlb[idx];
565     tlb->VPN = env->CP0_EntryHi & 0xFFFFE000;
566     tlb->ASID = env->CP0_EntryHi & 0xFF;
567     size = env->CP0_PageMask >> 13;
568     size = 4 * (size + 1);
569     tlb->end = tlb->VPN + (1 << (8 + size));
570     tlb->end2 = tlb->end + (1 << (8 + size));
571     tlb->G = env->CP0_EntryLo0 & env->CP0_EntryLo1 & 1;
572     tlb->V0 = (env->CP0_EntryLo0 & 2) != 0;
573     tlb->D0 = (env->CP0_EntryLo0 & 4) != 0;
574     tlb->C0 = (env->CP0_EntryLo0 >> 3) & 0x7;
575     tlb->PFN[0] = (env->CP0_EntryLo0 >> 6) << 12;
576     tlb->V1 = (env->CP0_EntryLo1 & 2) != 0;
577     tlb->D1 = (env->CP0_EntryLo1 & 4) != 0;
578     tlb->C1 = (env->CP0_EntryLo1 >> 3) & 0x7;
579     tlb->PFN[1] = (env->CP0_EntryLo1 >> 6) << 12;
580 }
581
582 void do_tlbwi (void)
583 {
584     /* Wildly undefined effects for CP0_index containing a too high value and
585        MIPS_TLB_NB not being a power of two.  But so does real silicon.  */
586     invalidate_tlb(env->CP0_index & (MIPS_TLB_NB - 1));
587     fill_tlb(env->CP0_index & (MIPS_TLB_NB - 1));
588 }
589
590 void do_tlbwr (void)
591 {
592     int r = cpu_mips_get_random(env);
593
594     invalidate_tlb(r);
595     fill_tlb(r);
596 }
597
598 void do_tlbp (void)
599 {
600     tlb_t *tlb;
601     target_ulong tag;
602     uint8_t ASID;
603     int i;
604
605     tag = env->CP0_EntryHi & 0xFFFFE000;
606     ASID = env->CP0_EntryHi & 0xFF;
607     for (i = 0; i < MIPS_TLB_NB; i++) {
608         tlb = &env->tlb[i];
609         /* Check ASID, virtual page number & size */
610         if ((tlb->G == 1 || tlb->ASID == ASID) && tlb->VPN == tag) {
611             /* TLB match */
612             env->CP0_index = i;
613             break;
614         }
615     }
616     if (i == MIPS_TLB_NB) {
617         env->CP0_index |= 0x80000000;
618     }
619 }
620
621 void do_tlbr (void)
622 {
623     tlb_t *tlb;
624     uint8_t ASID;
625     int size;
626
627     ASID = env->CP0_EntryHi & 0xFF;
628     tlb = &env->tlb[env->CP0_index & (MIPS_TLB_NB - 1)];
629
630     /* If this will change the current ASID, flush qemu's TLB.  */
631     if (ASID != tlb->ASID && tlb->G != 1)
632       tlb_flush (env, 1);
633
634     env->CP0_EntryHi = tlb->VPN | tlb->ASID;
635     size = (tlb->end - tlb->VPN) >> 12;
636     env->CP0_PageMask = (size - 1) << 13;
637     env->CP0_EntryLo0 = tlb->G | (tlb->V0 << 1) | (tlb->D0 << 2)
638                 | (tlb->C0 << 3) | (tlb->PFN[0] >> 6);
639     env->CP0_EntryLo1 = tlb->G | (tlb->V1 << 1) | (tlb->D1 << 2)
640                 | (tlb->C1 << 3) | (tlb->PFN[1] >> 6);
641 }
642 #endif
643
644 #endif /* !CONFIG_USER_ONLY */
645
646 void op_dump_ldst (const unsigned char *func)
647 {
648     if (loglevel)
649         fprintf(logfile, "%s => %08x %08x\n", __func__, T0, T1);
650 }
651
652 void dump_sc (void)
653 {
654     if (loglevel) {
655         fprintf(logfile, "%s %08x at %08x (%08x)\n", __func__,
656                 T1, T0, env->CP0_LLAddr);
657     }
658 }
659
660 void debug_eret (void)
661 {
662     if (loglevel) {
663         fprintf(logfile, "ERET: pc %08x EPC %08x ErrorEPC %08x (%d)\n",
664                 env->PC, env->CP0_EPC, env->CP0_ErrorEPC,
665                 env->hflags & MIPS_HFLAG_ERL ? 1 : 0);
666     }
667 }
668
669 void do_pmon (int function)
670 {
671     function /= 2;
672     switch (function) {
673     case 2: /* TODO: char inbyte(int waitflag); */
674         if (env->gpr[4] == 0)
675             env->gpr[2] = -1;
676         /* Fall through */
677     case 11: /* TODO: char inbyte (void); */
678         env->gpr[2] = -1;
679         break;
680     case 3:
681     case 12:
682         printf("%c", env->gpr[4] & 0xFF);
683         break;
684     case 17:
685         break;
686     case 158:
687         {
688             unsigned char *fmt = (void *)env->gpr[4];
689             printf("%s", fmt);
690         }
691         break;
692     }
693 }
694
695 #if !defined(CONFIG_USER_ONLY) 
696
697 static void do_unaligned_access (target_ulong addr, int is_write, int is_user, void *retaddr);
698
699 #define MMUSUFFIX _mmu
700 #define ALIGNED_ONLY
701
702 #define SHIFT 0
703 #include "softmmu_template.h"
704
705 #define SHIFT 1
706 #include "softmmu_template.h"
707
708 #define SHIFT 2
709 #include "softmmu_template.h"
710
711 #define SHIFT 3
712 #include "softmmu_template.h"
713
714 static void do_unaligned_access (target_ulong addr, int is_write, int is_user, void *retaddr)
715 {
716     env->CP0_BadVAddr = addr;
717     do_restore_state (retaddr);
718     do_raise_exception ((is_write == 1) ? EXCP_AdES : EXCP_AdEL);
719 }
720
721 void tlb_fill (target_ulong addr, int is_write, int is_user, void *retaddr)
722 {
723     TranslationBlock *tb;
724     CPUState *saved_env;
725     unsigned long pc;
726     int ret;
727
728     /* XXX: hack to restore env in all cases, even if not called from
729        generated code */
730     saved_env = env;
731     env = cpu_single_env;
732     ret = cpu_mips_handle_mmu_fault(env, addr, is_write, is_user, 1);
733     if (ret) {
734         if (retaddr) {
735             /* now we have a real cpu fault */
736             pc = (unsigned long)retaddr;
737             tb = tb_find_pc(pc);
738             if (tb) {
739                 /* the PC is inside the translated code. It means that we have
740                    a virtual CPU fault */
741                 cpu_restore_state(tb, env, pc, NULL);
742             }
743         }
744         do_raise_exception_err(env->exception_index, env->error_code);
745     }
746     env = saved_env;
747 }
748
749 #endif