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 |