| File: | target-sh4/helper.c |
| Location: | line 518, column 5 |
| Description: | Undefined or garbage value returned to caller |
| 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, see <http://www.gnu.org/licenses/>. | |||
| 18 | */ | |||
| 19 | #include <stdarg.h> | |||
| 20 | #include <stdlib.h> | |||
| 21 | #include <stdio.h> | |||
| 22 | #include <string.h> | |||
| 23 | #include <inttypes.h> | |||
| 24 | #include <signal.h> | |||
| 25 | ||||
| 26 | #include "cpu.h" | |||
| 27 | ||||
| 28 | #if !defined(CONFIG_USER_ONLY) | |||
| 29 | #include "hw/sh4/sh_intc.h" | |||
| 30 | #endif | |||
| 31 | ||||
| 32 | #if defined(CONFIG_USER_ONLY) | |||
| 33 | ||||
| 34 | void superh_cpu_do_interrupt(CPUState *cs) | |||
| 35 | { | |||
| 36 | SuperHCPU *cpu = SUPERH_CPU(cs)((SuperHCPU *)object_dynamic_cast_assert(((Object *)((cs))), ( "superh-cpu"), "/home/stefan/src/qemu/qemu.org/qemu/target-sh4/helper.c" , 36, __func__)); | |||
| 37 | CPUSH4State *env = &cpu->env; | |||
| 38 | ||||
| 39 | env->exception_index = -1; | |||
| 40 | } | |||
| 41 | ||||
| 42 | int cpu_sh4_handle_mmu_fault(CPUSH4State * env, target_ulong address, int rw, | |||
| 43 | int mmu_idx) | |||
| 44 | { | |||
| 45 | env->tea = address; | |||
| 46 | env->exception_index = -1; | |||
| 47 | switch (rw) { | |||
| 48 | case 0: | |||
| 49 | env->exception_index = 0x0a0; | |||
| 50 | break; | |||
| 51 | case 1: | |||
| 52 | env->exception_index = 0x0c0; | |||
| 53 | break; | |||
| 54 | case 2: | |||
| 55 | env->exception_index = 0x0a0; | |||
| 56 | break; | |||
| 57 | } | |||
| 58 | return 1; | |||
| 59 | } | |||
| 60 | ||||
| 61 | int cpu_sh4_is_cached(CPUSH4State * env, target_ulong addr) | |||
| 62 | { | |||
| 63 | /* For user mode, only U0 area is cachable. */ | |||
| 64 | return !(addr & 0x80000000); | |||
| 65 | } | |||
| 66 | ||||
| 67 | #else /* !CONFIG_USER_ONLY */ | |||
| 68 | ||||
| 69 | #define MMU_OK0 0 | |||
| 70 | #define MMU_ITLB_MISS(-1) (-1) | |||
| 71 | #define MMU_ITLB_MULTIPLE(-2) (-2) | |||
| 72 | #define MMU_ITLB_VIOLATION(-3) (-3) | |||
| 73 | #define MMU_DTLB_MISS_READ(-4) (-4) | |||
| 74 | #define MMU_DTLB_MISS_WRITE(-5) (-5) | |||
| 75 | #define MMU_DTLB_INITIAL_WRITE(-6) (-6) | |||
| 76 | #define MMU_DTLB_VIOLATION_READ(-7) (-7) | |||
| 77 | #define MMU_DTLB_VIOLATION_WRITE(-8) (-8) | |||
| 78 | #define MMU_DTLB_MULTIPLE(-9) (-9) | |||
| 79 | #define MMU_DTLB_MISS(-10) (-10) | |||
| 80 | #define MMU_IADDR_ERROR(-11) (-11) | |||
| 81 | #define MMU_DADDR_ERROR_READ(-12) (-12) | |||
| 82 | #define MMU_DADDR_ERROR_WRITE(-13) (-13) | |||
| 83 | ||||
| 84 | void superh_cpu_do_interrupt(CPUState *cs) | |||
| 85 | { | |||
| 86 | SuperHCPU *cpu = SUPERH_CPU(cs)((SuperHCPU *)object_dynamic_cast_assert(((Object *)((cs))), ( "superh-cpu"), "/home/stefan/src/qemu/qemu.org/qemu/target-sh4/helper.c" , 86, __func__)); | |||
| 87 | CPUSH4State *env = &cpu->env; | |||
| 88 | int do_irq = cs->interrupt_request & CPU_INTERRUPT_HARD0x0002; | |||
| 89 | int do_exp, irq_vector = env->exception_index; | |||
| 90 | ||||
| 91 | /* prioritize exceptions over interrupts */ | |||
| 92 | ||||
| 93 | do_exp = env->exception_index != -1; | |||
| 94 | do_irq = do_irq && (env->exception_index == -1); | |||
| 95 | ||||
| 96 | if (env->sr & SR_BL(1 << 28)) { | |||
| 97 | if (do_exp && env->exception_index != 0x1e0) { | |||
| 98 | env->exception_index = 0x000; /* masked exception -> reset */ | |||
| 99 | } | |||
| 100 | if (do_irq && !env->in_sleep) { | |||
| 101 | return; /* masked */ | |||
| 102 | } | |||
| 103 | } | |||
| 104 | env->in_sleep = 0; | |||
| 105 | ||||
| 106 | if (do_irq) { | |||
| 107 | irq_vector = sh_intc_get_pending_vector(env->intc_handle, | |||
| 108 | (env->sr >> 4) & 0xf); | |||
| 109 | if (irq_vector == -1) { | |||
| 110 | return; /* masked */ | |||
| 111 | } | |||
| 112 | } | |||
| 113 | ||||
| 114 | if (qemu_loglevel_mask(CPU_LOG_INT(1 << 4))) { | |||
| 115 | const char *expname; | |||
| 116 | switch (env->exception_index) { | |||
| 117 | case 0x0e0: | |||
| 118 | expname = "addr_error"; | |||
| 119 | break; | |||
| 120 | case 0x040: | |||
| 121 | expname = "tlb_miss"; | |||
| 122 | break; | |||
| 123 | case 0x0a0: | |||
| 124 | expname = "tlb_violation"; | |||
| 125 | break; | |||
| 126 | case 0x180: | |||
| 127 | expname = "illegal_instruction"; | |||
| 128 | break; | |||
| 129 | case 0x1a0: | |||
| 130 | expname = "slot_illegal_instruction"; | |||
| 131 | break; | |||
| 132 | case 0x800: | |||
| 133 | expname = "fpu_disable"; | |||
| 134 | break; | |||
| 135 | case 0x820: | |||
| 136 | expname = "slot_fpu"; | |||
| 137 | break; | |||
| 138 | case 0x100: | |||
| 139 | expname = "data_write"; | |||
| 140 | break; | |||
| 141 | case 0x060: | |||
| 142 | expname = "dtlb_miss_write"; | |||
| 143 | break; | |||
| 144 | case 0x0c0: | |||
| 145 | expname = "dtlb_violation_write"; | |||
| 146 | break; | |||
| 147 | case 0x120: | |||
| 148 | expname = "fpu_exception"; | |||
| 149 | break; | |||
| 150 | case 0x080: | |||
| 151 | expname = "initial_page_write"; | |||
| 152 | break; | |||
| 153 | case 0x160: | |||
| 154 | expname = "trapa"; | |||
| 155 | break; | |||
| 156 | default: | |||
| 157 | expname = do_irq ? "interrupt" : "???"; | |||
| 158 | break; | |||
| 159 | } | |||
| 160 | qemu_log("exception 0x%03x [%s] raised\n", | |||
| 161 | irq_vector, expname); | |||
| 162 | log_cpu_state(cs, 0); | |||
| 163 | } | |||
| 164 | ||||
| 165 | env->ssr = env->sr; | |||
| 166 | env->spc = env->pc; | |||
| 167 | env->sgr = env->gregs[15]; | |||
| 168 | env->sr |= SR_BL(1 << 28) | SR_MD(1 << 30) | SR_RB(1 << 29); | |||
| 169 | ||||
| 170 | if (env->flags & (DELAY_SLOT(1 << 0) | DELAY_SLOT_CONDITIONAL(1 << 1))) { | |||
| 171 | /* Branch instruction should be executed again before delay slot. */ | |||
| 172 | env->spc -= 2; | |||
| 173 | /* Clear flags for exception/interrupt routine. */ | |||
| 174 | env->flags &= ~(DELAY_SLOT(1 << 0) | DELAY_SLOT_CONDITIONAL(1 << 1) | DELAY_SLOT_TRUE(1 << 2)); | |||
| 175 | } | |||
| 176 | if (env->flags & DELAY_SLOT_CLEARME(1 << 3)) | |||
| 177 | env->flags = 0; | |||
| 178 | ||||
| 179 | if (do_exp) { | |||
| 180 | env->expevt = env->exception_index; | |||
| 181 | switch (env->exception_index) { | |||
| 182 | case 0x000: | |||
| 183 | case 0x020: | |||
| 184 | case 0x140: | |||
| 185 | env->sr &= ~SR_FD(1 << 15); | |||
| 186 | env->sr |= 0xf << 4; /* IMASK */ | |||
| 187 | env->pc = 0xa0000000; | |||
| 188 | break; | |||
| 189 | case 0x040: | |||
| 190 | case 0x060: | |||
| 191 | env->pc = env->vbr + 0x400; | |||
| 192 | break; | |||
| 193 | case 0x160: | |||
| 194 | env->spc += 2; /* special case for TRAPA */ | |||
| 195 | /* fall through */ | |||
| 196 | default: | |||
| 197 | env->pc = env->vbr + 0x100; | |||
| 198 | break; | |||
| 199 | } | |||
| 200 | return; | |||
| 201 | } | |||
| 202 | ||||
| 203 | if (do_irq) { | |||
| 204 | env->intevt = irq_vector; | |||
| 205 | env->pc = env->vbr + 0x600; | |||
| 206 | return; | |||
| 207 | } | |||
| 208 | } | |||
| 209 | ||||
| 210 | static void update_itlb_use(CPUSH4State * env, int itlbnb) | |||
| 211 | { | |||
| 212 | uint8_t or_mask = 0, and_mask = (uint8_t) - 1; | |||
| 213 | ||||
| 214 | switch (itlbnb) { | |||
| 215 | case 0: | |||
| 216 | and_mask = 0x1f; | |||
| 217 | break; | |||
| 218 | case 1: | |||
| 219 | and_mask = 0xe7; | |||
| 220 | or_mask = 0x80; | |||
| 221 | break; | |||
| 222 | case 2: | |||
| 223 | and_mask = 0xfb; | |||
| 224 | or_mask = 0x50; | |||
| 225 | break; | |||
| 226 | case 3: | |||
| 227 | or_mask = 0x2c; | |||
| 228 | break; | |||
| 229 | } | |||
| 230 | ||||
| 231 | env->mmucr &= (and_mask << 24) | 0x00ffffff; | |||
| 232 | env->mmucr |= (or_mask << 24); | |||
| 233 | } | |||
| 234 | ||||
| 235 | static int itlb_replacement(CPUSH4State * env) | |||
| 236 | { | |||
| 237 | if ((env->mmucr & 0xe0000000) == 0xe0000000) | |||
| 238 | return 0; | |||
| 239 | if ((env->mmucr & 0x98000000) == 0x18000000) | |||
| 240 | return 1; | |||
| 241 | if ((env->mmucr & 0x54000000) == 0x04000000) | |||
| 242 | return 2; | |||
| 243 | if ((env->mmucr & 0x2c000000) == 0x00000000) | |||
| 244 | return 3; | |||
| 245 | cpu_abort(env, "Unhandled itlb_replacement"); | |||
| 246 | } | |||
| 247 | ||||
| 248 | /* Find the corresponding entry in the right TLB | |||
| 249 | Return entry, MMU_DTLB_MISS or MMU_DTLB_MULTIPLE | |||
| 250 | */ | |||
| 251 | static int find_tlb_entry(CPUSH4State * env, target_ulong address, | |||
| 252 | tlb_t * entries, uint8_t nbtlb, int use_asid) | |||
| 253 | { | |||
| 254 | int match = MMU_DTLB_MISS(-10); | |||
| 255 | uint32_t start, end; | |||
| 256 | uint8_t asid; | |||
| 257 | int i; | |||
| 258 | ||||
| 259 | asid = env->pteh & 0xff; | |||
| 260 | ||||
| 261 | for (i = 0; i < nbtlb; i++) { | |||
| 262 | if (!entries[i].v) | |||
| 263 | continue; /* Invalid entry */ | |||
| 264 | if (!entries[i].sh && use_asid && entries[i].asid != asid) | |||
| 265 | continue; /* Bad ASID */ | |||
| 266 | start = (entries[i].vpn << 10) & ~(entries[i].size - 1); | |||
| 267 | end = start + entries[i].size - 1; | |||
| 268 | if (address >= start && address <= end) { /* Match */ | |||
| 269 | if (match != MMU_DTLB_MISS(-10)) | |||
| 270 | return MMU_DTLB_MULTIPLE(-9); /* Multiple match */ | |||
| 271 | match = i; | |||
| 272 | } | |||
| 273 | } | |||
| 274 | return match; | |||
| 275 | } | |||
| 276 | ||||
| 277 | static void increment_urc(CPUSH4State * env) | |||
| 278 | { | |||
| 279 | uint8_t urb, urc; | |||
| 280 | ||||
| 281 | /* Increment URC */ | |||
| 282 | urb = ((env->mmucr) >> 18) & 0x3f; | |||
| 283 | urc = ((env->mmucr) >> 10) & 0x3f; | |||
| 284 | urc++; | |||
| 285 | if ((urb > 0 && urc > urb) || urc > (UTLB_SIZE64 - 1)) | |||
| 286 | urc = 0; | |||
| 287 | env->mmucr = (env->mmucr & 0xffff03ff) | (urc << 10); | |||
| 288 | } | |||
| 289 | ||||
| 290 | /* Copy and utlb entry into itlb | |||
| 291 | Return entry | |||
| 292 | */ | |||
| 293 | static int copy_utlb_entry_itlb(CPUSH4State *env, int utlb) | |||
| 294 | { | |||
| 295 | int itlb; | |||
| 296 | ||||
| 297 | tlb_t * ientry; | |||
| 298 | itlb = itlb_replacement(env); | |||
| 299 | ientry = &env->itlb[itlb]; | |||
| 300 | if (ientry->v) { | |||
| 301 | tlb_flush_page(env, ientry->vpn << 10); | |||
| 302 | } | |||
| 303 | *ientry = env->utlb[utlb]; | |||
| 304 | update_itlb_use(env, itlb); | |||
| 305 | return itlb; | |||
| 306 | } | |||
| 307 | ||||
| 308 | /* Find itlb entry | |||
| 309 | Return entry, MMU_ITLB_MISS, MMU_ITLB_MULTIPLE or MMU_DTLB_MULTIPLE | |||
| 310 | */ | |||
| 311 | static int find_itlb_entry(CPUSH4State * env, target_ulong address, | |||
| 312 | int use_asid) | |||
| 313 | { | |||
| 314 | int e; | |||
| 315 | ||||
| 316 | e = find_tlb_entry(env, address, env->itlb, ITLB_SIZE4, use_asid); | |||
| 317 | if (e == MMU_DTLB_MULTIPLE(-9)) { | |||
| 318 | e = MMU_ITLB_MULTIPLE(-2); | |||
| 319 | } else if (e == MMU_DTLB_MISS(-10)) { | |||
| 320 | e = MMU_ITLB_MISS(-1); | |||
| 321 | } else if (e >= 0) { | |||
| 322 | update_itlb_use(env, e); | |||
| 323 | } | |||
| 324 | return e; | |||
| 325 | } | |||
| 326 | ||||
| 327 | /* Find utlb entry | |||
| 328 | Return entry, MMU_DTLB_MISS, MMU_DTLB_MULTIPLE */ | |||
| 329 | static int find_utlb_entry(CPUSH4State * env, target_ulong address, int use_asid) | |||
| 330 | { | |||
| 331 | /* per utlb access */ | |||
| 332 | increment_urc(env); | |||
| 333 | ||||
| 334 | /* Return entry */ | |||
| 335 | return find_tlb_entry(env, address, env->utlb, UTLB_SIZE64, use_asid); | |||
| 336 | } | |||
| 337 | ||||
| 338 | /* Match address against MMU | |||
| 339 | Return MMU_OK, MMU_DTLB_MISS_READ, MMU_DTLB_MISS_WRITE, | |||
| 340 | MMU_DTLB_INITIAL_WRITE, MMU_DTLB_VIOLATION_READ, | |||
| 341 | MMU_DTLB_VIOLATION_WRITE, MMU_ITLB_MISS, | |||
| 342 | MMU_ITLB_MULTIPLE, MMU_ITLB_VIOLATION, | |||
| 343 | MMU_IADDR_ERROR, MMU_DADDR_ERROR_READ, MMU_DADDR_ERROR_WRITE. | |||
| 344 | */ | |||
| 345 | static int get_mmu_address(CPUSH4State * env, target_ulong * physical, | |||
| 346 | int *prot, target_ulong address, | |||
| 347 | int rw, int access_type) | |||
| 348 | { | |||
| 349 | int use_asid, n; | |||
| 350 | tlb_t *matching = NULL((void*)0); | |||
| 351 | ||||
| 352 | use_asid = (env->mmucr & MMUCR_SV(1<<8)) == 0 || (env->sr & SR_MD(1 << 30)) == 0; | |||
| 353 | ||||
| 354 | if (rw == 2) { | |||
| 355 | n = find_itlb_entry(env, address, use_asid); | |||
| 356 | if (n >= 0) { | |||
| 357 | matching = &env->itlb[n]; | |||
| 358 | if (!(env->sr & SR_MD(1 << 30)) && !(matching->pr & 2)) | |||
| 359 | n = MMU_ITLB_VIOLATION(-3); | |||
| 360 | else | |||
| 361 | *prot = PAGE_EXEC0x0004; | |||
| 362 | } else { | |||
| 363 | n = find_utlb_entry(env, address, use_asid); | |||
| 364 | if (n >= 0) { | |||
| 365 | n = copy_utlb_entry_itlb(env, n); | |||
| 366 | matching = &env->itlb[n]; | |||
| 367 | if (!(env->sr & SR_MD(1 << 30)) && !(matching->pr & 2)) { | |||
| 368 | n = MMU_ITLB_VIOLATION(-3); | |||
| 369 | } else { | |||
| 370 | *prot = PAGE_READ0x0001 | PAGE_EXEC0x0004; | |||
| 371 | if ((matching->pr & 1) && matching->d) { | |||
| 372 | *prot |= PAGE_WRITE0x0002; | |||
| 373 | } | |||
| 374 | } | |||
| 375 | } else if (n == MMU_DTLB_MULTIPLE(-9)) { | |||
| 376 | n = MMU_ITLB_MULTIPLE(-2); | |||
| 377 | } else if (n == MMU_DTLB_MISS(-10)) { | |||
| 378 | n = MMU_ITLB_MISS(-1); | |||
| 379 | } | |||
| 380 | } | |||
| 381 | } else { | |||
| 382 | n = find_utlb_entry(env, address, use_asid); | |||
| 383 | if (n >= 0) { | |||
| 384 | matching = &env->utlb[n]; | |||
| 385 | if (!(env->sr & SR_MD(1 << 30)) && !(matching->pr & 2)) { | |||
| 386 | n = (rw == 1) ? MMU_DTLB_VIOLATION_WRITE(-8) : | |||
| 387 | MMU_DTLB_VIOLATION_READ(-7); | |||
| 388 | } else if ((rw == 1) && !(matching->pr & 1)) { | |||
| 389 | n = MMU_DTLB_VIOLATION_WRITE(-8); | |||
| 390 | } else if ((rw == 1) && !matching->d) { | |||
| 391 | n = MMU_DTLB_INITIAL_WRITE(-6); | |||
| 392 | } else { | |||
| 393 | *prot = PAGE_READ0x0001; | |||
| 394 | if ((matching->pr & 1) && matching->d) { | |||
| 395 | *prot |= PAGE_WRITE0x0002; | |||
| 396 | } | |||
| 397 | } | |||
| 398 | } else if (n == MMU_DTLB_MISS(-10)) { | |||
| 399 | n = (rw == 1) ? MMU_DTLB_MISS_WRITE(-5) : | |||
| 400 | MMU_DTLB_MISS_READ(-4); | |||
| 401 | } | |||
| 402 | } | |||
| 403 | if (n >= 0) { | |||
| 404 | n = MMU_OK0; | |||
| 405 | *physical = ((matching->ppn << 10) & ~(matching->size - 1)) | | |||
| 406 | (address & (matching->size - 1)); | |||
| 407 | } | |||
| 408 | return n; | |||
| 409 | } | |||
| 410 | ||||
| 411 | static int get_physical_address(CPUSH4State * env, target_ulong * physical, | |||
| 412 | int *prot, target_ulong address, | |||
| 413 | int rw, int access_type) | |||
| 414 | { | |||
| 415 | /* P1, P2 and P4 areas do not use translation */ | |||
| 416 | if ((address >= 0x80000000 && address < 0xc0000000) || | |||
| 417 | address >= 0xe0000000) { | |||
| 418 | if (!(env->sr & SR_MD(1 << 30)) | |||
| 419 | && (address < 0xe0000000 || address >= 0xe4000000)) { | |||
| 420 | /* Unauthorized access in user mode (only store queues are available) */ | |||
| 421 | fprintf(stderrstderr, "Unauthorized access\n"); | |||
| 422 | if (rw == 0) | |||
| 423 | return MMU_DADDR_ERROR_READ(-12); | |||
| 424 | else if (rw == 1) | |||
| 425 | return MMU_DADDR_ERROR_WRITE(-13); | |||
| 426 | else | |||
| 427 | return MMU_IADDR_ERROR(-11); | |||
| 428 | } | |||
| 429 | if (address >= 0x80000000 && address < 0xc0000000) { | |||
| 430 | /* Mask upper 3 bits for P1 and P2 areas */ | |||
| 431 | *physical = address & 0x1fffffff; | |||
| 432 | } else { | |||
| 433 | *physical = address; | |||
| 434 | } | |||
| 435 | *prot = PAGE_READ0x0001 | PAGE_WRITE0x0002 | PAGE_EXEC0x0004; | |||
| 436 | return MMU_OK0; | |||
| 437 | } | |||
| 438 | ||||
| 439 | /* If MMU is disabled, return the corresponding physical page */ | |||
| 440 | if (!(env->mmucr & MMUCR_AT(1<<0))) { | |||
| 441 | *physical = address & 0x1FFFFFFF; | |||
| 442 | *prot = PAGE_READ0x0001 | PAGE_WRITE0x0002 | PAGE_EXEC0x0004; | |||
| 443 | return MMU_OK0; | |||
| 444 | } | |||
| 445 | ||||
| 446 | /* We need to resort to the MMU */ | |||
| 447 | return get_mmu_address(env, physical, prot, address, rw, access_type); | |||
| 448 | } | |||
| 449 | ||||
| 450 | int cpu_sh4_handle_mmu_fault(CPUSH4State * env, target_ulong address, int rw, | |||
| 451 | int mmu_idx) | |||
| 452 | { | |||
| 453 | target_ulong physical; | |||
| 454 | int prot, ret, access_type; | |||
| 455 | ||||
| 456 | access_type = ACCESS_INT; | |||
| 457 | ret = | |||
| 458 | get_physical_address(env, &physical, &prot, address, rw, | |||
| 459 | access_type); | |||
| 460 | ||||
| 461 | if (ret != MMU_OK0) { | |||
| 462 | env->tea = address; | |||
| 463 | if (ret != MMU_DTLB_MULTIPLE(-9) && ret != MMU_ITLB_MULTIPLE(-2)) { | |||
| 464 | env->pteh = (env->pteh & PTEH_ASID_MASK((1 << (8)) - 1)) | | |||
| 465 | (address & PTEH_VPN_MASK((((1 << (22))) - 1) << (10))); | |||
| 466 | } | |||
| 467 | switch (ret) { | |||
| 468 | case MMU_ITLB_MISS(-1): | |||
| 469 | case MMU_DTLB_MISS_READ(-4): | |||
| 470 | env->exception_index = 0x040; | |||
| 471 | break; | |||
| 472 | case MMU_DTLB_MULTIPLE(-9): | |||
| 473 | case MMU_ITLB_MULTIPLE(-2): | |||
| 474 | env->exception_index = 0x140; | |||
| 475 | break; | |||
| 476 | case MMU_ITLB_VIOLATION(-3): | |||
| 477 | env->exception_index = 0x0a0; | |||
| 478 | break; | |||
| 479 | case MMU_DTLB_MISS_WRITE(-5): | |||
| 480 | env->exception_index = 0x060; | |||
| 481 | break; | |||
| 482 | case MMU_DTLB_INITIAL_WRITE(-6): | |||
| 483 | env->exception_index = 0x080; | |||
| 484 | break; | |||
| 485 | case MMU_DTLB_VIOLATION_READ(-7): | |||
| 486 | env->exception_index = 0x0a0; | |||
| 487 | break; | |||
| 488 | case MMU_DTLB_VIOLATION_WRITE(-8): | |||
| 489 | env->exception_index = 0x0c0; | |||
| 490 | break; | |||
| 491 | case MMU_IADDR_ERROR(-11): | |||
| 492 | case MMU_DADDR_ERROR_READ(-12): | |||
| 493 | env->exception_index = 0x0e0; | |||
| 494 | break; | |||
| 495 | case MMU_DADDR_ERROR_WRITE(-13): | |||
| 496 | env->exception_index = 0x100; | |||
| 497 | break; | |||
| 498 | default: | |||
| 499 | cpu_abort(env, "Unhandled MMU fault"); | |||
| 500 | } | |||
| 501 | return 1; | |||
| 502 | } | |||
| 503 | ||||
| 504 | address &= TARGET_PAGE_MASK~((1 << 12) - 1); | |||
| 505 | physical &= TARGET_PAGE_MASK~((1 << 12) - 1); | |||
| 506 | ||||
| 507 | tlb_set_page(env, address, physical, prot, mmu_idx, TARGET_PAGE_SIZE(1 << 12)); | |||
| 508 | return 0; | |||
| 509 | } | |||
| 510 | ||||
| 511 | hwaddr superh_cpu_get_phys_page_debug(CPUState *cs, vaddr addr) | |||
| 512 | { | |||
| 513 | SuperHCPU *cpu = SUPERH_CPU(cs)((SuperHCPU *)object_dynamic_cast_assert(((Object *)((cs))), ( "superh-cpu"), "/home/stefan/src/qemu/qemu.org/qemu/target-sh4/helper.c" , 513, __func__)); | |||
| 514 | target_ulong physical; | |||
| ||||
| 515 | int prot; | |||
| 516 | ||||
| 517 | get_physical_address(&cpu->env, &physical, &prot, addr, 0, 0); | |||
| 518 | return physical; | |||
| ||||
| 519 | } | |||
| 520 | ||||
| 521 | void cpu_load_tlb(CPUSH4State * env) | |||
| 522 | { | |||
| 523 | int n = cpu_mmucr_urc(env->mmucr); | |||
| 524 | tlb_t * entry = &env->utlb[n]; | |||
| 525 | ||||
| 526 | if (entry->v) { | |||
| 527 | /* Overwriting valid entry in utlb. */ | |||
| 528 | target_ulong address = entry->vpn << 10; | |||
| 529 | tlb_flush_page(env, address); | |||
| 530 | } | |||
| 531 | ||||
| 532 | /* Take values into cpu status from registers. */ | |||
| 533 | entry->asid = (uint8_t)cpu_pteh_asid(env->pteh)((env->pteh) & ((1 << (8)) - 1)); | |||
| 534 | entry->vpn = cpu_pteh_vpn(env->pteh); | |||
| 535 | entry->v = (uint8_t)cpu_ptel_v(env->ptel)(((env->ptel) & (1 << 8)) >> 8); | |||
| 536 | entry->ppn = cpu_ptel_ppn(env->ptel); | |||
| 537 | entry->sz = (uint8_t)cpu_ptel_sz(env->ptel); | |||
| 538 | switch (entry->sz) { | |||
| 539 | case 0: /* 00 */ | |||
| 540 | entry->size = 1024; /* 1K */ | |||
| 541 | break; | |||
| 542 | case 1: /* 01 */ | |||
| 543 | entry->size = 1024 * 4; /* 4K */ | |||
| 544 | break; | |||
| 545 | case 2: /* 10 */ | |||
| 546 | entry->size = 1024 * 64; /* 64K */ | |||
| 547 | break; | |||
| 548 | case 3: /* 11 */ | |||
| 549 | entry->size = 1024 * 1024; /* 1M */ | |||
| 550 | break; | |||
| 551 | default: | |||
| 552 | cpu_abort(env, "Unhandled load_tlb"); | |||
| 553 | break; | |||
| 554 | } | |||
| 555 | entry->sh = (uint8_t)cpu_ptel_sh(env->ptel)(((env->ptel) & (1 << 1)) >> 1); | |||
| 556 | entry->c = (uint8_t)cpu_ptel_c(env->ptel)(((env->ptel) & (1 << 3)) >> 3); | |||
| 557 | entry->pr = (uint8_t)cpu_ptel_pr(env->ptel); | |||
| 558 | entry->d = (uint8_t)cpu_ptel_d(env->ptel)(((env->ptel) & (1 << 2)) >> 2); | |||
| 559 | entry->wt = (uint8_t)cpu_ptel_wt(env->ptel)((env->ptel) & (1 << 0)); | |||
| 560 | entry->sa = (uint8_t)cpu_ptea_sa(env->ptea)((env->ptea) & ((1 << (3)) - 1)); | |||
| 561 | entry->tc = (uint8_t)cpu_ptea_tc(env->ptea)(((env->ptea) & (1 << 3)) >> 3); | |||
| 562 | } | |||
| 563 | ||||
| 564 | void cpu_sh4_invalidate_tlb(CPUSH4State *s) | |||
| 565 | { | |||
| 566 | int i; | |||
| 567 | ||||
| 568 | /* UTLB */ | |||
| 569 | for (i = 0; i < UTLB_SIZE64; i++) { | |||
| 570 | tlb_t * entry = &s->utlb[i]; | |||
| 571 | entry->v = 0; | |||
| 572 | } | |||
| 573 | /* ITLB */ | |||
| 574 | for (i = 0; i < ITLB_SIZE4; i++) { | |||
| 575 | tlb_t * entry = &s->itlb[i]; | |||
| 576 | entry->v = 0; | |||
| 577 | } | |||
| 578 | ||||
| 579 | tlb_flush(s, 1); | |||
| 580 | } | |||
| 581 | ||||
| 582 | uint32_t cpu_sh4_read_mmaped_itlb_addr(CPUSH4State *s, | |||
| 583 | hwaddr addr) | |||
| 584 | { | |||
| 585 | int index = (addr & 0x00000300) >> 8; | |||
| 586 | tlb_t * entry = &s->itlb[index]; | |||
| 587 | ||||
| 588 | return (entry->vpn << 10) | | |||
| 589 | (entry->v << 8) | | |||
| 590 | (entry->asid); | |||
| 591 | } | |||
| 592 | ||||
| 593 | void cpu_sh4_write_mmaped_itlb_addr(CPUSH4State *s, hwaddr addr, | |||
| 594 | uint32_t mem_value) | |||
| 595 | { | |||
| 596 | uint32_t vpn = (mem_value & 0xfffffc00) >> 10; | |||
| 597 | uint8_t v = (uint8_t)((mem_value & 0x00000100) >> 8); | |||
| 598 | uint8_t asid = (uint8_t)(mem_value & 0x000000ff); | |||
| 599 | ||||
| 600 | int index = (addr & 0x00000300) >> 8; | |||
| 601 | tlb_t * entry = &s->itlb[index]; | |||
| 602 | if (entry->v) { | |||
| 603 | /* Overwriting valid entry in itlb. */ | |||
| 604 | target_ulong address = entry->vpn << 10; | |||
| 605 | tlb_flush_page(s, address); | |||
| 606 | } | |||
| 607 | entry->asid = asid; | |||
| 608 | entry->vpn = vpn; | |||
| 609 | entry->v = v; | |||
| 610 | } | |||
| 611 | ||||
| 612 | uint32_t cpu_sh4_read_mmaped_itlb_data(CPUSH4State *s, | |||
| 613 | hwaddr addr) | |||
| 614 | { | |||
| 615 | int array = (addr & 0x00800000) >> 23; | |||
| 616 | int index = (addr & 0x00000300) >> 8; | |||
| 617 | tlb_t * entry = &s->itlb[index]; | |||
| 618 | ||||
| 619 | if (array == 0) { | |||
| 620 | /* ITLB Data Array 1 */ | |||
| 621 | return (entry->ppn << 10) | | |||
| 622 | (entry->v << 8) | | |||
| 623 | (entry->pr << 5) | | |||
| 624 | ((entry->sz & 1) << 6) | | |||
| 625 | ((entry->sz & 2) << 4) | | |||
| 626 | (entry->c << 3) | | |||
| 627 | (entry->sh << 1); | |||
| 628 | } else { | |||
| 629 | /* ITLB Data Array 2 */ | |||
| 630 | return (entry->tc << 1) | | |||
| 631 | (entry->sa); | |||
| 632 | } | |||
| 633 | } | |||
| 634 | ||||
| 635 | void cpu_sh4_write_mmaped_itlb_data(CPUSH4State *s, hwaddr addr, | |||
| 636 | uint32_t mem_value) | |||
| 637 | { | |||
| 638 | int array = (addr & 0x00800000) >> 23; | |||
| 639 | int index = (addr & 0x00000300) >> 8; | |||
| 640 | tlb_t * entry = &s->itlb[index]; | |||
| 641 | ||||
| 642 | if (array == 0) { | |||
| 643 | /* ITLB Data Array 1 */ | |||
| 644 | if (entry->v) { | |||
| 645 | /* Overwriting valid entry in utlb. */ | |||
| 646 | target_ulong address = entry->vpn << 10; | |||
| 647 | tlb_flush_page(s, address); | |||
| 648 | } | |||
| 649 | entry->ppn = (mem_value & 0x1ffffc00) >> 10; | |||
| 650 | entry->v = (mem_value & 0x00000100) >> 8; | |||
| 651 | entry->sz = (mem_value & 0x00000080) >> 6 | | |||
| 652 | (mem_value & 0x00000010) >> 4; | |||
| 653 | entry->pr = (mem_value & 0x00000040) >> 5; | |||
| 654 | entry->c = (mem_value & 0x00000008) >> 3; | |||
| 655 | entry->sh = (mem_value & 0x00000002) >> 1; | |||
| 656 | } else { | |||
| 657 | /* ITLB Data Array 2 */ | |||
| 658 | entry->tc = (mem_value & 0x00000008) >> 3; | |||
| 659 | entry->sa = (mem_value & 0x00000007); | |||
| 660 | } | |||
| 661 | } | |||
| 662 | ||||
| 663 | uint32_t cpu_sh4_read_mmaped_utlb_addr(CPUSH4State *s, | |||
| 664 | hwaddr addr) | |||
| 665 | { | |||
| 666 | int index = (addr & 0x00003f00) >> 8; | |||
| 667 | tlb_t * entry = &s->utlb[index]; | |||
| 668 | ||||
| 669 | increment_urc(s); /* per utlb access */ | |||
| 670 | ||||
| 671 | return (entry->vpn << 10) | | |||
| 672 | (entry->v << 8) | | |||
| 673 | (entry->asid); | |||
| 674 | } | |||
| 675 | ||||
| 676 | void cpu_sh4_write_mmaped_utlb_addr(CPUSH4State *s, hwaddr addr, | |||
| 677 | uint32_t mem_value) | |||
| 678 | { | |||
| 679 | int associate = addr & 0x0000080; | |||
| 680 | uint32_t vpn = (mem_value & 0xfffffc00) >> 10; | |||
| 681 | uint8_t d = (uint8_t)((mem_value & 0x00000200) >> 9); | |||
| 682 | uint8_t v = (uint8_t)((mem_value & 0x00000100) >> 8); | |||
| 683 | uint8_t asid = (uint8_t)(mem_value & 0x000000ff); | |||
| 684 | int use_asid = (s->mmucr & MMUCR_SV(1<<8)) == 0 || (s->sr & SR_MD(1 << 30)) == 0; | |||
| 685 | ||||
| 686 | if (associate) { | |||
| 687 | int i; | |||
| 688 | tlb_t * utlb_match_entry = NULL((void*)0); | |||
| 689 | int needs_tlb_flush = 0; | |||
| 690 | ||||
| 691 | /* search UTLB */ | |||
| 692 | for (i = 0; i < UTLB_SIZE64; i++) { | |||
| 693 | tlb_t * entry = &s->utlb[i]; | |||
| 694 | if (!entry->v) | |||
| 695 | continue; | |||
| 696 | ||||
| 697 | if (entry->vpn == vpn | |||
| 698 | && (!use_asid || entry->asid == asid || entry->sh)) { | |||
| 699 | if (utlb_match_entry) { | |||
| 700 | /* Multiple TLB Exception */ | |||
| 701 | s->exception_index = 0x140; | |||
| 702 | s->tea = addr; | |||
| 703 | break; | |||
| 704 | } | |||
| 705 | if (entry->v && !v) | |||
| 706 | needs_tlb_flush = 1; | |||
| 707 | entry->v = v; | |||
| 708 | entry->d = d; | |||
| 709 | utlb_match_entry = entry; | |||
| 710 | } | |||
| 711 | increment_urc(s); /* per utlb access */ | |||
| 712 | } | |||
| 713 | ||||
| 714 | /* search ITLB */ | |||
| 715 | for (i = 0; i < ITLB_SIZE4; i++) { | |||
| 716 | tlb_t * entry = &s->itlb[i]; | |||
| 717 | if (entry->vpn == vpn | |||
| 718 | && (!use_asid || entry->asid == asid || entry->sh)) { | |||
| 719 | if (entry->v && !v) | |||
| 720 | needs_tlb_flush = 1; | |||
| 721 | if (utlb_match_entry) | |||
| 722 | *entry = *utlb_match_entry; | |||
| 723 | else | |||
| 724 | entry->v = v; | |||
| 725 | break; | |||
| 726 | } | |||
| 727 | } | |||
| 728 | ||||
| 729 | if (needs_tlb_flush) | |||
| 730 | tlb_flush_page(s, vpn << 10); | |||
| 731 | ||||
| 732 | } else { | |||
| 733 | int index = (addr & 0x00003f00) >> 8; | |||
| 734 | tlb_t * entry = &s->utlb[index]; | |||
| 735 | if (entry->v) { | |||
| 736 | /* Overwriting valid entry in utlb. */ | |||
| 737 | target_ulong address = entry->vpn << 10; | |||
| 738 | tlb_flush_page(s, address); | |||
| 739 | } | |||
| 740 | entry->asid = asid; | |||
| 741 | entry->vpn = vpn; | |||
| 742 | entry->d = d; | |||
| 743 | entry->v = v; | |||
| 744 | increment_urc(s); | |||
| 745 | } | |||
| 746 | } | |||
| 747 | ||||
| 748 | uint32_t cpu_sh4_read_mmaped_utlb_data(CPUSH4State *s, | |||
| 749 | hwaddr addr) | |||
| 750 | { | |||
| 751 | int array = (addr & 0x00800000) >> 23; | |||
| 752 | int index = (addr & 0x00003f00) >> 8; | |||
| 753 | tlb_t * entry = &s->utlb[index]; | |||
| 754 | ||||
| 755 | increment_urc(s); /* per utlb access */ | |||
| 756 | ||||
| 757 | if (array == 0) { | |||
| 758 | /* ITLB Data Array 1 */ | |||
| 759 | return (entry->ppn << 10) | | |||
| 760 | (entry->v << 8) | | |||
| 761 | (entry->pr << 5) | | |||
| 762 | ((entry->sz & 1) << 6) | | |||
| 763 | ((entry->sz & 2) << 4) | | |||
| 764 | (entry->c << 3) | | |||
| 765 | (entry->d << 2) | | |||
| 766 | (entry->sh << 1) | | |||
| 767 | (entry->wt); | |||
| 768 | } else { | |||
| 769 | /* ITLB Data Array 2 */ | |||
| 770 | return (entry->tc << 1) | | |||
| 771 | (entry->sa); | |||
| 772 | } | |||
| 773 | } | |||
| 774 | ||||
| 775 | void cpu_sh4_write_mmaped_utlb_data(CPUSH4State *s, hwaddr addr, | |||
| 776 | uint32_t mem_value) | |||
| 777 | { | |||
| 778 | int array = (addr & 0x00800000) >> 23; | |||
| 779 | int index = (addr & 0x00003f00) >> 8; | |||
| 780 | tlb_t * entry = &s->utlb[index]; | |||
| 781 | ||||
| 782 | increment_urc(s); /* per utlb access */ | |||
| 783 | ||||
| 784 | if (array == 0) { | |||
| 785 | /* UTLB Data Array 1 */ | |||
| 786 | if (entry->v) { | |||
| 787 | /* Overwriting valid entry in utlb. */ | |||
| 788 | target_ulong address = entry->vpn << 10; | |||
| 789 | tlb_flush_page(s, address); | |||
| 790 | } | |||
| 791 | entry->ppn = (mem_value & 0x1ffffc00) >> 10; | |||
| 792 | entry->v = (mem_value & 0x00000100) >> 8; | |||
| 793 | entry->sz = (mem_value & 0x00000080) >> 6 | | |||
| 794 | (mem_value & 0x00000010) >> 4; | |||
| 795 | entry->pr = (mem_value & 0x00000060) >> 5; | |||
| 796 | entry->c = (mem_value & 0x00000008) >> 3; | |||
| 797 | entry->d = (mem_value & 0x00000004) >> 2; | |||
| 798 | entry->sh = (mem_value & 0x00000002) >> 1; | |||
| 799 | entry->wt = (mem_value & 0x00000001); | |||
| 800 | } else { | |||
| 801 | /* UTLB Data Array 2 */ | |||
| 802 | entry->tc = (mem_value & 0x00000008) >> 3; | |||
| 803 | entry->sa = (mem_value & 0x00000007); | |||
| 804 | } | |||
| 805 | } | |||
| 806 | ||||
| 807 | int cpu_sh4_is_cached(CPUSH4State * env, target_ulong addr) | |||
| 808 | { | |||
| 809 | int n; | |||
| 810 | int use_asid = (env->mmucr & MMUCR_SV(1<<8)) == 0 || (env->sr & SR_MD(1 << 30)) == 0; | |||
| 811 | ||||
| 812 | /* check area */ | |||
| 813 | if (env->sr & SR_MD(1 << 30)) { | |||
| 814 | /* For previledged mode, P2 and P4 area is not cachable. */ | |||
| 815 | if ((0xA0000000 <= addr && addr < 0xC0000000) || 0xE0000000 <= addr) | |||
| 816 | return 0; | |||
| 817 | } else { | |||
| 818 | /* For user mode, only U0 area is cachable. */ | |||
| 819 | if (0x80000000 <= addr) | |||
| 820 | return 0; | |||
| 821 | } | |||
| 822 | ||||
| 823 | /* | |||
| 824 | * TODO : Evaluate CCR and check if the cache is on or off. | |||
| 825 | * Now CCR is not in CPUSH4State, but in SH7750State. | |||
| 826 | * When you move the ccr into CPUSH4State, the code will be | |||
| 827 | * as follows. | |||
| 828 | */ | |||
| 829 | #if 0 | |||
| 830 | /* check if operand cache is enabled or not. */ | |||
| 831 | if (!(env->ccr & 1)) | |||
| 832 | return 0; | |||
| 833 | #endif | |||
| 834 | ||||
| 835 | /* if MMU is off, no check for TLB. */ | |||
| 836 | if (env->mmucr & MMUCR_AT(1<<0)) | |||
| 837 | return 1; | |||
| 838 | ||||
| 839 | /* check TLB */ | |||
| 840 | n = find_tlb_entry(env, addr, env->itlb, ITLB_SIZE4, use_asid); | |||
| 841 | if (n >= 0) | |||
| 842 | return env->itlb[n].c; | |||
| 843 | ||||
| 844 | n = find_tlb_entry(env, addr, env->utlb, UTLB_SIZE64, use_asid); | |||
| 845 | if (n >= 0) | |||
| 846 | return env->utlb[n].c; | |||
| 847 | ||||
| 848 | return 0; | |||
| 849 | } | |||
| 850 | ||||
| 851 | #endif |