full system SPARC emulation (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
24 #if 0
25 #define raise_exception_err(a, b)\
26 do {\
27     fprintf(logfile, "raise_exception line=%d\n", __LINE__);\
28     (raise_exception_err)(a, b);\
29 } while (0)
30 #endif
31
32 /* Sparc MMU emulation */
33 int cpu_sparc_handle_mmu_fault (CPUState *env, uint32_t address, int rw,
34                               int is_user, int is_softmmu);
35
36
37 /* thread support */
38
39 spinlock_t global_cpu_lock = SPIN_LOCK_UNLOCKED;
40
41 void cpu_lock(void)
42 {
43     spin_lock(&global_cpu_lock);
44 }
45
46 void cpu_unlock(void)
47 {
48     spin_unlock(&global_cpu_lock);
49 }
50
51 #if 0
52 void cpu_loop_exit(void)
53 {
54     /* NOTE: the register at this point must be saved by hand because
55        longjmp restore them */
56     longjmp(env->jmp_env, 1);
57 }
58 #endif
59
60 #if !defined(CONFIG_USER_ONLY) 
61
62 #define MMUSUFFIX _mmu
63 #define GETPC() (__builtin_return_address(0))
64
65 #define SHIFT 0
66 #include "softmmu_template.h"
67
68 #define SHIFT 1
69 #include "softmmu_template.h"
70
71 #define SHIFT 2
72 #include "softmmu_template.h"
73
74 #define SHIFT 3
75 #include "softmmu_template.h"
76
77
78 /* try to fill the TLB and return an exception if error. If retaddr is
79    NULL, it means that the function was called in C code (i.e. not
80    from generated code or from helper.c) */
81 /* XXX: fix it to restore all registers */
82 void tlb_fill(unsigned long addr, int is_write, int is_user, void *retaddr)
83 {
84     TranslationBlock *tb;
85     int ret;
86     unsigned long pc;
87     CPUState *saved_env;
88
89     /* XXX: hack to restore env in all cases, even if not called from
90        generated code */
91     saved_env = env;
92     env = cpu_single_env;
93
94     ret = cpu_sparc_handle_mmu_fault(env, addr, is_write, is_user, 1);
95     if (ret) {
96         if (retaddr) {
97             /* now we have a real cpu fault */
98             pc = (unsigned long)retaddr;
99             tb = tb_find_pc(pc);
100             if (tb) {
101                 /* the PC is inside the translated code. It means that we have
102                    a virtual CPU fault */
103                 cpu_restore_state(tb, env, pc, NULL);
104             }
105         }
106         raise_exception_err(ret, env->error_code);
107     }
108     env = saved_env;
109 }
110 #endif
111
112 static const int access_table[8][8] = {
113     { 0, 0, 0, 0, 2, 0, 3, 3 },
114     { 0, 0, 0, 0, 2, 0, 0, 0 },
115     { 2, 2, 0, 0, 0, 2, 3, 3 },
116     { 2, 2, 0, 0, 0, 2, 0, 0 },
117     { 2, 0, 2, 0, 2, 2, 3, 3 },
118     { 2, 0, 2, 0, 2, 0, 2, 0 },
119     { 2, 2, 2, 0, 2, 2, 3, 3 },
120     { 2, 2, 2, 0, 2, 2, 2, 0 }
121 };
122
123 /* 1 = write OK */
124 static const int rw_table[2][8] = {
125     { 0, 1, 0, 1, 0, 1, 0, 1 },
126     { 0, 1, 0, 1, 0, 0, 0, 0 }
127 };
128
129
130 /* Perform address translation */
131 int cpu_sparc_handle_mmu_fault (CPUState *env, uint32_t address, int rw,
132                               int is_user, int is_softmmu)
133 {
134     int exception = 0;
135     int access_type, access_perms = 0, access_index = 0;
136     uint8_t *pde_ptr;
137     uint32_t pde, virt_addr;
138     int error_code = 0, is_dirty, prot, ret = 0;
139     unsigned long paddr, vaddr, page_offset;
140
141     access_type = env->access_type;
142     if (env->user_mode_only) {
143         /* user mode only emulation */
144         ret = -2;
145         goto do_fault;
146     }
147
148     virt_addr = address & TARGET_PAGE_MASK;
149     if ((env->mmuregs[0] & MMU_E) == 0) { /* MMU disabled */
150         paddr = address;
151         page_offset = address & (TARGET_PAGE_SIZE - 1);
152         prot = PAGE_READ | PAGE_WRITE;
153         goto do_mapping;
154     }
155
156     /* SPARC reference MMU table walk: Context table->L1->L2->PTE */
157     /* Context base + context number */
158     pde_ptr = phys_ram_base + (env->mmuregs[1] << 4) + (env->mmuregs[2] << 4);
159     env->access_type = ACCESS_MMU;
160     pde = ldl_raw(pde_ptr);
161
162     /* Ctx pde */
163     switch (pde & PTE_ENTRYTYPE_MASK) {
164     case 0: /* Invalid */
165         error_code = 1;
166         goto do_fault;
167     case 2: /* PTE, maybe should not happen? */
168     case 3: /* Reserved */
169         error_code = 4;
170         goto do_fault;
171     case 1: /* L1 PDE */
172         pde_ptr = phys_ram_base + ((address >> 22) & ~3) + ((pde & ~3) << 4);
173         pde = ldl_raw(pde_ptr);
174
175         switch (pde & PTE_ENTRYTYPE_MASK) {
176         case 0: /* Invalid */
177             error_code = 1;
178             goto do_fault;
179         case 3: /* Reserved */
180             error_code = 4;
181             goto do_fault;
182         case 1: /* L2 PDE */
183             pde_ptr = phys_ram_base + ((address & 0xfc0000) >> 16) + ((pde & ~3) << 4);
184             pde = ldl_raw(pde_ptr);
185
186             switch (pde & PTE_ENTRYTYPE_MASK) {
187             case 0: /* Invalid */
188                 error_code = 1;
189                 goto do_fault;
190             case 3: /* Reserved */
191                 error_code = 4;
192                 goto do_fault;
193             case 1: /* L3 PDE */
194                 pde_ptr = phys_ram_base + ((address & 0x3f000) >> 10) + ((pde & ~3) << 4);
195                 pde = ldl_raw(pde_ptr);
196
197                 switch (pde & PTE_ENTRYTYPE_MASK) {
198                 case 0: /* Invalid */
199                     error_code = 1;
200                     goto do_fault;
201                 case 1: /* PDE, should not happen */
202                 case 3: /* Reserved */
203                     error_code = 4;
204                     goto do_fault;
205                 case 2: /* L3 PTE */
206                     virt_addr = address & TARGET_PAGE_MASK;
207                     page_offset = (address & TARGET_PAGE_MASK) & (TARGET_PAGE_SIZE - 1);
208                 }
209                 break;
210             case 2: /* L2 PTE */
211                 virt_addr = address & ~0x3ffff;
212                 page_offset = address & 0x3ffff;
213             }
214             break;
215         case 2: /* L1 PTE */
216             virt_addr = address & ~0xffffff;
217             page_offset = address & 0xffffff;
218         }
219     }
220
221     /* update page modified and dirty bits */
222     is_dirty = rw && !(pde & PG_MODIFIED_MASK);
223     if (!(pde & PG_ACCESSED_MASK) || is_dirty) {
224         pde |= PG_ACCESSED_MASK;
225         if (is_dirty)
226             pde |= PG_MODIFIED_MASK;
227         stl_raw(pde_ptr, pde);
228     }
229
230     /* check access */
231     access_index = (rw << 2) | ((access_type == ACCESS_CODE)? 2 : 0) | (is_user? 0 : 1);
232     access_perms = (pde & PTE_ACCESS_MASK) >> PTE_ACCESS_SHIFT;
233     error_code = access_table[access_index][access_perms];
234     if (error_code)
235         goto do_fault;
236
237     /* the page can be put in the TLB */
238     prot = PAGE_READ;
239     if (pde & PG_MODIFIED_MASK) {
240         /* only set write access if already dirty... otherwise wait
241            for dirty access */
242         if (rw_table[is_user][access_perms])
243                 prot |= PAGE_WRITE;
244     }
245
246     /* Even if large ptes, we map only one 4KB page in the cache to
247        avoid filling it too fast */
248     virt_addr = address & TARGET_PAGE_MASK;
249     paddr = ((pde & PTE_ADDR_MASK) << 4) + page_offset;
250
251  do_mapping:
252     env->access_type = access_type;
253     vaddr = virt_addr + ((address & TARGET_PAGE_MASK) & (TARGET_PAGE_SIZE - 1));
254
255     ret = tlb_set_page(env, vaddr, paddr, prot, is_user, is_softmmu);
256     return ret;
257
258  do_fault:
259     env->access_type = access_type;
260     if (env->mmuregs[3]) /* Fault status register */
261         env->mmuregs[3] = 1; /* overflow (not read before another fault) */
262     env->mmuregs[3] |= (access_index << 5) | (error_code << 2) | 2;
263     env->mmuregs[4] = address; /* Fault address register */
264
265     if (env->mmuregs[0] & MMU_NF) // No fault
266         return 0;
267
268     env->exception_index = exception;
269     env->error_code = error_code;
270     return error_code;
271 }
272
273 void memcpy32(uint32_t *dst, const uint32_t *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 /*
298  * Begin execution of an interruption. is_int is TRUE if coming from
299  * the int instruction. next_eip is the EIP value AFTER the interrupt
300  * instruction. It is only relevant if is_int is TRUE.  
301  */
302 void do_interrupt(int intno, int is_int, int error_code, 
303                   unsigned int next_eip, int is_hw)
304 {
305     int cwp;
306
307 #ifdef DEBUG_PCALL
308     if (loglevel & CPU_LOG_INT) {
309         static int count;
310         fprintf(logfile, "%6d: v=%02x e=%04x i=%d pc=%08x npc=%08x SP=%08x\n",
311                     count, intno, error_code, is_int,
312                     env->pc,
313                     env->npc, env->gregs[7]);
314 #if 0
315         cpu_sparc_dump_state(env, logfile, 0);
316         {
317             int i;
318             uint8_t *ptr;
319             fprintf(logfile, "       code=");
320             ptr = env->pc;
321             for(i = 0; i < 16; i++) {
322                 fprintf(logfile, " %02x", ldub(ptr + i));
323             }
324             fprintf(logfile, "\n");
325         }
326 #endif
327         count++;
328     }
329 #endif
330     env->psret = 0;
331     cwp = (env->cwp - 1) & (NWINDOWS - 1); 
332     set_cwp(cwp);
333     env->regwptr[9] = env->pc;
334     env->regwptr[10] = env->npc;
335     env->psrps = env->psrs;
336     env->psrs = 1;
337     env->tbr = (env->tbr & TBR_BASE_MASK) | (intno << 4);
338     env->pc = env->tbr;
339     env->npc = env->pc + 4;
340     env->exception_index = 0;
341 }
342
343 void raise_exception_err(int exception_index, int error_code)
344 {
345     raise_exception(exception_index);
346 }