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