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