]>
Commit | Line | Data |
---|---|---|
e8af50a3 FB |
1 | /* |
2 | * sparc helpers | |
3 | * | |
4 | * Copyright (c) 2003 Fabrice Bellard | |
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 "exec.h" | |
21 | ||
e80cfcfc FB |
22 | //#define DEBUG_PCALL |
23 | //#define DEBUG_MMU | |
e8af50a3 | 24 | |
e8af50a3 FB |
25 | /* Sparc MMU emulation */ |
26 | int cpu_sparc_handle_mmu_fault (CPUState *env, uint32_t address, int rw, | |
27 | int is_user, int is_softmmu); | |
28 | ||
e8af50a3 FB |
29 | /* thread support */ |
30 | ||
31 | spinlock_t global_cpu_lock = SPIN_LOCK_UNLOCKED; | |
32 | ||
33 | void cpu_lock(void) | |
34 | { | |
35 | spin_lock(&global_cpu_lock); | |
36 | } | |
37 | ||
38 | void cpu_unlock(void) | |
39 | { | |
40 | spin_unlock(&global_cpu_lock); | |
41 | } | |
42 | ||
e8af50a3 FB |
43 | #if !defined(CONFIG_USER_ONLY) |
44 | ||
45 | #define MMUSUFFIX _mmu | |
46 | #define GETPC() (__builtin_return_address(0)) | |
47 | ||
48 | #define SHIFT 0 | |
49 | #include "softmmu_template.h" | |
50 | ||
51 | #define SHIFT 1 | |
52 | #include "softmmu_template.h" | |
53 | ||
54 | #define SHIFT 2 | |
55 | #include "softmmu_template.h" | |
56 | ||
57 | #define SHIFT 3 | |
58 | #include "softmmu_template.h" | |
59 | ||
60 | ||
61 | /* try to fill the TLB and return an exception if error. If retaddr is | |
62 | NULL, it means that the function was called in C code (i.e. not | |
63 | from generated code or from helper.c) */ | |
64 | /* XXX: fix it to restore all registers */ | |
65 | void tlb_fill(unsigned long addr, int is_write, int is_user, void *retaddr) | |
66 | { | |
67 | TranslationBlock *tb; | |
68 | int ret; | |
69 | unsigned long pc; | |
70 | CPUState *saved_env; | |
71 | ||
72 | /* XXX: hack to restore env in all cases, even if not called from | |
73 | generated code */ | |
74 | saved_env = env; | |
75 | env = cpu_single_env; | |
76 | ||
77 | ret = cpu_sparc_handle_mmu_fault(env, addr, is_write, is_user, 1); | |
78 | if (ret) { | |
79 | if (retaddr) { | |
80 | /* now we have a real cpu fault */ | |
81 | pc = (unsigned long)retaddr; | |
82 | tb = tb_find_pc(pc); | |
83 | if (tb) { | |
84 | /* the PC is inside the translated code. It means that we have | |
85 | a virtual CPU fault */ | |
86 | cpu_restore_state(tb, env, pc, NULL); | |
87 | } | |
88 | } | |
89 | raise_exception_err(ret, env->error_code); | |
90 | } | |
91 | env = saved_env; | |
92 | } | |
93 | #endif | |
94 | ||
95 | static const int access_table[8][8] = { | |
96 | { 0, 0, 0, 0, 2, 0, 3, 3 }, | |
97 | { 0, 0, 0, 0, 2, 0, 0, 0 }, | |
98 | { 2, 2, 0, 0, 0, 2, 3, 3 }, | |
99 | { 2, 2, 0, 0, 0, 2, 0, 0 }, | |
100 | { 2, 0, 2, 0, 2, 2, 3, 3 }, | |
101 | { 2, 0, 2, 0, 2, 0, 2, 0 }, | |
102 | { 2, 2, 2, 0, 2, 2, 3, 3 }, | |
103 | { 2, 2, 2, 0, 2, 2, 2, 0 } | |
104 | }; | |
105 | ||
106 | /* 1 = write OK */ | |
107 | static const int rw_table[2][8] = { | |
108 | { 0, 1, 0, 1, 0, 1, 0, 1 }, | |
109 | { 0, 1, 0, 1, 0, 0, 0, 0 } | |
110 | }; | |
111 | ||
e80cfcfc FB |
112 | int get_physical_address (CPUState *env, uint32_t *physical, int *prot, |
113 | int *access_index, uint32_t address, int rw, | |
114 | int is_user) | |
e8af50a3 | 115 | { |
e80cfcfc FB |
116 | int access_perms = 0; |
117 | target_phys_addr_t pde_ptr; | |
e8af50a3 | 118 | uint32_t pde, virt_addr; |
e80cfcfc FB |
119 | int error_code = 0, is_dirty; |
120 | unsigned long page_offset; | |
e8af50a3 FB |
121 | |
122 | virt_addr = address & TARGET_PAGE_MASK; | |
123 | if ((env->mmuregs[0] & MMU_E) == 0) { /* MMU disabled */ | |
e80cfcfc FB |
124 | *physical = address; |
125 | *prot = PAGE_READ | PAGE_WRITE; | |
126 | return 0; | |
e8af50a3 FB |
127 | } |
128 | ||
129 | /* SPARC reference MMU table walk: Context table->L1->L2->PTE */ | |
130 | /* Context base + context number */ | |
e80cfcfc FB |
131 | pde_ptr = (env->mmuregs[1] << 4) + (env->mmuregs[2] << 4); |
132 | cpu_physical_memory_read(pde_ptr, (uint8_t *)&pde, 4); | |
133 | bswap32s(&pde); | |
e8af50a3 FB |
134 | |
135 | /* Ctx pde */ | |
136 | switch (pde & PTE_ENTRYTYPE_MASK) { | |
e80cfcfc | 137 | default: |
e8af50a3 | 138 | case 0: /* Invalid */ |
e80cfcfc FB |
139 | return 1; |
140 | case 2: /* L0 PTE, maybe should not happen? */ | |
e8af50a3 | 141 | case 3: /* Reserved */ |
e80cfcfc FB |
142 | return 4; |
143 | case 1: /* L0 PDE */ | |
144 | pde_ptr = ((address >> 22) & ~3) + ((pde & ~3) << 4); | |
145 | cpu_physical_memory_read(pde_ptr, (uint8_t *)&pde, 4); | |
146 | bswap32s(&pde); | |
e8af50a3 FB |
147 | |
148 | switch (pde & PTE_ENTRYTYPE_MASK) { | |
e80cfcfc | 149 | default: |
e8af50a3 | 150 | case 0: /* Invalid */ |
e80cfcfc | 151 | return 1; |
e8af50a3 | 152 | case 3: /* Reserved */ |
e80cfcfc FB |
153 | return 4; |
154 | case 1: /* L1 PDE */ | |
155 | pde_ptr = ((address & 0xfc0000) >> 16) + ((pde & ~3) << 4); | |
156 | cpu_physical_memory_read(pde_ptr, (uint8_t *)&pde, 4); | |
157 | bswap32s(&pde); | |
e8af50a3 FB |
158 | |
159 | switch (pde & PTE_ENTRYTYPE_MASK) { | |
e80cfcfc | 160 | default: |
e8af50a3 | 161 | case 0: /* Invalid */ |
e80cfcfc | 162 | return 1; |
e8af50a3 | 163 | case 3: /* Reserved */ |
e80cfcfc FB |
164 | return 4; |
165 | case 1: /* L2 PDE */ | |
166 | pde_ptr = ((address & 0x3f000) >> 10) + ((pde & ~3) << 4); | |
167 | cpu_physical_memory_read(pde_ptr, (uint8_t *)&pde, 4); | |
168 | bswap32s(&pde); | |
e8af50a3 FB |
169 | |
170 | switch (pde & PTE_ENTRYTYPE_MASK) { | |
e80cfcfc | 171 | default: |
e8af50a3 | 172 | case 0: /* Invalid */ |
e80cfcfc | 173 | return 1; |
e8af50a3 FB |
174 | case 1: /* PDE, should not happen */ |
175 | case 3: /* Reserved */ | |
e80cfcfc | 176 | return 4; |
e8af50a3 FB |
177 | case 2: /* L3 PTE */ |
178 | virt_addr = address & TARGET_PAGE_MASK; | |
179 | page_offset = (address & TARGET_PAGE_MASK) & (TARGET_PAGE_SIZE - 1); | |
180 | } | |
181 | break; | |
182 | case 2: /* L2 PTE */ | |
183 | virt_addr = address & ~0x3ffff; | |
184 | page_offset = address & 0x3ffff; | |
185 | } | |
186 | break; | |
187 | case 2: /* L1 PTE */ | |
188 | virt_addr = address & ~0xffffff; | |
189 | page_offset = address & 0xffffff; | |
190 | } | |
191 | } | |
192 | ||
193 | /* update page modified and dirty bits */ | |
b769d8fe | 194 | is_dirty = (rw & 1) && !(pde & PG_MODIFIED_MASK); |
e8af50a3 | 195 | if (!(pde & PG_ACCESSED_MASK) || is_dirty) { |
e80cfcfc | 196 | uint32_t tmppde; |
e8af50a3 FB |
197 | pde |= PG_ACCESSED_MASK; |
198 | if (is_dirty) | |
199 | pde |= PG_MODIFIED_MASK; | |
e80cfcfc FB |
200 | tmppde = bswap32(pde); |
201 | cpu_physical_memory_write(pde_ptr, (uint8_t *)&tmppde, 4); | |
e8af50a3 | 202 | } |
e8af50a3 | 203 | /* check access */ |
e80cfcfc | 204 | *access_index = ((rw & 1) << 2) | (rw & 2) | (is_user? 0 : 1); |
e8af50a3 | 205 | access_perms = (pde & PTE_ACCESS_MASK) >> PTE_ACCESS_SHIFT; |
e80cfcfc | 206 | error_code = access_table[*access_index][access_perms]; |
e8af50a3 | 207 | if (error_code) |
e80cfcfc | 208 | return error_code; |
e8af50a3 FB |
209 | |
210 | /* the page can be put in the TLB */ | |
e80cfcfc | 211 | *prot = PAGE_READ; |
e8af50a3 FB |
212 | if (pde & PG_MODIFIED_MASK) { |
213 | /* only set write access if already dirty... otherwise wait | |
214 | for dirty access */ | |
215 | if (rw_table[is_user][access_perms]) | |
e80cfcfc | 216 | *prot |= PAGE_WRITE; |
e8af50a3 FB |
217 | } |
218 | ||
219 | /* Even if large ptes, we map only one 4KB page in the cache to | |
220 | avoid filling it too fast */ | |
e80cfcfc FB |
221 | *physical = ((pde & PTE_ADDR_MASK) << 4) + page_offset; |
222 | return 0; | |
223 | } | |
224 | ||
225 | /* Perform address translation */ | |
226 | int cpu_sparc_handle_mmu_fault (CPUState *env, uint32_t address, int rw, | |
227 | int is_user, int is_softmmu) | |
228 | { | |
229 | int exception = 0; | |
230 | uint32_t virt_addr, paddr; | |
231 | unsigned long vaddr; | |
232 | int error_code = 0, prot, ret = 0, access_index; | |
e8af50a3 | 233 | |
e80cfcfc FB |
234 | if (env->user_mode_only) { |
235 | /* user mode only emulation */ | |
236 | error_code = -2; | |
237 | goto do_fault_user; | |
238 | } | |
e8af50a3 | 239 | |
e80cfcfc FB |
240 | error_code = get_physical_address(env, &paddr, &prot, &access_index, address, rw, is_user); |
241 | if (error_code == 0) { | |
242 | virt_addr = address & TARGET_PAGE_MASK; | |
243 | vaddr = virt_addr + ((address & TARGET_PAGE_MASK) & (TARGET_PAGE_SIZE - 1)); | |
244 | ret = tlb_set_page(env, vaddr, paddr, prot, is_user, is_softmmu); | |
245 | return ret; | |
246 | } | |
e8af50a3 | 247 | |
e8af50a3 FB |
248 | if (env->mmuregs[3]) /* Fault status register */ |
249 | env->mmuregs[3] = 1; /* overflow (not read before another fault) */ | |
250 | env->mmuregs[3] |= (access_index << 5) | (error_code << 2) | 2; | |
251 | env->mmuregs[4] = address; /* Fault address register */ | |
252 | ||
8d5f07fa | 253 | if (env->mmuregs[0] & MMU_NF || env->psret == 0) // No fault |
e8af50a3 | 254 | return 0; |
e80cfcfc | 255 | do_fault_user: |
e8af50a3 FB |
256 | env->exception_index = exception; |
257 | env->error_code = error_code; | |
258 | return error_code; | |
259 | } | |
260 | ||
261 | void memcpy32(uint32_t *dst, const uint32_t *src) | |
262 | { | |
263 | dst[0] = src[0]; | |
264 | dst[1] = src[1]; | |
265 | dst[2] = src[2]; | |
266 | dst[3] = src[3]; | |
267 | dst[4] = src[4]; | |
268 | dst[5] = src[5]; | |
269 | dst[6] = src[6]; | |
270 | dst[7] = src[7]; | |
271 | } | |
272 | ||
273 | void set_cwp(int new_cwp) | |
274 | { | |
275 | /* put the modified wrap registers at their proper location */ | |
276 | if (env->cwp == (NWINDOWS - 1)) | |
277 | memcpy32(env->regbase, env->regbase + NWINDOWS * 16); | |
278 | env->cwp = new_cwp; | |
279 | /* put the wrap registers at their temporary location */ | |
280 | if (new_cwp == (NWINDOWS - 1)) | |
281 | memcpy32(env->regbase + NWINDOWS * 16, env->regbase); | |
282 | env->regwptr = env->regbase + (new_cwp * 16); | |
283 | } | |
284 | ||
285 | /* | |
286 | * Begin execution of an interruption. is_int is TRUE if coming from | |
287 | * the int instruction. next_eip is the EIP value AFTER the interrupt | |
288 | * instruction. It is only relevant if is_int is TRUE. | |
289 | */ | |
290 | void do_interrupt(int intno, int is_int, int error_code, | |
291 | unsigned int next_eip, int is_hw) | |
292 | { | |
293 | int cwp; | |
294 | ||
295 | #ifdef DEBUG_PCALL | |
296 | if (loglevel & CPU_LOG_INT) { | |
297 | static int count; | |
298 | fprintf(logfile, "%6d: v=%02x e=%04x i=%d pc=%08x npc=%08x SP=%08x\n", | |
299 | count, intno, error_code, is_int, | |
300 | env->pc, | |
8d5f07fa | 301 | env->npc, env->regwptr[6]); |
e80cfcfc | 302 | #if 1 |
7fe48483 | 303 | cpu_dump_state(env, logfile, fprintf, 0); |
e8af50a3 FB |
304 | { |
305 | int i; | |
306 | uint8_t *ptr; | |
e80cfcfc | 307 | |
e8af50a3 | 308 | fprintf(logfile, " code="); |
e80cfcfc | 309 | ptr = (uint8_t *)env->pc; |
e8af50a3 FB |
310 | for(i = 0; i < 16; i++) { |
311 | fprintf(logfile, " %02x", ldub(ptr + i)); | |
312 | } | |
313 | fprintf(logfile, "\n"); | |
314 | } | |
315 | #endif | |
316 | count++; | |
317 | } | |
e80cfcfc FB |
318 | #endif |
319 | #if !defined(CONFIG_USER_ONLY) | |
320 | if (env->psret == 0) { | |
321 | fprintf(logfile, "Trap while interrupts disabled, Error state!\n"); | |
322 | qemu_system_shutdown_request(); | |
323 | return; | |
324 | } | |
e8af50a3 FB |
325 | #endif |
326 | env->psret = 0; | |
327 | cwp = (env->cwp - 1) & (NWINDOWS - 1); | |
328 | set_cwp(cwp); | |
e80cfcfc FB |
329 | env->regwptr[9] = env->pc - 4; // XXX? |
330 | env->regwptr[10] = env->pc; | |
e8af50a3 FB |
331 | env->psrps = env->psrs; |
332 | env->psrs = 1; | |
333 | env->tbr = (env->tbr & TBR_BASE_MASK) | (intno << 4); | |
334 | env->pc = env->tbr; | |
335 | env->npc = env->pc + 4; | |
336 | env->exception_index = 0; | |
337 | } | |
338 | ||
339 | void raise_exception_err(int exception_index, int error_code) | |
340 | { | |
341 | raise_exception(exception_index); | |
342 | } | |
e80cfcfc FB |
343 | |
344 | uint32_t mmu_probe(uint32_t address, int mmulev) | |
345 | { | |
346 | target_phys_addr_t pde_ptr; | |
347 | uint32_t pde; | |
348 | ||
349 | /* Context base + context number */ | |
350 | pde_ptr = (env->mmuregs[1] << 4) + (env->mmuregs[2] << 4); | |
351 | cpu_physical_memory_read(pde_ptr, (uint8_t *)&pde, 4); | |
352 | bswap32s(&pde); | |
353 | switch (pde & PTE_ENTRYTYPE_MASK) { | |
354 | default: | |
355 | case 0: /* Invalid */ | |
356 | case 2: /* PTE, maybe should not happen? */ | |
357 | case 3: /* Reserved */ | |
358 | return 0; | |
359 | case 1: /* L1 PDE */ | |
360 | if (mmulev == 3) | |
361 | return pde; | |
362 | pde_ptr = ((address >> 22) & ~3) + ((pde & ~3) << 4); | |
363 | cpu_physical_memory_read(pde_ptr, (uint8_t *)&pde, 4); | |
364 | bswap32s(&pde); | |
365 | ||
366 | switch (pde & PTE_ENTRYTYPE_MASK) { | |
367 | default: | |
368 | case 0: /* Invalid */ | |
369 | case 3: /* Reserved */ | |
370 | return 0; | |
371 | case 2: /* L1 PTE */ | |
372 | return pde; | |
373 | case 1: /* L2 PDE */ | |
374 | if (mmulev == 2) | |
375 | return pde; | |
376 | pde_ptr = ((address & 0xfc0000) >> 16) + ((pde & ~3) << 4); | |
377 | cpu_physical_memory_read(pde_ptr, (uint8_t *)&pde, 4); | |
378 | bswap32s(&pde); | |
379 | ||
380 | switch (pde & PTE_ENTRYTYPE_MASK) { | |
381 | default: | |
382 | case 0: /* Invalid */ | |
383 | case 3: /* Reserved */ | |
384 | return 0; | |
385 | case 2: /* L2 PTE */ | |
386 | return pde; | |
387 | case 1: /* L3 PDE */ | |
388 | if (mmulev == 1) | |
389 | return pde; | |
390 | pde_ptr = ((address & 0x3f000) >> 10) + ((pde & ~3) << 4); | |
391 | cpu_physical_memory_read(pde_ptr, (uint8_t *)&pde, 4); | |
392 | bswap32s(&pde); | |
393 | ||
394 | switch (pde & PTE_ENTRYTYPE_MASK) { | |
395 | default: | |
396 | case 0: /* Invalid */ | |
397 | case 1: /* PDE, should not happen */ | |
398 | case 3: /* Reserved */ | |
399 | return 0; | |
400 | case 2: /* L3 PTE */ | |
401 | return pde; | |
402 | } | |
403 | } | |
404 | } | |
405 | } | |
406 | return 0; | |
407 | } | |
408 | ||
409 | void dump_mmu(void) | |
410 | { | |
411 | #ifdef DEBUG_MMU | |
412 | uint32_t pa, va, va1, va2; | |
413 | int n, m, o; | |
414 | target_phys_addr_t pde_ptr; | |
415 | uint32_t pde; | |
416 | ||
417 | printf("MMU dump:\n"); | |
418 | pde_ptr = (env->mmuregs[1] << 4) + (env->mmuregs[2] << 4); | |
419 | cpu_physical_memory_read(pde_ptr, (uint8_t *)&pde, 4); | |
420 | bswap32s(&pde); | |
421 | printf("Root ptr: 0x%08x, ctx: %d\n", env->mmuregs[1] << 4, env->mmuregs[2]); | |
422 | for (n = 0, va = 0; n < 256; n++, va += 16 * 1024 * 1024) { | |
423 | pde_ptr = mmu_probe(va, 2); | |
424 | if (pde_ptr) { | |
425 | pa = cpu_get_phys_page_debug(env, va); | |
426 | printf("VA: 0x%08x, PA: 0x%08x PDE: 0x%08x\n", va, pa, pde_ptr); | |
427 | for (m = 0, va1 = va; m < 64; m++, va1 += 256 * 1024) { | |
428 | pde_ptr = mmu_probe(va1, 1); | |
429 | if (pde_ptr) { | |
430 | pa = cpu_get_phys_page_debug(env, va1); | |
431 | printf(" VA: 0x%08x, PA: 0x%08x PDE: 0x%08x\n", va1, pa, pde_ptr); | |
432 | for (o = 0, va2 = va1; o < 64; o++, va2 += 4 * 1024) { | |
433 | pde_ptr = mmu_probe(va2, 0); | |
434 | if (pde_ptr) { | |
435 | pa = cpu_get_phys_page_debug(env, va2); | |
436 | printf(" VA: 0x%08x, PA: 0x%08x PTE: 0x%08x\n", va2, pa, pde_ptr); | |
437 | } | |
438 | } | |
439 | } | |
440 | } | |
441 | } | |
442 | } | |
443 | printf("MMU dump ends\n"); | |
444 | #endif | |
445 | } |