]>
Commit | Line | Data |
---|---|---|
e8af50a3 FB |
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 | } |