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.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
  29static 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
  36static 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
  96static 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
 106bool 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
 141hwaddr 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_EXEC | PAGE_READ | PAGE_WRITE,
 152                            (sr & SR_SM) != 0);
 153        return excp ? -1 : phys_addr;
 154
 155    default:
 156        /* The mmu is partially enabled, and we don't really have
 157           a "real" access type.  Begin by trying the mmu, but if
 158           that fails try again without.  */
 159        excp = get_phys_mmu(cpu, &phys_addr, &prot, addr,
 160                            PAGE_EXEC | PAGE_READ | PAGE_WRITE,
 161                            (sr & SR_SM) != 0);
 162        if (!excp) {
 163            return phys_addr;
 164        }
 165        /* fallthru */
 166
 167    case 0:
 168        /* The mmu is definitely disabled; lookups never fail.  */
 169        get_phys_nommu(&phys_addr, &prot, addr);
 170        return phys_addr;
 171    }
 172}
 173