qemu-devel
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

Re: [Qemu-devel] [PATCH V8 2/7] nios2: Add architecture emulation suppor


From: Marek Vasut
Subject: Re: [Qemu-devel] [PATCH V8 2/7] nios2: Add architecture emulation support
Date: Tue, 17 Jan 2017 01:18:51 +0100
User-agent: Mozilla/5.0 (X11; Linux x86_64; rv:45.0) Gecko/20100101 Icedove/45.4.0

On 01/16/2017 11:21 PM, Alexander Graf wrote:
> 
> 
> On 31/12/2016 14:22, Marek Vasut wrote:
>> From: Chris Wulff <address@hidden>
>>
>> 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 <address@hidden>
>> Cc: Chris Wulff <address@hidden>
>> Cc: Jeff Da Silva <address@hidden>
>> Cc: Ley Foon Tan <address@hidden>
>> Cc: Sandra Loosemore <address@hidden>
>> Cc: Yves Vandervennet <address@hidden>
>> ---
>> 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 <address@hidden>
>> + *
>> + * 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 <address@hidden>
>> + *
>> + * 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 <address@hidden>
>> + *
>> + * 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 <address@hidden>
>> + *
>> + * 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 <address@hidden>
>> + *
>> + * 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


-- 
Best regards,
Marek Vasut




reply via email to

[Prev in Thread] Current Thread [Next in Thread]