|
1 /* |
|
2 * Software MMU support |
|
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 #define DATA_SIZE (1 << SHIFT) |
|
21 |
|
22 #if DATA_SIZE == 8 |
|
23 #define SUFFIX q |
|
24 #define USUFFIX q |
|
25 #define DATA_TYPE uint64_t |
|
26 #elif DATA_SIZE == 4 |
|
27 #define SUFFIX l |
|
28 #define USUFFIX l |
|
29 #define DATA_TYPE uint32_t |
|
30 #elif DATA_SIZE == 2 |
|
31 #define SUFFIX w |
|
32 #define USUFFIX uw |
|
33 #define DATA_TYPE uint16_t |
|
34 #elif DATA_SIZE == 1 |
|
35 #define SUFFIX b |
|
36 #define USUFFIX ub |
|
37 #define DATA_TYPE uint8_t |
|
38 #else |
|
39 #error unsupported data size |
|
40 #endif |
|
41 |
|
42 #ifdef SOFTMMU_CODE_ACCESS |
|
43 #define READ_ACCESS_TYPE 2 |
|
44 #define ADDR_READ addr_code |
|
45 #else |
|
46 #define READ_ACCESS_TYPE 0 |
|
47 #define ADDR_READ addr_read |
|
48 #endif |
|
49 |
|
50 static DATA_TYPE glue(glue(slow_ld, SUFFIX), MMUSUFFIX)(target_ulong addr, |
|
51 int mmu_idx, |
|
52 void *retaddr); |
|
53 static inline DATA_TYPE glue(io_read, SUFFIX)(target_phys_addr_t physaddr, |
|
54 target_ulong addr, |
|
55 void *retaddr) |
|
56 { |
|
57 DATA_TYPE res; |
|
58 int index; |
|
59 index = (physaddr >> IO_MEM_SHIFT) & (IO_MEM_NB_ENTRIES - 1); |
|
60 physaddr = (physaddr & TARGET_PAGE_MASK) + addr; |
|
61 env->mem_io_pc = (unsigned long)retaddr; |
|
62 if (index > (IO_MEM_NOTDIRTY >> IO_MEM_SHIFT) |
|
63 && !can_do_io(env)) { |
|
64 cpu_io_recompile(env, retaddr); |
|
65 } |
|
66 |
|
67 env->mem_io_vaddr = addr; |
|
68 #if SHIFT <= 2 |
|
69 res = io_mem_read[index][SHIFT](io_mem_opaque[index], physaddr); |
|
70 #else |
|
71 #ifdef TARGET_WORDS_BIGENDIAN |
|
72 res = (uint64_t)io_mem_read[index][2](io_mem_opaque[index], physaddr) << 32; |
|
73 res |= io_mem_read[index][2](io_mem_opaque[index], physaddr + 4); |
|
74 #else |
|
75 res = io_mem_read[index][2](io_mem_opaque[index], physaddr); |
|
76 res |= (uint64_t)io_mem_read[index][2](io_mem_opaque[index], physaddr + 4) << 32; |
|
77 #endif |
|
78 #endif /* SHIFT > 2 */ |
|
79 #ifdef USE_KQEMU |
|
80 env->last_io_time = cpu_get_time_fast(); |
|
81 #endif |
|
82 return res; |
|
83 } |
|
84 |
|
85 /* handle all cases except unaligned access which span two pages */ |
|
86 DATA_TYPE REGPARM glue(glue(__ld, SUFFIX), MMUSUFFIX)(target_ulong addr, |
|
87 int mmu_idx) |
|
88 { |
|
89 DATA_TYPE res; |
|
90 int index; |
|
91 target_ulong tlb_addr; |
|
92 target_phys_addr_t addend; |
|
93 void *retaddr; |
|
94 |
|
95 /* test if there is match for unaligned or IO access */ |
|
96 /* XXX: could done more in memory macro in a non portable way */ |
|
97 index = (addr >> TARGET_PAGE_BITS) & (CPU_TLB_SIZE - 1); |
|
98 redo: |
|
99 tlb_addr = env->tlb_table[mmu_idx][index].ADDR_READ; |
|
100 if ((addr & TARGET_PAGE_MASK) == (tlb_addr & (TARGET_PAGE_MASK | TLB_INVALID_MASK))) { |
|
101 if (tlb_addr & ~TARGET_PAGE_MASK) { |
|
102 /* IO access */ |
|
103 if ((addr & (DATA_SIZE - 1)) != 0) |
|
104 goto do_unaligned_access; |
|
105 retaddr = GETPC(); |
|
106 addend = env->iotlb[mmu_idx][index]; |
|
107 res = glue(io_read, SUFFIX)(addend, addr, retaddr); |
|
108 } else if (((addr & ~TARGET_PAGE_MASK) + DATA_SIZE - 1) >= TARGET_PAGE_SIZE) { |
|
109 /* slow unaligned access (it spans two pages or IO) */ |
|
110 do_unaligned_access: |
|
111 retaddr = GETPC(); |
|
112 #ifdef ALIGNED_ONLY |
|
113 do_unaligned_access(addr, READ_ACCESS_TYPE, mmu_idx, retaddr); |
|
114 #endif |
|
115 res = glue(glue(slow_ld, SUFFIX), MMUSUFFIX)(addr, |
|
116 mmu_idx, retaddr); |
|
117 } else { |
|
118 /* unaligned/aligned access in the same page */ |
|
119 #ifdef ALIGNED_ONLY |
|
120 if ((addr & (DATA_SIZE - 1)) != 0) { |
|
121 retaddr = GETPC(); |
|
122 do_unaligned_access(addr, READ_ACCESS_TYPE, mmu_idx, retaddr); |
|
123 } |
|
124 #endif |
|
125 addend = env->tlb_table[mmu_idx][index].addend; |
|
126 res = glue(glue(ld, USUFFIX), _raw)((uint8_t *)(long)(addr+addend)); |
|
127 } |
|
128 } else { |
|
129 /* the page is not in the TLB : fill it */ |
|
130 retaddr = GETPC(); |
|
131 #ifdef ALIGNED_ONLY |
|
132 if ((addr & (DATA_SIZE - 1)) != 0) |
|
133 do_unaligned_access(addr, READ_ACCESS_TYPE, mmu_idx, retaddr); |
|
134 #endif |
|
135 tlb_fill(addr, READ_ACCESS_TYPE, mmu_idx, retaddr); |
|
136 goto redo; |
|
137 } |
|
138 return res; |
|
139 } |
|
140 |
|
141 /* handle all unaligned cases */ |
|
142 static DATA_TYPE glue(glue(slow_ld, SUFFIX), MMUSUFFIX)(target_ulong addr, |
|
143 int mmu_idx, |
|
144 void *retaddr) |
|
145 { |
|
146 DATA_TYPE res, res1, res2; |
|
147 int index, shift; |
|
148 target_phys_addr_t addend; |
|
149 target_ulong tlb_addr, addr1, addr2; |
|
150 |
|
151 index = (addr >> TARGET_PAGE_BITS) & (CPU_TLB_SIZE - 1); |
|
152 redo: |
|
153 tlb_addr = env->tlb_table[mmu_idx][index].ADDR_READ; |
|
154 if ((addr & TARGET_PAGE_MASK) == (tlb_addr & (TARGET_PAGE_MASK | TLB_INVALID_MASK))) { |
|
155 if (tlb_addr & ~TARGET_PAGE_MASK) { |
|
156 /* IO access */ |
|
157 if ((addr & (DATA_SIZE - 1)) != 0) |
|
158 goto do_unaligned_access; |
|
159 retaddr = GETPC(); |
|
160 addend = env->iotlb[mmu_idx][index]; |
|
161 res = glue(io_read, SUFFIX)(addend, addr, retaddr); |
|
162 } else if (((addr & ~TARGET_PAGE_MASK) + DATA_SIZE - 1) >= TARGET_PAGE_SIZE) { |
|
163 do_unaligned_access: |
|
164 /* slow unaligned access (it spans two pages) */ |
|
165 addr1 = addr & ~(DATA_SIZE - 1); |
|
166 addr2 = addr1 + DATA_SIZE; |
|
167 res1 = glue(glue(slow_ld, SUFFIX), MMUSUFFIX)(addr1, |
|
168 mmu_idx, retaddr); |
|
169 res2 = glue(glue(slow_ld, SUFFIX), MMUSUFFIX)(addr2, |
|
170 mmu_idx, retaddr); |
|
171 shift = (addr & (DATA_SIZE - 1)) * 8; |
|
172 #ifdef TARGET_WORDS_BIGENDIAN |
|
173 res = (res1 << shift) | (res2 >> ((DATA_SIZE * 8) - shift)); |
|
174 #else |
|
175 res = (res1 >> shift) | (res2 << ((DATA_SIZE * 8) - shift)); |
|
176 #endif |
|
177 res = (DATA_TYPE)res; |
|
178 } else { |
|
179 /* unaligned/aligned access in the same page */ |
|
180 addend = env->tlb_table[mmu_idx][index].addend; |
|
181 res = glue(glue(ld, USUFFIX), _raw)((uint8_t *)(long)(addr+addend)); |
|
182 } |
|
183 } else { |
|
184 /* the page is not in the TLB : fill it */ |
|
185 tlb_fill(addr, READ_ACCESS_TYPE, mmu_idx, retaddr); |
|
186 goto redo; |
|
187 } |
|
188 return res; |
|
189 } |
|
190 |
|
191 #ifndef SOFTMMU_CODE_ACCESS |
|
192 |
|
193 static void glue(glue(slow_st, SUFFIX), MMUSUFFIX)(target_ulong addr, |
|
194 DATA_TYPE val, |
|
195 int mmu_idx, |
|
196 void *retaddr); |
|
197 |
|
198 static inline void glue(io_write, SUFFIX)(target_phys_addr_t physaddr, |
|
199 DATA_TYPE val, |
|
200 target_ulong addr, |
|
201 void *retaddr) |
|
202 { |
|
203 int index; |
|
204 index = (physaddr >> IO_MEM_SHIFT) & (IO_MEM_NB_ENTRIES - 1); |
|
205 physaddr = (physaddr & TARGET_PAGE_MASK) + addr; |
|
206 if (index > (IO_MEM_NOTDIRTY >> IO_MEM_SHIFT) |
|
207 && !can_do_io(env)) { |
|
208 cpu_io_recompile(env, retaddr); |
|
209 } |
|
210 |
|
211 env->mem_io_vaddr = addr; |
|
212 env->mem_io_pc = (unsigned long)retaddr; |
|
213 #if SHIFT <= 2 |
|
214 io_mem_write[index][SHIFT](io_mem_opaque[index], physaddr, val); |
|
215 #else |
|
216 #ifdef TARGET_WORDS_BIGENDIAN |
|
217 io_mem_write[index][2](io_mem_opaque[index], physaddr, val >> 32); |
|
218 io_mem_write[index][2](io_mem_opaque[index], physaddr + 4, val); |
|
219 #else |
|
220 io_mem_write[index][2](io_mem_opaque[index], physaddr, val); |
|
221 io_mem_write[index][2](io_mem_opaque[index], physaddr + 4, val >> 32); |
|
222 #endif |
|
223 #endif /* SHIFT > 2 */ |
|
224 #ifdef USE_KQEMU |
|
225 env->last_io_time = cpu_get_time_fast(); |
|
226 #endif |
|
227 } |
|
228 |
|
229 void REGPARM glue(glue(__st, SUFFIX), MMUSUFFIX)(target_ulong addr, |
|
230 DATA_TYPE val, |
|
231 int mmu_idx) |
|
232 { |
|
233 target_phys_addr_t addend; |
|
234 target_ulong tlb_addr; |
|
235 void *retaddr; |
|
236 int index; |
|
237 |
|
238 index = (addr >> TARGET_PAGE_BITS) & (CPU_TLB_SIZE - 1); |
|
239 redo: |
|
240 tlb_addr = env->tlb_table[mmu_idx][index].addr_write; |
|
241 if ((addr & TARGET_PAGE_MASK) == (tlb_addr & (TARGET_PAGE_MASK | TLB_INVALID_MASK))) { |
|
242 if (tlb_addr & ~TARGET_PAGE_MASK) { |
|
243 /* IO access */ |
|
244 if ((addr & (DATA_SIZE - 1)) != 0) |
|
245 goto do_unaligned_access; |
|
246 retaddr = GETPC(); |
|
247 addend = env->iotlb[mmu_idx][index]; |
|
248 glue(io_write, SUFFIX)(addend, val, addr, retaddr); |
|
249 } else if (((addr & ~TARGET_PAGE_MASK) + DATA_SIZE - 1) >= TARGET_PAGE_SIZE) { |
|
250 do_unaligned_access: |
|
251 retaddr = GETPC(); |
|
252 #ifdef ALIGNED_ONLY |
|
253 do_unaligned_access(addr, 1, mmu_idx, retaddr); |
|
254 #endif |
|
255 glue(glue(slow_st, SUFFIX), MMUSUFFIX)(addr, val, |
|
256 mmu_idx, retaddr); |
|
257 } else { |
|
258 /* aligned/unaligned access in the same page */ |
|
259 #ifdef ALIGNED_ONLY |
|
260 if ((addr & (DATA_SIZE - 1)) != 0) { |
|
261 retaddr = GETPC(); |
|
262 do_unaligned_access(addr, 1, mmu_idx, retaddr); |
|
263 } |
|
264 #endif |
|
265 addend = env->tlb_table[mmu_idx][index].addend; |
|
266 glue(glue(st, SUFFIX), _raw)((uint8_t *)(long)(addr+addend), val); |
|
267 } |
|
268 } else { |
|
269 /* the page is not in the TLB : fill it */ |
|
270 retaddr = GETPC(); |
|
271 #ifdef ALIGNED_ONLY |
|
272 if ((addr & (DATA_SIZE - 1)) != 0) |
|
273 do_unaligned_access(addr, 1, mmu_idx, retaddr); |
|
274 #endif |
|
275 tlb_fill(addr, 1, mmu_idx, retaddr); |
|
276 goto redo; |
|
277 } |
|
278 } |
|
279 |
|
280 /* handles all unaligned cases */ |
|
281 static void glue(glue(slow_st, SUFFIX), MMUSUFFIX)(target_ulong addr, |
|
282 DATA_TYPE val, |
|
283 int mmu_idx, |
|
284 void *retaddr) |
|
285 { |
|
286 target_phys_addr_t addend; |
|
287 target_ulong tlb_addr; |
|
288 int index, i; |
|
289 |
|
290 index = (addr >> TARGET_PAGE_BITS) & (CPU_TLB_SIZE - 1); |
|
291 redo: |
|
292 tlb_addr = env->tlb_table[mmu_idx][index].addr_write; |
|
293 if ((addr & TARGET_PAGE_MASK) == (tlb_addr & (TARGET_PAGE_MASK | TLB_INVALID_MASK))) { |
|
294 if (tlb_addr & ~TARGET_PAGE_MASK) { |
|
295 /* IO access */ |
|
296 if ((addr & (DATA_SIZE - 1)) != 0) |
|
297 goto do_unaligned_access; |
|
298 addend = env->iotlb[mmu_idx][index]; |
|
299 glue(io_write, SUFFIX)(addend, val, addr, retaddr); |
|
300 } else if (((addr & ~TARGET_PAGE_MASK) + DATA_SIZE - 1) >= TARGET_PAGE_SIZE) { |
|
301 do_unaligned_access: |
|
302 /* XXX: not efficient, but simple */ |
|
303 /* Note: relies on the fact that tlb_fill() does not remove the |
|
304 * previous page from the TLB cache. */ |
|
305 for(i = DATA_SIZE - 1; i >= 0; i--) { |
|
306 #ifdef TARGET_WORDS_BIGENDIAN |
|
307 glue(slow_stb, MMUSUFFIX)(addr + i, val >> (((DATA_SIZE - 1) * 8) - (i * 8)), |
|
308 mmu_idx, retaddr); |
|
309 #else |
|
310 glue(slow_stb, MMUSUFFIX)(addr + i, val >> (i * 8), |
|
311 mmu_idx, retaddr); |
|
312 #endif |
|
313 } |
|
314 } else { |
|
315 /* aligned/unaligned access in the same page */ |
|
316 addend = env->tlb_table[mmu_idx][index].addend; |
|
317 glue(glue(st, SUFFIX), _raw)((uint8_t *)(long)(addr+addend), val); |
|
318 } |
|
319 } else { |
|
320 /* the page is not in the TLB : fill it */ |
|
321 tlb_fill(addr, 1, mmu_idx, retaddr); |
|
322 goto redo; |
|
323 } |
|
324 } |
|
325 |
|
326 #endif /* !defined(SOFTMMU_CODE_ACCESS) */ |
|
327 |
|
328 #undef READ_ACCESS_TYPE |
|
329 #undef SHIFT |
|
330 #undef DATA_TYPE |
|
331 #undef SUFFIX |
|
332 #undef USUFFIX |
|
333 #undef DATA_SIZE |
|
334 #undef ADDR_READ |