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