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
  32static void do_exception(Nios2CPU *cpu, uint32_t exception_addr,
  33                         uint32_t tlbmisc_set, bool is_break)
  34{
  35    CPUNios2State *env = &cpu->env;
  36    CPUState *cs = CPU(cpu);
  37    uint32_t old_status = env->ctrl[CR_STATUS];
  38    uint32_t new_status = old_status;
  39
  40    /* With shadow regs, exceptions are always taken into CRS 0. */
  41    new_status &= ~R_CR_STATUS_CRS_MASK;
  42    env->regs = env->shadow_regs[0];
  43
  44    if ((old_status & CR_STATUS_EH) == 0) {
  45        int r_ea = R_EA, cr_es = CR_ESTATUS;
  46
  47        if (is_break) {
  48            r_ea = R_BA;
  49            cr_es = CR_BSTATUS;
  50        }
  51        env->ctrl[cr_es] = old_status;
  52        env->regs[r_ea] = env->pc;
  53
  54        if (cpu->mmu_present) {
  55            new_status |= CR_STATUS_EH;
  56
  57            /*
  58             * There are 4 bits that are always written.
  59             * Explicitly clear them, to be set via the argument.
  60             */
  61            env->ctrl[CR_TLBMISC] &= ~(CR_TLBMISC_D |
  62                                       CR_TLBMISC_PERM |
  63                                       CR_TLBMISC_BAD |
  64                                       CR_TLBMISC_DBL);
  65            env->ctrl[CR_TLBMISC] |= tlbmisc_set;
  66        }
  67
  68        /*
  69         * With shadow regs, and EH == 0, PRS is set from CRS.
  70         * At least, so says Table 3-9, and some other text,
  71         * though Table 3-38 says otherwise.
  72         */
  73        new_status = FIELD_DP32(new_status, CR_STATUS, PRS,
  74                                FIELD_EX32(old_status, CR_STATUS, CRS));
  75    }
  76
  77    new_status &= ~(CR_STATUS_PIE | CR_STATUS_U);
  78
  79    env->ctrl[CR_STATUS] = new_status;
  80    if (!is_break) {
  81        env->ctrl[CR_EXCEPTION] = FIELD_DP32(0, CR_EXCEPTION, CAUSE,
  82                                             cs->exception_index);
  83    }
  84    env->pc = exception_addr;
  85}
  86
  87static void do_iic_irq(Nios2CPU *cpu)
  88{
  89    do_exception(cpu, cpu->exception_addr, 0, false);
  90}
  91
  92static void do_eic_irq(Nios2CPU *cpu)
  93{
  94    CPUNios2State *env = &cpu->env;
  95    uint32_t old_status = env->ctrl[CR_STATUS];
  96    uint32_t new_status = old_status;
  97    uint32_t old_rs = FIELD_EX32(old_status, CR_STATUS, CRS);
  98    uint32_t new_rs = cpu->rrs;
  99
 100    new_status = FIELD_DP32(new_status, CR_STATUS, CRS, new_rs);
 101    new_status = FIELD_DP32(new_status, CR_STATUS, IL, cpu->ril);
 102    new_status = FIELD_DP32(new_status, CR_STATUS, NMI, cpu->rnmi);
 103    new_status &= ~(CR_STATUS_RSIE | CR_STATUS_U);
 104    new_status |= CR_STATUS_IH;
 105
 106    if (!(new_status & CR_STATUS_EH)) {
 107        new_status = FIELD_DP32(new_status, CR_STATUS, PRS, old_rs);
 108        if (new_rs == 0) {
 109            env->ctrl[CR_ESTATUS] = old_status;
 110        } else {
 111            if (new_rs != old_rs) {
 112                old_status |= CR_STATUS_SRS;
 113            }
 114            env->shadow_regs[new_rs][R_SSTATUS] = old_status;
 115        }
 116        env->shadow_regs[new_rs][R_EA] = env->pc;
 117    }
 118
 119    env->ctrl[CR_STATUS] = new_status;
 120    nios2_update_crs(env);
 121
 122    env->pc = cpu->rha;
 123}
 124
 125void nios2_cpu_do_interrupt(CPUState *cs)
 126{
 127    Nios2CPU *cpu = NIOS2_CPU(cs);
 128    CPUNios2State *env = &cpu->env;
 129    uint32_t tlbmisc_set = 0;
 130
 131    if (qemu_loglevel_mask(CPU_LOG_INT)) {
 132        const char *name = NULL;
 133
 134        switch (cs->exception_index) {
 135        case EXCP_IRQ:
 136            name = "interrupt";
 137            break;
 138        case EXCP_TLB_X:
 139        case EXCP_TLB_D:
 140            if (env->ctrl[CR_STATUS] & CR_STATUS_EH) {
 141                name = "TLB MISS (double)";
 142            } else {
 143                name = "TLB MISS (fast)";
 144            }
 145            break;
 146        case EXCP_PERM_R:
 147        case EXCP_PERM_W:
 148        case EXCP_PERM_X:
 149            name = "TLB PERM";
 150            break;
 151        case EXCP_SUPERA_X:
 152        case EXCP_SUPERA_D:
 153            name = "SUPERVISOR (address)";
 154            break;
 155        case EXCP_SUPERI:
 156            name = "SUPERVISOR (insn)";
 157            break;
 158        case EXCP_ILLEGAL:
 159            name = "ILLEGAL insn";
 160            break;
 161        case EXCP_UNALIGN:
 162            name = "Misaligned (data)";
 163            break;
 164        case EXCP_UNALIGND:
 165            name = "Misaligned (destination)";
 166            break;
 167        case EXCP_DIV:
 168            name = "DIV error";
 169            break;
 170        case EXCP_TRAP:
 171            name = "TRAP insn";
 172            break;
 173        case EXCP_BREAK:
 174            name = "BREAK insn";
 175            break;
 176        case EXCP_SEMIHOST:
 177            name = "SEMIHOST insn";
 178            break;
 179        }
 180        if (name) {
 181            qemu_log("%s at pc=0x%08x\n", name, env->pc);
 182        } else {
 183            qemu_log("Unknown exception %d at pc=0x%08x\n",
 184                     cs->exception_index, env->pc);
 185        }
 186    }
 187
 188    switch (cs->exception_index) {
 189    case EXCP_IRQ:
 190        /* Note that PC is advanced for interrupts as well. */
 191        env->pc += 4;
 192        if (cpu->eic_present) {
 193            do_eic_irq(cpu);
 194        } else {
 195            do_iic_irq(cpu);
 196        }
 197        break;
 198
 199    case EXCP_TLB_D:
 200        tlbmisc_set = CR_TLBMISC_D;
 201        /* fall through */
 202    case EXCP_TLB_X:
 203        if (env->ctrl[CR_STATUS] & CR_STATUS_EH) {
 204            tlbmisc_set |= CR_TLBMISC_DBL;
 205            /*
 206             * Normally, we don't write to tlbmisc unless !EH,
 207             * so do it manually for the double-tlb miss exception.
 208             */
 209            env->ctrl[CR_TLBMISC] &= ~(CR_TLBMISC_D |
 210                                       CR_TLBMISC_PERM |
 211                                       CR_TLBMISC_BAD);
 212            env->ctrl[CR_TLBMISC] |= tlbmisc_set;
 213            do_exception(cpu, cpu->exception_addr, 0, false);
 214        } else {
 215            tlbmisc_set |= CR_TLBMISC_WE;
 216            do_exception(cpu, cpu->fast_tlb_miss_addr, tlbmisc_set, false);
 217        }
 218        break;
 219
 220    case EXCP_PERM_R:
 221    case EXCP_PERM_W:
 222        tlbmisc_set = CR_TLBMISC_D;
 223        /* fall through */
 224    case EXCP_PERM_X:
 225        tlbmisc_set |= CR_TLBMISC_PERM;
 226        if (!(env->ctrl[CR_STATUS] & CR_STATUS_EH)) {
 227            tlbmisc_set |= CR_TLBMISC_WE;
 228        }
 229        do_exception(cpu, cpu->exception_addr, tlbmisc_set, false);
 230        break;
 231
 232    case EXCP_SUPERA_D:
 233    case EXCP_UNALIGN:
 234        tlbmisc_set = CR_TLBMISC_D;
 235        /* fall through */
 236    case EXCP_SUPERA_X:
 237    case EXCP_UNALIGND:
 238        tlbmisc_set |= CR_TLBMISC_BAD;
 239        do_exception(cpu, cpu->exception_addr, tlbmisc_set, false);
 240        break;
 241
 242    case EXCP_SUPERI:
 243    case EXCP_ILLEGAL:
 244    case EXCP_DIV:
 245    case EXCP_TRAP:
 246        do_exception(cpu, cpu->exception_addr, 0, false);
 247        break;
 248
 249    case EXCP_BREAK:
 250        do_exception(cpu, cpu->exception_addr, 0, true);
 251        break;
 252
 253    case EXCP_SEMIHOST:
 254        do_nios2_semihosting(env);
 255        break;
 256
 257    default:
 258        cpu_abort(cs, "unhandled exception type=%d\n", cs->exception_index);
 259    }
 260}
 261
 262hwaddr nios2_cpu_get_phys_page_debug(CPUState *cs, vaddr addr)
 263{
 264    Nios2CPU *cpu = NIOS2_CPU(cs);
 265    CPUNios2State *env = &cpu->env;
 266    target_ulong vaddr, paddr = 0;
 267    Nios2MMULookup lu;
 268    unsigned int hit;
 269
 270    if (cpu->mmu_present && (addr < 0xC0000000)) {
 271        hit = mmu_translate(env, &lu, addr, 0, 0);
 272        if (hit) {
 273            vaddr = addr & TARGET_PAGE_MASK;
 274            paddr = lu.paddr + vaddr - lu.vaddr;
 275        } else {
 276            paddr = -1;
 277            qemu_log("cpu_get_phys_page debug MISS: %#" PRIx64 "\n", addr);
 278        }
 279    } else {
 280        paddr = addr & TARGET_PAGE_MASK;
 281    }
 282
 283    return paddr;
 284}
 285
 286void nios2_cpu_do_unaligned_access(CPUState *cs, vaddr addr,
 287                                   MMUAccessType access_type,
 288                                   int mmu_idx, uintptr_t retaddr)
 289{
 290    Nios2CPU *cpu = NIOS2_CPU(cs);
 291    CPUNios2State *env = &cpu->env;
 292
 293    env->ctrl[CR_BADADDR] = addr;
 294    cs->exception_index = EXCP_UNALIGN;
 295    nios2_cpu_loop_exit_advance(env, retaddr);
 296}
 297
 298bool nios2_cpu_tlb_fill(CPUState *cs, vaddr address, int size,
 299                        MMUAccessType access_type, int mmu_idx,
 300                        bool probe, uintptr_t retaddr)
 301{
 302    Nios2CPU *cpu = NIOS2_CPU(cs);
 303    CPUNios2State *env = &cpu->env;
 304    unsigned int excp;
 305    target_ulong vaddr, paddr;
 306    Nios2MMULookup lu;
 307    unsigned int hit;
 308
 309    if (!cpu->mmu_present) {
 310        /* No MMU */
 311        address &= TARGET_PAGE_MASK;
 312        tlb_set_page(cs, address, address, PAGE_BITS,
 313                     mmu_idx, TARGET_PAGE_SIZE);
 314        return true;
 315    }
 316
 317    if (MMU_SUPERVISOR_IDX == mmu_idx) {
 318        if (address >= 0xC0000000) {
 319            /* Kernel physical page - TLB bypassed */
 320            address &= TARGET_PAGE_MASK;
 321            tlb_set_page(cs, address, address, PAGE_BITS,
 322                         mmu_idx, TARGET_PAGE_SIZE);
 323            return true;
 324        }
 325    } else {
 326        if (address >= 0x80000000) {
 327            /* Illegal access from user mode */
 328            if (probe) {
 329                return false;
 330            }
 331            cs->exception_index = (access_type == MMU_INST_FETCH
 332                                   ? EXCP_SUPERA_X : EXCP_SUPERA_D);
 333            env->ctrl[CR_BADADDR] = address;
 334            nios2_cpu_loop_exit_advance(env, retaddr);
 335        }
 336    }
 337
 338    /* Virtual page.  */
 339    hit = mmu_translate(env, &lu, address, access_type, mmu_idx);
 340    if (hit) {
 341        vaddr = address & TARGET_PAGE_MASK;
 342        paddr = lu.paddr + vaddr - lu.vaddr;
 343
 344        if (((access_type == MMU_DATA_LOAD) && (lu.prot & PAGE_READ)) ||
 345            ((access_type == MMU_DATA_STORE) && (lu.prot & PAGE_WRITE)) ||
 346            ((access_type == MMU_INST_FETCH) && (lu.prot & PAGE_EXEC))) {
 347            tlb_set_page(cs, vaddr, paddr, lu.prot,
 348                         mmu_idx, TARGET_PAGE_SIZE);
 349            return true;
 350        }
 351
 352        /* Permission violation */
 353        excp = (access_type == MMU_DATA_LOAD ? EXCP_PERM_R :
 354                access_type == MMU_DATA_STORE ? EXCP_PERM_W : EXCP_PERM_X);
 355    } else {
 356        excp = (access_type == MMU_INST_FETCH ? EXCP_TLB_X: EXCP_TLB_D);
 357    }
 358
 359    if (probe) {
 360        return false;
 361    }
 362
 363    env->ctrl[CR_TLBMISC] = FIELD_DP32(env->ctrl[CR_TLBMISC], CR_TLBMISC, D,
 364                                       access_type != MMU_INST_FETCH);
 365    env->ctrl[CR_PTEADDR] = FIELD_DP32(env->ctrl[CR_PTEADDR], CR_PTEADDR, VPN,
 366                                       address >> TARGET_PAGE_BITS);
 367    env->mmu.pteaddr_wr = env->ctrl[CR_PTEADDR];
 368
 369    cs->exception_index = excp;
 370    env->ctrl[CR_BADADDR] = address;
 371    nios2_cpu_loop_exit_advance(env, retaddr);
 372}
 373