root / target-sparc / helper.c @ f68c781c
History | View | Annotate | Download (17 kB)
1 | e8af50a3 | bellard | /*
|
---|---|---|---|
2 | e8af50a3 | bellard | * sparc helpers
|
3 | e8af50a3 | bellard | *
|
4 | e8af50a3 | bellard | * Copyright (c) 2003 Fabrice Bellard
|
5 | e8af50a3 | bellard | *
|
6 | e8af50a3 | bellard | * This library is free software; you can redistribute it and/or
|
7 | e8af50a3 | bellard | * modify it under the terms of the GNU Lesser General Public
|
8 | e8af50a3 | bellard | * License as published by the Free Software Foundation; either
|
9 | e8af50a3 | bellard | * version 2 of the License, or (at your option) any later version.
|
10 | e8af50a3 | bellard | *
|
11 | e8af50a3 | bellard | * This library is distributed in the hope that it will be useful,
|
12 | e8af50a3 | bellard | * but WITHOUT ANY WARRANTY; without even the implied warranty of
|
13 | e8af50a3 | bellard | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
14 | e8af50a3 | bellard | * Lesser General Public License for more details.
|
15 | e8af50a3 | bellard | *
|
16 | e8af50a3 | bellard | * You should have received a copy of the GNU Lesser General Public
|
17 | e8af50a3 | bellard | * License along with this library; if not, write to the Free Software
|
18 | e8af50a3 | bellard | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
19 | e8af50a3 | bellard | */
|
20 | e8af50a3 | bellard | #include "exec.h" |
21 | e8af50a3 | bellard | |
22 | e80cfcfc | bellard | //#define DEBUG_PCALL
|
23 | e80cfcfc | bellard | //#define DEBUG_MMU
|
24 | e8af50a3 | bellard | |
25 | e8af50a3 | bellard | /* Sparc MMU emulation */
|
26 | e8af50a3 | bellard | |
27 | e8af50a3 | bellard | /* thread support */
|
28 | e8af50a3 | bellard | |
29 | e8af50a3 | bellard | spinlock_t global_cpu_lock = SPIN_LOCK_UNLOCKED; |
30 | e8af50a3 | bellard | |
31 | e8af50a3 | bellard | void cpu_lock(void) |
32 | e8af50a3 | bellard | { |
33 | e8af50a3 | bellard | spin_lock(&global_cpu_lock); |
34 | e8af50a3 | bellard | } |
35 | e8af50a3 | bellard | |
36 | e8af50a3 | bellard | void cpu_unlock(void) |
37 | e8af50a3 | bellard | { |
38 | e8af50a3 | bellard | spin_unlock(&global_cpu_lock); |
39 | e8af50a3 | bellard | } |
40 | e8af50a3 | bellard | |
41 | 9d893301 | bellard | #if defined(CONFIG_USER_ONLY)
|
42 | 9d893301 | bellard | |
43 | 9d893301 | bellard | int cpu_sparc_handle_mmu_fault(CPUState *env, target_ulong address, int rw, |
44 | 9d893301 | bellard | int is_user, int is_softmmu) |
45 | 9d893301 | bellard | { |
46 | 878d3096 | bellard | if (rw & 2) |
47 | 878d3096 | bellard | env->exception_index = TT_TFAULT; |
48 | 878d3096 | bellard | else
|
49 | 878d3096 | bellard | env->exception_index = TT_DFAULT; |
50 | 9d893301 | bellard | return 1; |
51 | 9d893301 | bellard | } |
52 | 9d893301 | bellard | |
53 | 9d893301 | bellard | #else
|
54 | e8af50a3 | bellard | |
55 | e8af50a3 | bellard | #define MMUSUFFIX _mmu
|
56 | e8af50a3 | bellard | #define GETPC() (__builtin_return_address(0)) |
57 | e8af50a3 | bellard | |
58 | e8af50a3 | bellard | #define SHIFT 0 |
59 | e8af50a3 | bellard | #include "softmmu_template.h" |
60 | e8af50a3 | bellard | |
61 | e8af50a3 | bellard | #define SHIFT 1 |
62 | e8af50a3 | bellard | #include "softmmu_template.h" |
63 | e8af50a3 | bellard | |
64 | e8af50a3 | bellard | #define SHIFT 2 |
65 | e8af50a3 | bellard | #include "softmmu_template.h" |
66 | e8af50a3 | bellard | |
67 | e8af50a3 | bellard | #define SHIFT 3 |
68 | e8af50a3 | bellard | #include "softmmu_template.h" |
69 | e8af50a3 | bellard | |
70 | e8af50a3 | bellard | |
71 | e8af50a3 | bellard | /* try to fill the TLB and return an exception if error. If retaddr is
|
72 | e8af50a3 | bellard | NULL, it means that the function was called in C code (i.e. not
|
73 | e8af50a3 | bellard | from generated code or from helper.c) */
|
74 | e8af50a3 | bellard | /* XXX: fix it to restore all registers */
|
75 | 0fa85d43 | bellard | void tlb_fill(target_ulong addr, int is_write, int is_user, void *retaddr) |
76 | e8af50a3 | bellard | { |
77 | e8af50a3 | bellard | TranslationBlock *tb; |
78 | e8af50a3 | bellard | int ret;
|
79 | e8af50a3 | bellard | unsigned long pc; |
80 | e8af50a3 | bellard | CPUState *saved_env; |
81 | e8af50a3 | bellard | |
82 | e8af50a3 | bellard | /* XXX: hack to restore env in all cases, even if not called from
|
83 | e8af50a3 | bellard | generated code */
|
84 | e8af50a3 | bellard | saved_env = env; |
85 | e8af50a3 | bellard | env = cpu_single_env; |
86 | e8af50a3 | bellard | |
87 | e8af50a3 | bellard | ret = cpu_sparc_handle_mmu_fault(env, addr, is_write, is_user, 1);
|
88 | e8af50a3 | bellard | if (ret) {
|
89 | e8af50a3 | bellard | if (retaddr) {
|
90 | e8af50a3 | bellard | /* now we have a real cpu fault */
|
91 | e8af50a3 | bellard | pc = (unsigned long)retaddr; |
92 | e8af50a3 | bellard | tb = tb_find_pc(pc); |
93 | e8af50a3 | bellard | if (tb) {
|
94 | e8af50a3 | bellard | /* the PC is inside the translated code. It means that we have
|
95 | e8af50a3 | bellard | a virtual CPU fault */
|
96 | c3278b7b | bellard | cpu_restore_state(tb, env, pc, (void *)T2);
|
97 | e8af50a3 | bellard | } |
98 | e8af50a3 | bellard | } |
99 | 878d3096 | bellard | cpu_loop_exit(); |
100 | e8af50a3 | bellard | } |
101 | e8af50a3 | bellard | env = saved_env; |
102 | e8af50a3 | bellard | } |
103 | e8af50a3 | bellard | |
104 | 3475187d | bellard | #ifndef TARGET_SPARC64
|
105 | e8af50a3 | bellard | static const int access_table[8][8] = { |
106 | e8af50a3 | bellard | { 0, 0, 0, 0, 2, 0, 3, 3 }, |
107 | e8af50a3 | bellard | { 0, 0, 0, 0, 2, 0, 0, 0 }, |
108 | e8af50a3 | bellard | { 2, 2, 0, 0, 0, 2, 3, 3 }, |
109 | e8af50a3 | bellard | { 2, 2, 0, 0, 0, 2, 0, 0 }, |
110 | e8af50a3 | bellard | { 2, 0, 2, 0, 2, 2, 3, 3 }, |
111 | e8af50a3 | bellard | { 2, 0, 2, 0, 2, 0, 2, 0 }, |
112 | e8af50a3 | bellard | { 2, 2, 2, 0, 2, 2, 3, 3 }, |
113 | e8af50a3 | bellard | { 2, 2, 2, 0, 2, 2, 2, 0 } |
114 | e8af50a3 | bellard | }; |
115 | e8af50a3 | bellard | |
116 | e8af50a3 | bellard | /* 1 = write OK */
|
117 | e8af50a3 | bellard | static const int rw_table[2][8] = { |
118 | e8af50a3 | bellard | { 0, 1, 0, 1, 0, 1, 0, 1 }, |
119 | e8af50a3 | bellard | { 0, 1, 0, 1, 0, 0, 0, 0 } |
120 | e8af50a3 | bellard | }; |
121 | e8af50a3 | bellard | |
122 | af7bf89b | bellard | int get_physical_address (CPUState *env, target_phys_addr_t *physical, int *prot, |
123 | af7bf89b | bellard | int *access_index, target_ulong address, int rw, |
124 | e80cfcfc | bellard | int is_user)
|
125 | e8af50a3 | bellard | { |
126 | e80cfcfc | bellard | int access_perms = 0; |
127 | e80cfcfc | bellard | target_phys_addr_t pde_ptr; |
128 | af7bf89b | bellard | uint32_t pde; |
129 | af7bf89b | bellard | target_ulong virt_addr; |
130 | e80cfcfc | bellard | int error_code = 0, is_dirty; |
131 | e80cfcfc | bellard | unsigned long page_offset; |
132 | e8af50a3 | bellard | |
133 | e8af50a3 | bellard | virt_addr = address & TARGET_PAGE_MASK; |
134 | e8af50a3 | bellard | if ((env->mmuregs[0] & MMU_E) == 0) { /* MMU disabled */ |
135 | e80cfcfc | bellard | *physical = address; |
136 | e80cfcfc | bellard | *prot = PAGE_READ | PAGE_WRITE; |
137 | e80cfcfc | bellard | return 0; |
138 | e8af50a3 | bellard | } |
139 | e8af50a3 | bellard | |
140 | 7483750d | bellard | *access_index = ((rw & 1) << 2) | (rw & 2) | (is_user? 0 : 1); |
141 | 6f7e9aec | bellard | *physical = 0xfffff000;
|
142 | 7483750d | bellard | |
143 | e8af50a3 | bellard | /* SPARC reference MMU table walk: Context table->L1->L2->PTE */
|
144 | e8af50a3 | bellard | /* Context base + context number */
|
145 | b3180cdc | bellard | pde_ptr = (env->mmuregs[1] << 4) + (env->mmuregs[2] << 2); |
146 | 49be8030 | bellard | pde = ldl_phys(pde_ptr); |
147 | e8af50a3 | bellard | |
148 | e8af50a3 | bellard | /* Ctx pde */
|
149 | e8af50a3 | bellard | switch (pde & PTE_ENTRYTYPE_MASK) {
|
150 | e80cfcfc | bellard | default:
|
151 | e8af50a3 | bellard | case 0: /* Invalid */ |
152 | 7483750d | bellard | return 1 << 2; |
153 | e80cfcfc | bellard | case 2: /* L0 PTE, maybe should not happen? */ |
154 | e8af50a3 | bellard | case 3: /* Reserved */ |
155 | 7483750d | bellard | return 4 << 2; |
156 | e80cfcfc | bellard | case 1: /* L0 PDE */ |
157 | e80cfcfc | bellard | pde_ptr = ((address >> 22) & ~3) + ((pde & ~3) << 4); |
158 | 49be8030 | bellard | pde = ldl_phys(pde_ptr); |
159 | e8af50a3 | bellard | |
160 | e8af50a3 | bellard | switch (pde & PTE_ENTRYTYPE_MASK) {
|
161 | e80cfcfc | bellard | default:
|
162 | e8af50a3 | bellard | case 0: /* Invalid */ |
163 | 7483750d | bellard | return (1 << 8) | (1 << 2); |
164 | e8af50a3 | bellard | case 3: /* Reserved */ |
165 | 7483750d | bellard | return (1 << 8) | (4 << 2); |
166 | e80cfcfc | bellard | case 1: /* L1 PDE */ |
167 | e80cfcfc | bellard | pde_ptr = ((address & 0xfc0000) >> 16) + ((pde & ~3) << 4); |
168 | 49be8030 | bellard | pde = ldl_phys(pde_ptr); |
169 | e8af50a3 | bellard | |
170 | e8af50a3 | bellard | switch (pde & PTE_ENTRYTYPE_MASK) {
|
171 | e80cfcfc | bellard | default:
|
172 | e8af50a3 | bellard | case 0: /* Invalid */ |
173 | 7483750d | bellard | return (2 << 8) | (1 << 2); |
174 | e8af50a3 | bellard | case 3: /* Reserved */ |
175 | 7483750d | bellard | return (2 << 8) | (4 << 2); |
176 | e80cfcfc | bellard | case 1: /* L2 PDE */ |
177 | e80cfcfc | bellard | pde_ptr = ((address & 0x3f000) >> 10) + ((pde & ~3) << 4); |
178 | 49be8030 | bellard | pde = ldl_phys(pde_ptr); |
179 | e8af50a3 | bellard | |
180 | e8af50a3 | bellard | switch (pde & PTE_ENTRYTYPE_MASK) {
|
181 | e80cfcfc | bellard | default:
|
182 | e8af50a3 | bellard | case 0: /* Invalid */ |
183 | 7483750d | bellard | return (3 << 8) | (1 << 2); |
184 | e8af50a3 | bellard | case 1: /* PDE, should not happen */ |
185 | e8af50a3 | bellard | case 3: /* Reserved */ |
186 | 7483750d | bellard | return (3 << 8) | (4 << 2); |
187 | e8af50a3 | bellard | case 2: /* L3 PTE */ |
188 | e8af50a3 | bellard | virt_addr = address & TARGET_PAGE_MASK; |
189 | e8af50a3 | bellard | page_offset = (address & TARGET_PAGE_MASK) & (TARGET_PAGE_SIZE - 1);
|
190 | e8af50a3 | bellard | } |
191 | e8af50a3 | bellard | break;
|
192 | e8af50a3 | bellard | case 2: /* L2 PTE */ |
193 | e8af50a3 | bellard | virt_addr = address & ~0x3ffff;
|
194 | e8af50a3 | bellard | page_offset = address & 0x3ffff;
|
195 | e8af50a3 | bellard | } |
196 | e8af50a3 | bellard | break;
|
197 | e8af50a3 | bellard | case 2: /* L1 PTE */ |
198 | e8af50a3 | bellard | virt_addr = address & ~0xffffff;
|
199 | e8af50a3 | bellard | page_offset = address & 0xffffff;
|
200 | e8af50a3 | bellard | } |
201 | e8af50a3 | bellard | } |
202 | e8af50a3 | bellard | |
203 | e8af50a3 | bellard | /* update page modified and dirty bits */
|
204 | b769d8fe | bellard | is_dirty = (rw & 1) && !(pde & PG_MODIFIED_MASK);
|
205 | e8af50a3 | bellard | if (!(pde & PG_ACCESSED_MASK) || is_dirty) {
|
206 | e8af50a3 | bellard | pde |= PG_ACCESSED_MASK; |
207 | e8af50a3 | bellard | if (is_dirty)
|
208 | e8af50a3 | bellard | pde |= PG_MODIFIED_MASK; |
209 | 49be8030 | bellard | stl_phys_notdirty(pde_ptr, pde); |
210 | e8af50a3 | bellard | } |
211 | e8af50a3 | bellard | /* check access */
|
212 | e8af50a3 | bellard | access_perms = (pde & PTE_ACCESS_MASK) >> PTE_ACCESS_SHIFT; |
213 | e80cfcfc | bellard | error_code = access_table[*access_index][access_perms]; |
214 | 6f7e9aec | bellard | if (error_code && !(env->mmuregs[0] & MMU_NF)) |
215 | e80cfcfc | bellard | return error_code;
|
216 | e8af50a3 | bellard | |
217 | e8af50a3 | bellard | /* the page can be put in the TLB */
|
218 | e80cfcfc | bellard | *prot = PAGE_READ; |
219 | e8af50a3 | bellard | if (pde & PG_MODIFIED_MASK) {
|
220 | e8af50a3 | bellard | /* only set write access if already dirty... otherwise wait
|
221 | e8af50a3 | bellard | for dirty access */
|
222 | e8af50a3 | bellard | if (rw_table[is_user][access_perms])
|
223 | e80cfcfc | bellard | *prot |= PAGE_WRITE; |
224 | e8af50a3 | bellard | } |
225 | e8af50a3 | bellard | |
226 | e8af50a3 | bellard | /* Even if large ptes, we map only one 4KB page in the cache to
|
227 | e8af50a3 | bellard | avoid filling it too fast */
|
228 | e80cfcfc | bellard | *physical = ((pde & PTE_ADDR_MASK) << 4) + page_offset;
|
229 | 6f7e9aec | bellard | return error_code;
|
230 | e80cfcfc | bellard | } |
231 | e80cfcfc | bellard | |
232 | e80cfcfc | bellard | /* Perform address translation */
|
233 | af7bf89b | bellard | int cpu_sparc_handle_mmu_fault (CPUState *env, target_ulong address, int rw, |
234 | e80cfcfc | bellard | int is_user, int is_softmmu) |
235 | e80cfcfc | bellard | { |
236 | af7bf89b | bellard | target_ulong virt_addr; |
237 | af7bf89b | bellard | target_phys_addr_t paddr; |
238 | e80cfcfc | bellard | unsigned long vaddr; |
239 | e80cfcfc | bellard | int error_code = 0, prot, ret = 0, access_index; |
240 | e8af50a3 | bellard | |
241 | e80cfcfc | bellard | error_code = get_physical_address(env, &paddr, &prot, &access_index, address, rw, is_user); |
242 | e80cfcfc | bellard | if (error_code == 0) { |
243 | e80cfcfc | bellard | virt_addr = address & TARGET_PAGE_MASK; |
244 | e80cfcfc | bellard | vaddr = virt_addr + ((address & TARGET_PAGE_MASK) & (TARGET_PAGE_SIZE - 1));
|
245 | e80cfcfc | bellard | ret = tlb_set_page(env, vaddr, paddr, prot, is_user, is_softmmu); |
246 | e80cfcfc | bellard | return ret;
|
247 | e80cfcfc | bellard | } |
248 | e8af50a3 | bellard | |
249 | e8af50a3 | bellard | if (env->mmuregs[3]) /* Fault status register */ |
250 | e8af50a3 | bellard | env->mmuregs[3] = 1; /* overflow (not read before another fault) */ |
251 | 7483750d | bellard | env->mmuregs[3] |= (access_index << 5) | error_code | 2; |
252 | e8af50a3 | bellard | env->mmuregs[4] = address; /* Fault address register */ |
253 | e8af50a3 | bellard | |
254 | 878d3096 | bellard | if ((env->mmuregs[0] & MMU_NF) || env->psret == 0) { |
255 | 6f7e9aec | bellard | // No fault mode: if a mapping is available, just override
|
256 | 6f7e9aec | bellard | // permissions. If no mapping is available, redirect accesses to
|
257 | 6f7e9aec | bellard | // neverland. Fake/overridden mappings will be flushed when
|
258 | 6f7e9aec | bellard | // switching to normal mode.
|
259 | 7483750d | bellard | vaddr = address & TARGET_PAGE_MASK; |
260 | 7483750d | bellard | prot = PAGE_READ | PAGE_WRITE; |
261 | 7483750d | bellard | ret = tlb_set_page(env, vaddr, paddr, prot, is_user, is_softmmu); |
262 | 7483750d | bellard | return ret;
|
263 | 7483750d | bellard | } else {
|
264 | 7483750d | bellard | if (rw & 2) |
265 | 7483750d | bellard | env->exception_index = TT_TFAULT; |
266 | 7483750d | bellard | else
|
267 | 7483750d | bellard | env->exception_index = TT_DFAULT; |
268 | 7483750d | bellard | return 1; |
269 | 878d3096 | bellard | } |
270 | e8af50a3 | bellard | } |
271 | 3475187d | bellard | #else
|
272 | 3475187d | bellard | static int get_physical_address_data(CPUState *env, target_phys_addr_t *physical, int *prot, |
273 | 3475187d | bellard | int *access_index, target_ulong address, int rw, |
274 | 3475187d | bellard | int is_user)
|
275 | 3475187d | bellard | { |
276 | 3475187d | bellard | target_ulong mask; |
277 | 3475187d | bellard | unsigned int i; |
278 | 3475187d | bellard | |
279 | 3475187d | bellard | if ((env->lsu & DMMU_E) == 0) { /* DMMU disabled */ |
280 | 3475187d | bellard | *physical = address & 0xffffffff;
|
281 | 3475187d | bellard | *prot = PAGE_READ | PAGE_WRITE; |
282 | 3475187d | bellard | return 0; |
283 | 3475187d | bellard | } |
284 | 3475187d | bellard | |
285 | 3475187d | bellard | for (i = 0; i < 64; i++) { |
286 | 3475187d | bellard | if ((env->dtlb_tte[i] & 0x8000000000000000ULL) != 0) { |
287 | 3475187d | bellard | switch (env->dtlb_tte[i] >> 60) { |
288 | 3475187d | bellard | default:
|
289 | 3475187d | bellard | case 0x4: // 8k |
290 | 3475187d | bellard | mask = 0xffffffffffffe000ULL;
|
291 | 3475187d | bellard | break;
|
292 | 3475187d | bellard | case 0x5: // 64k |
293 | 3475187d | bellard | mask = 0xffffffffffff0000ULL;
|
294 | 3475187d | bellard | break;
|
295 | 3475187d | bellard | case 0x6: // 512k |
296 | 3475187d | bellard | mask = 0xfffffffffff80000ULL;
|
297 | 3475187d | bellard | break;
|
298 | 3475187d | bellard | case 0x7: // 4M |
299 | 3475187d | bellard | mask = 0xffffffffffc00000ULL;
|
300 | 3475187d | bellard | break;
|
301 | 3475187d | bellard | } |
302 | 3475187d | bellard | // ctx match, vaddr match?
|
303 | 3475187d | bellard | if (env->dmmuregs[1] == (env->dtlb_tag[i] & 0x1fff) && |
304 | 3475187d | bellard | (address & mask) == (env->dtlb_tag[i] & ~0x1fffULL)) {
|
305 | 3475187d | bellard | // access ok?
|
306 | 3475187d | bellard | if (((env->dtlb_tte[i] & 0x4) && !(env->pstate & PS_PRIV)) || |
307 | 3475187d | bellard | (!(env->dtlb_tte[i] & 0x2) && (rw == 1))) { |
308 | 3475187d | bellard | env->exception_index = TT_DFAULT; |
309 | 3475187d | bellard | return 1; |
310 | 3475187d | bellard | } |
311 | 3475187d | bellard | *physical = env->dtlb_tte[i] & 0xffffe000;
|
312 | 3475187d | bellard | *prot = PAGE_READ; |
313 | 3475187d | bellard | if (env->dtlb_tte[i] & 0x2) |
314 | 3475187d | bellard | *prot |= PAGE_WRITE; |
315 | 3475187d | bellard | return 0; |
316 | 3475187d | bellard | } |
317 | 3475187d | bellard | } |
318 | 3475187d | bellard | } |
319 | 3475187d | bellard | env->exception_index = TT_DFAULT; |
320 | 3475187d | bellard | return 1; |
321 | 3475187d | bellard | } |
322 | 3475187d | bellard | |
323 | 3475187d | bellard | static int get_physical_address_code(CPUState *env, target_phys_addr_t *physical, int *prot, |
324 | 3475187d | bellard | int *access_index, target_ulong address, int rw, |
325 | 3475187d | bellard | int is_user)
|
326 | 3475187d | bellard | { |
327 | 3475187d | bellard | target_ulong mask; |
328 | 3475187d | bellard | unsigned int i; |
329 | 3475187d | bellard | |
330 | 3475187d | bellard | if ((env->lsu & IMMU_E) == 0) { /* IMMU disabled */ |
331 | 3475187d | bellard | *physical = address & 0xffffffff;
|
332 | 3475187d | bellard | *prot = PAGE_READ; |
333 | 3475187d | bellard | return 0; |
334 | 3475187d | bellard | } |
335 | 3475187d | bellard | for (i = 0; i < 64; i++) { |
336 | 3475187d | bellard | if ((env->itlb_tte[i] & 0x8000000000000000ULL) != 0) { |
337 | 3475187d | bellard | switch (env->itlb_tte[i] >> 60) { |
338 | 3475187d | bellard | default:
|
339 | 3475187d | bellard | case 0x4: // 8k |
340 | 3475187d | bellard | mask = 0xffffffffffffe000ULL;
|
341 | 3475187d | bellard | break;
|
342 | 3475187d | bellard | case 0x5: // 64k |
343 | 3475187d | bellard | mask = 0xffffffffffff0000ULL;
|
344 | 3475187d | bellard | break;
|
345 | 3475187d | bellard | case 0x6: // 512k |
346 | 3475187d | bellard | mask = 0xfffffffffff80000ULL;
|
347 | 3475187d | bellard | break;
|
348 | 3475187d | bellard | case 0x7: // 4M |
349 | 3475187d | bellard | mask = 0xffffffffffc00000ULL;
|
350 | 3475187d | bellard | break;
|
351 | 3475187d | bellard | } |
352 | 3475187d | bellard | // ctx match, vaddr match?
|
353 | 3475187d | bellard | if (env->immuregs[1] == (env->itlb_tag[i] & 0x1fff) && |
354 | 3475187d | bellard | (address & mask) == (env->itlb_tag[i] & ~0x1fffULL)) {
|
355 | 3475187d | bellard | // access ok?
|
356 | 3475187d | bellard | if ((env->itlb_tte[i] & 0x4) && !(env->pstate & PS_PRIV)) { |
357 | 3475187d | bellard | env->exception_index = TT_TFAULT; |
358 | 3475187d | bellard | return 1; |
359 | 3475187d | bellard | } |
360 | 3475187d | bellard | *physical = env->itlb_tte[i] & 0xffffe000;
|
361 | 3475187d | bellard | *prot = PAGE_READ; |
362 | 3475187d | bellard | return 0; |
363 | 3475187d | bellard | } |
364 | 3475187d | bellard | } |
365 | 3475187d | bellard | } |
366 | 3475187d | bellard | env->exception_index = TT_TFAULT; |
367 | 3475187d | bellard | return 1; |
368 | 3475187d | bellard | } |
369 | 3475187d | bellard | |
370 | 3475187d | bellard | int get_physical_address(CPUState *env, target_phys_addr_t *physical, int *prot, |
371 | 3475187d | bellard | int *access_index, target_ulong address, int rw, |
372 | 3475187d | bellard | int is_user)
|
373 | 3475187d | bellard | { |
374 | 3475187d | bellard | if (rw == 2) |
375 | 3475187d | bellard | return get_physical_address_code(env, physical, prot, access_index, address, rw, is_user);
|
376 | 3475187d | bellard | else
|
377 | 3475187d | bellard | return get_physical_address_data(env, physical, prot, access_index, address, rw, is_user);
|
378 | 3475187d | bellard | } |
379 | 3475187d | bellard | |
380 | 3475187d | bellard | /* Perform address translation */
|
381 | 3475187d | bellard | int cpu_sparc_handle_mmu_fault (CPUState *env, target_ulong address, int rw, |
382 | 3475187d | bellard | int is_user, int is_softmmu) |
383 | 3475187d | bellard | { |
384 | 3475187d | bellard | target_ulong virt_addr; |
385 | 3475187d | bellard | target_phys_addr_t paddr; |
386 | 3475187d | bellard | unsigned long vaddr; |
387 | 3475187d | bellard | int error_code = 0, prot, ret = 0, access_index; |
388 | 3475187d | bellard | |
389 | 3475187d | bellard | error_code = get_physical_address(env, &paddr, &prot, &access_index, address, rw, is_user); |
390 | 3475187d | bellard | if (error_code == 0) { |
391 | 3475187d | bellard | virt_addr = address & TARGET_PAGE_MASK; |
392 | 3475187d | bellard | vaddr = virt_addr + ((address & TARGET_PAGE_MASK) & (TARGET_PAGE_SIZE - 1));
|
393 | 3475187d | bellard | ret = tlb_set_page(env, vaddr, paddr, prot, is_user, is_softmmu); |
394 | 3475187d | bellard | return ret;
|
395 | 3475187d | bellard | } |
396 | 3475187d | bellard | // XXX
|
397 | 3475187d | bellard | return 1; |
398 | 3475187d | bellard | } |
399 | 3475187d | bellard | |
400 | 3475187d | bellard | #endif
|
401 | 9d893301 | bellard | #endif
|
402 | e8af50a3 | bellard | |
403 | af7bf89b | bellard | void memcpy32(target_ulong *dst, const target_ulong *src) |
404 | e8af50a3 | bellard | { |
405 | e8af50a3 | bellard | dst[0] = src[0]; |
406 | e8af50a3 | bellard | dst[1] = src[1]; |
407 | e8af50a3 | bellard | dst[2] = src[2]; |
408 | e8af50a3 | bellard | dst[3] = src[3]; |
409 | e8af50a3 | bellard | dst[4] = src[4]; |
410 | e8af50a3 | bellard | dst[5] = src[5]; |
411 | e8af50a3 | bellard | dst[6] = src[6]; |
412 | e8af50a3 | bellard | dst[7] = src[7]; |
413 | e8af50a3 | bellard | } |
414 | e8af50a3 | bellard | |
415 | e8af50a3 | bellard | void set_cwp(int new_cwp) |
416 | e8af50a3 | bellard | { |
417 | e8af50a3 | bellard | /* put the modified wrap registers at their proper location */
|
418 | e8af50a3 | bellard | if (env->cwp == (NWINDOWS - 1)) |
419 | e8af50a3 | bellard | memcpy32(env->regbase, env->regbase + NWINDOWS * 16);
|
420 | e8af50a3 | bellard | env->cwp = new_cwp; |
421 | e8af50a3 | bellard | /* put the wrap registers at their temporary location */
|
422 | e8af50a3 | bellard | if (new_cwp == (NWINDOWS - 1)) |
423 | e8af50a3 | bellard | memcpy32(env->regbase + NWINDOWS * 16, env->regbase);
|
424 | e8af50a3 | bellard | env->regwptr = env->regbase + (new_cwp * 16);
|
425 | 3475187d | bellard | REGWPTR = env->regwptr; |
426 | e8af50a3 | bellard | } |
427 | e8af50a3 | bellard | |
428 | 0fa85d43 | bellard | void cpu_set_cwp(CPUState *env1, int new_cwp) |
429 | 0fa85d43 | bellard | { |
430 | 0fa85d43 | bellard | CPUState *saved_env; |
431 | 3475187d | bellard | #ifdef reg_REGWPTR
|
432 | 3475187d | bellard | target_ulong *saved_regwptr; |
433 | 3475187d | bellard | #endif
|
434 | 3475187d | bellard | |
435 | 0fa85d43 | bellard | saved_env = env; |
436 | 3475187d | bellard | #ifdef reg_REGWPTR
|
437 | 3475187d | bellard | saved_regwptr = REGWPTR; |
438 | 3475187d | bellard | #endif
|
439 | 0fa85d43 | bellard | env = env1; |
440 | 0fa85d43 | bellard | set_cwp(new_cwp); |
441 | 0fa85d43 | bellard | env = saved_env; |
442 | 3475187d | bellard | #ifdef reg_REGWPTR
|
443 | 3475187d | bellard | REGWPTR = saved_regwptr; |
444 | 3475187d | bellard | #endif
|
445 | 0fa85d43 | bellard | } |
446 | 0fa85d43 | bellard | |
447 | 3475187d | bellard | #ifdef TARGET_SPARC64
|
448 | 3475187d | bellard | void do_interrupt(int intno) |
449 | 3475187d | bellard | { |
450 | 3475187d | bellard | #ifdef DEBUG_PCALL
|
451 | 3475187d | bellard | if (loglevel & CPU_LOG_INT) {
|
452 | 3475187d | bellard | static int count; |
453 | 3475187d | bellard | fprintf(logfile, "%6d: v=%02x pc=%08x npc=%08x SP=%08x\n",
|
454 | 3475187d | bellard | count, intno, |
455 | 3475187d | bellard | env->pc, |
456 | 3475187d | bellard | env->npc, env->regwptr[6]);
|
457 | 3475187d | bellard | cpu_dump_state(env, logfile, fprintf, 0);
|
458 | 3475187d | bellard | #if 0
|
459 | 3475187d | bellard | {
|
460 | 3475187d | bellard | int i;
|
461 | 3475187d | bellard | uint8_t *ptr;
|
462 | 3475187d | bellard | |
463 | 3475187d | bellard | fprintf(logfile, " code=");
|
464 | 3475187d | bellard | ptr = (uint8_t *)env->pc;
|
465 | 3475187d | bellard | for(i = 0; i < 16; i++) {
|
466 | 3475187d | bellard | fprintf(logfile, " %02x", ldub(ptr + i));
|
467 | 3475187d | bellard | }
|
468 | 3475187d | bellard | fprintf(logfile, "\n");
|
469 | 3475187d | bellard | }
|
470 | 3475187d | bellard | #endif
|
471 | 3475187d | bellard | count++; |
472 | 3475187d | bellard | } |
473 | 3475187d | bellard | #endif
|
474 | 3475187d | bellard | #if !defined(CONFIG_USER_ONLY)
|
475 | 3475187d | bellard | if (env->pstate & PS_IE) {
|
476 | 3475187d | bellard | cpu_abort(cpu_single_env, "Trap 0x%02x while interrupts disabled, Error state", env->exception_index);
|
477 | 3475187d | bellard | return;
|
478 | 3475187d | bellard | } |
479 | 3475187d | bellard | #endif
|
480 | 3475187d | bellard | env->tstate[env->tl] = ((uint64_t)GET_CCR(env) << 32) | ((env->asi & 0xff) << 24) | |
481 | 3475187d | bellard | ((env->pstate & 0xfff) << 8) | (env->cwp & 0xff); |
482 | 3475187d | bellard | env->tpc[env->tl] = env->pc; |
483 | 3475187d | bellard | env->tnpc[env->tl] = env->npc; |
484 | 3475187d | bellard | env->tt[env->tl] = intno; |
485 | 3475187d | bellard | env->tbr = env->tbr | (env->tl > 1) ? 1 << 14 : 0 | (intno << 4); |
486 | 3475187d | bellard | env->tl++; |
487 | 3475187d | bellard | env->pc = env->tbr; |
488 | 3475187d | bellard | env->npc = env->pc + 4;
|
489 | 3475187d | bellard | env->exception_index = 0;
|
490 | 3475187d | bellard | } |
491 | 3475187d | bellard | #else
|
492 | 878d3096 | bellard | void do_interrupt(int intno) |
493 | e8af50a3 | bellard | { |
494 | e8af50a3 | bellard | int cwp;
|
495 | e8af50a3 | bellard | |
496 | e8af50a3 | bellard | #ifdef DEBUG_PCALL
|
497 | e8af50a3 | bellard | if (loglevel & CPU_LOG_INT) {
|
498 | e8af50a3 | bellard | static int count; |
499 | 878d3096 | bellard | fprintf(logfile, "%6d: v=%02x pc=%08x npc=%08x SP=%08x\n",
|
500 | 878d3096 | bellard | count, intno, |
501 | 9d893301 | bellard | env->pc, |
502 | 9d893301 | bellard | env->npc, env->regwptr[6]);
|
503 | 7fe48483 | bellard | cpu_dump_state(env, logfile, fprintf, 0);
|
504 | 6f7e9aec | bellard | #if 0
|
505 | e8af50a3 | bellard | {
|
506 | e8af50a3 | bellard | int i;
|
507 | e8af50a3 | bellard | uint8_t *ptr;
|
508 | e80cfcfc | bellard | |
509 | e8af50a3 | bellard | fprintf(logfile, " code=");
|
510 | e80cfcfc | bellard | ptr = (uint8_t *)env->pc;
|
511 | e8af50a3 | bellard | for(i = 0; i < 16; i++) {
|
512 | e8af50a3 | bellard | fprintf(logfile, " %02x", ldub(ptr + i));
|
513 | e8af50a3 | bellard | }
|
514 | e8af50a3 | bellard | fprintf(logfile, "\n");
|
515 | e8af50a3 | bellard | }
|
516 | e8af50a3 | bellard | #endif
|
517 | e8af50a3 | bellard | count++; |
518 | e8af50a3 | bellard | } |
519 | e8af50a3 | bellard | #endif
|
520 | e80cfcfc | bellard | #if !defined(CONFIG_USER_ONLY)
|
521 | e80cfcfc | bellard | if (env->psret == 0) { |
522 | 878d3096 | bellard | cpu_abort(cpu_single_env, "Trap 0x%02x while interrupts disabled, Error state", env->exception_index);
|
523 | e80cfcfc | bellard | return;
|
524 | e80cfcfc | bellard | } |
525 | e80cfcfc | bellard | #endif
|
526 | e8af50a3 | bellard | env->psret = 0;
|
527 | e8af50a3 | bellard | cwp = (env->cwp - 1) & (NWINDOWS - 1); |
528 | e8af50a3 | bellard | set_cwp(cwp); |
529 | 878d3096 | bellard | env->regwptr[9] = env->pc;
|
530 | 878d3096 | bellard | env->regwptr[10] = env->npc;
|
531 | e8af50a3 | bellard | env->psrps = env->psrs; |
532 | e8af50a3 | bellard | env->psrs = 1;
|
533 | e8af50a3 | bellard | env->tbr = (env->tbr & TBR_BASE_MASK) | (intno << 4);
|
534 | e8af50a3 | bellard | env->pc = env->tbr; |
535 | e8af50a3 | bellard | env->npc = env->pc + 4;
|
536 | e8af50a3 | bellard | env->exception_index = 0;
|
537 | e8af50a3 | bellard | } |
538 | e8af50a3 | bellard | |
539 | af7bf89b | bellard | target_ulong mmu_probe(target_ulong address, int mmulev)
|
540 | e80cfcfc | bellard | { |
541 | e80cfcfc | bellard | target_phys_addr_t pde_ptr; |
542 | e80cfcfc | bellard | uint32_t pde; |
543 | e80cfcfc | bellard | |
544 | e80cfcfc | bellard | /* Context base + context number */
|
545 | b3180cdc | bellard | pde_ptr = (env->mmuregs[1] << 4) + (env->mmuregs[2] << 2); |
546 | 49be8030 | bellard | pde = ldl_phys(pde_ptr); |
547 | 49be8030 | bellard | |
548 | e80cfcfc | bellard | switch (pde & PTE_ENTRYTYPE_MASK) {
|
549 | e80cfcfc | bellard | default:
|
550 | e80cfcfc | bellard | case 0: /* Invalid */ |
551 | e80cfcfc | bellard | case 2: /* PTE, maybe should not happen? */ |
552 | e80cfcfc | bellard | case 3: /* Reserved */ |
553 | e80cfcfc | bellard | return 0; |
554 | e80cfcfc | bellard | case 1: /* L1 PDE */ |
555 | e80cfcfc | bellard | if (mmulev == 3) |
556 | e80cfcfc | bellard | return pde;
|
557 | e80cfcfc | bellard | pde_ptr = ((address >> 22) & ~3) + ((pde & ~3) << 4); |
558 | 49be8030 | bellard | pde = ldl_phys(pde_ptr); |
559 | e80cfcfc | bellard | |
560 | e80cfcfc | bellard | switch (pde & PTE_ENTRYTYPE_MASK) {
|
561 | e80cfcfc | bellard | default:
|
562 | e80cfcfc | bellard | case 0: /* Invalid */ |
563 | e80cfcfc | bellard | case 3: /* Reserved */ |
564 | e80cfcfc | bellard | return 0; |
565 | e80cfcfc | bellard | case 2: /* L1 PTE */ |
566 | e80cfcfc | bellard | return pde;
|
567 | e80cfcfc | bellard | case 1: /* L2 PDE */ |
568 | e80cfcfc | bellard | if (mmulev == 2) |
569 | e80cfcfc | bellard | return pde;
|
570 | e80cfcfc | bellard | pde_ptr = ((address & 0xfc0000) >> 16) + ((pde & ~3) << 4); |
571 | 49be8030 | bellard | pde = ldl_phys(pde_ptr); |
572 | e80cfcfc | bellard | |
573 | e80cfcfc | bellard | switch (pde & PTE_ENTRYTYPE_MASK) {
|
574 | e80cfcfc | bellard | default:
|
575 | e80cfcfc | bellard | case 0: /* Invalid */ |
576 | e80cfcfc | bellard | case 3: /* Reserved */ |
577 | e80cfcfc | bellard | return 0; |
578 | e80cfcfc | bellard | case 2: /* L2 PTE */ |
579 | e80cfcfc | bellard | return pde;
|
580 | e80cfcfc | bellard | case 1: /* L3 PDE */ |
581 | e80cfcfc | bellard | if (mmulev == 1) |
582 | e80cfcfc | bellard | return pde;
|
583 | e80cfcfc | bellard | pde_ptr = ((address & 0x3f000) >> 10) + ((pde & ~3) << 4); |
584 | 49be8030 | bellard | pde = ldl_phys(pde_ptr); |
585 | e80cfcfc | bellard | |
586 | e80cfcfc | bellard | switch (pde & PTE_ENTRYTYPE_MASK) {
|
587 | e80cfcfc | bellard | default:
|
588 | e80cfcfc | bellard | case 0: /* Invalid */ |
589 | e80cfcfc | bellard | case 1: /* PDE, should not happen */ |
590 | e80cfcfc | bellard | case 3: /* Reserved */ |
591 | e80cfcfc | bellard | return 0; |
592 | e80cfcfc | bellard | case 2: /* L3 PTE */ |
593 | e80cfcfc | bellard | return pde;
|
594 | e80cfcfc | bellard | } |
595 | e80cfcfc | bellard | } |
596 | e80cfcfc | bellard | } |
597 | e80cfcfc | bellard | } |
598 | e80cfcfc | bellard | return 0; |
599 | e80cfcfc | bellard | } |
600 | e80cfcfc | bellard | |
601 | 55754d9e | bellard | #ifdef DEBUG_MMU
|
602 | e80cfcfc | bellard | void dump_mmu(void) |
603 | e80cfcfc | bellard | { |
604 | af7bf89b | bellard | target_ulong va, va1, va2; |
605 | af7bf89b | bellard | unsigned int n, m, o; |
606 | af7bf89b | bellard | target_phys_addr_t pde_ptr, pa; |
607 | e80cfcfc | bellard | uint32_t pde; |
608 | e80cfcfc | bellard | |
609 | e80cfcfc | bellard | printf("MMU dump:\n");
|
610 | b3180cdc | bellard | pde_ptr = (env->mmuregs[1] << 4) + (env->mmuregs[2] << 2); |
611 | 49be8030 | bellard | pde = ldl_phys(pde_ptr); |
612 | af7bf89b | bellard | printf("Root ptr: " TARGET_FMT_lx ", ctx: %d\n", env->mmuregs[1] << 4, env->mmuregs[2]); |
613 | e80cfcfc | bellard | for (n = 0, va = 0; n < 256; n++, va += 16 * 1024 * 1024) { |
614 | e80cfcfc | bellard | pde_ptr = mmu_probe(va, 2);
|
615 | e80cfcfc | bellard | if (pde_ptr) {
|
616 | e80cfcfc | bellard | pa = cpu_get_phys_page_debug(env, va); |
617 | af7bf89b | bellard | printf("VA: " TARGET_FMT_lx ", PA: " TARGET_FMT_lx " PDE: " TARGET_FMT_lx "\n", va, pa, pde_ptr); |
618 | e80cfcfc | bellard | for (m = 0, va1 = va; m < 64; m++, va1 += 256 * 1024) { |
619 | e80cfcfc | bellard | pde_ptr = mmu_probe(va1, 1);
|
620 | e80cfcfc | bellard | if (pde_ptr) {
|
621 | e80cfcfc | bellard | pa = cpu_get_phys_page_debug(env, va1); |
622 | af7bf89b | bellard | printf(" VA: " TARGET_FMT_lx ", PA: " TARGET_FMT_lx " PDE: " TARGET_FMT_lx "\n", va1, pa, pde_ptr); |
623 | e80cfcfc | bellard | for (o = 0, va2 = va1; o < 64; o++, va2 += 4 * 1024) { |
624 | e80cfcfc | bellard | pde_ptr = mmu_probe(va2, 0);
|
625 | e80cfcfc | bellard | if (pde_ptr) {
|
626 | e80cfcfc | bellard | pa = cpu_get_phys_page_debug(env, va2); |
627 | af7bf89b | bellard | printf(" VA: " TARGET_FMT_lx ", PA: " TARGET_FMT_lx " PTE: " TARGET_FMT_lx "\n", va2, pa, pde_ptr); |
628 | e80cfcfc | bellard | } |
629 | e80cfcfc | bellard | } |
630 | e80cfcfc | bellard | } |
631 | e80cfcfc | bellard | } |
632 | e80cfcfc | bellard | } |
633 | e80cfcfc | bellard | } |
634 | e80cfcfc | bellard | printf("MMU dump ends\n");
|
635 | e80cfcfc | bellard | } |
636 | 55754d9e | bellard | #endif
|
637 | 3475187d | bellard | #endif |