diff mbox

[V8,2/7] nios2: Add architecture emulation support

Message ID 20161231132226.9595-2-marex@denx.de (mailing list archive)
State New, archived
Headers show

Commit Message

Marek Vasut Dec. 31, 2016, 1:22 p.m. UTC
From: Chris Wulff <crwulff@gmail.com>

Add support for emulating Altera NiosII R1 architecture into qemu.
This patch is based on previous work by Chris Wulff from 2012 and
updated to latest mainline QEMU.

Signed-off-by: Marek Vasut <marex@denx.de>
Cc: Chris Wulff <crwulff@gmail.com>
Cc: Jeff Da Silva <jdasilva@altera.com>
Cc: Ley Foon Tan <lftan@altera.com>
Cc: Sandra Loosemore <sandra@codesourcery.com>
Cc: Yves Vandervennet <yvanderv@altera.com>
---
V3: Thorough cleanup, deal with the review comments all over the place
V4: - Use extract32()
    - Fix gen_goto_tb() , suppress tcg_gen_goto_tb()
    - Clean up gen_check_supervisor() helper
    - Use TCGMemOp type for flags
    - Drop jump labels from wrctl/rdctl
    - More TCG cleanup
V5: - Simplify load/store handling
    - Handle loads into R_ZERO from protected page, add comment
V6: - Fix division opcode handling
    - Add missing disas handling
    - V5 review comments cleanup
V7: - Drop newline at the end of file
V8: - Rebase on top of qemu/master
    - Move the target-nios2 to target/nios2
---
 target/nios2/Makefile.objs |   4 +
 target/nios2/cpu.c         | 232 +++++++++++
 target/nios2/cpu.h         | 269 +++++++++++++
 target/nios2/helper.c      | 313 +++++++++++++++
 target/nios2/helper.h      |  27 ++
 target/nios2/mmu.c         | 292 ++++++++++++++
 target/nios2/mmu.h         |  54 +++
 target/nios2/monitor.c     |  35 ++
 target/nios2/op_helper.c   |  47 +++
 target/nios2/translate.c   | 953 +++++++++++++++++++++++++++++++++++++++++++++
 10 files changed, 2226 insertions(+)
 create mode 100644 target/nios2/Makefile.objs
 create mode 100644 target/nios2/cpu.c
 create mode 100644 target/nios2/cpu.h
 create mode 100644 target/nios2/helper.c
 create mode 100644 target/nios2/helper.h
 create mode 100644 target/nios2/mmu.c
 create mode 100644 target/nios2/mmu.h
 create mode 100644 target/nios2/monitor.c
 create mode 100644 target/nios2/op_helper.c
 create mode 100644 target/nios2/translate.c

Comments

Alexander Graf Jan. 16, 2017, 10:21 p.m. UTC | #1
On 31/12/2016 14:22, Marek Vasut wrote:
> From: Chris Wulff <crwulff@gmail.com>
>
> Add support for emulating Altera NiosII R1 architecture into qemu.
> This patch is based on previous work by Chris Wulff from 2012 and
> updated to latest mainline QEMU.
>
> Signed-off-by: Marek Vasut <marex@denx.de>
> Cc: Chris Wulff <crwulff@gmail.com>
> Cc: Jeff Da Silva <jdasilva@altera.com>
> Cc: Ley Foon Tan <lftan@altera.com>
> Cc: Sandra Loosemore <sandra@codesourcery.com>
> Cc: Yves Vandervennet <yvanderv@altera.com>
> ---
> V3: Thorough cleanup, deal with the review comments all over the place
> V4: - Use extract32()
>     - Fix gen_goto_tb() , suppress tcg_gen_goto_tb()
>     - Clean up gen_check_supervisor() helper
>     - Use TCGMemOp type for flags
>     - Drop jump labels from wrctl/rdctl
>     - More TCG cleanup
> V5: - Simplify load/store handling
>     - Handle loads into R_ZERO from protected page, add comment
> V6: - Fix division opcode handling
>     - Add missing disas handling
>     - V5 review comments cleanup
> V7: - Drop newline at the end of file
> V8: - Rebase on top of qemu/master
>     - Move the target-nios2 to target/nios2
> ---
>  target/nios2/Makefile.objs |   4 +
>  target/nios2/cpu.c         | 232 +++++++++++
>  target/nios2/cpu.h         | 269 +++++++++++++
>  target/nios2/helper.c      | 313 +++++++++++++++
>  target/nios2/helper.h      |  27 ++
>  target/nios2/mmu.c         | 292 ++++++++++++++
>  target/nios2/mmu.h         |  54 +++
>  target/nios2/monitor.c     |  35 ++
>  target/nios2/op_helper.c   |  47 +++
>  target/nios2/translate.c   | 953 +++++++++++++++++++++++++++++++++++++++++++++
>  10 files changed, 2226 insertions(+)
>  create mode 100644 target/nios2/Makefile.objs
>  create mode 100644 target/nios2/cpu.c
>  create mode 100644 target/nios2/cpu.h
>  create mode 100644 target/nios2/helper.c
>  create mode 100644 target/nios2/helper.h
>  create mode 100644 target/nios2/mmu.c
>  create mode 100644 target/nios2/mmu.h
>  create mode 100644 target/nios2/monitor.c
>  create mode 100644 target/nios2/op_helper.c
>  create mode 100644 target/nios2/translate.c
>
> diff --git a/target/nios2/Makefile.objs b/target/nios2/Makefile.objs
> new file mode 100644
> index 0000000..2a11c5c
> --- /dev/null
> +++ b/target/nios2/Makefile.objs
> @@ -0,0 +1,4 @@
> +obj-y += translate.o op_helper.o helper.o cpu.o mmu.o
> +obj-$(CONFIG_SOFTMMU) += monitor.o
> +
> +$(obj)/op_helper.o: QEMU_CFLAGS += $(HELPER_CFLAGS)
> diff --git a/target/nios2/cpu.c b/target/nios2/cpu.c
> new file mode 100644
> index 0000000..658d684
> --- /dev/null
> +++ b/target/nios2/cpu.c
> @@ -0,0 +1,232 @@
> +/*
> + * QEMU Nios II CPU
> + *
> + * Copyright (c) 2012 Chris Wulff <crwulff@gmail.com>
> + *
> + * This library is free software; you can redistribute it and/or
> + * modify it under the terms of the GNU Lesser General Public
> + * License as published by the Free Software Foundation; either
> + * version 2.1 of the License, or (at your option) any later version.
> + *
> + * This library is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
> + * Lesser General Public License for more details.
> + *
> + * You should have received a copy of the GNU Lesser General Public
> + * License along with this library; if not, see
> + * <http://www.gnu.org/licenses/lgpl-2.1.html>
> + */
> +
> +#include "qemu/osdep.h"
> +#include "qemu-common.h"
> +#include "qapi/error.h"
> +#include "cpu.h"
> +#include "exec/log.h"
> +#include "exec/gdbstub.h"
> +#include "hw/qdev-properties.h"
> +
> +static void nios2_cpu_set_pc(CPUState *cs, vaddr value)
> +{
> +    Nios2CPU *cpu = NIOS2_CPU(cs);
> +    CPUNios2State *env = &cpu->env;
> +
> +    env->regs[R_PC] = value;
> +}
> +
> +static bool nios2_cpu_has_work(CPUState *cs)
> +{
> +    return cs->interrupt_request & (CPU_INTERRUPT_HARD | CPU_INTERRUPT_NMI);
> +}
> +
> +/* CPUClass::reset() */
> +static void nios2_cpu_reset(CPUState *cs)
> +{
> +    Nios2CPU *cpu = NIOS2_CPU(cs);
> +    Nios2CPUClass *ncc = NIOS2_CPU_GET_CLASS(cpu);
> +    CPUNios2State *env = &cpu->env;
> +
> +    if (qemu_loglevel_mask(CPU_LOG_RESET)) {
> +        qemu_log("CPU Reset (CPU %d)\n", cs->cpu_index);
> +        log_cpu_state(cs, 0);
> +    }
> +
> +    ncc->parent_reset(cs);
> +
> +    tlb_flush(cs, 1);
> +
> +    memset(env->regs, 0, sizeof(uint32_t) * NUM_CORE_REGS);
> +    env->regs[R_PC] = cpu->reset_addr;
> +
> +#if defined(CONFIG_USER_ONLY)
> +    /* Start in user mode with interrupts enabled. */
> +    env->regs[CR_STATUS] = CR_STATUS_U | CR_STATUS_PIE;

So what is the value of CR_STATUS after reset in softmmu land then? 
Random value from before reset? Probably not what you want :).

> +#endif
> +}
> +
> +static void nios2_cpu_initfn(Object *obj)
> +{
> +    CPUState *cs = CPU(obj);
> +    Nios2CPU *cpu = NIOS2_CPU(obj);
> +    CPUNios2State *env = &cpu->env;
> +    static bool tcg_initialized;
> +
> +    cpu->mmu_present = true;
> +    cs->env_ptr = env;
> +
> +#if !defined(CONFIG_USER_ONLY)
> +    mmu_init(&env->mmu);
> +#endif
> +
> +    if (tcg_enabled() && !tcg_initialized) {
> +        tcg_initialized = true;
> +        nios2_tcg_init();
> +    }
> +}
> +
> +Nios2CPU *cpu_nios2_init(const char *cpu_model)
> +{
> +    Nios2CPU *cpu = NIOS2_CPU(object_new(TYPE_NIOS2_CPU));
> +
> +    object_property_set_bool(OBJECT(cpu), true, "realized", NULL);
> +
> +    return cpu;
> +}
> +
> +static void nios2_cpu_realizefn(DeviceState *dev, Error **errp)
> +{
> +    CPUState *cs = CPU(dev);
> +    Nios2CPUClass *ncc = NIOS2_CPU_GET_CLASS(dev);
> +    Error *local_err = NULL;
> +
> +    cpu_exec_realizefn(cs, &local_err);
> +    if (local_err != NULL) {
> +        error_propagate(errp, local_err);
> +        return;
> +    }
> +
> +    qemu_init_vcpu(cs);
> +    cpu_reset(cs);
> +
> +    ncc->parent_realize(dev, errp);
> +}
> +
> +static bool nios2_cpu_exec_interrupt(CPUState *cs, int interrupt_request)
> +{
> +    Nios2CPU *cpu = NIOS2_CPU(cs);
> +    CPUNios2State *env = &cpu->env;
> +
> +    if ((interrupt_request & CPU_INTERRUPT_HARD) &&
> +        (env->regs[CR_STATUS] & CR_STATUS_PIE)) {
> +        cs->exception_index = EXCP_IRQ;
> +        nios2_cpu_do_interrupt(cs);
> +        return true;
> +    }
> +    return false;
> +}
> +
> +
> +static void nios2_cpu_disas_set_info(CPUState *cpu, disassemble_info *info)
> +{
> +    /* NOTE: NiosII R2 is not supported yet. */
> +    info->mach = bfd_arch_nios2;
> +#ifdef TARGET_WORDS_BIGENDIAN
> +    info->print_insn = print_insn_big_nios2;
> +#else
> +    info->print_insn = print_insn_little_nios2;
> +#endif

I take it there is no runtime switch for endianness? Most architectures 
eventually got one and moved to a single default endianness for softmmu 
with swizzling for the "other" one (LE for ARM, BE for ppc).

> +}
> +
> +static int nios2_cpu_gdb_read_register(CPUState *cs, uint8_t *mem_buf, int n)
> +{
> +    Nios2CPU *cpu = NIOS2_CPU(cs);
> +    CPUClass *cc = CPU_GET_CLASS(cs);
> +    CPUNios2State *env = &cpu->env;
> +
> +    if (n > cc->gdb_num_core_regs) {
> +        return 0;
> +    }
> +
> +    if (n < 32) {          /* GP regs */
> +        return gdb_get_reg32(mem_buf, env->regs[n]);
> +    } else if (n == 32) {    /* PC */
> +        return gdb_get_reg32(mem_buf, env->regs[R_PC]);
> +    } else if (n < 49) {     /* Status regs */
> +        return gdb_get_reg32(mem_buf, env->regs[n - 1]);
> +    }
> +
> +    /* Invalid regs */
> +    return 0;
> +}
> +
> +static int nios2_cpu_gdb_write_register(CPUState *cs, uint8_t *mem_buf, int n)
> +{
> +    Nios2CPU *cpu = NIOS2_CPU(cs);
> +    CPUClass *cc = CPU_GET_CLASS(cs);
> +    CPUNios2State *env = &cpu->env;
> +
> +    if (n > cc->gdb_num_core_regs) {
> +        return 0;
> +    }
> +
> +    if (n < 32) {            /* GP regs */
> +        env->regs[n] = ldl_p(mem_buf);
> +    } else if (n == 32) {    /* PC */
> +        env->regs[R_PC] = ldl_p(mem_buf);
> +    } else if (n < 49) {     /* Status regs */
> +        env->regs[n - 1] = ldl_p(mem_buf);
> +    }
> +
> +    return 4;
> +}
> +
> +static Property nios2_properties[] = {
> +    DEFINE_PROP_BOOL("mmu_present", Nios2CPU, mmu_present, true),
> +    DEFINE_PROP_END_OF_LIST(),
> +};
> +
> +
> +static void nios2_cpu_class_init(ObjectClass *oc, void *data)
> +{
> +    DeviceClass *dc = DEVICE_CLASS(oc);
> +    CPUClass *cc = CPU_CLASS(oc);
> +    Nios2CPUClass *ncc = NIOS2_CPU_CLASS(oc);
> +
> +    ncc->parent_realize = dc->realize;
> +    dc->realize = nios2_cpu_realizefn;
> +    dc->props = nios2_properties;
> +    ncc->parent_reset = cc->reset;
> +    cc->reset = nios2_cpu_reset;
> +
> +    cc->has_work = nios2_cpu_has_work;
> +    cc->do_interrupt = nios2_cpu_do_interrupt;
> +    cc->cpu_exec_interrupt = nios2_cpu_exec_interrupt;
> +    cc->dump_state = nios2_cpu_dump_state;
> +    cc->set_pc = nios2_cpu_set_pc;
> +    cc->disas_set_info = nios2_cpu_disas_set_info;
> +#ifdef CONFIG_USER_ONLY
> +    cc->handle_mmu_fault = nios2_cpu_handle_mmu_fault;
> +#else
> +    cc->do_unaligned_access = nios2_cpu_do_unaligned_access;
> +    cc->get_phys_page_debug = nios2_cpu_get_phys_page_debug;
> +#endif
> +    cc->gdb_read_register = nios2_cpu_gdb_read_register;
> +    cc->gdb_write_register = nios2_cpu_gdb_write_register;
> +    cc->gdb_num_core_regs = 49;
> +}
> +
> +static const TypeInfo nios2_cpu_type_info = {
> +    .name = TYPE_NIOS2_CPU,
> +    .parent = TYPE_CPU,
> +    .instance_size = sizeof(Nios2CPU),
> +    .instance_init = nios2_cpu_initfn,
> +    .class_size = sizeof(Nios2CPUClass),
> +    .class_init = nios2_cpu_class_init,
> +};
> +
> +static void nios2_cpu_register_types(void)
> +{
> +    type_register_static(&nios2_cpu_type_info);
> +}
> +
> +type_init(nios2_cpu_register_types)
> diff --git a/target/nios2/cpu.h b/target/nios2/cpu.h
> new file mode 100644
> index 0000000..91e73af
> --- /dev/null
> +++ b/target/nios2/cpu.h
> @@ -0,0 +1,269 @@
> +/*
> + * Altera Nios II virtual CPU header
> + *
> + * Copyright (c) 2012 Chris Wulff <crwulff@gmail.com>
> + *
> + * This library is free software; you can redistribute it and/or
> + * modify it under the terms of the GNU Lesser General Public
> + * License as published by the Free Software Foundation; either
> + * version 2.1 of the License, or (at your option) any later version.
> + *
> + * This library is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
> + * Lesser General Public License for more details.
> + *
> + * You should have received a copy of the GNU Lesser General Public
> + * License along with this library; if not, see
> + * <http://www.gnu.org/licenses/lgpl-2.1.html>
> + */
> +#ifndef CPU_NIOS2_H
> +#define CPU_NIOS2_H
> +
> +#include "qemu/osdep.h"
> +#include "qemu-common.h"
> +
> +#define TARGET_LONG_BITS 32
> +
> +#define CPUArchState struct CPUNios2State
> +
> +#include "exec/cpu-defs.h"
> +#include "fpu/softfloat.h"
> +#include "qom/cpu.h"
> +struct CPUNios2State;
> +typedef struct CPUNios2State CPUNios2State;
> +#if !defined(CONFIG_USER_ONLY)
> +#include "mmu.h"
> +#endif
> +
> +#define TYPE_NIOS2_CPU "nios2-cpu"
> +
> +#define NIOS2_CPU_CLASS(klass) \
> +    OBJECT_CLASS_CHECK(Nios2CPUClass, (klass), TYPE_NIOS2_CPU)
> +#define NIOS2_CPU(obj) \
> +    OBJECT_CHECK(Nios2CPU, (obj), TYPE_NIOS2_CPU)
> +#define NIOS2_CPU_GET_CLASS(obj) \
> +    OBJECT_GET_CLASS(Nios2CPUClass, (obj), TYPE_NIOS2_CPU)
> +
> +/**
> + * Nios2CPUClass:
> + * @parent_reset: The parent class' reset handler.
> + *
> + * A Nios2 CPU model.
> + */
> +typedef struct Nios2CPUClass {
> +    /*< private >*/
> +    CPUClass parent_class;
> +    /*< public >*/
> +
> +    DeviceRealize parent_realize;
> +    void (*parent_reset)(CPUState *cpu);
> +} Nios2CPUClass;
> +
> +#define TARGET_HAS_ICE 1
> +
> +/* Configuration options for Nios II */
> +#define RESET_ADDRESS         0x00000000
> +#define EXCEPTION_ADDRESS     0x00000004
> +#define FAST_TLB_MISS_ADDRESS 0x00000008
> +
> +
> +/* GP regs + CR regs + PC */
> +#define NUM_CORE_REGS (32 + 32 + 1)
> +
> +/* General purpose register aliases */
> +#define R_ZERO   0
> +#define R_AT     1
> +#define R_RET0   2
> +#define R_RET1   3
> +#define R_ARG0   4
> +#define R_ARG1   5
> +#define R_ARG2   6
> +#define R_ARG3   7
> +#define R_ET     24
> +#define R_BT     25
> +#define R_GP     26
> +#define R_SP     27
> +#define R_FP     28
> +#define R_EA     29
> +#define R_BA     30
> +#define R_RA     31
> +
> +/* Control register aliases */
> +#define CR_BASE  32
> +#define CR_STATUS    (CR_BASE + 0)
> +#define   CR_STATUS_PIE  (1 << 0)
> +#define   CR_STATUS_U    (1 << 1)
> +#define   CR_STATUS_EH   (1 << 2)
> +#define   CR_STATUS_IH   (1 << 3)
> +#define   CR_STATUS_IL   (63 << 4)
> +#define   CR_STATUS_CRS  (63 << 10)
> +#define   CR_STATUS_PRS  (63 << 16)
> +#define   CR_STATUS_NMI  (1 << 22)
> +#define   CR_STATUS_RSIE (1 << 23)
> +#define CR_ESTATUS   (CR_BASE + 1)
> +#define CR_BSTATUS   (CR_BASE + 2)
> +#define CR_IENABLE   (CR_BASE + 3)
> +#define CR_IPENDING  (CR_BASE + 4)
> +#define CR_CPUID     (CR_BASE + 5)
> +#define CR_CTL6      (CR_BASE + 6)
> +#define CR_EXCEPTION (CR_BASE + 7)
> +#define CR_PTEADDR   (CR_BASE + 8)
> +#define   CR_PTEADDR_PTBASE_SHIFT 22
> +#define   CR_PTEADDR_PTBASE_MASK  (0x3FF << CR_PTEADDR_PTBASE_SHIFT)
> +#define   CR_PTEADDR_VPN_SHIFT    2
> +#define   CR_PTEADDR_VPN_MASK     (0xFFFFF << CR_PTEADDR_VPN_SHIFT)
> +#define CR_TLBACC    (CR_BASE + 9)
> +#define   CR_TLBACC_IGN_SHIFT 25
> +#define   CR_TLBACC_IGN_MASK  (0x7F << CR_TLBACC_IGN_SHIFT)
> +#define   CR_TLBACC_C         (1 << 24)
> +#define   CR_TLBACC_R         (1 << 23)
> +#define   CR_TLBACC_W         (1 << 22)
> +#define   CR_TLBACC_X         (1 << 21)
> +#define   CR_TLBACC_G         (1 << 20)
> +#define   CR_TLBACC_PFN_MASK  0x000FFFFF
> +#define CR_TLBMISC   (CR_BASE + 10)
> +#define   CR_TLBMISC_WAY_SHIFT 20
> +#define   CR_TLBMISC_WAY_MASK  (0xF << CR_TLBMISC_WAY_SHIFT)
> +#define   CR_TLBMISC_RD        (1 << 19)
> +#define   CR_TLBMISC_WR        (1 << 18)
> +#define   CR_TLBMISC_PID_SHIFT 4
> +#define   CR_TLBMISC_PID_MASK  (0x3FFF << CR_TLBMISC_PID_SHIFT)
> +#define   CR_TLBMISC_DBL       (1 << 3)
> +#define   CR_TLBMISC_BAD       (1 << 2)
> +#define   CR_TLBMISC_PERM      (1 << 1)
> +#define   CR_TLBMISC_D         (1 << 0)
> +#define CR_ENCINJ    (CR_BASE + 11)
> +#define CR_BADADDR   (CR_BASE + 12)
> +#define CR_CONFIG    (CR_BASE + 13)
> +#define CR_MPUBASE   (CR_BASE + 14)
> +#define CR_MPUACC    (CR_BASE + 15)
> +
> +/* Other registers */
> +#define R_PC         64
> +
> +/* Exceptions */
> +#define EXCP_BREAK    -1
> +#define EXCP_RESET    0
> +#define EXCP_PRESET   1
> +#define EXCP_IRQ      2
> +#define EXCP_TRAP     3
> +#define EXCP_UNIMPL   4
> +#define EXCP_ILLEGAL  5
> +#define EXCP_UNALIGN  6
> +#define EXCP_UNALIGND 7
> +#define EXCP_DIV      8
> +#define EXCP_SUPERA   9
> +#define EXCP_SUPERI   10
> +#define EXCP_SUPERD   11
> +#define EXCP_TLBD     12
> +#define EXCP_TLBX     13
> +#define EXCP_TLBR     14
> +#define EXCP_TLBW     15
> +#define EXCP_MPUI     16
> +#define EXCP_MPUD     17
> +
> +#define CPU_INTERRUPT_NMI       CPU_INTERRUPT_TGT_EXT_3
> +
> +#define NB_MMU_MODES 2
> +
> +struct CPUNios2State {
> +    uint32_t regs[NUM_CORE_REGS];
> +
> +#if !defined(CONFIG_USER_ONLY)
> +    Nios2MMU mmu;
> +
> +    uint32_t irq_pending;
> +#endif
> +
> +    CPU_COMMON
> +};
> +
> +/**
> + * Nios2CPU:
> + * @env: #CPUNios2State
> + *
> + * A Nios2 CPU.
> + */
> +typedef struct Nios2CPU {
> +    /*< private >*/
> +    CPUState parent_obj;
> +    /*< public >*/
> +
> +    CPUNios2State env;
> +    bool mmu_present;
> +
> +    /* Addresses that are hard-coded in the FPGA build settings */
> +    uint32_t reset_addr;
> +    uint32_t exception_addr;
> +    uint32_t fast_tlb_miss_addr;
> +} Nios2CPU;
> +
> +static inline Nios2CPU *nios2_env_get_cpu(CPUNios2State *env)
> +{
> +    return NIOS2_CPU(container_of(env, Nios2CPU, env));
> +}
> +
> +#define ENV_GET_CPU(e) CPU(nios2_env_get_cpu(e))
> +
> +#define ENV_OFFSET offsetof(Nios2CPU, env)
> +
> +void nios2_tcg_init(void);
> +Nios2CPU *cpu_nios2_init(const char *cpu_model);
> +void nios2_cpu_do_interrupt(CPUState *cs);
> +int cpu_nios2_signal_handler(int host_signum, void *pinfo, void *puc);
> +void dump_mmu(FILE *f, fprintf_function cpu_fprintf, CPUNios2State *env);
> +void nios2_cpu_dump_state(CPUState *cpu, FILE *f, fprintf_function cpu_fprintf,
> +                          int flags);
> +hwaddr nios2_cpu_get_phys_page_debug(CPUState *cpu, vaddr addr);
> +void nios2_cpu_do_unaligned_access(CPUState *cpu, vaddr addr,
> +                                   MMUAccessType access_type,
> +                                   int mmu_idx, uintptr_t retaddr);
> +
> +qemu_irq *nios2_cpu_pic_init(Nios2CPU *cpu);
> +void nios2_check_interrupts(CPUNios2State *env);
> +
> +#define TARGET_PHYS_ADDR_SPACE_BITS 32
> +#define TARGET_VIRT_ADDR_SPACE_BITS 32
> +
> +#define cpu_init(cpu_model) CPU(cpu_nios2_init(cpu_model))
> +
> +#define cpu_gen_code cpu_nios2_gen_code
> +#define cpu_signal_handler cpu_nios2_signal_handler
> +
> +#define CPU_SAVE_VERSION 1
> +
> +#define TARGET_PAGE_BITS 12
> +
> +/* MMU modes definitions */
> +#define MMU_MODE0_SUFFIX _kernel
> +#define MMU_MODE1_SUFFIX _user
> +#define MMU_SUPERVISOR_IDX  0
> +#define MMU_USER_IDX        1
> +
> +static inline int cpu_mmu_index(CPUNios2State *env, bool ifetch)
> +{
> +    return (env->regs[CR_STATUS] & CR_STATUS_U) ? MMU_USER_IDX :
> +                                                  MMU_SUPERVISOR_IDX;
> +}
> +
> +int nios2_cpu_handle_mmu_fault(CPUState *env, vaddr address,
> +                               int rw, int mmu_idx);
> +
> +static inline int cpu_interrupts_enabled(CPUNios2State *env)
> +{
> +    return env->regs[CR_STATUS] & CR_STATUS_PIE;
> +}
> +
> +#include "exec/cpu-all.h"
> +#include "exec/exec-all.h"
> +
> +static inline void cpu_get_tb_cpu_state(CPUNios2State *env, target_ulong *pc,
> +                                        target_ulong *cs_base, uint32_t *flags)
> +{
> +    *pc = env->regs[R_PC];
> +    *cs_base = 0;
> +    *flags = (env->regs[CR_STATUS] & (CR_STATUS_EH | CR_STATUS_U));
> +}
> +
> +#endif /* CPU_NIOS2_H */
> diff --git a/target/nios2/helper.c b/target/nios2/helper.c
> new file mode 100644
> index 0000000..cd4f353
> --- /dev/null
> +++ b/target/nios2/helper.c
> @@ -0,0 +1,313 @@
> +/*
> + * Altera Nios II helper routines.
> + *
> + * Copyright (c) 2012 Chris Wulff <crwulff@gmail.com>
> + *
> + * This library is free software; you can redistribute it and/or
> + * modify it under the terms of the GNU Lesser General Public
> + * License as published by the Free Software Foundation; either
> + * version 2.1 of the License, or (at your option) any later version.
> + *
> + * This library is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
> + * Lesser General Public License for more details.
> + *
> + * You should have received a copy of the GNU Lesser General Public
> + * License along with this library; if not, see
> + * <http://www.gnu.org/licenses/lgpl-2.1.html>
> + */
> +
> +#include <stdio.h>
> +#include <string.h>
> +#include <assert.h>
> +
> +#include "cpu.h"
> +#include "qemu/osdep.h"
> +#include "qemu/host-utils.h"
> +#include "qapi/error.h"
> +#include "exec/exec-all.h"
> +#include "exec/log.h"
> +#include "exec/helper-proto.h"
> +
> +#if defined(CONFIG_USER_ONLY)
> +
> +void nios2_cpu_do_interrupt(CPUState *cs)
> +{
> +    Nios2CPU *cpu = NIOS2_CPU(cs);
> +    CPUNios2State *env = &cpu->env;
> +    cs->exception_index = -1;
> +    env->regs[R_EA] = env->regs[R_PC] + 4;
> +}
> +
> +int nios2_cpu_handle_mmu_fault(CPUState *cs, vaddr address, int rw, int mmu_idx)
> +{
> +    cs->exception_index = 0xaa;
> +    /* Page 0x1000 is kuser helper */
> +    if (address < 0x1000 || address >= 0x2000) {
> +        cpu_dump_state(cs, stderr, fprintf, 0);
> +    }
> +    return 1;
> +}
> +
> +#else /* !CONFIG_USER_ONLY */
> +
> +void nios2_cpu_do_interrupt(CPUState *cs)
> +{
> +    Nios2CPU *cpu = NIOS2_CPU(cs);
> +    CPUNios2State *env = &cpu->env;
> +
> +    switch (cs->exception_index) {
> +    case EXCP_IRQ:
> +        assert(env->regs[CR_STATUS] & CR_STATUS_PIE);
> +
> +        qemu_log_mask(CPU_LOG_INT, "interrupt at pc=%x\n", env->regs[R_PC]);
> +
> +        env->regs[CR_ESTATUS] = env->regs[CR_STATUS];
> +        env->regs[CR_STATUS] |= CR_STATUS_IH;
> +        env->regs[CR_STATUS] &= ~(CR_STATUS_PIE | CR_STATUS_U);
> +
> +        env->regs[CR_EXCEPTION] &= ~(0x1F << 2);
> +        env->regs[CR_EXCEPTION] |= (cs->exception_index & 0x1F) << 2;
> +
> +        env->regs[R_EA] = env->regs[R_PC] + 4;
> +        env->regs[R_PC] = cpu->exception_addr;
> +        break;
> +
> +    case EXCP_TLBD:
> +        if ((env->regs[CR_STATUS] & CR_STATUS_EH) == 0) {
> +            qemu_log_mask(CPU_LOG_INT, "TLB MISS (fast) at pc=%x\n",
> +                          env->regs[R_PC]);
> +
> +            /* Fast TLB miss */
> +            /* Variation from the spec. Table 3-35 of the cpu reference shows
> +             * estatus not being changed for TLB miss but this appears to
> +             * be incorrect. */
> +            env->regs[CR_ESTATUS] = env->regs[CR_STATUS];
> +            env->regs[CR_STATUS] |= CR_STATUS_EH;
> +            env->regs[CR_STATUS] &= ~(CR_STATUS_PIE | CR_STATUS_U);
> +
> +            env->regs[CR_EXCEPTION] &= ~(0x1F << 2);
> +            env->regs[CR_EXCEPTION] |= (cs->exception_index & 0x1F) << 2;
> +
> +            env->regs[CR_TLBMISC] &= ~CR_TLBMISC_DBL;
> +            env->regs[CR_TLBMISC] |= CR_TLBMISC_WR;
> +
> +            env->regs[R_EA] = env->regs[R_PC] + 4;
> +            env->regs[R_PC] = cpu->fast_tlb_miss_addr;
> +        } else {
> +            qemu_log_mask(CPU_LOG_INT, "TLB MISS (double) at pc=%x\n",
> +                          env->regs[R_PC]);
> +
> +            /* Double TLB miss */
> +            env->regs[CR_STATUS] |= CR_STATUS_EH;
> +            env->regs[CR_STATUS] &= ~(CR_STATUS_PIE | CR_STATUS_U);
> +
> +            env->regs[CR_EXCEPTION] &= ~(0x1F << 2);
> +            env->regs[CR_EXCEPTION] |= (cs->exception_index & 0x1F) << 2;
> +
> +            env->regs[CR_TLBMISC] |= CR_TLBMISC_DBL;
> +
> +            env->regs[R_PC] = cpu->exception_addr;
> +        }
> +        break;
> +
> +    case EXCP_TLBR:
> +    case EXCP_TLBW:
> +    case EXCP_TLBX:
> +        qemu_log_mask(CPU_LOG_INT, "TLB PERM at pc=%x\n", env->regs[R_PC]);
> +
> +        env->regs[CR_ESTATUS] = env->regs[CR_STATUS];
> +        env->regs[CR_STATUS] |= CR_STATUS_EH;
> +        env->regs[CR_STATUS] &= ~(CR_STATUS_PIE | CR_STATUS_U);
> +
> +        env->regs[CR_EXCEPTION] &= ~(0x1F << 2);
> +        env->regs[CR_EXCEPTION] |= (cs->exception_index & 0x1F) << 2;
> +
> +        if ((env->regs[CR_STATUS] & CR_STATUS_EH) == 0) {
> +            env->regs[CR_TLBMISC] |= CR_TLBMISC_WR;
> +        }
> +
> +        env->regs[R_EA] = env->regs[R_PC] + 4;
> +        env->regs[R_PC] = cpu->exception_addr;
> +        break;
> +
> +    case EXCP_SUPERA:
> +    case EXCP_SUPERI:
> +    case EXCP_SUPERD:
> +        qemu_log_mask(CPU_LOG_INT, "SUPERVISOR exception at pc=%x\n",
> +                      env->regs[R_PC]);
> +
> +        if ((env->regs[CR_STATUS] & CR_STATUS_EH) == 0) {
> +            env->regs[CR_ESTATUS] = env->regs[CR_STATUS];
> +            env->regs[R_EA] = env->regs[R_PC] + 4;
> +        }
> +
> +        env->regs[CR_STATUS] |= CR_STATUS_EH;
> +        env->regs[CR_STATUS] &= ~(CR_STATUS_PIE | CR_STATUS_U);
> +
> +        env->regs[CR_EXCEPTION] &= ~(0x1F << 2);
> +        env->regs[CR_EXCEPTION] |= (cs->exception_index & 0x1F) << 2;
> +
> +        env->regs[R_PC] = cpu->exception_addr;
> +        break;
> +
> +    case EXCP_ILLEGAL:
> +    case EXCP_TRAP:
> +        qemu_log_mask(CPU_LOG_INT, "TRAP exception at pc=%x\n",
> +                      env->regs[R_PC]);
> +
> +        if ((env->regs[CR_STATUS] & CR_STATUS_EH) == 0) {
> +            env->regs[CR_ESTATUS] = env->regs[CR_STATUS];
> +            env->regs[R_EA] = env->regs[R_PC] + 4;
> +        }
> +
> +        env->regs[CR_STATUS] |= CR_STATUS_EH;
> +        env->regs[CR_STATUS] &= ~(CR_STATUS_PIE | CR_STATUS_U);
> +
> +        env->regs[CR_EXCEPTION] &= ~(0x1F << 2);
> +        env->regs[CR_EXCEPTION] |= (cs->exception_index & 0x1F) << 2;
> +
> +        env->regs[R_PC] = cpu->exception_addr;
> +        break;
> +
> +    case EXCP_BREAK:
> +        if ((env->regs[CR_STATUS] & CR_STATUS_EH) == 0) {
> +            env->regs[CR_BSTATUS] = env->regs[CR_STATUS];
> +            env->regs[R_BA] = env->regs[R_PC] + 4;
> +        }
> +
> +        env->regs[CR_STATUS] |= CR_STATUS_EH;
> +        env->regs[CR_STATUS] &= ~(CR_STATUS_PIE | CR_STATUS_U);
> +
> +        env->regs[CR_EXCEPTION] &= ~(0x1F << 2);
> +        env->regs[CR_EXCEPTION] |= (cs->exception_index & 0x1F) << 2;
> +
> +        env->regs[R_PC] = cpu->exception_addr;
> +        break;
> +
> +    default:
> +        cpu_abort(cs, "unhandled exception type=%d\n",
> +                  cs->exception_index);
> +        break;
> +    }
> +}
> +
> +static int cpu_nios2_handle_virtual_page(
> +    CPUState *cs, target_ulong address, int rw, int mmu_idx)
> +{
> +    Nios2CPU *cpu = NIOS2_CPU(cs);
> +    CPUNios2State *env = &cpu->env;
> +    target_ulong vaddr, paddr;
> +    Nios2MMULookup lu;
> +    unsigned int hit;
> +    hit = mmu_translate(env, &lu, address, rw, mmu_idx);
> +    if (hit) {
> +        vaddr = address & TARGET_PAGE_MASK;
> +        paddr = lu.paddr + vaddr - lu.vaddr;
> +
> +        if (((rw == 0) && (lu.prot & PAGE_READ)) ||
> +            ((rw == 1) && (lu.prot & PAGE_WRITE)) ||
> +            ((rw == 2) && (lu.prot & PAGE_EXEC))) {
> +
> +            tlb_set_page(cs, vaddr, paddr, lu.prot,
> +                         mmu_idx, TARGET_PAGE_SIZE);
> +            return 0;
> +        } else {
> +            /* Permission violation */
> +            cs->exception_index = (rw == 0) ? EXCP_TLBR :
> +                                               ((rw == 1) ? EXCP_TLBW :
> +                                                            EXCP_TLBX);
> +        }
> +    } else {
> +        cs->exception_index = EXCP_TLBD;
> +    }
> +
> +    if (rw == 2) {
> +        env->regs[CR_TLBMISC] &= ~CR_TLBMISC_D;
> +    } else {
> +        env->regs[CR_TLBMISC] |= CR_TLBMISC_D;
> +    }
> +    env->regs[CR_PTEADDR] &= CR_PTEADDR_PTBASE_MASK;
> +    env->regs[CR_PTEADDR] |= (address >> 10) & CR_PTEADDR_VPN_MASK;
> +    env->mmu.pteaddr_wr = env->regs[CR_PTEADDR];
> +    env->regs[CR_BADADDR] = address;
> +    return 1;
> +}
> +
> +int nios2_cpu_handle_mmu_fault(CPUState *cs, vaddr address, int rw, int mmu_idx)
> +{
> +    Nios2CPU *cpu = NIOS2_CPU(cs);
> +    CPUNios2State *env = &cpu->env;
> +
> +    if (cpu->mmu_present) {
> +        if (MMU_SUPERVISOR_IDX == mmu_idx) {
> +            if (address >= 0xC0000000) {
> +                /* Kernel physical page - TLB bypassed */
> +                address &= TARGET_PAGE_MASK;
> +                tlb_set_page(cs, address, address, PAGE_BITS,
> +                             mmu_idx, TARGET_PAGE_SIZE);
> +            } else if (address >= 0x80000000) {
> +                /* Kernel virtual page */
> +                return cpu_nios2_handle_virtual_page(cs, address, rw, mmu_idx);
> +            } else {
> +                /* User virtual page */
> +                return cpu_nios2_handle_virtual_page(cs, address, rw, mmu_idx);
> +            }
> +        } else {
> +            if (address >= 0x80000000) {
> +                /* Illegal access from user mode */
> +                cs->exception_index = EXCP_SUPERA;
> +                env->regs[CR_BADADDR] = address;
> +                return 1;
> +            } else {
> +                /* User virtual page */
> +                return cpu_nios2_handle_virtual_page(cs, address, rw, mmu_idx);
> +            }
> +        }
> +    } else {
> +        /* No MMU */
> +        address &= TARGET_PAGE_MASK;
> +        tlb_set_page(cs, address, address, PAGE_BITS,
> +                     mmu_idx, TARGET_PAGE_SIZE);
> +    }
> +
> +    return 0;
> +}
> +
> +hwaddr nios2_cpu_get_phys_page_debug(CPUState *cs, vaddr addr)
> +{
> +    Nios2CPU *cpu = NIOS2_CPU(cs);
> +    CPUNios2State *env = &cpu->env;
> +    target_ulong vaddr, paddr = 0;
> +    Nios2MMULookup lu;
> +    unsigned int hit;
> +
> +    if (cpu->mmu_present && (addr < 0xC0000000)) {
> +        hit = mmu_translate(env, &lu, addr, 0, 0);
> +        if (hit) {
> +            vaddr = addr & TARGET_PAGE_MASK;
> +            paddr = lu.paddr + vaddr - lu.vaddr;
> +        } else {
> +            paddr = -1;
> +            qemu_log("cpu_get_phys_page debug MISS: %08lX\n", addr);
> +        }
> +    } else {
> +        paddr = addr & TARGET_PAGE_MASK;
> +    }
> +
> +    return paddr;
> +}
> +
> +void nios2_cpu_do_unaligned_access(CPUState *cs, vaddr addr,
> +                                   MMUAccessType access_type,
> +                                   int mmu_idx, uintptr_t retaddr)
> +{
> +    Nios2CPU *cpu = NIOS2_CPU(cs);
> +    CPUNios2State *env = &cpu->env;
> +
> +    env->regs[CR_BADADDR] = addr;
> +    env->regs[CR_EXCEPTION] = EXCP_UNALIGN << 2;
> +    helper_raise_exception(env, EXCP_UNALIGN);
> +}
> +#endif /* !CONFIG_USER_ONLY */
> diff --git a/target/nios2/helper.h b/target/nios2/helper.h
> new file mode 100644
> index 0000000..b86a2f2
> --- /dev/null
> +++ b/target/nios2/helper.h
> @@ -0,0 +1,27 @@
> +/*
> + * Altera Nios II helper routines header.
> + *
> + * Copyright (c) 2012 Chris Wulff <crwulff@gmail.com>
> + *
> + * This library is free software; you can redistribute it and/or
> + * modify it under the terms of the GNU Lesser General Public
> + * License as published by the Free Software Foundation; either
> + * version 2.1 of the License, or (at your option) any later version.
> + *
> + * This library is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
> + * Lesser General Public License for more details.
> + *
> + * You should have received a copy of the GNU Lesser General Public
> + * License along with this library; if not, see
> + * <http://www.gnu.org/licenses/lgpl-2.1.html>
> + */
> +
> +DEF_HELPER_2(raise_exception, void, env, i32)
> +
> +#if !defined(CONFIG_USER_ONLY)
> +DEF_HELPER_2(mmu_read, i32, env, i32)
> +DEF_HELPER_3(mmu_write, void, env, i32, i32)
> +DEF_HELPER_1(check_interrupts, void, env)
> +#endif
> diff --git a/target/nios2/mmu.c b/target/nios2/mmu.c
> new file mode 100644
> index 0000000..875fbb0
> --- /dev/null
> +++ b/target/nios2/mmu.c
> @@ -0,0 +1,292 @@
> +/*
> + * Altera Nios II MMU emulation for qemu.
> + *
> + * Copyright (C) 2012 Chris Wulff <crwulff@gmail.com>
> + *
> + * This library is free software; you can redistribute it and/or
> + * modify it under the terms of the GNU Lesser General Public
> + * License as published by the Free Software Foundation; either
> + * version 2.1 of the License, or (at your option) any later version.
> + *
> + * This library is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
> + * Lesser General Public License for more details.
> + *
> + * You should have received a copy of the GNU Lesser General Public
> + * License along with this library; if not, see
> + * <http://www.gnu.org/licenses/lgpl-2.1.html>
> + */
> +
> +#include "qemu/osdep.h"
> +#include "qemu-common.h"
> +#include "cpu.h"
> +#include "exec/exec-all.h"
> +#include "mmu.h"
> +
> +#if !defined(CONFIG_USER_ONLY)
> +
> +/* Define this to enable MMU debug messages */
> +/* #define DEBUG_MMU */
> +
> +#ifdef DEBUG_MMU
> +#define MMU_LOG(x) x
> +#else
> +#define MMU_LOG(x)
> +#endif
> +
> +void tlb_fill(CPUState *cs, target_ulong addr, MMUAccessType access_type,
> +              int mmu_idx, uintptr_t retaddr)
> +{
> +    int ret;
> +
> +    ret = nios2_cpu_handle_mmu_fault(cs, addr, access_type, mmu_idx);
> +    if (unlikely(ret)) {
> +        if (retaddr) {
> +            /* now we have a real cpu fault */
> +            cpu_restore_state(cs, retaddr);
> +        }
> +        cpu_loop_exit(cs);
> +    }
> +}
> +
> +uint32_t mmu_read(CPUNios2State *env, uint32_t rn)
> +{
> +    switch (rn) {
> +    case CR_TLBACC:
> +        MMU_LOG(qemu_log("TLBACC READ %08X\n", env->regs[rn]));
> +        break;
> +
> +    case CR_TLBMISC:
> +        MMU_LOG(qemu_log("TLBMISC READ %08X\n", env->regs[rn]));
> +        break;
> +
> +    case CR_PTEADDR:
> +        MMU_LOG(qemu_log("PTEADDR READ %08X\n", env->regs[rn]));
> +        break;
> +
> +    default:
> +        break;
> +    }
> +    return env->regs[rn];

This function should get split into a logging function (which is a 
helper and translated in conditionally depending on your debug define) 
and a TCG register for the actual read.

> +}
> +
> +/* rw - 0 = read, 1 = write, 2 = fetch.  */
> +unsigned int mmu_translate(CPUNios2State *env,
> +                           Nios2MMULookup *lu,
> +                           target_ulong vaddr, int rw, int mmu_idx)
> +{
> +    int pid = (env->mmu.tlbmisc_wr & CR_TLBMISC_PID_MASK) >> 4;
> +    int vpn = vaddr >> 12;
> +
> +    MMU_LOG(qemu_log("mmu_translate vaddr %08X, pid %08X, vpn %08X\n",
> +                     vaddr, pid, vpn));
> +
> +    int way;
> +    for (way = 0; way < env->mmu.tlb_num_ways; way++) {
> +
> +        Nios2TLBEntry *entry =
> +            &env->mmu.tlb[(way * env->mmu.tlb_num_ways) +
> +                          (vpn & env->mmu.tlb_entry_mask)];
> +
> +        MMU_LOG(qemu_log("TLB[%d] TAG %08X, VPN %08X\n",
> +                         (way * env->mmu.tlb_num_ways) +
> +                         (vpn & env->mmu.tlb_entry_mask),
> +                         entry->tag, (entry->tag >> 12)));
> +
> +        if (((entry->tag >> 12) != vpn) ||
> +            (((entry->tag & (1 << 11)) == 0) &&
> +            ((entry->tag & ((1 << env->mmu.pid_bits) - 1)) != pid))) {
> +            continue;
> +        }
> +        lu->vaddr = vaddr & TARGET_PAGE_MASK;
> +        lu->paddr = (entry->data & CR_TLBACC_PFN_MASK) << TARGET_PAGE_BITS;
> +        lu->prot = ((entry->data & CR_TLBACC_R) ? PAGE_READ : 0) |
> +                   ((entry->data & CR_TLBACC_W) ? PAGE_WRITE : 0) |
> +                   ((entry->data & CR_TLBACC_X) ? PAGE_EXEC : 0);
> +
> +        MMU_LOG(qemu_log("HIT TLB[%d] %08X %08X %08X\n",
> +                         (way * env->mmu.tlb_num_ways) +
> +                         (vpn & env->mmu.tlb_entry_mask),
> +                         lu->vaddr, lu->paddr, lu->prot));
> +        return 1;
> +    }
> +    return 0;
> +}
> +
> +static void mmu_flush_pid(CPUNios2State *env, uint32_t pid)
> +{
> +    CPUState *cs = ENV_GET_CPU(env);
> +    int idx;
> +    MMU_LOG(qemu_log("TLB Flush PID %d\n", pid));
> +
> +    for (idx = 0; idx < env->mmu.tlb_num_entries; idx++) {
> +        Nios2TLBEntry *entry = &env->mmu.tlb[idx];
> +
> +        MMU_LOG(qemu_log("TLB[%d] => %08X %08X\n",
> +                         idx, entry->tag, entry->data));
> +
> +        if ((entry->tag & (1 << 10)) && (!(entry->tag & (1 << 11))) &&
> +            ((entry->tag & ((1 << env->mmu.pid_bits) - 1)) == pid)) {
> +            uint32_t vaddr = entry->tag & TARGET_PAGE_MASK;
> +
> +            MMU_LOG(qemu_log("TLB Flush Page %08X\n", vaddr));
> +
> +            tlb_flush_page(cs, vaddr);
> +        }
> +    }
> +}
> +
> +void mmu_write(CPUNios2State *env, uint32_t rn, uint32_t v)
> +{
> +    CPUState *cs = ENV_GET_CPU(env);
> +
> +    MMU_LOG(qemu_log("mmu_write %08X = %08X\n", rn, v));
> +
> +    switch (rn) {
> +    case CR_TLBACC:
> +        MMU_LOG(qemu_log("TLBACC: IG %02X, FLAGS %c%c%c%c%c, PFN %05X\n",
> +                         v >> CR_TLBACC_IGN_SHIFT,
> +                         (v & CR_TLBACC_C) ? 'C' : '.',
> +                         (v & CR_TLBACC_R) ? 'R' : '.',
> +                         (v & CR_TLBACC_W) ? 'W' : '.',
> +                         (v & CR_TLBACC_X) ? 'X' : '.',
> +                         (v & CR_TLBACC_G) ? 'G' : '.',
> +                         v & CR_TLBACC_PFN_MASK));
> +
> +        /* if tlbmisc.WE == 1 then trigger a TLB write on writes to TLBACC */
> +        if (env->regs[CR_TLBMISC] & CR_TLBMISC_WR) {
> +            int way = (env->regs[CR_TLBMISC] >> CR_TLBMISC_WAY_SHIFT);
> +            int vpn = (env->mmu.pteaddr_wr & CR_PTEADDR_VPN_MASK) >> 2;
> +            int pid = (env->mmu.tlbmisc_wr & CR_TLBMISC_PID_MASK) >> 4;
> +            int g = (v & CR_TLBACC_G) ? 1 : 0;
> +            int valid = ((vpn & CR_TLBACC_PFN_MASK) < 0xC0000) ? 1 : 0;
> +            Nios2TLBEntry *entry =
> +                &env->mmu.tlb[(way * env->mmu.tlb_num_ways) +
> +                              (vpn & env->mmu.tlb_entry_mask)];
> +            uint32_t newTag = (vpn << 12) | (g << 11) | (valid << 10) | pid;
> +            uint32_t newData = v & (CR_TLBACC_C | CR_TLBACC_R | CR_TLBACC_W |
> +                                    CR_TLBACC_X | CR_TLBACC_PFN_MASK);
> +
> +            if ((entry->tag != newTag) || (entry->data != newData)) {
> +                if (entry->tag & (1 << 10)) {
> +                    /* Flush existing entry */
> +                    MMU_LOG(qemu_log("TLB Flush Page (OLD) %08X\n",
> +                                     entry->tag & TARGET_PAGE_MASK));
> +                    tlb_flush_page(cs, entry->tag & TARGET_PAGE_MASK);
> +                }
> +                entry->tag = newTag;
> +                entry->data = newData;
> +                MMU_LOG(qemu_log("TLB[%d] = %08X %08X\n",
> +                                 (way * env->mmu.tlb_num_ways) +
> +                                 (vpn & env->mmu.tlb_entry_mask),
> +                                 entry->tag, entry->data));
> +            }
> +            /* Auto-increment tlbmisc.WAY */
> +            env->regs[CR_TLBMISC] =
> +                (env->regs[CR_TLBMISC] & ~CR_TLBMISC_WAY_MASK) |
> +                (((way + 1) & (env->mmu.tlb_num_ways - 1)) <<
> +                 CR_TLBMISC_WAY_SHIFT);
> +        }
> +
> +        /* Writes to TLBACC don't change the read-back value */
> +        env->mmu.tlbacc_wr = v;
> +        break;
> +
> +    case CR_TLBMISC:
> +        MMU_LOG(qemu_log("TLBMISC: WAY %X, FLAGS %c%c%c%c%c%c, PID %04X\n",
> +                         v >> CR_TLBMISC_WAY_SHIFT,
> +                         (v & CR_TLBMISC_RD) ? 'R' : '.',
> +                         (v & CR_TLBMISC_WR) ? 'W' : '.',
> +                         (v & CR_TLBMISC_DBL) ? '2' : '.',
> +                         (v & CR_TLBMISC_BAD) ? 'B' : '.',
> +                         (v & CR_TLBMISC_PERM) ? 'P' : '.',
> +                         (v & CR_TLBMISC_D) ? 'D' : '.',
> +                         (v & CR_TLBMISC_PID_MASK) >> 4));
> +
> +        if ((v & CR_TLBMISC_PID_MASK) !=
> +            (env->mmu.tlbmisc_wr & CR_TLBMISC_PID_MASK)) {
> +            mmu_flush_pid(env, (env->mmu.tlbmisc_wr & CR_TLBMISC_PID_MASK) >>
> +                               CR_TLBMISC_PID_SHIFT);
> +        }
> +        /* if tlbmisc.RD == 1 then trigger a TLB read on writes to TLBMISC */
> +        if (v & CR_TLBMISC_RD) {
> +            int way = (v >> CR_TLBMISC_WAY_SHIFT);
> +            int vpn = (env->mmu.pteaddr_wr & CR_PTEADDR_VPN_MASK) >> 2;
> +            Nios2TLBEntry *entry =
> +                &env->mmu.tlb[(way * env->mmu.tlb_num_ways) +
> +                              (vpn & env->mmu.tlb_entry_mask)];
> +
> +            env->regs[CR_TLBACC] &= CR_TLBACC_IGN_MASK;
> +            env->regs[CR_TLBACC] |= entry->data;
> +            env->regs[CR_TLBACC] |= (entry->tag & (1 << 11)) ? CR_TLBACC_G : 0;
> +            env->regs[CR_TLBMISC] =
> +                (v & ~CR_TLBMISC_PID_MASK) |
> +                ((entry->tag & ((1 << env->mmu.pid_bits) - 1)) <<
> +                 CR_TLBMISC_PID_SHIFT);
> +            env->regs[CR_PTEADDR] &= ~CR_PTEADDR_VPN_MASK;
> +            env->regs[CR_PTEADDR] |= (entry->tag >> 12) << CR_PTEADDR_VPN_SHIFT;
> +            MMU_LOG(qemu_log("TLB READ way %d, vpn %05X, tag %08X, data %08X, "
> +                             "tlbacc %08X, tlbmisc %08X, pteaddr %08X\n",
> +                             way, vpn, entry->tag, entry->data,
> +                             env->regs[CR_TLBACC], env->regs[CR_TLBMISC],
> +                             env->regs[CR_PTEADDR]));
> +        } else {
> +            env->regs[CR_TLBMISC] = v;
> +        }
> +
> +        env->mmu.tlbmisc_wr = v;
> +        break;
> +
> +    case CR_PTEADDR:
> +        MMU_LOG(qemu_log("PTEADDR: PTBASE %03X, VPN %05X\n",
> +                         v >> CR_PTEADDR_PTBASE_SHIFT,
> +                         (v & CR_PTEADDR_VPN_MASK) >> CR_PTEADDR_VPN_SHIFT));
> +
> +        /* Writes to PTEADDR don't change the read-back VPN value */
> +        env->regs[CR_PTEADDR] = (v & ~CR_PTEADDR_VPN_MASK) |
> +                                (env->regs[CR_PTEADDR] & CR_PTEADDR_VPN_MASK);
> +        env->mmu.pteaddr_wr = v;
> +        break;
> +
> +    default:
> +        break;
> +    }
> +}
> +
> +void mmu_init(Nios2MMU *mmu)
> +{
> +    MMU_LOG(qemu_log("mmu_init\n"));
> +
> +    mmu->pid_bits = 8;          /* TODO: get this from ALTR,pid-num-bits */
> +    mmu->tlb_num_ways = 16;     /* TODO: get this from ALTR,tlb-num-ways */
> +    mmu->tlb_num_entries = 256; /* TODO: get this from ALTR,tlb-num-entries */

If they're going to get set from outside, you probably want to make 
these QOM properties on the CPU objects. That way your board file can 
modify them depending on dt properties (which is what you're referring 
to I suppose?)


Alex
Sandra Loosemore Jan. 16, 2017, 11:31 p.m. UTC | #2
On 01/16/2017 03:21 PM, Alexander Graf wrote:

>> +static void nios2_cpu_disas_set_info(CPUState *cpu, disassemble_info
>> *info)
>> +{
>> +    /* NOTE: NiosII R2 is not supported yet. */
>> +    info->mach = bfd_arch_nios2;
>> +#ifdef TARGET_WORDS_BIGENDIAN
>> +    info->print_insn = print_insn_big_nios2;
>> +#else
>> +    info->print_insn = print_insn_little_nios2;
>> +#endif
>
> I take it there is no runtime switch for endianness? Most architectures
> eventually got one and moved to a single default endianness for softmmu
> with swizzling for the "other" one (LE for ARM, BE for ppc).

Maybe QEMU should just error out if configured for big-endianness on 
this target.  Per the published Nios II Processor Reference Handbook, 
"The Nios II architecture uses little-endian byte ordering."  When I was 
working on preparing the nios2 binutils patches for submission, Altera 
asked me to retain the big-endian hooks because they didn't want to rule 
out officially supporting that feature.  I had no way to test anything 
big-endian, of course.

-Sandra
Marek Vasut Jan. 17, 2017, 12:18 a.m. UTC | #3
On 01/16/2017 11:21 PM, Alexander Graf wrote:
> 
> 
> On 31/12/2016 14:22, Marek Vasut wrote:
>> From: Chris Wulff <crwulff@gmail.com>
>>
>> Add support for emulating Altera NiosII R1 architecture into qemu.
>> This patch is based on previous work by Chris Wulff from 2012 and
>> updated to latest mainline QEMU.
>>
>> Signed-off-by: Marek Vasut <marex@denx.de>
>> Cc: Chris Wulff <crwulff@gmail.com>
>> Cc: Jeff Da Silva <jdasilva@altera.com>
>> Cc: Ley Foon Tan <lftan@altera.com>
>> Cc: Sandra Loosemore <sandra@codesourcery.com>
>> Cc: Yves Vandervennet <yvanderv@altera.com>
>> ---
>> V3: Thorough cleanup, deal with the review comments all over the place
>> V4: - Use extract32()
>>     - Fix gen_goto_tb() , suppress tcg_gen_goto_tb()
>>     - Clean up gen_check_supervisor() helper
>>     - Use TCGMemOp type for flags
>>     - Drop jump labels from wrctl/rdctl
>>     - More TCG cleanup
>> V5: - Simplify load/store handling
>>     - Handle loads into R_ZERO from protected page, add comment
>> V6: - Fix division opcode handling
>>     - Add missing disas handling
>>     - V5 review comments cleanup
>> V7: - Drop newline at the end of file
>> V8: - Rebase on top of qemu/master
>>     - Move the target-nios2 to target/nios2
>> ---
>>  target/nios2/Makefile.objs |   4 +
>>  target/nios2/cpu.c         | 232 +++++++++++
>>  target/nios2/cpu.h         | 269 +++++++++++++
>>  target/nios2/helper.c      | 313 +++++++++++++++
>>  target/nios2/helper.h      |  27 ++
>>  target/nios2/mmu.c         | 292 ++++++++++++++
>>  target/nios2/mmu.h         |  54 +++
>>  target/nios2/monitor.c     |  35 ++
>>  target/nios2/op_helper.c   |  47 +++
>>  target/nios2/translate.c   | 953
>> +++++++++++++++++++++++++++++++++++++++++++++
>>  10 files changed, 2226 insertions(+)
>>  create mode 100644 target/nios2/Makefile.objs
>>  create mode 100644 target/nios2/cpu.c
>>  create mode 100644 target/nios2/cpu.h
>>  create mode 100644 target/nios2/helper.c
>>  create mode 100644 target/nios2/helper.h
>>  create mode 100644 target/nios2/mmu.c
>>  create mode 100644 target/nios2/mmu.h
>>  create mode 100644 target/nios2/monitor.c
>>  create mode 100644 target/nios2/op_helper.c
>>  create mode 100644 target/nios2/translate.c
>>
>> diff --git a/target/nios2/Makefile.objs b/target/nios2/Makefile.objs
>> new file mode 100644
>> index 0000000..2a11c5c
>> --- /dev/null
>> +++ b/target/nios2/Makefile.objs
>> @@ -0,0 +1,4 @@
>> +obj-y += translate.o op_helper.o helper.o cpu.o mmu.o
>> +obj-$(CONFIG_SOFTMMU) += monitor.o
>> +
>> +$(obj)/op_helper.o: QEMU_CFLAGS += $(HELPER_CFLAGS)
>> diff --git a/target/nios2/cpu.c b/target/nios2/cpu.c
>> new file mode 100644
>> index 0000000..658d684
>> --- /dev/null
>> +++ b/target/nios2/cpu.c
>> @@ -0,0 +1,232 @@
>> +/*
>> + * QEMU Nios II CPU
>> + *
>> + * Copyright (c) 2012 Chris Wulff <crwulff@gmail.com>
>> + *
>> + * This library is free software; you can redistribute it and/or
>> + * modify it under the terms of the GNU Lesser General Public
>> + * License as published by the Free Software Foundation; either
>> + * version 2.1 of the License, or (at your option) any later version.
>> + *
>> + * This library is distributed in the hope that it will be useful,
>> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
>> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
>> + * Lesser General Public License for more details.
>> + *
>> + * You should have received a copy of the GNU Lesser General Public
>> + * License along with this library; if not, see
>> + * <http://www.gnu.org/licenses/lgpl-2.1.html>
>> + */
>> +
>> +#include "qemu/osdep.h"
>> +#include "qemu-common.h"
>> +#include "qapi/error.h"
>> +#include "cpu.h"
>> +#include "exec/log.h"
>> +#include "exec/gdbstub.h"
>> +#include "hw/qdev-properties.h"
>> +
>> +static void nios2_cpu_set_pc(CPUState *cs, vaddr value)
>> +{
>> +    Nios2CPU *cpu = NIOS2_CPU(cs);
>> +    CPUNios2State *env = &cpu->env;
>> +
>> +    env->regs[R_PC] = value;
>> +}
>> +
>> +static bool nios2_cpu_has_work(CPUState *cs)
>> +{
>> +    return cs->interrupt_request & (CPU_INTERRUPT_HARD |
>> CPU_INTERRUPT_NMI);
>> +}
>> +
>> +/* CPUClass::reset() */
>> +static void nios2_cpu_reset(CPUState *cs)
>> +{
>> +    Nios2CPU *cpu = NIOS2_CPU(cs);
>> +    Nios2CPUClass *ncc = NIOS2_CPU_GET_CLASS(cpu);
>> +    CPUNios2State *env = &cpu->env;
>> +
>> +    if (qemu_loglevel_mask(CPU_LOG_RESET)) {
>> +        qemu_log("CPU Reset (CPU %d)\n", cs->cpu_index);
>> +        log_cpu_state(cs, 0);
>> +    }
>> +
>> +    ncc->parent_reset(cs);
>> +
>> +    tlb_flush(cs, 1);
>> +
>> +    memset(env->regs, 0, sizeof(uint32_t) * NUM_CORE_REGS);
>> +    env->regs[R_PC] = cpu->reset_addr;
>> +
>> +#if defined(CONFIG_USER_ONLY)
>> +    /* Start in user mode with interrupts enabled. */
>> +    env->regs[CR_STATUS] = CR_STATUS_U | CR_STATUS_PIE;
> 
> So what is the value of CR_STATUS after reset in softmmu land then?
> Random value from before reset? Probably not what you want :).

Dropped, yeah.

>> +#endif
>> +}
>> +
>> +static void nios2_cpu_initfn(Object *obj)
>> +{
>> +    CPUState *cs = CPU(obj);
>> +    Nios2CPU *cpu = NIOS2_CPU(obj);
>> +    CPUNios2State *env = &cpu->env;
>> +    static bool tcg_initialized;
>> +
>> +    cpu->mmu_present = true;
>> +    cs->env_ptr = env;
>> +
>> +#if !defined(CONFIG_USER_ONLY)
>> +    mmu_init(&env->mmu);
>> +#endif
>> +
>> +    if (tcg_enabled() && !tcg_initialized) {
>> +        tcg_initialized = true;
>> +        nios2_tcg_init();
>> +    }
>> +}
>> +
>> +Nios2CPU *cpu_nios2_init(const char *cpu_model)
>> +{
>> +    Nios2CPU *cpu = NIOS2_CPU(object_new(TYPE_NIOS2_CPU));
>> +
>> +    object_property_set_bool(OBJECT(cpu), true, "realized", NULL);
>> +
>> +    return cpu;
>> +}
>> +
>> +static void nios2_cpu_realizefn(DeviceState *dev, Error **errp)
>> +{
>> +    CPUState *cs = CPU(dev);
>> +    Nios2CPUClass *ncc = NIOS2_CPU_GET_CLASS(dev);
>> +    Error *local_err = NULL;
>> +
>> +    cpu_exec_realizefn(cs, &local_err);
>> +    if (local_err != NULL) {
>> +        error_propagate(errp, local_err);
>> +        return;
>> +    }
>> +
>> +    qemu_init_vcpu(cs);
>> +    cpu_reset(cs);
>> +
>> +    ncc->parent_realize(dev, errp);
>> +}
>> +
>> +static bool nios2_cpu_exec_interrupt(CPUState *cs, int
>> interrupt_request)
>> +{
>> +    Nios2CPU *cpu = NIOS2_CPU(cs);
>> +    CPUNios2State *env = &cpu->env;
>> +
>> +    if ((interrupt_request & CPU_INTERRUPT_HARD) &&
>> +        (env->regs[CR_STATUS] & CR_STATUS_PIE)) {
>> +        cs->exception_index = EXCP_IRQ;
>> +        nios2_cpu_do_interrupt(cs);
>> +        return true;
>> +    }
>> +    return false;
>> +}
>> +
>> +
>> +static void nios2_cpu_disas_set_info(CPUState *cpu, disassemble_info
>> *info)
>> +{
>> +    /* NOTE: NiosII R2 is not supported yet. */
>> +    info->mach = bfd_arch_nios2;
>> +#ifdef TARGET_WORDS_BIGENDIAN
>> +    info->print_insn = print_insn_big_nios2;
>> +#else
>> +    info->print_insn = print_insn_little_nios2;
>> +#endif
> 
> I take it there is no runtime switch for endianness? Most architectures
> eventually got one and moved to a single default endianness for softmmu
> with swizzling for the "other" one (LE for ARM, BE for ppc).

By runtime, you mean using an instruction or such ? Nope, there isn't
such mechanism to my knowledge. I know it can be done on ARM and MIPS,
but it isn't possible on nios2. The endianness is synthesis-time option.

>> +}
>> +
>> +static int nios2_cpu_gdb_read_register(CPUState *cs, uint8_t
>> *mem_buf, int n)
>> +{
>> +    Nios2CPU *cpu = NIOS2_CPU(cs);
>> +    CPUClass *cc = CPU_GET_CLASS(cs);
>> +    CPUNios2State *env = &cpu->env;
>> +
>> +    if (n > cc->gdb_num_core_regs) {
>> +        return 0;
>> +    }
>> +
>> +    if (n < 32) {          /* GP regs */
>> +        return gdb_get_reg32(mem_buf, env->regs[n]);
>> +    } else if (n == 32) {    /* PC */
>> +        return gdb_get_reg32(mem_buf, env->regs[R_PC]);
>> +    } else if (n < 49) {     /* Status regs */
>> +        return gdb_get_reg32(mem_buf, env->regs[n - 1]);
>> +    }
>> +
>> +    /* Invalid regs */
>> +    return 0;
>> +}
>> +
>> +static int nios2_cpu_gdb_write_register(CPUState *cs, uint8_t
>> *mem_buf, int n)
>> +{
>> +    Nios2CPU *cpu = NIOS2_CPU(cs);
>> +    CPUClass *cc = CPU_GET_CLASS(cs);
>> +    CPUNios2State *env = &cpu->env;
>> +
>> +    if (n > cc->gdb_num_core_regs) {
>> +        return 0;
>> +    }
>> +
>> +    if (n < 32) {            /* GP regs */
>> +        env->regs[n] = ldl_p(mem_buf);
>> +    } else if (n == 32) {    /* PC */
>> +        env->regs[R_PC] = ldl_p(mem_buf);
>> +    } else if (n < 49) {     /* Status regs */
>> +        env->regs[n - 1] = ldl_p(mem_buf);
>> +    }
>> +
>> +    return 4;
>> +}
>> +
>> +static Property nios2_properties[] = {
>> +    DEFINE_PROP_BOOL("mmu_present", Nios2CPU, mmu_present, true),
>> +    DEFINE_PROP_END_OF_LIST(),
>> +};
>> +
>> +
>> +static void nios2_cpu_class_init(ObjectClass *oc, void *data)
>> +{
>> +    DeviceClass *dc = DEVICE_CLASS(oc);
>> +    CPUClass *cc = CPU_CLASS(oc);
>> +    Nios2CPUClass *ncc = NIOS2_CPU_CLASS(oc);
>> +
>> +    ncc->parent_realize = dc->realize;
>> +    dc->realize = nios2_cpu_realizefn;
>> +    dc->props = nios2_properties;
>> +    ncc->parent_reset = cc->reset;
>> +    cc->reset = nios2_cpu_reset;
>> +
>> +    cc->has_work = nios2_cpu_has_work;
>> +    cc->do_interrupt = nios2_cpu_do_interrupt;
>> +    cc->cpu_exec_interrupt = nios2_cpu_exec_interrupt;
>> +    cc->dump_state = nios2_cpu_dump_state;
>> +    cc->set_pc = nios2_cpu_set_pc;
>> +    cc->disas_set_info = nios2_cpu_disas_set_info;
>> +#ifdef CONFIG_USER_ONLY
>> +    cc->handle_mmu_fault = nios2_cpu_handle_mmu_fault;
>> +#else
>> +    cc->do_unaligned_access = nios2_cpu_do_unaligned_access;
>> +    cc->get_phys_page_debug = nios2_cpu_get_phys_page_debug;
>> +#endif
>> +    cc->gdb_read_register = nios2_cpu_gdb_read_register;
>> +    cc->gdb_write_register = nios2_cpu_gdb_write_register;
>> +    cc->gdb_num_core_regs = 49;
>> +}
>> +
>> +static const TypeInfo nios2_cpu_type_info = {
>> +    .name = TYPE_NIOS2_CPU,
>> +    .parent = TYPE_CPU,
>> +    .instance_size = sizeof(Nios2CPU),
>> +    .instance_init = nios2_cpu_initfn,
>> +    .class_size = sizeof(Nios2CPUClass),
>> +    .class_init = nios2_cpu_class_init,
>> +};
>> +
>> +static void nios2_cpu_register_types(void)
>> +{
>> +    type_register_static(&nios2_cpu_type_info);
>> +}
>> +
>> +type_init(nios2_cpu_register_types)
>> diff --git a/target/nios2/cpu.h b/target/nios2/cpu.h
>> new file mode 100644
>> index 0000000..91e73af
>> --- /dev/null
>> +++ b/target/nios2/cpu.h
>> @@ -0,0 +1,269 @@
>> +/*
>> + * Altera Nios II virtual CPU header
>> + *
>> + * Copyright (c) 2012 Chris Wulff <crwulff@gmail.com>
>> + *
>> + * This library is free software; you can redistribute it and/or
>> + * modify it under the terms of the GNU Lesser General Public
>> + * License as published by the Free Software Foundation; either
>> + * version 2.1 of the License, or (at your option) any later version.
>> + *
>> + * This library is distributed in the hope that it will be useful,
>> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
>> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
>> + * Lesser General Public License for more details.
>> + *
>> + * You should have received a copy of the GNU Lesser General Public
>> + * License along with this library; if not, see
>> + * <http://www.gnu.org/licenses/lgpl-2.1.html>
>> + */
>> +#ifndef CPU_NIOS2_H
>> +#define CPU_NIOS2_H
>> +
>> +#include "qemu/osdep.h"
>> +#include "qemu-common.h"
>> +
>> +#define TARGET_LONG_BITS 32
>> +
>> +#define CPUArchState struct CPUNios2State
>> +
>> +#include "exec/cpu-defs.h"
>> +#include "fpu/softfloat.h"
>> +#include "qom/cpu.h"
>> +struct CPUNios2State;
>> +typedef struct CPUNios2State CPUNios2State;
>> +#if !defined(CONFIG_USER_ONLY)
>> +#include "mmu.h"
>> +#endif
>> +
>> +#define TYPE_NIOS2_CPU "nios2-cpu"
>> +
>> +#define NIOS2_CPU_CLASS(klass) \
>> +    OBJECT_CLASS_CHECK(Nios2CPUClass, (klass), TYPE_NIOS2_CPU)
>> +#define NIOS2_CPU(obj) \
>> +    OBJECT_CHECK(Nios2CPU, (obj), TYPE_NIOS2_CPU)
>> +#define NIOS2_CPU_GET_CLASS(obj) \
>> +    OBJECT_GET_CLASS(Nios2CPUClass, (obj), TYPE_NIOS2_CPU)
>> +
>> +/**
>> + * Nios2CPUClass:
>> + * @parent_reset: The parent class' reset handler.
>> + *
>> + * A Nios2 CPU model.
>> + */
>> +typedef struct Nios2CPUClass {
>> +    /*< private >*/
>> +    CPUClass parent_class;
>> +    /*< public >*/
>> +
>> +    DeviceRealize parent_realize;
>> +    void (*parent_reset)(CPUState *cpu);
>> +} Nios2CPUClass;
>> +
>> +#define TARGET_HAS_ICE 1
>> +
>> +/* Configuration options for Nios II */
>> +#define RESET_ADDRESS         0x00000000
>> +#define EXCEPTION_ADDRESS     0x00000004
>> +#define FAST_TLB_MISS_ADDRESS 0x00000008
>> +
>> +
>> +/* GP regs + CR regs + PC */
>> +#define NUM_CORE_REGS (32 + 32 + 1)
>> +
>> +/* General purpose register aliases */
>> +#define R_ZERO   0
>> +#define R_AT     1
>> +#define R_RET0   2
>> +#define R_RET1   3
>> +#define R_ARG0   4
>> +#define R_ARG1   5
>> +#define R_ARG2   6
>> +#define R_ARG3   7
>> +#define R_ET     24
>> +#define R_BT     25
>> +#define R_GP     26
>> +#define R_SP     27
>> +#define R_FP     28
>> +#define R_EA     29
>> +#define R_BA     30
>> +#define R_RA     31
>> +
>> +/* Control register aliases */
>> +#define CR_BASE  32
>> +#define CR_STATUS    (CR_BASE + 0)
>> +#define   CR_STATUS_PIE  (1 << 0)
>> +#define   CR_STATUS_U    (1 << 1)
>> +#define   CR_STATUS_EH   (1 << 2)
>> +#define   CR_STATUS_IH   (1 << 3)
>> +#define   CR_STATUS_IL   (63 << 4)
>> +#define   CR_STATUS_CRS  (63 << 10)
>> +#define   CR_STATUS_PRS  (63 << 16)
>> +#define   CR_STATUS_NMI  (1 << 22)
>> +#define   CR_STATUS_RSIE (1 << 23)
>> +#define CR_ESTATUS   (CR_BASE + 1)
>> +#define CR_BSTATUS   (CR_BASE + 2)
>> +#define CR_IENABLE   (CR_BASE + 3)
>> +#define CR_IPENDING  (CR_BASE + 4)
>> +#define CR_CPUID     (CR_BASE + 5)
>> +#define CR_CTL6      (CR_BASE + 6)
>> +#define CR_EXCEPTION (CR_BASE + 7)
>> +#define CR_PTEADDR   (CR_BASE + 8)
>> +#define   CR_PTEADDR_PTBASE_SHIFT 22
>> +#define   CR_PTEADDR_PTBASE_MASK  (0x3FF << CR_PTEADDR_PTBASE_SHIFT)
>> +#define   CR_PTEADDR_VPN_SHIFT    2
>> +#define   CR_PTEADDR_VPN_MASK     (0xFFFFF << CR_PTEADDR_VPN_SHIFT)
>> +#define CR_TLBACC    (CR_BASE + 9)
>> +#define   CR_TLBACC_IGN_SHIFT 25
>> +#define   CR_TLBACC_IGN_MASK  (0x7F << CR_TLBACC_IGN_SHIFT)
>> +#define   CR_TLBACC_C         (1 << 24)
>> +#define   CR_TLBACC_R         (1 << 23)
>> +#define   CR_TLBACC_W         (1 << 22)
>> +#define   CR_TLBACC_X         (1 << 21)
>> +#define   CR_TLBACC_G         (1 << 20)
>> +#define   CR_TLBACC_PFN_MASK  0x000FFFFF
>> +#define CR_TLBMISC   (CR_BASE + 10)
>> +#define   CR_TLBMISC_WAY_SHIFT 20
>> +#define   CR_TLBMISC_WAY_MASK  (0xF << CR_TLBMISC_WAY_SHIFT)
>> +#define   CR_TLBMISC_RD        (1 << 19)
>> +#define   CR_TLBMISC_WR        (1 << 18)
>> +#define   CR_TLBMISC_PID_SHIFT 4
>> +#define   CR_TLBMISC_PID_MASK  (0x3FFF << CR_TLBMISC_PID_SHIFT)
>> +#define   CR_TLBMISC_DBL       (1 << 3)
>> +#define   CR_TLBMISC_BAD       (1 << 2)
>> +#define   CR_TLBMISC_PERM      (1 << 1)
>> +#define   CR_TLBMISC_D         (1 << 0)
>> +#define CR_ENCINJ    (CR_BASE + 11)
>> +#define CR_BADADDR   (CR_BASE + 12)
>> +#define CR_CONFIG    (CR_BASE + 13)
>> +#define CR_MPUBASE   (CR_BASE + 14)
>> +#define CR_MPUACC    (CR_BASE + 15)
>> +
>> +/* Other registers */
>> +#define R_PC         64
>> +
>> +/* Exceptions */
>> +#define EXCP_BREAK    -1
>> +#define EXCP_RESET    0
>> +#define EXCP_PRESET   1
>> +#define EXCP_IRQ      2
>> +#define EXCP_TRAP     3
>> +#define EXCP_UNIMPL   4
>> +#define EXCP_ILLEGAL  5
>> +#define EXCP_UNALIGN  6
>> +#define EXCP_UNALIGND 7
>> +#define EXCP_DIV      8
>> +#define EXCP_SUPERA   9
>> +#define EXCP_SUPERI   10
>> +#define EXCP_SUPERD   11
>> +#define EXCP_TLBD     12
>> +#define EXCP_TLBX     13
>> +#define EXCP_TLBR     14
>> +#define EXCP_TLBW     15
>> +#define EXCP_MPUI     16
>> +#define EXCP_MPUD     17
>> +
>> +#define CPU_INTERRUPT_NMI       CPU_INTERRUPT_TGT_EXT_3
>> +
>> +#define NB_MMU_MODES 2
>> +
>> +struct CPUNios2State {
>> +    uint32_t regs[NUM_CORE_REGS];
>> +
>> +#if !defined(CONFIG_USER_ONLY)
>> +    Nios2MMU mmu;
>> +
>> +    uint32_t irq_pending;
>> +#endif
>> +
>> +    CPU_COMMON
>> +};
>> +
>> +/**
>> + * Nios2CPU:
>> + * @env: #CPUNios2State
>> + *
>> + * A Nios2 CPU.
>> + */
>> +typedef struct Nios2CPU {
>> +    /*< private >*/
>> +    CPUState parent_obj;
>> +    /*< public >*/
>> +
>> +    CPUNios2State env;
>> +    bool mmu_present;
>> +
>> +    /* Addresses that are hard-coded in the FPGA build settings */
>> +    uint32_t reset_addr;
>> +    uint32_t exception_addr;
>> +    uint32_t fast_tlb_miss_addr;
>> +} Nios2CPU;
>> +
>> +static inline Nios2CPU *nios2_env_get_cpu(CPUNios2State *env)
>> +{
>> +    return NIOS2_CPU(container_of(env, Nios2CPU, env));
>> +}
>> +
>> +#define ENV_GET_CPU(e) CPU(nios2_env_get_cpu(e))
>> +
>> +#define ENV_OFFSET offsetof(Nios2CPU, env)
>> +
>> +void nios2_tcg_init(void);
>> +Nios2CPU *cpu_nios2_init(const char *cpu_model);
>> +void nios2_cpu_do_interrupt(CPUState *cs);
>> +int cpu_nios2_signal_handler(int host_signum, void *pinfo, void *puc);
>> +void dump_mmu(FILE *f, fprintf_function cpu_fprintf, CPUNios2State
>> *env);
>> +void nios2_cpu_dump_state(CPUState *cpu, FILE *f, fprintf_function
>> cpu_fprintf,
>> +                          int flags);
>> +hwaddr nios2_cpu_get_phys_page_debug(CPUState *cpu, vaddr addr);
>> +void nios2_cpu_do_unaligned_access(CPUState *cpu, vaddr addr,
>> +                                   MMUAccessType access_type,
>> +                                   int mmu_idx, uintptr_t retaddr);
>> +
>> +qemu_irq *nios2_cpu_pic_init(Nios2CPU *cpu);
>> +void nios2_check_interrupts(CPUNios2State *env);
>> +
>> +#define TARGET_PHYS_ADDR_SPACE_BITS 32
>> +#define TARGET_VIRT_ADDR_SPACE_BITS 32
>> +
>> +#define cpu_init(cpu_model) CPU(cpu_nios2_init(cpu_model))
>> +
>> +#define cpu_gen_code cpu_nios2_gen_code
>> +#define cpu_signal_handler cpu_nios2_signal_handler
>> +
>> +#define CPU_SAVE_VERSION 1
>> +
>> +#define TARGET_PAGE_BITS 12
>> +
>> +/* MMU modes definitions */
>> +#define MMU_MODE0_SUFFIX _kernel
>> +#define MMU_MODE1_SUFFIX _user
>> +#define MMU_SUPERVISOR_IDX  0
>> +#define MMU_USER_IDX        1
>> +
>> +static inline int cpu_mmu_index(CPUNios2State *env, bool ifetch)
>> +{
>> +    return (env->regs[CR_STATUS] & CR_STATUS_U) ? MMU_USER_IDX :
>> +                                                  MMU_SUPERVISOR_IDX;
>> +}
>> +
>> +int nios2_cpu_handle_mmu_fault(CPUState *env, vaddr address,
>> +                               int rw, int mmu_idx);
>> +
>> +static inline int cpu_interrupts_enabled(CPUNios2State *env)
>> +{
>> +    return env->regs[CR_STATUS] & CR_STATUS_PIE;
>> +}
>> +
>> +#include "exec/cpu-all.h"
>> +#include "exec/exec-all.h"
>> +
>> +static inline void cpu_get_tb_cpu_state(CPUNios2State *env,
>> target_ulong *pc,
>> +                                        target_ulong *cs_base,
>> uint32_t *flags)
>> +{
>> +    *pc = env->regs[R_PC];
>> +    *cs_base = 0;
>> +    *flags = (env->regs[CR_STATUS] & (CR_STATUS_EH | CR_STATUS_U));
>> +}
>> +
>> +#endif /* CPU_NIOS2_H */
>> diff --git a/target/nios2/helper.c b/target/nios2/helper.c
>> new file mode 100644
>> index 0000000..cd4f353
>> --- /dev/null
>> +++ b/target/nios2/helper.c
>> @@ -0,0 +1,313 @@
>> +/*
>> + * Altera Nios II helper routines.
>> + *
>> + * Copyright (c) 2012 Chris Wulff <crwulff@gmail.com>
>> + *
>> + * This library is free software; you can redistribute it and/or
>> + * modify it under the terms of the GNU Lesser General Public
>> + * License as published by the Free Software Foundation; either
>> + * version 2.1 of the License, or (at your option) any later version.
>> + *
>> + * This library is distributed in the hope that it will be useful,
>> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
>> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
>> + * Lesser General Public License for more details.
>> + *
>> + * You should have received a copy of the GNU Lesser General Public
>> + * License along with this library; if not, see
>> + * <http://www.gnu.org/licenses/lgpl-2.1.html>
>> + */
>> +
>> +#include <stdio.h>
>> +#include <string.h>
>> +#include <assert.h>
>> +
>> +#include "cpu.h"
>> +#include "qemu/osdep.h"
>> +#include "qemu/host-utils.h"
>> +#include "qapi/error.h"
>> +#include "exec/exec-all.h"
>> +#include "exec/log.h"
>> +#include "exec/helper-proto.h"
>> +
>> +#if defined(CONFIG_USER_ONLY)
>> +
>> +void nios2_cpu_do_interrupt(CPUState *cs)
>> +{
>> +    Nios2CPU *cpu = NIOS2_CPU(cs);
>> +    CPUNios2State *env = &cpu->env;
>> +    cs->exception_index = -1;
>> +    env->regs[R_EA] = env->regs[R_PC] + 4;
>> +}
>> +
>> +int nios2_cpu_handle_mmu_fault(CPUState *cs, vaddr address, int rw,
>> int mmu_idx)
>> +{
>> +    cs->exception_index = 0xaa;
>> +    /* Page 0x1000 is kuser helper */
>> +    if (address < 0x1000 || address >= 0x2000) {
>> +        cpu_dump_state(cs, stderr, fprintf, 0);
>> +    }
>> +    return 1;
>> +}
>> +
>> +#else /* !CONFIG_USER_ONLY */
>> +
>> +void nios2_cpu_do_interrupt(CPUState *cs)
>> +{
>> +    Nios2CPU *cpu = NIOS2_CPU(cs);
>> +    CPUNios2State *env = &cpu->env;
>> +
>> +    switch (cs->exception_index) {
>> +    case EXCP_IRQ:
>> +        assert(env->regs[CR_STATUS] & CR_STATUS_PIE);
>> +
>> +        qemu_log_mask(CPU_LOG_INT, "interrupt at pc=%x\n",
>> env->regs[R_PC]);
>> +
>> +        env->regs[CR_ESTATUS] = env->regs[CR_STATUS];
>> +        env->regs[CR_STATUS] |= CR_STATUS_IH;
>> +        env->regs[CR_STATUS] &= ~(CR_STATUS_PIE | CR_STATUS_U);
>> +
>> +        env->regs[CR_EXCEPTION] &= ~(0x1F << 2);
>> +        env->regs[CR_EXCEPTION] |= (cs->exception_index & 0x1F) << 2;
>> +
>> +        env->regs[R_EA] = env->regs[R_PC] + 4;
>> +        env->regs[R_PC] = cpu->exception_addr;
>> +        break;
>> +
>> +    case EXCP_TLBD:
>> +        if ((env->regs[CR_STATUS] & CR_STATUS_EH) == 0) {
>> +            qemu_log_mask(CPU_LOG_INT, "TLB MISS (fast) at pc=%x\n",
>> +                          env->regs[R_PC]);
>> +
>> +            /* Fast TLB miss */
>> +            /* Variation from the spec. Table 3-35 of the cpu
>> reference shows
>> +             * estatus not being changed for TLB miss but this
>> appears to
>> +             * be incorrect. */
>> +            env->regs[CR_ESTATUS] = env->regs[CR_STATUS];
>> +            env->regs[CR_STATUS] |= CR_STATUS_EH;
>> +            env->regs[CR_STATUS] &= ~(CR_STATUS_PIE | CR_STATUS_U);
>> +
>> +            env->regs[CR_EXCEPTION] &= ~(0x1F << 2);
>> +            env->regs[CR_EXCEPTION] |= (cs->exception_index & 0x1F)
>> << 2;
>> +
>> +            env->regs[CR_TLBMISC] &= ~CR_TLBMISC_DBL;
>> +            env->regs[CR_TLBMISC] |= CR_TLBMISC_WR;
>> +
>> +            env->regs[R_EA] = env->regs[R_PC] + 4;
>> +            env->regs[R_PC] = cpu->fast_tlb_miss_addr;
>> +        } else {
>> +            qemu_log_mask(CPU_LOG_INT, "TLB MISS (double) at pc=%x\n",
>> +                          env->regs[R_PC]);
>> +
>> +            /* Double TLB miss */
>> +            env->regs[CR_STATUS] |= CR_STATUS_EH;
>> +            env->regs[CR_STATUS] &= ~(CR_STATUS_PIE | CR_STATUS_U);
>> +
>> +            env->regs[CR_EXCEPTION] &= ~(0x1F << 2);
>> +            env->regs[CR_EXCEPTION] |= (cs->exception_index & 0x1F)
>> << 2;
>> +
>> +            env->regs[CR_TLBMISC] |= CR_TLBMISC_DBL;
>> +
>> +            env->regs[R_PC] = cpu->exception_addr;
>> +        }
>> +        break;
>> +
>> +    case EXCP_TLBR:
>> +    case EXCP_TLBW:
>> +    case EXCP_TLBX:
>> +        qemu_log_mask(CPU_LOG_INT, "TLB PERM at pc=%x\n",
>> env->regs[R_PC]);
>> +
>> +        env->regs[CR_ESTATUS] = env->regs[CR_STATUS];
>> +        env->regs[CR_STATUS] |= CR_STATUS_EH;
>> +        env->regs[CR_STATUS] &= ~(CR_STATUS_PIE | CR_STATUS_U);
>> +
>> +        env->regs[CR_EXCEPTION] &= ~(0x1F << 2);
>> +        env->regs[CR_EXCEPTION] |= (cs->exception_index & 0x1F) << 2;
>> +
>> +        if ((env->regs[CR_STATUS] & CR_STATUS_EH) == 0) {
>> +            env->regs[CR_TLBMISC] |= CR_TLBMISC_WR;
>> +        }
>> +
>> +        env->regs[R_EA] = env->regs[R_PC] + 4;
>> +        env->regs[R_PC] = cpu->exception_addr;
>> +        break;
>> +
>> +    case EXCP_SUPERA:
>> +    case EXCP_SUPERI:
>> +    case EXCP_SUPERD:
>> +        qemu_log_mask(CPU_LOG_INT, "SUPERVISOR exception at pc=%x\n",
>> +                      env->regs[R_PC]);
>> +
>> +        if ((env->regs[CR_STATUS] & CR_STATUS_EH) == 0) {
>> +            env->regs[CR_ESTATUS] = env->regs[CR_STATUS];
>> +            env->regs[R_EA] = env->regs[R_PC] + 4;
>> +        }
>> +
>> +        env->regs[CR_STATUS] |= CR_STATUS_EH;
>> +        env->regs[CR_STATUS] &= ~(CR_STATUS_PIE | CR_STATUS_U);
>> +
>> +        env->regs[CR_EXCEPTION] &= ~(0x1F << 2);
>> +        env->regs[CR_EXCEPTION] |= (cs->exception_index & 0x1F) << 2;
>> +
>> +        env->regs[R_PC] = cpu->exception_addr;
>> +        break;
>> +
>> +    case EXCP_ILLEGAL:
>> +    case EXCP_TRAP:
>> +        qemu_log_mask(CPU_LOG_INT, "TRAP exception at pc=%x\n",
>> +                      env->regs[R_PC]);
>> +
>> +        if ((env->regs[CR_STATUS] & CR_STATUS_EH) == 0) {
>> +            env->regs[CR_ESTATUS] = env->regs[CR_STATUS];
>> +            env->regs[R_EA] = env->regs[R_PC] + 4;
>> +        }
>> +
>> +        env->regs[CR_STATUS] |= CR_STATUS_EH;
>> +        env->regs[CR_STATUS] &= ~(CR_STATUS_PIE | CR_STATUS_U);
>> +
>> +        env->regs[CR_EXCEPTION] &= ~(0x1F << 2);
>> +        env->regs[CR_EXCEPTION] |= (cs->exception_index & 0x1F) << 2;
>> +
>> +        env->regs[R_PC] = cpu->exception_addr;
>> +        break;
>> +
>> +    case EXCP_BREAK:
>> +        if ((env->regs[CR_STATUS] & CR_STATUS_EH) == 0) {
>> +            env->regs[CR_BSTATUS] = env->regs[CR_STATUS];
>> +            env->regs[R_BA] = env->regs[R_PC] + 4;
>> +        }
>> +
>> +        env->regs[CR_STATUS] |= CR_STATUS_EH;
>> +        env->regs[CR_STATUS] &= ~(CR_STATUS_PIE | CR_STATUS_U);
>> +
>> +        env->regs[CR_EXCEPTION] &= ~(0x1F << 2);
>> +        env->regs[CR_EXCEPTION] |= (cs->exception_index & 0x1F) << 2;
>> +
>> +        env->regs[R_PC] = cpu->exception_addr;
>> +        break;
>> +
>> +    default:
>> +        cpu_abort(cs, "unhandled exception type=%d\n",
>> +                  cs->exception_index);
>> +        break;
>> +    }
>> +}
>> +
>> +static int cpu_nios2_handle_virtual_page(
>> +    CPUState *cs, target_ulong address, int rw, int mmu_idx)
>> +{
>> +    Nios2CPU *cpu = NIOS2_CPU(cs);
>> +    CPUNios2State *env = &cpu->env;
>> +    target_ulong vaddr, paddr;
>> +    Nios2MMULookup lu;
>> +    unsigned int hit;
>> +    hit = mmu_translate(env, &lu, address, rw, mmu_idx);
>> +    if (hit) {
>> +        vaddr = address & TARGET_PAGE_MASK;
>> +        paddr = lu.paddr + vaddr - lu.vaddr;
>> +
>> +        if (((rw == 0) && (lu.prot & PAGE_READ)) ||
>> +            ((rw == 1) && (lu.prot & PAGE_WRITE)) ||
>> +            ((rw == 2) && (lu.prot & PAGE_EXEC))) {
>> +
>> +            tlb_set_page(cs, vaddr, paddr, lu.prot,
>> +                         mmu_idx, TARGET_PAGE_SIZE);
>> +            return 0;
>> +        } else {
>> +            /* Permission violation */
>> +            cs->exception_index = (rw == 0) ? EXCP_TLBR :
>> +                                               ((rw == 1) ? EXCP_TLBW :
>> +                                                            EXCP_TLBX);
>> +        }
>> +    } else {
>> +        cs->exception_index = EXCP_TLBD;
>> +    }
>> +
>> +    if (rw == 2) {
>> +        env->regs[CR_TLBMISC] &= ~CR_TLBMISC_D;
>> +    } else {
>> +        env->regs[CR_TLBMISC] |= CR_TLBMISC_D;
>> +    }
>> +    env->regs[CR_PTEADDR] &= CR_PTEADDR_PTBASE_MASK;
>> +    env->regs[CR_PTEADDR] |= (address >> 10) & CR_PTEADDR_VPN_MASK;
>> +    env->mmu.pteaddr_wr = env->regs[CR_PTEADDR];
>> +    env->regs[CR_BADADDR] = address;
>> +    return 1;
>> +}
>> +
>> +int nios2_cpu_handle_mmu_fault(CPUState *cs, vaddr address, int rw,
>> int mmu_idx)
>> +{
>> +    Nios2CPU *cpu = NIOS2_CPU(cs);
>> +    CPUNios2State *env = &cpu->env;
>> +
>> +    if (cpu->mmu_present) {
>> +        if (MMU_SUPERVISOR_IDX == mmu_idx) {
>> +            if (address >= 0xC0000000) {
>> +                /* Kernel physical page - TLB bypassed */
>> +                address &= TARGET_PAGE_MASK;
>> +                tlb_set_page(cs, address, address, PAGE_BITS,
>> +                             mmu_idx, TARGET_PAGE_SIZE);
>> +            } else if (address >= 0x80000000) {
>> +                /* Kernel virtual page */
>> +                return cpu_nios2_handle_virtual_page(cs, address, rw,
>> mmu_idx);
>> +            } else {
>> +                /* User virtual page */
>> +                return cpu_nios2_handle_virtual_page(cs, address, rw,
>> mmu_idx);
>> +            }
>> +        } else {
>> +            if (address >= 0x80000000) {
>> +                /* Illegal access from user mode */
>> +                cs->exception_index = EXCP_SUPERA;
>> +                env->regs[CR_BADADDR] = address;
>> +                return 1;
>> +            } else {
>> +                /* User virtual page */
>> +                return cpu_nios2_handle_virtual_page(cs, address, rw,
>> mmu_idx);
>> +            }
>> +        }
>> +    } else {
>> +        /* No MMU */
>> +        address &= TARGET_PAGE_MASK;
>> +        tlb_set_page(cs, address, address, PAGE_BITS,
>> +                     mmu_idx, TARGET_PAGE_SIZE);
>> +    }
>> +
>> +    return 0;
>> +}
>> +
>> +hwaddr nios2_cpu_get_phys_page_debug(CPUState *cs, vaddr addr)
>> +{
>> +    Nios2CPU *cpu = NIOS2_CPU(cs);
>> +    CPUNios2State *env = &cpu->env;
>> +    target_ulong vaddr, paddr = 0;
>> +    Nios2MMULookup lu;
>> +    unsigned int hit;
>> +
>> +    if (cpu->mmu_present && (addr < 0xC0000000)) {
>> +        hit = mmu_translate(env, &lu, addr, 0, 0);
>> +        if (hit) {
>> +            vaddr = addr & TARGET_PAGE_MASK;
>> +            paddr = lu.paddr + vaddr - lu.vaddr;
>> +        } else {
>> +            paddr = -1;
>> +            qemu_log("cpu_get_phys_page debug MISS: %08lX\n", addr);
>> +        }
>> +    } else {
>> +        paddr = addr & TARGET_PAGE_MASK;
>> +    }
>> +
>> +    return paddr;
>> +}
>> +
>> +void nios2_cpu_do_unaligned_access(CPUState *cs, vaddr addr,
>> +                                   MMUAccessType access_type,
>> +                                   int mmu_idx, uintptr_t retaddr)
>> +{
>> +    Nios2CPU *cpu = NIOS2_CPU(cs);
>> +    CPUNios2State *env = &cpu->env;
>> +
>> +    env->regs[CR_BADADDR] = addr;
>> +    env->regs[CR_EXCEPTION] = EXCP_UNALIGN << 2;
>> +    helper_raise_exception(env, EXCP_UNALIGN);
>> +}
>> +#endif /* !CONFIG_USER_ONLY */
>> diff --git a/target/nios2/helper.h b/target/nios2/helper.h
>> new file mode 100644
>> index 0000000..b86a2f2
>> --- /dev/null
>> +++ b/target/nios2/helper.h
>> @@ -0,0 +1,27 @@
>> +/*
>> + * Altera Nios II helper routines header.
>> + *
>> + * Copyright (c) 2012 Chris Wulff <crwulff@gmail.com>
>> + *
>> + * This library is free software; you can redistribute it and/or
>> + * modify it under the terms of the GNU Lesser General Public
>> + * License as published by the Free Software Foundation; either
>> + * version 2.1 of the License, or (at your option) any later version.
>> + *
>> + * This library is distributed in the hope that it will be useful,
>> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
>> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
>> + * Lesser General Public License for more details.
>> + *
>> + * You should have received a copy of the GNU Lesser General Public
>> + * License along with this library; if not, see
>> + * <http://www.gnu.org/licenses/lgpl-2.1.html>
>> + */
>> +
>> +DEF_HELPER_2(raise_exception, void, env, i32)
>> +
>> +#if !defined(CONFIG_USER_ONLY)
>> +DEF_HELPER_2(mmu_read, i32, env, i32)
>> +DEF_HELPER_3(mmu_write, void, env, i32, i32)
>> +DEF_HELPER_1(check_interrupts, void, env)
>> +#endif
>> diff --git a/target/nios2/mmu.c b/target/nios2/mmu.c
>> new file mode 100644
>> index 0000000..875fbb0
>> --- /dev/null
>> +++ b/target/nios2/mmu.c
>> @@ -0,0 +1,292 @@
>> +/*
>> + * Altera Nios II MMU emulation for qemu.
>> + *
>> + * Copyright (C) 2012 Chris Wulff <crwulff@gmail.com>
>> + *
>> + * This library is free software; you can redistribute it and/or
>> + * modify it under the terms of the GNU Lesser General Public
>> + * License as published by the Free Software Foundation; either
>> + * version 2.1 of the License, or (at your option) any later version.
>> + *
>> + * This library is distributed in the hope that it will be useful,
>> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
>> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
>> + * Lesser General Public License for more details.
>> + *
>> + * You should have received a copy of the GNU Lesser General Public
>> + * License along with this library; if not, see
>> + * <http://www.gnu.org/licenses/lgpl-2.1.html>
>> + */
>> +
>> +#include "qemu/osdep.h"
>> +#include "qemu-common.h"
>> +#include "cpu.h"
>> +#include "exec/exec-all.h"
>> +#include "mmu.h"
>> +
>> +#if !defined(CONFIG_USER_ONLY)
>> +
>> +/* Define this to enable MMU debug messages */
>> +/* #define DEBUG_MMU */
>> +
>> +#ifdef DEBUG_MMU
>> +#define MMU_LOG(x) x
>> +#else
>> +#define MMU_LOG(x)
>> +#endif
>> +
>> +void tlb_fill(CPUState *cs, target_ulong addr, MMUAccessType
>> access_type,
>> +              int mmu_idx, uintptr_t retaddr)
>> +{
>> +    int ret;
>> +
>> +    ret = nios2_cpu_handle_mmu_fault(cs, addr, access_type, mmu_idx);
>> +    if (unlikely(ret)) {
>> +        if (retaddr) {
>> +            /* now we have a real cpu fault */
>> +            cpu_restore_state(cs, retaddr);
>> +        }
>> +        cpu_loop_exit(cs);
>> +    }
>> +}
>> +
>> +uint32_t mmu_read(CPUNios2State *env, uint32_t rn)
>> +{
>> +    switch (rn) {
>> +    case CR_TLBACC:
>> +        MMU_LOG(qemu_log("TLBACC READ %08X\n", env->regs[rn]));
>> +        break;
>> +
>> +    case CR_TLBMISC:
>> +        MMU_LOG(qemu_log("TLBMISC READ %08X\n", env->regs[rn]));
>> +        break;
>> +
>> +    case CR_PTEADDR:
>> +        MMU_LOG(qemu_log("PTEADDR READ %08X\n", env->regs[rn]));
>> +        break;
>> +
>> +    default:
>> +        break;
>> +    }
>> +    return env->regs[rn];
> 
> This function should get split into a logging function (which is a
> helper and translated in conditionally depending on your debug define)
> and a TCG register for the actual read.

Fixed

>> +}
>> +
>> +/* rw - 0 = read, 1 = write, 2 = fetch.  */
>> +unsigned int mmu_translate(CPUNios2State *env,
>> +                           Nios2MMULookup *lu,
>> +                           target_ulong vaddr, int rw, int mmu_idx)
>> +{
>> +    int pid = (env->mmu.tlbmisc_wr & CR_TLBMISC_PID_MASK) >> 4;
>> +    int vpn = vaddr >> 12;
>> +
>> +    MMU_LOG(qemu_log("mmu_translate vaddr %08X, pid %08X, vpn %08X\n",
>> +                     vaddr, pid, vpn));
>> +
>> +    int way;
>> +    for (way = 0; way < env->mmu.tlb_num_ways; way++) {
>> +
>> +        Nios2TLBEntry *entry =
>> +            &env->mmu.tlb[(way * env->mmu.tlb_num_ways) +
>> +                          (vpn & env->mmu.tlb_entry_mask)];
>> +
>> +        MMU_LOG(qemu_log("TLB[%d] TAG %08X, VPN %08X\n",
>> +                         (way * env->mmu.tlb_num_ways) +
>> +                         (vpn & env->mmu.tlb_entry_mask),
>> +                         entry->tag, (entry->tag >> 12)));
>> +
>> +        if (((entry->tag >> 12) != vpn) ||
>> +            (((entry->tag & (1 << 11)) == 0) &&
>> +            ((entry->tag & ((1 << env->mmu.pid_bits) - 1)) != pid))) {
>> +            continue;
>> +        }
>> +        lu->vaddr = vaddr & TARGET_PAGE_MASK;
>> +        lu->paddr = (entry->data & CR_TLBACC_PFN_MASK) <<
>> TARGET_PAGE_BITS;
>> +        lu->prot = ((entry->data & CR_TLBACC_R) ? PAGE_READ : 0) |
>> +                   ((entry->data & CR_TLBACC_W) ? PAGE_WRITE : 0) |
>> +                   ((entry->data & CR_TLBACC_X) ? PAGE_EXEC : 0);
>> +
>> +        MMU_LOG(qemu_log("HIT TLB[%d] %08X %08X %08X\n",
>> +                         (way * env->mmu.tlb_num_ways) +
>> +                         (vpn & env->mmu.tlb_entry_mask),
>> +                         lu->vaddr, lu->paddr, lu->prot));
>> +        return 1;
>> +    }
>> +    return 0;
>> +}
>> +
>> +static void mmu_flush_pid(CPUNios2State *env, uint32_t pid)
>> +{
>> +    CPUState *cs = ENV_GET_CPU(env);
>> +    int idx;
>> +    MMU_LOG(qemu_log("TLB Flush PID %d\n", pid));
>> +
>> +    for (idx = 0; idx < env->mmu.tlb_num_entries; idx++) {
>> +        Nios2TLBEntry *entry = &env->mmu.tlb[idx];
>> +
>> +        MMU_LOG(qemu_log("TLB[%d] => %08X %08X\n",
>> +                         idx, entry->tag, entry->data));
>> +
>> +        if ((entry->tag & (1 << 10)) && (!(entry->tag & (1 << 11))) &&
>> +            ((entry->tag & ((1 << env->mmu.pid_bits) - 1)) == pid)) {
>> +            uint32_t vaddr = entry->tag & TARGET_PAGE_MASK;
>> +
>> +            MMU_LOG(qemu_log("TLB Flush Page %08X\n", vaddr));
>> +
>> +            tlb_flush_page(cs, vaddr);
>> +        }
>> +    }
>> +}
>> +
>> +void mmu_write(CPUNios2State *env, uint32_t rn, uint32_t v)
>> +{
>> +    CPUState *cs = ENV_GET_CPU(env);
>> +
>> +    MMU_LOG(qemu_log("mmu_write %08X = %08X\n", rn, v));
>> +
>> +    switch (rn) {
>> +    case CR_TLBACC:
>> +        MMU_LOG(qemu_log("TLBACC: IG %02X, FLAGS %c%c%c%c%c, PFN
>> %05X\n",
>> +                         v >> CR_TLBACC_IGN_SHIFT,
>> +                         (v & CR_TLBACC_C) ? 'C' : '.',
>> +                         (v & CR_TLBACC_R) ? 'R' : '.',
>> +                         (v & CR_TLBACC_W) ? 'W' : '.',
>> +                         (v & CR_TLBACC_X) ? 'X' : '.',
>> +                         (v & CR_TLBACC_G) ? 'G' : '.',
>> +                         v & CR_TLBACC_PFN_MASK));
>> +
>> +        /* if tlbmisc.WE == 1 then trigger a TLB write on writes to
>> TLBACC */
>> +        if (env->regs[CR_TLBMISC] & CR_TLBMISC_WR) {
>> +            int way = (env->regs[CR_TLBMISC] >> CR_TLBMISC_WAY_SHIFT);
>> +            int vpn = (env->mmu.pteaddr_wr & CR_PTEADDR_VPN_MASK) >> 2;
>> +            int pid = (env->mmu.tlbmisc_wr & CR_TLBMISC_PID_MASK) >> 4;
>> +            int g = (v & CR_TLBACC_G) ? 1 : 0;
>> +            int valid = ((vpn & CR_TLBACC_PFN_MASK) < 0xC0000) ? 1 : 0;
>> +            Nios2TLBEntry *entry =
>> +                &env->mmu.tlb[(way * env->mmu.tlb_num_ways) +
>> +                              (vpn & env->mmu.tlb_entry_mask)];
>> +            uint32_t newTag = (vpn << 12) | (g << 11) | (valid << 10)
>> | pid;
>> +            uint32_t newData = v & (CR_TLBACC_C | CR_TLBACC_R |
>> CR_TLBACC_W |
>> +                                    CR_TLBACC_X | CR_TLBACC_PFN_MASK);
>> +
>> +            if ((entry->tag != newTag) || (entry->data != newData)) {
>> +                if (entry->tag & (1 << 10)) {
>> +                    /* Flush existing entry */
>> +                    MMU_LOG(qemu_log("TLB Flush Page (OLD) %08X\n",
>> +                                     entry->tag & TARGET_PAGE_MASK));
>> +                    tlb_flush_page(cs, entry->tag & TARGET_PAGE_MASK);
>> +                }
>> +                entry->tag = newTag;
>> +                entry->data = newData;
>> +                MMU_LOG(qemu_log("TLB[%d] = %08X %08X\n",
>> +                                 (way * env->mmu.tlb_num_ways) +
>> +                                 (vpn & env->mmu.tlb_entry_mask),
>> +                                 entry->tag, entry->data));
>> +            }
>> +            /* Auto-increment tlbmisc.WAY */
>> +            env->regs[CR_TLBMISC] =
>> +                (env->regs[CR_TLBMISC] & ~CR_TLBMISC_WAY_MASK) |
>> +                (((way + 1) & (env->mmu.tlb_num_ways - 1)) <<
>> +                 CR_TLBMISC_WAY_SHIFT);
>> +        }
>> +
>> +        /* Writes to TLBACC don't change the read-back value */
>> +        env->mmu.tlbacc_wr = v;
>> +        break;
>> +
>> +    case CR_TLBMISC:
>> +        MMU_LOG(qemu_log("TLBMISC: WAY %X, FLAGS %c%c%c%c%c%c, PID
>> %04X\n",
>> +                         v >> CR_TLBMISC_WAY_SHIFT,
>> +                         (v & CR_TLBMISC_RD) ? 'R' : '.',
>> +                         (v & CR_TLBMISC_WR) ? 'W' : '.',
>> +                         (v & CR_TLBMISC_DBL) ? '2' : '.',
>> +                         (v & CR_TLBMISC_BAD) ? 'B' : '.',
>> +                         (v & CR_TLBMISC_PERM) ? 'P' : '.',
>> +                         (v & CR_TLBMISC_D) ? 'D' : '.',
>> +                         (v & CR_TLBMISC_PID_MASK) >> 4));
>> +
>> +        if ((v & CR_TLBMISC_PID_MASK) !=
>> +            (env->mmu.tlbmisc_wr & CR_TLBMISC_PID_MASK)) {
>> +            mmu_flush_pid(env, (env->mmu.tlbmisc_wr &
>> CR_TLBMISC_PID_MASK) >>
>> +                               CR_TLBMISC_PID_SHIFT);
>> +        }
>> +        /* if tlbmisc.RD == 1 then trigger a TLB read on writes to
>> TLBMISC */
>> +        if (v & CR_TLBMISC_RD) {
>> +            int way = (v >> CR_TLBMISC_WAY_SHIFT);
>> +            int vpn = (env->mmu.pteaddr_wr & CR_PTEADDR_VPN_MASK) >> 2;
>> +            Nios2TLBEntry *entry =
>> +                &env->mmu.tlb[(way * env->mmu.tlb_num_ways) +
>> +                              (vpn & env->mmu.tlb_entry_mask)];
>> +
>> +            env->regs[CR_TLBACC] &= CR_TLBACC_IGN_MASK;
>> +            env->regs[CR_TLBACC] |= entry->data;
>> +            env->regs[CR_TLBACC] |= (entry->tag & (1 << 11)) ?
>> CR_TLBACC_G : 0;
>> +            env->regs[CR_TLBMISC] =
>> +                (v & ~CR_TLBMISC_PID_MASK) |
>> +                ((entry->tag & ((1 << env->mmu.pid_bits) - 1)) <<
>> +                 CR_TLBMISC_PID_SHIFT);
>> +            env->regs[CR_PTEADDR] &= ~CR_PTEADDR_VPN_MASK;
>> +            env->regs[CR_PTEADDR] |= (entry->tag >> 12) <<
>> CR_PTEADDR_VPN_SHIFT;
>> +            MMU_LOG(qemu_log("TLB READ way %d, vpn %05X, tag %08X,
>> data %08X, "
>> +                             "tlbacc %08X, tlbmisc %08X, pteaddr
>> %08X\n",
>> +                             way, vpn, entry->tag, entry->data,
>> +                             env->regs[CR_TLBACC],
>> env->regs[CR_TLBMISC],
>> +                             env->regs[CR_PTEADDR]));
>> +        } else {
>> +            env->regs[CR_TLBMISC] = v;
>> +        }
>> +
>> +        env->mmu.tlbmisc_wr = v;
>> +        break;
>> +
>> +    case CR_PTEADDR:
>> +        MMU_LOG(qemu_log("PTEADDR: PTBASE %03X, VPN %05X\n",
>> +                         v >> CR_PTEADDR_PTBASE_SHIFT,
>> +                         (v & CR_PTEADDR_VPN_MASK) >>
>> CR_PTEADDR_VPN_SHIFT));
>> +
>> +        /* Writes to PTEADDR don't change the read-back VPN value */
>> +        env->regs[CR_PTEADDR] = (v & ~CR_PTEADDR_VPN_MASK) |
>> +                                (env->regs[CR_PTEADDR] &
>> CR_PTEADDR_VPN_MASK);
>> +        env->mmu.pteaddr_wr = v;
>> +        break;
>> +
>> +    default:
>> +        break;
>> +    }
>> +}
>> +
>> +void mmu_init(Nios2MMU *mmu)
>> +{
>> +    MMU_LOG(qemu_log("mmu_init\n"));
>> +
>> +    mmu->pid_bits = 8;          /* TODO: get this from
>> ALTR,pid-num-bits */
>> +    mmu->tlb_num_ways = 16;     /* TODO: get this from
>> ALTR,tlb-num-ways */
>> +    mmu->tlb_num_entries = 256; /* TODO: get this from
>> ALTR,tlb-num-entries */
> 
> If they're going to get set from outside, you probably want to make
> these QOM properties on the CPU objects. That way your board file can
> modify them depending on dt properties (which is what you're referring
> to I suppose?)

Yeah, fixed
Alexander Graf Jan. 17, 2017, 8:16 a.m. UTC | #4
> Am 17.01.2017 um 01:18 schrieb Marek Vasut <marex@denx.de>:
> 
>> On 01/16/2017 11:21 PM, Alexander Graf wrote:
>> 
>> 
>>> On 31/12/2016 14:22, Marek Vasut wrote:
>>> From: Chris Wulff <crwulff@gmail.com>
>>> 
>>> Add support for emulating Altera NiosII R1 architecture into qemu.
>>> This patch is based on previous work by Chris Wulff from 2012 and
>>> updated to latest mainline QEMU.
>>> 
>>> Signed-off-by: Marek Vasut <marex@denx.de>
>>> Cc: Chris Wulff <crwulff@gmail.com>
>>> Cc: Jeff Da Silva <jdasilva@altera.com>
>>> Cc: Ley Foon Tan <lftan@altera.com>
>>> Cc: Sandra Loosemore <sandra@codesourcery.com>
>>> Cc: Yves Vandervennet <yvanderv@altera.com>
>>> ---
>>> V3: Thorough cleanup, deal with the review comments all over the place
>>> V4: - Use extract32()
>>>    - Fix gen_goto_tb() , suppress tcg_gen_goto_tb()
>>>    - Clean up gen_check_supervisor() helper
>>>    - Use TCGMemOp type for flags
>>>    - Drop jump labels from wrctl/rdctl
>>>    - More TCG cleanup
>>> V5: - Simplify load/store handling
>>>    - Handle loads into R_ZERO from protected page, add comment
>>> V6: - Fix division opcode handling
>>>    - Add missing disas handling
>>>    - V5 review comments cleanup
>>> V7: - Drop newline at the end of file
>>> V8: - Rebase on top of qemu/master
>>>    - Move the target-nios2 to target/nios2
>>> ---
>>> target/nios2/Makefile.objs |   4 +
>>> target/nios2/cpu.c         | 232 +++++++++++
>>> target/nios2/cpu.h         | 269 +++++++++++++
>>> target/nios2/helper.c      | 313 +++++++++++++++
>>> target/nios2/helper.h      |  27 ++
>>> target/nios2/mmu.c         | 292 ++++++++++++++
>>> target/nios2/mmu.h         |  54 +++
>>> target/nios2/monitor.c     |  35 ++
>>> target/nios2/op_helper.c   |  47 +++
>>> target/nios2/translate.c   | 953
>>> +++++++++++++++++++++++++++++++++++++++++++++
>>> 10 files changed, 2226 insertions(+)
>>> create mode 100644 target/nios2/Makefile.objs
>>> create mode 100644 target/nios2/cpu.c
>>> create mode 100644 target/nios2/cpu.h
>>> create mode 100644 target/nios2/helper.c
>>> create mode 100644 target/nios2/helper.h
>>> create mode 100644 target/nios2/mmu.c
>>> create mode 100644 target/nios2/mmu.h
>>> create mode 100644 target/nios2/monitor.c
>>> create mode 100644 target/nios2/op_helper.c
>>> create mode 100644 target/nios2/translate.c
>>> 
>>> diff --git a/target/nios2/Makefile.objs b/target/nios2/Makefile.objs
>>> new file mode 100644
>>> index 0000000..2a11c5c
>>> --- /dev/null
>>> +++ b/target/nios2/Makefile.objs
>>> @@ -0,0 +1,4 @@
>>> +obj-y += translate.o op_helper.o helper.o cpu.o mmu.o
>>> +obj-$(CONFIG_SOFTMMU) += monitor.o
>>> +
>>> +$(obj)/op_helper.o: QEMU_CFLAGS += $(HELPER_CFLAGS)
>>> diff --git a/target/nios2/cpu.c b/target/nios2/cpu.c
>>> new file mode 100644
>>> index 0000000..658d684
>>> --- /dev/null
>>> +++ b/target/nios2/cpu.c
>>> @@ -0,0 +1,232 @@
>>> +/*
>>> + * QEMU Nios II CPU
>>> + *
>>> + * Copyright (c) 2012 Chris Wulff <crwulff@gmail.com>
>>> + *
>>> + * This library is free software; you can redistribute it and/or
>>> + * modify it under the terms of the GNU Lesser General Public
>>> + * License as published by the Free Software Foundation; either
>>> + * version 2.1 of the License, or (at your option) any later version.
>>> + *
>>> + * This library is distributed in the hope that it will be useful,
>>> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
>>> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
>>> + * Lesser General Public License for more details.
>>> + *
>>> + * You should have received a copy of the GNU Lesser General Public
>>> + * License along with this library; if not, see
>>> + * <http://www.gnu.org/licenses/lgpl-2.1.html>
>>> + */
>>> +
>>> +#include "qemu/osdep.h"
>>> +#include "qemu-common.h"
>>> +#include "qapi/error.h"
>>> +#include "cpu.h"
>>> +#include "exec/log.h"
>>> +#include "exec/gdbstub.h"
>>> +#include "hw/qdev-properties.h"
>>> +
>>> +static void nios2_cpu_set_pc(CPUState *cs, vaddr value)
>>> +{
>>> +    Nios2CPU *cpu = NIOS2_CPU(cs);
>>> +    CPUNios2State *env = &cpu->env;
>>> +
>>> +    env->regs[R_PC] = value;
>>> +}
>>> +
>>> +static bool nios2_cpu_has_work(CPUState *cs)
>>> +{
>>> +    return cs->interrupt_request & (CPU_INTERRUPT_HARD |
>>> CPU_INTERRUPT_NMI);
>>> +}
>>> +
>>> +/* CPUClass::reset() */
>>> +static void nios2_cpu_reset(CPUState *cs)
>>> +{
>>> +    Nios2CPU *cpu = NIOS2_CPU(cs);
>>> +    Nios2CPUClass *ncc = NIOS2_CPU_GET_CLASS(cpu);
>>> +    CPUNios2State *env = &cpu->env;
>>> +
>>> +    if (qemu_loglevel_mask(CPU_LOG_RESET)) {
>>> +        qemu_log("CPU Reset (CPU %d)\n", cs->cpu_index);
>>> +        log_cpu_state(cs, 0);
>>> +    }
>>> +
>>> +    ncc->parent_reset(cs);
>>> +
>>> +    tlb_flush(cs, 1);
>>> +
>>> +    memset(env->regs, 0, sizeof(uint32_t) * NUM_CORE_REGS);
>>> +    env->regs[R_PC] = cpu->reset_addr;
>>> +
>>> +#if defined(CONFIG_USER_ONLY)
>>> +    /* Start in user mode with interrupts enabled. */
>>> +    env->regs[CR_STATUS] = CR_STATUS_U | CR_STATUS_PIE;
>> 
>> So what is the value of CR_STATUS after reset in softmmu land then?
>> Random value from before reset? Probably not what you want :).
> 
> Dropped, yeah.

Dropped? That wasn't my intention :).

I was just too tired to grasp the code. CR_STATUS is 0 for softmmu thanks to the memset, so irqs are disabled. For linux-user, applications expect irqs to work, so it needs to be explicitly set to on.

The code above is perfectly correct I think.


Alex
Marek Vasut Jan. 17, 2017, 8:49 a.m. UTC | #5
On 01/17/2017 09:16 AM, Alexander Graf wrote:
> 
> 
>> Am 17.01.2017 um 01:18 schrieb Marek Vasut <marex@denx.de>:
>>
>>> On 01/16/2017 11:21 PM, Alexander Graf wrote:
>>>
>>>
>>>> On 31/12/2016 14:22, Marek Vasut wrote:
>>>> From: Chris Wulff <crwulff@gmail.com>
>>>>
>>>> Add support for emulating Altera NiosII R1 architecture into qemu.
>>>> This patch is based on previous work by Chris Wulff from 2012 and
>>>> updated to latest mainline QEMU.
>>>>
>>>> Signed-off-by: Marek Vasut <marex@denx.de>
>>>> Cc: Chris Wulff <crwulff@gmail.com>
>>>> Cc: Jeff Da Silva <jdasilva@altera.com>
>>>> Cc: Ley Foon Tan <lftan@altera.com>
>>>> Cc: Sandra Loosemore <sandra@codesourcery.com>
>>>> Cc: Yves Vandervennet <yvanderv@altera.com>
>>>> ---
>>>> V3: Thorough cleanup, deal with the review comments all over the place
>>>> V4: - Use extract32()
>>>>    - Fix gen_goto_tb() , suppress tcg_gen_goto_tb()
>>>>    - Clean up gen_check_supervisor() helper
>>>>    - Use TCGMemOp type for flags
>>>>    - Drop jump labels from wrctl/rdctl
>>>>    - More TCG cleanup
>>>> V5: - Simplify load/store handling
>>>>    - Handle loads into R_ZERO from protected page, add comment
>>>> V6: - Fix division opcode handling
>>>>    - Add missing disas handling
>>>>    - V5 review comments cleanup
>>>> V7: - Drop newline at the end of file
>>>> V8: - Rebase on top of qemu/master
>>>>    - Move the target-nios2 to target/nios2
>>>> ---
>>>> target/nios2/Makefile.objs |   4 +
>>>> target/nios2/cpu.c         | 232 +++++++++++
>>>> target/nios2/cpu.h         | 269 +++++++++++++
>>>> target/nios2/helper.c      | 313 +++++++++++++++
>>>> target/nios2/helper.h      |  27 ++
>>>> target/nios2/mmu.c         | 292 ++++++++++++++
>>>> target/nios2/mmu.h         |  54 +++
>>>> target/nios2/monitor.c     |  35 ++
>>>> target/nios2/op_helper.c   |  47 +++
>>>> target/nios2/translate.c   | 953
>>>> +++++++++++++++++++++++++++++++++++++++++++++
>>>> 10 files changed, 2226 insertions(+)
>>>> create mode 100644 target/nios2/Makefile.objs
>>>> create mode 100644 target/nios2/cpu.c
>>>> create mode 100644 target/nios2/cpu.h
>>>> create mode 100644 target/nios2/helper.c
>>>> create mode 100644 target/nios2/helper.h
>>>> create mode 100644 target/nios2/mmu.c
>>>> create mode 100644 target/nios2/mmu.h
>>>> create mode 100644 target/nios2/monitor.c
>>>> create mode 100644 target/nios2/op_helper.c
>>>> create mode 100644 target/nios2/translate.c
>>>>
>>>> diff --git a/target/nios2/Makefile.objs b/target/nios2/Makefile.objs
>>>> new file mode 100644
>>>> index 0000000..2a11c5c
>>>> --- /dev/null
>>>> +++ b/target/nios2/Makefile.objs
>>>> @@ -0,0 +1,4 @@
>>>> +obj-y += translate.o op_helper.o helper.o cpu.o mmu.o
>>>> +obj-$(CONFIG_SOFTMMU) += monitor.o
>>>> +
>>>> +$(obj)/op_helper.o: QEMU_CFLAGS += $(HELPER_CFLAGS)
>>>> diff --git a/target/nios2/cpu.c b/target/nios2/cpu.c
>>>> new file mode 100644
>>>> index 0000000..658d684
>>>> --- /dev/null
>>>> +++ b/target/nios2/cpu.c
>>>> @@ -0,0 +1,232 @@
>>>> +/*
>>>> + * QEMU Nios II CPU
>>>> + *
>>>> + * Copyright (c) 2012 Chris Wulff <crwulff@gmail.com>
>>>> + *
>>>> + * This library is free software; you can redistribute it and/or
>>>> + * modify it under the terms of the GNU Lesser General Public
>>>> + * License as published by the Free Software Foundation; either
>>>> + * version 2.1 of the License, or (at your option) any later version.
>>>> + *
>>>> + * This library is distributed in the hope that it will be useful,
>>>> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
>>>> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
>>>> + * Lesser General Public License for more details.
>>>> + *
>>>> + * You should have received a copy of the GNU Lesser General Public
>>>> + * License along with this library; if not, see
>>>> + * <http://www.gnu.org/licenses/lgpl-2.1.html>
>>>> + */
>>>> +
>>>> +#include "qemu/osdep.h"
>>>> +#include "qemu-common.h"
>>>> +#include "qapi/error.h"
>>>> +#include "cpu.h"
>>>> +#include "exec/log.h"
>>>> +#include "exec/gdbstub.h"
>>>> +#include "hw/qdev-properties.h"
>>>> +
>>>> +static void nios2_cpu_set_pc(CPUState *cs, vaddr value)
>>>> +{
>>>> +    Nios2CPU *cpu = NIOS2_CPU(cs);
>>>> +    CPUNios2State *env = &cpu->env;
>>>> +
>>>> +    env->regs[R_PC] = value;
>>>> +}
>>>> +
>>>> +static bool nios2_cpu_has_work(CPUState *cs)
>>>> +{
>>>> +    return cs->interrupt_request & (CPU_INTERRUPT_HARD |
>>>> CPU_INTERRUPT_NMI);
>>>> +}
>>>> +
>>>> +/* CPUClass::reset() */
>>>> +static void nios2_cpu_reset(CPUState *cs)
>>>> +{
>>>> +    Nios2CPU *cpu = NIOS2_CPU(cs);
>>>> +    Nios2CPUClass *ncc = NIOS2_CPU_GET_CLASS(cpu);
>>>> +    CPUNios2State *env = &cpu->env;
>>>> +
>>>> +    if (qemu_loglevel_mask(CPU_LOG_RESET)) {
>>>> +        qemu_log("CPU Reset (CPU %d)\n", cs->cpu_index);
>>>> +        log_cpu_state(cs, 0);
>>>> +    }
>>>> +
>>>> +    ncc->parent_reset(cs);
>>>> +
>>>> +    tlb_flush(cs, 1);
>>>> +
>>>> +    memset(env->regs, 0, sizeof(uint32_t) * NUM_CORE_REGS);
>>>> +    env->regs[R_PC] = cpu->reset_addr;
>>>> +
>>>> +#if defined(CONFIG_USER_ONLY)
>>>> +    /* Start in user mode with interrupts enabled. */
>>>> +    env->regs[CR_STATUS] = CR_STATUS_U | CR_STATUS_PIE;
>>>
>>> So what is the value of CR_STATUS after reset in softmmu land then?
>>> Random value from before reset? Probably not what you want :).
>>
>> Dropped, yeah.
> 
> Dropped? That wasn't my intention :).
> 
> I was just too tired to grasp the code. CR_STATUS is 0 for softmmu thanks to the memset, so irqs are disabled. For linux-user, applications expect irqs to work, so it needs to be explicitly set to on.
> 
> The code above is perfectly correct I think.

I ran into an issue booting Linux, so now I'm explicitly initing the
CR_STATUS in both cases. For user case, to the above, otherwise to 0.
diff mbox

Patch

diff --git a/target/nios2/Makefile.objs b/target/nios2/Makefile.objs
new file mode 100644
index 0000000..2a11c5c
--- /dev/null
+++ b/target/nios2/Makefile.objs
@@ -0,0 +1,4 @@ 
+obj-y += translate.o op_helper.o helper.o cpu.o mmu.o
+obj-$(CONFIG_SOFTMMU) += monitor.o
+
+$(obj)/op_helper.o: QEMU_CFLAGS += $(HELPER_CFLAGS)
diff --git a/target/nios2/cpu.c b/target/nios2/cpu.c
new file mode 100644
index 0000000..658d684
--- /dev/null
+++ b/target/nios2/cpu.c
@@ -0,0 +1,232 @@ 
+/*
+ * QEMU Nios II CPU
+ *
+ * Copyright (c) 2012 Chris Wulff <crwulff@gmail.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, see
+ * <http://www.gnu.org/licenses/lgpl-2.1.html>
+ */
+
+#include "qemu/osdep.h"
+#include "qemu-common.h"
+#include "qapi/error.h"
+#include "cpu.h"
+#include "exec/log.h"
+#include "exec/gdbstub.h"
+#include "hw/qdev-properties.h"
+
+static void nios2_cpu_set_pc(CPUState *cs, vaddr value)
+{
+    Nios2CPU *cpu = NIOS2_CPU(cs);
+    CPUNios2State *env = &cpu->env;
+
+    env->regs[R_PC] = value;
+}
+
+static bool nios2_cpu_has_work(CPUState *cs)
+{
+    return cs->interrupt_request & (CPU_INTERRUPT_HARD | CPU_INTERRUPT_NMI);
+}
+
+/* CPUClass::reset() */
+static void nios2_cpu_reset(CPUState *cs)
+{
+    Nios2CPU *cpu = NIOS2_CPU(cs);
+    Nios2CPUClass *ncc = NIOS2_CPU_GET_CLASS(cpu);
+    CPUNios2State *env = &cpu->env;
+
+    if (qemu_loglevel_mask(CPU_LOG_RESET)) {
+        qemu_log("CPU Reset (CPU %d)\n", cs->cpu_index);
+        log_cpu_state(cs, 0);
+    }
+
+    ncc->parent_reset(cs);
+
+    tlb_flush(cs, 1);
+
+    memset(env->regs, 0, sizeof(uint32_t) * NUM_CORE_REGS);
+    env->regs[R_PC] = cpu->reset_addr;
+
+#if defined(CONFIG_USER_ONLY)
+    /* Start in user mode with interrupts enabled. */
+    env->regs[CR_STATUS] = CR_STATUS_U | CR_STATUS_PIE;
+#endif
+}
+
+static void nios2_cpu_initfn(Object *obj)
+{
+    CPUState *cs = CPU(obj);
+    Nios2CPU *cpu = NIOS2_CPU(obj);
+    CPUNios2State *env = &cpu->env;
+    static bool tcg_initialized;
+
+    cpu->mmu_present = true;
+    cs->env_ptr = env;
+
+#if !defined(CONFIG_USER_ONLY)
+    mmu_init(&env->mmu);
+#endif
+
+    if (tcg_enabled() && !tcg_initialized) {
+        tcg_initialized = true;
+        nios2_tcg_init();
+    }
+}
+
+Nios2CPU *cpu_nios2_init(const char *cpu_model)
+{
+    Nios2CPU *cpu = NIOS2_CPU(object_new(TYPE_NIOS2_CPU));
+
+    object_property_set_bool(OBJECT(cpu), true, "realized", NULL);
+
+    return cpu;
+}
+
+static void nios2_cpu_realizefn(DeviceState *dev, Error **errp)
+{
+    CPUState *cs = CPU(dev);
+    Nios2CPUClass *ncc = NIOS2_CPU_GET_CLASS(dev);
+    Error *local_err = NULL;
+
+    cpu_exec_realizefn(cs, &local_err);
+    if (local_err != NULL) {
+        error_propagate(errp, local_err);
+        return;
+    }
+
+    qemu_init_vcpu(cs);
+    cpu_reset(cs);
+
+    ncc->parent_realize(dev, errp);
+}
+
+static bool nios2_cpu_exec_interrupt(CPUState *cs, int interrupt_request)
+{
+    Nios2CPU *cpu = NIOS2_CPU(cs);
+    CPUNios2State *env = &cpu->env;
+
+    if ((interrupt_request & CPU_INTERRUPT_HARD) &&
+        (env->regs[CR_STATUS] & CR_STATUS_PIE)) {
+        cs->exception_index = EXCP_IRQ;
+        nios2_cpu_do_interrupt(cs);
+        return true;
+    }
+    return false;
+}
+
+
+static void nios2_cpu_disas_set_info(CPUState *cpu, disassemble_info *info)
+{
+    /* NOTE: NiosII R2 is not supported yet. */
+    info->mach = bfd_arch_nios2;
+#ifdef TARGET_WORDS_BIGENDIAN
+    info->print_insn = print_insn_big_nios2;
+#else
+    info->print_insn = print_insn_little_nios2;
+#endif
+}
+
+static int nios2_cpu_gdb_read_register(CPUState *cs, uint8_t *mem_buf, int n)
+{
+    Nios2CPU *cpu = NIOS2_CPU(cs);
+    CPUClass *cc = CPU_GET_CLASS(cs);
+    CPUNios2State *env = &cpu->env;
+
+    if (n > cc->gdb_num_core_regs) {
+        return 0;
+    }
+
+    if (n < 32) {          /* GP regs */
+        return gdb_get_reg32(mem_buf, env->regs[n]);
+    } else if (n == 32) {    /* PC */
+        return gdb_get_reg32(mem_buf, env->regs[R_PC]);
+    } else if (n < 49) {     /* Status regs */
+        return gdb_get_reg32(mem_buf, env->regs[n - 1]);
+    }
+
+    /* Invalid regs */
+    return 0;
+}
+
+static int nios2_cpu_gdb_write_register(CPUState *cs, uint8_t *mem_buf, int n)
+{
+    Nios2CPU *cpu = NIOS2_CPU(cs);
+    CPUClass *cc = CPU_GET_CLASS(cs);
+    CPUNios2State *env = &cpu->env;
+
+    if (n > cc->gdb_num_core_regs) {
+        return 0;
+    }
+
+    if (n < 32) {            /* GP regs */
+        env->regs[n] = ldl_p(mem_buf);
+    } else if (n == 32) {    /* PC */
+        env->regs[R_PC] = ldl_p(mem_buf);
+    } else if (n < 49) {     /* Status regs */
+        env->regs[n - 1] = ldl_p(mem_buf);
+    }
+
+    return 4;
+}
+
+static Property nios2_properties[] = {
+    DEFINE_PROP_BOOL("mmu_present", Nios2CPU, mmu_present, true),
+    DEFINE_PROP_END_OF_LIST(),
+};
+
+
+static void nios2_cpu_class_init(ObjectClass *oc, void *data)
+{
+    DeviceClass *dc = DEVICE_CLASS(oc);
+    CPUClass *cc = CPU_CLASS(oc);
+    Nios2CPUClass *ncc = NIOS2_CPU_CLASS(oc);
+
+    ncc->parent_realize = dc->realize;
+    dc->realize = nios2_cpu_realizefn;
+    dc->props = nios2_properties;
+    ncc->parent_reset = cc->reset;
+    cc->reset = nios2_cpu_reset;
+
+    cc->has_work = nios2_cpu_has_work;
+    cc->do_interrupt = nios2_cpu_do_interrupt;
+    cc->cpu_exec_interrupt = nios2_cpu_exec_interrupt;
+    cc->dump_state = nios2_cpu_dump_state;
+    cc->set_pc = nios2_cpu_set_pc;
+    cc->disas_set_info = nios2_cpu_disas_set_info;
+#ifdef CONFIG_USER_ONLY
+    cc->handle_mmu_fault = nios2_cpu_handle_mmu_fault;
+#else
+    cc->do_unaligned_access = nios2_cpu_do_unaligned_access;
+    cc->get_phys_page_debug = nios2_cpu_get_phys_page_debug;
+#endif
+    cc->gdb_read_register = nios2_cpu_gdb_read_register;
+    cc->gdb_write_register = nios2_cpu_gdb_write_register;
+    cc->gdb_num_core_regs = 49;
+}
+
+static const TypeInfo nios2_cpu_type_info = {
+    .name = TYPE_NIOS2_CPU,
+    .parent = TYPE_CPU,
+    .instance_size = sizeof(Nios2CPU),
+    .instance_init = nios2_cpu_initfn,
+    .class_size = sizeof(Nios2CPUClass),
+    .class_init = nios2_cpu_class_init,
+};
+
+static void nios2_cpu_register_types(void)
+{
+    type_register_static(&nios2_cpu_type_info);
+}
+
+type_init(nios2_cpu_register_types)
diff --git a/target/nios2/cpu.h b/target/nios2/cpu.h
new file mode 100644
index 0000000..91e73af
--- /dev/null
+++ b/target/nios2/cpu.h
@@ -0,0 +1,269 @@ 
+/*
+ * Altera Nios II virtual CPU header
+ *
+ * Copyright (c) 2012 Chris Wulff <crwulff@gmail.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, see
+ * <http://www.gnu.org/licenses/lgpl-2.1.html>
+ */
+#ifndef CPU_NIOS2_H
+#define CPU_NIOS2_H
+
+#include "qemu/osdep.h"
+#include "qemu-common.h"
+
+#define TARGET_LONG_BITS 32
+
+#define CPUArchState struct CPUNios2State
+
+#include "exec/cpu-defs.h"
+#include "fpu/softfloat.h"
+#include "qom/cpu.h"
+struct CPUNios2State;
+typedef struct CPUNios2State CPUNios2State;
+#if !defined(CONFIG_USER_ONLY)
+#include "mmu.h"
+#endif
+
+#define TYPE_NIOS2_CPU "nios2-cpu"
+
+#define NIOS2_CPU_CLASS(klass) \
+    OBJECT_CLASS_CHECK(Nios2CPUClass, (klass), TYPE_NIOS2_CPU)
+#define NIOS2_CPU(obj) \
+    OBJECT_CHECK(Nios2CPU, (obj), TYPE_NIOS2_CPU)
+#define NIOS2_CPU_GET_CLASS(obj) \
+    OBJECT_GET_CLASS(Nios2CPUClass, (obj), TYPE_NIOS2_CPU)
+
+/**
+ * Nios2CPUClass:
+ * @parent_reset: The parent class' reset handler.
+ *
+ * A Nios2 CPU model.
+ */
+typedef struct Nios2CPUClass {
+    /*< private >*/
+    CPUClass parent_class;
+    /*< public >*/
+
+    DeviceRealize parent_realize;
+    void (*parent_reset)(CPUState *cpu);
+} Nios2CPUClass;
+
+#define TARGET_HAS_ICE 1
+
+/* Configuration options for Nios II */
+#define RESET_ADDRESS         0x00000000
+#define EXCEPTION_ADDRESS     0x00000004
+#define FAST_TLB_MISS_ADDRESS 0x00000008
+
+
+/* GP regs + CR regs + PC */
+#define NUM_CORE_REGS (32 + 32 + 1)
+
+/* General purpose register aliases */
+#define R_ZERO   0
+#define R_AT     1
+#define R_RET0   2
+#define R_RET1   3
+#define R_ARG0   4
+#define R_ARG1   5
+#define R_ARG2   6
+#define R_ARG3   7
+#define R_ET     24
+#define R_BT     25
+#define R_GP     26
+#define R_SP     27
+#define R_FP     28
+#define R_EA     29
+#define R_BA     30
+#define R_RA     31
+
+/* Control register aliases */
+#define CR_BASE  32
+#define CR_STATUS    (CR_BASE + 0)
+#define   CR_STATUS_PIE  (1 << 0)
+#define   CR_STATUS_U    (1 << 1)
+#define   CR_STATUS_EH   (1 << 2)
+#define   CR_STATUS_IH   (1 << 3)
+#define   CR_STATUS_IL   (63 << 4)
+#define   CR_STATUS_CRS  (63 << 10)
+#define   CR_STATUS_PRS  (63 << 16)
+#define   CR_STATUS_NMI  (1 << 22)
+#define   CR_STATUS_RSIE (1 << 23)
+#define CR_ESTATUS   (CR_BASE + 1)
+#define CR_BSTATUS   (CR_BASE + 2)
+#define CR_IENABLE   (CR_BASE + 3)
+#define CR_IPENDING  (CR_BASE + 4)
+#define CR_CPUID     (CR_BASE + 5)
+#define CR_CTL6      (CR_BASE + 6)
+#define CR_EXCEPTION (CR_BASE + 7)
+#define CR_PTEADDR   (CR_BASE + 8)
+#define   CR_PTEADDR_PTBASE_SHIFT 22
+#define   CR_PTEADDR_PTBASE_MASK  (0x3FF << CR_PTEADDR_PTBASE_SHIFT)
+#define   CR_PTEADDR_VPN_SHIFT    2
+#define   CR_PTEADDR_VPN_MASK     (0xFFFFF << CR_PTEADDR_VPN_SHIFT)
+#define CR_TLBACC    (CR_BASE + 9)
+#define   CR_TLBACC_IGN_SHIFT 25
+#define   CR_TLBACC_IGN_MASK  (0x7F << CR_TLBACC_IGN_SHIFT)
+#define   CR_TLBACC_C         (1 << 24)
+#define   CR_TLBACC_R         (1 << 23)
+#define   CR_TLBACC_W         (1 << 22)
+#define   CR_TLBACC_X         (1 << 21)
+#define   CR_TLBACC_G         (1 << 20)
+#define   CR_TLBACC_PFN_MASK  0x000FFFFF
+#define CR_TLBMISC   (CR_BASE + 10)
+#define   CR_TLBMISC_WAY_SHIFT 20
+#define   CR_TLBMISC_WAY_MASK  (0xF << CR_TLBMISC_WAY_SHIFT)
+#define   CR_TLBMISC_RD        (1 << 19)
+#define   CR_TLBMISC_WR        (1 << 18)
+#define   CR_TLBMISC_PID_SHIFT 4
+#define   CR_TLBMISC_PID_MASK  (0x3FFF << CR_TLBMISC_PID_SHIFT)
+#define   CR_TLBMISC_DBL       (1 << 3)
+#define   CR_TLBMISC_BAD       (1 << 2)
+#define   CR_TLBMISC_PERM      (1 << 1)
+#define   CR_TLBMISC_D         (1 << 0)
+#define CR_ENCINJ    (CR_BASE + 11)
+#define CR_BADADDR   (CR_BASE + 12)
+#define CR_CONFIG    (CR_BASE + 13)
+#define CR_MPUBASE   (CR_BASE + 14)
+#define CR_MPUACC    (CR_BASE + 15)
+
+/* Other registers */
+#define R_PC         64
+
+/* Exceptions */
+#define EXCP_BREAK    -1
+#define EXCP_RESET    0
+#define EXCP_PRESET   1
+#define EXCP_IRQ      2
+#define EXCP_TRAP     3
+#define EXCP_UNIMPL   4
+#define EXCP_ILLEGAL  5
+#define EXCP_UNALIGN  6
+#define EXCP_UNALIGND 7
+#define EXCP_DIV      8
+#define EXCP_SUPERA   9
+#define EXCP_SUPERI   10
+#define EXCP_SUPERD   11
+#define EXCP_TLBD     12
+#define EXCP_TLBX     13
+#define EXCP_TLBR     14
+#define EXCP_TLBW     15
+#define EXCP_MPUI     16
+#define EXCP_MPUD     17
+
+#define CPU_INTERRUPT_NMI       CPU_INTERRUPT_TGT_EXT_3
+
+#define NB_MMU_MODES 2
+
+struct CPUNios2State {
+    uint32_t regs[NUM_CORE_REGS];
+
+#if !defined(CONFIG_USER_ONLY)
+    Nios2MMU mmu;
+
+    uint32_t irq_pending;
+#endif
+
+    CPU_COMMON
+};
+
+/**
+ * Nios2CPU:
+ * @env: #CPUNios2State
+ *
+ * A Nios2 CPU.
+ */
+typedef struct Nios2CPU {
+    /*< private >*/
+    CPUState parent_obj;
+    /*< public >*/
+
+    CPUNios2State env;
+    bool mmu_present;
+
+    /* Addresses that are hard-coded in the FPGA build settings */
+    uint32_t reset_addr;
+    uint32_t exception_addr;
+    uint32_t fast_tlb_miss_addr;
+} Nios2CPU;
+
+static inline Nios2CPU *nios2_env_get_cpu(CPUNios2State *env)
+{
+    return NIOS2_CPU(container_of(env, Nios2CPU, env));
+}
+
+#define ENV_GET_CPU(e) CPU(nios2_env_get_cpu(e))
+
+#define ENV_OFFSET offsetof(Nios2CPU, env)
+
+void nios2_tcg_init(void);
+Nios2CPU *cpu_nios2_init(const char *cpu_model);
+void nios2_cpu_do_interrupt(CPUState *cs);
+int cpu_nios2_signal_handler(int host_signum, void *pinfo, void *puc);
+void dump_mmu(FILE *f, fprintf_function cpu_fprintf, CPUNios2State *env);
+void nios2_cpu_dump_state(CPUState *cpu, FILE *f, fprintf_function cpu_fprintf,
+                          int flags);
+hwaddr nios2_cpu_get_phys_page_debug(CPUState *cpu, vaddr addr);
+void nios2_cpu_do_unaligned_access(CPUState *cpu, vaddr addr,
+                                   MMUAccessType access_type,
+                                   int mmu_idx, uintptr_t retaddr);
+
+qemu_irq *nios2_cpu_pic_init(Nios2CPU *cpu);
+void nios2_check_interrupts(CPUNios2State *env);
+
+#define TARGET_PHYS_ADDR_SPACE_BITS 32
+#define TARGET_VIRT_ADDR_SPACE_BITS 32
+
+#define cpu_init(cpu_model) CPU(cpu_nios2_init(cpu_model))
+
+#define cpu_gen_code cpu_nios2_gen_code
+#define cpu_signal_handler cpu_nios2_signal_handler
+
+#define CPU_SAVE_VERSION 1
+
+#define TARGET_PAGE_BITS 12
+
+/* MMU modes definitions */
+#define MMU_MODE0_SUFFIX _kernel
+#define MMU_MODE1_SUFFIX _user
+#define MMU_SUPERVISOR_IDX  0
+#define MMU_USER_IDX        1
+
+static inline int cpu_mmu_index(CPUNios2State *env, bool ifetch)
+{
+    return (env->regs[CR_STATUS] & CR_STATUS_U) ? MMU_USER_IDX :
+                                                  MMU_SUPERVISOR_IDX;
+}
+
+int nios2_cpu_handle_mmu_fault(CPUState *env, vaddr address,
+                               int rw, int mmu_idx);
+
+static inline int cpu_interrupts_enabled(CPUNios2State *env)
+{
+    return env->regs[CR_STATUS] & CR_STATUS_PIE;
+}
+
+#include "exec/cpu-all.h"
+#include "exec/exec-all.h"
+
+static inline void cpu_get_tb_cpu_state(CPUNios2State *env, target_ulong *pc,
+                                        target_ulong *cs_base, uint32_t *flags)
+{
+    *pc = env->regs[R_PC];
+    *cs_base = 0;
+    *flags = (env->regs[CR_STATUS] & (CR_STATUS_EH | CR_STATUS_U));
+}
+
+#endif /* CPU_NIOS2_H */
diff --git a/target/nios2/helper.c b/target/nios2/helper.c
new file mode 100644
index 0000000..cd4f353
--- /dev/null
+++ b/target/nios2/helper.c
@@ -0,0 +1,313 @@ 
+/*
+ * Altera Nios II helper routines.
+ *
+ * Copyright (c) 2012 Chris Wulff <crwulff@gmail.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, see
+ * <http://www.gnu.org/licenses/lgpl-2.1.html>
+ */
+
+#include <stdio.h>
+#include <string.h>
+#include <assert.h>
+
+#include "cpu.h"
+#include "qemu/osdep.h"
+#include "qemu/host-utils.h"
+#include "qapi/error.h"
+#include "exec/exec-all.h"
+#include "exec/log.h"
+#include "exec/helper-proto.h"
+
+#if defined(CONFIG_USER_ONLY)
+
+void nios2_cpu_do_interrupt(CPUState *cs)
+{
+    Nios2CPU *cpu = NIOS2_CPU(cs);
+    CPUNios2State *env = &cpu->env;
+    cs->exception_index = -1;
+    env->regs[R_EA] = env->regs[R_PC] + 4;
+}
+
+int nios2_cpu_handle_mmu_fault(CPUState *cs, vaddr address, int rw, int mmu_idx)
+{
+    cs->exception_index = 0xaa;
+    /* Page 0x1000 is kuser helper */
+    if (address < 0x1000 || address >= 0x2000) {
+        cpu_dump_state(cs, stderr, fprintf, 0);
+    }
+    return 1;
+}
+
+#else /* !CONFIG_USER_ONLY */
+
+void nios2_cpu_do_interrupt(CPUState *cs)
+{
+    Nios2CPU *cpu = NIOS2_CPU(cs);
+    CPUNios2State *env = &cpu->env;
+
+    switch (cs->exception_index) {
+    case EXCP_IRQ:
+        assert(env->regs[CR_STATUS] & CR_STATUS_PIE);
+
+        qemu_log_mask(CPU_LOG_INT, "interrupt at pc=%x\n", env->regs[R_PC]);
+
+        env->regs[CR_ESTATUS] = env->regs[CR_STATUS];
+        env->regs[CR_STATUS] |= CR_STATUS_IH;
+        env->regs[CR_STATUS] &= ~(CR_STATUS_PIE | CR_STATUS_U);
+
+        env->regs[CR_EXCEPTION] &= ~(0x1F << 2);
+        env->regs[CR_EXCEPTION] |= (cs->exception_index & 0x1F) << 2;
+
+        env->regs[R_EA] = env->regs[R_PC] + 4;
+        env->regs[R_PC] = cpu->exception_addr;
+        break;
+
+    case EXCP_TLBD:
+        if ((env->regs[CR_STATUS] & CR_STATUS_EH) == 0) {
+            qemu_log_mask(CPU_LOG_INT, "TLB MISS (fast) at pc=%x\n",
+                          env->regs[R_PC]);
+
+            /* Fast TLB miss */
+            /* Variation from the spec. Table 3-35 of the cpu reference shows
+             * estatus not being changed for TLB miss but this appears to
+             * be incorrect. */
+            env->regs[CR_ESTATUS] = env->regs[CR_STATUS];
+            env->regs[CR_STATUS] |= CR_STATUS_EH;
+            env->regs[CR_STATUS] &= ~(CR_STATUS_PIE | CR_STATUS_U);
+
+            env->regs[CR_EXCEPTION] &= ~(0x1F << 2);
+            env->regs[CR_EXCEPTION] |= (cs->exception_index & 0x1F) << 2;
+
+            env->regs[CR_TLBMISC] &= ~CR_TLBMISC_DBL;
+            env->regs[CR_TLBMISC] |= CR_TLBMISC_WR;
+
+            env->regs[R_EA] = env->regs[R_PC] + 4;
+            env->regs[R_PC] = cpu->fast_tlb_miss_addr;
+        } else {
+            qemu_log_mask(CPU_LOG_INT, "TLB MISS (double) at pc=%x\n",
+                          env->regs[R_PC]);
+
+            /* Double TLB miss */
+            env->regs[CR_STATUS] |= CR_STATUS_EH;
+            env->regs[CR_STATUS] &= ~(CR_STATUS_PIE | CR_STATUS_U);
+
+            env->regs[CR_EXCEPTION] &= ~(0x1F << 2);
+            env->regs[CR_EXCEPTION] |= (cs->exception_index & 0x1F) << 2;
+
+            env->regs[CR_TLBMISC] |= CR_TLBMISC_DBL;
+
+            env->regs[R_PC] = cpu->exception_addr;
+        }
+        break;
+
+    case EXCP_TLBR:
+    case EXCP_TLBW:
+    case EXCP_TLBX:
+        qemu_log_mask(CPU_LOG_INT, "TLB PERM at pc=%x\n", env->regs[R_PC]);
+
+        env->regs[CR_ESTATUS] = env->regs[CR_STATUS];
+        env->regs[CR_STATUS] |= CR_STATUS_EH;
+        env->regs[CR_STATUS] &= ~(CR_STATUS_PIE | CR_STATUS_U);
+
+        env->regs[CR_EXCEPTION] &= ~(0x1F << 2);
+        env->regs[CR_EXCEPTION] |= (cs->exception_index & 0x1F) << 2;
+
+        if ((env->regs[CR_STATUS] & CR_STATUS_EH) == 0) {
+            env->regs[CR_TLBMISC] |= CR_TLBMISC_WR;
+        }
+
+        env->regs[R_EA] = env->regs[R_PC] + 4;
+        env->regs[R_PC] = cpu->exception_addr;
+        break;
+
+    case EXCP_SUPERA:
+    case EXCP_SUPERI:
+    case EXCP_SUPERD:
+        qemu_log_mask(CPU_LOG_INT, "SUPERVISOR exception at pc=%x\n",
+                      env->regs[R_PC]);
+
+        if ((env->regs[CR_STATUS] & CR_STATUS_EH) == 0) {
+            env->regs[CR_ESTATUS] = env->regs[CR_STATUS];
+            env->regs[R_EA] = env->regs[R_PC] + 4;
+        }
+
+        env->regs[CR_STATUS] |= CR_STATUS_EH;
+        env->regs[CR_STATUS] &= ~(CR_STATUS_PIE | CR_STATUS_U);
+
+        env->regs[CR_EXCEPTION] &= ~(0x1F << 2);
+        env->regs[CR_EXCEPTION] |= (cs->exception_index & 0x1F) << 2;
+
+        env->regs[R_PC] = cpu->exception_addr;
+        break;
+
+    case EXCP_ILLEGAL:
+    case EXCP_TRAP:
+        qemu_log_mask(CPU_LOG_INT, "TRAP exception at pc=%x\n",
+                      env->regs[R_PC]);
+
+        if ((env->regs[CR_STATUS] & CR_STATUS_EH) == 0) {
+            env->regs[CR_ESTATUS] = env->regs[CR_STATUS];
+            env->regs[R_EA] = env->regs[R_PC] + 4;
+        }
+
+        env->regs[CR_STATUS] |= CR_STATUS_EH;
+        env->regs[CR_STATUS] &= ~(CR_STATUS_PIE | CR_STATUS_U);
+
+        env->regs[CR_EXCEPTION] &= ~(0x1F << 2);
+        env->regs[CR_EXCEPTION] |= (cs->exception_index & 0x1F) << 2;
+
+        env->regs[R_PC] = cpu->exception_addr;
+        break;
+
+    case EXCP_BREAK:
+        if ((env->regs[CR_STATUS] & CR_STATUS_EH) == 0) {
+            env->regs[CR_BSTATUS] = env->regs[CR_STATUS];
+            env->regs[R_BA] = env->regs[R_PC] + 4;
+        }
+
+        env->regs[CR_STATUS] |= CR_STATUS_EH;
+        env->regs[CR_STATUS] &= ~(CR_STATUS_PIE | CR_STATUS_U);
+
+        env->regs[CR_EXCEPTION] &= ~(0x1F << 2);
+        env->regs[CR_EXCEPTION] |= (cs->exception_index & 0x1F) << 2;
+
+        env->regs[R_PC] = cpu->exception_addr;
+        break;
+
+    default:
+        cpu_abort(cs, "unhandled exception type=%d\n",
+                  cs->exception_index);
+        break;
+    }
+}
+
+static int cpu_nios2_handle_virtual_page(
+    CPUState *cs, target_ulong address, int rw, int mmu_idx)
+{
+    Nios2CPU *cpu = NIOS2_CPU(cs);
+    CPUNios2State *env = &cpu->env;
+    target_ulong vaddr, paddr;
+    Nios2MMULookup lu;
+    unsigned int hit;
+    hit = mmu_translate(env, &lu, address, rw, mmu_idx);
+    if (hit) {
+        vaddr = address & TARGET_PAGE_MASK;
+        paddr = lu.paddr + vaddr - lu.vaddr;
+
+        if (((rw == 0) && (lu.prot & PAGE_READ)) ||
+            ((rw == 1) && (lu.prot & PAGE_WRITE)) ||
+            ((rw == 2) && (lu.prot & PAGE_EXEC))) {
+
+            tlb_set_page(cs, vaddr, paddr, lu.prot,
+                         mmu_idx, TARGET_PAGE_SIZE);
+            return 0;
+        } else {
+            /* Permission violation */
+            cs->exception_index = (rw == 0) ? EXCP_TLBR :
+                                               ((rw == 1) ? EXCP_TLBW :
+                                                            EXCP_TLBX);
+        }
+    } else {
+        cs->exception_index = EXCP_TLBD;
+    }
+
+    if (rw == 2) {
+        env->regs[CR_TLBMISC] &= ~CR_TLBMISC_D;
+    } else {
+        env->regs[CR_TLBMISC] |= CR_TLBMISC_D;
+    }
+    env->regs[CR_PTEADDR] &= CR_PTEADDR_PTBASE_MASK;
+    env->regs[CR_PTEADDR] |= (address >> 10) & CR_PTEADDR_VPN_MASK;
+    env->mmu.pteaddr_wr = env->regs[CR_PTEADDR];
+    env->regs[CR_BADADDR] = address;
+    return 1;
+}
+
+int nios2_cpu_handle_mmu_fault(CPUState *cs, vaddr address, int rw, int mmu_idx)
+{
+    Nios2CPU *cpu = NIOS2_CPU(cs);
+    CPUNios2State *env = &cpu->env;
+
+    if (cpu->mmu_present) {
+        if (MMU_SUPERVISOR_IDX == mmu_idx) {
+            if (address >= 0xC0000000) {
+                /* Kernel physical page - TLB bypassed */
+                address &= TARGET_PAGE_MASK;
+                tlb_set_page(cs, address, address, PAGE_BITS,
+                             mmu_idx, TARGET_PAGE_SIZE);
+            } else if (address >= 0x80000000) {
+                /* Kernel virtual page */
+                return cpu_nios2_handle_virtual_page(cs, address, rw, mmu_idx);
+            } else {
+                /* User virtual page */
+                return cpu_nios2_handle_virtual_page(cs, address, rw, mmu_idx);
+            }
+        } else {
+            if (address >= 0x80000000) {
+                /* Illegal access from user mode */
+                cs->exception_index = EXCP_SUPERA;
+                env->regs[CR_BADADDR] = address;
+                return 1;
+            } else {
+                /* User virtual page */
+                return cpu_nios2_handle_virtual_page(cs, address, rw, mmu_idx);
+            }
+        }
+    } else {
+        /* No MMU */
+        address &= TARGET_PAGE_MASK;
+        tlb_set_page(cs, address, address, PAGE_BITS,
+                     mmu_idx, TARGET_PAGE_SIZE);
+    }
+
+    return 0;
+}
+
+hwaddr nios2_cpu_get_phys_page_debug(CPUState *cs, vaddr addr)
+{
+    Nios2CPU *cpu = NIOS2_CPU(cs);
+    CPUNios2State *env = &cpu->env;
+    target_ulong vaddr, paddr = 0;
+    Nios2MMULookup lu;
+    unsigned int hit;
+
+    if (cpu->mmu_present && (addr < 0xC0000000)) {
+        hit = mmu_translate(env, &lu, addr, 0, 0);
+        if (hit) {
+            vaddr = addr & TARGET_PAGE_MASK;
+            paddr = lu.paddr + vaddr - lu.vaddr;
+        } else {
+            paddr = -1;
+            qemu_log("cpu_get_phys_page debug MISS: %08lX\n", addr);
+        }
+    } else {
+        paddr = addr & TARGET_PAGE_MASK;
+    }
+
+    return paddr;
+}
+
+void nios2_cpu_do_unaligned_access(CPUState *cs, vaddr addr,
+                                   MMUAccessType access_type,
+                                   int mmu_idx, uintptr_t retaddr)
+{
+    Nios2CPU *cpu = NIOS2_CPU(cs);
+    CPUNios2State *env = &cpu->env;
+
+    env->regs[CR_BADADDR] = addr;
+    env->regs[CR_EXCEPTION] = EXCP_UNALIGN << 2;
+    helper_raise_exception(env, EXCP_UNALIGN);
+}
+#endif /* !CONFIG_USER_ONLY */
diff --git a/target/nios2/helper.h b/target/nios2/helper.h
new file mode 100644
index 0000000..b86a2f2
--- /dev/null
+++ b/target/nios2/helper.h
@@ -0,0 +1,27 @@ 
+/*
+ * Altera Nios II helper routines header.
+ *
+ * Copyright (c) 2012 Chris Wulff <crwulff@gmail.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, see
+ * <http://www.gnu.org/licenses/lgpl-2.1.html>
+ */
+
+DEF_HELPER_2(raise_exception, void, env, i32)
+
+#if !defined(CONFIG_USER_ONLY)
+DEF_HELPER_2(mmu_read, i32, env, i32)
+DEF_HELPER_3(mmu_write, void, env, i32, i32)
+DEF_HELPER_1(check_interrupts, void, env)
+#endif
diff --git a/target/nios2/mmu.c b/target/nios2/mmu.c
new file mode 100644
index 0000000..875fbb0
--- /dev/null
+++ b/target/nios2/mmu.c
@@ -0,0 +1,292 @@ 
+/*
+ * Altera Nios II MMU emulation for qemu.
+ *
+ * Copyright (C) 2012 Chris Wulff <crwulff@gmail.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, see
+ * <http://www.gnu.org/licenses/lgpl-2.1.html>
+ */
+
+#include "qemu/osdep.h"
+#include "qemu-common.h"
+#include "cpu.h"
+#include "exec/exec-all.h"
+#include "mmu.h"
+
+#if !defined(CONFIG_USER_ONLY)
+
+/* Define this to enable MMU debug messages */
+/* #define DEBUG_MMU */
+
+#ifdef DEBUG_MMU
+#define MMU_LOG(x) x
+#else
+#define MMU_LOG(x)
+#endif
+
+void tlb_fill(CPUState *cs, target_ulong addr, MMUAccessType access_type,
+              int mmu_idx, uintptr_t retaddr)
+{
+    int ret;
+
+    ret = nios2_cpu_handle_mmu_fault(cs, addr, access_type, mmu_idx);
+    if (unlikely(ret)) {
+        if (retaddr) {
+            /* now we have a real cpu fault */
+            cpu_restore_state(cs, retaddr);
+        }
+        cpu_loop_exit(cs);
+    }
+}
+
+uint32_t mmu_read(CPUNios2State *env, uint32_t rn)
+{
+    switch (rn) {
+    case CR_TLBACC:
+        MMU_LOG(qemu_log("TLBACC READ %08X\n", env->regs[rn]));
+        break;
+
+    case CR_TLBMISC:
+        MMU_LOG(qemu_log("TLBMISC READ %08X\n", env->regs[rn]));
+        break;
+
+    case CR_PTEADDR:
+        MMU_LOG(qemu_log("PTEADDR READ %08X\n", env->regs[rn]));
+        break;
+
+    default:
+        break;
+    }
+    return env->regs[rn];
+}
+
+/* rw - 0 = read, 1 = write, 2 = fetch.  */
+unsigned int mmu_translate(CPUNios2State *env,
+                           Nios2MMULookup *lu,
+                           target_ulong vaddr, int rw, int mmu_idx)
+{
+    int pid = (env->mmu.tlbmisc_wr & CR_TLBMISC_PID_MASK) >> 4;
+    int vpn = vaddr >> 12;
+
+    MMU_LOG(qemu_log("mmu_translate vaddr %08X, pid %08X, vpn %08X\n",
+                     vaddr, pid, vpn));
+
+    int way;
+    for (way = 0; way < env->mmu.tlb_num_ways; way++) {
+
+        Nios2TLBEntry *entry =
+            &env->mmu.tlb[(way * env->mmu.tlb_num_ways) +
+                          (vpn & env->mmu.tlb_entry_mask)];
+
+        MMU_LOG(qemu_log("TLB[%d] TAG %08X, VPN %08X\n",
+                         (way * env->mmu.tlb_num_ways) +
+                         (vpn & env->mmu.tlb_entry_mask),
+                         entry->tag, (entry->tag >> 12)));
+
+        if (((entry->tag >> 12) != vpn) ||
+            (((entry->tag & (1 << 11)) == 0) &&
+            ((entry->tag & ((1 << env->mmu.pid_bits) - 1)) != pid))) {
+            continue;
+        }
+        lu->vaddr = vaddr & TARGET_PAGE_MASK;
+        lu->paddr = (entry->data & CR_TLBACC_PFN_MASK) << TARGET_PAGE_BITS;
+        lu->prot = ((entry->data & CR_TLBACC_R) ? PAGE_READ : 0) |
+                   ((entry->data & CR_TLBACC_W) ? PAGE_WRITE : 0) |
+                   ((entry->data & CR_TLBACC_X) ? PAGE_EXEC : 0);
+
+        MMU_LOG(qemu_log("HIT TLB[%d] %08X %08X %08X\n",
+                         (way * env->mmu.tlb_num_ways) +
+                         (vpn & env->mmu.tlb_entry_mask),
+                         lu->vaddr, lu->paddr, lu->prot));
+        return 1;
+    }
+    return 0;
+}
+
+static void mmu_flush_pid(CPUNios2State *env, uint32_t pid)
+{
+    CPUState *cs = ENV_GET_CPU(env);
+    int idx;
+    MMU_LOG(qemu_log("TLB Flush PID %d\n", pid));
+
+    for (idx = 0; idx < env->mmu.tlb_num_entries; idx++) {
+        Nios2TLBEntry *entry = &env->mmu.tlb[idx];
+
+        MMU_LOG(qemu_log("TLB[%d] => %08X %08X\n",
+                         idx, entry->tag, entry->data));
+
+        if ((entry->tag & (1 << 10)) && (!(entry->tag & (1 << 11))) &&
+            ((entry->tag & ((1 << env->mmu.pid_bits) - 1)) == pid)) {
+            uint32_t vaddr = entry->tag & TARGET_PAGE_MASK;
+
+            MMU_LOG(qemu_log("TLB Flush Page %08X\n", vaddr));
+
+            tlb_flush_page(cs, vaddr);
+        }
+    }
+}
+
+void mmu_write(CPUNios2State *env, uint32_t rn, uint32_t v)
+{
+    CPUState *cs = ENV_GET_CPU(env);
+
+    MMU_LOG(qemu_log("mmu_write %08X = %08X\n", rn, v));
+
+    switch (rn) {
+    case CR_TLBACC:
+        MMU_LOG(qemu_log("TLBACC: IG %02X, FLAGS %c%c%c%c%c, PFN %05X\n",
+                         v >> CR_TLBACC_IGN_SHIFT,
+                         (v & CR_TLBACC_C) ? 'C' : '.',
+                         (v & CR_TLBACC_R) ? 'R' : '.',
+                         (v & CR_TLBACC_W) ? 'W' : '.',
+                         (v & CR_TLBACC_X) ? 'X' : '.',
+                         (v & CR_TLBACC_G) ? 'G' : '.',
+                         v & CR_TLBACC_PFN_MASK));
+
+        /* if tlbmisc.WE == 1 then trigger a TLB write on writes to TLBACC */
+        if (env->regs[CR_TLBMISC] & CR_TLBMISC_WR) {
+            int way = (env->regs[CR_TLBMISC] >> CR_TLBMISC_WAY_SHIFT);
+            int vpn = (env->mmu.pteaddr_wr & CR_PTEADDR_VPN_MASK) >> 2;
+            int pid = (env->mmu.tlbmisc_wr & CR_TLBMISC_PID_MASK) >> 4;
+            int g = (v & CR_TLBACC_G) ? 1 : 0;
+            int valid = ((vpn & CR_TLBACC_PFN_MASK) < 0xC0000) ? 1 : 0;
+            Nios2TLBEntry *entry =
+                &env->mmu.tlb[(way * env->mmu.tlb_num_ways) +
+                              (vpn & env->mmu.tlb_entry_mask)];
+            uint32_t newTag = (vpn << 12) | (g << 11) | (valid << 10) | pid;
+            uint32_t newData = v & (CR_TLBACC_C | CR_TLBACC_R | CR_TLBACC_W |
+                                    CR_TLBACC_X | CR_TLBACC_PFN_MASK);
+
+            if ((entry->tag != newTag) || (entry->data != newData)) {
+                if (entry->tag & (1 << 10)) {
+                    /* Flush existing entry */
+                    MMU_LOG(qemu_log("TLB Flush Page (OLD) %08X\n",
+                                     entry->tag & TARGET_PAGE_MASK));
+                    tlb_flush_page(cs, entry->tag & TARGET_PAGE_MASK);
+                }
+                entry->tag = newTag;
+                entry->data = newData;
+                MMU_LOG(qemu_log("TLB[%d] = %08X %08X\n",
+                                 (way * env->mmu.tlb_num_ways) +
+                                 (vpn & env->mmu.tlb_entry_mask),
+                                 entry->tag, entry->data));
+            }
+            /* Auto-increment tlbmisc.WAY */
+            env->regs[CR_TLBMISC] =
+                (env->regs[CR_TLBMISC] & ~CR_TLBMISC_WAY_MASK) |
+                (((way + 1) & (env->mmu.tlb_num_ways - 1)) <<
+                 CR_TLBMISC_WAY_SHIFT);
+        }
+
+        /* Writes to TLBACC don't change the read-back value */
+        env->mmu.tlbacc_wr = v;
+        break;
+
+    case CR_TLBMISC:
+        MMU_LOG(qemu_log("TLBMISC: WAY %X, FLAGS %c%c%c%c%c%c, PID %04X\n",
+                         v >> CR_TLBMISC_WAY_SHIFT,
+                         (v & CR_TLBMISC_RD) ? 'R' : '.',
+                         (v & CR_TLBMISC_WR) ? 'W' : '.',
+                         (v & CR_TLBMISC_DBL) ? '2' : '.',
+                         (v & CR_TLBMISC_BAD) ? 'B' : '.',
+                         (v & CR_TLBMISC_PERM) ? 'P' : '.',
+                         (v & CR_TLBMISC_D) ? 'D' : '.',
+                         (v & CR_TLBMISC_PID_MASK) >> 4));
+
+        if ((v & CR_TLBMISC_PID_MASK) !=
+            (env->mmu.tlbmisc_wr & CR_TLBMISC_PID_MASK)) {
+            mmu_flush_pid(env, (env->mmu.tlbmisc_wr & CR_TLBMISC_PID_MASK) >>
+                               CR_TLBMISC_PID_SHIFT);
+        }
+        /* if tlbmisc.RD == 1 then trigger a TLB read on writes to TLBMISC */
+        if (v & CR_TLBMISC_RD) {
+            int way = (v >> CR_TLBMISC_WAY_SHIFT);
+            int vpn = (env->mmu.pteaddr_wr & CR_PTEADDR_VPN_MASK) >> 2;
+            Nios2TLBEntry *entry =
+                &env->mmu.tlb[(way * env->mmu.tlb_num_ways) +
+                              (vpn & env->mmu.tlb_entry_mask)];
+
+            env->regs[CR_TLBACC] &= CR_TLBACC_IGN_MASK;
+            env->regs[CR_TLBACC] |= entry->data;
+            env->regs[CR_TLBACC] |= (entry->tag & (1 << 11)) ? CR_TLBACC_G : 0;
+            env->regs[CR_TLBMISC] =
+                (v & ~CR_TLBMISC_PID_MASK) |
+                ((entry->tag & ((1 << env->mmu.pid_bits) - 1)) <<
+                 CR_TLBMISC_PID_SHIFT);
+            env->regs[CR_PTEADDR] &= ~CR_PTEADDR_VPN_MASK;
+            env->regs[CR_PTEADDR] |= (entry->tag >> 12) << CR_PTEADDR_VPN_SHIFT;
+            MMU_LOG(qemu_log("TLB READ way %d, vpn %05X, tag %08X, data %08X, "
+                             "tlbacc %08X, tlbmisc %08X, pteaddr %08X\n",
+                             way, vpn, entry->tag, entry->data,
+                             env->regs[CR_TLBACC], env->regs[CR_TLBMISC],
+                             env->regs[CR_PTEADDR]));
+        } else {
+            env->regs[CR_TLBMISC] = v;
+        }
+
+        env->mmu.tlbmisc_wr = v;
+        break;
+
+    case CR_PTEADDR:
+        MMU_LOG(qemu_log("PTEADDR: PTBASE %03X, VPN %05X\n",
+                         v >> CR_PTEADDR_PTBASE_SHIFT,
+                         (v & CR_PTEADDR_VPN_MASK) >> CR_PTEADDR_VPN_SHIFT));
+
+        /* Writes to PTEADDR don't change the read-back VPN value */
+        env->regs[CR_PTEADDR] = (v & ~CR_PTEADDR_VPN_MASK) |
+                                (env->regs[CR_PTEADDR] & CR_PTEADDR_VPN_MASK);
+        env->mmu.pteaddr_wr = v;
+        break;
+
+    default:
+        break;
+    }
+}
+
+void mmu_init(Nios2MMU *mmu)
+{
+    MMU_LOG(qemu_log("mmu_init\n"));
+
+    mmu->pid_bits = 8;          /* TODO: get this from ALTR,pid-num-bits */
+    mmu->tlb_num_ways = 16;     /* TODO: get this from ALTR,tlb-num-ways */
+    mmu->tlb_num_entries = 256; /* TODO: get this from ALTR,tlb-num-entries */
+    mmu->tlb_entry_mask = (mmu->tlb_num_entries / mmu->tlb_num_ways) - 1;
+    mmu->tlb = g_new0(Nios2TLBEntry, mmu->tlb_num_entries);
+}
+
+void dump_mmu(FILE *f, fprintf_function cpu_fprintf, CPUNios2State *env)
+{
+    int i;
+    cpu_fprintf(f, "MMU: ways %d, entries %d, pid bits %d\n",
+                env->mmu.tlb_num_ways, env->mmu.tlb_num_entries,
+                env->mmu.pid_bits);
+
+    for (i = 0; i < env->mmu.tlb_num_entries; i++) {
+        Nios2TLBEntry *entry = &env->mmu.tlb[i];
+        cpu_fprintf(f, "TLB[%d] = %08X %08X %c VPN %05X "
+                    "PID %02X %c PFN %05X %c%c%c%c\n",
+                    i, entry->tag, entry->data,
+                    (entry->tag & (1 << 10)) ? 'V' : '-',
+                    entry->tag >> 12,
+                    entry->tag & ((1 << env->mmu.pid_bits) - 1),
+                    (entry->tag & (1 << 11)) ? 'G' : '-',
+                    entry->data & CR_TLBACC_PFN_MASK,
+                    (entry->data & CR_TLBACC_C) ? 'C' : '-',
+                    (entry->data & CR_TLBACC_R) ? 'R' : '-',
+                    (entry->data & CR_TLBACC_W) ? 'W' : '-',
+                    (entry->data & CR_TLBACC_X) ? 'X' : '-');
+    }
+}
+
+#endif /* !CONFIG_USER_ONLY */
diff --git a/target/nios2/mmu.h b/target/nios2/mmu.h
new file mode 100644
index 0000000..797db67
--- /dev/null
+++ b/target/nios2/mmu.h
@@ -0,0 +1,54 @@ 
+/*
+ * Altera Nios II MMU emulation for qemu.
+ *
+ * Copyright (C) 2012 Chris Wulff <crwulff@gmail.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, see
+ * <http://www.gnu.org/licenses/lgpl-2.1.html>
+ */
+#ifndef MMU_NIOS2_H
+#define MMU_NIOS2_H
+
+
+typedef struct Nios2TLBEntry {
+    target_ulong tag;
+    target_ulong data;
+} Nios2TLBEntry;
+
+typedef struct Nios2MMU {
+    int pid_bits;
+    int tlb_num_ways;
+    int tlb_num_entries;
+    int tlb_entry_mask;
+    uint32_t pteaddr_wr;
+    uint32_t tlbacc_wr;
+    uint32_t tlbmisc_wr;
+    Nios2TLBEntry *tlb;
+} Nios2MMU;
+
+typedef struct Nios2MMULookup {
+    target_ulong vaddr;
+    target_ulong paddr;
+    int prot;
+} Nios2MMULookup;
+
+void mmu_flip_um(CPUNios2State *env, unsigned int um);
+unsigned int mmu_translate(CPUNios2State *env,
+                           Nios2MMULookup *lu,
+                           target_ulong vaddr, int rw, int mmu_idx);
+uint32_t mmu_read(CPUNios2State *env, uint32_t rn);
+void mmu_write(CPUNios2State *env, uint32_t rn, uint32_t v);
+void mmu_init(Nios2MMU *mmu);
+
+#endif /* MMU_NIOS2_H */
diff --git a/target/nios2/monitor.c b/target/nios2/monitor.c
new file mode 100644
index 0000000..422c816
--- /dev/null
+++ b/target/nios2/monitor.c
@@ -0,0 +1,35 @@ 
+/*
+ * QEMU monitor
+ *
+ * Copyright (c) 2003-2004 Fabrice Bellard
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+#include "qemu/osdep.h"
+#include "cpu.h"
+#include "monitor/monitor.h"
+#include "monitor/hmp-target.h"
+#include "hmp.h"
+
+void hmp_info_tlb(Monitor *mon, const QDict *qdict)
+{
+    CPUArchState *env1 = mon_get_cpu_env();
+
+    dump_mmu((FILE *)mon, (fprintf_function)monitor_printf, env1);
+}
diff --git a/target/nios2/op_helper.c b/target/nios2/op_helper.c
new file mode 100644
index 0000000..27e4de1
--- /dev/null
+++ b/target/nios2/op_helper.c
@@ -0,0 +1,47 @@ 
+/*
+ * Altera Nios II helper routines.
+ *
+ * Copyright (C) 2012 Chris Wulff <crwulff@gmail.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, see
+ * <http://www.gnu.org/licenses/lgpl-2.1.html>
+ */
+
+#include "cpu.h"
+#include "exec/helper-proto.h"
+#include "exec/cpu_ldst.h"
+
+#if !defined(CONFIG_USER_ONLY)
+uint32_t helper_mmu_read(CPUNios2State *env, uint32_t rn)
+{
+    return mmu_read(env, rn);
+}
+
+void helper_mmu_write(CPUNios2State *env, uint32_t rn, uint32_t v)
+{
+    mmu_write(env, rn, v);
+}
+
+void helper_check_interrupts(CPUNios2State *env)
+{
+    nios2_check_interrupts(env);
+}
+#endif /* !CONFIG_USER_ONLY */
+
+void helper_raise_exception(CPUNios2State *env, uint32_t index)
+{
+    CPUState *cs = ENV_GET_CPU(env);
+    cs->exception_index = index;
+    cpu_loop_exit(cs);
+}
diff --git a/target/nios2/translate.c b/target/nios2/translate.c
new file mode 100644
index 0000000..57905e1
--- /dev/null
+++ b/target/nios2/translate.c
@@ -0,0 +1,953 @@ 
+/*
+ * Altera Nios II emulation for qemu: main translation routines.
+ *
+ * Copyright (C) 2016 Marek Vasut <marex@denx.de>
+ * Copyright (C) 2012 Chris Wulff <crwulff@gmail.com>
+ * Copyright (C) 2010 Tobias Klauser <tklauser@distanz.ch>
+ *  (Portions of this file that were originally from nios2sim-ng.)
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, see
+ * <http://www.gnu.org/licenses/lgpl-2.1.html>
+ */
+
+#include "cpu.h"
+#include "tcg-op.h"
+#include "exec/exec-all.h"
+#include "disas/disas.h"
+#include "exec/helper-proto.h"
+#include "exec/helper-gen.h"
+#include "exec/log.h"
+#include "exec/cpu_ldst.h"
+
+#define INSTRUCTION_FLG(func, flags) { (func), (flags) }
+#define INSTRUCTION(func)                  \
+        INSTRUCTION_FLG(func, 0)
+#define INSTRUCTION_NOP()                  \
+        INSTRUCTION_FLG(nop, 0)
+#define INSTRUCTION_UNIMPLEMENTED()        \
+        INSTRUCTION_FLG(gen_excp, EXCP_UNIMPL)
+#define INSTRUCTION_ILLEGAL()              \
+        INSTRUCTION_FLG(gen_excp, EXCP_ILLEGAL)
+
+/* Special R-Type instruction opcode */
+#define INSN_R_TYPE 0x3A
+
+/* I-Type instruction parsing */
+#define I_TYPE(instr, code)                \
+    struct {                               \
+        uint8_t op;                        \
+        union {                            \
+            uint16_t imm16;                \
+            int16_t imm16s;                \
+        };                                 \
+        uint8_t b;                         \
+        uint8_t a;                         \
+    } (instr) = {                          \
+        .op    = extract32((code), 0, 6),  \
+        .imm16 = extract32((code), 6, 16), \
+        .b     = extract32((code), 22, 5), \
+        .a     = extract32((code), 27, 5), \
+    }
+
+/* R-Type instruction parsing */
+#define R_TYPE(instr, code)                \
+    struct {                               \
+        uint8_t op;                        \
+        uint8_t imm5;                      \
+        uint8_t opx;                       \
+        uint8_t c;                         \
+        uint8_t b;                         \
+        uint8_t a;                         \
+    } (instr) = {                          \
+        .op    = extract32((code), 0, 6),  \
+        .imm5  = extract32((code), 6, 5),  \
+        .opx   = extract32((code), 11, 6), \
+        .c     = extract32((code), 17, 5), \
+        .b     = extract32((code), 22, 5), \
+        .a     = extract32((code), 27, 5), \
+    }
+
+/* J-Type instruction parsing */
+#define J_TYPE(instr, code)                \
+    struct {                               \
+        uint8_t op;                        \
+        uint32_t imm26;                    \
+    } (instr) = {                          \
+        .op    = extract32((code), 0, 6),  \
+        .imm26 = extract32((code), 6, 26), \
+    }
+
+typedef struct DisasContext {
+    TCGv_ptr          cpu_env;
+    TCGv             *cpu_R;
+    TCGv_i32          zero;
+    int               is_jmp;
+    target_ulong      pc;
+    TranslationBlock *tb;
+    int               mem_idx;
+    bool              singlestep_enabled;
+} DisasContext;
+
+typedef struct Nios2Instruction {
+    void     (*handler)(DisasContext *dc, uint32_t code, uint32_t flags);
+    uint32_t  flags;
+} Nios2Instruction;
+
+static uint8_t get_opcode(uint32_t code)
+{
+    I_TYPE(instr, code);
+    return instr.op;
+}
+
+static uint8_t get_opxcode(uint32_t code)
+{
+    R_TYPE(instr, code);
+    return instr.opx;
+}
+
+static TCGv load_zero(DisasContext *dc)
+{
+    if (TCGV_IS_UNUSED_I32(dc->zero)) {
+        dc->zero = tcg_const_i32(0);
+    }
+    return dc->zero;
+}
+
+static TCGv load_gpr(DisasContext *dc, uint8_t reg)
+{
+    if (likely(reg != R_ZERO)) {
+        return dc->cpu_R[reg];
+    } else {
+        return load_zero(dc);
+    }
+}
+
+static void t_gen_helper_raise_exception(DisasContext *dc,
+                                         uint32_t index)
+{
+    TCGv_i32 tmp = tcg_const_i32(index);
+
+    tcg_gen_movi_tl(dc->cpu_R[R_PC], dc->pc);
+    gen_helper_raise_exception(dc->cpu_env, tmp);
+    tcg_temp_free_i32(tmp);
+    dc->is_jmp = DISAS_UPDATE;
+}
+
+static bool use_goto_tb(DisasContext *dc, uint32_t dest)
+{
+    if (unlikely(dc->singlestep_enabled)) {
+        return false;
+    }
+
+#ifndef CONFIG_USER_ONLY
+    return (dc->tb->pc & TARGET_PAGE_MASK) == (dest & TARGET_PAGE_MASK);
+#else
+    return true;
+#endif
+}
+
+static void gen_goto_tb(DisasContext *dc, int n, uint32_t dest)
+{
+    TranslationBlock *tb = dc->tb;
+
+    if (use_goto_tb(dc, dest)) {
+        tcg_gen_goto_tb(n);
+        tcg_gen_movi_tl(dc->cpu_R[R_PC], dest);
+        tcg_gen_exit_tb((tcg_target_long)tb + n);
+    } else {
+        tcg_gen_movi_tl(dc->cpu_R[R_PC], dest);
+        tcg_gen_exit_tb(0);
+    }
+}
+
+static void gen_excp(DisasContext *dc, uint32_t code, uint32_t flags)
+{
+    t_gen_helper_raise_exception(dc, flags);
+}
+
+static void gen_check_supervisor(DisasContext *dc)
+{
+    if (dc->tb->flags & CR_STATUS_U) {
+        /* CPU in user mode, privileged instruction called, stop. */
+        t_gen_helper_raise_exception(dc, EXCP_SUPERI);
+    }
+}
+
+/*
+ * Used as a placeholder for all instructions which do not have
+ * an effect on the simulator (e.g. flush, sync)
+ */
+static void nop(DisasContext *dc, uint32_t code, uint32_t flags)
+{
+    /* Nothing to do here */
+}
+
+/*
+ * J-Type instructions
+ */
+static void jmpi(DisasContext *dc, uint32_t code, uint32_t flags)
+{
+    J_TYPE(instr, code);
+    gen_goto_tb(dc, 0, (dc->pc & 0xF0000000) | (instr.imm26 << 2));
+    dc->is_jmp = DISAS_TB_JUMP;
+}
+
+static void call(DisasContext *dc, uint32_t code, uint32_t flags)
+{
+    tcg_gen_movi_tl(dc->cpu_R[R_RA], dc->pc + 4);
+    jmpi(dc, code, flags);
+}
+
+/*
+ * I-Type instructions
+ */
+/* Load instructions */
+static void gen_ldx(DisasContext *dc, uint32_t code, uint32_t flags)
+{
+    I_TYPE(instr, code);
+
+    TCGv addr = tcg_temp_new();
+    TCGv data;
+
+    /*
+     * WARNING: Loads into R_ZERO are ignored, but we must generate the
+     *          memory access itself to emulate the CPU precisely. Load
+     *          from a protected page to R_ZERO will cause SIGSEGV on
+     *          the Nios2 CPU.
+     */
+    if (likely(instr.b != R_ZERO)) {
+        data = dc->cpu_R[instr.b];
+    } else {
+        data = tcg_temp_new();
+    }
+
+    tcg_gen_addi_tl(addr, load_gpr(dc, instr.a), instr.imm16s);
+    tcg_gen_qemu_ld_tl(data, addr, dc->mem_idx, flags);
+
+    if (unlikely(instr.b == R_ZERO)) {
+        tcg_temp_free(data);
+    }
+
+    tcg_temp_free(addr);
+}
+
+/* Store instructions */
+static void gen_stx(DisasContext *dc, uint32_t code, uint32_t flags)
+{
+    I_TYPE(instr, code);
+    TCGv val = load_gpr(dc, instr.b);
+
+    TCGv addr = tcg_temp_new();
+    tcg_gen_addi_tl(addr, load_gpr(dc, instr.a), instr.imm16s);
+    tcg_gen_qemu_st_tl(val, addr, dc->mem_idx, flags);
+    tcg_temp_free(addr);
+}
+
+/* Branch instructions */
+static void br(DisasContext *dc, uint32_t code, uint32_t flags)
+{
+    I_TYPE(instr, code);
+
+    gen_goto_tb(dc, 0, dc->pc + 4 + (instr.imm16s & -4));
+    dc->is_jmp = DISAS_TB_JUMP;
+}
+
+static void gen_bxx(DisasContext *dc, uint32_t code, uint32_t flags)
+{
+    I_TYPE(instr, code);
+
+    TCGLabel *l1 = gen_new_label();
+    tcg_gen_brcond_tl(flags, dc->cpu_R[instr.a], dc->cpu_R[instr.b], l1);
+    gen_goto_tb(dc, 0, dc->pc + 4);
+    gen_set_label(l1);
+    gen_goto_tb(dc, 1, dc->pc + 4 + (instr.imm16s & -4));
+    dc->is_jmp = DISAS_TB_JUMP;
+}
+
+/* Comparison instructions */
+#define gen_i_cmpxx(fname, op3)                                              \
+static void (fname)(DisasContext *dc, uint32_t code, uint32_t flags)         \
+{                                                                            \
+    I_TYPE(instr, (code));                                                   \
+    tcg_gen_setcondi_tl(flags, (dc)->cpu_R[instr.b], (dc)->cpu_R[instr.a],   \
+                        (op3));                                              \
+}
+
+gen_i_cmpxx(gen_cmpxxsi, instr.imm16s)
+gen_i_cmpxx(gen_cmpxxui, instr.imm16)
+
+/* Math/logic instructions */
+#define gen_i_math_logic(fname, insn, resimm, op3)                          \
+static void (fname)(DisasContext *dc, uint32_t code, uint32_t flags)        \
+{                                                                           \
+    I_TYPE(instr, (code));                                                  \
+    if (unlikely(instr.b == R_ZERO)) { /* Store to R_ZERO is ignored */     \
+        return;                                                             \
+    } else if (instr.a == R_ZERO) { /* MOVxI optimizations */               \
+        tcg_gen_movi_tl(dc->cpu_R[instr.b], (resimm) ? (op3) : 0);          \
+    } else {                                                                \
+        tcg_gen_##insn##_tl((dc)->cpu_R[instr.b], (dc)->cpu_R[instr.a],     \
+                            (op3));                                         \
+    }                                                                       \
+}
+
+gen_i_math_logic(addi,  addi, 1, instr.imm16s)
+gen_i_math_logic(muli,  muli, 0, instr.imm16s)
+
+gen_i_math_logic(andi,  andi, 0, instr.imm16)
+gen_i_math_logic(ori,   ori,  1, instr.imm16)
+gen_i_math_logic(xori,  xori, 1, instr.imm16)
+
+gen_i_math_logic(andhi, andi, 0, instr.imm16 << 16)
+gen_i_math_logic(orhi , ori,  1, instr.imm16 << 16)
+gen_i_math_logic(xorhi, xori, 1, instr.imm16 << 16)
+
+/* Prototype only, defined below */
+static void handle_r_type_instr(DisasContext *dc, uint32_t code,
+                                uint32_t flags);
+
+static const Nios2Instruction i_type_instructions[] = {
+    INSTRUCTION(call),                                /* call */
+    INSTRUCTION(jmpi),                                /* jmpi */
+    INSTRUCTION_ILLEGAL(),
+    INSTRUCTION_FLG(gen_ldx, MO_UB),                  /* ldbu */
+    INSTRUCTION(addi),                                /* addi */
+    INSTRUCTION_FLG(gen_stx, MO_UB),                  /* stb */
+    INSTRUCTION(br),                                  /* br */
+    INSTRUCTION_FLG(gen_ldx, MO_SB),                  /* ldb */
+    INSTRUCTION_FLG(gen_cmpxxsi, TCG_COND_GE),        /* cmpgei */
+    INSTRUCTION_ILLEGAL(),
+    INSTRUCTION_ILLEGAL(),
+    INSTRUCTION_FLG(gen_ldx, MO_UW),                  /* ldhu */
+    INSTRUCTION(andi),                                /* andi */
+    INSTRUCTION_FLG(gen_stx, MO_UW),                  /* sth */
+    INSTRUCTION_FLG(gen_bxx, TCG_COND_GE),            /* bge */
+    INSTRUCTION_FLG(gen_ldx, MO_SW),                  /* ldh */
+    INSTRUCTION_FLG(gen_cmpxxsi, TCG_COND_LT),        /* cmplti */
+    INSTRUCTION_ILLEGAL(),
+    INSTRUCTION_ILLEGAL(),
+    INSTRUCTION_NOP(),                                /* initda */
+    INSTRUCTION(ori),                                 /* ori */
+    INSTRUCTION_FLG(gen_stx, MO_UL),                  /* stw */
+    INSTRUCTION_FLG(gen_bxx, TCG_COND_LT),            /* blt */
+    INSTRUCTION_FLG(gen_ldx, MO_UL),                  /* ldw */
+    INSTRUCTION_FLG(gen_cmpxxsi, TCG_COND_NE),        /* cmpnei */
+    INSTRUCTION_ILLEGAL(),
+    INSTRUCTION_ILLEGAL(),
+    INSTRUCTION_NOP(),                                /* flushda */
+    INSTRUCTION(xori),                                /* xori */
+    INSTRUCTION_ILLEGAL(),
+    INSTRUCTION_FLG(gen_bxx, TCG_COND_NE),            /* bne */
+    INSTRUCTION_ILLEGAL(),
+    INSTRUCTION_FLG(gen_cmpxxsi, TCG_COND_EQ),        /* cmpeqi */
+    INSTRUCTION_ILLEGAL(),
+    INSTRUCTION_ILLEGAL(),
+    INSTRUCTION_FLG(gen_ldx, MO_UB),                  /* ldbuio */
+    INSTRUCTION(muli),                                /* muli */
+    INSTRUCTION_FLG(gen_stx, MO_UB),                  /* stbio */
+    INSTRUCTION_FLG(gen_bxx, TCG_COND_EQ),            /* beq */
+    INSTRUCTION_FLG(gen_ldx, MO_SB),                  /* ldbio */
+    INSTRUCTION_FLG(gen_cmpxxui, TCG_COND_GEU),       /* cmpgeui */
+    INSTRUCTION_ILLEGAL(),
+    INSTRUCTION_ILLEGAL(),
+    INSTRUCTION_FLG(gen_ldx, MO_UW),                  /* ldhuio */
+    INSTRUCTION(andhi),                               /* andhi */
+    INSTRUCTION_FLG(gen_stx, MO_UW),                  /* sthio */
+    INSTRUCTION_FLG(gen_bxx, TCG_COND_GEU),           /* bgeu */
+    INSTRUCTION_FLG(gen_ldx, MO_SW),                  /* ldhio */
+    INSTRUCTION_FLG(gen_cmpxxui, TCG_COND_LTU),       /* cmpltui */
+    INSTRUCTION_ILLEGAL(),
+    INSTRUCTION_UNIMPLEMENTED(),                      /* custom */
+    INSTRUCTION_NOP(),                                /* initd */
+    INSTRUCTION(orhi),                                /* orhi */
+    INSTRUCTION_FLG(gen_stx, MO_SL),                  /* stwio */
+    INSTRUCTION_FLG(gen_bxx, TCG_COND_LTU),           /* bltu */
+    INSTRUCTION_FLG(gen_ldx, MO_UL),                  /* ldwio */
+    INSTRUCTION_UNIMPLEMENTED(),                      /* rdprs */
+    INSTRUCTION_ILLEGAL(),
+    INSTRUCTION_FLG(handle_r_type_instr, 0),          /* R-Type */
+    INSTRUCTION_NOP(),                                /* flushd */
+    INSTRUCTION(xorhi),                               /* xorhi */
+    INSTRUCTION_ILLEGAL(),
+    INSTRUCTION_ILLEGAL(),
+    INSTRUCTION_ILLEGAL(),
+};
+
+/*
+ * R-Type instructions
+ */
+/*
+ * status <- estatus
+ * PC <- ea
+ */
+static void eret(DisasContext *dc, uint32_t code, uint32_t flags)
+{
+    tcg_gen_mov_tl(dc->cpu_R[CR_STATUS], dc->cpu_R[CR_ESTATUS]);
+    tcg_gen_mov_tl(dc->cpu_R[R_PC], dc->cpu_R[R_EA]);
+
+    dc->is_jmp = DISAS_JUMP;
+}
+
+/* PC <- ra */
+static void ret(DisasContext *dc, uint32_t code, uint32_t flags)
+{
+    tcg_gen_mov_tl(dc->cpu_R[R_PC], dc->cpu_R[R_RA]);
+
+    dc->is_jmp = DISAS_JUMP;
+}
+
+/* PC <- ba */
+static void bret(DisasContext *dc, uint32_t code, uint32_t flags)
+{
+    tcg_gen_mov_tl(dc->cpu_R[R_PC], dc->cpu_R[R_BA]);
+
+    dc->is_jmp = DISAS_JUMP;
+}
+
+/* PC <- rA */
+static void jmp(DisasContext *dc, uint32_t code, uint32_t flags)
+{
+    R_TYPE(instr, code);
+
+    tcg_gen_mov_tl(dc->cpu_R[R_PC], load_gpr(dc, instr.a));
+
+    dc->is_jmp = DISAS_JUMP;
+}
+
+/* rC <- PC + 4 */
+static void nextpc(DisasContext *dc, uint32_t code, uint32_t flags)
+{
+    R_TYPE(instr, code);
+
+    if (likely(instr.c != R_ZERO)) {
+        tcg_gen_movi_tl(dc->cpu_R[instr.c], dc->pc + 4);
+    }
+}
+
+/*
+ * ra <- PC + 4
+ * PC <- rA
+ */
+static void callr(DisasContext *dc, uint32_t code, uint32_t flags)
+{
+    R_TYPE(instr, code);
+
+    tcg_gen_mov_tl(dc->cpu_R[R_PC], load_gpr(dc, instr.a));
+    tcg_gen_movi_tl(dc->cpu_R[R_RA], dc->pc + 4);
+
+    dc->is_jmp = DISAS_JUMP;
+}
+
+/* rC <- ctlN */
+static void rdctl(DisasContext *dc, uint32_t code, uint32_t flags)
+{
+    R_TYPE(instr, code);
+
+    gen_check_supervisor(dc);
+
+    switch (instr.imm5 + 32) {
+    case CR_PTEADDR:
+    case CR_TLBACC:
+    case CR_TLBMISC:
+    {
+#if !defined(CONFIG_USER_ONLY)
+        if (likely(instr.c != R_ZERO)) {
+            TCGv_i32 tmp = tcg_const_i32(instr.imm5 + 32);
+            gen_helper_mmu_read(dc->cpu_R[instr.c], dc->cpu_env, tmp);
+            tcg_temp_free_i32(tmp);
+        }
+#endif
+        break;
+    }
+
+    default:
+        if (likely(instr.c != R_ZERO)) {
+            tcg_gen_mov_tl(dc->cpu_R[instr.c], dc->cpu_R[instr.imm5 + 32]);
+        }
+        break;
+    }
+}
+
+/* ctlN <- rA */
+static void wrctl(DisasContext *dc, uint32_t code, uint32_t flags)
+{
+    R_TYPE(instr, code);
+
+    gen_check_supervisor(dc);
+
+    switch (instr.imm5 + 32) {
+    case CR_PTEADDR:
+    case CR_TLBACC:
+    case CR_TLBMISC:
+    {
+#if !defined(CONFIG_USER_ONLY)
+        TCGv_i32 tmp = tcg_const_i32(instr.imm5 + 32);
+        gen_helper_mmu_write(dc->cpu_env, tmp, load_gpr(dc, instr.a));
+        tcg_temp_free_i32(tmp);
+#endif
+        break;
+    }
+
+    default:
+        tcg_gen_mov_tl(dc->cpu_R[instr.imm5 + 32], load_gpr(dc, instr.a));
+        break;
+    }
+
+    /* If interrupts were enabled using WRCTL, trigger them. */
+#if !defined(CONFIG_USER_ONLY)
+    if ((instr.imm5 + 32) == CR_STATUS) {
+        gen_helper_check_interrupts(dc->cpu_env);
+    }
+#endif
+}
+
+/* Comparison instructions */
+static void gen_cmpxx(DisasContext *dc, uint32_t code, uint32_t flags)
+{
+    R_TYPE(instr, code);
+    if (likely(instr.c != R_ZERO)) {
+        tcg_gen_setcond_tl(flags, dc->cpu_R[instr.c], dc->cpu_R[instr.a],
+                           dc->cpu_R[instr.b]);
+    }
+}
+
+/* Math/logic instructions */
+#define gen_r_math_logic(fname, insn, op3)                                 \
+static void (fname)(DisasContext *dc, uint32_t code, uint32_t flags)       \
+{                                                                          \
+    R_TYPE(instr, (code));                                                 \
+    if (likely(instr.c != R_ZERO)) {                                       \
+        tcg_gen_##insn((dc)->cpu_R[instr.c], load_gpr((dc), instr.a),      \
+                       (op3));                                             \
+    }                                                                      \
+}
+
+gen_r_math_logic(add,  add_tl,   load_gpr(dc, instr.b))
+gen_r_math_logic(sub,  sub_tl,   load_gpr(dc, instr.b))
+gen_r_math_logic(mul,  mul_tl,   load_gpr(dc, instr.b))
+
+gen_r_math_logic(and,  and_tl,   load_gpr(dc, instr.b))
+gen_r_math_logic(or,   or_tl,    load_gpr(dc, instr.b))
+gen_r_math_logic(xor,  xor_tl,   load_gpr(dc, instr.b))
+gen_r_math_logic(nor,  nor_tl,   load_gpr(dc, instr.b))
+
+gen_r_math_logic(srai, sari_tl,  instr.imm5)
+gen_r_math_logic(srli, shri_tl,  instr.imm5)
+gen_r_math_logic(slli, shli_tl,  instr.imm5)
+gen_r_math_logic(roli, rotli_tl, instr.imm5)
+
+#define gen_r_mul(fname, insn)                                         \
+static void (fname)(DisasContext *dc, uint32_t code, uint32_t flags)   \
+{                                                                      \
+    R_TYPE(instr, (code));                                             \
+    if (likely(instr.c != R_ZERO)) {                                   \
+        TCGv t0 = tcg_temp_new();                                      \
+        tcg_gen_##insn(t0, dc->cpu_R[instr.c],                         \
+                       load_gpr(dc, instr.a), load_gpr(dc, instr.b)); \
+        tcg_temp_free(t0);                                             \
+    }                                                                  \
+}
+
+gen_r_mul(mulxss, muls2_tl)
+gen_r_mul(mulxuu, mulu2_tl)
+gen_r_mul(mulxsu, mulsu2_tl)
+
+#define gen_r_shift_s(fname, insn)                                         \
+static void (fname)(DisasContext *dc, uint32_t code, uint32_t flags)       \
+{                                                                          \
+    R_TYPE(instr, (code));                                                 \
+    if (likely(instr.c != R_ZERO)) {                                       \
+        TCGv t0 = tcg_temp_new();                                          \
+        tcg_gen_andi_tl(t0, load_gpr((dc), instr.b), 31);                  \
+        tcg_gen_##insn((dc)->cpu_R[instr.c], load_gpr((dc), instr.a), t0); \
+        tcg_temp_free(t0);                                                 \
+    }                                                                      \
+}
+
+gen_r_shift_s(sra, sar_tl)
+gen_r_shift_s(srl, shr_tl)
+gen_r_shift_s(sll, shl_tl)
+gen_r_shift_s(rol, rotl_tl)
+gen_r_shift_s(ror, rotr_tl)
+
+static void divs(DisasContext *dc, uint32_t code, uint32_t flags)
+{
+    R_TYPE(instr, (code));
+
+    /* Stores into R_ZERO are ignored */
+    if (unlikely(instr.c == R_ZERO)) {
+        return;
+    }
+
+    TCGv t0 = tcg_temp_new();
+    TCGv t1 = tcg_temp_new();
+    TCGv t2 = tcg_temp_new();
+    TCGv t3 = tcg_temp_new();
+
+    tcg_gen_ext32s_tl(t0, load_gpr(dc, instr.a));
+    tcg_gen_ext32s_tl(t1, load_gpr(dc, instr.b));
+    tcg_gen_setcondi_tl(TCG_COND_EQ, t2, t0, INT_MIN);
+    tcg_gen_setcondi_tl(TCG_COND_EQ, t3, t1, -1);
+    tcg_gen_and_tl(t2, t2, t3);
+    tcg_gen_setcondi_tl(TCG_COND_EQ, t3, t1, 0);
+    tcg_gen_or_tl(t2, t2, t3);
+    tcg_gen_movi_tl(t3, 0);
+    tcg_gen_movcond_tl(TCG_COND_NE, t1, t2, t3, t2, t1);
+    tcg_gen_div_tl(dc->cpu_R[instr.c], t0, t1);
+    tcg_gen_ext32s_tl(dc->cpu_R[instr.c], dc->cpu_R[instr.c]);
+
+    tcg_temp_free(t3);
+    tcg_temp_free(t2);
+    tcg_temp_free(t1);
+    tcg_temp_free(t0);
+}
+
+static void divu(DisasContext *dc, uint32_t code, uint32_t flags)
+{
+    R_TYPE(instr, (code));
+
+    /* Stores into R_ZERO are ignored */
+    if (unlikely(instr.c == R_ZERO)) {
+        return;
+    }
+
+    TCGv t0 = tcg_temp_new();
+    TCGv t1 = tcg_temp_new();
+    TCGv t2 = tcg_const_tl(0);
+    TCGv t3 = tcg_const_tl(1);
+
+    tcg_gen_ext32u_tl(t0, load_gpr(dc, instr.a));
+    tcg_gen_ext32u_tl(t1, load_gpr(dc, instr.b));
+    tcg_gen_movcond_tl(TCG_COND_EQ, t1, t1, t2, t3, t1);
+    tcg_gen_divu_tl(dc->cpu_R[instr.c], t0, t1);
+    tcg_gen_ext32s_tl(dc->cpu_R[instr.c], dc->cpu_R[instr.c]);
+
+    tcg_temp_free(t3);
+    tcg_temp_free(t2);
+    tcg_temp_free(t1);
+    tcg_temp_free(t0);
+}
+
+static const Nios2Instruction r_type_instructions[] = {
+    INSTRUCTION_ILLEGAL(),
+    INSTRUCTION(eret),                                /* eret */
+    INSTRUCTION(roli),                                /* roli */
+    INSTRUCTION(rol),                                 /* rol */
+    INSTRUCTION_NOP(),                                /* flushp */
+    INSTRUCTION(ret),                                 /* ret */
+    INSTRUCTION(nor),                                 /* nor */
+    INSTRUCTION(mulxuu),                              /* mulxuu */
+    INSTRUCTION_FLG(gen_cmpxx, TCG_COND_GE),          /* cmpge */
+    INSTRUCTION(bret),                                /* bret */
+    INSTRUCTION_ILLEGAL(),
+    INSTRUCTION(ror),                                 /* ror */
+    INSTRUCTION_NOP(),                                /* flushi */
+    INSTRUCTION(jmp),                                 /* jmp */
+    INSTRUCTION(and),                                 /* and */
+    INSTRUCTION_ILLEGAL(),
+    INSTRUCTION_FLG(gen_cmpxx, TCG_COND_LT),          /* cmplt */
+    INSTRUCTION_ILLEGAL(),
+    INSTRUCTION(slli),                                /* slli */
+    INSTRUCTION(sll),                                 /* sll */
+    INSTRUCTION_UNIMPLEMENTED(),                      /* wrprs */
+    INSTRUCTION_ILLEGAL(),
+    INSTRUCTION(or),                                  /* or */
+    INSTRUCTION(mulxsu),                              /* mulxsu */
+    INSTRUCTION_FLG(gen_cmpxx, TCG_COND_NE),          /* cmpne */
+    INSTRUCTION_ILLEGAL(),
+    INSTRUCTION(srli),                                /* srli */
+    INSTRUCTION(srl),                                 /* srl */
+    INSTRUCTION(nextpc),                              /* nextpc */
+    INSTRUCTION(callr),                               /* callr */
+    INSTRUCTION(xor),                                 /* xor */
+    INSTRUCTION(mulxss),                              /* mulxss */
+    INSTRUCTION_FLG(gen_cmpxx, TCG_COND_EQ),          /* cmpeq */
+    INSTRUCTION_ILLEGAL(),
+    INSTRUCTION_ILLEGAL(),
+    INSTRUCTION_ILLEGAL(),
+    INSTRUCTION(divu),                                /* divu */
+    INSTRUCTION(divs),                                /* div */
+    INSTRUCTION(rdctl),                               /* rdctl */
+    INSTRUCTION(mul),                                 /* mul */
+    INSTRUCTION_FLG(gen_cmpxx, TCG_COND_GEU),         /* cmpgeu */
+    INSTRUCTION_NOP(),                                /* initi */
+    INSTRUCTION_ILLEGAL(),
+    INSTRUCTION_ILLEGAL(),
+    INSTRUCTION_ILLEGAL(),
+    INSTRUCTION_FLG(gen_excp, EXCP_TRAP),             /* trap */
+    INSTRUCTION(wrctl),                               /* wrctl */
+    INSTRUCTION_ILLEGAL(),
+    INSTRUCTION_FLG(gen_cmpxx, TCG_COND_LTU),         /* cmpltu */
+    INSTRUCTION(add),                                 /* add */
+    INSTRUCTION_ILLEGAL(),
+    INSTRUCTION_ILLEGAL(),
+    INSTRUCTION_FLG(gen_excp, EXCP_BREAK),            /* break */
+    INSTRUCTION_ILLEGAL(),
+    INSTRUCTION(nop),                                 /* nop */
+    INSTRUCTION_ILLEGAL(),
+    INSTRUCTION_ILLEGAL(),
+    INSTRUCTION(sub),                                 /* sub */
+    INSTRUCTION(srai),                                /* srai */
+    INSTRUCTION(sra),                                 /* sra */
+    INSTRUCTION_ILLEGAL(),
+    INSTRUCTION_ILLEGAL(),
+    INSTRUCTION_ILLEGAL(),
+    INSTRUCTION_ILLEGAL(),
+};
+
+static void handle_r_type_instr(DisasContext *dc, uint32_t code, uint32_t flags)
+{
+    uint8_t opx;
+    const Nios2Instruction *instr;
+
+    opx = get_opxcode(code);
+    if (unlikely(opx >= ARRAY_SIZE(r_type_instructions))) {
+        goto illegal_op;
+    }
+
+    instr = &r_type_instructions[opx];
+    instr->handler(dc, code, instr->flags);
+
+    return;
+
+illegal_op:
+    t_gen_helper_raise_exception(dc, EXCP_ILLEGAL);
+}
+
+static void handle_instruction(DisasContext *dc, CPUNios2State *env)
+{
+    uint32_t code;
+    uint8_t op;
+    const Nios2Instruction *instr;
+#if defined(CONFIG_USER_ONLY)
+    /* FIXME: Is this needed ? */
+    if (dc->pc >= 0x1000 && dc->pc < 0x2000) {
+        env->regs[R_PC] = dc->pc;
+        t_gen_helper_raise_exception(dc, 0xaa);
+        return;
+    }
+#endif
+    code = cpu_ldl_code(env, dc->pc);
+    op = get_opcode(code);
+
+    if (unlikely(op >= ARRAY_SIZE(i_type_instructions))) {
+        goto illegal_op;
+    }
+
+    TCGV_UNUSED_I32(dc->zero);
+
+    instr = &i_type_instructions[op];
+    instr->handler(dc, code, instr->flags);
+
+    if (!TCGV_IS_UNUSED_I32(dc->zero)) {
+        tcg_temp_free(dc->zero);
+    }
+
+    return;
+
+illegal_op:
+    t_gen_helper_raise_exception(dc, EXCP_ILLEGAL);
+}
+
+static const char *regnames[] = {
+    "zero",     "at",       "r2",       "r3",
+    "r4",       "r5",       "r6",       "r7",
+    "r8",       "r9",       "r10",      "r11",
+    "r12",      "r13",      "r14",      "r15",
+    "r16",      "r17",      "r18",      "r19",
+    "r20",      "r21",      "r22",      "r23",
+    "et",       "bt",       "gp",       "sp",
+    "fp",       "ea",       "ba",       "ra",
+    "status",   "estatus",  "bstatus",  "ienable",
+    "ipending", "cpuid",    "reserved", "exception",
+    "pteaddr",  "tlbacc",   "tlbmisc",  "reserved",
+    "badaddr",  "config",   "mpubase",  "mpuacc",
+    "reserved", "reserved", "reserved", "reserved",
+    "reserved", "reserved", "reserved", "reserved",
+    "reserved", "reserved", "reserved", "reserved",
+    "reserved", "reserved", "reserved", "reserved",
+    "rpc"
+};
+
+static TCGv_ptr cpu_env;
+static TCGv cpu_R[NUM_CORE_REGS];
+
+#include "exec/gen-icount.h"
+
+static void gen_exception(DisasContext *dc, uint32_t excp)
+{
+    TCGv_i32 tmp = tcg_const_i32(excp);
+
+    tcg_gen_movi_tl(cpu_R[R_PC], dc->pc);
+    gen_helper_raise_exception(cpu_env, tmp);
+    tcg_temp_free_i32(tmp);
+    dc->is_jmp = DISAS_UPDATE;
+}
+
+/* generate intermediate code for basic block 'tb'.  */
+void gen_intermediate_code(CPUNios2State *env, TranslationBlock *tb)
+{
+    Nios2CPU *cpu = nios2_env_get_cpu(env);
+    CPUState *cs = CPU(cpu);
+    DisasContext dc1, *dc = &dc1;
+    int num_insns;
+    int max_insns;
+
+    /* Initialize DC */
+    dc->cpu_env = cpu_env;
+    dc->cpu_R   = cpu_R;
+    dc->is_jmp  = DISAS_NEXT;
+    dc->pc      = tb->pc;
+    dc->tb      = tb;
+    dc->mem_idx = cpu_mmu_index(env, false);
+    dc->singlestep_enabled = cs->singlestep_enabled;
+
+    /* Set up instruction counts */
+    num_insns = 0;
+    if (cs->singlestep_enabled || singlestep) {
+        max_insns = 1;
+    } else {
+        int page_insns = (TARGET_PAGE_SIZE - (tb->pc & TARGET_PAGE_MASK)) / 4;
+        max_insns = tb->cflags & CF_COUNT_MASK;
+        if (max_insns == 0) {
+            max_insns = CF_COUNT_MASK;
+        }
+        if (max_insns > page_insns) {
+            max_insns = page_insns;
+        }
+        if (max_insns > TCG_MAX_INSNS) {
+            max_insns = TCG_MAX_INSNS;
+        }
+    }
+
+    gen_tb_start(tb);
+    do {
+        tcg_gen_insn_start(dc->pc);
+        num_insns++;
+
+        if (unlikely(cpu_breakpoint_test(cs, dc->pc, BP_ANY))) {
+            gen_exception(dc, EXCP_DEBUG);
+            /* The address covered by the breakpoint must be included in
+               [tb->pc, tb->pc + tb->size) in order to for it to be
+               properly cleared -- thus we increment the PC here so that
+               the logic setting tb->size below does the right thing.  */
+            dc->pc += 4;
+            break;
+        }
+
+        if (num_insns + 1 == max_insns && (tb->cflags & CF_LAST_IO)) {
+            gen_io_start();
+        }
+
+        /* Decode an instruction */
+        handle_instruction(dc, env);
+
+        dc->pc += 4;
+
+        /* Translation stops when a conditional branch is encountered.
+         * Otherwise the subsequent code could get translated several times.
+         * Also stop translation when a page boundary is reached.  This
+         * ensures prefetch aborts occur at the right place.  */
+    } while (!dc->is_jmp &&
+             !tcg_op_buf_full() &&
+             num_insns < max_insns);
+
+    if (tb->cflags & CF_LAST_IO) {
+        gen_io_end();
+    }
+
+    /* Indicate where the next block should start */
+    switch (dc->is_jmp) {
+    case DISAS_NEXT:
+        /* Save the current PC back into the CPU register */
+        tcg_gen_movi_tl(cpu_R[R_PC], dc->pc);
+        tcg_gen_exit_tb(0);
+        break;
+
+    default:
+    case DISAS_JUMP:
+    case DISAS_UPDATE:
+        /* The jump will already have updated the PC register */
+        tcg_gen_exit_tb(0);
+        break;
+
+    case DISAS_TB_JUMP:
+        /* nothing more to generate */
+        break;
+    }
+
+    /* End off the block */
+    gen_tb_end(tb, num_insns);
+
+    /* Mark instruction starts for the final generated instruction */
+    tb->size = dc->pc - tb->pc;
+    tb->icount = num_insns;
+
+#ifdef DEBUG_DISAS
+    if (qemu_loglevel_mask(CPU_LOG_TB_IN_ASM)
+        && qemu_log_in_addr_range(tb->pc)) {
+        qemu_log("IN: %s\n", lookup_symbol(tb->pc));
+        log_target_disas(cs, tb->pc, dc->pc - tb->pc, 0);
+        qemu_log("\n");
+    }
+#endif
+}
+
+void nios2_cpu_dump_state(CPUState *cs, FILE *f, fprintf_function cpu_fprintf,
+                          int flags)
+{
+    Nios2CPU *cpu = NIOS2_CPU(cs);
+    CPUNios2State *env = &cpu->env;
+    int i;
+
+    if (!env || !f) {
+        return;
+    }
+
+    cpu_fprintf(f, "IN: PC=%x %s\n",
+                env->regs[R_PC], lookup_symbol(env->regs[R_PC]));
+
+    for (i = 0; i < NUM_CORE_REGS; i++) {
+        cpu_fprintf(f, "%9s=%8.8x ", regnames[i], env->regs[i]);
+        if ((i + 1) % 4 == 0) {
+            cpu_fprintf(f, "\n");
+        }
+    }
+#if !defined(CONFIG_USER_ONLY)
+    cpu_fprintf(f, " mmu write: VPN=%05X PID %02X TLBACC %08X\n",
+                env->mmu.pteaddr_wr & CR_PTEADDR_VPN_MASK,
+                (env->mmu.tlbmisc_wr & CR_TLBMISC_PID_MASK) >> 4,
+                env->mmu.tlbacc_wr);
+#endif
+    cpu_fprintf(f, "\n\n");
+}
+
+void nios2_tcg_init(void)
+{
+    int i;
+
+    cpu_env = tcg_global_reg_new_ptr(TCG_AREG0, "env");
+
+    for (i = 0; i < NUM_CORE_REGS; i++) {
+        cpu_R[i] = tcg_global_mem_new(cpu_env,
+                                      offsetof(CPUNios2State, regs[i]),
+                                      regnames[i]);
+    }
+}
+
+void restore_state_to_opc(CPUNios2State *env, TranslationBlock *tb,
+                          target_ulong *data)
+{
+    env->regs[R_PC] = data[0];
+}