PAGE_EXEC support (Blue Swirl)
[qemu] / target-sparc / helper.c
1 /*
2  *  sparc helpers
3  * 
4  *  Copyright (c) 2003-2005 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 <stdarg.h>
21 #include <stdlib.h>
22 #include <stdio.h>
23 #include <string.h>
24 #include <inttypes.h>
25 #include <signal.h>
26 #include <assert.h>
27
28 #include "cpu.h"
29 #include "exec-all.h"
30
31 //#define DEBUG_MMU
32
33 /* Sparc MMU emulation */
34
35 /* thread support */
36
37 spinlock_t global_cpu_lock = SPIN_LOCK_UNLOCKED;
38
39 void cpu_lock(void)
40 {
41     spin_lock(&global_cpu_lock);
42 }
43
44 void cpu_unlock(void)
45 {
46     spin_unlock(&global_cpu_lock);
47 }
48
49 #if defined(CONFIG_USER_ONLY) 
50
51 int cpu_sparc_handle_mmu_fault(CPUState *env, target_ulong address, int rw,
52                                int is_user, int is_softmmu)
53 {
54     if (rw & 2)
55         env->exception_index = TT_TFAULT;
56     else
57         env->exception_index = TT_DFAULT;
58     return 1;
59 }
60
61 #else
62
63 #ifndef TARGET_SPARC64
64 /*
65  * Sparc V8 Reference MMU (SRMMU)
66  */
67 static const int access_table[8][8] = {
68     { 0, 0, 0, 0, 2, 0, 3, 3 },
69     { 0, 0, 0, 0, 2, 0, 0, 0 },
70     { 2, 2, 0, 0, 0, 2, 3, 3 },
71     { 2, 2, 0, 0, 0, 2, 0, 0 },
72     { 2, 0, 2, 0, 2, 2, 3, 3 },
73     { 2, 0, 2, 0, 2, 0, 2, 0 },
74     { 2, 2, 2, 0, 2, 2, 3, 3 },
75     { 2, 2, 2, 0, 2, 2, 2, 0 }
76 };
77
78 static const int perm_table[2][8] = {
79     {
80         PAGE_READ,
81         PAGE_READ | PAGE_WRITE,
82         PAGE_READ | PAGE_EXEC,
83         PAGE_READ | PAGE_WRITE | PAGE_EXEC,
84         PAGE_EXEC,
85         PAGE_READ | PAGE_WRITE,
86         PAGE_READ | PAGE_EXEC,
87         PAGE_READ | PAGE_WRITE | PAGE_EXEC
88     },
89     {
90         PAGE_READ,
91         PAGE_READ | PAGE_WRITE,
92         PAGE_READ | PAGE_EXEC,
93         PAGE_READ | PAGE_WRITE | PAGE_EXEC,
94         PAGE_EXEC,
95         PAGE_READ,
96         0,
97         0,
98     }
99 };
100
101 int get_physical_address (CPUState *env, target_phys_addr_t *physical, int *prot,
102                           int *access_index, target_ulong address, int rw,
103                           int is_user)
104 {
105     int access_perms = 0;
106     target_phys_addr_t pde_ptr;
107     uint32_t pde;
108     target_ulong virt_addr;
109     int error_code = 0, is_dirty;
110     unsigned long page_offset;
111
112     virt_addr = address & TARGET_PAGE_MASK;
113     if ((env->mmuregs[0] & MMU_E) == 0) { /* MMU disabled */
114         *physical = address;
115         *prot = PAGE_READ | PAGE_WRITE | PAGE_EXEC;
116         return 0;
117     }
118
119     *access_index = ((rw & 1) << 2) | (rw & 2) | (is_user? 0 : 1);
120     *physical = 0xfffff000;
121
122     /* SPARC reference MMU table walk: Context table->L1->L2->PTE */
123     /* Context base + context number */
124     pde_ptr = (env->mmuregs[1] << 4) + (env->mmuregs[2] << 2);
125     pde = ldl_phys(pde_ptr);
126
127     /* Ctx pde */
128     switch (pde & PTE_ENTRYTYPE_MASK) {
129     default:
130     case 0: /* Invalid */
131         return 1 << 2;
132     case 2: /* L0 PTE, maybe should not happen? */
133     case 3: /* Reserved */
134         return 4 << 2;
135     case 1: /* L0 PDE */
136         pde_ptr = ((address >> 22) & ~3) + ((pde & ~3) << 4);
137         pde = ldl_phys(pde_ptr);
138
139         switch (pde & PTE_ENTRYTYPE_MASK) {
140         default:
141         case 0: /* Invalid */
142             return (1 << 8) | (1 << 2);
143         case 3: /* Reserved */
144             return (1 << 8) | (4 << 2);
145         case 1: /* L1 PDE */
146             pde_ptr = ((address & 0xfc0000) >> 16) + ((pde & ~3) << 4);
147             pde = ldl_phys(pde_ptr);
148
149             switch (pde & PTE_ENTRYTYPE_MASK) {
150             default:
151             case 0: /* Invalid */
152                 return (2 << 8) | (1 << 2);
153             case 3: /* Reserved */
154                 return (2 << 8) | (4 << 2);
155             case 1: /* L2 PDE */
156                 pde_ptr = ((address & 0x3f000) >> 10) + ((pde & ~3) << 4);
157                 pde = ldl_phys(pde_ptr);
158
159                 switch (pde & PTE_ENTRYTYPE_MASK) {
160                 default:
161                 case 0: /* Invalid */
162                     return (3 << 8) | (1 << 2);
163                 case 1: /* PDE, should not happen */
164                 case 3: /* Reserved */
165                     return (3 << 8) | (4 << 2);
166                 case 2: /* L3 PTE */
167                     virt_addr = address & TARGET_PAGE_MASK;
168                     page_offset = (address & TARGET_PAGE_MASK) & (TARGET_PAGE_SIZE - 1);
169                 }
170                 break;
171             case 2: /* L2 PTE */
172                 virt_addr = address & ~0x3ffff;
173                 page_offset = address & 0x3ffff;
174             }
175             break;
176         case 2: /* L1 PTE */
177             virt_addr = address & ~0xffffff;
178             page_offset = address & 0xffffff;
179         }
180     }
181
182     /* update page modified and dirty bits */
183     is_dirty = (rw & 1) && !(pde & PG_MODIFIED_MASK);
184     if (!(pde & PG_ACCESSED_MASK) || is_dirty) {
185         pde |= PG_ACCESSED_MASK;
186         if (is_dirty)
187             pde |= PG_MODIFIED_MASK;
188         stl_phys_notdirty(pde_ptr, pde);
189     }
190     /* check access */
191     access_perms = (pde & PTE_ACCESS_MASK) >> PTE_ACCESS_SHIFT;
192     error_code = access_table[*access_index][access_perms];
193     if (error_code && !(env->mmuregs[0] & MMU_NF))
194         return error_code;
195
196     /* the page can be put in the TLB */
197     *prot = perm_table[is_user][access_perms];
198     if (!(pde & PG_MODIFIED_MASK)) {
199         /* only set write access if already dirty... otherwise wait
200            for dirty access */
201         *prot &= ~PAGE_WRITE;
202     }
203
204     /* Even if large ptes, we map only one 4KB page in the cache to
205        avoid filling it too fast */
206     *physical = ((pde & PTE_ADDR_MASK) << 4) + page_offset;
207     return error_code;
208 }
209
210 /* Perform address translation */
211 int cpu_sparc_handle_mmu_fault (CPUState *env, target_ulong address, int rw,
212                               int is_user, int is_softmmu)
213 {
214     target_phys_addr_t paddr;
215     unsigned long vaddr;
216     int error_code = 0, prot, ret = 0, access_index;
217
218     error_code = get_physical_address(env, &paddr, &prot, &access_index, address, rw, is_user);
219     if (error_code == 0) {
220         vaddr = address & TARGET_PAGE_MASK;
221         paddr &= TARGET_PAGE_MASK;
222 #ifdef DEBUG_MMU
223         printf("Translate at 0x%lx -> 0x%lx, vaddr 0x%lx\n", (long)address, (long)paddr, (long)vaddr);
224 #endif
225         ret = tlb_set_page_exec(env, vaddr, paddr, prot, is_user, is_softmmu);
226         return ret;
227     }
228
229     if (env->mmuregs[3]) /* Fault status register */
230         env->mmuregs[3] = 1; /* overflow (not read before another fault) */
231     env->mmuregs[3] |= (access_index << 5) | error_code | 2;
232     env->mmuregs[4] = address; /* Fault address register */
233
234     if ((env->mmuregs[0] & MMU_NF) || env->psret == 0)  {
235         // No fault mode: if a mapping is available, just override
236         // permissions. If no mapping is available, redirect accesses to
237         // neverland. Fake/overridden mappings will be flushed when
238         // switching to normal mode.
239         vaddr = address & TARGET_PAGE_MASK;
240         prot = PAGE_READ | PAGE_WRITE | PAGE_EXEC;
241         ret = tlb_set_page_exec(env, vaddr, paddr, prot, is_user, is_softmmu);
242         return ret;
243     } else {
244         if (rw & 2)
245             env->exception_index = TT_TFAULT;
246         else
247             env->exception_index = TT_DFAULT;
248         return 1;
249     }
250 }
251
252 target_ulong mmu_probe(CPUState *env, target_ulong address, int mmulev)
253 {
254     target_phys_addr_t pde_ptr;
255     uint32_t pde;
256
257     /* Context base + context number */
258     pde_ptr = (env->mmuregs[1] << 4) + (env->mmuregs[2] << 2);
259     pde = ldl_phys(pde_ptr);
260
261     switch (pde & PTE_ENTRYTYPE_MASK) {
262     default:
263     case 0: /* Invalid */
264     case 2: /* PTE, maybe should not happen? */
265     case 3: /* Reserved */
266         return 0;
267     case 1: /* L1 PDE */
268         if (mmulev == 3)
269             return pde;
270         pde_ptr = ((address >> 22) & ~3) + ((pde & ~3) << 4);
271         pde = ldl_phys(pde_ptr);
272
273         switch (pde & PTE_ENTRYTYPE_MASK) {
274         default:
275         case 0: /* Invalid */
276         case 3: /* Reserved */
277             return 0;
278         case 2: /* L1 PTE */
279             return pde;
280         case 1: /* L2 PDE */
281             if (mmulev == 2)
282                 return pde;
283             pde_ptr = ((address & 0xfc0000) >> 16) + ((pde & ~3) << 4);
284             pde = ldl_phys(pde_ptr);
285
286             switch (pde & PTE_ENTRYTYPE_MASK) {
287             default:
288             case 0: /* Invalid */
289             case 3: /* Reserved */
290                 return 0;
291             case 2: /* L2 PTE */
292                 return pde;
293             case 1: /* L3 PDE */
294                 if (mmulev == 1)
295                     return pde;
296                 pde_ptr = ((address & 0x3f000) >> 10) + ((pde & ~3) << 4);
297                 pde = ldl_phys(pde_ptr);
298
299                 switch (pde & PTE_ENTRYTYPE_MASK) {
300                 default:
301                 case 0: /* Invalid */
302                 case 1: /* PDE, should not happen */
303                 case 3: /* Reserved */
304                     return 0;
305                 case 2: /* L3 PTE */
306                     return pde;
307                 }
308             }
309         }
310     }
311     return 0;
312 }
313
314 #ifdef DEBUG_MMU
315 void dump_mmu(CPUState *env)
316 {
317      target_ulong va, va1, va2;
318      unsigned int n, m, o;
319      target_phys_addr_t pde_ptr, pa;
320     uint32_t pde;
321
322     printf("MMU dump:\n");
323     pde_ptr = (env->mmuregs[1] << 4) + (env->mmuregs[2] << 2);
324     pde = ldl_phys(pde_ptr);
325     printf("Root ptr: " TARGET_FMT_lx ", ctx: %d\n", env->mmuregs[1] << 4, env->mmuregs[2]);
326     for (n = 0, va = 0; n < 256; n++, va += 16 * 1024 * 1024) {
327         pde_ptr = mmu_probe(env, va, 2);
328         if (pde_ptr) {
329             pa = cpu_get_phys_page_debug(env, va);
330             printf("VA: " TARGET_FMT_lx ", PA: " TARGET_FMT_lx " PDE: " TARGET_FMT_lx "\n", va, pa, pde_ptr);
331             for (m = 0, va1 = va; m < 64; m++, va1 += 256 * 1024) {
332                 pde_ptr = mmu_probe(env, va1, 1);
333                 if (pde_ptr) {
334                     pa = cpu_get_phys_page_debug(env, va1);
335                     printf(" VA: " TARGET_FMT_lx ", PA: " TARGET_FMT_lx " PDE: " TARGET_FMT_lx "\n", va1, pa, pde_ptr);
336                     for (o = 0, va2 = va1; o < 64; o++, va2 += 4 * 1024) {
337                         pde_ptr = mmu_probe(env, va2, 0);
338                         if (pde_ptr) {
339                             pa = cpu_get_phys_page_debug(env, va2);
340                             printf("  VA: " TARGET_FMT_lx ", PA: " TARGET_FMT_lx " PTE: " TARGET_FMT_lx "\n", va2, pa, pde_ptr);
341                         }
342                     }
343                 }
344             }
345         }
346     }
347     printf("MMU dump ends\n");
348 }
349 #endif /* DEBUG_MMU */
350
351 #else /* !TARGET_SPARC64 */
352 /*
353  * UltraSparc IIi I/DMMUs
354  */
355 static int get_physical_address_data(CPUState *env, target_phys_addr_t *physical, int *prot,
356                           int *access_index, target_ulong address, int rw,
357                           int is_user)
358 {
359     target_ulong mask;
360     unsigned int i;
361
362     if ((env->lsu & DMMU_E) == 0) { /* DMMU disabled */
363         *physical = address;
364         *prot = PAGE_READ | PAGE_WRITE;
365         return 0;
366     }
367
368     for (i = 0; i < 64; i++) {
369         switch ((env->dtlb_tte[i] >> 61) & 3) {
370         default:
371         case 0x0: // 8k
372             mask = 0xffffffffffffe000ULL;
373             break;
374         case 0x1: // 64k
375             mask = 0xffffffffffff0000ULL;
376             break;
377         case 0x2: // 512k
378             mask = 0xfffffffffff80000ULL;
379             break;
380         case 0x3: // 4M
381             mask = 0xffffffffffc00000ULL;
382             break;
383         }
384         // ctx match, vaddr match?
385         if (env->dmmuregs[1] == (env->dtlb_tag[i] & 0x1fff) &&
386             (address & mask) == (env->dtlb_tag[i] & ~0x1fffULL)) {
387             // valid, access ok?
388             if ((env->dtlb_tte[i] & 0x8000000000000000ULL) == 0 ||
389                 ((env->dtlb_tte[i] & 0x4) && is_user) ||
390                 (!(env->dtlb_tte[i] & 0x2) && (rw == 1))) {
391                 if (env->dmmuregs[3]) /* Fault status register */
392                     env->dmmuregs[3] = 2; /* overflow (not read before another fault) */
393                 env->dmmuregs[3] |= (is_user << 3) | ((rw == 1) << 2) | 1;
394                 env->dmmuregs[4] = address; /* Fault address register */
395                 env->exception_index = TT_DFAULT;
396 #ifdef DEBUG_MMU
397                 printf("DFAULT at 0x%llx\n", address);
398 #endif
399                 return 1;
400             }
401             *physical = (env->dtlb_tte[i] & mask & 0x1fffffff000ULL) + (address & ~mask & 0x1fffffff000ULL);
402             *prot = PAGE_READ;
403             if (env->dtlb_tte[i] & 0x2)
404                 *prot |= PAGE_WRITE;
405             return 0;
406         }
407     }
408 #ifdef DEBUG_MMU
409     printf("DMISS at 0x%llx\n", address);
410 #endif
411     env->exception_index = TT_DMISS;
412     return 1;
413 }
414
415 static int get_physical_address_code(CPUState *env, target_phys_addr_t *physical, int *prot,
416                           int *access_index, target_ulong address, int rw,
417                           int is_user)
418 {
419     target_ulong mask;
420     unsigned int i;
421
422     if ((env->lsu & IMMU_E) == 0) { /* IMMU disabled */
423         *physical = address;
424         *prot = PAGE_EXEC;
425         return 0;
426     }
427
428     for (i = 0; i < 64; i++) {
429         switch ((env->itlb_tte[i] >> 61) & 3) {
430         default:
431         case 0x0: // 8k
432             mask = 0xffffffffffffe000ULL;
433             break;
434         case 0x1: // 64k
435             mask = 0xffffffffffff0000ULL;
436             break;
437         case 0x2: // 512k
438             mask = 0xfffffffffff80000ULL;
439             break;
440         case 0x3: // 4M
441             mask = 0xffffffffffc00000ULL;
442                 break;
443         }
444         // ctx match, vaddr match?
445         if (env->dmmuregs[1] == (env->itlb_tag[i] & 0x1fff) &&
446             (address & mask) == (env->itlb_tag[i] & ~0x1fffULL)) {
447             // valid, access ok?
448             if ((env->itlb_tte[i] & 0x8000000000000000ULL) == 0 ||
449                 ((env->itlb_tte[i] & 0x4) && is_user)) {
450                 if (env->immuregs[3]) /* Fault status register */
451                     env->immuregs[3] = 2; /* overflow (not read before another fault) */
452                 env->immuregs[3] |= (is_user << 3) | 1;
453                 env->exception_index = TT_TFAULT;
454 #ifdef DEBUG_MMU
455                 printf("TFAULT at 0x%llx\n", address);
456 #endif
457                 return 1;
458             }
459             *physical = (env->itlb_tte[i] & mask & 0x1fffffff000ULL) + (address & ~mask & 0x1fffffff000ULL);
460             *prot = PAGE_EXEC;
461             return 0;
462         }
463     }
464 #ifdef DEBUG_MMU
465     printf("TMISS at 0x%llx\n", address);
466 #endif
467     env->exception_index = TT_TMISS;
468     return 1;
469 }
470
471 int get_physical_address(CPUState *env, target_phys_addr_t *physical, int *prot,
472                           int *access_index, target_ulong address, int rw,
473                           int is_user)
474 {
475     if (rw == 2)
476         return get_physical_address_code(env, physical, prot, access_index, address, rw, is_user);
477     else
478         return get_physical_address_data(env, physical, prot, access_index, address, rw, is_user);
479 }
480
481 /* Perform address translation */
482 int cpu_sparc_handle_mmu_fault (CPUState *env, target_ulong address, int rw,
483                               int is_user, int is_softmmu)
484 {
485     target_ulong virt_addr, vaddr;
486     target_phys_addr_t paddr;
487     int error_code = 0, prot, ret = 0, access_index;
488
489     error_code = get_physical_address(env, &paddr, &prot, &access_index, address, rw, is_user);
490     if (error_code == 0) {
491         virt_addr = address & TARGET_PAGE_MASK;
492         vaddr = virt_addr + ((address & TARGET_PAGE_MASK) & (TARGET_PAGE_SIZE - 1));
493 #ifdef DEBUG_MMU
494         printf("Translate at 0x%llx -> 0x%llx, vaddr 0x%llx\n", address, paddr, vaddr);
495 #endif
496         ret = tlb_set_page_exec(env, vaddr, paddr, prot, is_user, is_softmmu);
497         return ret;
498     }
499     // XXX
500     return 1;
501 }
502
503 #ifdef DEBUG_MMU
504 void dump_mmu(CPUState *env)
505 {
506     unsigned int i;
507     const char *mask;
508
509     printf("MMU contexts: Primary: %lld, Secondary: %lld\n", env->dmmuregs[1], env->dmmuregs[2]);
510     if ((env->lsu & DMMU_E) == 0) {
511         printf("DMMU disabled\n");
512     } else {
513         printf("DMMU dump:\n");
514         for (i = 0; i < 64; i++) {
515             switch ((env->dtlb_tte[i] >> 61) & 3) {
516             default:
517             case 0x0:
518                 mask = "  8k";
519                 break;
520             case 0x1:
521                 mask = " 64k";
522                 break;
523             case 0x2:
524                 mask = "512k";
525                 break;
526             case 0x3:
527                 mask = "  4M";
528                 break;
529             }
530             if ((env->dtlb_tte[i] & 0x8000000000000000ULL) != 0) {
531                 printf("VA: " TARGET_FMT_lx ", PA: " TARGET_FMT_lx ", %s, %s, %s, %s, ctx %lld\n",
532                        env->dtlb_tag[i] & ~0x1fffULL,
533                        env->dtlb_tte[i] & 0x1ffffffe000ULL,
534                        mask,
535                        env->dtlb_tte[i] & 0x4? "priv": "user",
536                        env->dtlb_tte[i] & 0x2? "RW": "RO",
537                        env->dtlb_tte[i] & 0x40? "locked": "unlocked",
538                        env->dtlb_tag[i] & 0x1fffULL);
539             }
540         }
541     }
542     if ((env->lsu & IMMU_E) == 0) {
543         printf("IMMU disabled\n");
544     } else {
545         printf("IMMU dump:\n");
546         for (i = 0; i < 64; i++) {
547             switch ((env->itlb_tte[i] >> 61) & 3) {
548             default:
549             case 0x0:
550                 mask = "  8k";
551                 break;
552             case 0x1:
553                 mask = " 64k";
554                 break;
555             case 0x2:
556                 mask = "512k";
557                 break;
558             case 0x3:
559                 mask = "  4M";
560                 break;
561             }
562             if ((env->itlb_tte[i] & 0x8000000000000000ULL) != 0) {
563                 printf("VA: " TARGET_FMT_lx ", PA: " TARGET_FMT_lx ", %s, %s, %s, ctx %lld\n",
564                        env->itlb_tag[i] & ~0x1fffULL,
565                        env->itlb_tte[i] & 0x1ffffffe000ULL,
566                        mask,
567                        env->itlb_tte[i] & 0x4? "priv": "user",
568                        env->itlb_tte[i] & 0x40? "locked": "unlocked",
569                        env->itlb_tag[i] & 0x1fffULL);
570             }
571         }
572     }
573 }
574 #endif /* DEBUG_MMU */
575
576 #endif /* TARGET_SPARC64 */
577 #endif /* !CONFIG_USER_ONLY */
578
579 void memcpy32(target_ulong *dst, const target_ulong *src)
580 {
581     dst[0] = src[0];
582     dst[1] = src[1];
583     dst[2] = src[2];
584     dst[3] = src[3];
585     dst[4] = src[4];
586     dst[5] = src[5];
587     dst[6] = src[6];
588     dst[7] = src[7];
589 }