19 |
*/ |
*/ |
20 |
#include "exec.h" |
#include "exec.h" |
21 |
|
|
22 |
#define DEBUG_PCALL |
//#define DEBUG_PCALL |
23 |
|
//#define DEBUG_MMU |
24 |
|
|
25 |
/* Sparc MMU emulation */ |
/* Sparc MMU emulation */ |
26 |
int cpu_sparc_handle_mmu_fault (CPUState *env, uint32_t address, int rw, |
int cpu_sparc_handle_mmu_fault (CPUState *env, uint32_t address, int rw, |
109 |
{ 0, 1, 0, 1, 0, 0, 0, 0 } |
{ 0, 1, 0, 1, 0, 0, 0, 0 } |
110 |
}; |
}; |
111 |
|
|
112 |
|
int get_physical_address (CPUState *env, uint32_t *physical, int *prot, |
113 |
/* Perform address translation */ |
int *access_index, uint32_t address, int rw, |
114 |
int cpu_sparc_handle_mmu_fault (CPUState *env, uint32_t address, int rw, |
int is_user) |
|
int is_user, int is_softmmu) |
|
115 |
{ |
{ |
116 |
int exception = 0; |
int access_perms = 0; |
117 |
int access_perms = 0, access_index = 0; |
target_phys_addr_t pde_ptr; |
|
uint8_t *pde_ptr; |
|
118 |
uint32_t pde, virt_addr; |
uint32_t pde, virt_addr; |
119 |
int error_code = 0, is_dirty, prot, ret = 0; |
int error_code = 0, is_dirty; |
120 |
unsigned long paddr, vaddr, page_offset; |
unsigned long page_offset; |
|
|
|
|
if (env->user_mode_only) { |
|
|
/* user mode only emulation */ |
|
|
ret = -2; |
|
|
goto do_fault; |
|
|
} |
|
121 |
|
|
122 |
virt_addr = address & TARGET_PAGE_MASK; |
virt_addr = address & TARGET_PAGE_MASK; |
123 |
if ((env->mmuregs[0] & MMU_E) == 0) { /* MMU disabled */ |
if ((env->mmuregs[0] & MMU_E) == 0) { /* MMU disabled */ |
124 |
paddr = address; |
*physical = address; |
125 |
page_offset = address & (TARGET_PAGE_SIZE - 1); |
*prot = PAGE_READ | PAGE_WRITE; |
126 |
prot = PAGE_READ | PAGE_WRITE; |
return 0; |
|
goto do_mapping; |
|
127 |
} |
} |
128 |
|
|
129 |
/* SPARC reference MMU table walk: Context table->L1->L2->PTE */ |
/* SPARC reference MMU table walk: Context table->L1->L2->PTE */ |
130 |
/* Context base + context number */ |
/* Context base + context number */ |
131 |
pde_ptr = phys_ram_base + (env->mmuregs[1] << 4) + (env->mmuregs[2] << 4); |
pde_ptr = (env->mmuregs[1] << 4) + (env->mmuregs[2] << 4); |
132 |
pde = ldl_raw(pde_ptr); |
cpu_physical_memory_read(pde_ptr, (uint8_t *)&pde, 4); |
133 |
|
bswap32s(&pde); |
134 |
|
|
135 |
/* Ctx pde */ |
/* Ctx pde */ |
136 |
switch (pde & PTE_ENTRYTYPE_MASK) { |
switch (pde & PTE_ENTRYTYPE_MASK) { |
137 |
|
default: |
138 |
case 0: /* Invalid */ |
case 0: /* Invalid */ |
139 |
error_code = 1; |
return 1; |
140 |
goto do_fault; |
case 2: /* L0 PTE, maybe should not happen? */ |
|
case 2: /* PTE, maybe should not happen? */ |
|
141 |
case 3: /* Reserved */ |
case 3: /* Reserved */ |
142 |
error_code = 4; |
return 4; |
143 |
goto do_fault; |
case 1: /* L0 PDE */ |
144 |
case 1: /* L1 PDE */ |
pde_ptr = ((address >> 22) & ~3) + ((pde & ~3) << 4); |
145 |
pde_ptr = phys_ram_base + ((address >> 22) & ~3) + ((pde & ~3) << 4); |
cpu_physical_memory_read(pde_ptr, (uint8_t *)&pde, 4); |
146 |
pde = ldl_raw(pde_ptr); |
bswap32s(&pde); |
147 |
|
|
148 |
switch (pde & PTE_ENTRYTYPE_MASK) { |
switch (pde & PTE_ENTRYTYPE_MASK) { |
149 |
|
default: |
150 |
case 0: /* Invalid */ |
case 0: /* Invalid */ |
151 |
error_code = 1; |
return 1; |
|
goto do_fault; |
|
152 |
case 3: /* Reserved */ |
case 3: /* Reserved */ |
153 |
error_code = 4; |
return 4; |
154 |
goto do_fault; |
case 1: /* L1 PDE */ |
155 |
case 1: /* L2 PDE */ |
pde_ptr = ((address & 0xfc0000) >> 16) + ((pde & ~3) << 4); |
156 |
pde_ptr = phys_ram_base + ((address & 0xfc0000) >> 16) + ((pde & ~3) << 4); |
cpu_physical_memory_read(pde_ptr, (uint8_t *)&pde, 4); |
157 |
pde = ldl_raw(pde_ptr); |
bswap32s(&pde); |
158 |
|
|
159 |
switch (pde & PTE_ENTRYTYPE_MASK) { |
switch (pde & PTE_ENTRYTYPE_MASK) { |
160 |
|
default: |
161 |
case 0: /* Invalid */ |
case 0: /* Invalid */ |
162 |
error_code = 1; |
return 1; |
|
goto do_fault; |
|
163 |
case 3: /* Reserved */ |
case 3: /* Reserved */ |
164 |
error_code = 4; |
return 4; |
165 |
goto do_fault; |
case 1: /* L2 PDE */ |
166 |
case 1: /* L3 PDE */ |
pde_ptr = ((address & 0x3f000) >> 10) + ((pde & ~3) << 4); |
167 |
pde_ptr = phys_ram_base + ((address & 0x3f000) >> 10) + ((pde & ~3) << 4); |
cpu_physical_memory_read(pde_ptr, (uint8_t *)&pde, 4); |
168 |
pde = ldl_raw(pde_ptr); |
bswap32s(&pde); |
169 |
|
|
170 |
switch (pde & PTE_ENTRYTYPE_MASK) { |
switch (pde & PTE_ENTRYTYPE_MASK) { |
171 |
|
default: |
172 |
case 0: /* Invalid */ |
case 0: /* Invalid */ |
173 |
error_code = 1; |
return 1; |
|
goto do_fault; |
|
174 |
case 1: /* PDE, should not happen */ |
case 1: /* PDE, should not happen */ |
175 |
case 3: /* Reserved */ |
case 3: /* Reserved */ |
176 |
error_code = 4; |
return 4; |
|
goto do_fault; |
|
177 |
case 2: /* L3 PTE */ |
case 2: /* L3 PTE */ |
178 |
virt_addr = address & TARGET_PAGE_MASK; |
virt_addr = address & TARGET_PAGE_MASK; |
179 |
page_offset = (address & TARGET_PAGE_MASK) & (TARGET_PAGE_SIZE - 1); |
page_offset = (address & TARGET_PAGE_MASK) & (TARGET_PAGE_SIZE - 1); |
193 |
/* update page modified and dirty bits */ |
/* update page modified and dirty bits */ |
194 |
is_dirty = (rw & 1) && !(pde & PG_MODIFIED_MASK); |
is_dirty = (rw & 1) && !(pde & PG_MODIFIED_MASK); |
195 |
if (!(pde & PG_ACCESSED_MASK) || is_dirty) { |
if (!(pde & PG_ACCESSED_MASK) || is_dirty) { |
196 |
|
uint32_t tmppde; |
197 |
pde |= PG_ACCESSED_MASK; |
pde |= PG_ACCESSED_MASK; |
198 |
if (is_dirty) |
if (is_dirty) |
199 |
pde |= PG_MODIFIED_MASK; |
pde |= PG_MODIFIED_MASK; |
200 |
stl_raw(pde_ptr, pde); |
tmppde = bswap32(pde); |
201 |
|
cpu_physical_memory_write(pde_ptr, (uint8_t *)&tmppde, 4); |
202 |
} |
} |
|
|
|
203 |
/* check access */ |
/* check access */ |
204 |
access_index = ((rw & 1) << 2) | (rw & 2) | (is_user? 0 : 1); |
*access_index = ((rw & 1) << 2) | (rw & 2) | (is_user? 0 : 1); |
205 |
access_perms = (pde & PTE_ACCESS_MASK) >> PTE_ACCESS_SHIFT; |
access_perms = (pde & PTE_ACCESS_MASK) >> PTE_ACCESS_SHIFT; |
206 |
error_code = access_table[access_index][access_perms]; |
error_code = access_table[*access_index][access_perms]; |
207 |
if (error_code) |
if (error_code) |
208 |
goto do_fault; |
return error_code; |
209 |
|
|
210 |
/* the page can be put in the TLB */ |
/* the page can be put in the TLB */ |
211 |
prot = PAGE_READ; |
*prot = PAGE_READ; |
212 |
if (pde & PG_MODIFIED_MASK) { |
if (pde & PG_MODIFIED_MASK) { |
213 |
/* only set write access if already dirty... otherwise wait |
/* only set write access if already dirty... otherwise wait |
214 |
for dirty access */ |
for dirty access */ |
215 |
if (rw_table[is_user][access_perms]) |
if (rw_table[is_user][access_perms]) |
216 |
prot |= PAGE_WRITE; |
*prot |= PAGE_WRITE; |
217 |
} |
} |
218 |
|
|
219 |
/* Even if large ptes, we map only one 4KB page in the cache to |
/* Even if large ptes, we map only one 4KB page in the cache to |
220 |
avoid filling it too fast */ |
avoid filling it too fast */ |
221 |
virt_addr = address & TARGET_PAGE_MASK; |
*physical = ((pde & PTE_ADDR_MASK) << 4) + page_offset; |
222 |
paddr = ((pde & PTE_ADDR_MASK) << 4) + page_offset; |
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; |
233 |
|
|
234 |
do_mapping: |
if (env->user_mode_only) { |
235 |
vaddr = virt_addr + ((address & TARGET_PAGE_MASK) & (TARGET_PAGE_SIZE - 1)); |
/* user mode only emulation */ |
236 |
|
error_code = -2; |
237 |
|
goto do_fault_user; |
238 |
|
} |
239 |
|
|
240 |
ret = tlb_set_page(env, vaddr, paddr, prot, is_user, is_softmmu); |
error_code = get_physical_address(env, &paddr, &prot, &access_index, address, rw, is_user); |
241 |
return ret; |
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 |
|
} |
247 |
|
|
|
do_fault: |
|
248 |
if (env->mmuregs[3]) /* Fault status register */ |
if (env->mmuregs[3]) /* Fault status register */ |
249 |
env->mmuregs[3] = 1; /* overflow (not read before another fault) */ |
env->mmuregs[3] = 1; /* overflow (not read before another fault) */ |
250 |
env->mmuregs[3] |= (access_index << 5) | (error_code << 2) | 2; |
env->mmuregs[3] |= (access_index << 5) | (error_code << 2) | 2; |
252 |
|
|
253 |
if (env->mmuregs[0] & MMU_NF || env->psret == 0) // No fault |
if (env->mmuregs[0] & MMU_NF || env->psret == 0) // No fault |
254 |
return 0; |
return 0; |
255 |
|
do_fault_user: |
256 |
env->exception_index = exception; |
env->exception_index = exception; |
257 |
env->error_code = error_code; |
env->error_code = error_code; |
258 |
return error_code; |
return error_code; |
299 |
count, intno, error_code, is_int, |
count, intno, error_code, is_int, |
300 |
env->pc, |
env->pc, |
301 |
env->npc, env->regwptr[6]); |
env->npc, env->regwptr[6]); |
302 |
#if 0 |
#if 1 |
303 |
cpu_dump_state(env, logfile, fprintf, 0); |
cpu_dump_state(env, logfile, fprintf, 0); |
304 |
{ |
{ |
305 |
int i; |
int i; |
306 |
uint8_t *ptr; |
uint8_t *ptr; |
307 |
|
|
308 |
fprintf(logfile, " code="); |
fprintf(logfile, " code="); |
309 |
ptr = env->pc; |
ptr = (uint8_t *)env->pc; |
310 |
for(i = 0; i < 16; i++) { |
for(i = 0; i < 16; i++) { |
311 |
fprintf(logfile, " %02x", ldub(ptr + i)); |
fprintf(logfile, " %02x", ldub(ptr + i)); |
312 |
} |
} |
316 |
count++; |
count++; |
317 |
} |
} |
318 |
#endif |
#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 |
|
} |
325 |
|
#endif |
326 |
env->psret = 0; |
env->psret = 0; |
327 |
cwp = (env->cwp - 1) & (NWINDOWS - 1); |
cwp = (env->cwp - 1) & (NWINDOWS - 1); |
328 |
set_cwp(cwp); |
set_cwp(cwp); |
329 |
env->regwptr[9] = env->pc; |
env->regwptr[9] = env->pc - 4; // XXX? |
330 |
env->regwptr[10] = env->npc; |
env->regwptr[10] = env->pc; |
331 |
env->psrps = env->psrs; |
env->psrps = env->psrs; |
332 |
env->psrs = 1; |
env->psrs = 1; |
333 |
env->tbr = (env->tbr & TBR_BASE_MASK) | (intno << 4); |
env->tbr = (env->tbr & TBR_BASE_MASK) | (intno << 4); |
340 |
{ |
{ |
341 |
raise_exception(exception_index); |
raise_exception(exception_index); |
342 |
} |
} |
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 |
|
} |