qemu/target/openrisc/mmu.c
<<
>>
Prefs
   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 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 "cpu.h"
  23#include "exec/exec-all.h"
  24#include "qemu-common.h"
  25#include "exec/gdbstub.h"
  26#include "qemu/host-utils.h"
  27#ifndef CONFIG_USER_ONLY
  28#include "hw/loader.h"
  29#endif
  30
  31#ifndef CONFIG_USER_ONLY
  32static inline void get_phys_nommu(hwaddr *phys_addr, int *prot,
  33                                  target_ulong address)
  34{
  35    *phys_addr = address;
  36    *prot = PAGE_READ | PAGE_WRITE | PAGE_EXEC;
  37}
  38
  39static int get_phys_mmu(OpenRISCCPU *cpu, hwaddr *phys_addr, int *prot,
  40                        target_ulong addr, int need, bool super)
  41{
  42    int idx = (addr >> TARGET_PAGE_BITS) & TLB_MASK;
  43    uint32_t imr = cpu->env.tlb.itlb[idx].mr;
  44    uint32_t itr = cpu->env.tlb.itlb[idx].tr;
  45    uint32_t dmr = cpu->env.tlb.dtlb[idx].mr;
  46    uint32_t dtr = cpu->env.tlb.dtlb[idx].tr;
  47    int right, match, valid;
  48
  49    /* If the ITLB and DTLB indexes map to the same page, we want to
  50       load all permissions all at once.  If the destination pages do
  51       not match, zap the one we don't need.  */
  52    if (unlikely((itr ^ dtr) & TARGET_PAGE_MASK)) {
  53        if (need & PAGE_EXEC) {
  54            dmr = dtr = 0;
  55        } else {
  56            imr = itr = 0;
  57        }
  58    }
  59
  60    /* Check if either of the entries matches the source address.  */
  61    match  = (imr ^ addr) & TARGET_PAGE_MASK ? 0 : PAGE_EXEC;
  62    match |= (dmr ^ addr) & TARGET_PAGE_MASK ? 0 : PAGE_READ | PAGE_WRITE;
  63
  64    /* Check if either of the entries is valid.  */
  65    valid  = imr & 1 ? PAGE_EXEC : 0;
  66    valid |= dmr & 1 ? PAGE_READ | PAGE_WRITE : 0;
  67    valid &= match;
  68
  69    /* Collect the permissions from the entries.  */
  70    right  = itr & (super ? SXE : UXE) ? PAGE_EXEC : 0;
  71    right |= dtr & (super ? SRE : URE) ? PAGE_READ : 0;
  72    right |= dtr & (super ? SWE : UWE) ? PAGE_WRITE : 0;
  73    right &= valid;
  74
  75    /* Note that above we validated that itr and dtr match on page.
  76       So oring them together changes nothing without having to
  77       check which one we needed.  We also want to store to these
  78       variables even on failure, as it avoids compiler warnings.  */
  79    *phys_addr = ((itr | dtr) & TARGET_PAGE_MASK) | (addr & ~TARGET_PAGE_MASK);
  80    *prot = right;
  81
  82    qemu_log_mask(CPU_LOG_MMU,
  83                  "MMU lookup: need %d match %d valid %d right %d -> %s\n",
  84                  need, match, valid, right, (need & right) ? "OK" : "FAIL");
  85
  86    /* Check the collective permissions are present.  */
  87    if (likely(need & right)) {
  88        return 0;  /* success! */
  89    }
  90
  91    /* Determine what kind of failure we have.  */
  92    if (need & valid) {
  93        return need & PAGE_EXEC ? EXCP_IPF : EXCP_DPF;
  94    } else {
  95        return need & PAGE_EXEC ? EXCP_ITLBMISS : EXCP_DTLBMISS;
  96    }
  97}
  98#endif
  99
 100static void raise_mmu_exception(OpenRISCCPU *cpu, target_ulong address,
 101                                int exception)
 102{
 103    CPUState *cs = CPU(cpu);
 104
 105    cs->exception_index = exception;
 106    cpu->env.eear = address;
 107    cpu->env.lock_addr = -1;
 108}
 109
 110int openrisc_cpu_handle_mmu_fault(CPUState *cs, vaddr address, int size,
 111                                  int rw, int mmu_idx)
 112{
 113#ifdef CONFIG_USER_ONLY
 114    OpenRISCCPU *cpu = OPENRISC_CPU(cs);
 115    raise_mmu_exception(cpu, address, EXCP_DPF);
 116    return 1;
 117#else
 118    g_assert_not_reached();
 119#endif
 120}
 121
 122#ifndef CONFIG_USER_ONLY
 123hwaddr openrisc_cpu_get_phys_page_debug(CPUState *cs, vaddr addr)
 124{
 125    OpenRISCCPU *cpu = OPENRISC_CPU(cs);
 126    int prot, excp, sr = cpu->env.sr;
 127    hwaddr phys_addr;
 128
 129    switch (sr & (SR_DME | SR_IME)) {
 130    case SR_DME | SR_IME:
 131        /* The mmu is definitely enabled.  */
 132        excp = get_phys_mmu(cpu, &phys_addr, &prot, addr,
 133                            PAGE_EXEC | PAGE_READ | PAGE_WRITE,
 134                            (sr & SR_SM) != 0);
 135        return excp ? -1 : phys_addr;
 136
 137    default:
 138        /* The mmu is partially enabled, and we don't really have
 139           a "real" access type.  Begin by trying the mmu, but if
 140           that fails try again without.  */
 141        excp = get_phys_mmu(cpu, &phys_addr, &prot, addr,
 142                            PAGE_EXEC | PAGE_READ | PAGE_WRITE,
 143                            (sr & SR_SM) != 0);
 144        if (!excp) {
 145            return phys_addr;
 146        }
 147        /* fallthru */
 148
 149    case 0:
 150        /* The mmu is definitely disabled; lookups never fail.  */
 151        get_phys_nommu(&phys_addr, &prot, addr);
 152        return phys_addr;
 153    }
 154}
 155
 156void tlb_fill(CPUState *cs, target_ulong addr, int size,
 157              MMUAccessType access_type, int mmu_idx, uintptr_t retaddr)
 158{
 159    OpenRISCCPU *cpu = OPENRISC_CPU(cs);
 160    int prot, excp;
 161    hwaddr phys_addr;
 162
 163    if (mmu_idx == MMU_NOMMU_IDX) {
 164        /* The mmu is disabled; lookups never fail.  */
 165        get_phys_nommu(&phys_addr, &prot, addr);
 166        excp = 0;
 167    } else {
 168        bool super = mmu_idx == MMU_SUPERVISOR_IDX;
 169        int need = (access_type == MMU_INST_FETCH ? PAGE_EXEC
 170                    : access_type == MMU_DATA_STORE ? PAGE_WRITE
 171                    : PAGE_READ);
 172        excp = get_phys_mmu(cpu, &phys_addr, &prot, addr, need, super);
 173    }
 174
 175    if (unlikely(excp)) {
 176        raise_mmu_exception(cpu, addr, excp);
 177        cpu_loop_exit_restore(cs, retaddr);
 178    }
 179
 180    tlb_set_page(cs, addr & TARGET_PAGE_MASK,
 181                 phys_addr & TARGET_PAGE_MASK, prot,
 182                 mmu_idx, TARGET_PAGE_SIZE);
 183}
 184#endif
 185