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:
Philippe Mathieu-Daudé 2024-03-27 12:10:58 +01:00
parent 92360d6e62
commit 6c3014858c
85 changed files with 31 additions and 8949 deletions

View file

@ -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

View file

@ -9,7 +9,6 @@ subdir('loongarch')
subdir('m68k')
subdir('microblaze')
subdir('mips')
subdir('nios2')
subdir('openrisc')
subdir('ppc')
subdir('riscv')

View file

@ -1,3 +0,0 @@
config NIOS2
bool
select SEMIHOSTING

View file

@ -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

View file

@ -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

View file

@ -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)

View file

@ -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 */

View file

@ -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);
}

View file

@ -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

View file

@ -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}

View file

@ -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' : '-');
}
}

View file

@ -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 */

View file

@ -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);
}

View file

@ -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;
}
}

View file

@ -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 */

View file

@ -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