diff options
author | Philippe Mathieu-Daudé <philmd@linaro.org> | 2024-03-27 12:10:58 +0100 |
---|---|---|
committer | Philippe Mathieu-Daudé <philmd@linaro.org> | 2024-04-24 16:03:38 +0200 |
commit | 6c3014858c4c0024dd0560f08a6eda0f92f658d6 (patch) | |
tree | dd474c2e68b37c51c9cb7526e48452aaddccb19e /target | |
parent | 92360d6e624404492afe5d32ca669a33df181742 (diff) | |
download | qemu-6c3014858c4c0024dd0560f08a6eda0f92f658d6.zip qemu-6c3014858c4c0024dd0560f08a6eda0f92f658d6.tar.gz qemu-6c3014858c4c0024dd0560f08a6eda0f92f658d6.tar.bz2 |
target/nios2: Remove the deprecated Nios II target
The Nios II target is deprecated since v8.2 in commit 9997771bc1
("target/nios2: Deprecate the Nios II architecture").
Remove:
- Buildsys / CI infra
- User emulation
- System emulation (10m50-ghrd & nios2-generic-nommu machines)
- Tests
Signed-off-by: Philippe Mathieu-Daudé <philmd@linaro.org>
Reviewed-by: Richard Henderson <richard.henderson@linaro.org>
Acked-by: Marek Vasut <marex@denx.de>
Message-Id: <20240327144806.11319-3-philmd@linaro.org>
Diffstat (limited to 'target')
-rw-r--r-- | target/Kconfig | 1 | ||||
-rw-r--r-- | target/meson.build | 1 | ||||
-rw-r--r-- | target/nios2/Kconfig | 3 | ||||
-rw-r--r-- | target/nios2/cpu-param.h | 20 | ||||
-rw-r--r-- | target/nios2/cpu-qom.h | 18 | ||||
-rw-r--r-- | target/nios2/cpu.c | 410 | ||||
-rw-r--r-- | target/nios2/cpu.h | 301 | ||||
-rw-r--r-- | target/nios2/helper.c | 371 | ||||
-rw-r--r-- | target/nios2/helper.h | 32 | ||||
-rw-r--r-- | target/nios2/meson.build | 17 | ||||
-rw-r--r-- | target/nios2/mmu.c | 216 | ||||
-rw-r--r-- | target/nios2/mmu.h | 52 | ||||
-rw-r--r-- | target/nios2/monitor.c | 35 | ||||
-rw-r--r-- | target/nios2/nios2-semi.c | 230 | ||||
-rw-r--r-- | target/nios2/op_helper.c | 119 | ||||
-rw-r--r-- | target/nios2/trace-events | 10 | ||||
-rw-r--r-- | target/nios2/translate.c | 1107 |
17 files changed, 0 insertions, 2943 deletions
diff --git a/target/Kconfig b/target/Kconfig index 83da0bd..5275a93 100644 --- a/target/Kconfig +++ b/target/Kconfig @@ -8,7 +8,6 @@ source loongarch/Kconfig source m68k/Kconfig source microblaze/Kconfig source mips/Kconfig -source nios2/Kconfig source openrisc/Kconfig source ppc/Kconfig source riscv/Kconfig diff --git a/target/meson.build b/target/meson.build index dee2ac4..59b46b2 100644 --- a/target/meson.build +++ b/target/meson.build @@ -9,7 +9,6 @@ subdir('loongarch') subdir('m68k') subdir('microblaze') subdir('mips') -subdir('nios2') subdir('openrisc') subdir('ppc') subdir('riscv') diff --git a/target/nios2/Kconfig b/target/nios2/Kconfig deleted file mode 100644 index c65550c..0000000 --- a/target/nios2/Kconfig +++ /dev/null @@ -1,3 +0,0 @@ -config NIOS2 - bool - select SEMIHOSTING diff --git a/target/nios2/cpu-param.h b/target/nios2/cpu-param.h deleted file mode 100644 index 767bba4..0000000 --- a/target/nios2/cpu-param.h +++ /dev/null @@ -1,20 +0,0 @@ -/* - * Altera Nios II cpu parameters for qemu. - * - * Copyright (c) 2012 Chris Wulff <crwulff@gmail.com> - * SPDX-License-Identifier: LGPL-2.1+ - */ - -#ifndef NIOS2_CPU_PARAM_H -#define NIOS2_CPU_PARAM_H - -#define TARGET_LONG_BITS 32 -#define TARGET_PAGE_BITS 12 -#define TARGET_PHYS_ADDR_SPACE_BITS 32 -#ifdef CONFIG_USER_ONLY -# define TARGET_VIRT_ADDR_SPACE_BITS 31 -#else -# define TARGET_VIRT_ADDR_SPACE_BITS 32 -#endif - -#endif diff --git a/target/nios2/cpu-qom.h b/target/nios2/cpu-qom.h deleted file mode 100644 index 2fd9121..0000000 --- a/target/nios2/cpu-qom.h +++ /dev/null @@ -1,18 +0,0 @@ -/* - * QEMU Nios II CPU QOM header (target agnostic) - * - * Copyright (c) 2012 Chris Wulff <crwulff@gmail.com> - * - * SPDX-License-Identifier: LGPL-2.1-or-later - */ - -#ifndef QEMU_NIOS2_CPU_QOM_H -#define QEMU_NIOS2_CPU_QOM_H - -#include "hw/core/cpu.h" - -#define TYPE_NIOS2_CPU "nios2-cpu" - -OBJECT_DECLARE_CPU_TYPE(Nios2CPU, Nios2CPUClass, NIOS2_CPU) - -#endif diff --git a/target/nios2/cpu.c b/target/nios2/cpu.c deleted file mode 100644 index 679aff5..0000000 --- a/target/nios2/cpu.c +++ /dev/null @@ -1,410 +0,0 @@ -/* - * 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/module.h" -#include "qapi/error.h" -#include "cpu.h" -#include "exec/log.h" -#include "gdbstub/helpers.h" -#include "hw/qdev-properties.h" - -static void nios2_cpu_set_pc(CPUState *cs, vaddr value) -{ - cpu_env(cs)->pc = value; -} - -static vaddr nios2_cpu_get_pc(CPUState *cs) -{ - return cpu_env(cs)->pc; -} - -static void nios2_restore_state_to_opc(CPUState *cs, - const TranslationBlock *tb, - const uint64_t *data) -{ - cpu_env(cs)->pc = data[0]; -} - -static bool nios2_cpu_has_work(CPUState *cs) -{ - return cs->interrupt_request & CPU_INTERRUPT_HARD; -} - -static int nios2_cpu_mmu_index(CPUState *cs, bool ifetch) -{ - return (cpu_env(cs)->ctrl[CR_STATUS] & CR_STATUS_U - ? MMU_USER_IDX : MMU_SUPERVISOR_IDX); -} - -static void nios2_cpu_reset_hold(Object *obj) -{ - CPUState *cs = CPU(obj); - Nios2CPU *cpu = NIOS2_CPU(cs); - Nios2CPUClass *ncc = NIOS2_CPU_GET_CLASS(obj); - CPUNios2State *env = &cpu->env; - - if (ncc->parent_phases.hold) { - ncc->parent_phases.hold(obj); - } - - memset(env->ctrl, 0, sizeof(env->ctrl)); - env->pc = cpu->reset_addr; - -#if defined(CONFIG_USER_ONLY) - /* Start in user mode with interrupts enabled. */ - env->ctrl[CR_STATUS] = CR_STATUS_RSIE | CR_STATUS_U | CR_STATUS_PIE; - memset(env->regs, 0, sizeof(env->regs)); -#else - env->ctrl[CR_STATUS] = CR_STATUS_RSIE; - nios2_update_crs(env); - memset(env->shadow_regs, 0, sizeof(env->shadow_regs)); -#endif -} - -#ifndef CONFIG_USER_ONLY -static void eic_set_irq(void *opaque, int irq, int level) -{ - Nios2CPU *cpu = opaque; - CPUState *cs = CPU(cpu); - - if (level) { - cpu_interrupt(cs, CPU_INTERRUPT_HARD); - } else { - cpu_reset_interrupt(cs, CPU_INTERRUPT_HARD); - } -} - -static void iic_set_irq(void *opaque, int irq, int level) -{ - Nios2CPU *cpu = opaque; - CPUNios2State *env = &cpu->env; - CPUState *cs = CPU(cpu); - - env->ctrl[CR_IPENDING] = deposit32(env->ctrl[CR_IPENDING], irq, 1, !!level); - - if (env->ctrl[CR_IPENDING]) { - cpu_interrupt(cs, CPU_INTERRUPT_HARD); - } else { - cpu_reset_interrupt(cs, CPU_INTERRUPT_HARD); - } -} -#endif - -static void nios2_cpu_initfn(Object *obj) -{ -#if !defined(CONFIG_USER_ONLY) - Nios2CPU *cpu = NIOS2_CPU(obj); - - mmu_init(&cpu->env); -#endif -} - -static ObjectClass *nios2_cpu_class_by_name(const char *cpu_model) -{ - return object_class_by_name(TYPE_NIOS2_CPU); -} - -static void realize_cr_status(CPUState *cs) -{ - Nios2CPU *cpu = NIOS2_CPU(cs); - - /* Begin with all fields of all registers are reserved. */ - memset(cpu->cr_state, 0, sizeof(cpu->cr_state)); - - /* - * The combination of writable and readonly is the set of all - * non-reserved fields. We apply writable as a mask to bits, - * and merge in existing readonly bits, before storing. - */ -#define WR_REG(C) cpu->cr_state[C].writable = -1 -#define RO_REG(C) cpu->cr_state[C].readonly = -1 -#define WR_FIELD(C, F) cpu->cr_state[C].writable |= R_##C##_##F##_MASK -#define RO_FIELD(C, F) cpu->cr_state[C].readonly |= R_##C##_##F##_MASK - - WR_FIELD(CR_STATUS, PIE); - WR_REG(CR_ESTATUS); - WR_REG(CR_BSTATUS); - RO_REG(CR_CPUID); - RO_REG(CR_EXCEPTION); - WR_REG(CR_BADADDR); - - if (cpu->eic_present) { - WR_FIELD(CR_STATUS, RSIE); - RO_FIELD(CR_STATUS, NMI); - WR_FIELD(CR_STATUS, PRS); - RO_FIELD(CR_STATUS, CRS); - WR_FIELD(CR_STATUS, IL); - WR_FIELD(CR_STATUS, IH); - } else { - RO_FIELD(CR_STATUS, RSIE); - WR_REG(CR_IENABLE); - RO_REG(CR_IPENDING); - } - - if (cpu->mmu_present) { - WR_FIELD(CR_STATUS, U); - WR_FIELD(CR_STATUS, EH); - - WR_FIELD(CR_PTEADDR, VPN); - WR_FIELD(CR_PTEADDR, PTBASE); - - RO_FIELD(CR_TLBMISC, D); - RO_FIELD(CR_TLBMISC, PERM); - RO_FIELD(CR_TLBMISC, BAD); - RO_FIELD(CR_TLBMISC, DBL); - WR_FIELD(CR_TLBMISC, PID); - WR_FIELD(CR_TLBMISC, WE); - WR_FIELD(CR_TLBMISC, RD); - WR_FIELD(CR_TLBMISC, WAY); - - WR_REG(CR_TLBACC); - } - - /* - * TODO: ECC (config, eccinj) and MPU (config, mpubase, mpuacc) are - * unimplemented, so their corresponding control regs remain reserved. - */ - -#undef WR_REG -#undef RO_REG -#undef WR_FIELD -#undef RO_FIELD -} - -static void nios2_cpu_realizefn(DeviceState *dev, Error **errp) -{ - CPUState *cs = CPU(dev); - Nios2CPU *cpu = NIOS2_CPU(cs); - 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; - } - - realize_cr_status(cs); - qemu_init_vcpu(cs); - cpu_reset(cs); - - /* We have reserved storage for cpuid; might as well use it. */ - cpu->env.ctrl[CR_CPUID] = cs->cpu_index; - -#ifndef CONFIG_USER_ONLY - if (cpu->eic_present) { - qdev_init_gpio_in_named(DEVICE(cpu), eic_set_irq, "EIC", 1); - } else { - qdev_init_gpio_in_named(DEVICE(cpu), iic_set_irq, "IRQ", 32); - } -#endif - - ncc->parent_realize(dev, errp); -} - -#ifndef CONFIG_USER_ONLY -static bool eic_take_interrupt(Nios2CPU *cpu) -{ - CPUNios2State *env = &cpu->env; - const uint32_t status = env->ctrl[CR_STATUS]; - - if (cpu->rnmi) { - return !(status & CR_STATUS_NMI); - } - if (!(status & CR_STATUS_PIE)) { - return false; - } - if (cpu->ril <= FIELD_EX32(status, CR_STATUS, IL)) { - return false; - } - if (cpu->rrs != FIELD_EX32(status, CR_STATUS, CRS)) { - return true; - } - return status & CR_STATUS_RSIE; -} - -static bool iic_take_interrupt(Nios2CPU *cpu) -{ - CPUNios2State *env = &cpu->env; - - if (!(env->ctrl[CR_STATUS] & CR_STATUS_PIE)) { - return false; - } - return env->ctrl[CR_IPENDING] & env->ctrl[CR_IENABLE]; -} - -static bool nios2_cpu_exec_interrupt(CPUState *cs, int interrupt_request) -{ - Nios2CPU *cpu = NIOS2_CPU(cs); - - if (interrupt_request & CPU_INTERRUPT_HARD) { - if (cpu->eic_present - ? eic_take_interrupt(cpu) - : iic_take_interrupt(cpu)) { - cs->exception_index = EXCP_IRQ; - nios2_cpu_do_interrupt(cs); - return true; - } - } - return false; -} -#endif /* !CONFIG_USER_ONLY */ - -static void nios2_cpu_disas_set_info(CPUState *cpu, disassemble_info *info) -{ - /* NOTE: NiosII R2 is not supported yet. */ - info->mach = bfd_arch_nios2; - info->print_insn = print_insn_nios2; -} - -static int nios2_cpu_gdb_read_register(CPUState *cs, GByteArray *mem_buf, int n) -{ - Nios2CPU *cpu = NIOS2_CPU(cs); - CPUNios2State *env = &cpu->env; - uint32_t val; - - if (n < 32) { /* GP regs */ - val = env->regs[n]; - } else if (n == 32) { /* PC */ - val = env->pc; - } else if (n < 49) { /* Status regs */ - unsigned cr = n - 33; - if (nios2_cr_reserved(&cpu->cr_state[cr])) { - val = 0; - } else { - val = env->ctrl[n - 33]; - } - } else { - /* Invalid regs */ - return 0; - } - - return gdb_get_reg32(mem_buf, val); -} - -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; - uint32_t val; - - if (n > cc->gdb_num_core_regs) { - return 0; - } - val = ldl_p(mem_buf); - - if (n < 32) { /* GP regs */ - env->regs[n] = val; - } else if (n == 32) { /* PC */ - env->pc = val; - } else if (n < 49) { /* Status regs */ - unsigned cr = n - 33; - /* ??? Maybe allow the debugger to write to readonly fields. */ - val &= cpu->cr_state[cr].writable; - val |= cpu->cr_state[cr].readonly & env->ctrl[cr]; - env->ctrl[cr] = val; - } else { - g_assert_not_reached(); - } - - return 4; -} - -static Property nios2_properties[] = { - DEFINE_PROP_BOOL("diverr_present", Nios2CPU, diverr_present, true), - DEFINE_PROP_BOOL("mmu_present", Nios2CPU, mmu_present, true), - /* ALTR,pid-num-bits */ - DEFINE_PROP_UINT32("mmu_pid_num_bits", Nios2CPU, pid_num_bits, 8), - /* ALTR,tlb-num-ways */ - DEFINE_PROP_UINT32("mmu_tlb_num_ways", Nios2CPU, tlb_num_ways, 16), - /* ALTR,tlb-num-entries */ - DEFINE_PROP_UINT32("mmu_pid_num_entries", Nios2CPU, tlb_num_entries, 256), - DEFINE_PROP_END_OF_LIST(), -}; - -#ifndef CONFIG_USER_ONLY -#include "hw/core/sysemu-cpu-ops.h" - -static const struct SysemuCPUOps nios2_sysemu_ops = { - .get_phys_page_debug = nios2_cpu_get_phys_page_debug, -}; -#endif - -#include "hw/core/tcg-cpu-ops.h" - -static const TCGCPUOps nios2_tcg_ops = { - .initialize = nios2_tcg_init, - .restore_state_to_opc = nios2_restore_state_to_opc, - -#ifndef CONFIG_USER_ONLY - .tlb_fill = nios2_cpu_tlb_fill, - .cpu_exec_interrupt = nios2_cpu_exec_interrupt, - .do_interrupt = nios2_cpu_do_interrupt, - .do_unaligned_access = nios2_cpu_do_unaligned_access, -#endif /* !CONFIG_USER_ONLY */ -}; - -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); - ResettableClass *rc = RESETTABLE_CLASS(oc); - - device_class_set_parent_realize(dc, nios2_cpu_realizefn, - &ncc->parent_realize); - device_class_set_props(dc, nios2_properties); - resettable_class_set_parent_phases(rc, NULL, nios2_cpu_reset_hold, NULL, - &ncc->parent_phases); - - cc->class_by_name = nios2_cpu_class_by_name; - cc->has_work = nios2_cpu_has_work; - cc->mmu_index = nios2_cpu_mmu_index; - cc->dump_state = nios2_cpu_dump_state; - cc->set_pc = nios2_cpu_set_pc; - cc->get_pc = nios2_cpu_get_pc; - cc->disas_set_info = nios2_cpu_disas_set_info; -#ifndef CONFIG_USER_ONLY - cc->sysemu_ops = &nios2_sysemu_ops; -#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; - cc->tcg_ops = &nios2_tcg_ops; -} - -static const TypeInfo nios2_cpu_type_info = { - .name = TYPE_NIOS2_CPU, - .parent = TYPE_CPU, - .instance_size = sizeof(Nios2CPU), - .instance_align = __alignof(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 deleted file mode 100644 index 4164a34..0000000 --- a/target/nios2/cpu.h +++ /dev/null @@ -1,301 +0,0 @@ -/* - * 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 NIOS2_CPU_H -#define NIOS2_CPU_H - -#include "cpu-qom.h" -#include "exec/cpu-defs.h" -#include "hw/registerfields.h" - -typedef struct CPUArchState CPUNios2State; -#if !defined(CONFIG_USER_ONLY) -#include "mmu.h" -#endif - -/** - * Nios2CPUClass: - * @parent_phases: The parent class' reset phase handlers. - * - * A Nios2 CPU model. - */ -struct Nios2CPUClass { - CPUClass parent_class; - - DeviceRealize parent_realize; - ResettablePhases parent_phases; -}; - -#define TARGET_HAS_ICE 1 - -/* Configuration options for Nios II */ -#define RESET_ADDRESS 0x00000000 -#define EXCEPTION_ADDRESS 0x00000004 -#define FAST_TLB_MISS_ADDRESS 0x00000008 - -#define NUM_GP_REGS 32 -#define NUM_CR_REGS 32 - -#ifndef CONFIG_USER_ONLY -/* 63 shadow register sets; index 0 is the primary register set. */ -#define NUM_REG_SETS 64 -#endif - -/* General purpose register aliases */ -enum { - R_ZERO = 0, - R_AT = 1, - R_RET0 = 2, - R_RET1 = 3, - R_ARG0 = 4, - R_ARG1 = 5, - R_ARG2 = 6, - R_ARG3 = 7, - R_ET = 24, - R_BT = 25, - R_GP = 26, - R_SP = 27, - R_FP = 28, - R_EA = 29, - R_BA = 30, - R_SSTATUS = 30, - R_RA = 31, -}; - -/* Control register aliases */ -enum { - CR_STATUS = 0, - CR_ESTATUS = 1, - CR_BSTATUS = 2, - CR_IENABLE = 3, - CR_IPENDING = 4, - CR_CPUID = 5, - CR_EXCEPTION = 7, - CR_PTEADDR = 8, - CR_TLBACC = 9, - CR_TLBMISC = 10, - CR_ENCINJ = 11, - CR_BADADDR = 12, - CR_CONFIG = 13, - CR_MPUBASE = 14, - CR_MPUACC = 15, -}; - -FIELD(CR_STATUS, PIE, 0, 1) -FIELD(CR_STATUS, U, 1, 1) -FIELD(CR_STATUS, EH, 2, 1) -FIELD(CR_STATUS, IH, 3, 1) -FIELD(CR_STATUS, IL, 4, 6) -FIELD(CR_STATUS, CRS, 10, 6) -FIELD(CR_STATUS, PRS, 16, 6) -FIELD(CR_STATUS, NMI, 22, 1) -FIELD(CR_STATUS, RSIE, 23, 1) -FIELD(CR_STATUS, SRS, 31, 1) /* only in sstatus */ - -#define CR_STATUS_PIE R_CR_STATUS_PIE_MASK -#define CR_STATUS_U R_CR_STATUS_U_MASK -#define CR_STATUS_EH R_CR_STATUS_EH_MASK -#define CR_STATUS_IH R_CR_STATUS_IH_MASK -#define CR_STATUS_NMI R_CR_STATUS_NMI_MASK -#define CR_STATUS_RSIE R_CR_STATUS_RSIE_MASK -#define CR_STATUS_SRS R_CR_STATUS_SRS_MASK - -FIELD(CR_EXCEPTION, CAUSE, 2, 5) -FIELD(CR_EXCEPTION, ECCFTL, 31, 1) - -FIELD(CR_PTEADDR, VPN, 2, 20) -FIELD(CR_PTEADDR, PTBASE, 22, 10) - -FIELD(CR_TLBACC, PFN, 0, 20) -FIELD(CR_TLBACC, G, 20, 1) -FIELD(CR_TLBACC, X, 21, 1) -FIELD(CR_TLBACC, W, 22, 1) -FIELD(CR_TLBACC, R, 23, 1) -FIELD(CR_TLBACC, C, 24, 1) -FIELD(CR_TLBACC, IG, 25, 7) - -#define CR_TLBACC_C R_CR_TLBACC_C_MASK -#define CR_TLBACC_R R_CR_TLBACC_R_MASK -#define CR_TLBACC_W R_CR_TLBACC_W_MASK -#define CR_TLBACC_X R_CR_TLBACC_X_MASK -#define CR_TLBACC_G R_CR_TLBACC_G_MASK - -FIELD(CR_TLBMISC, D, 0, 1) -FIELD(CR_TLBMISC, PERM, 1, 1) -FIELD(CR_TLBMISC, BAD, 2, 1) -FIELD(CR_TLBMISC, DBL, 3, 1) -FIELD(CR_TLBMISC, PID, 4, 14) -FIELD(CR_TLBMISC, WE, 18, 1) -FIELD(CR_TLBMISC, RD, 19, 1) -FIELD(CR_TLBMISC, WAY, 20, 4) -FIELD(CR_TLBMISC, EE, 24, 1) - -#define CR_TLBMISC_EE R_CR_TLBMISC_EE_MASK -#define CR_TLBMISC_RD R_CR_TLBMISC_RD_MASK -#define CR_TLBMISC_WE R_CR_TLBMISC_WE_MASK -#define CR_TLBMISC_DBL R_CR_TLBMISC_DBL_MASK -#define CR_TLBMISC_BAD R_CR_TLBMISC_BAD_MASK -#define CR_TLBMISC_PERM R_CR_TLBMISC_PERM_MASK -#define CR_TLBMISC_D R_CR_TLBMISC_D_MASK - -/* Exceptions */ -#define EXCP_BREAK 0x1000 -#define EXCP_SEMIHOST 0x1001 -#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_X 9 -#define EXCP_SUPERI 10 -#define EXCP_SUPERA_D 11 -#define EXCP_TLB_X 12 -#define EXCP_TLB_D (0x1000 | EXCP_TLB_X) -#define EXCP_PERM_X 13 -#define EXCP_PERM_R 14 -#define EXCP_PERM_W 15 -#define EXCP_MPUI 16 -#define EXCP_MPUD 17 - -struct CPUArchState { -#ifdef CONFIG_USER_ONLY - uint32_t regs[NUM_GP_REGS]; -#else - uint32_t shadow_regs[NUM_REG_SETS][NUM_GP_REGS]; - /* Pointer into shadow_regs for the current register set. */ - uint32_t *regs; -#endif - uint32_t ctrl[NUM_CR_REGS]; - uint32_t pc; - -#if !defined(CONFIG_USER_ONLY) - Nios2MMU mmu; -#endif - int error_code; -}; - -typedef struct { - uint32_t writable; - uint32_t readonly; -} ControlRegState; - -/** - * Nios2CPU: - * @env: #CPUNios2State - * - * A Nios2 CPU. - */ -struct ArchCPU { - CPUState parent_obj; - - CPUNios2State env; - - bool diverr_present; - bool mmu_present; - bool eic_present; - - uint32_t pid_num_bits; - uint32_t tlb_num_ways; - uint32_t tlb_num_entries; - - /* Addresses that are hard-coded in the FPGA build settings */ - uint32_t reset_addr; - uint32_t exception_addr; - uint32_t fast_tlb_miss_addr; - - /* Bits within each control register which are reserved or readonly. */ - ControlRegState cr_state[NUM_CR_REGS]; - - /* External Interrupt Controller Interface */ - uint32_t rha; /* Requested handler address */ - uint32_t ril; /* Requested interrupt level */ - uint32_t rrs; /* Requested register set */ - bool rnmi; /* Requested nonmaskable interrupt */ -}; - - -static inline bool nios2_cr_reserved(const ControlRegState *s) -{ - return (s->writable | s->readonly) == 0; -} - -static inline void nios2_update_crs(CPUNios2State *env) -{ -#ifndef CONFIG_USER_ONLY - unsigned crs = FIELD_EX32(env->ctrl[CR_STATUS], CR_STATUS, CRS); - env->regs = env->shadow_regs[crs]; -#endif -} - -void nios2_tcg_init(void); -void nios2_cpu_do_interrupt(CPUState *cs); -void dump_mmu(CPUNios2State *env); -void nios2_cpu_dump_state(CPUState *cpu, FILE *f, int flags); -G_NORETURN void nios2_cpu_do_unaligned_access(CPUState *cpu, vaddr addr, - MMUAccessType access_type, int mmu_idx, - uintptr_t retaddr); -G_NORETURN void nios2_cpu_loop_exit_advance(CPUNios2State *env, - uintptr_t retaddr); - -void do_nios2_semihosting(CPUNios2State *env); - -#define CPU_RESOLVING_TYPE TYPE_NIOS2_CPU - -#define cpu_gen_code cpu_nios2_gen_code - -#define CPU_SAVE_VERSION 1 - -/* MMU modes definitions */ -#define MMU_SUPERVISOR_IDX 0 -#define MMU_USER_IDX 1 - -#ifndef CONFIG_USER_ONLY -hwaddr nios2_cpu_get_phys_page_debug(CPUState *cpu, vaddr addr); -bool nios2_cpu_tlb_fill(CPUState *cs, vaddr address, int size, - MMUAccessType access_type, int mmu_idx, - bool probe, uintptr_t retaddr); -#endif - -typedef CPUNios2State CPUArchState; -typedef Nios2CPU ArchCPU; - -#include "exec/cpu-all.h" - -FIELD(TBFLAGS, CRS0, 0, 1) /* Set if CRS == 0. */ -FIELD(TBFLAGS, U, 1, 1) /* Overlaps CR_STATUS_U */ -FIELD(TBFLAGS, R0_0, 2, 1) /* Set if R0 == 0. */ - -static inline void cpu_get_tb_cpu_state(CPUNios2State *env, vaddr *pc, - uint64_t *cs_base, uint32_t *flags) -{ - unsigned crs = FIELD_EX32(env->ctrl[CR_STATUS], CR_STATUS, CRS); - - *pc = env->pc; - *cs_base = 0; - *flags = (env->ctrl[CR_STATUS] & CR_STATUS_U) - | (crs ? 0 : R_TBFLAGS_CRS0_MASK) - | (env->regs[0] ? 0 : R_TBFLAGS_R0_0_MASK); -} - -#endif /* NIOS2_CPU_H */ diff --git a/target/nios2/helper.c b/target/nios2/helper.c deleted file mode 100644 index ac57121..0000000 --- a/target/nios2/helper.c +++ /dev/null @@ -1,371 +0,0 @@ -/* - * 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 "qemu/osdep.h" - -#include "cpu.h" -#include "qemu/host-utils.h" -#include "exec/exec-all.h" -#include "exec/cpu_ldst.h" -#include "exec/log.h" -#include "exec/helper-proto.h" -#include "semihosting/semihost.h" - - -static void do_exception(Nios2CPU *cpu, uint32_t exception_addr, - uint32_t tlbmisc_set, bool is_break) -{ - CPUNios2State *env = &cpu->env; - CPUState *cs = CPU(cpu); - uint32_t old_status = env->ctrl[CR_STATUS]; - uint32_t new_status = old_status; - - /* With shadow regs, exceptions are always taken into CRS 0. */ - new_status &= ~R_CR_STATUS_CRS_MASK; - env->regs = env->shadow_regs[0]; - - if ((old_status & CR_STATUS_EH) == 0) { - int r_ea = R_EA, cr_es = CR_ESTATUS; - - if (is_break) { - r_ea = R_BA; - cr_es = CR_BSTATUS; - } - env->ctrl[cr_es] = old_status; - env->regs[r_ea] = env->pc; - - if (cpu->mmu_present) { - new_status |= CR_STATUS_EH; - - /* - * There are 4 bits that are always written. - * Explicitly clear them, to be set via the argument. - */ - env->ctrl[CR_TLBMISC] &= ~(CR_TLBMISC_D | - CR_TLBMISC_PERM | - CR_TLBMISC_BAD | - CR_TLBMISC_DBL); - env->ctrl[CR_TLBMISC] |= tlbmisc_set; - } - - /* - * With shadow regs, and EH == 0, PRS is set from CRS. - * At least, so says Table 3-9, and some other text, - * though Table 3-38 says otherwise. - */ - new_status = FIELD_DP32(new_status, CR_STATUS, PRS, - FIELD_EX32(old_status, CR_STATUS, CRS)); - } - - new_status &= ~(CR_STATUS_PIE | CR_STATUS_U); - - env->ctrl[CR_STATUS] = new_status; - if (!is_break) { - env->ctrl[CR_EXCEPTION] = FIELD_DP32(0, CR_EXCEPTION, CAUSE, - cs->exception_index); - } - env->pc = exception_addr; -} - -static void do_iic_irq(Nios2CPU *cpu) -{ - do_exception(cpu, cpu->exception_addr, 0, false); -} - -static void do_eic_irq(Nios2CPU *cpu) -{ - CPUNios2State *env = &cpu->env; - uint32_t old_status = env->ctrl[CR_STATUS]; - uint32_t new_status = old_status; - uint32_t old_rs = FIELD_EX32(old_status, CR_STATUS, CRS); - uint32_t new_rs = cpu->rrs; - - new_status = FIELD_DP32(new_status, CR_STATUS, CRS, new_rs); - new_status = FIELD_DP32(new_status, CR_STATUS, IL, cpu->ril); - new_status = FIELD_DP32(new_status, CR_STATUS, NMI, cpu->rnmi); - new_status &= ~(CR_STATUS_RSIE | CR_STATUS_U); - new_status |= CR_STATUS_IH; - - if (!(new_status & CR_STATUS_EH)) { - new_status = FIELD_DP32(new_status, CR_STATUS, PRS, old_rs); - if (new_rs == 0) { - env->ctrl[CR_ESTATUS] = old_status; - } else { - if (new_rs != old_rs) { - old_status |= CR_STATUS_SRS; - } - env->shadow_regs[new_rs][R_SSTATUS] = old_status; - } - env->shadow_regs[new_rs][R_EA] = env->pc; - } - - env->ctrl[CR_STATUS] = new_status; - nios2_update_crs(env); - - env->pc = cpu->rha; -} - -void nios2_cpu_do_interrupt(CPUState *cs) -{ - Nios2CPU *cpu = NIOS2_CPU(cs); - CPUNios2State *env = &cpu->env; - uint32_t tlbmisc_set = 0; - - if (qemu_loglevel_mask(CPU_LOG_INT)) { - const char *name = NULL; - - switch (cs->exception_index) { - case EXCP_IRQ: - name = "interrupt"; - break; - case EXCP_TLB_X: - case EXCP_TLB_D: - if (env->ctrl[CR_STATUS] & CR_STATUS_EH) { - name = "TLB MISS (double)"; - } else { - name = "TLB MISS (fast)"; - } - break; - case EXCP_PERM_R: - case EXCP_PERM_W: - case EXCP_PERM_X: - name = "TLB PERM"; - break; - case EXCP_SUPERA_X: - case EXCP_SUPERA_D: - name = "SUPERVISOR (address)"; - break; - case EXCP_SUPERI: - name = "SUPERVISOR (insn)"; - break; - case EXCP_ILLEGAL: - name = "ILLEGAL insn"; - break; - case EXCP_UNALIGN: - name = "Misaligned (data)"; - break; - case EXCP_UNALIGND: - name = "Misaligned (destination)"; - break; - case EXCP_DIV: - name = "DIV error"; - break; - case EXCP_TRAP: - name = "TRAP insn"; - break; - case EXCP_BREAK: - name = "BREAK insn"; - break; - case EXCP_SEMIHOST: - name = "SEMIHOST insn"; - break; - } - if (name) { - qemu_log("%s at pc=0x%08x\n", name, env->pc); - } else { - qemu_log("Unknown exception %d at pc=0x%08x\n", - cs->exception_index, env->pc); - } - } - - switch (cs->exception_index) { - case EXCP_IRQ: - /* Note that PC is advanced for interrupts as well. */ - env->pc += 4; - if (cpu->eic_present) { - do_eic_irq(cpu); - } else { - do_iic_irq(cpu); - } - break; - - case EXCP_TLB_D: - tlbmisc_set = CR_TLBMISC_D; - /* fall through */ - case EXCP_TLB_X: - if (env->ctrl[CR_STATUS] & CR_STATUS_EH) { - tlbmisc_set |= CR_TLBMISC_DBL; - /* - * Normally, we don't write to tlbmisc unless !EH, - * so do it manually for the double-tlb miss exception. - */ - env->ctrl[CR_TLBMISC] &= ~(CR_TLBMISC_D | - CR_TLBMISC_PERM | - CR_TLBMISC_BAD); - env->ctrl[CR_TLBMISC] |= tlbmisc_set; - do_exception(cpu, cpu->exception_addr, 0, false); - } else { - tlbmisc_set |= CR_TLBMISC_WE; - do_exception(cpu, cpu->fast_tlb_miss_addr, tlbmisc_set, false); - } - break; - - case EXCP_PERM_R: - case EXCP_PERM_W: - tlbmisc_set = CR_TLBMISC_D; - /* fall through */ - case EXCP_PERM_X: - tlbmisc_set |= CR_TLBMISC_PERM; - if (!(env->ctrl[CR_STATUS] & CR_STATUS_EH)) { - tlbmisc_set |= CR_TLBMISC_WE; - } - do_exception(cpu, cpu->exception_addr, tlbmisc_set, false); - break; - - case EXCP_SUPERA_D: - case EXCP_UNALIGN: - tlbmisc_set = CR_TLBMISC_D; - /* fall through */ - case EXCP_SUPERA_X: - case EXCP_UNALIGND: - tlbmisc_set |= CR_TLBMISC_BAD; - do_exception(cpu, cpu->exception_addr, tlbmisc_set, false); - break; - - case EXCP_SUPERI: - case EXCP_ILLEGAL: - case EXCP_DIV: - case EXCP_TRAP: - do_exception(cpu, cpu->exception_addr, 0, false); - break; - - case EXCP_BREAK: - do_exception(cpu, cpu->exception_addr, 0, true); - break; - - case EXCP_SEMIHOST: - do_nios2_semihosting(env); - break; - - default: - cpu_abort(cs, "unhandled exception type=%d\n", cs->exception_index); - } -} - -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: %#" PRIx64 "\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) -{ - CPUNios2State *env = cpu_env(cs); - - env->ctrl[CR_BADADDR] = addr; - cs->exception_index = EXCP_UNALIGN; - nios2_cpu_loop_exit_advance(env, retaddr); -} - -bool nios2_cpu_tlb_fill(CPUState *cs, vaddr address, int size, - MMUAccessType access_type, int mmu_idx, - bool probe, uintptr_t retaddr) -{ - Nios2CPU *cpu = NIOS2_CPU(cs); - CPUNios2State *env = &cpu->env; - unsigned int excp; - target_ulong vaddr, paddr; - Nios2MMULookup lu; - unsigned int hit; - - if (!cpu->mmu_present) { - /* No MMU */ - address &= TARGET_PAGE_MASK; - tlb_set_page(cs, address, address, PAGE_BITS, - mmu_idx, TARGET_PAGE_SIZE); - return true; - } - - 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); - return true; - } - } else { - if (address >= 0x80000000) { - /* Illegal access from user mode */ - if (probe) { - return false; - } - cs->exception_index = (access_type == MMU_INST_FETCH - ? EXCP_SUPERA_X : EXCP_SUPERA_D); - env->ctrl[CR_BADADDR] = address; - nios2_cpu_loop_exit_advance(env, retaddr); - } - } - - /* Virtual page. */ - hit = mmu_translate(env, &lu, address, access_type, mmu_idx); - if (hit) { - vaddr = address & TARGET_PAGE_MASK; - paddr = lu.paddr + vaddr - lu.vaddr; - - if (((access_type == MMU_DATA_LOAD) && (lu.prot & PAGE_READ)) || - ((access_type == MMU_DATA_STORE) && (lu.prot & PAGE_WRITE)) || - ((access_type == MMU_INST_FETCH) && (lu.prot & PAGE_EXEC))) { - tlb_set_page(cs, vaddr, paddr, lu.prot, - mmu_idx, TARGET_PAGE_SIZE); - return true; - } - - /* Permission violation */ - excp = (access_type == MMU_DATA_LOAD ? EXCP_PERM_R : - access_type == MMU_DATA_STORE ? EXCP_PERM_W : EXCP_PERM_X); - } else { - excp = (access_type == MMU_INST_FETCH ? EXCP_TLB_X: EXCP_TLB_D); - } - - if (probe) { - return false; - } - - env->ctrl[CR_TLBMISC] = FIELD_DP32(env->ctrl[CR_TLBMISC], CR_TLBMISC, D, - access_type != MMU_INST_FETCH); - env->ctrl[CR_PTEADDR] = FIELD_DP32(env->ctrl[CR_PTEADDR], CR_PTEADDR, VPN, - address >> TARGET_PAGE_BITS); - env->mmu.pteaddr_wr = env->ctrl[CR_PTEADDR]; - - cs->exception_index = excp; - env->ctrl[CR_BADADDR] = address; - nios2_cpu_loop_exit_advance(env, retaddr); -} diff --git a/target/nios2/helper.h b/target/nios2/helper.h deleted file mode 100644 index 1648d76..0000000 --- a/target/nios2/helper.h +++ /dev/null @@ -1,32 +0,0 @@ -/* - * 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_FLAGS_2(raise_exception, TCG_CALL_NO_WG, noreturn, env, i32) -DEF_HELPER_FLAGS_3(divs, TCG_CALL_NO_WG, s32, env, s32, s32) -DEF_HELPER_FLAGS_3(divu, TCG_CALL_NO_WG, i32, env, i32, i32) - -#if !defined(CONFIG_USER_ONLY) -DEF_HELPER_3(eret, noreturn, env, i32, i32) -DEF_HELPER_FLAGS_2(rdprs, TCG_CALL_NO_WG, i32, env, i32) -DEF_HELPER_3(wrprs, void, env, i32, i32) -DEF_HELPER_2(mmu_write_tlbacc, void, env, i32) -DEF_HELPER_2(mmu_write_tlbmisc, void, env, i32) -DEF_HELPER_2(mmu_write_pteaddr, void, env, i32) -#endif diff --git a/target/nios2/meson.build b/target/nios2/meson.build deleted file mode 100644 index 12d8abf..0000000 --- a/target/nios2/meson.build +++ /dev/null @@ -1,17 +0,0 @@ -nios2_ss = ss.source_set() -nios2_ss.add(files( - 'cpu.c', - 'op_helper.c', - 'translate.c', -)) - -nios2_system_ss = ss.source_set() -nios2_system_ss.add(files( - 'helper.c', - 'monitor.c', - 'mmu.c', - 'nios2-semi.c', -)) - -target_arch += {'nios2': nios2_ss} -target_system_arch += {'nios2': nios2_system_ss} diff --git a/target/nios2/mmu.c b/target/nios2/mmu.c deleted file mode 100644 index d9b690b..0000000 --- a/target/nios2/mmu.c +++ /dev/null @@ -1,216 +0,0 @@ -/* - * 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/qemu-print.h" -#include "cpu.h" -#include "exec/exec-all.h" -#include "mmu.h" -#include "exec/helper-proto.h" -#include "trace/trace-target_nios2.h" - - -/* rw - 0 = read, 1 = write, 2 = fetch. */ -unsigned int mmu_translate(CPUNios2State *env, - Nios2MMULookup *lu, - target_ulong vaddr, int rw, int mmu_idx) -{ - Nios2CPU *cpu = env_archcpu(env); - int pid = FIELD_EX32(env->mmu.tlbmisc_wr, CR_TLBMISC, PID); - int vpn = vaddr >> 12; - int way, n_ways = cpu->tlb_num_ways; - - for (way = 0; way < n_ways; way++) { - uint32_t index = (way * n_ways) + (vpn & env->mmu.tlb_entry_mask); - Nios2TLBEntry *entry = &env->mmu.tlb[index]; - - if (((entry->tag >> 12) != vpn) || - (((entry->tag & (1 << 11)) == 0) && - ((entry->tag & ((1 << cpu->pid_num_bits) - 1)) != pid))) { - trace_nios2_mmu_translate_miss(vaddr, pid, index, entry->tag); - continue; - } - - lu->vaddr = vaddr & TARGET_PAGE_MASK; - lu->paddr = FIELD_EX32(entry->data, CR_TLBACC, PFN) << 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); - - trace_nios2_mmu_translate_hit(vaddr, pid, index, lu->paddr, lu->prot); - return 1; - } - return 0; -} - -static void mmu_flush_pid(CPUNios2State *env, uint32_t pid) -{ - CPUState *cs = env_cpu(env); - Nios2CPU *cpu = env_archcpu(env); - int idx; - - for (idx = 0; idx < cpu->tlb_num_entries; idx++) { - Nios2TLBEntry *entry = &env->mmu.tlb[idx]; - - if ((entry->tag & (1 << 10)) && (!(entry->tag & (1 << 11))) && - ((entry->tag & ((1 << cpu->pid_num_bits) - 1)) == pid)) { - uint32_t vaddr = entry->tag & TARGET_PAGE_MASK; - - trace_nios2_mmu_flush_pid_hit(pid, idx, vaddr); - tlb_flush_page(cs, vaddr); - } else { - trace_nios2_mmu_flush_pid_miss(pid, idx, entry->tag); - } - } -} - -void helper_mmu_write_tlbacc(CPUNios2State *env, uint32_t v) -{ - CPUState *cs = env_cpu(env); - Nios2CPU *cpu = env_archcpu(env); - - trace_nios2_mmu_write_tlbacc(FIELD_EX32(v, CR_TLBACC, IG), - (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' : '.', - FIELD_EX32(v, CR_TLBACC, PFN)); - - /* if tlbmisc.WE == 1 then trigger a TLB write on writes to TLBACC */ - if (env->ctrl[CR_TLBMISC] & CR_TLBMISC_WE) { - int way = FIELD_EX32(env->ctrl[CR_TLBMISC], CR_TLBMISC, WAY); - int vpn = FIELD_EX32(env->mmu.pteaddr_wr, CR_PTEADDR, VPN); - int pid = FIELD_EX32(env->mmu.tlbmisc_wr, CR_TLBMISC, PID); - int g = FIELD_EX32(v, CR_TLBACC, G); - int valid = FIELD_EX32(vpn, CR_TLBACC, PFN) < 0xC0000; - Nios2TLBEntry *entry = - &env->mmu.tlb[(way * cpu->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 | R_CR_TLBACC_PFN_MASK); - - if ((entry->tag != newTag) || (entry->data != newData)) { - if (entry->tag & (1 << 10)) { - /* Flush existing entry */ - tlb_flush_page(cs, entry->tag & TARGET_PAGE_MASK); - } - entry->tag = newTag; - entry->data = newData; - } - /* Auto-increment tlbmisc.WAY */ - env->ctrl[CR_TLBMISC] = FIELD_DP32(env->ctrl[CR_TLBMISC], - CR_TLBMISC, WAY, - (way + 1) & (cpu->tlb_num_ways - 1)); - } - - /* Writes to TLBACC don't change the read-back value */ - env->mmu.tlbacc_wr = v; -} - -void helper_mmu_write_tlbmisc(CPUNios2State *env, uint32_t v) -{ - Nios2CPU *cpu = env_archcpu(env); - uint32_t new_pid = FIELD_EX32(v, CR_TLBMISC, PID); - uint32_t old_pid = FIELD_EX32(env->mmu.tlbmisc_wr, CR_TLBMISC, PID); - uint32_t way = FIELD_EX32(v, CR_TLBMISC, WAY); - - trace_nios2_mmu_write_tlbmisc(way, - (v & CR_TLBMISC_RD) ? 'R' : '.', - (v & CR_TLBMISC_WE) ? 'W' : '.', - (v & CR_TLBMISC_DBL) ? '2' : '.', - (v & CR_TLBMISC_BAD) ? 'B' : '.', - (v & CR_TLBMISC_PERM) ? 'P' : '.', - (v & CR_TLBMISC_D) ? 'D' : '.', - new_pid); - - if (new_pid != old_pid) { - mmu_flush_pid(env, old_pid); - } - - /* if tlbmisc.RD == 1 then trigger a TLB read on writes to TLBMISC */ - if (v & CR_TLBMISC_RD) { - int vpn = FIELD_EX32(env->mmu.pteaddr_wr, CR_PTEADDR, VPN); - Nios2TLBEntry *entry = - &env->mmu.tlb[(way * cpu->tlb_num_ways) + - (vpn & env->mmu.tlb_entry_mask)]; - - env->ctrl[CR_TLBACC] &= R_CR_TLBACC_IG_MASK; - env->ctrl[CR_TLBACC] |= entry->data; - env->ctrl[CR_TLBACC] |= (entry->tag & (1 << 11)) ? CR_TLBACC_G : 0; - env->ctrl[CR_TLBMISC] = FIELD_DP32(v, CR_TLBMISC, PID, - entry->tag & - ((1 << cpu->pid_num_bits) - 1)); - env->ctrl[CR_PTEADDR] = FIELD_DP32(env->ctrl[CR_PTEADDR], - CR_PTEADDR, VPN, - entry->tag >> TARGET_PAGE_BITS); - } else { - env->ctrl[CR_TLBMISC] = v; - } - - env->mmu.tlbmisc_wr = v; -} - -void helper_mmu_write_pteaddr(CPUNios2State *env, uint32_t v) -{ - trace_nios2_mmu_write_pteaddr(FIELD_EX32(v, CR_PTEADDR, PTBASE), - FIELD_EX32(v, CR_PTEADDR, VPN)); - - /* Writes to PTEADDR don't change the read-back VPN value */ - env->ctrl[CR_PTEADDR] = ((v & ~R_CR_PTEADDR_VPN_MASK) | - (env->ctrl[CR_PTEADDR] & R_CR_PTEADDR_VPN_MASK)); - env->mmu.pteaddr_wr = v; -} - -void mmu_init(CPUNios2State *env) -{ - Nios2CPU *cpu = env_archcpu(env); - Nios2MMU *mmu = &env->mmu; - - mmu->tlb_entry_mask = (cpu->tlb_num_entries / cpu->tlb_num_ways) - 1; - mmu->tlb = g_new0(Nios2TLBEntry, cpu->tlb_num_entries); -} - -void dump_mmu(CPUNios2State *env) -{ - Nios2CPU *cpu = env_archcpu(env); - int i; - - qemu_printf("MMU: ways %d, entries %d, pid bits %d\n", - cpu->tlb_num_ways, cpu->tlb_num_entries, - cpu->pid_num_bits); - - for (i = 0; i < cpu->tlb_num_entries; i++) { - Nios2TLBEntry *entry = &env->mmu.tlb[i]; - qemu_printf("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 << cpu->pid_num_bits) - 1), - (entry->tag & (1 << 11)) ? 'G' : '-', - FIELD_EX32(entry->data, CR_TLBACC, PFN), - (entry->data & CR_TLBACC_C) ? 'C' : '-', - (entry->data & CR_TLBACC_R) ? 'R' : '-', - (entry->data & CR_TLBACC_W) ? 'W' : '-', - (entry->data & CR_TLBACC_X) ? 'X' : '-'); - } -} diff --git a/target/nios2/mmu.h b/target/nios2/mmu.h deleted file mode 100644 index 5b08590..0000000 --- a/target/nios2/mmu.h +++ /dev/null @@ -1,52 +0,0 @@ -/* - * 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 NIOS2_MMU_H -#define NIOS2_MMU_H - -#include "cpu.h" - -typedef struct Nios2TLBEntry { - target_ulong tag; - target_ulong data; -} Nios2TLBEntry; - -typedef struct Nios2MMU { - 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); -void mmu_write(CPUNios2State *env, uint32_t rn, uint32_t v); -void mmu_init(CPUNios2State *env); - -#endif /* NIOS2_MMU_H */ diff --git a/target/nios2/monitor.c b/target/nios2/monitor.c deleted file mode 100644 index 0152dec..0000000 --- a/target/nios2/monitor.c +++ /dev/null @@ -1,35 +0,0 @@ -/* - * 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 "monitor/hmp.h" - -void hmp_info_tlb(Monitor *mon, const QDict *qdict) -{ - CPUArchState *env1 = mon_get_cpu_env(mon); - - dump_mmu(env1); -} diff --git a/target/nios2/nios2-semi.c b/target/nios2/nios2-semi.c deleted file mode 100644 index 420702e..0000000 --- a/target/nios2/nios2-semi.c +++ /dev/null @@ -1,230 +0,0 @@ -/* - * Nios II Semihosting syscall interface. - * This code is derived from m68k-semi.c. - * The semihosting protocol implemented here is described in the - * libgloss sources: - * https://sourceware.org/git/gitweb.cgi?p=newlib-cygwin.git;a=blob;f=libgloss/nios2/nios2-semi.txt;hb=HEAD - * - * Copyright (c) 2017-2019 Mentor Graphics - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program 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 General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, see <http://www.gnu.org/licenses/>. - */ - -#include "qemu/osdep.h" -#include "cpu.h" -#include "gdbstub/syscalls.h" -#include "gdbstub/helpers.h" -#include "semihosting/syscalls.h" -#include "semihosting/uaccess.h" -#include "qemu/log.h" - -#define HOSTED_EXIT 0 -#define HOSTED_INIT_SIM 1 -#define HOSTED_OPEN 2 -#define HOSTED_CLOSE 3 -#define HOSTED_READ 4 -#define HOSTED_WRITE 5 -#define HOSTED_LSEEK 6 -#define HOSTED_RENAME 7 -#define HOSTED_UNLINK 8 -#define HOSTED_STAT 9 -#define HOSTED_FSTAT 10 -#define HOSTED_GETTIMEOFDAY 11 -#define HOSTED_ISATTY 12 -#define HOSTED_SYSTEM 13 - -static int host_to_gdb_errno(int err) -{ -#define E(X) case E##X: return GDB_E##X - switch (err) { - E(PERM); - E(NOENT); - E(INTR); - E(BADF); - E(ACCES); - E(FAULT); - E(BUSY); - E(EXIST); - E(NODEV); - E(NOTDIR); - E(ISDIR); - E(INVAL); - E(NFILE); - E(MFILE); - E(FBIG); - E(NOSPC); - E(SPIPE); - E(ROFS); - E(NAMETOOLONG); - default: - return GDB_EUNKNOWN; - } -#undef E -} - -static void nios2_semi_u32_cb(CPUState *cs, uint64_t ret, int err) -{ - CPUNios2State *env = cpu_env(cs); - target_ulong args = env->regs[R_ARG1]; - - if (put_user_u32(ret, args) || - put_user_u32(host_to_gdb_errno(err), args + 4)) { - /* - * The nios2 semihosting ABI does not provide any way to report this - * error to the guest, so the best we can do is log it in qemu. - * It is always a guest error not to pass us a valid argument block. - */ - qemu_log_mask(LOG_GUEST_ERROR, "nios2-semihosting: return value " - "discarded because argument block not writable\n"); - } -} - -static void nios2_semi_u64_cb(CPUState *cs, uint64_t ret, int err) -{ - CPUNios2State *env = cpu_env(cs); - target_ulong args = env->regs[R_ARG1]; - - if (put_user_u32(ret >> 32, args) || - put_user_u32(ret, args + 4) || - put_user_u32(host_to_gdb_errno(err), args + 8)) { - /* No way to report this via nios2 semihosting ABI; just log it */ - qemu_log_mask(LOG_GUEST_ERROR, "nios2-semihosting: return value " - "discarded because argument block not writable\n"); - } -} - -/* - * Read the input value from the argument block; fail the semihosting - * call if the memory read fails. - */ -#define GET_ARG(n) do { \ - if (get_user_ual(arg ## n, args + (n) * 4)) { \ - goto failed; \ - } \ -} while (0) - -#define GET_ARG64(n) do { \ - if (get_user_ual(arg ## n, args + (n) * 4)) { \ - goto failed64; \ - } \ -} while (0) - -void do_nios2_semihosting(CPUNios2State *env) -{ - CPUState *cs = env_cpu(env); - int nr; - uint32_t args; - target_ulong arg0, arg1, arg2, arg3; - - nr = env->regs[R_ARG0]; - args = env->regs[R_ARG1]; - switch (nr) { - case HOSTED_EXIT: - gdb_exit(env->regs[R_ARG1]); - exit(env->regs[R_ARG1]); - - case HOSTED_OPEN: - GET_ARG(0); - GET_ARG(1); - GET_ARG(2); - GET_ARG(3); - semihost_sys_open(cs, nios2_semi_u32_cb, arg0, arg1, arg2, arg3); - break; - - case HOSTED_CLOSE: - GET_ARG(0); - semihost_sys_close(cs, nios2_semi_u32_cb, arg0); - break; - - case HOSTED_READ: - GET_ARG(0); - GET_ARG(1); - GET_ARG(2); - semihost_sys_read(cs, nios2_semi_u32_cb, arg0, arg1, arg2); - break; - - case HOSTED_WRITE: - GET_ARG(0); - GET_ARG(1); - GET_ARG(2); - semihost_sys_write(cs, nios2_semi_u32_cb, arg0, arg1, arg2); - break; - - case HOSTED_LSEEK: - GET_ARG64(0); - GET_ARG64(1); - GET_ARG64(2); - GET_ARG64(3); - semihost_sys_lseek(cs, nios2_semi_u64_cb, arg0, - deposit64(arg2, 32, 32, arg1), arg3); - break; - - case HOSTED_RENAME: - GET_ARG(0); - GET_ARG(1); - GET_ARG(2); - GET_ARG(3); - semihost_sys_rename(cs, nios2_semi_u32_cb, arg0, arg1, arg2, arg3); - break; - - case HOSTED_UNLINK: - GET_ARG(0); - GET_ARG(1); - semihost_sys_remove(cs, nios2_semi_u32_cb, arg0, arg1); - break; - - case HOSTED_STAT: - GET_ARG(0); - GET_ARG(1); - GET_ARG(2); - semihost_sys_stat(cs, nios2_semi_u32_cb, arg0, arg1, arg2); - break; - - case HOSTED_FSTAT: - GET_ARG(0); - GET_ARG(1); - semihost_sys_fstat(cs, nios2_semi_u32_cb, arg0, arg1); - break; - - case HOSTED_GETTIMEOFDAY: - GET_ARG(0); - GET_ARG(1); - semihost_sys_gettimeofday(cs, nios2_semi_u32_cb, arg0, arg1); - break; - - case HOSTED_ISATTY: - GET_ARG(0); - semihost_sys_isatty(cs, nios2_semi_u32_cb, arg0); - break; - - case HOSTED_SYSTEM: - GET_ARG(0); - GET_ARG(1); - semihost_sys_system(cs, nios2_semi_u32_cb, arg0, arg1); - break; - - default: - qemu_log_mask(LOG_GUEST_ERROR, "nios2-semihosting: unsupported " - "semihosting syscall %d\n", nr); - nios2_semi_u32_cb(cs, -1, ENOSYS); - break; - - failed: - nios2_semi_u32_cb(cs, -1, EFAULT); - break; - failed64: - nios2_semi_u64_cb(cs, -1, EFAULT); - break; - } -} diff --git a/target/nios2/op_helper.c b/target/nios2/op_helper.c deleted file mode 100644 index 5017457..0000000 --- a/target/nios2/op_helper.c +++ /dev/null @@ -1,119 +0,0 @@ -/* - * 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 "qemu/osdep.h" -#include "cpu.h" -#include "exec/helper-proto.h" -#include "exec/exec-all.h" - -void helper_raise_exception(CPUNios2State *env, uint32_t index) -{ - CPUState *cs = env_cpu(env); - cs->exception_index = index; - cpu_loop_exit(cs); -} - -void nios2_cpu_loop_exit_advance(CPUNios2State *env, uintptr_t retaddr) -{ - CPUState *cs = env_cpu(env); - - /* - * Note that PC is advanced for all hardware exceptions. - * Do this here, rather than in restore_state_to_opc(), - * lest we affect QEMU internal exceptions, like EXCP_DEBUG. - */ - cpu_restore_state(cs, retaddr); - env->pc += 4; - cpu_loop_exit(cs); -} - -static void maybe_raise_div(CPUNios2State *env, uintptr_t ra) -{ - Nios2CPU *cpu = env_archcpu(env); - CPUState *cs = env_cpu(env); - - if (cpu->diverr_present) { - cs->exception_index = EXCP_DIV; - nios2_cpu_loop_exit_advance(env, ra); - } -} - -int32_t helper_divs(CPUNios2State *env, int32_t num, int32_t den) -{ - if (unlikely(den == 0) || unlikely(den == -1 && num == INT32_MIN)) { - maybe_raise_div(env, GETPC()); - return num; /* undefined */ - } - return num / den; -} - -uint32_t helper_divu(CPUNios2State *env, uint32_t num, uint32_t den) -{ - if (unlikely(den == 0)) { - maybe_raise_div(env, GETPC()); - return num; /* undefined */ - } - return num / den; -} - -#ifndef CONFIG_USER_ONLY -void helper_eret(CPUNios2State *env, uint32_t new_status, uint32_t new_pc) -{ - Nios2CPU *cpu = env_archcpu(env); - CPUState *cs = env_cpu(env); - - if (unlikely(new_pc & 3)) { - env->ctrl[CR_BADADDR] = new_pc; - cs->exception_index = EXCP_UNALIGND; - nios2_cpu_loop_exit_advance(env, GETPC()); - } - - /* - * None of estatus, bstatus, or sstatus have constraints on write; - * do not allow reserved fields in status to be set. - * When shadow registers are enabled, eret *does* restore CRS. - * Rather than testing eic_present to decide, mask CRS out of - * the set of readonly fields. - */ - new_status &= cpu->cr_state[CR_STATUS].writable | - (cpu->cr_state[CR_STATUS].readonly & R_CR_STATUS_CRS_MASK); - - env->ctrl[CR_STATUS] = new_status; - env->pc = new_pc; - nios2_update_crs(env); - cpu_loop_exit(cs); -} - -/* - * RDPRS and WRPRS are implemented out of line so that if PRS == CRS, - * all of the tcg global temporaries are synced back to ENV. - */ -uint32_t helper_rdprs(CPUNios2State *env, uint32_t regno) -{ - unsigned prs = FIELD_EX32(env->ctrl[CR_STATUS], CR_STATUS, PRS); - return env->shadow_regs[prs][regno]; -} - -void helper_wrprs(CPUNios2State *env, uint32_t regno, uint32_t val) -{ - unsigned prs = FIELD_EX32(env->ctrl[CR_STATUS], CR_STATUS, PRS); - env->shadow_regs[prs][regno] = val; -} -#endif /* !CONFIG_USER_ONLY */ diff --git a/target/nios2/trace-events b/target/nios2/trace-events deleted file mode 100644 index 07f1f0a..0000000 --- a/target/nios2/trace-events +++ /dev/null @@ -1,10 +0,0 @@ -# mmu.c -nios2_mmu_translate_miss(uint32_t vaddr, uint32_t pid, uint32_t index, uint32_t tag) "mmu_translate: MISS vaddr=0x%08x pid=%u TLB[%u] tag=0x%08x" -nios2_mmu_translate_hit(uint32_t vaddr, uint32_t pid, uint32_t index, uint32_t paddr, uint32_t prot) "mmu_translate: HIT vaddr=0x%08x pid=%u TLB[%u] paddr=0x%08x prot=0x%x" - -nios2_mmu_flush_pid_miss(uint32_t pid, uint32_t index, uint32_t vaddr) "mmu_flush: MISS pid=%u TLB[%u] tag=0x%08x" -nios2_mmu_flush_pid_hit(uint32_t pid, uint32_t index, uint32_t vaddr) "mmu_flush: HIT pid=%u TLB[%u] vaddr=0x%08x" - -nios2_mmu_write_tlbacc(uint32_t ig, char c, char r, char w, char x, char g, uint32_t pfn) "mmu_write_tlbacc: ig=0x%02x flags=%c%c%c%c%c pfn=0x%08x" -nios2_mmu_write_tlbmisc(uint32_t way, char r, char w, char t, char b, char p, char d, uint32_t pid) "mmu_write_tlbmisc: way=0x%x flags=%c%c%c%c%c%c pid=%u" -nios2_mmu_write_pteaddr(uint32_t ptb, uint32_t vpn) "mmu_write_pteaddr: ptbase=0x%03x vpn=0x%05x" diff --git a/target/nios2/translate.c b/target/nios2/translate.c deleted file mode 100644 index 7ddc6ac..0000000 --- a/target/nios2/translate.c +++ /dev/null @@ -1,1107 +0,0 @@ -/* - * 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 "qemu/osdep.h" -#include "cpu.h" -#include "tcg/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/translator.h" -#include "qemu/qemu-print.h" -#include "semihosting/semihost.h" - -#define HELPER_H "helper.h" -#include "exec/helper-info.c.inc" -#undef HELPER_H - - -/* is_jmp field values */ -#define DISAS_UPDATE DISAS_TARGET_1 /* cpu state was modified dynamically */ - -#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 */ -typedef struct { - uint8_t op; - union { - uint16_t u; - int16_t s; - } imm16; - uint8_t b; - uint8_t a; -} InstrIType; - -#define I_TYPE(instr, code) \ - InstrIType (instr) = { \ - .op = extract32((code), 0, 6), \ - .imm16.u = extract32((code), 6, 16), \ - .b = extract32((code), 22, 5), \ - .a = extract32((code), 27, 5), \ - } - -typedef target_ulong ImmFromIType(const InstrIType *); - -static target_ulong imm_unsigned(const InstrIType *i) -{ - return i->imm16.u; -} - -static target_ulong imm_signed(const InstrIType *i) -{ - return i->imm16.s; -} - -static target_ulong imm_shifted(const InstrIType *i) -{ - return i->imm16.u << 16; -} - -/* R-Type instruction parsing */ -typedef struct { - uint8_t op; - uint8_t imm5; - uint8_t opx; - uint8_t c; - uint8_t b; - uint8_t a; -} InstrRType; - -#define R_TYPE(instr, code) \ - InstrRType (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 */ -typedef struct { - uint8_t op; - uint32_t imm26; -} InstrJType; - -#define J_TYPE(instr, code) \ - InstrJType (instr) = { \ - .op = extract32((code), 0, 6), \ - .imm26 = extract32((code), 6, 26), \ - } - -typedef void GenFn2i(TCGv, TCGv, target_long); -typedef void GenFn3(TCGv, TCGv, TCGv); -typedef void GenFn4(TCGv, TCGv, TCGv, TCGv); - -typedef struct DisasContext { - DisasContextBase base; - target_ulong pc; - int mem_idx; - uint32_t tb_flags; - TCGv sink; - const ControlRegState *cr_state; - bool eic_present; -} DisasContext; - -static TCGv cpu_R[NUM_GP_REGS]; -static TCGv cpu_pc; -#ifndef CONFIG_USER_ONLY -static TCGv cpu_crs_R[NUM_GP_REGS]; -#endif - -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_gpr(DisasContext *dc, unsigned reg) -{ - assert(reg < NUM_GP_REGS); - - /* - * With shadow register sets, register r0 does not necessarily contain 0, - * but it is overwhelmingly likely that it does -- software is supposed - * to have set r0 to 0 in every shadow register set before use. - */ - if (unlikely(reg == R_ZERO) && FIELD_EX32(dc->tb_flags, TBFLAGS, R0_0)) { - return tcg_constant_tl(0); - } - if (FIELD_EX32(dc->tb_flags, TBFLAGS, CRS0)) { - return cpu_R[reg]; - } -#ifdef CONFIG_USER_ONLY - g_assert_not_reached(); -#else - return cpu_crs_R[reg]; -#endif -} - -static TCGv dest_gpr(DisasContext *dc, unsigned reg) -{ - assert(reg < NUM_GP_REGS); - - /* - * The spec for shadow register sets isn't clear, but we assume that - * writes to r0 are discarded regardless of CRS. - */ - if (unlikely(reg == R_ZERO)) { - if (dc->sink == NULL) { - dc->sink = tcg_temp_new(); - } - return dc->sink; - } - if (FIELD_EX32(dc->tb_flags, TBFLAGS, CRS0)) { - return cpu_R[reg]; - } -#ifdef CONFIG_USER_ONLY - g_assert_not_reached(); -#else - return cpu_crs_R[reg]; -#endif -} - -static void t_gen_helper_raise_exception(DisasContext *dc, uint32_t index) -{ - /* Note that PC is advanced for all hardware exceptions. */ - tcg_gen_movi_tl(cpu_pc, dc->base.pc_next); - gen_helper_raise_exception(tcg_env, tcg_constant_i32(index)); - dc->base.is_jmp = DISAS_NORETURN; -} - -static void gen_goto_tb(DisasContext *dc, int n, uint32_t dest) -{ - const TranslationBlock *tb = dc->base.tb; - - if (translator_use_goto_tb(&dc->base, dest)) { - tcg_gen_goto_tb(n); - tcg_gen_movi_tl(cpu_pc, dest); - tcg_gen_exit_tb(tb, n); - } else { - tcg_gen_movi_tl(cpu_pc, dest); - tcg_gen_lookup_and_goto_ptr(); - } - dc->base.is_jmp = DISAS_NORETURN; -} - -static void gen_jumpr(DisasContext *dc, int regno, bool is_call) -{ - TCGLabel *l = gen_new_label(); - TCGv test = tcg_temp_new(); - TCGv dest = load_gpr(dc, regno); - - tcg_gen_andi_tl(test, dest, 3); - tcg_gen_brcondi_tl(TCG_COND_NE, test, 0, l); - - tcg_gen_mov_tl(cpu_pc, dest); - if (is_call) { - tcg_gen_movi_tl(dest_gpr(dc, R_RA), dc->base.pc_next); - } - tcg_gen_lookup_and_goto_ptr(); - - gen_set_label(l); - tcg_gen_st_tl(dest, tcg_env, offsetof(CPUNios2State, ctrl[CR_BADADDR])); - t_gen_helper_raise_exception(dc, EXCP_UNALIGND); - - dc->base.is_jmp = DISAS_NORETURN; -} - -static void gen_excp(DisasContext *dc, uint32_t code, uint32_t flags) -{ - t_gen_helper_raise_exception(dc, flags); -} - -static bool gen_check_supervisor(DisasContext *dc) -{ - if (FIELD_EX32(dc->tb_flags, TBFLAGS, U)) { - /* CPU in user mode, privileged instruction called, stop. */ - t_gen_helper_raise_exception(dc, EXCP_SUPERI); - return false; - } - return true; -} - -/* - * 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)); -} - -static void call(DisasContext *dc, uint32_t code, uint32_t flags) -{ - tcg_gen_movi_tl(dest_gpr(dc, R_RA), dc->base.pc_next); - 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 = dest_gpr(dc, instr.b); - - tcg_gen_addi_tl(addr, load_gpr(dc, instr.a), instr.imm16.s); -#ifdef CONFIG_USER_ONLY - flags |= MO_UNALN; -#else - flags |= MO_ALIGN; -#endif - tcg_gen_qemu_ld_tl(data, addr, dc->mem_idx, flags); -} - -/* 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.imm16.s); -#ifdef CONFIG_USER_ONLY - flags |= MO_UNALN; -#else - flags |= MO_ALIGN; -#endif - tcg_gen_qemu_st_tl(val, addr, dc->mem_idx, flags); -} - -/* Branch instructions */ -static void br(DisasContext *dc, uint32_t code, uint32_t flags) -{ - I_TYPE(instr, code); - - gen_goto_tb(dc, 0, dc->base.pc_next + (instr.imm16.s & -4)); -} - -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, load_gpr(dc, instr.a), load_gpr(dc, instr.b), l1); - gen_goto_tb(dc, 0, dc->base.pc_next); - gen_set_label(l1); - gen_goto_tb(dc, 1, dc->base.pc_next + (instr.imm16.s & -4)); -} - -/* Comparison instructions */ -static void do_i_cmpxx(DisasContext *dc, uint32_t insn, - TCGCond cond, ImmFromIType *imm) -{ - I_TYPE(instr, insn); - tcg_gen_setcondi_tl(cond, dest_gpr(dc, instr.b), - load_gpr(dc, instr.a), imm(&instr)); -} - -#define gen_i_cmpxx(fname, imm) \ - static void (fname)(DisasContext *dc, uint32_t code, uint32_t flags) \ - { do_i_cmpxx(dc, code, flags, imm); } - -gen_i_cmpxx(gen_cmpxxsi, imm_signed) -gen_i_cmpxx(gen_cmpxxui, imm_unsigned) - -/* Math/logic instructions */ -static void do_i_math_logic(DisasContext *dc, uint32_t insn, - GenFn2i *fn, ImmFromIType *imm, - bool x_op_0_eq_x) -{ - I_TYPE(instr, insn); - target_ulong val; - - if (unlikely(instr.b == R_ZERO)) { - /* Store to R_ZERO is ignored -- this catches the canonical NOP. */ - return; - } - - val = imm(&instr); - - if (instr.a == R_ZERO && FIELD_EX32(dc->tb_flags, TBFLAGS, R0_0)) { - /* This catches the canonical expansions of movi and movhi. */ - tcg_gen_movi_tl(dest_gpr(dc, instr.b), x_op_0_eq_x ? val : 0); - } else { - fn(dest_gpr(dc, instr.b), load_gpr(dc, instr.a), val); - } -} - -#define gen_i_math_logic(fname, insn, x_op_0, imm) \ - static void (fname)(DisasContext *dc, uint32_t code, uint32_t flags) \ - { do_i_math_logic(dc, code, tcg_gen_##insn##_tl, imm, x_op_0); } - -gen_i_math_logic(addi, addi, 1, imm_signed) -gen_i_math_logic(muli, muli, 0, imm_signed) - -gen_i_math_logic(andi, andi, 0, imm_unsigned) -gen_i_math_logic(ori, ori, 1, imm_unsigned) -gen_i_math_logic(xori, xori, 1, imm_unsigned) - -gen_i_math_logic(andhi, andi, 0, imm_shifted) -gen_i_math_logic(orhi , ori, 1, imm_shifted) -gen_i_math_logic(xorhi, xori, 1, imm_shifted) - -/* rB <- prs.rA + sigma(IMM16) */ -static void rdprs(DisasContext *dc, uint32_t code, uint32_t flags) -{ - if (!dc->eic_present) { - t_gen_helper_raise_exception(dc, EXCP_ILLEGAL); - return; - } - if (!gen_check_supervisor(dc)) { - return; - } - -#ifdef CONFIG_USER_ONLY - g_assert_not_reached(); -#else - I_TYPE(instr, code); - TCGv dest = dest_gpr(dc, instr.b); - gen_helper_rdprs(dest, tcg_env, tcg_constant_i32(instr.a)); - tcg_gen_addi_tl(dest, dest, instr.imm16.s); -#endif -} - -/* 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_TEUW), /* ldhu */ - INSTRUCTION(andi), /* andi */ - INSTRUCTION_FLG(gen_stx, MO_TEUW), /* sth */ - INSTRUCTION_FLG(gen_bxx, TCG_COND_GE), /* bge */ - INSTRUCTION_FLG(gen_ldx, MO_TESW), /* ldh */ - INSTRUCTION_FLG(gen_cmpxxsi, TCG_COND_LT), /* cmplti */ - INSTRUCTION_ILLEGAL(), - INSTRUCTION_ILLEGAL(), - INSTRUCTION_NOP(), /* initda */ - INSTRUCTION(ori), /* ori */ - INSTRUCTION_FLG(gen_stx, MO_TEUL), /* stw */ - INSTRUCTION_FLG(gen_bxx, TCG_COND_LT), /* blt */ - INSTRUCTION_FLG(gen_ldx, MO_TEUL), /* 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_TEUW), /* ldhuio */ - INSTRUCTION(andhi), /* andhi */ - INSTRUCTION_FLG(gen_stx, MO_TEUW), /* sthio */ - INSTRUCTION_FLG(gen_bxx, TCG_COND_GEU), /* bgeu */ - INSTRUCTION_FLG(gen_ldx, MO_TESW), /* 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_TESL), /* stwio */ - INSTRUCTION_FLG(gen_bxx, TCG_COND_LTU), /* bltu */ - INSTRUCTION_FLG(gen_ldx, MO_TEUL), /* ldwio */ - INSTRUCTION(rdprs), /* 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) -{ - if (!gen_check_supervisor(dc)) { - return; - } - -#ifdef CONFIG_USER_ONLY - g_assert_not_reached(); -#else - if (FIELD_EX32(dc->tb_flags, TBFLAGS, CRS0)) { - TCGv tmp = tcg_temp_new(); - tcg_gen_ld_tl(tmp, tcg_env, offsetof(CPUNios2State, ctrl[CR_ESTATUS])); - gen_helper_eret(tcg_env, tmp, load_gpr(dc, R_EA)); - } else { - gen_helper_eret(tcg_env, load_gpr(dc, R_SSTATUS), load_gpr(dc, R_EA)); - } - dc->base.is_jmp = DISAS_NORETURN; -#endif -} - -/* PC <- ra */ -static void ret(DisasContext *dc, uint32_t code, uint32_t flags) -{ - gen_jumpr(dc, R_RA, false); -} - -/* - * status <- bstatus - * PC <- ba - */ -static void bret(DisasContext *dc, uint32_t code, uint32_t flags) -{ - if (!gen_check_supervisor(dc)) { - return; - } - -#ifdef CONFIG_USER_ONLY - g_assert_not_reached(); -#else - TCGv tmp = tcg_temp_new(); - tcg_gen_ld_tl(tmp, tcg_env, offsetof(CPUNios2State, ctrl[CR_BSTATUS])); - gen_helper_eret(tcg_env, tmp, load_gpr(dc, R_BA)); - - dc->base.is_jmp = DISAS_NORETURN; -#endif -} - -/* PC <- rA */ -static void jmp(DisasContext *dc, uint32_t code, uint32_t flags) -{ - R_TYPE(instr, code); - - gen_jumpr(dc, instr.a, false); -} - -/* rC <- PC + 4 */ -static void nextpc(DisasContext *dc, uint32_t code, uint32_t flags) -{ - R_TYPE(instr, code); - - tcg_gen_movi_tl(dest_gpr(dc, instr.c), dc->base.pc_next); -} - -/* - * ra <- PC + 4 - * PC <- rA - */ -static void callr(DisasContext *dc, uint32_t code, uint32_t flags) -{ - R_TYPE(instr, code); - - gen_jumpr(dc, instr.a, true); -} - -/* rC <- ctlN */ -static void rdctl(DisasContext *dc, uint32_t code, uint32_t flags) -{ - if (!gen_check_supervisor(dc)) { - return; - } - -#ifdef CONFIG_USER_ONLY - g_assert_not_reached(); -#else - R_TYPE(instr, code); - TCGv t1, t2, dest = dest_gpr(dc, instr.c); - - /* Reserved registers read as zero. */ - if (nios2_cr_reserved(&dc->cr_state[instr.imm5])) { - tcg_gen_movi_tl(dest, 0); - return; - } - - switch (instr.imm5) { - case CR_IPENDING: - /* - * The value of the ipending register is synthetic. - * In hw, this is the AND of a set of hardware irq lines - * with the ienable register. In qemu, we re-use the space - * of CR_IPENDING to store the set of irq lines, and so we - * must perform the AND here, and anywhere else we need the - * guest value of ipending. - */ - t1 = tcg_temp_new(); - t2 = tcg_temp_new(); - tcg_gen_ld_tl(t1, tcg_env, offsetof(CPUNios2State, ctrl[CR_IPENDING])); - tcg_gen_ld_tl(t2, tcg_env, offsetof(CPUNios2State, ctrl[CR_IENABLE])); - tcg_gen_and_tl(dest, t1, t2); - break; - default: - tcg_gen_ld_tl(dest, tcg_env, - offsetof(CPUNios2State, ctrl[instr.imm5])); - break; - } -#endif -} - -/* ctlN <- rA */ -static void wrctl(DisasContext *dc, uint32_t code, uint32_t flags) -{ - if (!gen_check_supervisor(dc)) { - return; - } - -#ifdef CONFIG_USER_ONLY - g_assert_not_reached(); -#else - R_TYPE(instr, code); - TCGv v = load_gpr(dc, instr.a); - uint32_t ofs = offsetof(CPUNios2State, ctrl[instr.imm5]); - uint32_t wr = dc->cr_state[instr.imm5].writable; - uint32_t ro = dc->cr_state[instr.imm5].readonly; - - /* Skip reserved or readonly registers. */ - if (wr == 0) { - return; - } - - switch (instr.imm5) { - case CR_PTEADDR: - gen_helper_mmu_write_pteaddr(tcg_env, v); - break; - case CR_TLBACC: - gen_helper_mmu_write_tlbacc(tcg_env, v); - break; - case CR_TLBMISC: - gen_helper_mmu_write_tlbmisc(tcg_env, v); - break; - case CR_STATUS: - case CR_IENABLE: - /* If interrupts were enabled using WRCTL, trigger them. */ - dc->base.is_jmp = DISAS_UPDATE; - /* fall through */ - default: - if (wr == -1) { - /* The register is entirely writable. */ - tcg_gen_st_tl(v, tcg_env, ofs); - } else { - /* - * The register is partially read-only or reserved: - * merge the value. - */ - TCGv n = tcg_temp_new(); - - tcg_gen_andi_tl(n, v, wr); - - if (ro != 0) { - TCGv o = tcg_temp_new(); - tcg_gen_ld_tl(o, tcg_env, ofs); - tcg_gen_andi_tl(o, o, ro); - tcg_gen_or_tl(n, n, o); - } - - tcg_gen_st_tl(n, tcg_env, ofs); - } - break; - } -#endif -} - -/* prs.rC <- rA */ -static void wrprs(DisasContext *dc, uint32_t code, uint32_t flags) -{ - if (!dc->eic_present) { - t_gen_helper_raise_exception(dc, EXCP_ILLEGAL); - return; - } - if (!gen_check_supervisor(dc)) { - return; - } - -#ifdef CONFIG_USER_ONLY - g_assert_not_reached(); -#else - R_TYPE(instr, code); - gen_helper_wrprs(tcg_env, tcg_constant_i32(instr.c), - load_gpr(dc, instr.a)); - /* - * The expected write to PRS[r0] is 0, from CRS[r0]. - * If not, and CRS == PRS (which we cannot tell from here), - * we may now have a non-zero value in our current r0. - * By ending the TB, we re-evaluate tb_flags and find out. - */ - if (instr.c == 0 - && (instr.a != 0 || !FIELD_EX32(dc->tb_flags, TBFLAGS, R0_0))) { - dc->base.is_jmp = DISAS_UPDATE; - } -#endif -} - -/* Comparison instructions */ -static void gen_cmpxx(DisasContext *dc, uint32_t code, uint32_t flags) -{ - R_TYPE(instr, code); - tcg_gen_setcond_tl(flags, dest_gpr(dc, instr.c), - load_gpr(dc, instr.a), load_gpr(dc, instr.b)); -} - -/* Math/logic instructions */ -static void do_ri_math_logic(DisasContext *dc, uint32_t insn, GenFn2i *fn) -{ - R_TYPE(instr, insn); - fn(dest_gpr(dc, instr.c), load_gpr(dc, instr.a), instr.imm5); -} - -static void do_rr_math_logic(DisasContext *dc, uint32_t insn, GenFn3 *fn) -{ - R_TYPE(instr, insn); - fn(dest_gpr(dc, instr.c), load_gpr(dc, instr.a), load_gpr(dc, instr.b)); -} - -#define gen_ri_math_logic(fname, insn) \ - static void (fname)(DisasContext *dc, uint32_t code, uint32_t flags) \ - { do_ri_math_logic(dc, code, tcg_gen_##insn##_tl); } - -#define gen_rr_math_logic(fname, insn) \ - static void (fname)(DisasContext *dc, uint32_t code, uint32_t flags) \ - { do_rr_math_logic(dc, code, tcg_gen_##insn##_tl); } - -gen_rr_math_logic(add, add) -gen_rr_math_logic(sub, sub) -gen_rr_math_logic(mul, mul) - -gen_rr_math_logic(and, and) -gen_rr_math_logic(or, or) -gen_rr_math_logic(xor, xor) -gen_rr_math_logic(nor, nor) - -gen_ri_math_logic(srai, sari) -gen_ri_math_logic(srli, shri) -gen_ri_math_logic(slli, shli) -gen_ri_math_logic(roli, rotli) - -static void do_rr_mul_high(DisasContext *dc, uint32_t insn, GenFn4 *fn) -{ - R_TYPE(instr, insn); - TCGv discard = tcg_temp_new(); - - fn(discard, dest_gpr(dc, instr.c), - load_gpr(dc, instr.a), load_gpr(dc, instr.b)); -} - -#define gen_rr_mul_high(fname, insn) \ - static void (fname)(DisasContext *dc, uint32_t code, uint32_t flags) \ - { do_rr_mul_high(dc, code, tcg_gen_##insn##_tl); } - -gen_rr_mul_high(mulxss, muls2) -gen_rr_mul_high(mulxuu, mulu2) -gen_rr_mul_high(mulxsu, mulsu2) - -static void do_rr_shift(DisasContext *dc, uint32_t insn, GenFn3 *fn) -{ - R_TYPE(instr, insn); - TCGv sh = tcg_temp_new(); - - tcg_gen_andi_tl(sh, load_gpr(dc, instr.b), 31); - fn(dest_gpr(dc, instr.c), load_gpr(dc, instr.a), sh); -} - -#define gen_rr_shift(fname, insn) \ - static void (fname)(DisasContext *dc, uint32_t code, uint32_t flags) \ - { do_rr_shift(dc, code, tcg_gen_##insn##_tl); } - -gen_rr_shift(sra, sar) -gen_rr_shift(srl, shr) -gen_rr_shift(sll, shl) -gen_rr_shift(rol, rotl) -gen_rr_shift(ror, rotr) - -static void divs(DisasContext *dc, uint32_t code, uint32_t flags) -{ - R_TYPE(instr, (code)); - gen_helper_divs(dest_gpr(dc, instr.c), tcg_env, - load_gpr(dc, instr.a), load_gpr(dc, instr.b)); -} - -static void divu(DisasContext *dc, uint32_t code, uint32_t flags) -{ - R_TYPE(instr, (code)); - gen_helper_divu(dest_gpr(dc, instr.c), tcg_env, - load_gpr(dc, instr.a), load_gpr(dc, instr.b)); -} - -static void trap(DisasContext *dc, uint32_t code, uint32_t flags) -{ -#ifdef CONFIG_USER_ONLY - /* - * The imm5 field is not stored anywhere on real hw; the kernel - * has to load the insn and extract the field. But we can make - * things easier for cpu_loop if we pop this into env->error_code. - */ - R_TYPE(instr, code); - tcg_gen_st_i32(tcg_constant_i32(instr.imm5), tcg_env, - offsetof(CPUNios2State, error_code)); -#endif - t_gen_helper_raise_exception(dc, EXCP_TRAP); -} - -static void gen_break(DisasContext *dc, uint32_t code, uint32_t flags) -{ -#ifndef CONFIG_USER_ONLY - /* The semihosting instruction is "break 1". */ - bool is_user = FIELD_EX32(dc->tb_flags, TBFLAGS, U); - R_TYPE(instr, code); - if (semihosting_enabled(is_user) && instr.imm5 == 1) { - t_gen_helper_raise_exception(dc, EXCP_SEMIHOST); - return; - } -#endif - - t_gen_helper_raise_exception(dc, EXCP_BREAK); -} - -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(wrprs), /* 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(trap), /* trap */ - INSTRUCTION(wrctl), /* wrctl */ - INSTRUCTION_ILLEGAL(), - INSTRUCTION_FLG(gen_cmpxx, TCG_COND_LTU), /* cmpltu */ - INSTRUCTION(add), /* add */ - INSTRUCTION_ILLEGAL(), - INSTRUCTION_ILLEGAL(), - INSTRUCTION(gen_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 const char * const gr_regnames[NUM_GP_REGS] = { - "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", -}; - -#ifndef CONFIG_USER_ONLY -static const char * const cr_regnames[NUM_CR_REGS] = { - "status", "estatus", "bstatus", "ienable", - "ipending", "cpuid", "res6", "exception", - "pteaddr", "tlbacc", "tlbmisc", "reserved1", - "badaddr", "config", "mpubase", "mpuacc", - "res16", "res17", "res18", "res19", - "res20", "res21", "res22", "res23", - "res24", "res25", "res26", "res27", - "res28", "res29", "res30", "res31", -}; -#endif - -/* generate intermediate code for basic block 'tb'. */ -static void nios2_tr_init_disas_context(DisasContextBase *dcbase, CPUState *cs) -{ - DisasContext *dc = container_of(dcbase, DisasContext, base); - CPUNios2State *env = cpu_env(cs); - Nios2CPU *cpu = env_archcpu(env); - int page_insns; - - dc->mem_idx = cpu_mmu_index(cs, false); - dc->cr_state = cpu->cr_state; - dc->tb_flags = dc->base.tb->flags; - dc->eic_present = cpu->eic_present; - - /* Bound the number of insns to execute to those left on the page. */ - page_insns = -(dc->base.pc_first | TARGET_PAGE_MASK) / 4; - dc->base.max_insns = MIN(page_insns, dc->base.max_insns); -} - -static void nios2_tr_tb_start(DisasContextBase *db, CPUState *cs) -{ -} - -static void nios2_tr_insn_start(DisasContextBase *dcbase, CPUState *cs) -{ - tcg_gen_insn_start(dcbase->pc_next); -} - -static void nios2_tr_translate_insn(DisasContextBase *dcbase, CPUState *cs) -{ - DisasContext *dc = container_of(dcbase, DisasContext, base); - const Nios2Instruction *instr; - uint32_t code, pc; - uint8_t op; - - pc = dc->base.pc_next; - dc->pc = pc; - dc->base.pc_next = pc + 4; - - /* Decode an instruction */ - code = cpu_ldl_code(cpu_env(cs), pc); - op = get_opcode(code); - - if (unlikely(op >= ARRAY_SIZE(i_type_instructions))) { - t_gen_helper_raise_exception(dc, EXCP_ILLEGAL); - return; - } - - dc->sink = NULL; - - instr = &i_type_instructions[op]; - instr->handler(dc, code, instr->flags); -} - -static void nios2_tr_tb_stop(DisasContextBase *dcbase, CPUState *cs) -{ - DisasContext *dc = container_of(dcbase, DisasContext, base); - - /* Indicate where the next block should start */ - switch (dc->base.is_jmp) { - case DISAS_TOO_MANY: - gen_goto_tb(dc, 0, dc->base.pc_next); - break; - - case DISAS_UPDATE: - /* Save the current PC, and return to the main loop. */ - tcg_gen_movi_tl(cpu_pc, dc->base.pc_next); - tcg_gen_exit_tb(NULL, 0); - break; - - case DISAS_NORETURN: - /* nothing more to generate */ - break; - - default: - g_assert_not_reached(); - } -} - -static void nios2_tr_disas_log(const DisasContextBase *dcbase, - CPUState *cpu, FILE *logfile) -{ - fprintf(logfile, "IN: %s\n", lookup_symbol(dcbase->pc_first)); - target_disas(logfile, cpu, dcbase->pc_first, dcbase->tb->size); -} - -static const TranslatorOps nios2_tr_ops = { - .init_disas_context = nios2_tr_init_disas_context, - .tb_start = nios2_tr_tb_start, - .insn_start = nios2_tr_insn_start, - .translate_insn = nios2_tr_translate_insn, - .tb_stop = nios2_tr_tb_stop, - .disas_log = nios2_tr_disas_log, -}; - -void gen_intermediate_code(CPUState *cs, TranslationBlock *tb, int *max_insns, - vaddr pc, void *host_pc) -{ - DisasContext dc; - translator_loop(cs, tb, max_insns, pc, host_pc, &nios2_tr_ops, &dc.base); -} - -void nios2_cpu_dump_state(CPUState *cs, FILE *f, int flags) -{ - Nios2CPU *cpu = NIOS2_CPU(cs); - CPUNios2State *env = &cpu->env; - int i; - - qemu_fprintf(f, "IN: PC=%x %s\n", env->pc, lookup_symbol(env->pc)); - - for (i = 0; i < NUM_GP_REGS; i++) { - qemu_fprintf(f, "%9s=%8.8x ", gr_regnames[i], env->regs[i]); - if ((i + 1) % 4 == 0) { - qemu_fprintf(f, "\n"); - } - } - -#if !defined(CONFIG_USER_ONLY) - int j; - - for (i = j = 0; i < NUM_CR_REGS; i++) { - if (!nios2_cr_reserved(&cpu->cr_state[i])) { - qemu_fprintf(f, "%9s=%8.8x ", cr_regnames[i], env->ctrl[i]); - if (++j % 4 == 0) { - qemu_fprintf(f, "\n"); - } - } - } - if (j % 4 != 0) { - qemu_fprintf(f, "\n"); - } - if (cpu->mmu_present) { - qemu_fprintf(f, " mmu write: VPN=%05X PID %02X TLBACC %08X\n", - env->mmu.pteaddr_wr & R_CR_PTEADDR_VPN_MASK, - FIELD_EX32(env->mmu.tlbmisc_wr, CR_TLBMISC, PID), - env->mmu.tlbacc_wr); - } -#endif - qemu_fprintf(f, "\n\n"); -} - -void nios2_tcg_init(void) -{ -#ifndef CONFIG_USER_ONLY - TCGv_ptr crs = tcg_global_mem_new_ptr(tcg_env, - offsetof(CPUNios2State, regs), "crs"); - - for (int i = 0; i < NUM_GP_REGS; i++) { - cpu_crs_R[i] = tcg_global_mem_new(crs, 4 * i, gr_regnames[i]); - } - -#define offsetof_regs0(N) offsetof(CPUNios2State, shadow_regs[0][N]) -#else -#define offsetof_regs0(N) offsetof(CPUNios2State, regs[N]) -#endif - - for (int i = 0; i < NUM_GP_REGS; i++) { - cpu_R[i] = tcg_global_mem_new(tcg_env, offsetof_regs0(i), - gr_regnames[i]); - } - -#undef offsetof_regs0 - - cpu_pc = tcg_global_mem_new(tcg_env, - offsetof(CPUNios2State, pc), "pc"); -} |