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