• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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, see <http://www.gnu.org/licenses/>.
18  */
19 #include "qemu-timer.h"
20 
21 #define DATA_SIZE (1 << SHIFT)
22 
23 #if DATA_SIZE == 8
24 #define SUFFIX q
25 #define USUFFIX q
26 #define DATA_TYPE uint64_t
27 #elif DATA_SIZE == 4
28 #define SUFFIX l
29 #define USUFFIX l
30 #define DATA_TYPE uint32_t
31 #elif DATA_SIZE == 2
32 #define SUFFIX w
33 #define USUFFIX uw
34 #define DATA_TYPE uint16_t
35 #elif DATA_SIZE == 1
36 #define SUFFIX b
37 #define USUFFIX ub
38 #define DATA_TYPE uint8_t
39 #else
40 #error unsupported data size
41 #endif
42 
43 #ifdef SOFTMMU_CODE_ACCESS
44 #define READ_ACCESS_TYPE 2
45 #define ADDR_READ addr_code
46 #else
47 #define READ_ACCESS_TYPE 0
48 #define ADDR_READ addr_read
49 #endif
50 
51 #if defined(CONFIG_MEMCHECK) && !defined(OUTSIDE_JIT) && !defined(SOFTMMU_CODE_ACCESS)
52 /*
53  * Support for memory access checker.
54  * We need to instrument __ldx/__stx_mmu routines implemented in this file with
55  * callbacks to access validation routines implemented by the memory checker.
56  * Note that (at least for now) we don't do that instrumentation for memory
57  * addressing the code (SOFTMMU_CODE_ACCESS controls that). Also, we don't want
58  * to instrument code that is used by emulator itself (OUTSIDE_JIT controls
59  * that).
60  */
61 #define CONFIG_MEMCHECK_MMU
62 #include "memcheck/memcheck_api.h"
63 #endif  // CONFIG_MEMCHECK && !OUTSIDE_JIT && !SOFTMMU_CODE_ACCESS
64 
65 static DATA_TYPE glue(glue(slow_ld, SUFFIX), MMUSUFFIX)(target_ulong addr,
66                                                         int mmu_idx,
67                                                         void *retaddr);
glue(io_read,SUFFIX)68 static inline DATA_TYPE glue(io_read, SUFFIX)(target_phys_addr_t physaddr,
69                                               target_ulong addr,
70                                               void *retaddr)
71 {
72     DATA_TYPE res;
73     int index;
74     index = (physaddr >> IO_MEM_SHIFT) & (IO_MEM_NB_ENTRIES - 1);
75     physaddr = (physaddr & TARGET_PAGE_MASK) + addr;
76     env->mem_io_pc = (unsigned long)retaddr;
77     if (index > (IO_MEM_NOTDIRTY >> IO_MEM_SHIFT)
78             && !can_do_io(env)) {
79         cpu_io_recompile(env, retaddr);
80     }
81 
82     env->mem_io_vaddr = addr;
83 #if SHIFT <= 2
84     res = io_mem_read[index][SHIFT](io_mem_opaque[index], physaddr);
85 #else
86 #ifdef TARGET_WORDS_BIGENDIAN
87     res = (uint64_t)io_mem_read[index][2](io_mem_opaque[index], physaddr) << 32;
88     res |= io_mem_read[index][2](io_mem_opaque[index], physaddr + 4);
89 #else
90     res = io_mem_read[index][2](io_mem_opaque[index], physaddr);
91     res |= (uint64_t)io_mem_read[index][2](io_mem_opaque[index], physaddr + 4) << 32;
92 #endif
93 #endif /* SHIFT > 2 */
94     return res;
95 }
96 
97 /* handle all cases except unaligned access which span two pages */
glue(glue (__ld,SUFFIX),MMUSUFFIX)98 DATA_TYPE REGPARM glue(glue(__ld, SUFFIX), MMUSUFFIX)(target_ulong addr,
99                                                       int mmu_idx)
100 {
101     DATA_TYPE res;
102     int index;
103     target_ulong tlb_addr;
104     target_phys_addr_t ioaddr;
105     unsigned long addend;
106     void *retaddr;
107 #ifdef CONFIG_MEMCHECK_MMU
108     int invalidate_cache = 0;
109 #endif  // CONFIG_MEMCHECK_MMU
110 
111     /* test if there is match for unaligned or IO access */
112     /* XXX: could done more in memory macro in a non portable way */
113     index = (addr >> TARGET_PAGE_BITS) & (CPU_TLB_SIZE - 1);
114  redo:
115     tlb_addr = env->tlb_table[mmu_idx][index].ADDR_READ;
116     if ((addr & TARGET_PAGE_MASK) == (tlb_addr & (TARGET_PAGE_MASK | TLB_INVALID_MASK))) {
117         if (tlb_addr & ~TARGET_PAGE_MASK) {
118             /* IO access */
119             if ((addr & (DATA_SIZE - 1)) != 0)
120                 goto do_unaligned_access;
121             retaddr = GETPC();
122             ioaddr = env->iotlb[mmu_idx][index];
123             res = glue(io_read, SUFFIX)(ioaddr, addr, retaddr);
124         } else if (((addr & ~TARGET_PAGE_MASK) + DATA_SIZE - 1) >= TARGET_PAGE_SIZE) {
125             /* This is not I/O access: do access verification. */
126 #ifdef CONFIG_MEMCHECK_MMU
127             /* We only validate access to the guest's user space, for which
128              * mmu_idx is set to 1. */
129             if (memcheck_instrument_mmu && mmu_idx == 1 &&
130                 memcheck_validate_ld(addr, DATA_SIZE, (target_ulong)(ptrdiff_t)GETPC())) {
131                 /* Memory read breaks page boundary. So, if required, we
132                  * must invalidate two caches in TLB. */
133                 invalidate_cache = 2;
134             }
135 #endif  // CONFIG_MEMCHECK_MMU
136             /* slow unaligned access (it spans two pages or IO) */
137         do_unaligned_access:
138             retaddr = GETPC();
139 #ifdef ALIGNED_ONLY
140             do_unaligned_access(addr, READ_ACCESS_TYPE, mmu_idx, retaddr);
141 #endif
142             res = glue(glue(slow_ld, SUFFIX), MMUSUFFIX)(addr,
143                                                          mmu_idx, retaddr);
144         } else {
145 #ifdef CONFIG_MEMCHECK_MMU
146             /* We only validate access to the guest's user space, for which
147              * mmu_idx is set to 1. */
148             if (memcheck_instrument_mmu && mmu_idx == 1) {
149                 invalidate_cache = memcheck_validate_ld(addr, DATA_SIZE,
150                                                         (target_ulong)(ptrdiff_t)GETPC());
151             }
152 #endif  // CONFIG_MEMCHECK_MMU
153             /* unaligned/aligned access in the same page */
154 #ifdef ALIGNED_ONLY
155             if ((addr & (DATA_SIZE - 1)) != 0) {
156                 retaddr = GETPC();
157                 do_unaligned_access(addr, READ_ACCESS_TYPE, mmu_idx, retaddr);
158             }
159 #endif
160             addend = env->tlb_table[mmu_idx][index].addend;
161             res = glue(glue(ld, USUFFIX), _raw)((uint8_t *)(long)(addr+addend));
162         }
163 #ifdef CONFIG_MEMCHECK_MMU
164         if (invalidate_cache) {
165             /* Accessed memory is under memchecker control. We must invalidate
166              * containing page(s) in order to make sure that next access to them
167              * will invoke _ld/_st_mmu. */
168             env->tlb_table[mmu_idx][index].addr_read ^= TARGET_PAGE_MASK;
169             env->tlb_table[mmu_idx][index].addr_write ^= TARGET_PAGE_MASK;
170             if ((invalidate_cache == 2) && (index < CPU_TLB_SIZE)) {
171                 // Read crossed page boundaris. Invalidate second cache too.
172                 env->tlb_table[mmu_idx][index + 1].addr_read ^= TARGET_PAGE_MASK;
173                 env->tlb_table[mmu_idx][index + 1].addr_write ^= TARGET_PAGE_MASK;
174             }
175         }
176 #endif  // CONFIG_MEMCHECK_MMU
177     } else {
178         /* the page is not in the TLB : fill it */
179         retaddr = GETPC();
180 #ifdef ALIGNED_ONLY
181         if ((addr & (DATA_SIZE - 1)) != 0)
182             do_unaligned_access(addr, READ_ACCESS_TYPE, mmu_idx, retaddr);
183 #endif
184         tlb_fill(addr, READ_ACCESS_TYPE, mmu_idx, retaddr);
185         goto redo;
186     }
187     return res;
188 }
189 
190 /* handle all unaligned cases */
glue(glue (slow_ld,SUFFIX),MMUSUFFIX)191 static DATA_TYPE glue(glue(slow_ld, SUFFIX), MMUSUFFIX)(target_ulong addr,
192                                                         int mmu_idx,
193                                                         void *retaddr)
194 {
195     DATA_TYPE res, res1, res2;
196     int index, shift;
197     target_phys_addr_t ioaddr;
198     unsigned long addend;
199     target_ulong tlb_addr, addr1, addr2;
200 
201     index = (addr >> TARGET_PAGE_BITS) & (CPU_TLB_SIZE - 1);
202  redo:
203     tlb_addr = env->tlb_table[mmu_idx][index].ADDR_READ;
204     if ((addr & TARGET_PAGE_MASK) == (tlb_addr & (TARGET_PAGE_MASK | TLB_INVALID_MASK))) {
205         if (tlb_addr & ~TARGET_PAGE_MASK) {
206             /* IO access */
207             if ((addr & (DATA_SIZE - 1)) != 0)
208                 goto do_unaligned_access;
209             ioaddr = env->iotlb[mmu_idx][index];
210             res = glue(io_read, SUFFIX)(ioaddr, addr, retaddr);
211         } else if (((addr & ~TARGET_PAGE_MASK) + DATA_SIZE - 1) >= TARGET_PAGE_SIZE) {
212         do_unaligned_access:
213             /* slow unaligned access (it spans two pages) */
214             addr1 = addr & ~(DATA_SIZE - 1);
215             addr2 = addr1 + DATA_SIZE;
216             res1 = glue(glue(slow_ld, SUFFIX), MMUSUFFIX)(addr1,
217                                                           mmu_idx, retaddr);
218             res2 = glue(glue(slow_ld, SUFFIX), MMUSUFFIX)(addr2,
219                                                           mmu_idx, retaddr);
220             shift = (addr & (DATA_SIZE - 1)) * 8;
221 #ifdef TARGET_WORDS_BIGENDIAN
222             res = (res1 << shift) | (res2 >> ((DATA_SIZE * 8) - shift));
223 #else
224             res = (res1 >> shift) | (res2 << ((DATA_SIZE * 8) - shift));
225 #endif
226             res = (DATA_TYPE)res;
227         } else {
228             /* unaligned/aligned access in the same page */
229             addend = env->tlb_table[mmu_idx][index].addend;
230             res = glue(glue(ld, USUFFIX), _raw)((uint8_t *)(long)(addr+addend));
231         }
232     } else {
233         /* the page is not in the TLB : fill it */
234         tlb_fill(addr, READ_ACCESS_TYPE, mmu_idx, retaddr);
235         goto redo;
236     }
237     return res;
238 }
239 
240 #ifndef SOFTMMU_CODE_ACCESS
241 
242 static void glue(glue(slow_st, SUFFIX), MMUSUFFIX)(target_ulong addr,
243                                                    DATA_TYPE val,
244                                                    int mmu_idx,
245                                                    void *retaddr);
246 
glue(io_write,SUFFIX)247 static inline void glue(io_write, SUFFIX)(target_phys_addr_t physaddr,
248                                           DATA_TYPE val,
249                                           target_ulong addr,
250                                           void *retaddr)
251 {
252     int index;
253     index = (physaddr >> IO_MEM_SHIFT) & (IO_MEM_NB_ENTRIES - 1);
254     physaddr = (physaddr & TARGET_PAGE_MASK) + addr;
255     if (index > (IO_MEM_NOTDIRTY >> IO_MEM_SHIFT)
256             && !can_do_io(env)) {
257         cpu_io_recompile(env, retaddr);
258     }
259 
260     env->mem_io_vaddr = addr;
261     env->mem_io_pc = (unsigned long)retaddr;
262 #if SHIFT <= 2
263     io_mem_write[index][SHIFT](io_mem_opaque[index], physaddr, val);
264 #else
265 #ifdef TARGET_WORDS_BIGENDIAN
266     io_mem_write[index][2](io_mem_opaque[index], physaddr, val >> 32);
267     io_mem_write[index][2](io_mem_opaque[index], physaddr + 4, val);
268 #else
269     io_mem_write[index][2](io_mem_opaque[index], physaddr, val);
270     io_mem_write[index][2](io_mem_opaque[index], physaddr + 4, val >> 32);
271 #endif
272 #endif /* SHIFT > 2 */
273 }
274 
glue(glue (__st,SUFFIX),MMUSUFFIX)275 void REGPARM glue(glue(__st, SUFFIX), MMUSUFFIX)(target_ulong addr,
276                                                  DATA_TYPE val,
277                                                  int mmu_idx)
278 {
279     target_phys_addr_t ioaddr;
280     unsigned long addend;
281     target_ulong tlb_addr;
282     void *retaddr;
283     int index;
284 #ifdef CONFIG_MEMCHECK_MMU
285     int invalidate_cache = 0;
286 #endif  // CONFIG_MEMCHECK_MMU
287 
288     index = (addr >> TARGET_PAGE_BITS) & (CPU_TLB_SIZE - 1);
289  redo:
290     tlb_addr = env->tlb_table[mmu_idx][index].addr_write;
291     if ((addr & TARGET_PAGE_MASK) == (tlb_addr & (TARGET_PAGE_MASK | TLB_INVALID_MASK))) {
292         if (tlb_addr & ~TARGET_PAGE_MASK) {
293             /* IO access */
294             if ((addr & (DATA_SIZE - 1)) != 0)
295                 goto do_unaligned_access;
296             retaddr = GETPC();
297             ioaddr = env->iotlb[mmu_idx][index];
298             glue(io_write, SUFFIX)(ioaddr, val, addr, retaddr);
299         } else if (((addr & ~TARGET_PAGE_MASK) + DATA_SIZE - 1) >= TARGET_PAGE_SIZE) {
300             /* This is not I/O access: do access verification. */
301 #ifdef CONFIG_MEMCHECK_MMU
302             /* We only validate access to the guest's user space, for which
303              * mmu_idx is set to 1. */
304             if (memcheck_instrument_mmu && mmu_idx == 1 &&
305                 memcheck_validate_st(addr, DATA_SIZE, (uint64_t)val,
306                                      (target_ulong)(ptrdiff_t)GETPC())) {
307                 /* Memory write breaks page boundary. So, if required, we
308                  * must invalidate two caches in TLB. */
309                 invalidate_cache = 2;
310             }
311 #endif  // CONFIG_MEMCHECK_MMU
312         do_unaligned_access:
313             retaddr = GETPC();
314 #ifdef ALIGNED_ONLY
315             do_unaligned_access(addr, 1, mmu_idx, retaddr);
316 #endif
317             glue(glue(slow_st, SUFFIX), MMUSUFFIX)(addr, val,
318                                                    mmu_idx, retaddr);
319         } else {
320 #ifdef CONFIG_MEMCHECK_MMU
321             /* We only validate access to the guest's user space, for which
322              * mmu_idx is set to 1. */
323             if (memcheck_instrument_mmu && mmu_idx == 1) {
324                 invalidate_cache = memcheck_validate_st(addr, DATA_SIZE,
325                                                         (uint64_t)val,
326                                                         (target_ulong)(ptrdiff_t)GETPC());
327             }
328 #endif  // CONFIG_MEMCHECK_MMU
329             /* aligned/unaligned access in the same page */
330 #ifdef ALIGNED_ONLY
331             if ((addr & (DATA_SIZE - 1)) != 0) {
332                 retaddr = GETPC();
333                 do_unaligned_access(addr, 1, mmu_idx, retaddr);
334             }
335 #endif
336             addend = env->tlb_table[mmu_idx][index].addend;
337             glue(glue(st, SUFFIX), _raw)((uint8_t *)(long)(addr+addend), val);
338         }
339 #ifdef CONFIG_MEMCHECK_MMU
340         if (invalidate_cache) {
341             /* Accessed memory is under memchecker control. We must invalidate
342              * containing page(s) in order to make sure that next access to them
343              * will invoke _ld/_st_mmu. */
344             env->tlb_table[mmu_idx][index].addr_read ^= TARGET_PAGE_MASK;
345             env->tlb_table[mmu_idx][index].addr_write ^= TARGET_PAGE_MASK;
346             if ((invalidate_cache == 2) && (index < CPU_TLB_SIZE)) {
347                 // Write crossed page boundaris. Invalidate second cache too.
348                 env->tlb_table[mmu_idx][index + 1].addr_read ^= TARGET_PAGE_MASK;
349                 env->tlb_table[mmu_idx][index + 1].addr_write ^= TARGET_PAGE_MASK;
350             }
351         }
352 #endif  // CONFIG_MEMCHECK_MMU
353     } else {
354         /* the page is not in the TLB : fill it */
355         retaddr = GETPC();
356 #ifdef ALIGNED_ONLY
357         if ((addr & (DATA_SIZE - 1)) != 0)
358             do_unaligned_access(addr, 1, mmu_idx, retaddr);
359 #endif
360         tlb_fill(addr, 1, mmu_idx, retaddr);
361         goto redo;
362     }
363 }
364 
365 /* handles all unaligned cases */
glue(glue (slow_st,SUFFIX),MMUSUFFIX)366 static void glue(glue(slow_st, SUFFIX), MMUSUFFIX)(target_ulong addr,
367                                                    DATA_TYPE val,
368                                                    int mmu_idx,
369                                                    void *retaddr)
370 {
371     target_phys_addr_t ioaddr;
372     unsigned long addend;
373     target_ulong tlb_addr;
374     int index, i;
375 
376     index = (addr >> TARGET_PAGE_BITS) & (CPU_TLB_SIZE - 1);
377  redo:
378     tlb_addr = env->tlb_table[mmu_idx][index].addr_write;
379     if ((addr & TARGET_PAGE_MASK) == (tlb_addr & (TARGET_PAGE_MASK | TLB_INVALID_MASK))) {
380         if (tlb_addr & ~TARGET_PAGE_MASK) {
381             /* IO access */
382             if ((addr & (DATA_SIZE - 1)) != 0)
383                 goto do_unaligned_access;
384             ioaddr = env->iotlb[mmu_idx][index];
385             glue(io_write, SUFFIX)(ioaddr, val, addr, retaddr);
386         } else if (((addr & ~TARGET_PAGE_MASK) + DATA_SIZE - 1) >= TARGET_PAGE_SIZE) {
387         do_unaligned_access:
388             /* XXX: not efficient, but simple */
389             /* Note: relies on the fact that tlb_fill() does not remove the
390              * previous page from the TLB cache.  */
391             for(i = DATA_SIZE - 1; i >= 0; i--) {
392 #ifdef TARGET_WORDS_BIGENDIAN
393                 glue(slow_stb, MMUSUFFIX)(addr + i, val >> (((DATA_SIZE - 1) * 8) - (i * 8)),
394                                           mmu_idx, retaddr);
395 #else
396                 glue(slow_stb, MMUSUFFIX)(addr + i, val >> (i * 8),
397                                           mmu_idx, retaddr);
398 #endif
399             }
400         } else {
401             /* aligned/unaligned access in the same page */
402             addend = env->tlb_table[mmu_idx][index].addend;
403             glue(glue(st, SUFFIX), _raw)((uint8_t *)(long)(addr+addend), val);
404         }
405     } else {
406         /* the page is not in the TLB : fill it */
407         tlb_fill(addr, 1, mmu_idx, retaddr);
408         goto redo;
409     }
410 }
411 
412 #endif /* !defined(SOFTMMU_CODE_ACCESS) */
413 
414 #undef READ_ACCESS_TYPE
415 #undef SHIFT
416 #undef DATA_TYPE
417 #undef SUFFIX
418 #undef USUFFIX
419 #undef DATA_SIZE
420 #undef ADDR_READ
421