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