Full MIPS64 MMU implementation, by Aurelien Jarno.
[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 <stdlib.h>
21 #include "exec.h"
22
23 #define GETPC() (__builtin_return_address(0))
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 void do_restore_state (void *pc_ptr)
50 {
51   TranslationBlock *tb;
52   unsigned long pc = (unsigned long) pc_ptr;
53
54   tb = tb_find_pc (pc);
55   cpu_restore_state (tb, env, pc, NULL);
56 }
57
58 void do_raise_exception_direct_err (uint32_t exception, int error_code)
59 {
60     do_restore_state (GETPC ());
61     do_raise_exception_err (exception, error_code);
62 }
63
64 void do_raise_exception_direct (uint32_t exception)
65 {
66     do_raise_exception_direct_err (exception, 0);
67 }
68
69 #define MEMSUFFIX _raw
70 #include "op_helper_mem.c"
71 #undef MEMSUFFIX
72 #if !defined(CONFIG_USER_ONLY)
73 #define MEMSUFFIX _user
74 #include "op_helper_mem.c"
75 #undef MEMSUFFIX
76 #define MEMSUFFIX _kernel
77 #include "op_helper_mem.c"
78 #undef MEMSUFFIX
79 #endif
80
81 #ifdef TARGET_MIPS64
82 #if TARGET_LONG_BITS > HOST_LONG_BITS
83 /* Those might call libgcc functions.  */
84 void do_dsll (void)
85 {
86     T0 = T0 << T1;
87 }
88
89 void do_dsll32 (void)
90 {
91     T0 = T0 << (T1 + 32);
92 }
93
94 void do_dsra (void)
95 {
96     T0 = (int64_t)T0 >> T1;
97 }
98
99 void do_dsra32 (void)
100 {
101     T0 = (int64_t)T0 >> (T1 + 32);
102 }
103
104 void do_dsrl (void)
105 {
106     T0 = T0 >> T1;
107 }
108
109 void do_dsrl32 (void)
110 {
111     T0 = T0 >> (T1 + 32);
112 }
113
114 void do_drotr (void)
115 {
116     target_ulong tmp;
117
118     if (T1) {
119        tmp = T0 << (0x40 - T1);
120        T0 = (T0 >> T1) | tmp;
121     }
122 }
123
124 void do_drotr32 (void)
125 {
126     target_ulong tmp;
127
128     if (T1) {
129        tmp = T0 << (0x40 - (32 + T1));
130        T0 = (T0 >> (32 + T1)) | tmp;
131     }
132 }
133
134 void do_dsllv (void)
135 {
136     T0 = T1 << (T0 & 0x3F);
137 }
138
139 void do_dsrav (void)
140 {
141     T0 = (int64_t)T1 >> (T0 & 0x3F);
142 }
143
144 void do_dsrlv (void)
145 {
146     T0 = T1 >> (T0 & 0x3F);
147 }
148
149 void do_drotrv (void)
150 {
151     target_ulong tmp;
152
153     T0 &= 0x3F;
154     if (T0) {
155        tmp = T1 << (0x40 - T0);
156        T0 = (T1 >> T0) | tmp;
157     } else
158        T0 = T1;
159 }
160 #endif /* TARGET_LONG_BITS > HOST_LONG_BITS */
161 #endif /* TARGET_MIPS64 */
162
163 /* 64 bits arithmetic for 32 bits hosts */
164 #if TARGET_LONG_BITS > HOST_LONG_BITS
165 static inline uint64_t get_HILO (void)
166 {
167     return (env->HI << 32) | (uint32_t)env->LO;
168 }
169
170 static inline void set_HILO (uint64_t HILO)
171 {
172     env->LO = (int32_t)HILO;
173     env->HI = (int32_t)(HILO >> 32);
174 }
175
176 void do_mult (void)
177 {
178     set_HILO((int64_t)(int32_t)T0 * (int64_t)(int32_t)T1);
179 }
180
181 void do_multu (void)
182 {
183     set_HILO((uint64_t)(uint32_t)T0 * (uint64_t)(uint32_t)T1);
184 }
185
186 void do_madd (void)
187 {
188     int64_t tmp;
189
190     tmp = ((int64_t)(int32_t)T0 * (int64_t)(int32_t)T1);
191     set_HILO((int64_t)get_HILO() + tmp);
192 }
193
194 void do_maddu (void)
195 {
196     uint64_t tmp;
197
198     tmp = ((uint64_t)(uint32_t)T0 * (uint64_t)(uint32_t)T1);
199     set_HILO(get_HILO() + tmp);
200 }
201
202 void do_msub (void)
203 {
204     int64_t tmp;
205
206     tmp = ((int64_t)(int32_t)T0 * (int64_t)(int32_t)T1);
207     set_HILO((int64_t)get_HILO() - tmp);
208 }
209
210 void do_msubu (void)
211 {
212     uint64_t tmp;
213
214     tmp = ((uint64_t)(uint32_t)T0 * (uint64_t)(uint32_t)T1);
215     set_HILO(get_HILO() - tmp);
216 }
217 #endif
218
219 #if HOST_LONG_BITS < 64
220 void do_div (void)
221 {
222     /* 64bit datatypes because we may see overflow/underflow. */
223     if (T1 != 0) {
224         env->LO = (int32_t)((int64_t)(int32_t)T0 / (int32_t)T1);
225         env->HI = (int32_t)((int64_t)(int32_t)T0 % (int32_t)T1);
226     }
227 }
228 #endif
229
230 #ifdef TARGET_MIPS64
231 void do_dmult (void)
232 {
233     env->LO = (int64_t)T0 * (int64_t)T1;
234     /* XXX */
235     env->HI = (env->LO | (1ULL << 63)) ? ~0ULL : 0ULL;
236 }
237
238 void do_dmultu (void)
239 {
240     env->LO = T0 * T1;
241     /* XXX */
242     env->HI = 0;
243 }
244
245 void do_ddiv (void)
246 {
247     if (T1 != 0) {
248         lldiv_t res = lldiv((int64_t)T0, (int64_t)T1);
249         env->LO = res.quot;
250         env->HI = res.rem;
251     }
252 }
253
254 void do_ddivu (void)
255 {
256     if (T1 != 0) {
257         /* XXX: lldivu? */
258         lldiv_t res = lldiv(T0, T1);
259         env->LO = (uint64_t)res.quot;
260         env->HI = (uint64_t)res.rem;
261     }
262 }
263 #endif
264
265 #if defined(CONFIG_USER_ONLY) 
266 void do_mfc0_random (void)
267 {
268     cpu_abort(env, "mfc0 random\n");
269 }
270
271 void do_mfc0_count (void)
272 {
273     cpu_abort(env, "mfc0 count\n");
274 }
275
276 void cpu_mips_store_count(CPUState *env, uint32_t value)
277 {
278     cpu_abort(env, "mtc0 count\n");
279 }
280
281 void cpu_mips_store_compare(CPUState *env, uint32_t value)
282 {
283     cpu_abort(env, "mtc0 compare\n");
284 }
285
286 void cpu_mips_update_irq(CPUState *env)
287 {
288     cpu_abort(env, "mtc0 status / mtc0 cause\n");
289 }
290
291 void do_mtc0_status_debug(uint32_t old, uint32_t val)
292 {
293     cpu_abort(env, "mtc0 status debug\n");
294 }
295
296 void do_mtc0_status_irqraise_debug (void)
297 {
298     cpu_abort(env, "mtc0 status irqraise debug\n");
299 }
300
301 void cpu_mips_tlb_flush (CPUState *env, int flush_global)
302 {
303     cpu_abort(env, "mips_tlb_flush\n");
304 }
305
306 #else
307
308 /* CP0 helpers */
309 void do_mfc0_random (void)
310 {
311     T0 = (int32_t)cpu_mips_get_random(env);
312 }
313
314 void do_mfc0_count (void)
315 {
316     T0 = (int32_t)cpu_mips_get_count(env);
317 }
318
319 void do_mtc0_status_debug(uint32_t old, uint32_t val)
320 {
321     fprintf(logfile, "Status %08x (%08x) => %08x (%08x) Cause %08x",
322             old, old & env->CP0_Cause & CP0Ca_IP_mask,
323             val, val & env->CP0_Cause & CP0Ca_IP_mask,
324             env->CP0_Cause);
325     (env->hflags & MIPS_HFLAG_UM) ? fputs(", UM\n", logfile)
326                                   : fputs("\n", logfile);
327 }
328
329 void do_mtc0_status_irqraise_debug(void)
330 {
331     fprintf(logfile, "Raise pending IRQs\n");
332 }
333
334 void fpu_handle_exception(void)
335 {
336 #ifdef CONFIG_SOFTFLOAT
337     int flags = get_float_exception_flags(&env->fp_status);
338     unsigned int cpuflags = 0, enable, cause = 0;
339
340     enable = GET_FP_ENABLE(env->fcr31);
341
342     /* determine current flags */   
343     if (flags & float_flag_invalid) {
344         cpuflags |= FP_INVALID;
345         cause |= FP_INVALID & enable;
346     }
347     if (flags & float_flag_divbyzero) {
348         cpuflags |= FP_DIV0;    
349         cause |= FP_DIV0 & enable;
350     }
351     if (flags & float_flag_overflow) {
352         cpuflags |= FP_OVERFLOW;    
353         cause |= FP_OVERFLOW & enable;
354     }
355     if (flags & float_flag_underflow) {
356         cpuflags |= FP_UNDERFLOW;   
357         cause |= FP_UNDERFLOW & enable;
358     }
359     if (flags & float_flag_inexact) {
360         cpuflags |= FP_INEXACT; 
361         cause |= FP_INEXACT & enable;
362     }
363     SET_FP_FLAGS(env->fcr31, cpuflags);
364     SET_FP_CAUSE(env->fcr31, cause);
365 #else
366     SET_FP_FLAGS(env->fcr31, 0);
367     SET_FP_CAUSE(env->fcr31, 0);
368 #endif
369 }
370
371 /* TLB management */
372 void cpu_mips_tlb_flush (CPUState *env, int flush_global)
373 {
374     /* Flush qemu's TLB and discard all shadowed entries.  */
375     tlb_flush (env, flush_global);
376     env->tlb_in_use = env->nb_tlb;
377 }
378
379 static void r4k_mips_tlb_flush_extra (CPUState *env, int first)
380 {
381     /* Discard entries from env->tlb[first] onwards.  */
382     while (env->tlb_in_use > first) {
383         r4k_invalidate_tlb(env, --env->tlb_in_use, 0);
384     }
385 }
386
387 static void r4k_fill_tlb (int idx)
388 {
389     r4k_tlb_t *tlb;
390
391     /* XXX: detect conflicting TLBs and raise a MCHECK exception when needed */
392     tlb = &env->mmu.r4k.tlb[idx];
393     tlb->VPN = env->CP0_EntryHi & (TARGET_PAGE_MASK << 1);
394 #ifdef TARGET_MIPS64
395     tlb->VPN &= 0xC00000FFFFFFFFFFULL;
396 #endif
397     tlb->ASID = env->CP0_EntryHi & 0xFF;
398     tlb->PageMask = env->CP0_PageMask;
399     tlb->G = env->CP0_EntryLo0 & env->CP0_EntryLo1 & 1;
400     tlb->V0 = (env->CP0_EntryLo0 & 2) != 0;
401     tlb->D0 = (env->CP0_EntryLo0 & 4) != 0;
402     tlb->C0 = (env->CP0_EntryLo0 >> 3) & 0x7;
403     tlb->PFN[0] = (env->CP0_EntryLo0 >> 6) << 12;
404     tlb->V1 = (env->CP0_EntryLo1 & 2) != 0;
405     tlb->D1 = (env->CP0_EntryLo1 & 4) != 0;
406     tlb->C1 = (env->CP0_EntryLo1 >> 3) & 0x7;
407     tlb->PFN[1] = (env->CP0_EntryLo1 >> 6) << 12;
408 }
409
410 void r4k_do_tlbwi (void)
411 {
412     /* Discard cached TLB entries.  We could avoid doing this if the
413        tlbwi is just upgrading access permissions on the current entry;
414        that might be a further win.  */
415     r4k_mips_tlb_flush_extra (env, env->nb_tlb);
416
417     r4k_invalidate_tlb(env, env->CP0_Index % env->nb_tlb, 0);
418     r4k_fill_tlb(env->CP0_Index % env->nb_tlb);
419 }
420
421 void r4k_do_tlbwr (void)
422 {
423     int r = cpu_mips_get_random(env);
424
425     r4k_invalidate_tlb(env, r, 1);
426     r4k_fill_tlb(r);
427 }
428
429 void r4k_do_tlbp (void)
430 {
431     r4k_tlb_t *tlb;
432     target_ulong mask;
433     target_ulong tag;
434     target_ulong VPN;
435     uint8_t ASID;
436     int i;
437
438     ASID = env->CP0_EntryHi & 0xFF;
439     for (i = 0; i < env->nb_tlb; i++) {
440         tlb = &env->mmu.r4k.tlb[i];
441         /* 1k pages are not supported. */
442         mask = tlb->PageMask | ~(TARGET_PAGE_MASK << 1);
443         tag = env->CP0_EntryHi & ~mask;
444         VPN = tlb->VPN & ~mask;
445         /* Check ASID, virtual page number & size */
446         if ((tlb->G == 1 || tlb->ASID == ASID) && VPN == tag) {
447             /* TLB match */
448             env->CP0_Index = i;
449             break;
450         }
451     }
452     if (i == env->nb_tlb) {
453         /* No match.  Discard any shadow entries, if any of them match.  */
454         for (i = env->nb_tlb; i < env->tlb_in_use; i++) {
455             tlb = &env->mmu.r4k.tlb[i];
456             /* 1k pages are not supported. */
457             mask = tlb->PageMask | ~(TARGET_PAGE_MASK << 1);
458             tag = env->CP0_EntryHi & ~mask;
459             VPN = tlb->VPN & ~mask;
460             /* Check ASID, virtual page number & size */
461             if ((tlb->G == 1 || tlb->ASID == ASID) && VPN == tag) {
462                 r4k_mips_tlb_flush_extra (env, i);
463                 break;
464             }
465         }
466
467         env->CP0_Index |= 0x80000000;
468     }
469 }
470
471 void r4k_do_tlbr (void)
472 {
473     r4k_tlb_t *tlb;
474     uint8_t ASID;
475
476     ASID = env->CP0_EntryHi & 0xFF;
477     tlb = &env->mmu.r4k.tlb[env->CP0_Index % env->nb_tlb];
478
479     /* If this will change the current ASID, flush qemu's TLB.  */
480     if (ASID != tlb->ASID)
481         cpu_mips_tlb_flush (env, 1);
482
483     r4k_mips_tlb_flush_extra(env, env->nb_tlb);
484
485     env->CP0_EntryHi = tlb->VPN | tlb->ASID;
486     env->CP0_PageMask = tlb->PageMask;
487     env->CP0_EntryLo0 = tlb->G | (tlb->V0 << 1) | (tlb->D0 << 2) |
488                         (tlb->C0 << 3) | (tlb->PFN[0] >> 6);
489     env->CP0_EntryLo1 = tlb->G | (tlb->V1 << 1) | (tlb->D1 << 2) |
490                         (tlb->C1 << 3) | (tlb->PFN[1] >> 6);
491 }
492
493 #endif /* !CONFIG_USER_ONLY */
494
495 void dump_ldst (const unsigned char *func)
496 {
497     if (loglevel)
498         fprintf(logfile, "%s => " TARGET_FMT_lx " " TARGET_FMT_lx "\n", __func__, T0, T1);
499 }
500
501 void dump_sc (void)
502 {
503     if (loglevel) {
504         fprintf(logfile, "%s " TARGET_FMT_lx " at " TARGET_FMT_lx " (" TARGET_FMT_lx ")\n", __func__,
505                 T1, T0, env->CP0_LLAddr);
506     }
507 }
508
509 void debug_pre_eret (void)
510 {
511     fprintf(logfile, "ERET: PC " TARGET_FMT_lx " EPC " TARGET_FMT_lx,
512             env->PC, env->CP0_EPC);
513     if (env->CP0_Status & (1 << CP0St_ERL))
514         fprintf(logfile, " ErrorEPC " TARGET_FMT_lx, env->CP0_ErrorEPC);
515     if (env->hflags & MIPS_HFLAG_DM)
516         fprintf(logfile, " DEPC " TARGET_FMT_lx, env->CP0_DEPC);
517     fputs("\n", logfile);
518 }
519
520 void debug_post_eret (void)
521 {
522     fprintf(logfile, "  =>  PC " TARGET_FMT_lx " EPC " TARGET_FMT_lx,
523             env->PC, env->CP0_EPC);
524     if (env->CP0_Status & (1 << CP0St_ERL))
525         fprintf(logfile, " ErrorEPC " TARGET_FMT_lx, env->CP0_ErrorEPC);
526     if (env->hflags & MIPS_HFLAG_DM)
527         fprintf(logfile, " DEPC " TARGET_FMT_lx, env->CP0_DEPC);
528     if (env->hflags & MIPS_HFLAG_UM)
529         fputs(", UM\n", logfile);
530     else
531         fputs("\n", logfile);
532 }
533
534 void do_pmon (int function)
535 {
536     function /= 2;
537     switch (function) {
538     case 2: /* TODO: char inbyte(int waitflag); */
539         if (env->gpr[4] == 0)
540             env->gpr[2] = -1;
541         /* Fall through */
542     case 11: /* TODO: char inbyte (void); */
543         env->gpr[2] = -1;
544         break;
545     case 3:
546     case 12:
547         printf("%c", (char)(env->gpr[4] & 0xFF));
548         break;
549     case 17:
550         break;
551     case 158:
552         {
553             unsigned char *fmt = (void *)(unsigned long)env->gpr[4];
554             printf("%s", fmt);
555         }
556         break;
557     }
558 }
559
560 #if !defined(CONFIG_USER_ONLY) 
561
562 static void do_unaligned_access (target_ulong addr, int is_write, int is_user, void *retaddr);
563
564 #define MMUSUFFIX _mmu
565 #define ALIGNED_ONLY
566
567 #define SHIFT 0
568 #include "softmmu_template.h"
569
570 #define SHIFT 1
571 #include "softmmu_template.h"
572
573 #define SHIFT 2
574 #include "softmmu_template.h"
575
576 #define SHIFT 3
577 #include "softmmu_template.h"
578
579 static void do_unaligned_access (target_ulong addr, int is_write, int is_user, void *retaddr)
580 {
581     env->CP0_BadVAddr = addr;
582     do_restore_state (retaddr);
583     do_raise_exception ((is_write == 1) ? EXCP_AdES : EXCP_AdEL);
584 }
585
586 void tlb_fill (target_ulong addr, int is_write, int is_user, void *retaddr)
587 {
588     TranslationBlock *tb;
589     CPUState *saved_env;
590     unsigned long pc;
591     int ret;
592
593     /* XXX: hack to restore env in all cases, even if not called from
594        generated code */
595     saved_env = env;
596     env = cpu_single_env;
597     ret = cpu_mips_handle_mmu_fault(env, addr, is_write, is_user, 1);
598     if (ret) {
599         if (retaddr) {
600             /* now we have a real cpu fault */
601             pc = (unsigned long)retaddr;
602             tb = tb_find_pc(pc);
603             if (tb) {
604                 /* the PC is inside the translated code. It means that we have
605                    a virtual CPU fault */
606                 cpu_restore_state(tb, env, pc, NULL);
607             }
608         }
609         do_raise_exception_err(env->exception_index, env->error_code);
610     }
611     env = saved_env;
612 }
613
614 #endif