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