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
  32int cpu_openrisc_get_phys_nommu(OpenRISCCPU *cpu,
  33                                hwaddr *physical,
  34                                int *prot, target_ulong address, int rw)
  35{
  36    *physical = address;
  37    *prot = PAGE_READ | PAGE_WRITE | PAGE_EXEC;
  38    return TLBRET_MATCH;
  39}
  40
  41int cpu_openrisc_get_phys_code(OpenRISCCPU *cpu,
  42                               hwaddr *physical,
  43                               int *prot, target_ulong address, int rw)
  44{
  45    int vpn = address >> TARGET_PAGE_BITS;
  46    int idx = vpn & ITLB_MASK;
  47    int right = 0;
  48
  49    if ((cpu->env.tlb->itlb[0][idx].mr >> TARGET_PAGE_BITS) != vpn) {
  50        return TLBRET_NOMATCH;
  51    }
  52    if (!(cpu->env.tlb->itlb[0][idx].mr & 1)) {
  53        return TLBRET_INVALID;
  54    }
  55
  56    if (cpu->env.sr & SR_SM) { /* supervisor mode */
  57        if (cpu->env.tlb->itlb[0][idx].tr & SXE) {
  58            right |= PAGE_EXEC;
  59        }
  60    } else {
  61        if (cpu->env.tlb->itlb[0][idx].tr & UXE) {
  62            right |= PAGE_EXEC;
  63        }
  64    }
  65
  66    if ((rw & 2) && ((right & PAGE_EXEC) == 0)) {
  67        return TLBRET_BADADDR;
  68    }
  69
  70    *physical = (cpu->env.tlb->itlb[0][idx].tr & TARGET_PAGE_MASK) |
  71                (address & (TARGET_PAGE_SIZE-1));
  72    *prot = right;
  73    return TLBRET_MATCH;
  74}
  75
  76int cpu_openrisc_get_phys_data(OpenRISCCPU *cpu,
  77                               hwaddr *physical,
  78                               int *prot, target_ulong address, int rw)
  79{
  80    int vpn = address >> TARGET_PAGE_BITS;
  81    int idx = vpn & DTLB_MASK;
  82    int right = 0;
  83
  84    if ((cpu->env.tlb->dtlb[0][idx].mr >> TARGET_PAGE_BITS) != vpn) {
  85        return TLBRET_NOMATCH;
  86    }
  87    if (!(cpu->env.tlb->dtlb[0][idx].mr & 1)) {
  88        return TLBRET_INVALID;
  89    }
  90
  91    if (cpu->env.sr & SR_SM) { /* supervisor mode */
  92        if (cpu->env.tlb->dtlb[0][idx].tr & SRE) {
  93            right |= PAGE_READ;
  94        }
  95        if (cpu->env.tlb->dtlb[0][idx].tr & SWE) {
  96            right |= PAGE_WRITE;
  97        }
  98    } else {
  99        if (cpu->env.tlb->dtlb[0][idx].tr & URE) {
 100            right |= PAGE_READ;
 101        }
 102        if (cpu->env.tlb->dtlb[0][idx].tr & UWE) {
 103            right |= PAGE_WRITE;
 104        }
 105    }
 106
 107    if (!(rw & 1) && ((right & PAGE_READ) == 0)) {
 108        return TLBRET_BADADDR;
 109    }
 110    if ((rw & 1) && ((right & PAGE_WRITE) == 0)) {
 111        return TLBRET_BADADDR;
 112    }
 113
 114    *physical = (cpu->env.tlb->dtlb[0][idx].tr & TARGET_PAGE_MASK) |
 115                (address & (TARGET_PAGE_SIZE-1));
 116    *prot = right;
 117    return TLBRET_MATCH;
 118}
 119
 120static int cpu_openrisc_get_phys_addr(OpenRISCCPU *cpu,
 121                                      hwaddr *physical,
 122                                      int *prot, target_ulong address,
 123                                      int rw)
 124{
 125    int ret = TLBRET_MATCH;
 126
 127    if (rw == MMU_INST_FETCH) {    /* ITLB */
 128       *physical = 0;
 129        ret = cpu->env.tlb->cpu_openrisc_map_address_code(cpu, physical,
 130                                                          prot, address, rw);
 131    } else {          /* DTLB */
 132        ret = cpu->env.tlb->cpu_openrisc_map_address_data(cpu, physical,
 133                                                          prot, address, rw);
 134    }
 135
 136    return ret;
 137}
 138#endif
 139
 140static void cpu_openrisc_raise_mmu_exception(OpenRISCCPU *cpu,
 141                                             target_ulong address,
 142                                             int rw, int tlb_error)
 143{
 144    CPUState *cs = CPU(cpu);
 145    int exception = 0;
 146
 147    switch (tlb_error) {
 148    default:
 149        if (rw == 2) {
 150            exception = EXCP_IPF;
 151        } else {
 152            exception = EXCP_DPF;
 153        }
 154        break;
 155#ifndef CONFIG_USER_ONLY
 156    case TLBRET_BADADDR:
 157        if (rw == 2) {
 158            exception = EXCP_IPF;
 159        } else {
 160            exception = EXCP_DPF;
 161        }
 162        break;
 163    case TLBRET_INVALID:
 164    case TLBRET_NOMATCH:
 165        /* No TLB match for a mapped address */
 166        if (rw == 2) {
 167            exception = EXCP_ITLBMISS;
 168        } else {
 169            exception = EXCP_DTLBMISS;
 170        }
 171        break;
 172#endif
 173    }
 174
 175    cs->exception_index = exception;
 176    cpu->env.eear = address;
 177    cpu->env.lock_addr = -1;
 178}
 179
 180#ifndef CONFIG_USER_ONLY
 181int openrisc_cpu_handle_mmu_fault(CPUState *cs,
 182                                  vaddr address, int rw, int mmu_idx)
 183{
 184    OpenRISCCPU *cpu = OPENRISC_CPU(cs);
 185    int ret = 0;
 186    hwaddr physical = 0;
 187    int prot = 0;
 188
 189    ret = cpu_openrisc_get_phys_addr(cpu, &physical, &prot,
 190                                     address, rw);
 191
 192    if (ret == TLBRET_MATCH) {
 193        tlb_set_page(cs, address & TARGET_PAGE_MASK,
 194                     physical & TARGET_PAGE_MASK, prot,
 195                     mmu_idx, TARGET_PAGE_SIZE);
 196        ret = 0;
 197    } else if (ret < 0) {
 198        cpu_openrisc_raise_mmu_exception(cpu, address, rw, ret);
 199        ret = 1;
 200    }
 201
 202    return ret;
 203}
 204#else
 205int openrisc_cpu_handle_mmu_fault(CPUState *cs,
 206                                  vaddr address, int rw, int mmu_idx)
 207{
 208    OpenRISCCPU *cpu = OPENRISC_CPU(cs);
 209    int ret = 0;
 210
 211    cpu_openrisc_raise_mmu_exception(cpu, address, rw, ret);
 212    ret = 1;
 213
 214    return ret;
 215}
 216#endif
 217
 218#ifndef CONFIG_USER_ONLY
 219hwaddr openrisc_cpu_get_phys_page_debug(CPUState *cs, vaddr addr)
 220{
 221    OpenRISCCPU *cpu = OPENRISC_CPU(cs);
 222    hwaddr phys_addr;
 223    int prot;
 224    int miss;
 225
 226    /* Check memory for any kind of address, since during debug the
 227       gdb can ask for anything, check data tlb for address */
 228    miss = cpu_openrisc_get_phys_addr(cpu, &phys_addr, &prot, addr, 0);
 229
 230    /* Check instruction tlb */
 231    if (miss) {
 232        miss = cpu_openrisc_get_phys_addr(cpu, &phys_addr, &prot, addr,
 233                                          MMU_INST_FETCH);
 234    }
 235
 236    /* Last, fall back to a plain address */
 237    if (miss) {
 238        miss = cpu_openrisc_get_phys_nommu(cpu, &phys_addr, &prot, addr, 0);
 239    }
 240
 241    if (miss) {
 242        return -1;
 243    } else {
 244        return phys_addr;
 245    }
 246}
 247
 248void cpu_openrisc_mmu_init(OpenRISCCPU *cpu)
 249{
 250    cpu->env.tlb = g_malloc0(sizeof(CPUOpenRISCTLBContext));
 251
 252    cpu->env.tlb->cpu_openrisc_map_address_code = &cpu_openrisc_get_phys_nommu;
 253    cpu->env.tlb->cpu_openrisc_map_address_data = &cpu_openrisc_get_phys_nommu;
 254}
 255#endif
 256