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