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