]> git.proxmox.com Git - qemu.git/blob - target-mips/op_helper.c
mips user emulation
[qemu.git] / 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