sparc64 marge (Blue Swirl)
[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     if (rw & 2)
47         env->exception_index = TT_TFAULT;
48     else
49         env->exception_index = TT_DFAULT;
50     return 1;
51 }
52
53 #else
54
55 #define MMUSUFFIX _mmu
56 #define GETPC() (__builtin_return_address(0))
57
58 #define SHIFT 0
59 #include "softmmu_template.h"
60
61 #define SHIFT 1
62 #include "softmmu_template.h"
63
64 #define SHIFT 2
65 #include "softmmu_template.h"
66
67 #define SHIFT 3
68 #include "softmmu_template.h"
69
70
71 /* try to fill the TLB and return an exception if error. If retaddr is
72    NULL, it means that the function was called in C code (i.e. not
73    from generated code or from helper.c) */
74 /* XXX: fix it to restore all registers */
75 void tlb_fill(target_ulong addr, int is_write, int is_user, void *retaddr)
76 {
77     TranslationBlock *tb;
78     int ret;
79     unsigned long pc;
80     CPUState *saved_env;
81
82     /* XXX: hack to restore env in all cases, even if not called from
83        generated code */
84     saved_env = env;
85     env = cpu_single_env;
86
87     ret = cpu_sparc_handle_mmu_fault(env, addr, is_write, is_user, 1);
88     if (ret) {
89         if (retaddr) {
90             /* now we have a real cpu fault */
91             pc = (unsigned long)retaddr;
92             tb = tb_find_pc(pc);
93             if (tb) {
94                 /* the PC is inside the translated code. It means that we have
95                    a virtual CPU fault */
96                 cpu_restore_state(tb, env, pc, (void *)T2);
97             }
98         }
99         cpu_loop_exit();
100     }
101     env = saved_env;
102 }
103
104 #ifndef TARGET_SPARC64
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 #else
272 static int get_physical_address_data(CPUState *env, target_phys_addr_t *physical, int *prot,
273                           int *access_index, target_ulong address, int rw,
274                           int is_user)
275 {
276     target_ulong mask;
277     unsigned int i;
278
279     if ((env->lsu & DMMU_E) == 0) { /* DMMU disabled */
280         *physical = address & 0xffffffff;
281         *prot = PAGE_READ | PAGE_WRITE;
282         return 0;
283     }
284
285     for (i = 0; i < 64; i++) {
286         if ((env->dtlb_tte[i] & 0x8000000000000000ULL) != 0) {
287             switch (env->dtlb_tte[i] >> 60) {
288             default:
289             case 0x4: // 8k
290                 mask = 0xffffffffffffe000ULL;
291                 break;
292             case 0x5: // 64k
293                 mask = 0xffffffffffff0000ULL;
294                 break;
295             case 0x6: // 512k
296                 mask = 0xfffffffffff80000ULL;
297                 break;
298             case 0x7: // 4M
299                 mask = 0xffffffffffc00000ULL;
300                 break;
301             }
302             // ctx match, vaddr match?
303             if (env->dmmuregs[1] == (env->dtlb_tag[i] & 0x1fff) &&
304                 (address & mask) == (env->dtlb_tag[i] & ~0x1fffULL)) {
305                 // access ok?
306                 if (((env->dtlb_tte[i] & 0x4) && !(env->pstate & PS_PRIV)) ||
307                     (!(env->dtlb_tte[i] & 0x2) && (rw == 1))) {
308                     env->exception_index = TT_DFAULT;
309                     return 1;
310                 }
311                 *physical = env->dtlb_tte[i] & 0xffffe000;
312                 *prot = PAGE_READ;
313                 if (env->dtlb_tte[i] & 0x2)
314                     *prot |= PAGE_WRITE;
315                 return 0;
316             }
317         }
318     }
319     env->exception_index = TT_DFAULT;
320     return 1;
321 }
322
323 static int get_physical_address_code(CPUState *env, target_phys_addr_t *physical, int *prot,
324                           int *access_index, target_ulong address, int rw,
325                           int is_user)
326 {
327     target_ulong mask;
328     unsigned int i;
329
330     if ((env->lsu & IMMU_E) == 0) { /* IMMU disabled */
331         *physical = address & 0xffffffff;
332         *prot = PAGE_READ;
333         return 0;
334     }
335     for (i = 0; i < 64; i++) {
336         if ((env->itlb_tte[i] & 0x8000000000000000ULL) != 0) {
337             switch (env->itlb_tte[i] >> 60) {
338             default:
339             case 0x4: // 8k
340                 mask = 0xffffffffffffe000ULL;
341                 break;
342             case 0x5: // 64k
343                 mask = 0xffffffffffff0000ULL;
344                 break;
345             case 0x6: // 512k
346                 mask = 0xfffffffffff80000ULL;
347                 break;
348             case 0x7: // 4M
349                 mask = 0xffffffffffc00000ULL;
350                 break;
351             }
352             // ctx match, vaddr match?
353             if (env->immuregs[1] == (env->itlb_tag[i] & 0x1fff) &&
354                 (address & mask) == (env->itlb_tag[i] & ~0x1fffULL)) {
355                 // access ok?
356                 if ((env->itlb_tte[i] & 0x4) && !(env->pstate & PS_PRIV)) {
357                     env->exception_index = TT_TFAULT;
358                     return 1;
359                 }
360                 *physical = env->itlb_tte[i] & 0xffffe000;
361                 *prot = PAGE_READ;
362                 return 0;
363             }
364         }
365     }
366     env->exception_index = TT_TFAULT;
367     return 1;
368 }
369
370 int get_physical_address(CPUState *env, target_phys_addr_t *physical, int *prot,
371                           int *access_index, target_ulong address, int rw,
372                           int is_user)
373 {
374     if (rw == 2)
375         return get_physical_address_code(env, physical, prot, access_index, address, rw, is_user);
376     else
377         return get_physical_address_data(env, physical, prot, access_index, address, rw, is_user);
378 }
379
380 /* Perform address translation */
381 int cpu_sparc_handle_mmu_fault (CPUState *env, target_ulong address, int rw,
382                               int is_user, int is_softmmu)
383 {
384     target_ulong virt_addr;
385     target_phys_addr_t paddr;
386     unsigned long vaddr;
387     int error_code = 0, prot, ret = 0, access_index;
388
389     error_code = get_physical_address(env, &paddr, &prot, &access_index, address, rw, is_user);
390     if (error_code == 0) {
391         virt_addr = address & TARGET_PAGE_MASK;
392         vaddr = virt_addr + ((address & TARGET_PAGE_MASK) & (TARGET_PAGE_SIZE - 1));
393         ret = tlb_set_page(env, vaddr, paddr, prot, is_user, is_softmmu);
394         return ret;
395     }
396     // XXX
397     return 1;
398 }
399
400 #endif
401 #endif
402
403 void memcpy32(target_ulong *dst, const target_ulong *src)
404 {
405     dst[0] = src[0];
406     dst[1] = src[1];
407     dst[2] = src[2];
408     dst[3] = src[3];
409     dst[4] = src[4];
410     dst[5] = src[5];
411     dst[6] = src[6];
412     dst[7] = src[7];
413 }
414
415 void set_cwp(int new_cwp)
416 {
417     /* put the modified wrap registers at their proper location */
418     if (env->cwp == (NWINDOWS - 1))
419         memcpy32(env->regbase, env->regbase + NWINDOWS * 16);
420     env->cwp = new_cwp;
421     /* put the wrap registers at their temporary location */
422     if (new_cwp == (NWINDOWS - 1))
423         memcpy32(env->regbase + NWINDOWS * 16, env->regbase);
424     env->regwptr = env->regbase + (new_cwp * 16);
425     REGWPTR = env->regwptr;
426 }
427
428 void cpu_set_cwp(CPUState *env1, int new_cwp)
429 {
430     CPUState *saved_env;
431 #ifdef reg_REGWPTR
432     target_ulong *saved_regwptr;
433 #endif
434
435     saved_env = env;
436 #ifdef reg_REGWPTR
437     saved_regwptr = REGWPTR;
438 #endif
439     env = env1;
440     set_cwp(new_cwp);
441     env = saved_env;
442 #ifdef reg_REGWPTR
443     REGWPTR = saved_regwptr;
444 #endif
445 }
446
447 #ifdef TARGET_SPARC64
448 void do_interrupt(int intno)
449 {
450 #ifdef DEBUG_PCALL
451     if (loglevel & CPU_LOG_INT) {
452         static int count;
453         fprintf(logfile, "%6d: v=%02x pc=%08x npc=%08x SP=%08x\n",
454                 count, intno,
455                 env->pc,
456                 env->npc, env->regwptr[6]);
457         cpu_dump_state(env, logfile, fprintf, 0);
458 #if 0
459         {
460             int i;
461             uint8_t *ptr;
462
463             fprintf(logfile, "       code=");
464             ptr = (uint8_t *)env->pc;
465             for(i = 0; i < 16; i++) {
466                 fprintf(logfile, " %02x", ldub(ptr + i));
467             }
468             fprintf(logfile, "\n");
469         }
470 #endif
471         count++;
472     }
473 #endif
474 #if !defined(CONFIG_USER_ONLY) 
475     if (env->pstate & PS_IE) {
476         cpu_abort(cpu_single_env, "Trap 0x%02x while interrupts disabled, Error state", env->exception_index);
477         return;
478     }
479 #endif
480     env->tstate[env->tl] = ((uint64_t)GET_CCR(env) << 32) | ((env->asi & 0xff) << 24) |
481         ((env->pstate & 0xfff) << 8) | (env->cwp & 0xff);
482     env->tpc[env->tl] = env->pc;
483     env->tnpc[env->tl] = env->npc;
484     env->tt[env->tl] = intno;
485     env->tbr = env->tbr | (env->tl > 1) ? 1 << 14 : 0 | (intno << 4);
486     env->tl++;
487     env->pc = env->tbr;
488     env->npc = env->pc + 4;
489     env->exception_index = 0;
490 }
491 #else
492 void do_interrupt(int intno)
493 {
494     int cwp;
495
496 #ifdef DEBUG_PCALL
497     if (loglevel & CPU_LOG_INT) {
498         static int count;
499         fprintf(logfile, "%6d: v=%02x pc=%08x npc=%08x SP=%08x\n",
500                 count, intno,
501                 env->pc,
502                 env->npc, env->regwptr[6]);
503         cpu_dump_state(env, logfile, fprintf, 0);
504 #if 0
505         {
506             int i;
507             uint8_t *ptr;
508
509             fprintf(logfile, "       code=");
510             ptr = (uint8_t *)env->pc;
511             for(i = 0; i < 16; i++) {
512                 fprintf(logfile, " %02x", ldub(ptr + i));
513             }
514             fprintf(logfile, "\n");
515         }
516 #endif
517         count++;
518     }
519 #endif
520 #if !defined(CONFIG_USER_ONLY) 
521     if (env->psret == 0) {
522         cpu_abort(cpu_single_env, "Trap 0x%02x while interrupts disabled, Error state", env->exception_index);
523         return;
524     }
525 #endif
526     env->psret = 0;
527     cwp = (env->cwp - 1) & (NWINDOWS - 1); 
528     set_cwp(cwp);
529     env->regwptr[9] = env->pc;
530     env->regwptr[10] = env->npc;
531     env->psrps = env->psrs;
532     env->psrs = 1;
533     env->tbr = (env->tbr & TBR_BASE_MASK) | (intno << 4);
534     env->pc = env->tbr;
535     env->npc = env->pc + 4;
536     env->exception_index = 0;
537 }
538
539 target_ulong mmu_probe(target_ulong address, int mmulev)
540 {
541     target_phys_addr_t pde_ptr;
542     uint32_t pde;
543
544     /* Context base + context number */
545     pde_ptr = (env->mmuregs[1] << 4) + (env->mmuregs[2] << 2);
546     pde = ldl_phys(pde_ptr);
547
548     switch (pde & PTE_ENTRYTYPE_MASK) {
549     default:
550     case 0: /* Invalid */
551     case 2: /* PTE, maybe should not happen? */
552     case 3: /* Reserved */
553         return 0;
554     case 1: /* L1 PDE */
555         if (mmulev == 3)
556             return pde;
557         pde_ptr = ((address >> 22) & ~3) + ((pde & ~3) << 4);
558         pde = ldl_phys(pde_ptr);
559
560         switch (pde & PTE_ENTRYTYPE_MASK) {
561         default:
562         case 0: /* Invalid */
563         case 3: /* Reserved */
564             return 0;
565         case 2: /* L1 PTE */
566             return pde;
567         case 1: /* L2 PDE */
568             if (mmulev == 2)
569                 return pde;
570             pde_ptr = ((address & 0xfc0000) >> 16) + ((pde & ~3) << 4);
571             pde = ldl_phys(pde_ptr);
572
573             switch (pde & PTE_ENTRYTYPE_MASK) {
574             default:
575             case 0: /* Invalid */
576             case 3: /* Reserved */
577                 return 0;
578             case 2: /* L2 PTE */
579                 return pde;
580             case 1: /* L3 PDE */
581                 if (mmulev == 1)
582                     return pde;
583                 pde_ptr = ((address & 0x3f000) >> 10) + ((pde & ~3) << 4);
584                 pde = ldl_phys(pde_ptr);
585
586                 switch (pde & PTE_ENTRYTYPE_MASK) {
587                 default:
588                 case 0: /* Invalid */
589                 case 1: /* PDE, should not happen */
590                 case 3: /* Reserved */
591                     return 0;
592                 case 2: /* L3 PTE */
593                     return pde;
594                 }
595             }
596         }
597     }
598     return 0;
599 }
600
601 #ifdef DEBUG_MMU
602 void dump_mmu(void)
603 {
604      target_ulong va, va1, va2;
605      unsigned int n, m, o;
606      target_phys_addr_t pde_ptr, pa;
607     uint32_t pde;
608
609     printf("MMU dump:\n");
610     pde_ptr = (env->mmuregs[1] << 4) + (env->mmuregs[2] << 2);
611     pde = ldl_phys(pde_ptr);
612     printf("Root ptr: " TARGET_FMT_lx ", ctx: %d\n", env->mmuregs[1] << 4, env->mmuregs[2]);
613     for (n = 0, va = 0; n < 256; n++, va += 16 * 1024 * 1024) {
614         pde_ptr = mmu_probe(va, 2);
615         if (pde_ptr) {
616             pa = cpu_get_phys_page_debug(env, va);
617             printf("VA: " TARGET_FMT_lx ", PA: " TARGET_FMT_lx " PDE: " TARGET_FMT_lx "\n", va, pa, pde_ptr);
618             for (m = 0, va1 = va; m < 64; m++, va1 += 256 * 1024) {
619                 pde_ptr = mmu_probe(va1, 1);
620                 if (pde_ptr) {
621                     pa = cpu_get_phys_page_debug(env, va1);
622                     printf(" VA: " TARGET_FMT_lx ", PA: " TARGET_FMT_lx " PDE: " TARGET_FMT_lx "\n", va1, pa, pde_ptr);
623                     for (o = 0, va2 = va1; o < 64; o++, va2 += 4 * 1024) {
624                         pde_ptr = mmu_probe(va2, 0);
625                         if (pde_ptr) {
626                             pa = cpu_get_phys_page_debug(env, va2);
627                             printf("  VA: " TARGET_FMT_lx ", PA: " TARGET_FMT_lx " PTE: " TARGET_FMT_lx "\n", va2, pa, pde_ptr);
628                         }
629                     }
630                 }
631             }
632         }
633     }
634     printf("MMU dump ends\n");
635 }
636 #endif
637 #endif