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/cpu_ldst.h"
  27#include "exec/log.h"
  28#include "exec/helper-proto.h"
  29#include "semihosting/semihost.h"
  30
  31#if defined(CONFIG_USER_ONLY)
  32
  33void nios2_cpu_do_interrupt(CPUState *cs)
  34{
  35    Nios2CPU *cpu = NIOS2_CPU(cs);
  36    CPUNios2State *env = &cpu->env;
  37    cs->exception_index = -1;
  38    env->regs[R_EA] = env->regs[R_PC] + 4;
  39}
  40
  41void nios2_cpu_record_sigsegv(CPUState *cs, vaddr addr,
  42                              MMUAccessType access_type,
  43                              bool maperr, uintptr_t retaddr)
  44{
  45    /* FIXME: Disentangle kuser page from linux-user sigsegv handling. */
  46    cs->exception_index = 0xaa;
  47    cpu_loop_exit_restore(cs, retaddr);
  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        qemu_log_mask(CPU_LOG_INT, "BREAK exception at pc=%x\n",
 173                      env->regs[R_PC]);
 174        /* The semihosting instruction is "break 1".  */
 175        if (semihosting_enabled() &&
 176            cpu_ldl_code(env, env->regs[R_PC]) == 0x003da07a)  {
 177            qemu_log_mask(CPU_LOG_INT, "Entering semihosting\n");
 178            env->regs[R_PC] += 4;
 179            do_nios2_semihosting(env);
 180            break;
 181        }
 182
 183        if ((env->regs[CR_STATUS] & CR_STATUS_EH) == 0) {
 184            env->regs[CR_BSTATUS] = env->regs[CR_STATUS];
 185            env->regs[R_BA] = env->regs[R_PC] + 4;
 186        }
 187
 188        env->regs[CR_STATUS] |= CR_STATUS_EH;
 189        env->regs[CR_STATUS] &= ~(CR_STATUS_PIE | CR_STATUS_U);
 190
 191        env->regs[CR_EXCEPTION] &= ~(0x1F << 2);
 192        env->regs[CR_EXCEPTION] |= (cs->exception_index & 0x1F) << 2;
 193
 194        env->regs[R_PC] = cpu->exception_addr;
 195        break;
 196
 197    default:
 198        cpu_abort(cs, "unhandled exception type=%d\n",
 199                  cs->exception_index);
 200        break;
 201    }
 202}
 203
 204hwaddr nios2_cpu_get_phys_page_debug(CPUState *cs, vaddr addr)
 205{
 206    Nios2CPU *cpu = NIOS2_CPU(cs);
 207    CPUNios2State *env = &cpu->env;
 208    target_ulong vaddr, paddr = 0;
 209    Nios2MMULookup lu;
 210    unsigned int hit;
 211
 212    if (cpu->mmu_present && (addr < 0xC0000000)) {
 213        hit = mmu_translate(env, &lu, addr, 0, 0);
 214        if (hit) {
 215            vaddr = addr & TARGET_PAGE_MASK;
 216            paddr = lu.paddr + vaddr - lu.vaddr;
 217        } else {
 218            paddr = -1;
 219            qemu_log("cpu_get_phys_page debug MISS: %#" PRIx64 "\n", addr);
 220        }
 221    } else {
 222        paddr = addr & TARGET_PAGE_MASK;
 223    }
 224
 225    return paddr;
 226}
 227
 228void nios2_cpu_do_unaligned_access(CPUState *cs, vaddr addr,
 229                                   MMUAccessType access_type,
 230                                   int mmu_idx, uintptr_t retaddr)
 231{
 232    Nios2CPU *cpu = NIOS2_CPU(cs);
 233    CPUNios2State *env = &cpu->env;
 234
 235    env->regs[CR_BADADDR] = addr;
 236    env->regs[CR_EXCEPTION] = EXCP_UNALIGN << 2;
 237    helper_raise_exception(env, EXCP_UNALIGN);
 238}
 239
 240bool nios2_cpu_tlb_fill(CPUState *cs, vaddr address, int size,
 241                        MMUAccessType access_type, int mmu_idx,
 242                        bool probe, uintptr_t retaddr)
 243{
 244    Nios2CPU *cpu = NIOS2_CPU(cs);
 245    CPUNios2State *env = &cpu->env;
 246    unsigned int excp = EXCP_TLBD;
 247    target_ulong vaddr, paddr;
 248    Nios2MMULookup lu;
 249    unsigned int hit;
 250
 251    if (!cpu->mmu_present) {
 252        /* No MMU */
 253        address &= TARGET_PAGE_MASK;
 254        tlb_set_page(cs, address, address, PAGE_BITS,
 255                     mmu_idx, TARGET_PAGE_SIZE);
 256        return true;
 257    }
 258
 259    if (MMU_SUPERVISOR_IDX == mmu_idx) {
 260        if (address >= 0xC0000000) {
 261            /* Kernel physical page - TLB bypassed */
 262            address &= TARGET_PAGE_MASK;
 263            tlb_set_page(cs, address, address, PAGE_BITS,
 264                         mmu_idx, TARGET_PAGE_SIZE);
 265            return true;
 266        }
 267    } else {
 268        if (address >= 0x80000000) {
 269            /* Illegal access from user mode */
 270            if (probe) {
 271                return false;
 272            }
 273            cs->exception_index = EXCP_SUPERA;
 274            env->regs[CR_BADADDR] = address;
 275            cpu_loop_exit_restore(cs, retaddr);
 276        }
 277    }
 278
 279    /* Virtual page.  */
 280    hit = mmu_translate(env, &lu, address, access_type, mmu_idx);
 281    if (hit) {
 282        vaddr = address & TARGET_PAGE_MASK;
 283        paddr = lu.paddr + vaddr - lu.vaddr;
 284
 285        if (((access_type == MMU_DATA_LOAD) && (lu.prot & PAGE_READ)) ||
 286            ((access_type == MMU_DATA_STORE) && (lu.prot & PAGE_WRITE)) ||
 287            ((access_type == MMU_INST_FETCH) && (lu.prot & PAGE_EXEC))) {
 288            tlb_set_page(cs, vaddr, paddr, lu.prot,
 289                         mmu_idx, TARGET_PAGE_SIZE);
 290            return true;
 291        }
 292
 293        /* Permission violation */
 294        excp = (access_type == MMU_DATA_LOAD ? EXCP_TLBR :
 295                access_type == MMU_DATA_STORE ? EXCP_TLBW : EXCP_TLBX);
 296    }
 297
 298    if (probe) {
 299        return false;
 300    }
 301
 302    if (access_type == MMU_INST_FETCH) {
 303        env->regs[CR_TLBMISC] &= ~CR_TLBMISC_D;
 304    } else {
 305        env->regs[CR_TLBMISC] |= CR_TLBMISC_D;
 306    }
 307    env->regs[CR_PTEADDR] &= CR_PTEADDR_PTBASE_MASK;
 308    env->regs[CR_PTEADDR] |= (address >> 10) & CR_PTEADDR_VPN_MASK;
 309    env->mmu.pteaddr_wr = env->regs[CR_PTEADDR];
 310
 311    cs->exception_index = excp;
 312    env->regs[CR_BADADDR] = address;
 313    cpu_loop_exit_restore(cs, retaddr);
 314}
 315#endif /* !CONFIG_USER_ONLY */
 316