mirror of
https://github.com/Motorhead1991/qemu.git
synced 2025-08-05 16:53:55 -06:00
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>
This commit is contained in:
parent
92360d6e62
commit
6c3014858c
85 changed files with 31 additions and 8949 deletions
|
@ -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
|
||||
|
|
|
@ -9,7 +9,6 @@ subdir('loongarch')
|
|||
subdir('m68k')
|
||||
subdir('microblaze')
|
||||
subdir('mips')
|
||||
subdir('nios2')
|
||||
subdir('openrisc')
|
||||
subdir('ppc')
|
||||
subdir('riscv')
|
||||
|
|
|
@ -1,3 +0,0 @@
|
|||
config NIOS2
|
||||
bool
|
||||
select SEMIHOSTING
|
|
@ -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
|
|
@ -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
|
|
@ -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)
|
|
@ -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 */
|
|
@ -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);
|
||||
}
|
|
@ -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
|
|
@ -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}
|
|
@ -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' : '-');
|
||||
}
|
||||
}
|
|
@ -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 */
|
|
@ -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);
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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 */
|
|
@ -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"
|
File diff suppressed because it is too large
Load diff
Loading…
Add table
Add a link
Reference in a new issue