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