removed access_type hack
[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_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     if (env->user_mode_only) {
142         /* user mode only emulation */
143         ret = -2;
144         goto do_fault;
145     }
146
147     virt_addr = address & TARGET_PAGE_MASK;
148     if ((env->mmuregs[0] & MMU_E) == 0) { /* MMU disabled */
149         paddr = address;
150         page_offset = address & (TARGET_PAGE_SIZE - 1);
151         prot = PAGE_READ | PAGE_WRITE;
152         goto do_mapping;
153     }
154
155     /* SPARC reference MMU table walk: Context table->L1->L2->PTE */
156     /* Context base + context number */
157     pde_ptr = phys_ram_base + (env->mmuregs[1] << 4) + (env->mmuregs[2] << 4);
158     pde = ldl_raw(pde_ptr);
159
160     /* Ctx pde */
161     switch (pde & PTE_ENTRYTYPE_MASK) {
162     case 0: /* Invalid */
163         error_code = 1;
164         goto do_fault;
165     case 2: /* PTE, maybe should not happen? */
166     case 3: /* Reserved */
167         error_code = 4;
168         goto do_fault;
169     case 1: /* L1 PDE */
170         pde_ptr = phys_ram_base + ((address >> 22) & ~3) + ((pde & ~3) << 4);
171         pde = ldl_raw(pde_ptr);
172
173         switch (pde & PTE_ENTRYTYPE_MASK) {
174         case 0: /* Invalid */
175             error_code = 1;
176             goto do_fault;
177         case 3: /* Reserved */
178             error_code = 4;
179             goto do_fault;
180         case 1: /* L2 PDE */
181             pde_ptr = phys_ram_base + ((address & 0xfc0000) >> 16) + ((pde & ~3) << 4);
182             pde = ldl_raw(pde_ptr);
183
184             switch (pde & PTE_ENTRYTYPE_MASK) {
185             case 0: /* Invalid */
186                 error_code = 1;
187                 goto do_fault;
188             case 3: /* Reserved */
189                 error_code = 4;
190                 goto do_fault;
191             case 1: /* L3 PDE */
192                 pde_ptr = phys_ram_base + ((address & 0x3f000) >> 10) + ((pde & ~3) << 4);
193                 pde = ldl_raw(pde_ptr);
194
195                 switch (pde & PTE_ENTRYTYPE_MASK) {
196                 case 0: /* Invalid */
197                     error_code = 1;
198                     goto do_fault;
199                 case 1: /* PDE, should not happen */
200                 case 3: /* Reserved */
201                     error_code = 4;
202                     goto do_fault;
203                 case 2: /* L3 PTE */
204                     virt_addr = address & TARGET_PAGE_MASK;
205                     page_offset = (address & TARGET_PAGE_MASK) & (TARGET_PAGE_SIZE - 1);
206                 }
207                 break;
208             case 2: /* L2 PTE */
209                 virt_addr = address & ~0x3ffff;
210                 page_offset = address & 0x3ffff;
211             }
212             break;
213         case 2: /* L1 PTE */
214             virt_addr = address & ~0xffffff;
215             page_offset = address & 0xffffff;
216         }
217     }
218
219     /* update page modified and dirty bits */
220     is_dirty = (rw & 1) && !(pde & PG_MODIFIED_MASK);
221     if (!(pde & PG_ACCESSED_MASK) || is_dirty) {
222         pde |= PG_ACCESSED_MASK;
223         if (is_dirty)
224             pde |= PG_MODIFIED_MASK;
225         stl_raw(pde_ptr, pde);
226     }
227
228     /* check access */
229     access_index = ((rw & 1) << 2) | (rw & 2) | (is_user? 0 : 1);
230     access_perms = (pde & PTE_ACCESS_MASK) >> PTE_ACCESS_SHIFT;
231     error_code = access_table[access_index][access_perms];
232     if (error_code)
233         goto do_fault;
234
235     /* the page can be put in the TLB */
236     prot = PAGE_READ;
237     if (pde & PG_MODIFIED_MASK) {
238         /* only set write access if already dirty... otherwise wait
239            for dirty access */
240         if (rw_table[is_user][access_perms])
241                 prot |= PAGE_WRITE;
242     }
243
244     /* Even if large ptes, we map only one 4KB page in the cache to
245        avoid filling it too fast */
246     virt_addr = address & TARGET_PAGE_MASK;
247     paddr = ((pde & PTE_ADDR_MASK) << 4) + page_offset;
248
249  do_mapping:
250     vaddr = virt_addr + ((address & TARGET_PAGE_MASK) & (TARGET_PAGE_SIZE - 1));
251
252     ret = tlb_set_page(env, vaddr, paddr, prot, is_user, is_softmmu);
253     return ret;
254
255  do_fault:
256     if (env->mmuregs[3]) /* Fault status register */
257         env->mmuregs[3] = 1; /* overflow (not read before another fault) */
258     env->mmuregs[3] |= (access_index << 5) | (error_code << 2) | 2;
259     env->mmuregs[4] = address; /* Fault address register */
260
261     if (env->mmuregs[0] & MMU_NF) // No fault
262         return 0;
263
264     env->exception_index = exception;
265     env->error_code = error_code;
266     return error_code;
267 }
268
269 void memcpy32(uint32_t *dst, const uint32_t *src)
270 {
271     dst[0] = src[0];
272     dst[1] = src[1];
273     dst[2] = src[2];
274     dst[3] = src[3];
275     dst[4] = src[4];
276     dst[5] = src[5];
277     dst[6] = src[6];
278     dst[7] = src[7];
279 }
280
281 void set_cwp(int new_cwp)
282 {
283     /* put the modified wrap registers at their proper location */
284     if (env->cwp == (NWINDOWS - 1))
285         memcpy32(env->regbase, env->regbase + NWINDOWS * 16);
286     env->cwp = new_cwp;
287     /* put the wrap registers at their temporary location */
288     if (new_cwp == (NWINDOWS - 1))
289         memcpy32(env->regbase + NWINDOWS * 16, env->regbase);
290     env->regwptr = env->regbase + (new_cwp * 16);
291 }
292
293 /*
294  * Begin execution of an interruption. is_int is TRUE if coming from
295  * the int instruction. next_eip is the EIP value AFTER the interrupt
296  * instruction. It is only relevant if is_int is TRUE.  
297  */
298 void do_interrupt(int intno, int is_int, int error_code, 
299                   unsigned int next_eip, int is_hw)
300 {
301     int cwp;
302
303 #ifdef DEBUG_PCALL
304     if (loglevel & CPU_LOG_INT) {
305         static int count;
306         fprintf(logfile, "%6d: v=%02x e=%04x i=%d pc=%08x npc=%08x SP=%08x\n",
307                     count, intno, error_code, is_int,
308                     env->pc,
309                     env->npc, env->gregs[7]);
310 #if 0
311         cpu_sparc_dump_state(env, logfile, 0);
312         {
313             int i;
314             uint8_t *ptr;
315             fprintf(logfile, "       code=");
316             ptr = env->pc;
317             for(i = 0; i < 16; i++) {
318                 fprintf(logfile, " %02x", ldub(ptr + i));
319             }
320             fprintf(logfile, "\n");
321         }
322 #endif
323         count++;
324     }
325 #endif
326     env->psret = 0;
327     cwp = (env->cwp - 1) & (NWINDOWS - 1); 
328     set_cwp(cwp);
329     env->regwptr[9] = env->pc;
330     env->regwptr[10] = env->npc;
331     env->psrps = env->psrs;
332     env->psrs = 1;
333     env->tbr = (env->tbr & TBR_BASE_MASK) | (intno << 4);
334     env->pc = env->tbr;
335     env->npc = env->pc + 4;
336     env->exception_index = 0;
337 }
338
339 void raise_exception_err(int exception_index, int error_code)
340 {
341     raise_exception(exception_index);
342 }