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