[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