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