qemu/target/nios2/helper.c
<<
>>
Prefs
   1/*
   2 * Altera Nios II helper routines.
   3 *
   4 * Copyright (c) 2012 Chris Wulff <crwulff@gmail.com>
   5 *
   6 * This library is free software; you can redistribute it and/or
   7 * modify it under the terms of the GNU Lesser General Public
   8 * License as published by the Free Software Foundation; either
   9 * version 2.1 of the License, or (at your option) any later version.
  10 *
  11 * This library is distributed in the hope that it will be useful,
  12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
  13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
  14 * Lesser General Public License for more details.
  15 *
  16 * You should have received a copy of the GNU Lesser General Public
  17 * License along with this library; if not, see
  18 * <http://www.gnu.org/licenses/lgpl-2.1.html>
  19 */
  20
  21#include "qemu/osdep.h"
  22
  23#include "cpu.h"
  24#include "qemu/host-utils.h"
  25#include "exec/exec-all.h"
  26#include "exec/log.h"
  27#include "exec/helper-proto.h"
  28
  29#if defined(CONFIG_USER_ONLY)
  30
  31void nios2_cpu_do_interrupt(CPUState *cs)
  32{
  33    Nios2CPU *cpu = NIOS2_CPU(cs);
  34    CPUNios2State *env = &cpu->env;
  35    cs->exception_index = -1;
  36    env->regs[R_EA] = env->regs[R_PC] + 4;
  37}
  38
  39int nios2_cpu_handle_mmu_fault(CPUState *cs, vaddr address, int size,
  40                               int rw, int mmu_idx)
  41{
  42    cs->exception_index = 0xaa;
  43    /* Page 0x1000 is kuser helper */
  44    if (address < 0x1000 || address >= 0x2000) {
  45        cpu_dump_state(cs, stderr, fprintf, 0);
  46    }
  47    return 1;
  48}
  49
  50#else /* !CONFIG_USER_ONLY */
  51
  52void nios2_cpu_do_interrupt(CPUState *cs)
  53{
  54    Nios2CPU *cpu = NIOS2_CPU(cs);
  55    CPUNios2State *env = &cpu->env;
  56
  57    switch (cs->exception_index) {
  58    case EXCP_IRQ:
  59        assert(env->regs[CR_STATUS] & CR_STATUS_PIE);
  60
  61        qemu_log_mask(CPU_LOG_INT, "interrupt at pc=%x\n", env->regs[R_PC]);
  62
  63        env->regs[CR_ESTATUS] = env->regs[CR_STATUS];
  64        env->regs[CR_STATUS] |= CR_STATUS_IH;
  65        env->regs[CR_STATUS] &= ~(CR_STATUS_PIE | CR_STATUS_U);
  66
  67        env->regs[CR_EXCEPTION] &= ~(0x1F << 2);
  68        env->regs[CR_EXCEPTION] |= (cs->exception_index & 0x1F) << 2;
  69
  70        env->regs[R_EA] = env->regs[R_PC] + 4;
  71        env->regs[R_PC] = cpu->exception_addr;
  72        break;
  73
  74    case EXCP_TLBD:
  75        if ((env->regs[CR_STATUS] & CR_STATUS_EH) == 0) {
  76            qemu_log_mask(CPU_LOG_INT, "TLB MISS (fast) at pc=%x\n",
  77                          env->regs[R_PC]);
  78
  79            /* Fast TLB miss */
  80            /* Variation from the spec. Table 3-35 of the cpu reference shows
  81             * estatus not being changed for TLB miss but this appears to
  82             * be incorrect. */
  83            env->regs[CR_ESTATUS] = env->regs[CR_STATUS];
  84            env->regs[CR_STATUS] |= CR_STATUS_EH;
  85            env->regs[CR_STATUS] &= ~(CR_STATUS_PIE | CR_STATUS_U);
  86
  87            env->regs[CR_EXCEPTION] &= ~(0x1F << 2);
  88            env->regs[CR_EXCEPTION] |= (cs->exception_index & 0x1F) << 2;
  89
  90            env->regs[CR_TLBMISC] &= ~CR_TLBMISC_DBL;
  91            env->regs[CR_TLBMISC] |= CR_TLBMISC_WR;
  92
  93            env->regs[R_EA] = env->regs[R_PC] + 4;
  94            env->regs[R_PC] = cpu->fast_tlb_miss_addr;
  95        } else {
  96            qemu_log_mask(CPU_LOG_INT, "TLB MISS (double) at pc=%x\n",
  97                          env->regs[R_PC]);
  98
  99            /* Double TLB miss */
 100            env->regs[CR_STATUS] |= CR_STATUS_EH;
 101            env->regs[CR_STATUS] &= ~(CR_STATUS_PIE | CR_STATUS_U);
 102
 103            env->regs[CR_EXCEPTION] &= ~(0x1F << 2);
 104            env->regs[CR_EXCEPTION] |= (cs->exception_index & 0x1F) << 2;
 105
 106            env->regs[CR_TLBMISC] |= CR_TLBMISC_DBL;
 107
 108            env->regs[R_PC] = cpu->exception_addr;
 109        }
 110        break;
 111
 112    case EXCP_TLBR:
 113    case EXCP_TLBW:
 114    case EXCP_TLBX:
 115        qemu_log_mask(CPU_LOG_INT, "TLB PERM at pc=%x\n", env->regs[R_PC]);
 116
 117        env->regs[CR_ESTATUS] = env->regs[CR_STATUS];
 118        env->regs[CR_STATUS] |= CR_STATUS_EH;
 119        env->regs[CR_STATUS] &= ~(CR_STATUS_PIE | CR_STATUS_U);
 120
 121        env->regs[CR_EXCEPTION] &= ~(0x1F << 2);
 122        env->regs[CR_EXCEPTION] |= (cs->exception_index & 0x1F) << 2;
 123
 124        if ((env->regs[CR_STATUS] & CR_STATUS_EH) == 0) {
 125            env->regs[CR_TLBMISC] |= CR_TLBMISC_WR;
 126        }
 127
 128        env->regs[R_EA] = env->regs[R_PC] + 4;
 129        env->regs[R_PC] = cpu->exception_addr;
 130        break;
 131
 132    case EXCP_SUPERA:
 133    case EXCP_SUPERI:
 134    case EXCP_SUPERD:
 135        qemu_log_mask(CPU_LOG_INT, "SUPERVISOR exception at pc=%x\n",
 136                      env->regs[R_PC]);
 137
 138        if ((env->regs[CR_STATUS] & CR_STATUS_EH) == 0) {
 139            env->regs[CR_ESTATUS] = env->regs[CR_STATUS];
 140            env->regs[R_EA] = env->regs[R_PC] + 4;
 141        }
 142
 143        env->regs[CR_STATUS] |= CR_STATUS_EH;
 144        env->regs[CR_STATUS] &= ~(CR_STATUS_PIE | CR_STATUS_U);
 145
 146        env->regs[CR_EXCEPTION] &= ~(0x1F << 2);
 147        env->regs[CR_EXCEPTION] |= (cs->exception_index & 0x1F) << 2;
 148
 149        env->regs[R_PC] = cpu->exception_addr;
 150        break;
 151
 152    case EXCP_ILLEGAL:
 153    case EXCP_TRAP:
 154        qemu_log_mask(CPU_LOG_INT, "TRAP exception at pc=%x\n",
 155                      env->regs[R_PC]);
 156
 157        if ((env->regs[CR_STATUS] & CR_STATUS_EH) == 0) {
 158            env->regs[CR_ESTATUS] = env->regs[CR_STATUS];
 159            env->regs[R_EA] = env->regs[R_PC] + 4;
 160        }
 161
 162        env->regs[CR_STATUS] |= CR_STATUS_EH;
 163        env->regs[CR_STATUS] &= ~(CR_STATUS_PIE | CR_STATUS_U);
 164
 165        env->regs[CR_EXCEPTION] &= ~(0x1F << 2);
 166        env->regs[CR_EXCEPTION] |= (cs->exception_index & 0x1F) << 2;
 167
 168        env->regs[R_PC] = cpu->exception_addr;
 169        break;
 170
 171    case EXCP_BREAK:
 172        if ((env->regs[CR_STATUS] & CR_STATUS_EH) == 0) {
 173            env->regs[CR_BSTATUS] = env->regs[CR_STATUS];
 174            env->regs[R_BA] = env->regs[R_PC] + 4;
 175        }
 176
 177        env->regs[CR_STATUS] |= CR_STATUS_EH;
 178        env->regs[CR_STATUS] &= ~(CR_STATUS_PIE | CR_STATUS_U);
 179
 180        env->regs[CR_EXCEPTION] &= ~(0x1F << 2);
 181        env->regs[CR_EXCEPTION] |= (cs->exception_index & 0x1F) << 2;
 182
 183        env->regs[R_PC] = cpu->exception_addr;
 184        break;
 185
 186    default:
 187        cpu_abort(cs, "unhandled exception type=%d\n",
 188                  cs->exception_index);
 189        break;
 190    }
 191}
 192
 193static int cpu_nios2_handle_virtual_page(
 194    CPUState *cs, target_ulong address, int rw, int mmu_idx)
 195{
 196    Nios2CPU *cpu = NIOS2_CPU(cs);
 197    CPUNios2State *env = &cpu->env;
 198    target_ulong vaddr, paddr;
 199    Nios2MMULookup lu;
 200    unsigned int hit;
 201    hit = mmu_translate(env, &lu, address, rw, mmu_idx);
 202    if (hit) {
 203        vaddr = address & TARGET_PAGE_MASK;
 204        paddr = lu.paddr + vaddr - lu.vaddr;
 205
 206        if (((rw == 0) && (lu.prot & PAGE_READ)) ||
 207            ((rw == 1) && (lu.prot & PAGE_WRITE)) ||
 208            ((rw == 2) && (lu.prot & PAGE_EXEC))) {
 209
 210            tlb_set_page(cs, vaddr, paddr, lu.prot,
 211                         mmu_idx, TARGET_PAGE_SIZE);
 212            return 0;
 213        } else {
 214            /* Permission violation */
 215            cs->exception_index = (rw == 0) ? EXCP_TLBR :
 216                                               ((rw == 1) ? EXCP_TLBW :
 217                                                            EXCP_TLBX);
 218        }
 219    } else {
 220        cs->exception_index = EXCP_TLBD;
 221    }
 222
 223    if (rw == 2) {
 224        env->regs[CR_TLBMISC] &= ~CR_TLBMISC_D;
 225    } else {
 226        env->regs[CR_TLBMISC] |= CR_TLBMISC_D;
 227    }
 228    env->regs[CR_PTEADDR] &= CR_PTEADDR_PTBASE_MASK;
 229    env->regs[CR_PTEADDR] |= (address >> 10) & CR_PTEADDR_VPN_MASK;
 230    env->mmu.pteaddr_wr = env->regs[CR_PTEADDR];
 231    env->regs[CR_BADADDR] = address;
 232    return 1;
 233}
 234
 235int nios2_cpu_handle_mmu_fault(CPUState *cs, vaddr address, int size,
 236                               int rw, int mmu_idx)
 237{
 238    Nios2CPU *cpu = NIOS2_CPU(cs);
 239    CPUNios2State *env = &cpu->env;
 240
 241    if (cpu->mmu_present) {
 242        if (MMU_SUPERVISOR_IDX == mmu_idx) {
 243            if (address >= 0xC0000000) {
 244                /* Kernel physical page - TLB bypassed */
 245                address &= TARGET_PAGE_MASK;
 246                tlb_set_page(cs, address, address, PAGE_BITS,
 247                             mmu_idx, TARGET_PAGE_SIZE);
 248            } else if (address >= 0x80000000) {
 249                /* Kernel virtual page */
 250                return cpu_nios2_handle_virtual_page(cs, address, rw, mmu_idx);
 251            } else {
 252                /* User virtual page */
 253                return cpu_nios2_handle_virtual_page(cs, address, rw, mmu_idx);
 254            }
 255        } else {
 256            if (address >= 0x80000000) {
 257                /* Illegal access from user mode */
 258                cs->exception_index = EXCP_SUPERA;
 259                env->regs[CR_BADADDR] = address;
 260                return 1;
 261            } else {
 262                /* User virtual page */
 263                return cpu_nios2_handle_virtual_page(cs, address, rw, mmu_idx);
 264            }
 265        }
 266    } else {
 267        /* No MMU */
 268        address &= TARGET_PAGE_MASK;
 269        tlb_set_page(cs, address, address, PAGE_BITS,
 270                     mmu_idx, TARGET_PAGE_SIZE);
 271    }
 272
 273    return 0;
 274}
 275
 276hwaddr nios2_cpu_get_phys_page_debug(CPUState *cs, vaddr addr)
 277{
 278    Nios2CPU *cpu = NIOS2_CPU(cs);
 279    CPUNios2State *env = &cpu->env;
 280    target_ulong vaddr, paddr = 0;
 281    Nios2MMULookup lu;
 282    unsigned int hit;
 283
 284    if (cpu->mmu_present && (addr < 0xC0000000)) {
 285        hit = mmu_translate(env, &lu, addr, 0, 0);
 286        if (hit) {
 287            vaddr = addr & TARGET_PAGE_MASK;
 288            paddr = lu.paddr + vaddr - lu.vaddr;
 289        } else {
 290            paddr = -1;
 291            qemu_log("cpu_get_phys_page debug MISS: %#" PRIx64 "\n", addr);
 292        }
 293    } else {
 294        paddr = addr & TARGET_PAGE_MASK;
 295    }
 296
 297    return paddr;
 298}
 299
 300void nios2_cpu_do_unaligned_access(CPUState *cs, vaddr addr,
 301                                   MMUAccessType access_type,
 302                                   int mmu_idx, uintptr_t retaddr)
 303{
 304    Nios2CPU *cpu = NIOS2_CPU(cs);
 305    CPUNios2State *env = &cpu->env;
 306
 307    env->regs[CR_BADADDR] = addr;
 308    env->regs[CR_EXCEPTION] = EXCP_UNALIGN << 2;
 309    helper_raise_exception(env, EXCP_UNALIGN);
 310}
 311#endif /* !CONFIG_USER_ONLY */
 312