|
|
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 <stdarg.h>
21: #include <stdlib.h>
22: #include <stdio.h>
23: #include <string.h>
24: #include <inttypes.h>
25: #include <signal.h>
26: #include <assert.h>
27:
28: #include "cpu.h"
29: #include "exec-all.h"
30:
1.1.1.4 ! root 31: enum {
! 32: TLBRET_DIRTY = -4,
! 33: TLBRET_INVALID = -3,
! 34: TLBRET_NOMATCH = -2,
! 35: TLBRET_BADADDR = -1,
! 36: TLBRET_MATCH = 0
! 37: };
! 38:
1.1 root 39: /* MIPS32 4K MMU emulation */
40: #ifdef MIPS_USES_R4K_TLB
41: static int map_address (CPUState *env, target_ulong *physical, int *prot,
42: target_ulong address, int rw, int access_type)
43: {
1.1.1.4 ! root 44: target_ulong tag = address & (TARGET_PAGE_MASK << 1);
! 45: uint8_t ASID = env->CP0_EntryHi & 0xFF;
1.1 root 46: tlb_t *tlb;
47: int i, n;
48:
49: for (i = 0; i < MIPS_TLB_NB; i++) {
50: tlb = &env->tlb[i];
51: /* Check ASID, virtual page number & size */
52: if ((tlb->G == 1 || tlb->ASID == ASID) &&
1.1.1.2 root 53: tlb->VPN == tag && address < tlb->end2) {
1.1 root 54: /* TLB match */
1.1.1.4 ! root 55: n = (address >> TARGET_PAGE_BITS) & 1;
1.1 root 56: /* Check access rights */
1.1.1.4 ! root 57: if (!(n ? tlb->V1 : tlb->V0))
! 58: return TLBRET_INVALID;
! 59: if (rw == 0 || (n ? tlb->D1 : tlb->D0)) {
! 60: *physical = tlb->PFN[n] | (address & ~TARGET_PAGE_MASK);
1.1 root 61: *prot = PAGE_READ;
1.1.1.3 root 62: if (n ? tlb->D1 : tlb->D0)
1.1 root 63: *prot |= PAGE_WRITE;
1.1.1.4 ! root 64: return TLBRET_MATCH;
1.1 root 65: }
1.1.1.4 ! root 66: return TLBRET_DIRTY;
1.1 root 67: }
68: }
1.1.1.4 ! root 69: return TLBRET_NOMATCH;
1.1 root 70: }
71: #endif
72:
1.1.1.4 ! root 73: static int get_physical_address (CPUState *env, target_ulong *physical,
! 74: int *prot, target_ulong address,
! 75: int rw, int access_type)
1.1 root 76: {
77: /* User mode can only access useg */
1.1.1.4 ! root 78: int user_mode = (env->hflags & MIPS_HFLAG_MODE) == MIPS_HFLAG_UM;
! 79: int ret = TLBRET_MATCH;
! 80:
1.1 root 81: #if 0
82: if (logfile) {
83: fprintf(logfile, "user mode %d h %08x\n",
84: user_mode, env->hflags);
85: }
86: #endif
87: if (user_mode && address > 0x7FFFFFFFUL)
1.1.1.4 ! root 88: return TLBRET_BADADDR;
1.1 root 89: if (address < 0x80000000UL) {
90: if (!(env->hflags & MIPS_HFLAG_ERL)) {
91: #ifdef MIPS_USES_R4K_TLB
92: ret = map_address(env, physical, prot, address, rw, access_type);
93: #else
94: *physical = address + 0x40000000UL;
95: *prot = PAGE_READ | PAGE_WRITE;
96: #endif
97: } else {
98: *physical = address;
99: *prot = PAGE_READ | PAGE_WRITE;
100: }
101: } else if (address < 0xA0000000UL) {
102: /* kseg0 */
103: /* XXX: check supervisor mode */
104: *physical = address - 0x80000000UL;
105: *prot = PAGE_READ | PAGE_WRITE;
106: } else if (address < 0xC0000000UL) {
107: /* kseg1 */
108: /* XXX: check supervisor mode */
109: *physical = address - 0xA0000000UL;
110: *prot = PAGE_READ | PAGE_WRITE;
111: } else if (address < 0xE0000000UL) {
112: /* kseg2 */
113: #ifdef MIPS_USES_R4K_TLB
114: ret = map_address(env, physical, prot, address, rw, access_type);
115: #else
116: *physical = address;
117: *prot = PAGE_READ | PAGE_WRITE;
118: #endif
119: } else {
120: /* kseg3 */
121: /* XXX: check supervisor mode */
122: /* XXX: debug segment is not emulated */
123: #ifdef MIPS_USES_R4K_TLB
124: ret = map_address(env, physical, prot, address, rw, access_type);
125: #else
126: *physical = address;
127: *prot = PAGE_READ | PAGE_WRITE;
128: #endif
129: }
130: #if 0
131: if (logfile) {
132: fprintf(logfile, "%08x %d %d => %08x %d (%d)\n", address, rw,
133: access_type, *physical, *prot, ret);
134: }
135: #endif
136:
137: return ret;
138: }
139:
140: #if defined(CONFIG_USER_ONLY)
141: target_ulong cpu_get_phys_page_debug(CPUState *env, target_ulong addr)
142: {
143: return addr;
144: }
145: #else
146: target_ulong cpu_get_phys_page_debug(CPUState *env, target_ulong addr)
147: {
148: target_ulong phys_addr;
149: int prot;
150:
151: if (get_physical_address(env, &phys_addr, &prot, addr, 0, ACCESS_INT) != 0)
152: return -1;
153: return phys_addr;
154: }
155:
156: void cpu_mips_init_mmu (CPUState *env)
157: {
158: }
159: #endif /* !defined(CONFIG_USER_ONLY) */
160:
161: int cpu_mips_handle_mmu_fault (CPUState *env, target_ulong address, int rw,
162: int is_user, int is_softmmu)
163: {
164: target_ulong physical;
165: int prot;
166: int exception = 0, error_code = 0;
167: int access_type;
168: int ret = 0;
169:
170: if (logfile) {
1.1.1.2 root 171: #if 0
1.1 root 172: cpu_dump_state(env, logfile, fprintf, 0);
1.1.1.2 root 173: #endif
1.1 root 174: fprintf(logfile, "%s pc %08x ad %08x rw %d is_user %d smmu %d\n",
175: __func__, env->PC, address, rw, is_user, is_softmmu);
176: }
1.1.1.2 root 177:
178: rw &= 1;
179:
1.1 root 180: /* data access */
181: /* XXX: put correct access by using cpu_restore_state()
182: correctly */
183: access_type = ACCESS_INT;
184: if (env->user_mode_only) {
185: /* user mode only emulation */
1.1.1.4 ! root 186: ret = TLBRET_NOMATCH;
1.1 root 187: goto do_fault;
188: }
189: ret = get_physical_address(env, &physical, &prot,
190: address, rw, access_type);
191: if (logfile) {
192: fprintf(logfile, "%s address=%08x ret %d physical %08x prot %d\n",
193: __func__, address, ret, physical, prot);
194: }
1.1.1.4 ! root 195: if (ret == TLBRET_MATCH) {
! 196: ret = tlb_set_page(env, address & TARGET_PAGE_MASK,
! 197: physical & TARGET_PAGE_MASK, prot,
! 198: is_user, is_softmmu);
1.1 root 199: } else if (ret < 0) {
200: do_fault:
201: switch (ret) {
202: default:
1.1.1.4 ! root 203: case TLBRET_BADADDR:
1.1 root 204: /* Reference to kernel address from user mode or supervisor mode */
205: /* Reference to supervisor address from user mode */
206: if (rw)
207: exception = EXCP_AdES;
208: else
209: exception = EXCP_AdEL;
210: break;
1.1.1.4 ! root 211: case TLBRET_NOMATCH:
1.1 root 212: /* No TLB match for a mapped address */
213: if (rw)
214: exception = EXCP_TLBS;
215: else
216: exception = EXCP_TLBL;
217: error_code = 1;
218: break;
1.1.1.4 ! root 219: case TLBRET_INVALID:
1.1 root 220: /* TLB match with no valid bit */
221: if (rw)
222: exception = EXCP_TLBS;
223: else
224: exception = EXCP_TLBL;
225: break;
1.1.1.4 ! root 226: case TLBRET_DIRTY:
1.1 root 227: /* TLB match but 'D' bit is cleared */
228: exception = EXCP_LTLBL;
229: break;
230:
231: }
232: /* Raise exception */
233: env->CP0_BadVAddr = address;
234: env->CP0_Context = (env->CP0_Context & 0xff800000) |
1.1.1.2 root 235: ((address >> 9) & 0x007ffff0);
1.1 root 236: env->CP0_EntryHi =
1.1.1.4 ! root 237: (env->CP0_EntryHi & 0xFF) | (address & (TARGET_PAGE_MASK << 1));
1.1 root 238: env->exception_index = exception;
239: env->error_code = error_code;
240: ret = 1;
241: }
242:
243: return ret;
244: }
245:
246: void do_interrupt (CPUState *env)
247: {
248: target_ulong pc, offset;
249: int cause = -1;
250:
251: if (logfile && env->exception_index != EXCP_EXT_INTERRUPT) {
252: fprintf(logfile, "%s enter: PC %08x EPC %08x cause %d excp %d\n",
253: __func__, env->PC, env->CP0_EPC, cause, env->exception_index);
254: }
255: if (env->exception_index == EXCP_EXT_INTERRUPT &&
256: (env->hflags & MIPS_HFLAG_DM))
257: env->exception_index = EXCP_DINT;
258: offset = 0x180;
259: switch (env->exception_index) {
260: case EXCP_DSS:
261: env->CP0_Debug |= 1 << CP0DB_DSS;
262: /* Debug single step cannot be raised inside a delay slot and
263: * resume will always occur on the next instruction
264: * (but we assume the pc has always been updated during
265: * code translation).
266: */
267: env->CP0_DEPC = env->PC;
268: goto enter_debug_mode;
269: case EXCP_DINT:
270: env->CP0_Debug |= 1 << CP0DB_DINT;
271: goto set_DEPC;
272: case EXCP_DIB:
273: env->CP0_Debug |= 1 << CP0DB_DIB;
274: goto set_DEPC;
275: case EXCP_DBp:
276: env->CP0_Debug |= 1 << CP0DB_DBp;
277: goto set_DEPC;
278: case EXCP_DDBS:
279: env->CP0_Debug |= 1 << CP0DB_DDBS;
280: goto set_DEPC;
281: case EXCP_DDBL:
282: env->CP0_Debug |= 1 << CP0DB_DDBL;
283: goto set_DEPC;
284: set_DEPC:
1.1.1.2 root 285: if (env->hflags & MIPS_HFLAG_BMASK) {
1.1 root 286: /* If the exception was raised from a delay slot,
287: * come back to the jump
288: */
289: env->CP0_DEPC = env->PC - 4;
1.1.1.2 root 290: env->hflags &= ~MIPS_HFLAG_BMASK;
1.1 root 291: } else {
292: env->CP0_DEPC = env->PC;
293: }
294: enter_debug_mode:
295: env->hflags |= MIPS_HFLAG_DM;
296: /* EJTAG probe trap enable is not implemented... */
297: pc = 0xBFC00480;
298: break;
299: case EXCP_RESET:
300: #ifdef MIPS_USES_R4K_TLB
301: env->CP0_random = MIPS_TLB_NB - 1;
302: #endif
303: env->CP0_Wired = 0;
304: env->CP0_Config0 = MIPS_CONFIG0;
305: #if defined (MIPS_CONFIG1)
306: env->CP0_Config1 = MIPS_CONFIG1;
307: #endif
308: #if defined (MIPS_CONFIG2)
309: env->CP0_Config2 = MIPS_CONFIG2;
310: #endif
311: #if defined (MIPS_CONFIG3)
312: env->CP0_Config3 = MIPS_CONFIG3;
313: #endif
314: env->CP0_WatchLo = 0;
315: env->CP0_Status = (1 << CP0St_CU0) | (1 << CP0St_BEV);
316: goto set_error_EPC;
317: case EXCP_SRESET:
318: env->CP0_Status = (1 << CP0St_CU0) | (1 << CP0St_BEV) |
319: (1 << CP0St_SR);
320: env->CP0_WatchLo = 0;
321: goto set_error_EPC;
322: case EXCP_NMI:
323: env->CP0_Status = (1 << CP0St_CU0) | (1 << CP0St_BEV) |
324: (1 << CP0St_NMI);
325: set_error_EPC:
1.1.1.2 root 326: if (env->hflags & MIPS_HFLAG_BMASK) {
1.1 root 327: /* If the exception was raised from a delay slot,
328: * come back to the jump
329: */
330: env->CP0_ErrorEPC = env->PC - 4;
1.1.1.3 root 331: env->hflags &= ~MIPS_HFLAG_BMASK;
1.1 root 332: } else {
333: env->CP0_ErrorEPC = env->PC;
334: }
1.1.1.4 ! root 335: env->hflags |= MIPS_HFLAG_ERL;
! 336: env->CP0_Status |= (1 << CP0St_ERL);
1.1 root 337: pc = 0xBFC00000;
338: break;
339: case EXCP_MCHECK:
340: cause = 24;
341: goto set_EPC;
342: case EXCP_EXT_INTERRUPT:
343: cause = 0;
344: if (env->CP0_Cause & (1 << CP0Ca_IV))
345: offset = 0x200;
346: goto set_EPC;
347: case EXCP_DWATCH:
348: cause = 23;
349: /* XXX: TODO: manage defered watch exceptions */
350: goto set_EPC;
351: case EXCP_AdEL:
352: case EXCP_AdES:
353: cause = 4;
354: goto set_EPC;
355: case EXCP_TLBL:
356: cause = 2;
357: if (env->error_code == 1 && !(env->hflags & MIPS_HFLAG_EXL))
358: offset = 0x000;
359: goto set_EPC;
360: case EXCP_IBE:
361: cause = 6;
362: goto set_EPC;
363: case EXCP_DBE:
364: cause = 7;
365: goto set_EPC;
366: case EXCP_SYSCALL:
367: cause = 8;
368: goto set_EPC;
369: case EXCP_BREAK:
370: cause = 9;
371: goto set_EPC;
372: case EXCP_RI:
373: cause = 10;
374: goto set_EPC;
375: case EXCP_CpU:
376: cause = 11;
1.1.1.2 root 377: env->CP0_Cause = (env->CP0_Cause & ~0x03000000) | (env->error_code << 28);
1.1 root 378: goto set_EPC;
379: case EXCP_OVERFLOW:
380: cause = 12;
381: goto set_EPC;
382: case EXCP_TRAP:
383: cause = 13;
384: goto set_EPC;
385: case EXCP_LTLBL:
386: cause = 1;
387: goto set_EPC;
388: case EXCP_TLBS:
389: cause = 3;
390: if (env->error_code == 1 && !(env->hflags & MIPS_HFLAG_EXL))
391: offset = 0x000;
392: goto set_EPC;
393: set_EPC:
394: if (env->CP0_Status & (1 << CP0St_BEV)) {
395: pc = 0xBFC00200;
396: } else {
397: pc = 0x80000000;
398: }
399: env->hflags |= MIPS_HFLAG_EXL;
1.1.1.4 ! root 400: env->CP0_Status |= (1 << CP0St_EXL);
1.1 root 401: pc += offset;
402: env->CP0_Cause = (env->CP0_Cause & ~0x7C) | (cause << 2);
1.1.1.2 root 403: if (env->hflags & MIPS_HFLAG_BMASK) {
1.1 root 404: /* If the exception was raised from a delay slot,
405: * come back to the jump
406: */
407: env->CP0_EPC = env->PC - 4;
408: env->CP0_Cause |= 0x80000000;
1.1.1.2 root 409: env->hflags &= ~MIPS_HFLAG_BMASK;
1.1 root 410: } else {
411: env->CP0_EPC = env->PC;
412: env->CP0_Cause &= ~0x80000000;
413: }
414: break;
415: default:
416: if (logfile) {
417: fprintf(logfile, "Invalid MIPS exception %d. Exiting\n",
418: env->exception_index);
419: }
420: printf("Invalid MIPS exception %d. Exiting\n", env->exception_index);
421: exit(1);
422: }
423: env->PC = pc;
424: if (logfile && env->exception_index != EXCP_EXT_INTERRUPT) {
425: fprintf(logfile, "%s: PC %08x EPC %08x cause %d excp %d\n"
426: " S %08x C %08x A %08x D %08x\n",
427: __func__, env->PC, env->CP0_EPC, cause, env->exception_index,
428: env->CP0_Status, env->CP0_Cause, env->CP0_BadVAddr,
429: env->CP0_DEPC);
430: }
431: env->exception_index = EXCP_NONE;
432: }
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.