qemu

FORK: QEMU emulator
git clone https://git.neptards.moe/neptards/qemu.git
Log | Files | Refs | Submodules | LICENSE

mmu.c (6087B)


      1 /*
      2  * OpenRISC MMU.
      3  *
      4  * Copyright (c) 2011-2012 Jia Liu <proljc@gmail.com>
      5  *                         Zhizhou Zhang <etouzh@gmail.com>
      6  *
      7  * This library is free software; you can redistribute it and/or
      8  * modify it under the terms of the GNU Lesser General Public
      9  * License as published by the Free Software Foundation; either
     10  * version 2.1 of the License, or (at your option) any later version.
     11  *
     12  * This library is distributed in the hope that it will be useful,
     13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
     14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
     15  * Lesser General Public License for more details.
     16  *
     17  * You should have received a copy of the GNU Lesser General Public
     18  * License along with this library; if not, see <http://www.gnu.org/licenses/>.
     19  */
     20 
     21 #include "qemu/osdep.h"
     22 #include "qemu/log.h"
     23 #include "cpu.h"
     24 #include "exec/exec-all.h"
     25 #include "exec/gdbstub.h"
     26 #include "qemu/host-utils.h"
     27 #include "hw/loader.h"
     28 
     29 static inline void get_phys_nommu(hwaddr *phys_addr, int *prot,
     30                                   target_ulong address)
     31 {
     32     *phys_addr = address;
     33     *prot = PAGE_READ | PAGE_WRITE | PAGE_EXEC;
     34 }
     35 
     36 static int get_phys_mmu(OpenRISCCPU *cpu, hwaddr *phys_addr, int *prot,
     37                         target_ulong addr, int need, bool super)
     38 {
     39     int idx = (addr >> TARGET_PAGE_BITS) & TLB_MASK;
     40     uint32_t imr = cpu->env.tlb.itlb[idx].mr;
     41     uint32_t itr = cpu->env.tlb.itlb[idx].tr;
     42     uint32_t dmr = cpu->env.tlb.dtlb[idx].mr;
     43     uint32_t dtr = cpu->env.tlb.dtlb[idx].tr;
     44     int right, match, valid;
     45 
     46     /* If the ITLB and DTLB indexes map to the same page, we want to
     47        load all permissions all at once.  If the destination pages do
     48        not match, zap the one we don't need.  */
     49     if (unlikely((itr ^ dtr) & TARGET_PAGE_MASK)) {
     50         if (need & PAGE_EXEC) {
     51             dmr = dtr = 0;
     52         } else {
     53             imr = itr = 0;
     54         }
     55     }
     56 
     57     /* Check if either of the entries matches the source address.  */
     58     match  = (imr ^ addr) & TARGET_PAGE_MASK ? 0 : PAGE_EXEC;
     59     match |= (dmr ^ addr) & TARGET_PAGE_MASK ? 0 : PAGE_READ | PAGE_WRITE;
     60 
     61     /* Check if either of the entries is valid.  */
     62     valid  = imr & 1 ? PAGE_EXEC : 0;
     63     valid |= dmr & 1 ? PAGE_READ | PAGE_WRITE : 0;
     64     valid &= match;
     65 
     66     /* Collect the permissions from the entries.  */
     67     right  = itr & (super ? SXE : UXE) ? PAGE_EXEC : 0;
     68     right |= dtr & (super ? SRE : URE) ? PAGE_READ : 0;
     69     right |= dtr & (super ? SWE : UWE) ? PAGE_WRITE : 0;
     70     right &= valid;
     71 
     72     /* Note that above we validated that itr and dtr match on page.
     73        So oring them together changes nothing without having to
     74        check which one we needed.  We also want to store to these
     75        variables even on failure, as it avoids compiler warnings.  */
     76     *phys_addr = ((itr | dtr) & TARGET_PAGE_MASK) | (addr & ~TARGET_PAGE_MASK);
     77     *prot = right;
     78 
     79     qemu_log_mask(CPU_LOG_MMU,
     80                   "MMU lookup: need %d match %d valid %d right %d -> %s\n",
     81                   need, match, valid, right, (need & right) ? "OK" : "FAIL");
     82 
     83     /* Check the collective permissions are present.  */
     84     if (likely(need & right)) {
     85         return 0;  /* success! */
     86     }
     87 
     88     /* Determine what kind of failure we have.  */
     89     if (need & valid) {
     90         return need & PAGE_EXEC ? EXCP_IPF : EXCP_DPF;
     91     } else {
     92         return need & PAGE_EXEC ? EXCP_ITLBMISS : EXCP_DTLBMISS;
     93     }
     94 }
     95 
     96 static void raise_mmu_exception(OpenRISCCPU *cpu, target_ulong address,
     97                                 int exception)
     98 {
     99     CPUState *cs = CPU(cpu);
    100 
    101     cs->exception_index = exception;
    102     cpu->env.eear = address;
    103     cpu->env.lock_addr = -1;
    104 }
    105 
    106 bool openrisc_cpu_tlb_fill(CPUState *cs, vaddr addr, int size,
    107                            MMUAccessType access_type, int mmu_idx,
    108                            bool probe, uintptr_t retaddr)
    109 {
    110     OpenRISCCPU *cpu = OPENRISC_CPU(cs);
    111     int excp = EXCP_DPF;
    112     int prot;
    113     hwaddr phys_addr;
    114 
    115     if (mmu_idx == MMU_NOMMU_IDX) {
    116         /* The mmu is disabled; lookups never fail.  */
    117         get_phys_nommu(&phys_addr, &prot, addr);
    118         excp = 0;
    119     } else {
    120         bool super = mmu_idx == MMU_SUPERVISOR_IDX;
    121         int need = (access_type == MMU_INST_FETCH ? PAGE_EXEC
    122                     : access_type == MMU_DATA_STORE ? PAGE_WRITE
    123                     : PAGE_READ);
    124         excp = get_phys_mmu(cpu, &phys_addr, &prot, addr, need, super);
    125     }
    126 
    127     if (likely(excp == 0)) {
    128         tlb_set_page(cs, addr & TARGET_PAGE_MASK,
    129                      phys_addr & TARGET_PAGE_MASK, prot,
    130                      mmu_idx, TARGET_PAGE_SIZE);
    131         return true;
    132     }
    133     if (probe) {
    134         return false;
    135     }
    136 
    137     raise_mmu_exception(cpu, addr, excp);
    138     cpu_loop_exit_restore(cs, retaddr);
    139 }
    140 
    141 hwaddr openrisc_cpu_get_phys_page_debug(CPUState *cs, vaddr addr)
    142 {
    143     OpenRISCCPU *cpu = OPENRISC_CPU(cs);
    144     int prot, excp, sr = cpu->env.sr;
    145     hwaddr phys_addr;
    146 
    147     switch (sr & (SR_DME | SR_IME)) {
    148     case SR_DME | SR_IME:
    149         /* The mmu is definitely enabled.  */
    150         excp = get_phys_mmu(cpu, &phys_addr, &prot, addr,
    151                             PAGE_READ,
    152                             (sr & SR_SM) != 0);
    153         if (!excp) {
    154             return phys_addr;
    155         }
    156         excp = get_phys_mmu(cpu, &phys_addr, &prot, addr,
    157                             PAGE_EXEC,
    158                             (sr & SR_SM) != 0);
    159         return excp ? -1 : phys_addr;
    160 
    161     default:
    162         /* The mmu is partially enabled, and we don't really have
    163            a "real" access type.  Begin by trying the mmu, but if
    164            that fails try again without.  */
    165         excp = get_phys_mmu(cpu, &phys_addr, &prot, addr,
    166                             PAGE_EXEC | PAGE_READ | PAGE_WRITE,
    167                             (sr & SR_SM) != 0);
    168         if (!excp) {
    169             return phys_addr;
    170         }
    171         /* fallthru */
    172 
    173     case 0:
    174         /* The mmu is definitely disabled; lookups never fail.  */
    175         get_phys_nommu(&phys_addr, &prot, addr);
    176         return phys_addr;
    177     }
    178 }