|
|
1.1 ! root 1: /* ! 2: * SH4 emulation ! 3: * ! 4: * Copyright (c) 2005 Samuel Tardieu ! 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: #define MMU_OK 0 ! 32: #define MMU_ITLB_MISS (-1) ! 33: #define MMU_ITLB_MULTIPLE (-2) ! 34: #define MMU_ITLB_VIOLATION (-3) ! 35: #define MMU_DTLB_MISS_READ (-4) ! 36: #define MMU_DTLB_MISS_WRITE (-5) ! 37: #define MMU_DTLB_INITIAL_WRITE (-6) ! 38: #define MMU_DTLB_VIOLATION_READ (-7) ! 39: #define MMU_DTLB_VIOLATION_WRITE (-8) ! 40: #define MMU_DTLB_MULTIPLE (-9) ! 41: #define MMU_DTLB_MISS (-10) ! 42: ! 43: void do_interrupt(CPUState * env) ! 44: { ! 45: if (loglevel & CPU_LOG_INT) { ! 46: const char *expname; ! 47: switch (env->exception_index) { ! 48: case 0x0e0: ! 49: expname = "addr_error"; ! 50: break; ! 51: case 0x040: ! 52: expname = "tlb_miss"; ! 53: break; ! 54: case 0x0a0: ! 55: expname = "tlb_violation"; ! 56: break; ! 57: case 0x180: ! 58: expname = "illegal_instruction"; ! 59: break; ! 60: case 0x1a0: ! 61: expname = "slot_illegal_instruction"; ! 62: break; ! 63: case 0x800: ! 64: expname = "fpu_disable"; ! 65: break; ! 66: case 0x820: ! 67: expname = "slot_fpu"; ! 68: break; ! 69: case 0x100: ! 70: expname = "data_write"; ! 71: break; ! 72: case 0x060: ! 73: expname = "dtlb_miss_write"; ! 74: break; ! 75: case 0x0c0: ! 76: expname = "dtlb_violation_write"; ! 77: break; ! 78: case 0x120: ! 79: expname = "fpu_exception"; ! 80: break; ! 81: case 0x080: ! 82: expname = "initial_page_write"; ! 83: break; ! 84: case 0x160: ! 85: expname = "trapa"; ! 86: break; ! 87: default: ! 88: expname = "???"; ! 89: break; ! 90: } ! 91: fprintf(logfile, "exception 0x%03x [%s] raised\n", ! 92: env->exception_index, expname); ! 93: cpu_dump_state(env, logfile, fprintf, 0); ! 94: } ! 95: ! 96: env->ssr = env->sr; ! 97: env->spc = env->spc; ! 98: env->sgr = env->gregs[15]; ! 99: env->sr |= SR_BL | SR_MD | SR_RB; ! 100: ! 101: env->expevt = env->exception_index & 0x7ff; ! 102: switch (env->exception_index) { ! 103: case 0x040: ! 104: case 0x060: ! 105: case 0x080: ! 106: env->pc = env->vbr + 0x400; ! 107: break; ! 108: case 0x140: ! 109: env->pc = 0xa0000000; ! 110: break; ! 111: default: ! 112: env->pc = env->vbr + 0x100; ! 113: break; ! 114: } ! 115: } ! 116: ! 117: static void update_itlb_use(CPUState * env, int itlbnb) ! 118: { ! 119: uint8_t or_mask = 0, and_mask = (uint8_t) - 1; ! 120: ! 121: switch (itlbnb) { ! 122: case 0: ! 123: and_mask = 0x7f; ! 124: break; ! 125: case 1: ! 126: and_mask = 0xe7; ! 127: or_mask = 0x80; ! 128: break; ! 129: case 2: ! 130: and_mask = 0xfb; ! 131: or_mask = 0x50; ! 132: break; ! 133: case 3: ! 134: or_mask = 0x2c; ! 135: break; ! 136: } ! 137: ! 138: env->mmucr &= (and_mask << 24); ! 139: env->mmucr |= (or_mask << 24); ! 140: } ! 141: ! 142: static int itlb_replacement(CPUState * env) ! 143: { ! 144: if ((env->mmucr & 0xe0000000) == 0xe0000000) ! 145: return 0; ! 146: if ((env->mmucr & 0x98000000) == 0x08000000) ! 147: return 1; ! 148: if ((env->mmucr & 0x54000000) == 0x04000000) ! 149: return 2; ! 150: if ((env->mmucr & 0x2c000000) == 0x00000000) ! 151: return 3; ! 152: assert(0); ! 153: } ! 154: ! 155: /* Find the corresponding entry in the right TLB ! 156: Return entry, MMU_DTLB_MISS or MMU_DTLB_MULTIPLE ! 157: */ ! 158: static int find_tlb_entry(CPUState * env, target_ulong address, ! 159: tlb_t * entries, uint8_t nbtlb, int use_asid) ! 160: { ! 161: int match = MMU_DTLB_MISS; ! 162: uint32_t start, end; ! 163: uint8_t asid; ! 164: int i; ! 165: ! 166: asid = env->pteh & 0xff; ! 167: ! 168: for (i = 0; i < nbtlb; i++) { ! 169: if (!entries[i].v) ! 170: continue; /* Invalid entry */ ! 171: if (use_asid && entries[i].asid != asid && !entries[i].sh) ! 172: continue; /* Bad ASID */ ! 173: #if 0 ! 174: switch (entries[i].sz) { ! 175: case 0: ! 176: size = 1024; /* 1kB */ ! 177: break; ! 178: case 1: ! 179: size = 4 * 1024; /* 4kB */ ! 180: break; ! 181: case 2: ! 182: size = 64 * 1024; /* 64kB */ ! 183: break; ! 184: case 3: ! 185: size = 1024 * 1024; /* 1MB */ ! 186: break; ! 187: default: ! 188: assert(0); ! 189: } ! 190: #endif ! 191: start = (entries[i].vpn << 10) & ~(entries[i].size - 1); ! 192: end = start + entries[i].size - 1; ! 193: if (address >= start && address <= end) { /* Match */ ! 194: if (match != -1) ! 195: return MMU_DTLB_MULTIPLE; /* Multiple match */ ! 196: match = i; ! 197: } ! 198: } ! 199: return match; ! 200: } ! 201: ! 202: /* Find itlb entry - update itlb from utlb if necessary and asked for ! 203: Return entry, MMU_ITLB_MISS, MMU_ITLB_MULTIPLE or MMU_DTLB_MULTIPLE ! 204: Update the itlb from utlb if update is not 0 ! 205: */ ! 206: int find_itlb_entry(CPUState * env, target_ulong address, ! 207: int use_asid, int update) ! 208: { ! 209: int e, n; ! 210: ! 211: e = find_tlb_entry(env, address, env->itlb, ITLB_SIZE, use_asid); ! 212: if (e == MMU_DTLB_MULTIPLE) ! 213: e = MMU_ITLB_MULTIPLE; ! 214: else if (e == MMU_DTLB_MISS && update) { ! 215: e = find_tlb_entry(env, address, env->utlb, UTLB_SIZE, use_asid); ! 216: if (e >= 0) { ! 217: n = itlb_replacement(env); ! 218: env->itlb[n] = env->utlb[e]; ! 219: e = n; ! 220: } ! 221: } ! 222: if (e >= 0) ! 223: update_itlb_use(env, e); ! 224: return e; ! 225: } ! 226: ! 227: /* Find utlb entry ! 228: Return entry, MMU_DTLB_MISS, MMU_DTLB_MULTIPLE */ ! 229: int find_utlb_entry(CPUState * env, target_ulong address, int use_asid) ! 230: { ! 231: uint8_t urb, urc; ! 232: ! 233: /* Increment URC */ ! 234: urb = ((env->mmucr) >> 18) & 0x3f; ! 235: urc = ((env->mmucr) >> 10) & 0x3f; ! 236: urc++; ! 237: if (urc == urb || urc == UTLB_SIZE - 1) ! 238: urc = 0; ! 239: env->mmucr = (env->mmucr & 0xffff03ff) | (urc << 10); ! 240: ! 241: /* Return entry */ ! 242: return find_tlb_entry(env, address, env->utlb, UTLB_SIZE, use_asid); ! 243: } ! 244: ! 245: /* Match address against MMU ! 246: Return MMU_OK, MMU_DTLB_MISS_READ, MMU_DTLB_MISS_WRITE, ! 247: MMU_DTLB_INITIAL_WRITE, MMU_DTLB_VIOLATION_READ, ! 248: MMU_DTLB_VIOLATION_WRITE, MMU_ITLB_MISS, ! 249: MMU_ITLB_MULTIPLE, MMU_ITLB_VIOLATION ! 250: */ ! 251: static int get_mmu_address(CPUState * env, target_ulong * physical, ! 252: int *prot, target_ulong address, ! 253: int rw, int access_type) ! 254: { ! 255: int use_asid, is_code, n; ! 256: tlb_t *matching = NULL; ! 257: ! 258: use_asid = (env->mmucr & MMUCR_SV) == 0 && (env->sr & SR_MD) == 0; ! 259: is_code = env->pc == address; /* Hack */ ! 260: ! 261: /* Use a hack to find if this is an instruction or data access */ ! 262: if (env->pc == address && !(rw & PAGE_WRITE)) { ! 263: n = find_itlb_entry(env, address, use_asid, 1); ! 264: if (n >= 0) { ! 265: matching = &env->itlb[n]; ! 266: if ((env->sr & SR_MD) & !(matching->pr & 2)) ! 267: n = MMU_ITLB_VIOLATION; ! 268: else ! 269: *prot = PAGE_READ; ! 270: } ! 271: } else { ! 272: n = find_utlb_entry(env, address, use_asid); ! 273: if (n >= 0) { ! 274: matching = &env->utlb[n]; ! 275: switch ((matching->pr << 1) | ((env->sr & SR_MD) ? 1 : 0)) { ! 276: case 0: /* 000 */ ! 277: case 2: /* 010 */ ! 278: n = (rw & PAGE_WRITE) ? MMU_DTLB_VIOLATION_WRITE : ! 279: MMU_DTLB_VIOLATION_READ; ! 280: break; ! 281: case 1: /* 001 */ ! 282: case 4: /* 100 */ ! 283: case 5: /* 101 */ ! 284: if (rw & PAGE_WRITE) ! 285: n = MMU_DTLB_VIOLATION_WRITE; ! 286: else ! 287: *prot = PAGE_READ; ! 288: break; ! 289: case 3: /* 011 */ ! 290: case 6: /* 110 */ ! 291: case 7: /* 111 */ ! 292: *prot = rw & (PAGE_READ | PAGE_WRITE); ! 293: break; ! 294: } ! 295: } else if (n == MMU_DTLB_MISS) { ! 296: n = (rw & PAGE_WRITE) ? MMU_DTLB_MISS_WRITE : ! 297: MMU_DTLB_MISS_READ; ! 298: } ! 299: } ! 300: if (n >= 0) { ! 301: *physical = ((matching->ppn << 10) & ~(matching->size - 1)) | ! 302: (address & (matching->size - 1)); ! 303: if ((rw & PAGE_WRITE) & !matching->d) ! 304: n = MMU_DTLB_INITIAL_WRITE; ! 305: else ! 306: n = MMU_OK; ! 307: } ! 308: return n; ! 309: } ! 310: ! 311: int get_physical_address(CPUState * env, target_ulong * physical, ! 312: int *prot, target_ulong address, ! 313: int rw, int access_type) ! 314: { ! 315: /* P1, P2 and P4 areas do not use translation */ ! 316: if ((address >= 0x80000000 && address < 0xc0000000) || ! 317: address >= 0xe0000000) { ! 318: if (!(env->sr & SR_MD) ! 319: && (address < 0xe0000000 || address > 0xe4000000)) { ! 320: /* Unauthorized access in user mode (only store queues are available) */ ! 321: fprintf(stderr, "Unauthorized access\n"); ! 322: return (rw & PAGE_WRITE) ? MMU_DTLB_MISS_WRITE : ! 323: MMU_DTLB_MISS_READ; ! 324: } ! 325: /* Mask upper 3 bits */ ! 326: *physical = address & 0x1FFFFFFF; ! 327: *prot = PAGE_READ | PAGE_WRITE; ! 328: return MMU_OK; ! 329: } ! 330: ! 331: /* If MMU is disabled, return the corresponding physical page */ ! 332: if (!env->mmucr & MMUCR_AT) { ! 333: *physical = address & 0x1FFFFFFF; ! 334: *prot = PAGE_READ | PAGE_WRITE; ! 335: return MMU_OK; ! 336: } ! 337: ! 338: /* We need to resort to the MMU */ ! 339: return get_mmu_address(env, physical, prot, address, rw, access_type); ! 340: } ! 341: ! 342: int cpu_sh4_handle_mmu_fault(CPUState * env, target_ulong address, int rw, ! 343: int is_user, int is_softmmu) ! 344: { ! 345: target_ulong physical, page_offset, page_size; ! 346: int prot, ret, access_type; ! 347: ! 348: /* XXXXX */ ! 349: #if 0 ! 350: fprintf(stderr, "%s pc %08x ad %08x rw %d is_user %d smmu %d\n", ! 351: __func__, env->pc, address, rw, is_user, is_softmmu); ! 352: #endif ! 353: ! 354: access_type = ACCESS_INT; ! 355: ret = ! 356: get_physical_address(env, &physical, &prot, address, rw, ! 357: access_type); ! 358: ! 359: if (ret != MMU_OK) { ! 360: env->tea = address; ! 361: switch (ret) { ! 362: case MMU_ITLB_MISS: ! 363: case MMU_DTLB_MISS_READ: ! 364: env->exception_index = 0x040; ! 365: break; ! 366: case MMU_DTLB_MULTIPLE: ! 367: case MMU_ITLB_MULTIPLE: ! 368: env->exception_index = 0x140; ! 369: break; ! 370: case MMU_ITLB_VIOLATION: ! 371: env->exception_index = 0x0a0; ! 372: break; ! 373: case MMU_DTLB_MISS_WRITE: ! 374: env->exception_index = 0x060; ! 375: break; ! 376: case MMU_DTLB_INITIAL_WRITE: ! 377: env->exception_index = 0x080; ! 378: break; ! 379: case MMU_DTLB_VIOLATION_READ: ! 380: env->exception_index = 0x0a0; ! 381: break; ! 382: case MMU_DTLB_VIOLATION_WRITE: ! 383: env->exception_index = 0x0c0; ! 384: break; ! 385: default: ! 386: assert(0); ! 387: } ! 388: return 1; ! 389: } ! 390: ! 391: page_size = TARGET_PAGE_SIZE; ! 392: page_offset = ! 393: (address - (address & TARGET_PAGE_MASK)) & ~(page_size - 1); ! 394: address = (address & TARGET_PAGE_MASK) + page_offset; ! 395: physical = (physical & TARGET_PAGE_MASK) + page_offset; ! 396: ! 397: return tlb_set_page(env, address, physical, prot, is_user, is_softmmu); ! 398: }
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.